@@ -1443,6 +1443,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
params->crypto.control_port_over_nl80211;
sdata->control_port_no_preauth =
params->crypto.control_port_no_preauth;
+ sdata->control_port_vlan_id = params->crypto.control_port_vlan_id;
list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
vlan->control_port_protocol =
@@ -1453,6 +1454,8 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
params->crypto.control_port_over_nl80211;
vlan->control_port_no_preauth =
params->crypto.control_port_no_preauth;
+ vlan->control_port_vlan_id =
+ params->crypto.control_port_vlan_id;
}
link_conf->dtim_period = params->dtim_period;
@@ -1142,6 +1142,7 @@ struct ieee80211_sub_if_data {
bool control_port_no_encrypt;
bool control_port_no_preauth;
bool control_port_over_nl80211;
+ u16 control_port_vlan_id;
atomic_t num_tx_queued;
struct mac80211_qos_map __rcu *qos_map;
@@ -2105,7 +2106,7 @@ void ieee80211_clear_fast_xmit(struct sta_info *sta);
int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev,
const u8 *buf, size_t len,
const u8 *dest, __be16 proto, bool unencrypted,
- int link_id, u64 *cookie);
+ int link_id, u64 *cookie, u16 vlan_id);
int ieee80211_probe_mesh_link(struct wiphy *wiphy, struct net_device *dev,
const u8 *buf, size_t len);
void __ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
@@ -1257,6 +1257,8 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
master->control_port_over_nl80211;
sdata->control_port_no_preauth =
master->control_port_no_preauth;
+ sdata->control_port_vlan_id =
+ master->control_port_vlan_id;
sdata->vif.cab_queue = master->vif.cab_queue;
memcpy(sdata->vif.hw_queue, master->vif.hw_queue,
sizeof(sdata->vif.hw_queue));
@@ -23,6 +23,7 @@
#include <net/mac80211.h>
#include <net/ieee80211_radiotap.h>
#include <linux/unaligned.h>
+#include <linux/if_vlan.h>
#include "ieee80211_i.h"
#include "driver-ops.h"
@@ -114,6 +115,28 @@ static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len,
return false;
}
+static bool ieee80211_vlan_control_allowed(struct ieee80211_rx_data *rx,
+ struct sk_buff *skb,
+ struct ethhdr *ehdr)
+{
+ struct vlan_ethhdr *vhdr = (void *)ehdr;
+ struct ieee80211_sub_if_data *sdata = rx->sdata;
+ u16 vlan_id;
+
+ if (!sdata->control_port_vlan_id)
+ return false;
+ if (ehdr->h_proto != cpu_to_be16(ETH_P_8021Q))
+ return false;
+ if (vhdr->h_vlan_encapsulated_proto != sdata->control_port_protocol)
+ return false;
+
+ vlan_id = be16_to_cpu(vhdr->h_vlan_TCI) & VLAN_VID_MASK;
+ if (vlan_id != sdata->control_port_vlan_id)
+ return false;
+
+ return true;
+}
+
static int
ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,
struct ieee80211_rx_status *status,
@@ -2553,6 +2576,8 @@ __ieee80211_data_to_8023(struct ieee80211_rx_data *rx, bool *port_control)
ehdr = (struct ethhdr *) rx->skb->data;
if (ehdr->h_proto == rx->sdata->control_port_protocol)
*port_control = true;
+ else if (ieee80211_vlan_control_allowed(rx, rx->skb, ehdr))
+ *port_control = true;
else if (check_port_control)
return RX_DROP_U_NOT_PORT_CONTROL;
@@ -2606,6 +2631,11 @@ static bool ieee80211_frame_allowed(struct ieee80211_rx_data *rx, __le16 fc)
return ieee80211_is_our_addr(rx->sdata, ehdr->h_dest, NULL) ||
ether_addr_equal(ehdr->h_dest, pae_group_addr);
+ /* Allow encapsulated EAPOL frames if requested */
+ if (ieee80211_vlan_control_allowed(rx, rx->skb, ehdr))
+ return ether_addr_equal(ehdr->h_dest, rx->sdata->vif.addr) ||
+ ether_addr_equal(ehdr->h_dest, pae_group_addr);
+
if (ieee80211_802_1x_port_control(rx) ||
ieee80211_drop_unencrypted(rx, fc))
return false;
@@ -2626,6 +2656,16 @@ static void ieee80211_deliver_skb_to_local_stack(struct sk_buff *skb,
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
bool noencrypt = !(status->flag & RX_FLAG_DECRYPTED);
+ cfg80211_rx_control_port(dev, skb, noencrypt, rx->link_id);
+ dev_kfree_skb(skb);
+ } else if (ieee80211_vlan_control_allowed(rx, skb, (void *)skb_mac_header(skb))) {
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+ bool noencrypt = !(status->flag & RX_FLAG_DECRYPTED);
+
+ /* strip VLAN */
+ skb_pull(skb, VLAN_HLEN);
+ skb->protocol = sdata->control_port_protocol;
+
cfg80211_rx_control_port(dev, skb, noencrypt, rx->link_id);
dev_kfree_skb(skb);
} else {
@@ -2862,7 +2862,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
if (unlikely(!ieee80211_vif_is_mesh(&sdata->vif) &&
(sdata->vif.type != NL80211_IFTYPE_OCB) &&
!multicast && !authorized &&
- (cpu_to_be16(ethertype) != sdata->control_port_protocol ||
+ (!(ctrl_flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO) ||
!ieee80211_is_our_addr(sdata, skb->data + ETH_ALEN, NULL)))) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
net_info_ratelimited("%s: dropped frame to %pM (unauthorized port)\n",
@@ -6139,10 +6139,12 @@ void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev,
const u8 *buf, size_t len,
const u8 *dest, __be16 proto, bool unencrypted,
- int link_id, u64 *cookie)
+ int link_id, u64 *cookie, u16 vlan_id)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
+ unsigned int hdrlen = sizeof(struct ethhdr);
+ struct vlan_hdr *vhdr;
struct sta_info *sta;
struct sk_buff *skb;
struct ethhdr *ehdr;
@@ -6170,17 +6172,29 @@ int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev,
if (cookie)
ctrl_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
- flags |= IEEE80211_TX_INTFL_NL80211_FRAME_TX;
+ flags |= IEEE80211_TX_INTFL_NL80211_FRAME_TX |
+ IEEE80211_TX_CTL_USE_MINRATE;
+
+ if (vlan_id)
+ hdrlen += sizeof(struct vlan_hdr);
skb = dev_alloc_skb(local->hw.extra_tx_headroom +
- sizeof(struct ethhdr) + len);
+ hdrlen + len);
if (!skb)
return -ENOMEM;
- skb_reserve(skb, local->hw.extra_tx_headroom + sizeof(struct ethhdr));
+ skb_reserve(skb, local->hw.extra_tx_headroom + hdrlen);
skb_put_data(skb, buf, len);
+ if (vlan_id) {
+ vhdr = skb_push(skb, VLAN_HLEN);
+ vhdr->h_vlan_encapsulated_proto =
+ sdata->control_port_protocol;
+ vhdr->h_vlan_TCI = cpu_to_be16(vlan_id & VLAN_VID_MASK);
+ proto = cpu_to_be16(ETH_P_8021Q);
+ }
+
ehdr = skb_push(skb, sizeof(struct ethhdr));
memcpy(ehdr->h_dest, dest, ETH_ALEN);
Tag/untag control packets when requested. Signed-off-by: Janusz Dziedzic <janusz.dziedzic@gmail.com> --- net/mac80211/cfg.c | 3 +++ net/mac80211/ieee80211_i.h | 3 ++- net/mac80211/iface.c | 2 ++ net/mac80211/rx.c | 40 ++++++++++++++++++++++++++++++++++++++ net/mac80211/tx.c | 24 ++++++++++++++++++----- 5 files changed, 66 insertions(+), 6 deletions(-)