nl80211/mac80211: allow non-linear skb in rx_control_port
The current implementation of cfg80211_rx_control_port assumed that the
caller could provide a contiguous region of memory for the control port
frame to be sent up to userspace. Unfortunately, many drivers produce
non-linear skbs, especially for data frames. This resulted in userspace
getting notified of control port frames with correct metadata (from
address, port, etc) yet garbage / nonsense contents, resulting in bad
handshakes, disconnections, etc.
mac80211 linearizes skbs containing management frames. But it didn't
seem worthwhile to do this for control port frames. Thus the signature
of cfg80211_rx_control_port was changed to take the skb directly.
nl80211 then takes care of obtaining control port frame data directly
from the (linear | non-linear) skb.
The caller is still responsible for freeing the skb,
cfg80211_rx_control_port does not take ownership of it.
Fixes: 6a671a50f8
("nl80211: Add CMD_CONTROL_PORT_FRAME API")
Signed-off-by: Denis Kenzior <denkenz@gmail.com>
[fix some kernel-doc formatting, add fixes tag]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
19103a4bfb
commit
a948f71384
|
@ -5835,10 +5835,11 @@ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
|
||||||
/**
|
/**
|
||||||
* cfg80211_rx_control_port - notification about a received control port frame
|
* cfg80211_rx_control_port - notification about a received control port frame
|
||||||
* @dev: The device the frame matched to
|
* @dev: The device the frame matched to
|
||||||
* @buf: control port frame
|
* @skb: The skbuf with the control port frame. It is assumed that the skbuf
|
||||||
* @len: length of the frame data
|
* is 802.3 formatted (with 802.3 header). The skb can be non-linear.
|
||||||
* @addr: The peer from which the frame was received
|
* This function does not take ownership of the skb, so the caller is
|
||||||
* @proto: frame protocol, typically PAE or Pre-authentication
|
* responsible for any cleanup. The caller must also ensure that
|
||||||
|
* skb->protocol is set appropriately.
|
||||||
* @unencrypted: Whether the frame was received unencrypted
|
* @unencrypted: Whether the frame was received unencrypted
|
||||||
*
|
*
|
||||||
* This function is used to inform userspace about a received control port
|
* This function is used to inform userspace about a received control port
|
||||||
|
@ -5851,8 +5852,7 @@ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
|
||||||
* Return: %true if the frame was passed to userspace
|
* Return: %true if the frame was passed to userspace
|
||||||
*/
|
*/
|
||||||
bool cfg80211_rx_control_port(struct net_device *dev,
|
bool cfg80211_rx_control_port(struct net_device *dev,
|
||||||
const u8 *buf, size_t len,
|
struct sk_buff *skb, bool unencrypted);
|
||||||
const u8 *addr, u16 proto, bool unencrypted);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cfg80211_cqm_rssi_notify - connection quality monitoring rssi event
|
* cfg80211_cqm_rssi_notify - connection quality monitoring rssi event
|
||||||
|
|
|
@ -2254,11 +2254,8 @@ static void ieee80211_deliver_skb_to_local_stack(struct sk_buff *skb,
|
||||||
sdata->control_port_over_nl80211)) {
|
sdata->control_port_over_nl80211)) {
|
||||||
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
|
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
|
||||||
bool noencrypt = status->flag & RX_FLAG_DECRYPTED;
|
bool noencrypt = status->flag & RX_FLAG_DECRYPTED;
|
||||||
struct ethhdr *ehdr = eth_hdr(skb);
|
|
||||||
|
|
||||||
cfg80211_rx_control_port(dev, skb->data, skb->len,
|
cfg80211_rx_control_port(dev, skb, noencrypt);
|
||||||
ehdr->h_source,
|
|
||||||
be16_to_cpu(skb->protocol), noencrypt);
|
|
||||||
dev_kfree_skb(skb);
|
dev_kfree_skb(skb);
|
||||||
} else {
|
} else {
|
||||||
/* deliver to local stack */
|
/* deliver to local stack */
|
||||||
|
|
|
@ -14923,20 +14923,24 @@ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
|
||||||
EXPORT_SYMBOL(cfg80211_mgmt_tx_status);
|
EXPORT_SYMBOL(cfg80211_mgmt_tx_status);
|
||||||
|
|
||||||
static int __nl80211_rx_control_port(struct net_device *dev,
|
static int __nl80211_rx_control_port(struct net_device *dev,
|
||||||
const u8 *buf, size_t len,
|
struct sk_buff *skb,
|
||||||
const u8 *addr, u16 proto,
|
|
||||||
bool unencrypted, gfp_t gfp)
|
bool unencrypted, gfp_t gfp)
|
||||||
{
|
{
|
||||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||||
|
struct ethhdr *ehdr = eth_hdr(skb);
|
||||||
|
const u8 *addr = ehdr->h_source;
|
||||||
|
u16 proto = be16_to_cpu(skb->protocol);
|
||||||
struct sk_buff *msg;
|
struct sk_buff *msg;
|
||||||
void *hdr;
|
void *hdr;
|
||||||
|
struct nlattr *frame;
|
||||||
|
|
||||||
u32 nlportid = READ_ONCE(wdev->conn_owner_nlportid);
|
u32 nlportid = READ_ONCE(wdev->conn_owner_nlportid);
|
||||||
|
|
||||||
if (!nlportid)
|
if (!nlportid)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
msg = nlmsg_new(100 + len, gfp);
|
msg = nlmsg_new(100 + skb->len, gfp);
|
||||||
if (!msg)
|
if (!msg)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -14950,13 +14954,17 @@ static int __nl80211_rx_control_port(struct net_device *dev,
|
||||||
nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
|
nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
|
||||||
nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
|
nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
|
||||||
NL80211_ATTR_PAD) ||
|
NL80211_ATTR_PAD) ||
|
||||||
nla_put(msg, NL80211_ATTR_FRAME, len, buf) ||
|
|
||||||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
|
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
|
||||||
nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, proto) ||
|
nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, proto) ||
|
||||||
(unencrypted && nla_put_flag(msg,
|
(unencrypted && nla_put_flag(msg,
|
||||||
NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)))
|
NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)))
|
||||||
goto nla_put_failure;
|
goto nla_put_failure;
|
||||||
|
|
||||||
|
frame = nla_reserve(msg, NL80211_ATTR_FRAME, skb->len);
|
||||||
|
if (!frame)
|
||||||
|
goto nla_put_failure;
|
||||||
|
|
||||||
|
skb_copy_bits(skb, 0, nla_data(frame), skb->len);
|
||||||
genlmsg_end(msg, hdr);
|
genlmsg_end(msg, hdr);
|
||||||
|
|
||||||
return genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
|
return genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
|
||||||
|
@ -14967,14 +14975,12 @@ static int __nl80211_rx_control_port(struct net_device *dev,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cfg80211_rx_control_port(struct net_device *dev,
|
bool cfg80211_rx_control_port(struct net_device *dev,
|
||||||
const u8 *buf, size_t len,
|
struct sk_buff *skb, bool unencrypted)
|
||||||
const u8 *addr, u16 proto, bool unencrypted)
|
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
trace_cfg80211_rx_control_port(dev, buf, len, addr, proto, unencrypted);
|
trace_cfg80211_rx_control_port(dev, skb, unencrypted);
|
||||||
ret = __nl80211_rx_control_port(dev, buf, len, addr, proto,
|
ret = __nl80211_rx_control_port(dev, skb, unencrypted, GFP_ATOMIC);
|
||||||
unencrypted, GFP_ATOMIC);
|
|
||||||
trace_cfg80211_return_bool(ret == 0);
|
trace_cfg80211_return_bool(ret == 0);
|
||||||
return ret == 0;
|
return ret == 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2627,23 +2627,25 @@ TRACE_EVENT(cfg80211_mgmt_tx_status,
|
||||||
);
|
);
|
||||||
|
|
||||||
TRACE_EVENT(cfg80211_rx_control_port,
|
TRACE_EVENT(cfg80211_rx_control_port,
|
||||||
TP_PROTO(struct net_device *netdev, const u8 *buf, size_t len,
|
TP_PROTO(struct net_device *netdev, struct sk_buff *skb,
|
||||||
const u8 *addr, u16 proto, bool unencrypted),
|
bool unencrypted),
|
||||||
TP_ARGS(netdev, buf, len, addr, proto, unencrypted),
|
TP_ARGS(netdev, skb, unencrypted),
|
||||||
TP_STRUCT__entry(
|
TP_STRUCT__entry(
|
||||||
NETDEV_ENTRY
|
NETDEV_ENTRY
|
||||||
MAC_ENTRY(addr)
|
__field(int, len)
|
||||||
|
MAC_ENTRY(from)
|
||||||
__field(u16, proto)
|
__field(u16, proto)
|
||||||
__field(bool, unencrypted)
|
__field(bool, unencrypted)
|
||||||
),
|
),
|
||||||
TP_fast_assign(
|
TP_fast_assign(
|
||||||
NETDEV_ASSIGN;
|
NETDEV_ASSIGN;
|
||||||
MAC_ASSIGN(addr, addr);
|
__entry->len = skb->len;
|
||||||
__entry->proto = proto;
|
MAC_ASSIGN(from, eth_hdr(skb)->h_source);
|
||||||
|
__entry->proto = be16_to_cpu(skb->protocol);
|
||||||
__entry->unencrypted = unencrypted;
|
__entry->unencrypted = unencrypted;
|
||||||
),
|
),
|
||||||
TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT " proto: 0x%x, unencrypted: %s",
|
TP_printk(NETDEV_PR_FMT ", len=%d, " MAC_PR_FMT ", proto: 0x%x, unencrypted: %s",
|
||||||
NETDEV_PR_ARG, MAC_PR_ARG(addr),
|
NETDEV_PR_ARG, __entry->len, MAC_PR_ARG(from),
|
||||||
__entry->proto, BOOL_TO_STR(__entry->unencrypted))
|
__entry->proto, BOOL_TO_STR(__entry->unencrypted))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue