diff mbox

[RFC,4/4] nl80211: Implement TX of control port frames

Message ID 20171228175832.3253-5-denkenz@gmail.com (mailing list archive)
State RFC
Delegated to: Johannes Berg
Headers show

Commit Message

Denis Kenzior Dec. 28, 2017, 5:58 p.m. UTC
This commit implements the TX side of NL80211_CMD_CONTROL_PORT_FRAME.
Userspace provides the raw EAPoL frame using NL80211_ATTR_FRAME.  A
skbuf is built and then injected onto the netdev of the wireless device.
The CONTROL_PORT_ETHERTYPE_NO_ENCRYPT will still in theory be honored by
the underlying TX path code.

Signed-off-by: Denis Kenzior <denkenz@gmail.com>
---
 net/wireless/nl80211.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 66 insertions(+), 1 deletion(-)

Comments

Johannes Berg Jan. 2, 2018, 1:30 p.m. UTC | #1
On Thu, 2017-12-28 at 11:58 -0600, Denis Kenzior wrote:
> This commit implements the TX side of NL80211_CMD_CONTROL_PORT_FRAME.
> Userspace provides the raw EAPoL frame using NL80211_ATTR_FRAME.  A
> skbuf is built and then injected onto the netdev of the wireless device.
> The CONTROL_PORT_ETHERTYPE_NO_ENCRYPT will still in theory be honored by

You mean CONTROL_PORT_NO_ENCRYPT :-)

> the underlying TX path code.

I think this isn't good enough.

There are cases where CONTROL_PORT_ETHERTYPE_NO_ENCRYPT should be
unset, but specific frames still shouldn't be encrypted.

So I think for this particular path it would be better to deprecate
CONTROL_PORT_ETHERTYPE_NO_ENCRYPT entirely, and have a separate per-
frame flag.

That also means that we can't really implement it fully in cfg80211,
but have to provide some functionality for the driver to do things to
be able to honour such flags.

Perhaps a new operation, where we pass a pre-built SKB and control
flags?

johannes
Denis Kenzior Jan. 2, 2018, 6:22 p.m. UTC | #2
Hi Johannes,

On 01/02/2018 07:30 AM, Johannes Berg wrote:
> On Thu, 2017-12-28 at 11:58 -0600, Denis Kenzior wrote:
>> This commit implements the TX side of NL80211_CMD_CONTROL_PORT_FRAME.
>> Userspace provides the raw EAPoL frame using NL80211_ATTR_FRAME.  A
>> skbuf is built and then injected onto the netdev of the wireless device.
>> The CONTROL_PORT_ETHERTYPE_NO_ENCRYPT will still in theory be honored by
> 
> You mean CONTROL_PORT_NO_ENCRYPT :-)

Right

> 
>> the underlying TX path code.
> 
> I think this isn't good enough.
> 

That was my thinking as well

> There are cases where CONTROL_PORT_ETHERTYPE_NO_ENCRYPT should be
> unset, but specific frames still shouldn't be encrypted.
> 
> So I think for this particular path it would be better to deprecate
> CONTROL_PORT_ETHERTYPE_NO_ENCRYPT entirely, and have a separate per-
> frame flag.
> 
> That also means that we can't really implement it fully in cfg80211,
> but have to provide some functionality for the driver to do things to
> be able to honour such flags.

Here's another thought I had while poking around.  Given the above I 
don't want to pursue it too seriously unless you think it might work:

We already have the IEEE80211_TX_INTFL_DONT_ENCRYPT flag on the skb and 
some drivers seem to honor this.  At least that seems to be the intent 
as the CONTROL_PORT_NO_ENCRYPT flag ends up being translated to this 
somewhere in net/mac80211/tx.c.  Are the drivers supposed to honor that 
flag?

If so, can we do something like what ieee80211_process_sa_query_req in 
net/mac80211/rx.c or ieee80211_tdls_prep_mgmt_packet in 
net/mac80211/tdls.c do?  E.g. use ieee80211_tx_skb or 
__ieee80211_subif_start_xmit or similar to inject the skb with the 
DONT_ENCRYPT flag?

> 
> Perhaps a new operation, where we pass a pre-built SKB and control
> flags?
> 

This would likely mean that legacy behavior would still have to be 
supported for quite some time (forever?) for drivers that don't get 
around to implementing this, which would be unfortunate.

Regards,
-Denis
Johannes Berg Jan. 2, 2018, 8:22 p.m. UTC | #3
On Tue, 2018-01-02 at 12:22 -0600, Denis Kenzior wrote:

> > There are cases where CONTROL_PORT_ETHERTYPE_NO_ENCRYPT should be
> > unset, but specific frames still shouldn't be encrypted.
> > 
> > So I think for this particular path it would be better to deprecate
> > CONTROL_PORT_ETHERTYPE_NO_ENCRYPT entirely, and have a separate per-
> > frame flag.
> > 
> > That also means that we can't really implement it fully in cfg80211,
> > but have to provide some functionality for the driver to do things to
> > be able to honour such flags.
> 
> Here's another thought I had while poking around.  Given the above I 
> don't want to pursue it too seriously unless you think it might work:
> 
> We already have the IEEE80211_TX_INTFL_DONT_ENCRYPT flag on the skb and 
> some drivers seem to honor this.  At least that seems to be the intent 
> as the CONTROL_PORT_NO_ENCRYPT flag ends up being translated to this 
> somewhere in net/mac80211/tx.c.  Are the drivers supposed to honor that 
> flag?

Drivers don't have a choice, and don't need to check the flag. What
happens internally in mac80211 is that either txinfo->control.key is
assigned, or not, and drivers make encryption decisions based on that.

> If so, can we do something like what ieee80211_process_sa_query_req in 
> net/mac80211/rx.c or ieee80211_tdls_prep_mgmt_packet in 
> net/mac80211/tdls.c do?  E.g. use ieee80211_tx_skb or 
> __ieee80211_subif_start_xmit or similar to inject the skb with the 
> DONT_ENCRYPT flag?

Yes, this will work - but like I said, it requires more control over
the SKB than cfg80211 has today, and than you can get through the
regular netdev xmit.

> > Perhaps a new operation, where we pass a pre-built SKB and control
> > flags?

> 
> This would likely mean that legacy behavior would still have to be 
> supported for quite some time (forever?) for drivers that don't get 
> around to implementing this, which would be unfortunate.

What do you mean by "legacy behaviour"? *Drivers* don't really need to
do anything one way or the other, and mac80211 is the only thing
implementing control port encrypt/ethertype right now, I suspect.

johannes
Denis Kenzior Jan. 3, 2018, 5:17 p.m. UTC | #4
Hi Johannes,

<snip>

>> If so, can we do something like what ieee80211_process_sa_query_req in
>> net/mac80211/rx.c or ieee80211_tdls_prep_mgmt_packet in
>> net/mac80211/tdls.c do?  E.g. use ieee80211_tx_skb or
>> __ieee80211_subif_start_xmit or similar to inject the skb with the
>> DONT_ENCRYPT flag?
> 
> Yes, this will work - but like I said, it requires more control over
> the SKB than cfg80211 has today, and than you can get through the
> regular netdev xmit.

So, I'm a bit lost here.  You're saying this will work but then it 
won't.  Are you saying this would only work for mac80211 based devices 
(e.g. ones that implement ieee80211_ops) but not ones that implement 
cfg80211_ops?  I presume because those provide their own net_device_ops?

> 
>>> Perhaps a new operation, where we pass a pre-built SKB and control
>>> flags?
> 
>>
>> This would likely mean that legacy behavior would still have to be
>> supported for quite some time (forever?) for drivers that don't get
>> around to implementing this, which would be unfortunate.
> 
> What do you mean by "legacy behaviour"? *Drivers* don't really need to
> do anything one way or the other, and mac80211 is the only thing
> implementing control port encrypt/ethertype right now, I suspect.
> 

When I say legacy I mean 'over netdev', e.g. not over nl80211.

Okay, so what you're saying is that the current CONTROL_PORT_* stuff 
doesn't work on anything besides mac80211 since drivers can completely 
ignore stuff inside cfg80211_connect_params.  And there's no way for 
userspace to know this ahead of time?

So essentially we'd need a new operation in cfg80211_ops that would 
accept the control port frame data and some control flags.  Do we want 
to pass in an skb with all the 802.11 headers set or a 802.3 formatted 
skb (since that is what other data frames look like initially on the 
netdev).

Regards,
-Denis
Arend van Spriel Jan. 3, 2018, 8:13 p.m. UTC | #5
On Wed, Jan 3, 2018 at 6:17 PM, Denis Kenzior <denkenz@gmail.com> wrote:
> Hi Johannes,
>
> <snip>
>
>>> If so, can we do something like what ieee80211_process_sa_query_req in
>>> net/mac80211/rx.c or ieee80211_tdls_prep_mgmt_packet in
>>> net/mac80211/tdls.c do?  E.g. use ieee80211_tx_skb or
>>> __ieee80211_subif_start_xmit or similar to inject the skb with the
>>> DONT_ENCRYPT flag?
>>
>>
>> Yes, this will work - but like I said, it requires more control over
>> the SKB than cfg80211 has today, and than you can get through the
>> regular netdev xmit.
>
>
> So, I'm a bit lost here.  You're saying this will work but then it won't.
> Are you saying this would only work for mac80211 based devices (e.g. ones
> that implement ieee80211_ops) but not ones that implement cfg80211_ops?  I
> presume because those provide their own net_device_ops?

That is my assumption as well.

>>
>>>> Perhaps a new operation, where we pass a pre-built SKB and control
>>>> flags?
>>
>>
>>>
>>> This would likely mean that legacy behavior would still have to be
>>> supported for quite some time (forever?) for drivers that don't get
>>> around to implementing this, which would be unfortunate.
>>
>>
>> What do you mean by "legacy behaviour"? *Drivers* don't really need to
>> do anything one way or the other, and mac80211 is the only thing
>> implementing control port encrypt/ethertype right now, I suspect.
>>
>
> When I say legacy I mean 'over netdev', e.g. not over nl80211.
>
> Okay, so what you're saying is that the current CONTROL_PORT_* stuff doesn't
> work on anything besides mac80211 since drivers can completely ignore stuff
> inside cfg80211_connect_params.  And there's no way for userspace to know
> this ahead of time?
>
> So essentially we'd need a new operation in cfg80211_ops that would accept
> the control port frame data and some control flags.  Do we want to pass in
> an skb with all the 802.11 headers set or a 802.3 formatted skb (since that
> is what other data frames look like initially on the netdev).

Our firmware expects EAPOL stuff to come in as 802.3 packet, which
probably applies to the other full-mac devices as well. Is there any
reason for passing 802.11 packets.

Regards,
Arend
Johannes Berg Jan. 3, 2018, 8:26 p.m. UTC | #6
Hi,

> > > If so, can we do something like what ieee80211_process_sa_query_req in
> > > net/mac80211/rx.c or ieee80211_tdls_prep_mgmt_packet in
> > > net/mac80211/tdls.c do?  E.g. use ieee80211_tx_skb or
> > > __ieee80211_subif_start_xmit or similar to inject the skb with the
> > > DONT_ENCRYPT flag?
> > 
> > Yes, this will work - but like I said, it requires more control over
> > the SKB than cfg80211 has today, and than you can get through the
> > regular netdev xmit.
> 
> So, I'm a bit lost here.  You're saying this will work but then it 
> won't.  Are you saying this would only work for mac80211 based devices 
> (e.g. ones that implement ieee80211_ops) but not ones that implement 
> cfg80211_ops?  I presume because those provide their own net_device_ops?

Heh, yeah, badly phrased. It'll work to do it like process_sa_query or
similar code, for mac80211 drivers. What will *not* work is actually
building and submitting the SKB in cfg80211 though, since you can't
even set the DONT_ENCRYPT flag from there.

> > > This would likely mean that legacy behavior would still have to be
> > > supported for quite some time (forever?) for drivers that don't get
> > > around to implementing this, which would be unfortunate.
> > 
> > What do you mean by "legacy behaviour"? *Drivers* don't really need to
> > do anything one way or the other, and mac80211 is the only thing
> > implementing control port encrypt/ethertype right now, I suspect.
> > 
> 
> When I say legacy I mean 'over netdev', e.g. not over nl80211.

We need to support that behaviour forever anyway, for existing versions
of userspace software.

> Okay, so what you're saying is that the current CONTROL_PORT_* stuff 
> doesn't work on anything besides mac80211 since drivers can completely 
> ignore stuff inside cfg80211_connect_params.  And there's no way for 
> userspace to know this ahead of time?

Yes, NL80211_ATTR_CONTROL_PORT_ETHERTYPE is an attribute set in the
wiphy info - see WIPHY_FLAG_CONTROL_PORT_PROTOCOL in the code.

> So essentially we'd need a new operation in cfg80211_ops that would 
> accept the control port frame data and some control flags.  Do we want 
> to pass in an skb with all the 802.11 headers set or a 802.3 formatted 
> skb (since that is what other data frames look like initially on the 
> netdev).

I think 802.3 format would be better - matches better for full-MAC
devices that might do all header conversion in the device, and getting
the BSSID might even be difficult from cfg80211, etc.

johannes
Denis Kenzior Jan. 3, 2018, 9 p.m. UTC | #7
Hi Arend,

>> So essentially we'd need a new operation in cfg80211_ops that would accept
>> the control port frame data and some control flags.  Do we want to pass in
>> an skb with all the 802.11 headers set or a 802.3 formatted skb (since that
>> is what other data frames look like initially on the netdev).
> 
> Our firmware expects EAPOL stuff to come in as 802.3 packet, which
> probably applies to the other full-mac devices as well. Is there any
> reason for passing 802.11 packets.
> 

I think it was suggested earlier somewhere in this thread to use 802.11 
header similar to how mgmt_tx does things.  To me it makes more sense to 
use 802.3 headers so I thought I would double check.

Regards,
-Denis
diff mbox

Patch

diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 220fe5bc57fd..d6191579f044 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -12464,6 +12464,64 @@  static int nl80211_del_pmk(struct sk_buff *skb, struct genl_info *info)
 	return ret;
 }
 
+static int nl80211_tx_control_port(struct sk_buff *skb, struct genl_info *info)
+{
+	struct wireless_dev *wdev = info->user_ptr[1];
+	const u8 *buf;
+	u8 *dest;
+	size_t len;
+	struct ethhdr *ehdr;
+	int err;
+
+	if (!info->attrs[NL80211_ATTR_FRAME])
+		return -EINVAL;
+
+	wdev_lock(wdev);
+
+	switch (wdev->iftype) {
+	case NL80211_IFTYPE_STATION:
+		if (wdev->current_bss)
+			break;
+		err = -ENOTCONN;
+		goto out;
+	default:
+		err = -EOPNOTSUPP;
+		goto out;
+	}
+
+	buf = nla_data(info->attrs[NL80211_ATTR_FRAME]);
+	len = nla_len(info->attrs[NL80211_ATTR_FRAME]);
+
+	skb = dev_alloc_skb(sizeof(struct ethhdr) + len);
+	if (!skb) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	skb_reserve(skb, sizeof(struct ethhdr));
+
+	dest = skb_put(skb, len);
+	memcpy(dest, buf, len);
+
+	ehdr = skb_push(skb, sizeof(struct ethhdr));
+	memcpy(ehdr->h_dest, wdev->current_bss->pub.bssid, ETH_ALEN);
+	memcpy(ehdr->h_source, wdev_address(wdev), ETH_ALEN);
+	ehdr->h_proto = cpu_to_be16(ETH_P_PAE); // TODO: How to get ethertype?
+
+	wdev_unlock(wdev);
+
+	skb->dev = wdev->netdev;
+	skb->protocol = htons(ETH_P_802_3);
+	skb_reset_network_header(skb);
+	skb_reset_mac_header(skb);
+	dev_queue_xmit(skb);
+	return 0;
+
+ out:
+	wdev_unlock(wdev);
+	return err;
+}
+
 #define NL80211_FLAG_NEED_WIPHY		0x01
 #define NL80211_FLAG_NEED_NETDEV	0x02
 #define NL80211_FLAG_NEED_RTNL		0x04
@@ -13359,7 +13417,14 @@  static const struct genl_ops nl80211_ops[] = {
 		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
 				  NL80211_FLAG_NEED_RTNL,
 	},
-
+	{
+		.cmd = NL80211_CMD_CONTROL_PORT_FRAME,
+		.doit = nl80211_tx_control_port,
+		.policy = nl80211_policy,
+		.flags = GENL_UNS_ADMIN_PERM,
+		.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+				  NL80211_FLAG_NEED_RTNL,
+	},
 };
 
 static struct genl_family nl80211_fam __ro_after_init = {