mac80211: clean up eapol frame handling/port control

This cleans up the eapol frame handling and some related code in the
receive and transmit paths. After this patch
 * EAPOL frames addressed to us or the EAPOL group address are
   always accepted regardless of whether they are encrypted or not
 * other frames from a station are dropped if PAE is enabled and
   the station is not authorized
 * unencrypted frames (except the EAPOL frames above) are dropped if
   drop_unencrypted is enabled
 * some superfluous code that eth_type_trans handles anyway is gone
 * port control is done for transmitted packets

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Johannes Berg
2007-12-19 01:31:22 +01:00
committed by David S. Miller
parent 1946b74ce0
commit ce3edf6d0b
5 changed files with 119 additions and 117 deletions

View File

@@ -208,7 +208,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
rthdr->it_len = cpu_to_le16(rtap_len);
}
skb_set_mac_header(skb, 0);
skb_reset_mac_header(skb);
skb->ip_summed = CHECKSUM_UNNECESSARY;
skb->pkt_type = PACKET_OTHERHOST;
skb->protocol = htons(ETH_P_802_2);
@@ -405,18 +405,6 @@ ieee80211_rx_h_check(struct ieee80211_txrx_data *rx)
return TXRX_DROP;
}
if (!(rx->flags & IEEE80211_TXRXD_RXRA_MATCH))
rx->skb->pkt_type = PACKET_OTHERHOST;
else if (compare_ether_addr(rx->dev->dev_addr, hdr->addr1) == 0)
rx->skb->pkt_type = PACKET_HOST;
else if (is_multicast_ether_addr(hdr->addr1)) {
if (is_broadcast_ether_addr(hdr->addr1))
rx->skb->pkt_type = PACKET_BROADCAST;
else
rx->skb->pkt_type = PACKET_MULTICAST;
} else
rx->skb->pkt_type = PACKET_OTHERHOST;
/* Drop disallowed frame classes based on STA auth/assoc state;
* IEEE 802.11, Chap 5.5.
*
@@ -990,18 +978,10 @@ ieee80211_rx_h_remove_qos_control(struct ieee80211_txrx_data *rx)
}
static int
ieee80211_drop_802_1x_pae(struct ieee80211_txrx_data *rx, int hdrlen)
ieee80211_802_1x_port_control(struct ieee80211_txrx_data *rx)
{
if (rx->sdata->eapol && ieee80211_is_eapol(rx->skb, hdrlen) &&
rx->sdata->type != IEEE80211_IF_TYPE_STA &&
(rx->flags & IEEE80211_TXRXD_RXRA_MATCH))
return 0;
if (unlikely(rx->sdata->ieee802_1x &&
(rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
(rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_NULLFUNC &&
(!rx->sta || !(rx->sta->flags & WLAN_STA_AUTHORIZED)) &&
!ieee80211_is_eapol(rx->skb, hdrlen))) {
if (unlikely(rx->sdata->ieee802_1x_pac &&
(!rx->sta || !(rx->sta->flags & WLAN_STA_AUTHORIZED)))) {
#ifdef CONFIG_MAC80211_DEBUG
printk(KERN_DEBUG "%s: dropped frame "
"(unauthorized port)\n", rx->dev->name);
@@ -1013,7 +993,7 @@ ieee80211_drop_802_1x_pae(struct ieee80211_txrx_data *rx, int hdrlen)
}
static int
ieee80211_drop_unencrypted(struct ieee80211_txrx_data *rx, int hdrlen)
ieee80211_drop_unencrypted(struct ieee80211_txrx_data *rx)
{
/*
* Pass through unencrypted frames if the hardware has
@@ -1026,9 +1006,7 @@ ieee80211_drop_unencrypted(struct ieee80211_txrx_data *rx, int hdrlen)
if (unlikely(!(rx->fc & IEEE80211_FCTL_PROTECTED) &&
(rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
(rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_NULLFUNC &&
(rx->key || rx->sdata->drop_unencrypted) &&
(rx->sdata->eapol == 0 ||
!ieee80211_is_eapol(rx->skb, hdrlen)))) {
(rx->key || rx->sdata->drop_unencrypted))) {
if (net_ratelimit())
printk(KERN_DEBUG "%s: RX non-WEP frame, but expected "
"encryption\n", rx->dev->name);
@@ -1156,6 +1134,7 @@ ieee80211_data_to_8023(struct ieee80211_txrx_data *rx)
} else {
struct ethhdr *ehdr;
__be16 len;
skb_pull(skb, hdrlen);
len = htons(skb->len);
ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr));
@@ -1166,6 +1145,34 @@ ieee80211_data_to_8023(struct ieee80211_txrx_data *rx)
return 0;
}
/*
* requires that rx->skb is a frame with ethernet header
*/
static bool ieee80211_frame_allowed(struct ieee80211_txrx_data *rx)
{
static const u8 pae_group_addr[ETH_ALEN]
= { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x03 };
struct ethhdr *ehdr = (struct ethhdr *) rx->skb->data;
/*
* Allow EAPOL frames to us/the PAE group address regardless
* of whether the frame was encrypted or not.
*/
if (ehdr->h_proto == htons(ETH_P_PAE) &&
(compare_ether_addr(ehdr->h_dest, rx->dev->dev_addr) == 0 ||
compare_ether_addr(ehdr->h_dest, pae_group_addr) == 0))
return true;
if (ieee80211_802_1x_port_control(rx) ||
ieee80211_drop_unencrypted(rx))
return false;
return true;
}
/*
* requires that rx->skb is a frame with ethernet header
*/
static void
ieee80211_deliver_skb(struct ieee80211_txrx_data *rx)
{
@@ -1173,31 +1180,32 @@ ieee80211_deliver_skb(struct ieee80211_txrx_data *rx)
struct ieee80211_local *local = rx->local;
struct sk_buff *skb, *xmit_skb;
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ethhdr *ehdr = (struct ethhdr *) rx->skb->data;
struct sta_info *dsta;
skb = rx->skb;
xmit_skb = NULL;
if (local->bridge_packets && (sdata->type == IEEE80211_IF_TYPE_AP
|| sdata->type == IEEE80211_IF_TYPE_VLAN) &&
if (local->bridge_packets && (sdata->type == IEEE80211_IF_TYPE_AP ||
sdata->type == IEEE80211_IF_TYPE_VLAN) &&
(rx->flags & IEEE80211_TXRXD_RXRA_MATCH)) {
if (is_multicast_ether_addr(skb->data)) {
/* send multicast frames both to higher layers in
* local net stack and back to the wireless media */
if (is_multicast_ether_addr(ehdr->h_dest)) {
/*
* send multicast frames both to higher layers in
* local net stack and back to the wireless medium
*/
xmit_skb = skb_copy(skb, GFP_ATOMIC);
if (!xmit_skb && net_ratelimit())
printk(KERN_DEBUG "%s: failed to clone "
"multicast frame\n", dev->name);
} else {
struct sta_info *dsta;
dsta = sta_info_get(local, skb->data);
if (dsta && !dsta->dev) {
if (net_ratelimit())
printk(KERN_DEBUG "Station with null "
"dev structure!\n");
} else if (dsta && dsta->dev == dev) {
/* Destination station is associated to this
* AP, so send the frame directly to it and
* do not pass the frame to local net stack.
if (dsta && dsta->dev == dev) {
/*
* The destination station is associated to
* this AP (in this VLAN), so send the frame
* directly to it and do not pass it to local
* net stack.
*/
xmit_skb = skb;
skb = NULL;
@@ -1217,8 +1225,8 @@ ieee80211_deliver_skb(struct ieee80211_txrx_data *rx)
if (xmit_skb) {
/* send to wireless media */
xmit_skb->protocol = htons(ETH_P_802_3);
skb_set_network_header(xmit_skb, 0);
skb_set_mac_header(xmit_skb, 0);
skb_reset_network_header(xmit_skb);
skb_reset_mac_header(xmit_skb);
dev_queue_xmit(xmit_skb);
}
}
@@ -1303,38 +1311,36 @@ ieee80211_rx_h_amsdu(struct ieee80211_txrx_data *rx)
}
}
skb_set_network_header(frame, 0);
skb_reset_network_header(frame);
frame->dev = dev;
frame->priority = skb->priority;
rx->skb = frame;
if ((ieee80211_drop_802_1x_pae(rx, 0)) ||
(ieee80211_drop_unencrypted(rx, 0))) {
if (skb == frame) /* last frame */
return TXRX_DROP;
dev_kfree_skb(frame);
continue;
}
payload = frame->data;
ethertype = (payload[6] << 8) | payload[7];
if (likely((compare_ether_addr(payload, rfc1042_header) == 0 &&
ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
compare_ether_addr(payload,
bridge_tunnel_header) == 0)) {
ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
compare_ether_addr(payload,
bridge_tunnel_header) == 0)) {
/* remove RFC1042 or Bridge-Tunnel
* encapsulation and replace EtherType */
skb_pull(frame, 6);
memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
} else {
memcpy(skb_push(frame, sizeof(__be16)), &len,
sizeof(__be16));
memcpy(skb_push(frame, sizeof(__be16)),
&len, sizeof(__be16));
memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
}
if (!ieee80211_frame_allowed(rx)) {
if (skb == frame) /* last frame */
return TXRX_DROP;
dev_kfree_skb(frame);
continue;
}
ieee80211_deliver_skb(rx);
}
@@ -1347,7 +1353,7 @@ ieee80211_rx_h_data(struct ieee80211_txrx_data *rx)
{
struct net_device *dev = rx->dev;
u16 fc;
int err, hdrlen;
int err;
fc = rx->fc;
if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA))
@@ -1356,16 +1362,13 @@ ieee80211_rx_h_data(struct ieee80211_txrx_data *rx)
if (unlikely(!WLAN_FC_DATA_PRESENT(fc)))
return TXRX_DROP;
hdrlen = ieee80211_get_hdrlen(fc);
if ((ieee80211_drop_802_1x_pae(rx, hdrlen)) ||
(ieee80211_drop_unencrypted(rx, hdrlen)))
return TXRX_DROP;
err = ieee80211_data_to_8023(rx);
if (unlikely(err))
return TXRX_DROP;
if (!ieee80211_frame_allowed(rx))
return TXRX_DROP;
rx->skb->dev = dev;
dev->stats.rx_packets++;