Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c99a89ed authored by Thomas Pedersen's avatar Thomas Pedersen Committed by Johannes Berg
Browse files

mac80211: factor out plink event gathering

parent c7e67811
Loading
Loading
Loading
Loading
+115 −80
Original line number Original line Diff line number Diff line
@@ -844,110 +844,69 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
	return changed;
	return changed;
}
}


static void
/*
mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata,
 * mesh_plink_get_event - get correct MPM event
			 struct ieee80211_mgmt *mgmt,
 *
			 struct ieee802_11_elems *elems)
 * @sdata: interface
 * @sta: peer, leave NULL if processing a frame from a new suitable peer
 * @elems: peering management IEs
 * @ftype: frame type
 * @llid: peer's peer link ID
 * @plid: peer's local link ID
 *
 * Return: new peering event for @sta, but PLINK_UNDEFINED should be treated as
 * an error.
 */
static enum plink_event
mesh_plink_get_event(struct ieee80211_sub_if_data *sdata,
		     struct sta_info *sta,
		     struct ieee802_11_elems *elems,
		     enum ieee80211_self_protected_actioncode ftype,
		     __le16 llid, __le16 plid)
{
{

	enum plink_event event = PLINK_UNDEFINED;
	struct sta_info *sta;
	u8 ie_len = elems->peering_len;
	enum plink_event event;
	enum ieee80211_self_protected_actioncode ftype;
	bool matches_local;
	bool matches_local;
	u32 changed = 0;
	u8 ie_len;
	__le16 plid, llid = 0;

	if (!elems->peering) {
		mpl_dbg(sdata,
			"Mesh plink: missing necessary peer link ie\n");
		return;
	}

	if (elems->rsn_len &&
	    sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) {
		mpl_dbg(sdata,
			"Mesh plink: can't establish link with secure peer\n");
		return;
	}

	ftype = mgmt->u.action.u.self_prot.action_code;
	ie_len = elems->peering_len;
	if ((ftype == WLAN_SP_MESH_PEERING_OPEN && ie_len != 4) ||
	    (ftype == WLAN_SP_MESH_PEERING_CONFIRM && ie_len != 6) ||
	    (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len != 6
							&& ie_len != 8)) {
		mpl_dbg(sdata,
			"Mesh plink: incorrect plink ie length %d %d\n",
			ftype, ie_len);
		return;
	}

	if (ftype != WLAN_SP_MESH_PEERING_CLOSE &&
	    (!elems->mesh_id || !elems->mesh_config)) {
		mpl_dbg(sdata, "Mesh plink: missing necessary ie\n");
		return;
	}
	/* Note the lines below are correct, the llid in the frame is the plid
	 * from the point of view of this host.
	 */
	memcpy(&plid, PLINK_GET_LLID(elems->peering), 2);
	if (ftype == WLAN_SP_MESH_PEERING_CONFIRM ||
	    (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8))
		memcpy(&llid, PLINK_GET_PLID(elems->peering), 2);

	/* WARNING: Only for sta pointer, is dropped & re-acquired */
	rcu_read_lock();

	sta = sta_info_get(sdata, mgmt->sa);


	matches_local = (ftype == WLAN_SP_MESH_PEERING_CLOSE ||
	matches_local = (ftype == WLAN_SP_MESH_PEERING_CLOSE ||
			 mesh_matches_local(sdata, elems));
			 mesh_matches_local(sdata, elems));


	if (ftype == WLAN_SP_MESH_PEERING_OPEN &&
	/* deny open request from non-matching peer */
	    !rssi_threshold_check(sdata, sta)) {
	if (!matches_local && !sta) {
		mpl_dbg(sdata, "Mesh plink: %pM does not meet rssi threshold\n",
		event = OPN_RJCT;
			mgmt->sa);
		goto out;
		goto unlock_rcu;
	}
	}


	if (!sta) {
	if (!sta) {
		if (ftype != WLAN_SP_MESH_PEERING_OPEN) {
		if (ftype != WLAN_SP_MESH_PEERING_OPEN) {
			mpl_dbg(sdata, "Mesh plink: cls or cnf from unknown peer\n");
			mpl_dbg(sdata, "Mesh plink: cls or cnf from unknown peer\n");
			goto unlock_rcu;
			goto out;
		}
		}
		/* ftype == WLAN_SP_MESH_PEERING_OPEN */
		/* ftype == WLAN_SP_MESH_PEERING_OPEN */
		if (!mesh_plink_free_count(sdata)) {
		if (!mesh_plink_free_count(sdata)) {
			mpl_dbg(sdata, "Mesh plink error: no more free plinks\n");
			mpl_dbg(sdata, "Mesh plink error: no more free plinks\n");
			goto unlock_rcu;
			goto out;
		}
		/* deny open request from non-matching peer */
		if (!matches_local) {
			mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
					    mgmt->sa, 0, plid,
					    cpu_to_le16(WLAN_REASON_MESH_CONFIG));
			goto unlock_rcu;
		}
		}
	} else {
	} else {
		if (!test_sta_flag(sta, WLAN_STA_AUTH)) {
		if (!test_sta_flag(sta, WLAN_STA_AUTH)) {
			mpl_dbg(sdata, "Mesh plink: Action frame from non-authed peer\n");
			mpl_dbg(sdata, "Mesh plink: Action frame from non-authed peer\n");
			goto unlock_rcu;
			goto out;
		}
		}
		if (sta->plink_state == NL80211_PLINK_BLOCKED)
		if (sta->plink_state == NL80211_PLINK_BLOCKED)
			goto unlock_rcu;
			goto out;
	}
	}


	/* Now we will figure out the appropriate event... */
	/* new matching peer */
	event = PLINK_UNDEFINED;
	if (!sta) {

	if (!sta)
		event = OPN_ACPT;
		event = OPN_ACPT;
	else {
		goto out;
	}

	switch (ftype) {
	switch (ftype) {
	case WLAN_SP_MESH_PEERING_OPEN:
	case WLAN_SP_MESH_PEERING_OPEN:
		if (!matches_local)
		if (!matches_local)
			event = OPN_RJCT;
			event = OPN_RJCT;
			else if (!mesh_plink_free_count(sdata) ||
		if (!mesh_plink_free_count(sdata) ||
		    (sta->plid && sta->plid != plid))
		    (sta->plid && sta->plid != plid))
			event = OPN_IGNR;
			event = OPN_IGNR;
		else
		else
@@ -956,7 +915,7 @@ mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata,
	case WLAN_SP_MESH_PEERING_CONFIRM:
	case WLAN_SP_MESH_PEERING_CONFIRM:
		if (!matches_local)
		if (!matches_local)
			event = CNF_RJCT;
			event = CNF_RJCT;
			else if (!mesh_plink_free_count(sdata) ||
		if (!mesh_plink_free_count(sdata) ||
		    (sta->llid != llid || sta->plid != plid))
		    (sta->llid != llid || sta->plid != plid))
			event = CNF_IGNR;
			event = CNF_IGNR;
		else
		else
@@ -983,10 +942,78 @@ mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata,
		break;
		break;
	default:
	default:
		mpl_dbg(sdata, "Mesh plink: unknown frame subtype\n");
		mpl_dbg(sdata, "Mesh plink: unknown frame subtype\n");
			goto unlock_rcu;
		break;
	}

out:
	return event;
}

static void
mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata,
			 struct ieee80211_mgmt *mgmt,
			 struct ieee802_11_elems *elems)
{

	struct sta_info *sta;
	enum plink_event event;
	enum ieee80211_self_protected_actioncode ftype;
	u32 changed = 0;
	u8 ie_len = elems->peering_len;
	__le16 plid, llid = 0;

	if (!elems->peering) {
		mpl_dbg(sdata,
			"Mesh plink: missing necessary peer link ie\n");
		return;
	}
	}

	if (elems->rsn_len &&
	    sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) {
		mpl_dbg(sdata,
			"Mesh plink: can't establish link with secure peer\n");
		return;
	}
	}


	ftype = mgmt->u.action.u.self_prot.action_code;
	if ((ftype == WLAN_SP_MESH_PEERING_OPEN && ie_len != 4) ||
	    (ftype == WLAN_SP_MESH_PEERING_CONFIRM && ie_len != 6) ||
	    (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len != 6
							&& ie_len != 8)) {
		mpl_dbg(sdata,
			"Mesh plink: incorrect plink ie length %d %d\n",
			ftype, ie_len);
		return;
	}

	if (ftype != WLAN_SP_MESH_PEERING_CLOSE &&
	    (!elems->mesh_id || !elems->mesh_config)) {
		mpl_dbg(sdata, "Mesh plink: missing necessary ie\n");
		return;
	}
	/* Note the lines below are correct, the llid in the frame is the plid
	 * from the point of view of this host.
	 */
	memcpy(&plid, PLINK_GET_LLID(elems->peering), 2);
	if (ftype == WLAN_SP_MESH_PEERING_CONFIRM ||
	    (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8))
		memcpy(&llid, PLINK_GET_PLID(elems->peering), 2);

	/* WARNING: Only for sta pointer, is dropped & re-acquired */
	rcu_read_lock();

	sta = sta_info_get(sdata, mgmt->sa);

	if (ftype == WLAN_SP_MESH_PEERING_OPEN &&
	    !rssi_threshold_check(sdata, sta)) {
		mpl_dbg(sdata, "Mesh plink: %pM does not meet rssi threshold\n",
			mgmt->sa);
		goto unlock_rcu;
	}

	/* Now we will figure out the appropriate event... */
	event = mesh_plink_get_event(sdata, sta, elems, ftype, llid, plid);

	if (event == OPN_ACPT) {
	if (event == OPN_ACPT) {
		rcu_read_unlock();
		rcu_read_unlock();
		/* allocate sta entry if necessary and update info */
		/* allocate sta entry if necessary and update info */
@@ -996,6 +1023,14 @@ mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata,
			goto unlock_rcu;
			goto unlock_rcu;
		}
		}
		sta->plid = plid;
		sta->plid = plid;
	} else if (!sta && event == OPN_RJCT) {
		mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
				    mgmt->sa, 0, plid,
				    cpu_to_le16(WLAN_REASON_MESH_CONFIG));
		goto unlock_rcu;
	} else if (!sta || event == PLINK_UNDEFINED) {
		/* something went wrong */
		goto unlock_rcu;
	}
	}


	changed |= mesh_plink_fsm(sdata, sta, event);
	changed |= mesh_plink_fsm(sdata, sta, event);