📄 hostap_ap.c
字号:
/* * Intersil Prism2 driver with Host AP (software access point) support * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen * <jkmaline@cc.hut.fi> * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> * * This file is to be included into hostap.c when S/W AP functionality is * compiled. * * AP: FIX: * - if unicast Class 2 (assoc,reassoc,disassoc) frame received from * unauthenticated STA, send deauth. frame (8802.11: 5.5) * - if unicast Class 3 (data with to/from DS,deauth,pspoll) frame received * from authenticated, but unassoc STA, send disassoc frame (8802.11: 5.5) * - if unicast Class 3 received from unauthenticated STA, send deauth. frame * (8802.11: 5.5) */static int other_ap_policy[MAX_PARM_DEVICES] = { AP_OTHER_AP_SKIP_ALL, DEF_INTS };module_param_array(other_ap_policy, int, NULL, 0444);MODULE_PARM_DESC(other_ap_policy, "Other AP beacon monitoring policy (0-3)");static int ap_max_inactivity[MAX_PARM_DEVICES] = { AP_MAX_INACTIVITY_SEC, DEF_INTS };module_param_array(ap_max_inactivity, int, NULL, 0444);MODULE_PARM_DESC(ap_max_inactivity, "AP timeout (in seconds) for station " "inactivity");static int ap_bridge_packets[MAX_PARM_DEVICES] = { 1, DEF_INTS };module_param_array(ap_bridge_packets, int, NULL, 0444);MODULE_PARM_DESC(ap_bridge_packets, "Bridge packets directly between " "stations");static int autom_ap_wds[MAX_PARM_DEVICES] = { 0, DEF_INTS };module_param_array(autom_ap_wds, int, NULL, 0444);MODULE_PARM_DESC(autom_ap_wds, "Add WDS connections to other APs " "automatically");static struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta);static void hostap_event_expired_sta(struct net_device *dev, struct sta_info *sta);static void handle_add_proc_queue(void *data);#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMTstatic void handle_wds_oper_queue(void *data);static void prism2_send_mgmt(struct net_device *dev, u16 type_subtype, char *body, int body_len, u8 *addr, u16 tx_cb_idx);#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */#ifndef PRISM2_NO_PROCFS_DEBUGstatic int ap_debug_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data){ char *p = page; struct ap_data *ap = (struct ap_data *) data; if (off != 0) { *eof = 1; return 0; } p += sprintf(p, "BridgedUnicastFrames=%u\n", ap->bridged_unicast); p += sprintf(p, "BridgedMulticastFrames=%u\n", ap->bridged_multicast); p += sprintf(p, "max_inactivity=%u\n", ap->max_inactivity / HZ); p += sprintf(p, "bridge_packets=%u\n", ap->bridge_packets); p += sprintf(p, "nullfunc_ack=%u\n", ap->nullfunc_ack); p += sprintf(p, "autom_ap_wds=%u\n", ap->autom_ap_wds); p += sprintf(p, "auth_algs=%u\n", ap->local->auth_algs); p += sprintf(p, "tx_drop_nonassoc=%u\n", ap->tx_drop_nonassoc); return (p - page);}#endif /* PRISM2_NO_PROCFS_DEBUG */static void ap_sta_hash_add(struct ap_data *ap, struct sta_info *sta){ sta->hnext = ap->sta_hash[STA_HASH(sta->addr)]; ap->sta_hash[STA_HASH(sta->addr)] = sta;}static void ap_sta_hash_del(struct ap_data *ap, struct sta_info *sta){ struct sta_info *s; s = ap->sta_hash[STA_HASH(sta->addr)]; if (s == NULL) return; if (memcmp(s->addr, sta->addr, ETH_ALEN) == 0) { ap->sta_hash[STA_HASH(sta->addr)] = s->hnext; return; } while (s->hnext != NULL && memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0) s = s->hnext; if (s->hnext != NULL) s->hnext = s->hnext->hnext; else printk("AP: could not remove STA " MACSTR " from hash table\n", MAC2STR(sta->addr));}static void ap_free_sta(struct ap_data *ap, struct sta_info *sta){ if (sta->ap && sta->local) hostap_event_expired_sta(sta->local->dev, sta); if (ap->proc != NULL) { char name[20]; sprintf(name, MACSTR, MAC2STR(sta->addr)); remove_proc_entry(name, ap->proc); } if (sta->crypt) { sta->crypt->ops->deinit(sta->crypt->priv); kfree(sta->crypt); sta->crypt = NULL; } skb_queue_purge(&sta->tx_buf); ap->num_sta--;#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT if (sta->aid > 0) ap->sta_aid[sta->aid - 1] = NULL; if (!sta->ap && sta->u.sta.challenge) kfree(sta->u.sta.challenge); del_timer(&sta->timer);#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ kfree(sta);}static void hostap_set_tim(local_info_t *local, int aid, int set){ if (local->func->set_tim) local->func->set_tim(local->dev, aid, set);}static void hostap_event_new_sta(struct net_device *dev, struct sta_info *sta){ union iwreq_data wrqu; memset(&wrqu, 0, sizeof(wrqu)); memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN); wrqu.addr.sa_family = ARPHRD_ETHER; wireless_send_event(dev, IWEVREGISTERED, &wrqu, NULL);}static void hostap_event_expired_sta(struct net_device *dev, struct sta_info *sta){ union iwreq_data wrqu; memset(&wrqu, 0, sizeof(wrqu)); memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN); wrqu.addr.sa_family = ARPHRD_ETHER; wireless_send_event(dev, IWEVEXPIRED, &wrqu, NULL);}#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMTstatic void ap_handle_timer(unsigned long data){ struct sta_info *sta = (struct sta_info *) data; local_info_t *local; struct ap_data *ap; unsigned long next_time = 0; int was_assoc; if (sta == NULL || sta->local == NULL || sta->local->ap == NULL) { PDEBUG(DEBUG_AP, "ap_handle_timer() called with NULL data\n"); return; } local = sta->local; ap = local->ap; was_assoc = sta->flags & WLAN_STA_ASSOC; if (atomic_read(&sta->users) != 0) next_time = jiffies + HZ; else if ((sta->flags & WLAN_STA_PERM) && !(sta->flags & WLAN_STA_AUTH)) next_time = jiffies + ap->max_inactivity; if (time_before(jiffies, sta->last_rx + ap->max_inactivity)) { /* station activity detected; reset timeout state */ sta->timeout_next = STA_NULLFUNC; next_time = sta->last_rx + ap->max_inactivity; } else if (sta->timeout_next == STA_DISASSOC && !(sta->flags & WLAN_STA_PENDING_POLL)) { /* STA ACKed data nullfunc frame poll */ sta->timeout_next = STA_NULLFUNC; next_time = jiffies + ap->max_inactivity; } if (next_time) { sta->timer.expires = next_time; add_timer(&sta->timer); return; } if (sta->ap) sta->timeout_next = STA_DEAUTH; if (sta->timeout_next == STA_DEAUTH && !(sta->flags & WLAN_STA_PERM)) { spin_lock(&ap->sta_table_lock); ap_sta_hash_del(ap, sta); list_del(&sta->list); spin_unlock(&ap->sta_table_lock); sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); } else if (sta->timeout_next == STA_DISASSOC) sta->flags &= ~WLAN_STA_ASSOC; if (was_assoc && !(sta->flags & WLAN_STA_ASSOC) && !sta->ap) hostap_event_expired_sta(local->dev, sta); if (sta->timeout_next == STA_DEAUTH && sta->aid > 0 && !skb_queue_empty(&sta->tx_buf)) { hostap_set_tim(local, sta->aid, 0); sta->flags &= ~WLAN_STA_TIM; } if (sta->ap) { if (ap->autom_ap_wds) { PDEBUG(DEBUG_AP, "%s: removing automatic WDS " "connection to AP " MACSTR "\n", local->dev->name, MAC2STR(sta->addr)); hostap_wds_link_oper(local, sta->addr, WDS_DEL); } } else if (sta->timeout_next == STA_NULLFUNC) { /* send data frame to poll STA and check whether this frame * is ACKed */ /* FIX: IEEE80211_STYPE_NULLFUNC would be more appropriate, but * it is apparently not retried so TX Exc events are not * received for it */ sta->flags |= WLAN_STA_PENDING_POLL; prism2_send_mgmt(local->dev, IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA, NULL, 0, sta->addr, ap->tx_callback_poll); } else { int deauth = sta->timeout_next == STA_DEAUTH; u16 resp; PDEBUG(DEBUG_AP, "%s: sending %s info to STA " MACSTR "(last=%lu, jiffies=%lu)\n", local->dev->name, deauth ? "deauthentication" : "disassociation", MAC2STR(sta->addr), sta->last_rx, jiffies); resp = cpu_to_le16(deauth ? WLAN_REASON_PREV_AUTH_NOT_VALID : WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); prism2_send_mgmt(local->dev, IEEE80211_FTYPE_MGMT | (deauth ? IEEE80211_STYPE_DEAUTH : IEEE80211_STYPE_DISASSOC), (char *) &resp, 2, sta->addr, 0); } if (sta->timeout_next == STA_DEAUTH) { if (sta->flags & WLAN_STA_PERM) { PDEBUG(DEBUG_AP, "%s: STA " MACSTR " would have been " "removed, but it has 'perm' flag\n", local->dev->name, MAC2STR(sta->addr)); } else ap_free_sta(ap, sta); return; } if (sta->timeout_next == STA_NULLFUNC) { sta->timeout_next = STA_DISASSOC; sta->timer.expires = jiffies + AP_DISASSOC_DELAY; } else { sta->timeout_next = STA_DEAUTH; sta->timer.expires = jiffies + AP_DEAUTH_DELAY; } add_timer(&sta->timer);}void hostap_deauth_all_stas(struct net_device *dev, struct ap_data *ap, int resend){ u8 addr[ETH_ALEN]; u16 resp; int i; PDEBUG(DEBUG_AP, "%s: Deauthenticate all stations\n", dev->name); memset(addr, 0xff, ETH_ALEN); resp = __constant_cpu_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); /* deauth message sent; try to resend it few times; the message is * broadcast, so it may be delayed until next DTIM; there is not much * else we can do at this point since the driver is going to be shut * down */ for (i = 0; i < 5; i++) { prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH, (char *) &resp, 2, addr, 0); if (!resend || ap->num_sta <= 0) return; mdelay(50); }}static int ap_control_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data){ char *p = page; struct ap_data *ap = (struct ap_data *) data; char *policy_txt; struct list_head *ptr; struct mac_entry *entry; if (off != 0) { *eof = 1; return 0; } switch (ap->mac_restrictions.policy) { case MAC_POLICY_OPEN: policy_txt = "open"; break; case MAC_POLICY_ALLOW: policy_txt = "allow"; break; case MAC_POLICY_DENY: policy_txt = "deny"; break; default: policy_txt = "unknown"; break; }; p += sprintf(p, "MAC policy: %s\n", policy_txt); p += sprintf(p, "MAC entries: %u\n", ap->mac_restrictions.entries); p += sprintf(p, "MAC list:\n"); spin_lock_bh(&ap->mac_restrictions.lock); for (ptr = ap->mac_restrictions.mac_list.next; ptr != &ap->mac_restrictions.mac_list; ptr = ptr->next) { if (p - page > PAGE_SIZE - 80) { p += sprintf(p, "All entries did not fit one page.\n"); break; } entry = list_entry(ptr, struct mac_entry, list); p += sprintf(p, MACSTR "\n", MAC2STR(entry->addr)); } spin_unlock_bh(&ap->mac_restrictions.lock); return (p - page);}static int ap_control_add_mac(struct mac_restrictions *mac_restrictions, u8 *mac){ struct mac_entry *entry; entry = kmalloc(sizeof(struct mac_entry), GFP_KERNEL); if (entry == NULL) return -1; memcpy(entry->addr, mac, ETH_ALEN); spin_lock_bh(&mac_restrictions->lock); list_add_tail(&entry->list, &mac_restrictions->mac_list); mac_restrictions->entries++; spin_unlock_bh(&mac_restrictions->lock); return 0;}static int ap_control_del_mac(struct mac_restrictions *mac_restrictions, u8 *mac){ struct list_head *ptr; struct mac_entry *entry; spin_lock_bh(&mac_restrictions->lock); for (ptr = mac_restrictions->mac_list.next; ptr != &mac_restrictions->mac_list; ptr = ptr->next) { entry = list_entry(ptr, struct mac_entry, list); if (memcmp(entry->addr, mac, ETH_ALEN) == 0) { list_del(ptr); kfree(entry); mac_restrictions->entries--; spin_unlock_bh(&mac_restrictions->lock); return 0; } } spin_unlock_bh(&mac_restrictions->lock); return -1;}static int ap_control_mac_deny(struct mac_restrictions *mac_restrictions, u8 *mac){ struct list_head *ptr; struct mac_entry *entry; int found = 0; if (mac_restrictions->policy == MAC_POLICY_OPEN) return 0; spin_lock_bh(&mac_restrictions->lock); for (ptr = mac_restrictions->mac_list.next; ptr != &mac_restrictions->mac_list; ptr = ptr->next) { entry = list_entry(ptr, struct mac_entry, list); if (memcmp(entry->addr, mac, ETH_ALEN) == 0) { found = 1; break; } } spin_unlock_bh(&mac_restrictions->lock); if (mac_restrictions->policy == MAC_POLICY_ALLOW) return !found; else return found;}static void ap_control_flush_macs(struct mac_restrictions *mac_restrictions){ struct list_head *ptr, *n; struct mac_entry *entry; if (mac_restrictions->entries == 0) return; spin_lock_bh(&mac_restrictions->lock); for (ptr = mac_restrictions->mac_list.next, n = ptr->next; ptr != &mac_restrictions->mac_list; ptr = n, n = ptr->next) { entry = list_entry(ptr, struct mac_entry, list); list_del(ptr); kfree(entry); } mac_restrictions->entries = 0; spin_unlock_bh(&mac_restrictions->lock);}static int ap_control_kick_mac(struct ap_data *ap, struct net_device *dev, u8 *mac){ struct sta_info *sta; u16 resp; spin_lock_bh(&ap->sta_table_lock); sta = ap_get_sta(ap, mac); if (sta) { ap_sta_hash_del(ap, sta); list_del(&sta->list); } spin_unlock_bh(&ap->sta_table_lock); if (!sta) return -EINVAL; resp = cpu_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH, (char *) &resp, 2, sta->addr, 0); if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap) hostap_event_expired_sta(dev, sta); ap_free_sta(ap, sta); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -