Thread (1 message) 1 message, 1 author, 2016-10-05

Re: [PATCH 3/3] mac80211: multicast to unicast conversion

From: Johannes Berg <hidden>
Date: 2016-10-05 10:19:51
Also in: linux-wireless

+netdev
IEEE802.11-2012 proposes directed multicast service (DMS) using A-
MSDU frames and a station initiated control protocol. It has the
advantage that the station can recover the destination multicast mac
address, but it is not backward compatible with non QOS stations and
does not enable the administrator of a BSS to force this mode of
operation within a BSS. Additionally, it would require both the ap
and the station to implement the control protocol, which is optional
on both ends. Furthermore, I've seen a few mobile phone stations
locally that indicate qos support but won't complete DHCP if their
broadcasts are encapsulated as A-MSDU. Though they work fine with
this series approach.
Presumably those phones also don't even try to use DMS, right?
This patch therefore does not opt to implement DMS but instead just
replicates the packet and changes the destination address. As this
works fine with ARP, IPv4 and IPv6, it is limited to these protocols
and normal 802.11 multicast frames are send out for all other payload
protocols.
How did you determine that it "works fine"?

I see at least one undesirable impact of this, which DMS doesn't have;
it breaks a client's MUST NOT requirement from RFC 1122:

         An ICMP error message MUST NOT be sent as the result of
         receiving:
[...]
         *    a datagram sent as a link-layer broadcast, or
[...]

since the client can no longer realize that the datagram was in fact
sent as a link-layer broadcast (or multicast).
 include/net/cfg80211.h        |   5 ++
 include/uapi/linux/nl80211.h  |   7 +++
 net/mac80211/cfg.c            |  14 ++++++
 net/mac80211/debugfs_netdev.c |  29 ++++++++++++
 net/mac80211/ieee80211_i.h    |   1 +
 net/mac80211/tx.c             | 103
++++++++++++++++++++++++++++++++++++++++++
 net/wireless/nl80211.c        |  33 ++++++++++++++
 net/wireless/rdev-ops.h       |  11 +++++
 net/wireless/trace.h          |  19 ++++++++
 9 files changed, 222 insertions(+)
You should split the patch into cfg80211 and mac80211, IMHO it's big
enough to do that.
+ * @set_ap_unicast: set the multicast to unicast flag for a AP
interface
That API name isn't very descriptive, I'm sure we can do something
better there.

Also, perhaps we should structure this already like we would DMS, with
a per-station toggle or even list of multicast addresses?
quoted hunk
@@ -2261,6 +2266,8 @@ enum nl80211_attrs {
 
 	NL80211_ATTR_MESH_PEER_AID,
 
+	NL80211_ATTR_UNICAST,
missing docs, but likely doesn't matter after the comment above
+static int ieee80211_set_ap_unicast(struct wiphy *wiphy, struct
net_device *dev,
+				    const bool unicast)
+{
+	struct ieee80211_sub_if_data *sdata =
IEEE80211_DEV_TO_SUB_IF(dev);
+
+	if (sdata->vif.type != NL80211_IFTYPE_AP)
+		return -1;
Was this not documented but also intended to apply to its dependent
VLANs?
+static ssize_t
+ieee80211_if_fmt_unicast(const struct ieee80211_sub_if_data *sdata,
+			 char *buf, int buflen)
+{
+	const struct ieee80211_if_ap *ifap = &sdata->u.ap;
+
+	return snprintf(buf, buflen, "0x%x\n", ifap->unicast);
+}
+
+static ssize_t
+ieee80211_if_parse_unicast(struct ieee80211_sub_if_data *sdata,
+			   const char *buf, int buflen)
+{
+	struct ieee80211_if_ap *ifap = &sdata->u.ap;
+	u8 val;
+	int ret;
+
+	ret = kstrtou8(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	ifap->unicast = val ? 1 : 0;
+
+	return buflen;
+}
+
+IEEE80211_IF_FILE_RW(unicast);
No need for this, at least the setter, any more.
+/* Check if multicast to unicast conversion is needed and do it.
+ * Returns 1 if skb was freed and should not be send out. */
wrong comment style :)
+static int
+ieee80211_tx_multicast_to_unicast(struct ieee80211_sub_if_data
*sdata,
+				  struct sk_buff *skb,
u32  info_flags)
+{
+	struct ieee80211_local *local = sdata->local;
+	const struct ethhdr *eth = (void *)skb->data;
+	const struct vlan_ethhdr *ethvlan = (void *)skb->data;
+	struct sta_info *sta, *prev = NULL;
+	struct sk_buff *cloned_skb;
+	u16 ethertype;
+
+	/* multicast to unicast conversion only for AP interfaces */
+	switch (sdata->vif.type) {
+	case NL80211_IFTYPE_AP_VLAN:
+		sta = rcu_dereference(sdata->u.vlan.sta);
+		if (sta) /* 4addr */
+			return 0;
+	case NL80211_IFTYPE_AP:
+		break;
+	default:
+		return 0;
+	}
+
+	/* check runtime toggle for this bss */
+	if (!sdata->bss->unicast)
+		return 0;
+
+	/* check if this is a multicast frame */
+	if (!is_multicast_ether_addr(eth->h_dest))
+		return 0;
That should probably come first, would make this far easier to read.
+		if (unlikely(!memcmp(eth->h_source, sta->sta.addr,
ETH_ALEN)))
+			/* do not send back to source */
+			continue;
ether_addr_something, instead of memcmp?
+		if (unlikely(is_multicast_ether_addr(sta-
quoted
sta.addr))) {
+			WARN_ONCE(1, "sta with multicast address
%pM",
+				  sta->sta.addr);
+			continue;
+		}
Err, no, remove this... it cannot happen. We could move the check into
cfg80211 from mac80211, but we surely shouldn't add it into the TX
hotpath!
+		if (prev) {
+			cloned_skb = skb_clone(skb, GFP_ATOMIC);
+			if (likely(!ieee80211_change_da(cloned_skb,
prev)))
+				ieee80211_subif_start_xmit(cloned_sk
b,
+							   cloned_sk
b->dev);
I'm not very happy with this recursion, but I guess it can't be avoided
easily. However, you can easily call the more
sensible __ieee80211_subif_start_xmit() instead of this one.
+	unicast = nla_data(info->attrs[NL80211_ATTR_UNICAST]);
What's this supposed to mean?

johannes
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help