📄 ieee80211_input.c
字号:
ni->ni_rxseqs[tid] = rxseq; } } switch (type) { case IEEE80211_FC0_TYPE_DATA: hdrspace = ieee80211_hdrspace(ic, wh); if (skb->len < hdrspace) { IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, wh, "data", "too short: len %u, expecting %u", skb->len, hdrspace); vap->iv_stats.is_rx_tooshort++; goto out; /* XXX */ } switch (vap->iv_opmode) { case IEEE80211_M_STA: if ((dir != IEEE80211_FC1_DIR_FROMDS) && (!((vap->iv_flags_ext & IEEE80211_FEXT_WDS) && (dir == IEEE80211_FC1_DIR_DSTODS)))) { IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, wh, "data", "invalid dir 0x%x", dir); vap->iv_stats.is_rx_wrongdir++; goto out; } if ((dev->flags & IFF_MULTICAST) && IEEE80211_IS_MULTICAST(wh->i_addr1)) { if (IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_myaddr)) { /* * In IEEE802.11 network, multicast packet * sent from me is broadcasted from AP. * It should be silently discarded for * SIMPLEX interface. * * NB: Linux has no IFF_ flag to indicate * if an interface is SIMPLEX or not; * so we always assume it to be true. */ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, NULL, "%s", "multicast echo"); vap->iv_stats.is_rx_mcastecho++; goto out; } /* * if it is brodcasted by me on behalf of * a station behind me, drop it. */ if (vap->iv_flags_ext & IEEE80211_FEXT_WDS) { struct ieee80211_node_table *nt; struct ieee80211_node *ni_wds = NULL; nt = &ic->ic_sta; ni_wds = ieee80211_find_wds_node(nt, wh->i_addr3); if (ni_wds) { ieee80211_free_node(ni_wds); /* Decr ref count */ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, NULL, "%s", "multicast echo originated from node behind me"); vap->iv_stats.is_rx_mcastecho++; goto out; } } } break; case IEEE80211_M_IBSS: case IEEE80211_M_AHDEMO: if (dir != IEEE80211_FC1_DIR_NODS) { IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, wh, "data", "invalid dir 0x%x", dir); vap->iv_stats.is_rx_wrongdir++; goto out; } /* XXX no power-save support */ break; case IEEE80211_M_HOSTAP: if ((dir != IEEE80211_FC1_DIR_TODS) && (dir != IEEE80211_FC1_DIR_DSTODS)) { IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, wh, "data", "invalid dir 0x%x", dir); vap->iv_stats.is_rx_wrongdir++; goto out; } /* check if source STA is associated */ if (ni == vap->iv_bss) { IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, "data", "%s", "unknown src"); /* NB: caller deals with reference */ if (vap->iv_state == IEEE80211_S_RUN) ieee80211_send_error(ni, wh->i_addr2, IEEE80211_FC0_SUBTYPE_DEAUTH, IEEE80211_REASON_NOT_AUTHED); vap->iv_stats.is_rx_notassoc++; goto err; } if (ni->ni_associd == 0) { IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, "data", "%s", "unassoc src"); IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DISASSOC, IEEE80211_REASON_NOT_ASSOCED); vap->iv_stats.is_rx_notassoc++; goto err; } /* * If we're a 4 address packet, make sure we have an entry in * the node table for the packet source address (addr4). * If not, add one. */ if (dir == IEEE80211_FC1_DIR_DSTODS) { struct ieee80211_node_table *nt; struct ieee80211_frame_addr4 *wh4; if (!(vap->iv_flags_ext & IEEE80211_FEXT_WDS)) { IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, "data", "%s", "4 addr not allowed"); goto err; } wh4 = (struct ieee80211_frame_addr4 *)skb->data; nt = &ic->ic_sta; ni_wds = ieee80211_find_wds_node(nt, wh4->i_addr4); /* Last call increments ref count if !NULL */ if ((ni_wds != NULL) && (ni_wds != ni)) { /* * node with source address (addr4) moved * to another WDS capable station. remove the * reference to the previous station and add * reference to the new one */ (void) ieee80211_remove_wds_addr(nt, wh4->i_addr4); ieee80211_add_wds_addr(nt, ni, wh4->i_addr4, 0); } if (ni_wds == NULL) ieee80211_add_wds_addr(nt, ni, wh4->i_addr4, 0); else ieee80211_free_node(ni_wds); /* Decr ref count */ } /* * Check for power save state change. */ if (!(ni->ni_flags & IEEE80211_NODE_UAPSD)) { if ((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^ (ni->ni_flags & IEEE80211_NODE_PWR_MGT)) ieee80211_node_pwrsave(ni, wh->i_fc[1] & IEEE80211_FC1_PWR_MGT); } else if (ni->ni_flags & IEEE80211_NODE_PS_CHANGED) { int pwr_save_changed = 0; IEEE80211_LOCK_IRQ(ic); if ((*(u_int16_t *)(&wh->i_seq[0])) == ni->ni_pschangeseq) { ni->ni_flags &= ~IEEE80211_NODE_PS_CHANGED; pwr_save_changed = 1; } IEEE80211_UNLOCK_IRQ(ic); if (pwr_save_changed) ieee80211_node_pwrsave(ni, wh->i_fc[1] & IEEE80211_FC1_PWR_MGT); } break; case IEEE80211_M_WDS: if (dir != IEEE80211_FC1_DIR_DSTODS) { IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, wh, "data", "invalid dir 0x%x", dir); vap->iv_stats.is_rx_wrongdir++; goto out; } break; default: /* XXX here to keep compiler happy */ goto out; } /* * Handle privacy requirements. Note that we * must not be preempted from here until after * we (potentially) call ieee80211_crypto_demic; * otherwise we may violate assumptions in the * crypto cipher modules used to do delayed update * of replay sequence numbers. */ if (wh->i_fc[1] & IEEE80211_FC1_PROT) { if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { /* * Discard encrypted frames when privacy is off. */ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, "WEP", "%s", "PRIVACY off"); vap->iv_stats.is_rx_noprivacy++; IEEE80211_NODE_STAT(ni, rx_noprivacy); goto out; } key = ieee80211_crypto_decap(ni, skb, hdrspace); if (key == NULL) { /* NB: stats+msgs handled in crypto_decap */ IEEE80211_NODE_STAT(ni, rx_wepfail); goto out; } wh = (struct ieee80211_frame *)skb->data; wh->i_fc[1] &= ~IEEE80211_FC1_PROT; } else key = NULL; /* * Next up, any fragmentation. */ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { skb = ieee80211_defrag(ni, skb, hdrspace); if (skb == NULL) { /* Fragment dropped or frame not complete yet */ goto out; } } wh = NULL; /* no longer valid, catch any uses */ /* * Next strip any MSDU crypto bits. */ if (key != NULL && !ieee80211_crypto_demic(vap, key, skb, hdrspace)) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, ni->ni_macaddr, "data", "%s", "demic error"); IEEE80211_NODE_STAT(ni, rx_demicfail); goto out; } /* * Finally, strip the 802.11 header. */ skb = ieee80211_decap(vap, skb, hdrspace); if (skb == NULL) { /* don't count Null data frames as errors */ if (subtype == IEEE80211_FC0_SUBTYPE_NODATA) goto out; IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, ni->ni_macaddr, "data", "%s", "decap error"); vap->iv_stats.is_rx_decap++; IEEE80211_NODE_STAT(ni, rx_decap); goto err; } eh = (struct ether_header *) skb->data; if (! accept_data_frame(vap, ni, key, skb, eh)) goto out; vap->iv_devstats.rx_packets++; vap->iv_devstats.rx_bytes += skb->len; IEEE80211_NODE_STAT(ni, rx_data); IEEE80211_NODE_STAT_ADD(ni, rx_bytes, skb->len); ic->ic_lastdata = jiffies;#ifdef ATH_SUPERG_FF /* check for FF */ llc = (struct llc *) (skb->data + sizeof(struct ether_header)); if (ntohs(llc->llc_snap.ether_type) == (u_int16_t)ATH_ETH_TYPE) { struct sk_buff *skb1 = NULL; struct ether_header *eh_tmp; struct athl2p_tunnel_hdr *ath_hdr; int frame_len; /* NB: assumes linear (i.e., non-fragmented) skb */ /* get to the tunneled headers */ ath_hdr = (struct athl2p_tunnel_hdr *) skb_pull(skb, sizeof(struct ether_header) + LLC_SNAPFRAMELEN); /* ignore invalid frames */ if(ath_hdr == NULL) goto err; /* only implementing FF now. drop all others. */ if (ath_hdr->proto != ATH_L2TUNNEL_PROTO_FF) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_SUPG | IEEE80211_MSG_INPUT, eh->ether_shost, "fast-frame", "bad atheros tunnel prot %u", ath_hdr->proto); vap->iv_stats.is_rx_badathtnl++; goto err; } vap->iv_stats.is_rx_ffcnt++; /* move past the tunneled header, with alignment */ skb_pull(skb, roundup(sizeof(struct athl2p_tunnel_hdr) - 2, 4) + 2); skb1 = skb_clone(skb, GFP_ATOMIC); /* XXX: GFP_ATOMIC is overkill? */ eh_tmp = (struct ether_header *)skb->data; /* ether_type must be length*/ frame_len = ntohs(eh_tmp->ether_type); /* we now have 802.3 MAC hdr followed by 802.2 LLC/SNAP. convert to DIX */ athff_decap(skb); /* remove second frame from end of first */ skb_trim(skb, sizeof(struct ether_header) + frame_len - LLC_SNAPFRAMELEN); /* prepare second tunneled frame */ skb_pull(skb1, roundup(sizeof(struct ether_header) + frame_len, 4)); eh_tmp = (struct ether_header *)skb1->data; frame_len = ntohs(eh_tmp->ether_type); athff_decap(skb1); /* deliver the frames */ ieee80211_deliver_data(ni, skb); ieee80211_deliver_data(ni, skb1); } else { /* assume non-atheros llc type */ ieee80211_deliver_data(ni, skb); }#else /* !ATH_SUPERG_FF */ ieee80211_deliver_data(ni, skb);#endif return IEEE80211_FC0_TYPE_DATA; case IEEE80211_FC0_TYPE_MGT: /* * WDS opmode do not support managment frames */ if (vap->iv_opmode == IEEE80211_M_WDS) { vap->iv_stats.is_rx_mgtdiscard++; goto out; } IEEE80211_NODE_STAT(ni, rx_mgmt); if (dir != IEEE80211_FC1_DIR_NODS) { vap->iv_stats.is_rx_wrongdir++; goto err; } if (skb->len < sizeof(struct ieee80211_frame)) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, "mgt", "too short: len %u", skb->len); vap->iv_stats.is_rx_tooshort++; goto out; }#ifdef IEEE80211_DEBUG if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) || ieee80211_msg_dumppkts(vap)) { ieee80211_note(vap, "received %s from %s rssi %d\n", ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT], ether_sprintf(wh->i_addr2), rssi); }#endif if (wh->i_fc[1] & IEEE80211_FC1_PROT) { if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) { /* * Only shared key auth frames with a challenge * should be encrypted, discard all others. */ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT], "%s", "WEP set but not permitted"); vap->iv_stats.is_rx_mgtdiscard++; /* XXX */ goto out; } if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { /* * Discard encrypted frames when privacy is off. */ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, "mgt", "%s", "WEP set but PRIVACY off"); vap->iv_stats.is_rx_noprivacy++; goto out; } hdrspace = ieee80211_hdrspace(ic, wh); key = ieee80211_crypto_decap(ni, skb, hdrspace); if (key == NULL) { /* NB: stats+msgs handled in crypto_decap */ goto out; } wh = (struct ieee80211_frame *)skb->data; wh->i_fc[1] &= ~IEEE80211_FC1_PROT; } ic->ic_recv_mgmt(ni, skb, subtype, rssi, rstamp); goto out; case IEEE80211_FC0_TYPE_CTL: IEEE80211_NODE_STAT(ni, rx_ctrl); vap->iv_stats.is_rx_ctl++; if (vap->iv_opmode == IEEE80211_M_HOSTAP) if (subtype == IEEE80211_FC0_SUBTYPE_PS_POLL) ieee80211_recv_pspoll(ni, skb); goto out; default: IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, wh, NULL, "bad frame type 0x%x", type); /* should not come here */ break; }err: vap->iv_devstats.rx_errors++;out: if (skb != NULL) dev_kfree_skb(skb); return type;#undef HAS_SEQ}EXPORT_SYMBOL(ieee80211_input);/* * Determines whether a frame should be accepted, based on information * about the frame's origin and encryption, and policy for this vap. */static int accept_data_frame(struct ieee80211vap *vap, struct ieee80211_node *ni, struct ieee80211_key *key, struct sk_buff *skb, struct ether_header *eh){#define IS_EAPOL(eh) ((eh)->ether_type == __constant_htons(ETHERTYPE_PAE))#define PAIRWISE_SET(vap) ((vap)->iv_nw_keys[0].wk_cipher != &ieee80211_cipher_none) if (IS_EAPOL(eh)) { /* encrypted eapol is always OK */ if (key) return 1; /* cleartext eapol is OK if we don't have pairwise keys yet */ if (! PAIRWISE_SET(vap)) return 1; /* cleartext eapol is OK if configured to allow it */ if (! IEEE80211_VAP_DROPUNENC_EAPOL(vap)) return 1; /* cleartext eapol is OK if other unencrypted is OK */ if (! (vap->iv_flags & IEEE80211_F_DROPUNENC)) return 1; /* not OK */ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, eh->ether_shost, "data", "unauthorized port: ether type 0x%x len %u", eh->ether_type, skb->len); vap->iv_stats.is_rx_unauth++; vap->iv_devstats.rx_errors++; IEEE80211_NODE_STAT(ni, rx_unauth); return 0; } if (!ieee80211_node_is_authorized(ni)) { /* * Deny any non-PAE frames received prior to * authorization. For open/shared-key * authentication the port is mark authorized * after authentication completes. For 802.1x * the port is not marked authorized by the * authenticator until the handshake has completed. */ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, eh->ether_shost, "data", "unauthorized port: ether type 0x%x len %u", eh->ether_type, skb->len); vap->iv_stats.is_rx_unauth++; vap->iv_devstats.rx_errors++; IEEE80211_NODE_STAT(ni, rx_unauth); return 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -