Hi,
I want to write a short summary here, to see what I changed to the old
implementation, please see last RFC patch.
How to use it (I know some commands are deprecated and should not be used),
I have two scripts to do that:
Node A (run at first):
---
#!/bin/sh
modprobe btusb
modprobe bluetooth_6lowpan
hciconfig hci0 up
echo "hci0 1" > /sys/kernel/debug/bluetooth/6lowpan_enable
hciconfig hci0 leadv
hciconfig
---
hcicondif will printout the address of hci dev, remember that.
Node B (run after Node A stuff):
---
#!/bin/sh
if [ "$#" -ne 1 ]; then
echo "run $0 \$SLAVE_ADDRESS, where SLAVE_ADDRESS is the BTLE slave"
exit 1
fi
modprobe btusb
modprobe bluetooth_6lowpan
hciconfig hci0 up
echo "hci0 1" > /sys/kernel/debug/bluetooth/6lowpan_enable
hcitool lecc $1
echo "connect $1 1" > /sys/kernel/debug/bluetooth/6lo0
---
Where $1 is the address of the Node A.
Well done. Your connection should be established. Except you have crap usb
dongles like me (broadcom) where sometimes the reset functionality seems to
be not working 100% correctly. btw: my workaround, replug usb dongles (but
I always need to tell the new usb bus information my qemu vm :-()
KNOWN BUGS and how to reproduce that one:
First, thanks to Luiz Augusto von Dentz, which tried to help me there.
But I gave up, the BUG still exists and it's because L2CAP implementation
or I use xmit functionality wrong.
The issue is "tx credits in L2CAP (bluetooth experts know what I mean here)
will be reach to zero at the same time on both nodes. This occurs a deadlock
and nobody will transmit anything anymore".
So far I understand these tx credits are to avoid buffer-bloating. My
workaround to fix that was to allow buffer bloating:
#define L2CAP_LE_MAX_CREDITS 10
changed to (some high number which doesn't reach 2^16):
#define L2CAP_LE_MAX_CREDITS 60000
This will introduce buffer-bloating which I happily see when doing high payload
pings. (I think on high traffic you will reach that issue also with this
workaround, it's just not likely.)
---
HOW TO REPRODUCE:
It's simple, do some high payloads which makes lot tx/rcv traffic on both sides:
Node A:
ping6 $IP_NODEB%6lo0 -s 60000
Node B:
ping6 ff02::1%6lo0
that's one example, but you need to produce some transmit data on both nodes to
see that the nodes stucks in -11 (EAGAIN) of l2cap_chan_send.
Doing that and activate "#define DEBUG" in "bluetooth/6lowpan.c". After some time
Node A print outs (depends on ping6 implementation, iputils in my case):
.... icmp_seq=33 Destination unreachable: Address unreachable
also on Node B, there comes maybe never a responds from Node B anymore. Then it's
likely that you hit the deadlock.
Now run dmesg on both nodes, you will see each transmit (l2cap_chan_send) ends in:
"transmit return value -11"
if this will be printed out via dmesg on both nodes, you hit the deadlock and
nothing will be transmitted anymore. I need bluetooth experts to solve this
issue. :-)
Otherwise I detected no issues yet, also I tried to remove my usb dongle while
high tx/rcv bandwidth at runtime from the qemu vm without crashing the kernel
(very important test for me).
- Alex
Alexander Aring (20):
6lowpan: ndisc: don't remove short address
nhc: add TODO for nhc work
ieee802154: 6lowpan: remove headroom check
ieee802154: 6lowpan: move skb cb BUILD_BUG_ON check
6lowpan: remove LOWPAN_IPHC_MAX_HEADER_LEN
6lowpan: hold netdev while unregister
6lowpan: introduce generic default naming
6lowpan: move rx defines to generic
bluetooth: introduce l2cap_hdev_chan_connect
bluetooth: add hci dev notifier
bluetooth: export functions and variables
6lowpan: bluetooth: remove implementation
ieee802154: 6lowpan: move header create to 6lowpan
6lowpan: move dev_init to generic
6lowpan: iphc: override l2 packet information
ipv6: addrconf: fix 48 bit 6lowpan autoconfiguration
6lowpan: iphc: add handling for btle
6lowpan: move multicast flags to generic
6lowpan: delete addr_len handling to generic
6lowpan: bluetooth: add new implementation
include/net/6lowpan.h | 30 +-
include/net/bluetooth/hci_core.h | 14 +
include/net/bluetooth/l2cap.h | 3 +
net/6lowpan/core.c | 47 +-
net/6lowpan/iphc.c | 111 +++
net/6lowpan/ndisc.c | 2 -
net/6lowpan/nhc.c | 15 +
net/bluetooth/6lowpan.c | 1788 ++++++++++++++----------------------
net/bluetooth/hci_core.c | 26 +
net/bluetooth/hci_sock.c | 2 +
net/bluetooth/l2cap_core.c | 29 +-
net/ieee802154/6lowpan/6lowpan_i.h | 9 -
net/ieee802154/6lowpan/core.c | 23 +-
net/ieee802154/6lowpan/tx.c | 94 +-
net/ipv6/addrconf.c | 19 +-
15 files changed, 1008 insertions(+), 1204 deletions(-)
--
2.9.0
Hi Johan,
>> mhhh, I am not a bluetooth expert. What really means "periodically
>> change is RPA" and how is this done in the Linux bluetooth?
>>
>> When "changing the RPA" happens, does that mean the all connections will
>> be lost (unregister interface). Then the previous connections will be
>> recreated (register interface) with a complete different MAC address?
>
> Connections aren't lost when the random address is changed. That's why
> we track the hci_conn->init_addr and hci_conn->resp_addr. The way this
> is dealt with e.g. the Security Manager protocol is that the connection
> creation address *is* the address for the remote device throughout the
> entire connection, no matter what happens to the local and remote random
> address during the connection. I would expect 6LoWPAN do do something
> similar.
>
> What was still unclear to me (maybe I missed it in the thread): does the
> 6LoWPAN for LE spec require using the connection creation address or the
> identity address? If it would be the latter then that's something that
> will never change and the whole interface recreation issue goes away.
of course we do not want to leak the identity address if we don't have to. That is why I am thinking that the IPv6 link should always use something close to the link layer address.
The basic assumption should be that when hci0 is powered on, then you get a 6lo network interface. If you power the controller down, then the 6lo interface gets removed.
By default, the 6lo interface should not have a single address assigned to it if there are no connections. When a connection is created, then a new address should be assigned to it. If the connection drops, then it should be removed. And of course there needs to be usage counter attached to each address. If two connections use the same address, then it only gets removed once there is no connection with that source address active anymore (meaning it needs some sort of reference counting).
With usage of public address or static random address, then most likely only one address on 6lo interface is used. This is not strictly mandated by the spec since you can in theory have one connection with a public address and the other with the static random address. However ignore that fact for a bit since it will just work out when dealing with RPAs anyway. The importance is the link layer address that is used when establishing the connection.
In case you use RPAs, then every connection will have a new link layer address (we rotate the RPA when disabling advertising). As long as the connection is active, that source address should be represented on 6lo interface. However this is no difference either since per spec each new connection can have a different address from all the existing connections. That is always valid with the link layer.
Regards
Marcel
Hi Johan,
On Wed, Jul 20, 2016 at 10:39 AM, Johan Hedberg <[email protected]> wrote:
> Hi Alex,
>
> On Tue, Jul 19, 2016, Alexander Aring wrote:
>> mhhh, I am not a bluetooth expert. What really means "periodically
>> change is RPA" and how is this done in the Linux bluetooth?
>>
>> When "changing the RPA" happens, does that mean the all connections will
>> be lost (unregister interface). Then the previous connections will be
>> recreated (register interface) with a complete different MAC address?
>
> Connections aren't lost when the random address is changed. That's why
> we track the hci_conn->init_addr and hci_conn->resp_addr. The way this
> is dealt with e.g. the Security Manager protocol is that the connection
> creation address *is* the address for the remote device throughout the
> entire connection, no matter what happens to the local and remote random
> address during the connection. I would expect 6LoWPAN do do something
> similar.
>
> What was still unclear to me (maybe I missed it in the thread): does the
> 6LoWPAN for LE spec require using the connection creation address or the
> identity address? If it would be the latter then that's something that
> will never change and the whole interface recreation issue goes away.
For the MAC address I don't really know, but for the link-local IPv6
address I think the RPA shall be used:
'The IPv6 link-local address configuration described in Section 3.2.2
only reveals information about the 6LN to the 6LBR that the 6LBR
already knows from the link-layer connection. This means that a
device using Bluetooth privacy features reveals the same information
in its IPv6 link-local addresses as in its device addresses.
Respectively, a device not using privacy at the Bluetooth level will
not have privacy at the IPv6 link-local address either.'
--
Luiz Augusto von Dentz
Hi Alex,
On Tue, Jul 19, 2016, Alexander Aring wrote:
> mhhh, I am not a bluetooth expert. What really means "periodically
> change is RPA" and how is this done in the Linux bluetooth?
>
> When "changing the RPA" happens, does that mean the all connections will
> be lost (unregister interface). Then the previous connections will be
> recreated (register interface) with a complete different MAC address?
Connections aren't lost when the random address is changed. That's why
we track the hci_conn->init_addr and hci_conn->resp_addr. The way this
is dealt with e.g. the Security Manager protocol is that the connection
creation address *is* the address for the remote device throughout the
entire connection, no matter what happens to the local and remote random
address during the connection. I would expect 6LoWPAN do do something
similar.
What was still unclear to me (maybe I missed it in the thread): does the
6LoWPAN for LE spec require using the connection creation address or the
identity address? If it would be the latter then that's something that
will never change and the whole interface recreation issue goes away.
Johan
Hi,
On 07/19/2016 10:49 AM, Luiz Augusto von Dentz wrote:
> Hi Alex,
>
> On Tue, Jul 19, 2016 at 12:52 AM, Alexander Aring <[email protected]> wrote:
>>>> If you have the current debugfs UAPI 1:1 already in some kind of stable
>>>> bluetooth API then I would give you the BIG advice to fix that and let
>>>> me look into that. (As far I know there exists idea's only, no
>>>> implementations).
>>>
>>> There is the patches posted by Patrik implementing the management interface:
>>>
>>> http://www.spinics.net/lists/linux-bluetooth/msg66486.html
>>>
>> Okay, just to be sure here, in the above document:
>>
>> Controller ID = hdev(usually var name, struct hci_dev)->id
>>
>> Interface Index = dev(usualy var name, struct net_device)->idx
>>
>> Is this right? I think so, so my next stuff will base on that.
>
> Yep.
>
>>> Note that these commands are per interface index, so you would be able
>>> to have two dongle talking to each other, we could in fact even
>>> automate this to test with 2 virtual controllers.
>>>
>>
>> We have similar automating testing in 802.15.4 6LoWPAN with virtual
>> transceiver drivers.
>>
>> I always looked somehow for some virtual driver in bluetooth, does this
>> exist mainline?
>
> Take a look at the emulator directory:
>
> https://git.kernel.org/cgit/bluetooth/bluez.git/tree/emulator
>
cool.
>>> It is about priorities, we should concentrate in making it work for
>>> simpler cases, besides the RFC don't talk about subnets either.
>>>
>>
>> I don't talking about subnets.
>>
>> I am talking about "make the connect command per interface". Patrik
>> works shows the following:
>>
>> Add Network Command
>> ===================
>>
>> Command Code: 0x0043
>> Controller Index: <controller id>
>> Command Parameters: Address (6 Octets)
>> Address_Type (1 Octet)
>> Return Parameters: Address (6 Octets)
>> Address_Type (1 Octet)
>> Interface Index (4 Octets)
>>
>> This command is used to connect to establish a network connection
>> to a remote BTLE node. A pre-requisite is that LE is supported
>> by the controller, otherwise this command will return a
>> "not supported" response.
>>
>> Possible values for the Address_Type parameter:
>> 0 Reserved
>> 1 LE Public
>> 2 LE Random
>>
>> This command generates a Command Complete event on success
>> or failure.
>>
>> Possible errors: Busy
>> Refused
>> Invalid Parameters
>> Not Supported
>> Invalid Index
>> Already Connected
>>
>> So far I understand, this is "make the connect command per device", the
>> device in this case is not a "struct net_device" it's "struct hci_dev".
>>
>> In the "possible" future case to having multiple interfaces you cannot
>> say on which interface this connect should be done. With "connect" I
>> mean the l2cap_chan_connect call.
>
> And why would we want more than one one net_dev per hci_dev, the
> rfc7668 only talks about star topology:
>
> https://tools.ietf.org/html/rfc7668#section-2.2
>
> Bluetooth currently don't have support for a mesh topology, and anyway
> I don't think the userspace would need to be involved in the selection
> of the netdev since this is beyond the Bluetooth interface. In PAN for
> example we end up doing one netdev per connection and the which the
> userspace managing using a bridge.
>
I am sure that there will be some use-case with namespaces some day.
In general:
In my opinion, having hci_dev as input parameter and as output the
interface will do something hidden behind which you can't control.
The Output return value "interface" will indicate on which interface
the operation was operated on. For me it's something behind which you
cannot control as user. E.g. When having multiple interfaces. On 1:1
mapping, I agree this should work.
But why not simple drop the interface idx from the Output and move it to
Input?
Command Code: 0x0043
Controller Index: <controller id>
Command Parameters: Address (6 Octets)
Address_Type (1 Octet)
Interface Index (4 Octets)
Return Parameters: Address (6 Octets)
Address_Type (1 Octet)
If we get some way that the 6LoWPAN interface can be created before and
not when doing the first "connect command" then the "Controller Index"
would be unnecessary because you would give that information when
creating an interface.
>> btw:
>> Also I don't know what's now the parameters "Address/Address_Type"
>> source/destination? Is the Input of them equal to the output?
>
> That is always the destination address, and we needs its type to know
> if it is public or private to act accordingly.
>
okay.
- Alex
Hi,
On 07/19/2016 10:23 AM, Luiz Augusto von Dentz wrote:
> Hi Alex, Johan,
>
> On Tue, Jul 19, 2016 at 8:45 AM, Johan Hedberg <[email protected]> wrote:
>> Hi Alex,
>>
...
>
> Following the recommendation of rfc7668 which say the node should
> always change its address when connecting, which means advertise a
> different address, also the router should periodically change is RPA,
> thus the network interface may have to assume different MAC addresses
> which I don't is currently possible or we are back to having the
> interfaces created on demand.
>
mhhh, I am not a bluetooth expert. What really means "periodically
change is RPA" and how is this done in the Linux bluetooth?
When "changing the RPA" happens, does that mean the all connections will
be lost (unregister interface). Then the previous connections will be
recreated (register interface) with a complete different MAC address?
I think it will be a complete different MAC address and this happens all
15 minutes.
"The recommended time interval before randomizing new private address is 15
minutes, as determined by timer T_GAP(private_addr_int) in Table 17.1 of
the Bluetooth Generic Access Profile [BTCorev4.1]."
If so, then I could understand the reason why recreation of interface.
The only question would for me, how does the bluetooth subsystem handle
such MAC address update with running l2cap channel connections. I
suppose it will close all connections, otherwise the current mainline
solution doesn't work here, because if all channels are disconnected we
get an interface deregister, so far I understood. Afterwards somehow all
connection will be re-established under a new interface, or?
---
Another idea would be stop the interface which allows to change mac
address. Maybe we can have some event handling here, that the mac
address will be changed. If so, such event occur change dev->dev_addr
and open the interface again. stop (ifdown) and open (ifup) with changing
the mac address should be handled by IPv6 applications.
We need at least to run [0] on mac address changes, this reacts on IFUP
and CHANGE netdev notifiers. This will generate the SLAAC address, but
it confuse me somehow to change this address all 15 minutes because the
MAC address changed. :-/
Deregister and register the interface is a no-go in my opinion and we
should have another solution. Especially when such deregister/register
mechanism happens periodically all 15 minutes.
---
I suppose the "RPA" will be the device address which also need to be
filled in as source/target address option in NS/NA/RS/RA then. It's just
difficult to deal with that when the address will be changed
periodically in 15 minutes.
btw: I talked with C. Gomez today, because I am at the IETF. He would
like to help here to make everything clear and agreed that I cc him here.
- Alex
[0] http://lxr.free-electrons.com/source/net/ipv6/addrconf.c#L3333
Hi,
On 07/19/2016 04:51 PM, Michael Richardson wrote:
> Jukka Rissanen <[email protected]> wrote:
> > According to packet(7), the "Packet sockets are used to receive or send
> > raw packets at the device driver (OSI Layer 2) level."
> > But lowpan0 interface is meant to compress IPv6 header so it is working
> > on L3 data (or more precisely between L2 and L3).
> > So I am wondering how this is suppose to work and what kind of use case
> > you have for this?
>
> First, one might want to send 802.15.4 packets with custom Information
> Elements. So you ask why we would want to do this through the lowpan
> interface... well, because lowpan will perhaps grow support for the 6tisch
> (802.15.4e) mode of operation, and the custom IEs need to be scheduled out
> with the other packets. In particular, you don't want to jump the 6lowpan
> queue of fragments.
>
I currently think this discussion goes into the wrong direction.
Here exists an issue that transmit AF_PACKET RAW will not work,
DGRAM (tx/rx) sockets has also no sense.
The root of this issue is the "header_create" callback and what's made
for. This callback is used to tell the subsystem "Please add a mac
header here and as parameter you have L2 source and destination address".
This callback will be used by AF_PACKET _DGRAM_ and IPv6 ndisc cache.
Both of them has some point "please add a mac header, here are source
and destination L2 addresses".
So we don't put any mac header there, our use-case is "IPv6 ndisc cache"
only. We need the destination address from the ndisc and this callback
is the super generic way to tel us that by IPv6 stack and I would use
this callback as well, because it seems to be the normal IPv6 way.
I already told that we don't put a mac header there, what we do is
adding into a hidden skb space (skb_headroom) the address information
(important here the L2 daddr). This hidden skb space will be evaluated
inside the xmit callback where we finally replace the IPv6 header with
6LoWPAN and put the mac header in there.
So why we don't replace the header in header_create? This has several
issues:
- We need to have a unshared skb buf and we cannot unshare in this callback.
- We need to have the IPv6 view in AF_PACKET RAW receive side and this
will be caputered between header_create and xmit.
Sending on AF_PACKET RAW sockets will occur that "create header"
callback will not be called and xmit callback directly. The xmit callback will
evaluate the "hidden skb room" (skb_headroom), we don't called "create_header"
before so there is garbage inside this skb_headroom.
btw:
What I mentioned before, this handling is weird but it works for IPv6.
We assume here that nobody runs skb_put/skb_push between "header_create"
and "xmit".
- Alex
Jukka Rissanen <[email protected]> wrote:
> According to packet(7), the "Packet sockets are used to receive or send
> raw packets at the device driver (OSI Layer 2) level."
> But lowpan0 interface is meant to compress IPv6 header so it is working
> on L3 data (or more precisely between L2 and L3).
> So I am wondering how this is suppose to work and what kind of use case
> you have for this?
First, one might want to send 802.15.4 packets with custom Information
Elements. So you ask why we would want to do this through the lowpan
interface... well, because lowpan will perhaps grow support for the 6tisch
(802.15.4e) mode of operation, and the custom IEs need to be scheduled out
with the other packets. In particular, you don't want to jump the 6lowpan
queue of fragments.
--
] Never tell me the odds! | ipv6 mesh networks [
] Michael Richardson, Sandelman Software Works | network architect [
] [email protected] http://www.sandelman.ca/ | ruby on rails [
Luiz Augusto von Dentz <[email protected]> wrote:
> And why would we want more than one one net_dev per hci_dev, the
> rfc7668 only talks about star topology:
> https://tools.ietf.org/html/rfc7668#section-2.2
> Bluetooth currently don't have support for a mesh topology, and anyway
> I don't think the userspace would need to be involved in the selection
> of the netdev since this is beyond the Bluetooth interface. In PAN for
> example we end up doing one netdev per connection and the which the
> userspace managing using a bridge.
BTLE 4.2 moves quite far to supporting a mesh, but doesn't yet get there.
I'm told that it will come. BTLE 4.2 does support switching to which
star one belongs quickly enough that one could actually route, but it's
probably not practical yet.
--
] Never tell me the odds! | ipv6 mesh networks [
] Michael Richardson, Sandelman Software Works | network architect [
] [email protected] http://www.sandelman.ca/ | ruby on rails [
Alexander Aring <[email protected]> wrote:
> The issue is "tx credits in L2CAP (bluetooth experts know what I mean
> here) will be reach to zero at the same time on both nodes. This occurs
> a deadlock
> and nobody will transmit anything anymore".
What puts more credits in the bucket?
Wouldn't it be a successful TX event?
--
] Never tell me the odds! | ipv6 mesh networks [
] Michael Richardson, Sandelman Software Works | network architect [
] [email protected] http://www.sandelman.ca/ | ruby on rails [
Hi Alex,
On Tue, Jul 19, 2016 at 12:52 AM, Alexander Aring <[email protected]> wrote:
>>> If you have the current debugfs UAPI 1:1 already in some kind of stable
>>> bluetooth API then I would give you the BIG advice to fix that and let
>>> me look into that. (As far I know there exists idea's only, no
>>> implementations).
>>
>> There is the patches posted by Patrik implementing the management interface:
>>
>> http://www.spinics.net/lists/linux-bluetooth/msg66486.html
>>
> Okay, just to be sure here, in the above document:
>
> Controller ID = hdev(usually var name, struct hci_dev)->id
>
> Interface Index = dev(usualy var name, struct net_device)->idx
>
> Is this right? I think so, so my next stuff will base on that.
Yep.
>> Note that these commands are per interface index, so you would be able
>> to have two dongle talking to each other, we could in fact even
>> automate this to test with 2 virtual controllers.
>>
>
> We have similar automating testing in 802.15.4 6LoWPAN with virtual
> transceiver drivers.
>
> I always looked somehow for some virtual driver in bluetooth, does this
> exist mainline?
Take a look at the emulator directory:
https://git.kernel.org/cgit/bluetooth/bluez.git/tree/emulator
>> It is about priorities, we should concentrate in making it work for
>> simpler cases, besides the RFC don't talk about subnets either.
>>
>
> I don't talking about subnets.
>
> I am talking about "make the connect command per interface". Patrik
> works shows the following:
>
> Add Network Command
> ===================
>
> Command Code: 0x0043
> Controller Index: <controller id>
> Command Parameters: Address (6 Octets)
> Address_Type (1 Octet)
> Return Parameters: Address (6 Octets)
> Address_Type (1 Octet)
> Interface Index (4 Octets)
>
> This command is used to connect to establish a network connection
> to a remote BTLE node. A pre-requisite is that LE is supported
> by the controller, otherwise this command will return a
> "not supported" response.
>
> Possible values for the Address_Type parameter:
> 0 Reserved
> 1 LE Public
> 2 LE Random
>
> This command generates a Command Complete event on success
> or failure.
>
> Possible errors: Busy
> Refused
> Invalid Parameters
> Not Supported
> Invalid Index
> Already Connected
>
> So far I understand, this is "make the connect command per device", the
> device in this case is not a "struct net_device" it's "struct hci_dev".
>
> In the "possible" future case to having multiple interfaces you cannot
> say on which interface this connect should be done. With "connect" I
> mean the l2cap_chan_connect call.
And why would we want more than one one net_dev per hci_dev, the
rfc7668 only talks about star topology:
https://tools.ietf.org/html/rfc7668#section-2.2
Bluetooth currently don't have support for a mesh topology, and anyway
I don't think the userspace would need to be involved in the selection
of the netdev since this is beyond the Bluetooth interface. In PAN for
example we end up doing one netdev per connection and the which the
userspace managing using a bridge.
> btw:
> Also I don't know what's now the parameters "Address/Address_Type"
> source/destination? Is the Input of them equal to the output?
That is always the destination address, and we needs its type to know
if it is public or private to act accordingly.
>>
>>> I observed, what I explained above. There exists two "connect" mechanism,
>>> these are L2 and L3.
>>>
>>> With L2 I mean $FIND_OUT_RIGHT_NOT_DEPRECATED_TOOL_IN_BT_UTILITY_DSCHUNGEL
>>> leadv/lecc stuff. This prepares somehow that you can say
>>> l2cap_chan_connect in the upper-layer to connect to slaves.
>>>
>>> With l2cap_chan_connect I mean L3 connection, which is the same like
>>> what you doing in L2 for L3. It's also not be a MUST, because you can
>>> run e.g. above example Node A, B, C, D on L2 and:
>>>
>>> BTLE 6LoWPAN: A <-> B
>>>
>>> L2CAP_SOCK: C <-> D
>>>
>>> or some other mixed scenario. I think you want to still have such behaviour.
>>> This would be the same like: A, B, C, D and 6lo0 is connected to A, B.
>>> Forget the C, D, make with them whatever you want (_maybe_ namespace with
>>> C, D or l2cap_sock).
>>
>> Im not sure where you are going with l2cap_sock here, yes we can make
>> it connect to different peers but usually each peer assumes a role in
>> BLE, the central role scans and connect and the peripheral only
>> advertise. If you do have more than one controller it should be fine
>> to have multiple network interfaces and then perhaps you can bridge
>> them together, or not, but this should be controlled via userspace,
>> the kernel shall only manage the network interface.
>>
>
> Yes, but the kernel needs an UAPI so the user can control it over userspace.
> See above my "make connect per interface" suggestion.
This is API is for Bluetooth only, it shall not be mixed with network
interface management.
Hi Alex, Johan,
On Tue, Jul 19, 2016 at 8:45 AM, Johan Hedberg <[email protected]> wrote:
> Hi Alex,
>
> On Mon, Jul 18, 2016, Alexander Aring wrote:
>> > This is tricky because the even using privacy the address of the
>> > interface is defined by the time the connection is established, but
>> > perhaps there is a way to set this properly on the network interface,
>> > anyway I do agree the making the interface persistent is probably much
>> > more convenient.
>> >
>>
>> This is currently something which I don't understand. With "privacy" you
>> mean the LE_PUBLIC and LE_PRIVATE address, the address type?
>
> Might be worth to do a quick read-through of section 1.3 in Vol 6, Part
> B of the Bluetooth Core specification (v4.2) for a quick overview of LE
> addresses. On the top-level you have public & random, but random itself
> is further split into three different sub-types out of which two are
> considered "private". The third one, a static random address, is
> considered an identity address the same way a public address is.
The so called privacy, the use of RPA, is actually recommended by RFC 7668:
https://tools.ietf.org/html/rfc7668#section-3.2.2
>> I think, we don't need that when we doing an interface register. This is
>> currently one of the big issue which this RFC fixes, the 8 byte vs 6
>> bytes address.
>>
>> At Interface register we need to know the source address only, which is
>> my opinion the hdev->bdaddr. This address has (in my opinion) nothing
>> todo with the address type.
>>
>> I can argument here with rfc7668:
>>
>> "This means that no bit in the IID indicates whether the
>> underlying Bluetooth device address is public or random."
See above the rfc7668 actually recommends using RPAs.
>> The IID is for autoconfiguration the last 8 bytes of IPv6 address and
>> usually generated by "dev->dev_addr" (dev = struct net_device).
>> Also the "dev->dev_addr" will be used in address options for
>> NS/NA/RS/RA...
>>
>> In the current implementation the IID generation is mixed with the
>> "dev->addr" (dev as struct net_device) field which is wrong.
>>
>> In summary:
>>
>> We don't need to know to wait for connection to setting the
>> "dev->dev_addr" (struct net_device). This field should be "hdev->bdaddr"
>> and hdev should be the hdev which is bounded to the dev(lowpan struct
>> net_device).
>>
>> I see no issues to create interfaces before, except you saying that
>> "hdev->bdaddr" is unknown.
>
> hdev->bdaddr tracks the public address of a device, however the
> Bluetooth specification doesn't mandate that all devices have a public
> address, i.e. it can also be all zeroes. In particular, single-mode
> (LE-only) devices may only use a random address and have a static random
> address as their identity address. Maybe it's the identity address that
> you should be using instead of hdev->bdaddr?
>
> Right now the identity address isn't stored in any variable, but we do
> have a hci_copy_identity_address() function to fetch it (you'd just
> ignore the addr_type if you don't need that).
>
> Also note that the identity address isn't necessarily the same as what
> was used to create the connection, so if the connection address is of
> importance we track that in hci_conn->init_addr and hci_conn->resp_addr.
Following the recommendation of rfc7668 which say the node should
always change its address when connecting, which means advertise a
different address, also the router should periodically change is RPA,
thus the network interface may have to assume different MAC addresses
which I don't is currently possible or we are back to having the
interfaces created on demand.
--
Luiz Augusto von Dentz
Hi Alex,
On Mon, Jul 18, 2016, Alexander Aring wrote:
> > This is tricky because the even using privacy the address of the
> > interface is defined by the time the connection is established, but
> > perhaps there is a way to set this properly on the network interface,
> > anyway I do agree the making the interface persistent is probably much
> > more convenient.
> >
>
> This is currently something which I don't understand. With "privacy" you
> mean the LE_PUBLIC and LE_PRIVATE address, the address type?
Might be worth to do a quick read-through of section 1.3 in Vol 6, Part
B of the Bluetooth Core specification (v4.2) for a quick overview of LE
addresses. On the top-level you have public & random, but random itself
is further split into three different sub-types out of which two are
considered "private". The third one, a static random address, is
considered an identity address the same way a public address is.
> I think, we don't need that when we doing an interface register. This is
> currently one of the big issue which this RFC fixes, the 8 byte vs 6
> bytes address.
>
> At Interface register we need to know the source address only, which is
> my opinion the hdev->bdaddr. This address has (in my opinion) nothing
> todo with the address type.
>
> I can argument here with rfc7668:
>
> "This means that no bit in the IID indicates whether the
> underlying Bluetooth device address is public or random."
>
> The IID is for autoconfiguration the last 8 bytes of IPv6 address and
> usually generated by "dev->dev_addr" (dev = struct net_device).
> Also the "dev->dev_addr" will be used in address options for
> NS/NA/RS/RA...
>
> In the current implementation the IID generation is mixed with the
> "dev->addr" (dev as struct net_device) field which is wrong.
>
> In summary:
>
> We don't need to know to wait for connection to setting the
> "dev->dev_addr" (struct net_device). This field should be "hdev->bdaddr"
> and hdev should be the hdev which is bounded to the dev(lowpan struct
> net_device).
>
> I see no issues to create interfaces before, except you saying that
> "hdev->bdaddr" is unknown.
hdev->bdaddr tracks the public address of a device, however the
Bluetooth specification doesn't mandate that all devices have a public
address, i.e. it can also be all zeroes. In particular, single-mode
(LE-only) devices may only use a random address and have a static random
address as their identity address. Maybe it's the identity address that
you should be using instead of hdev->bdaddr?
Right now the identity address isn't stored in any variable, but we do
have a hci_copy_identity_address() function to fetch it (you'd just
ignore the addr_type if you don't need that).
Also note that the identity address isn't necessarily the same as what
was used to create the connection, so if the connection address is of
importance we track that in hci_conn->init_addr and hci_conn->resp_addr.
Johan
Hi,
On 07/18/2016 10:59 AM, Luiz Augusto von Dentz wrote:
> Hi Alex,
>
> On Sun, Jul 17, 2016 at 6:52 PM, Alexander Aring <[email protected]> wrote:
>>
>> Hi,
>>
>> On 07/14/2016 01:40 PM, Luiz Augusto von Dentz wrote:
>>> Hi Alex,
>>>
>>> On Wed, Jul 13, 2016 at 12:19 AM, Alexander Aring <[email protected]> wrote:
>>>>
>>>> Hi,
>>>>
>>>> On 07/11/2016 09:50 PM, Alexander Aring wrote:
>>>>> This patch adds a new btle 6lowpan implementation in version 0.2. This
>>>>> new implementation has a new userspace API for debugfs.
>>>>>
>>>>> It's now possible to decide on which hci interface do you want to run
>>>>> your 6LoWPAN interface, for that you can run:
>>>>>
>>>>> echo "hci0 1" > /sys/kernel/debug/bluetooth/6lowpan_enable
>>>>>
>>>>> to enable, or:
>>>>>
>>>>> echo "hci0 0" > /sys/kernel/debug/bluetooth/6lowpan_enable
>>>
>>> No please no more designs for debugfs, we are past that and moving
>>> towards having proper management commands, so what you did here is a
>>> huge waste of time and you have instead just ask before hand.
>>>
>>
>> The first time when I used BTLE 6LoWPAN I complaint about that I can not
>> say "use this special hci%d for lowpan interface xy", I did that not on
>> the mailinglist it was on the irc channel and everybody agreed that it
>> need to be fixed. The basic idea on my side was to run only one Virtual
>> Machine with two BTLE usb dongles to communicate to each other, so I
>> needed to switch to two virtual machines to workaround this issue.
>>
>> If you have the current debugfs UAPI 1:1 already in some kind of stable
>> bluetooth API then I would give you the BIG advice to fix that and let
>> me look into that. (As far I know there exists idea's only, no
>> implementations).
>
> There is the patches posted by Patrik implementing the management interface:
>
> http://www.spinics.net/lists/linux-bluetooth/msg66486.html
>
Okay, just to be sure here, in the above document:
Controller ID = hdev(usually var name, struct hci_dev)->id
Interface Index = dev(usualy var name, struct net_device)->idx
Is this right? I think so, so my next stuff will base on that.
> Note that these commands are per interface index, so you would be able
> to have two dongle talking to each other, we could in fact even
> automate this to test with 2 virtual controllers.
>
We have similar automating testing in 802.15.4 6LoWPAN with virtual
transceiver drivers.
I always looked somehow for some virtual driver in bluetooth, does this
exist mainline?
>> Another really BIG thing is (it comes to the direction "breaking UAPI"
>> as well) that the interface depends on if Slaves are connected. So if
>> the last Slave will be disconnected then the interface will be deregistered.
>> Every IPv6 userspace application cannot deal with that. The interface
>> lifetime must not depend on if slaves connected or not.
>
> This is tricky because the even using privacy the address of the
> interface is defined by the time the connection is established, but
> perhaps there is a way to set this properly on the network interface,
> anyway I do agree the making the interface persistent is probably much
> more convenient.
>
This is currently something which I don't understand. With "privacy" you
mean the LE_PUBLIC and LE_PRIVATE address, the address type?
I think, we don't need that when we doing an interface register. This is
currently one of the big issue which this RFC fixes, the 8 byte vs 6
bytes address.
At Interface register we need to know the source address only, which is
my opinion the hdev->bdaddr. This address has (in my opinion) nothing
todo with the address type.
I can argument here with rfc7668:
"This means that no bit in the IID indicates whether the
underlying Bluetooth device address is public or random."
The IID is for autoconfiguration the last 8 bytes of IPv6 address and
usually generated by "dev->dev_addr" (dev = struct net_device).
Also the "dev->dev_addr" will be used in address options for
NS/NA/RS/RA...
In the current implementation the IID generation is mixed with the
"dev->addr" (dev as struct net_device) field which is wrong.
In summary:
We don't need to know to wait for connection to setting the
"dev->dev_addr" (struct net_device). This field should be "hdev->bdaddr"
and hdev should be the hdev which is bounded to the dev(lowpan struct
net_device).
I see no issues to create interfaces before, except you saying that
"hdev->bdaddr" is unknown.
>> At this case, I think every netdev people would agree.
>>
>>>>> to disable it. A cat on this file will show the current hci <-> 6lowpan
>>>>> interface mapping.
>>>>>
>>>>> Additional the different is, that the interface will be created after
>>>>> enable it. The old behaviour is that the interface will be created when
>>>>> one first connection is established and deleted after all connections
>>>>> (peers) will be disconnected. This handling is different now.
>>>>>
>>>>> The reason (in my opinion) is that the existing of such interface should
>>>>> not based on if there is a connection or not. At runtime of many IPv6
>>>>> application an interface will be removed -> I think the most userspace
>>>>> application can and will never handle that an IPv6 interface will be
>>>>> deleted and recreating during runtime.
>>>>>
>>>>> The new handling is that the interface will not removed and you can
>>>>> re-establish the connection to your peers.
>>>>>
>>>>> Connection to peers:
>>>>>
>>>>> Connection to peers are not binded to hci interface, it's binded to your
>>>>> 6LoWPAN interface. This will prepare handling for multiple 6LoWPAN
>>>>> interfaces on one hci (maybe also with netns support, which isn't
>>>>> available yet) and you can choose which peers should be handled by
>>>>> different BTLE 6LoWPAN interfaces. Example:
>>>>>
>>>>> L2: hci0 is connected to node A, B, C, D
>>>>>
>>>>> L3: 6lo0 and 6lo1 are two interfaces based on hci0.
>>>>> 6lo0 is connected to nodes: A, B, C
>>>>> 6lo1 is connected to nodes: D, C, B
>>>
>>> Is this is useful for what exactly? I much rather have a simple module
>>> that just deal with 1 connection than have to deal with complex
>>> topology in the beginning, so this is moving in the exact opposite
>>> direction.
>>>
>>
>> Well, I learned now to not talking about crazy use-cases which you could
>> do with that. I need more to talk about why this is really needed. I
>> will do that in future.
>
> It is about priorities, we should concentrate in making it work for
> simpler cases, besides the RFC don't talk about subnets either.
>
I don't talking about subnets.
I am talking about "make the connect command per interface". Patrik
works shows the following:
Add Network Command
===================
Command Code: 0x0043
Controller Index: <controller id>
Command Parameters: Address (6 Octets)
Address_Type (1 Octet)
Return Parameters: Address (6 Octets)
Address_Type (1 Octet)
Interface Index (4 Octets)
This command is used to connect to establish a network connection
to a remote BTLE node. A pre-requisite is that LE is supported
by the controller, otherwise this command will return a
"not supported" response.
Possible values for the Address_Type parameter:
0 Reserved
1 LE Public
2 LE Random
This command generates a Command Complete event on success
or failure.
Possible errors: Busy
Refused
Invalid Parameters
Not Supported
Invalid Index
Already Connected
So far I understand, this is "make the connect command per device", the
device in this case is not a "struct net_device" it's "struct hci_dev".
In the "possible" future case to having multiple interfaces you cannot
say on which interface this connect should be done. With "connect" I
mean the l2cap_chan_connect call.
btw:
Also I don't know what's now the parameters "Address/Address_Type"
source/destination? Is the Input of them equal to the output?
>
>> I observed, what I explained above. There exists two "connect" mechanism,
>> these are L2 and L3.
>>
>> With L2 I mean $FIND_OUT_RIGHT_NOT_DEPRECATED_TOOL_IN_BT_UTILITY_DSCHUNGEL
>> leadv/lecc stuff. This prepares somehow that you can say
>> l2cap_chan_connect in the upper-layer to connect to slaves.
>>
>> With l2cap_chan_connect I mean L3 connection, which is the same like
>> what you doing in L2 for L3. It's also not be a MUST, because you can
>> run e.g. above example Node A, B, C, D on L2 and:
>>
>> BTLE 6LoWPAN: A <-> B
>>
>> L2CAP_SOCK: C <-> D
>>
>> or some other mixed scenario. I think you want to still have such behaviour.
>> This would be the same like: A, B, C, D and 6lo0 is connected to A, B.
>> Forget the C, D, make with them whatever you want (_maybe_ namespace with
>> C, D or l2cap_sock).
>
> Im not sure where you are going with l2cap_sock here, yes we can make
> it connect to different peers but usually each peer assumes a role in
> BLE, the central role scans and connect and the peripheral only
> advertise. If you do have more than one controller it should be fine
> to have multiple network interfaces and then perhaps you can bridge
> them together, or not, but this should be controlled via userspace,
> the kernel shall only manage the network interface.
>
Yes, but the kernel needs an UAPI so the user can control it over userspace.
See above my "make connect per interface" suggestion.
>> At least if you want to have netns support with mutliple lowpan then
>> there will nothing to complex, it's just the same interface handling
>> like having one interface in init_net namespace. Just you can deal with
>> different namespaces with that (UAPI need to support namespaces for that).
>>
>> It makes completely more sense to say to "lowpan interface 6lo%d connect
>> to slave xy".
>>
>> The current way is that the user has no control of it and
>> l2cap_chan_connect call this "hci_get_route" function behind and with
>> bluetooth magic the user get one hci interface which best-fits and maybe
>> not that what you want.
>
> See http://www.spinics.net/lists/linux-bluetooth/msg66495.html, it
> takes the index of the interface upfront so you can specify which
> adapter to use so it shouldn't rely on hci_get_route anymore.
>
See above, make the "connect per hci device" will make it not clear if
having multiple interfaces on one hci dev.
>>>>> To connect to peers, there exists no "6lowpan_control" anymore. The
>>>>> control file is now peer interface, it's simple:
>>>>>
>>>>> echo "connect $PEER_ADDRESS 1" > /sys/kernel/debug/bluetooth/6lo0
>>>>>
>>>>> Also after connecting to peers it's still possible that another peer can
>>>>> to connect to the peer which has already a connected peer (complicated
>>>>> to describe). I saw that is somehow disabled in version before, see:
>>>>>
>>>>> http://lxr.free-electrons.com/source/net/bluetooth/6lowpan.c#L1247
>>>>>
>>>>> I am not a bluetooth expert, but I guess that is somehow if there exists
>>>>> limitation to devices which are not "peers" that means devices which can
>>>>> be run as master or slave only. So far I know such limitations for
>>>>> devices doesn't exists for BTLE 6LoWPAN, or?
>>>>>
>>>
>>> I guess that is because dual role is not specified, you either are a
>>> node or a router but not both at the same time.
>>>
>>
>> Okay, then I will close the "listen" l2cap channel after somebody did a
>> connect. But I bet there was some graphic somewhere which had a
>>
>> SLAVE.... <---- MASTER <----> MASTER ----> SLAVE...
>>
>> So with that we forbid such MASTER <-> MASTER connections.
>
> This master to master connection doesn't make much sense, usually a
> central cannot connect to another central since none advertise
> anything, but indeed we can have more than one slave/none connected to
> a master/router. Note we can in fact switch between peripheral and
> central roles, so in theory it is still possible to have subnets, but
> it is just very unlikely.
>
I rechecked that handling and I think closing that "listen" channel will
not forbid such MASTER <-> MASTER stuff. I previous mentioned that I
found in the current mainline implementation that the listen channel
will be closed somehow when running a "connect", I still get no the
reason why. I thought it's something that you cannot "accept other
connection" when running a "connect" command once.
But if I had the "listen" channel before running and already accept
connection and running "connect" afterwards, then I still have the
channels open which was accepted by the "listen" channel.
In short:
I need to know somehow what's possible. When running connect and when
doing the "listen" stuff for accepting incoming connection. We talking
here about l2cap layer.
Can it not be it's such simple that l2cap_chan_connect/etc. functionality
will drop an error if it's simple not possible? Then we let the
subsystem decide if such connections are possible or not. Depends on
what means the RFC and if we need to make more than the bluetooth
subsystem can handle.
Then it would be the question to map these errors to UAPI.
I am really confused about the closing "listen" stuff channel. :-/
>>>> I think the following will explain somehow when it's a SLAVE and when
>>>> MASTER as my current view.
>>>>
>>>> SLAVES:
>>>>
>>>> They don't makes this echo "connect ..." foo in control files.
>>>> they only do "hciconfig hci0 leadv" that somebody other can connect to
>>>> that node (some MASTER node).
>>>>
>>>> MASTERS:
>>>>
>>>> can do the same what the SLAVES does but to be as MASTER they need to call
>>>> additional stuff:
>>>>
>>>> hcitool lecc $SLAVE_ADDR (one or more times)
>>>
>>> Don't use hcitool, actually never mention any of hci related
>>> interfaces on the patches there are deprecated, btmgmt shall be used
>>> to enable things like advertising, I will be working on adding
>>> advertising support for bluetoothctl but we actually need IPSP UUID
>>> support in order to make this work properly so not even btmgmt will be
>>> enough in the end.
>>>
>>
>> Yes, Marcel already told me some tools are deprecated and I should not
>> use them. You don't want to know how long I took when I had some working
>> setup with my broadcom dongles. :-)
>>
>> I am too lazy to switch, I will use now btmgmt. I hope it can do the
>> same suff up/down (I think power on/off) and leadv and lecc.
>
> Yes, but if we keep using those people will never learn there deprecated.
>
I didn't know it either, is there some wiki webpage which says about
which tool is deprecated and which isn't?
>>
>>>> Additional for different lowpan interfaces (don't need the same addresses
>>>> which was used by previous one or more hcitool lecc calls):
>>>>
>>>> echo "connect SLAVE_ADDR 1" > /sys/kernel/debug/bluetooth/6lo0
>>>>
>>>> is this a correct view and can all BTLE transceivers run as SLAVE or
>>>> MASTER, or exists there some special transceivers outside which can be
>>>> run as SLAVE only?
>>>>
>>>>> Nevertheless I disabled that stuff, it's still possible to connect to
>>>>> some node which already run "connect ..." on his side.
>>>>>
>>>>> Otherwise I fixed the ndisc stuff and removed a lot of races/side
>>>>> effects. The old code had some static "lowpan_devices" list and most
>>>>> time there was some lookup mapping according to bdaddr to get the right
>>>>> 6lo netdevice struct. I removed that, also the skb->cb is used a lot in
>>>>> some callback which had some side-effects.
>>>
>>> Send the fixes, and the fixes only.
>>>
>>
>> There exists fixes only, no new features. In my opinion, UAPI is broken
>> because you can't control what you want. Again, because "hci_get_route" magic.
>> I added no new features, I fixed the broken things. Maybe there are some
>> little small cleanups if you say changing function namens is not a
>> bugfix it's a cleanup.
>
> See the RFC sent by Patrik, the MGMT commands use interface index so
> hci_get_route should not be needed.
>
See above, I would suggest to make some commands per interface and not
per hci_dev.
>> Removing the whole implementation and adding a new one is the only way
>> which I would think it is possible, because we need to fix stuff in
>> IPHC and as well in IPv6 as well.
>
> I guess these are separate fixes aren't they? Or are they depending on
> this patch?
>
yea, some of them. I will make a clearer version of the patch series and
send some patches mainline which I can do before.
The necessary stuff for btle 6lowpan begins at:
- Remove bluetooth implementation
and ends at:
- Add new bluetooth implementation
Patches between of them will changes something in different Layers, this
is IPHC and IPv6. This is mostly to change the stuff from 8 bytes
address to 6 byte address. If I would do it before, I would break the
btle 6lowpan stuff before. Besides that there are many races etc in btle
6lowpan so I just do a remove, prepare handling for 6 byte address and
add a new implementation. I didn't hit a race yet but I need to test
more with some unlikely testcases. The only issue which I get while
using 6lowpan is the "tx_credits" deadlock, which we need to talk about
more.
The current mainline implementation is very unstable on my side, means:
my kernel will crash.
>> I know the zephyr people wants to be backwardscompatible I could say, I
>> add a big BROKEN_IMPLEMENTATION Kconfig switch which let's you compile
>> the old broken version which is still compatible with stacks outside.
>
> No, in fact we need these fixes in order to work properly with zephyr.
>
Did you test this patch series with zephyr, or somebody else?
>> btw: stacks outside I see currently pull-request in RIOT-OS which do the
>> same L2 address length is 8 instead 6, which means they are currently
>> bug compatible with Linux.
>
> Sounds like a bug there, Bluetooth shall use MAC addresses of 6 bytes
> long the conversion for 8 bytes (64 bits) is just for the link local
> to form the so called IID (https://tools.ietf.org/html/rfc7668) I
> don't think it should be transmitted with 0xff, 0xfe encoded in the
> address.
>
This bug I mentioned earlier, see:
http://www.spinics.net/lists/linux-wpan/msg03899.html
Besides that another big bug is that BTLE ignores the neighbour
discovery cache. Only addresses which are generated by
autoconfiguration. Normally the ndisc cache has L3 -> L2 address
mapping (but includes more than just that). The mainline work stuff do
currently to reconstruct the L2 address from L3 address, which only for
autoconfiguration addresses.
In short:
If you set an own ipv6 address which is not based on mac address it will
not work. Also some other IPv6 mechanism in neighbour discovery will not
work.
This patch series will fix that and use the neighbour discovery.
btw:
I report that bug 3-4 months ago in irc channel.
IMPORTANT NOTE:
This means not that we using ARO DAC/DAR stuff from rfc6775.
---
At last I would like suggest some ideas for the UAPI handling, which is
some oriented (for interface generation) what we have in 802.15.4:
1. Command to add an 6lowpan interface according to hci dev
(You can add multiple interfaces then if calling more than once)
2. Command to del an 6lowpan interface
3. Command to connect to $PEER (or $SLAVE, whatever the right term is
here), but this command is per 6lowpan interface. Not hci_dev.
4. Command to disconnect to $PEER, per 6lowpan interface as well.
Maybe I we should make the UAPI more clear and I implement it for now as
debugfs. But you think it's waste of time, or isn't it?
I also detected that debugfs doesn't like to remove debugfs while
debugfs handling. So the second command is complicated to implement. (I
did some workqueue workaround for that which I mentioned many times
inside the comments that it is a workaround).
btw:
Some kernel hackers would also say thet doing the xmit workqueue is a
workaround because we need to use "async API". I don't saw any xmit
async functionality at bluetooth subsystem and I also need to hold the
l2cap chan->lock when I call l2cap_chan_send, so this is a mutex and I
need to run a workqueue. I try to deal with that in the current
implementation, so far it works... but running a workqueue to call
l2cap_chan_send which runs again a workqueue is bad.
- Alex
Hi Alex,
On Sun, Jul 17, 2016 at 6:52 PM, Alexander Aring <[email protected]> wrote:
>
> Hi,
>
> On 07/14/2016 01:40 PM, Luiz Augusto von Dentz wrote:
>> Hi Alex,
>>
>> On Wed, Jul 13, 2016 at 12:19 AM, Alexander Aring <[email protected]> wrote:
>>>
>>> Hi,
>>>
>>> On 07/11/2016 09:50 PM, Alexander Aring wrote:
>>>> This patch adds a new btle 6lowpan implementation in version 0.2. This
>>>> new implementation has a new userspace API for debugfs.
>>>>
>>>> It's now possible to decide on which hci interface do you want to run
>>>> your 6LoWPAN interface, for that you can run:
>>>>
>>>> echo "hci0 1" > /sys/kernel/debug/bluetooth/6lowpan_enable
>>>>
>>>> to enable, or:
>>>>
>>>> echo "hci0 0" > /sys/kernel/debug/bluetooth/6lowpan_enable
>>
>> No please no more designs for debugfs, we are past that and moving
>> towards having proper management commands, so what you did here is a
>> huge waste of time and you have instead just ask before hand.
>>
>
> The first time when I used BTLE 6LoWPAN I complaint about that I can not
> say "use this special hci%d for lowpan interface xy", I did that not on
> the mailinglist it was on the irc channel and everybody agreed that it
> need to be fixed. The basic idea on my side was to run only one Virtual
> Machine with two BTLE usb dongles to communicate to each other, so I
> needed to switch to two virtual machines to workaround this issue.
>
> If you have the current debugfs UAPI 1:1 already in some kind of stable
> bluetooth API then I would give you the BIG advice to fix that and let
> me look into that. (As far I know there exists idea's only, no
> implementations).
There is the patches posted by Patrik implementing the management interface:
http://www.spinics.net/lists/linux-bluetooth/msg66486.html
Note that these commands are per interface index, so you would be able
to have two dongle talking to each other, we could in fact even
automate this to test with 2 virtual controllers.
> Another really BIG thing is (it comes to the direction "breaking UAPI"
> as well) that the interface depends on if Slaves are connected. So if
> the last Slave will be disconnected then the interface will be deregistered.
> Every IPv6 userspace application cannot deal with that. The interface
> lifetime must not depend on if slaves connected or not.
This is tricky because the even using privacy the address of the
interface is defined by the time the connection is established, but
perhaps there is a way to set this properly on the network interface,
anyway I do agree the making the interface persistent is probably much
more convenient.
> At this case, I think every netdev people would agree.
>
>>>> to disable it. A cat on this file will show the current hci <-> 6lowpan
>>>> interface mapping.
>>>>
>>>> Additional the different is, that the interface will be created after
>>>> enable it. The old behaviour is that the interface will be created when
>>>> one first connection is established and deleted after all connections
>>>> (peers) will be disconnected. This handling is different now.
>>>>
>>>> The reason (in my opinion) is that the existing of such interface should
>>>> not based on if there is a connection or not. At runtime of many IPv6
>>>> application an interface will be removed -> I think the most userspace
>>>> application can and will never handle that an IPv6 interface will be
>>>> deleted and recreating during runtime.
>>>>
>>>> The new handling is that the interface will not removed and you can
>>>> re-establish the connection to your peers.
>>>>
>>>> Connection to peers:
>>>>
>>>> Connection to peers are not binded to hci interface, it's binded to your
>>>> 6LoWPAN interface. This will prepare handling for multiple 6LoWPAN
>>>> interfaces on one hci (maybe also with netns support, which isn't
>>>> available yet) and you can choose which peers should be handled by
>>>> different BTLE 6LoWPAN interfaces. Example:
>>>>
>>>> L2: hci0 is connected to node A, B, C, D
>>>>
>>>> L3: 6lo0 and 6lo1 are two interfaces based on hci0.
>>>> 6lo0 is connected to nodes: A, B, C
>>>> 6lo1 is connected to nodes: D, C, B
>>
>> Is this is useful for what exactly? I much rather have a simple module
>> that just deal with 1 connection than have to deal with complex
>> topology in the beginning, so this is moving in the exact opposite
>> direction.
>>
>
> Well, I learned now to not talking about crazy use-cases which you could
> do with that. I need more to talk about why this is really needed. I
> will do that in future.
It is about priorities, we should concentrate in making it work for
simpler cases, besides the RFC don't talk about subnets either.
> I observed, what I explained above. There exists two "connect" mechanism,
> these are L2 and L3.
>
> With L2 I mean $FIND_OUT_RIGHT_NOT_DEPRECATED_TOOL_IN_BT_UTILITY_DSCHUNGEL
> leadv/lecc stuff. This prepares somehow that you can say
> l2cap_chan_connect in the upper-layer to connect to slaves.
>
> With l2cap_chan_connect I mean L3 connection, which is the same like
> what you doing in L2 for L3. It's also not be a MUST, because you can
> run e.g. above example Node A, B, C, D on L2 and:
>
> BTLE 6LoWPAN: A <-> B
>
> L2CAP_SOCK: C <-> D
>
> or some other mixed scenario. I think you want to still have such behaviour.
> This would be the same like: A, B, C, D and 6lo0 is connected to A, B.
> Forget the C, D, make with them whatever you want (_maybe_ namespace with
> C, D or l2cap_sock).
Im not sure where you are going with l2cap_sock here, yes we can make
it connect to different peers but usually each peer assumes a role in
BLE, the central role scans and connect and the peripheral only
advertise. If you do have more than one controller it should be fine
to have multiple network interfaces and then perhaps you can bridge
them together, or not, but this should be controlled via userspace,
the kernel shall only manage the network interface.
> At least if you want to have netns support with mutliple lowpan then
> there will nothing to complex, it's just the same interface handling
> like having one interface in init_net namespace. Just you can deal with
> different namespaces with that (UAPI need to support namespaces for that).
>
> It makes completely more sense to say to "lowpan interface 6lo%d connect
> to slave xy".
>
> The current way is that the user has no control of it and
> l2cap_chan_connect call this "hci_get_route" function behind and with
> bluetooth magic the user get one hci interface which best-fits and maybe
> not that what you want.
See http://www.spinics.net/lists/linux-bluetooth/msg66495.html, it
takes the index of the interface upfront so you can specify which
adapter to use so it shouldn't rely on hci_get_route anymore.
>>>> To connect to peers, there exists no "6lowpan_control" anymore. The
>>>> control file is now peer interface, it's simple:
>>>>
>>>> echo "connect $PEER_ADDRESS 1" > /sys/kernel/debug/bluetooth/6lo0
>>>>
>>>> Also after connecting to peers it's still possible that another peer can
>>>> to connect to the peer which has already a connected peer (complicated
>>>> to describe). I saw that is somehow disabled in version before, see:
>>>>
>>>> http://lxr.free-electrons.com/source/net/bluetooth/6lowpan.c#L1247
>>>>
>>>> I am not a bluetooth expert, but I guess that is somehow if there exists
>>>> limitation to devices which are not "peers" that means devices which can
>>>> be run as master or slave only. So far I know such limitations for
>>>> devices doesn't exists for BTLE 6LoWPAN, or?
>>>>
>>
>> I guess that is because dual role is not specified, you either are a
>> node or a router but not both at the same time.
>>
>
> Okay, then I will close the "listen" l2cap channel after somebody did a
> connect. But I bet there was some graphic somewhere which had a
>
> SLAVE.... <---- MASTER <----> MASTER ----> SLAVE...
>
> So with that we forbid such MASTER <-> MASTER connections.
This master to master connection doesn't make much sense, usually a
central cannot connect to another central since none advertise
anything, but indeed we can have more than one slave/none connected to
a master/router. Note we can in fact switch between peripheral and
central roles, so in theory it is still possible to have subnets, but
it is just very unlikely.
>>> I think the following will explain somehow when it's a SLAVE and when
>>> MASTER as my current view.
>>>
>>> SLAVES:
>>>
>>> They don't makes this echo "connect ..." foo in control files.
>>> they only do "hciconfig hci0 leadv" that somebody other can connect to
>>> that node (some MASTER node).
>>>
>>> MASTERS:
>>>
>>> can do the same what the SLAVES does but to be as MASTER they need to call
>>> additional stuff:
>>>
>>> hcitool lecc $SLAVE_ADDR (one or more times)
>>
>> Don't use hcitool, actually never mention any of hci related
>> interfaces on the patches there are deprecated, btmgmt shall be used
>> to enable things like advertising, I will be working on adding
>> advertising support for bluetoothctl but we actually need IPSP UUID
>> support in order to make this work properly so not even btmgmt will be
>> enough in the end.
>>
>
> Yes, Marcel already told me some tools are deprecated and I should not
> use them. You don't want to know how long I took when I had some working
> setup with my broadcom dongles. :-)
>
> I am too lazy to switch, I will use now btmgmt. I hope it can do the
> same suff up/down (I think power on/off) and leadv and lecc.
Yes, but if we keep using those people will never learn there deprecated.
>
>>> Additional for different lowpan interfaces (don't need the same addresses
>>> which was used by previous one or more hcitool lecc calls):
>>>
>>> echo "connect SLAVE_ADDR 1" > /sys/kernel/debug/bluetooth/6lo0
>>>
>>> is this a correct view and can all BTLE transceivers run as SLAVE or
>>> MASTER, or exists there some special transceivers outside which can be
>>> run as SLAVE only?
>>>
>>>> Nevertheless I disabled that stuff, it's still possible to connect to
>>>> some node which already run "connect ..." on his side.
>>>>
>>>> Otherwise I fixed the ndisc stuff and removed a lot of races/side
>>>> effects. The old code had some static "lowpan_devices" list and most
>>>> time there was some lookup mapping according to bdaddr to get the right
>>>> 6lo netdevice struct. I removed that, also the skb->cb is used a lot in
>>>> some callback which had some side-effects.
>>
>> Send the fixes, and the fixes only.
>>
>
> There exists fixes only, no new features. In my opinion, UAPI is broken
> because you can't control what you want. Again, because "hci_get_route" magic.
> I added no new features, I fixed the broken things. Maybe there are some
> little small cleanups if you say changing function namens is not a
> bugfix it's a cleanup.
See the RFC sent by Patrik, the MGMT commands use interface index so
hci_get_route should not be needed.
> Removing the whole implementation and adding a new one is the only way
> which I would think it is possible, because we need to fix stuff in
> IPHC and as well in IPv6 as well.
I guess these are separate fixes aren't they? Or are they depending on
this patch?
> I know the zephyr people wants to be backwardscompatible I could say, I
> add a big BROKEN_IMPLEMENTATION Kconfig switch which let's you compile
> the old broken version which is still compatible with stacks outside.
No, in fact we need these fixes in order to work properly with zephyr.
> btw: stacks outside I see currently pull-request in RIOT-OS which do the
> same L2 address length is 8 instead 6, which means they are currently
> bug compatible with Linux.
Sounds like a bug there, Bluetooth shall use MAC addresses of 6 bytes
long the conversion for 8 bytes (64 bits) is just for the link local
to form the so called IID (https://tools.ietf.org/html/rfc7668) I
don't think it should be transmitted with 0xff, 0xfe encoded in the
address.
--
Luiz Augusto von Dentz
Hi,
On 07/14/2016 01:40 PM, Luiz Augusto von Dentz wrote:
> Hi Alex,
>
> On Wed, Jul 13, 2016 at 12:19 AM, Alexander Aring <[email protected]> wrote:
>>
>> Hi,
>>
>> On 07/11/2016 09:50 PM, Alexander Aring wrote:
>>> This patch adds a new btle 6lowpan implementation in version 0.2. This
>>> new implementation has a new userspace API for debugfs.
>>>
>>> It's now possible to decide on which hci interface do you want to run
>>> your 6LoWPAN interface, for that you can run:
>>>
>>> echo "hci0 1" > /sys/kernel/debug/bluetooth/6lowpan_enable
>>>
>>> to enable, or:
>>>
>>> echo "hci0 0" > /sys/kernel/debug/bluetooth/6lowpan_enable
>
> No please no more designs for debugfs, we are past that and moving
> towards having proper management commands, so what you did here is a
> huge waste of time and you have instead just ask before hand.
>
The first time when I used BTLE 6LoWPAN I complaint about that I can not
say "use this special hci%d for lowpan interface xy", I did that not on
the mailinglist it was on the irc channel and everybody agreed that it
need to be fixed. The basic idea on my side was to run only one Virtual
Machine with two BTLE usb dongles to communicate to each other, so I
needed to switch to two virtual machines to workaround this issue.
If you have the current debugfs UAPI 1:1 already in some kind of stable
bluetooth API then I would give you the BIG advice to fix that and let
me look into that. (As far I know there exists idea's only, no
implementations).
Another really BIG thing is (it comes to the direction "breaking UAPI"
as well) that the interface depends on if Slaves are connected. So if
the last Slave will be disconnected then the interface will be deregistered.
Every IPv6 userspace application cannot deal with that. The interface
lifetime must not depend on if slaves connected or not.
At this case, I think every netdev people would agree.
>>> to disable it. A cat on this file will show the current hci <-> 6lowpan
>>> interface mapping.
>>>
>>> Additional the different is, that the interface will be created after
>>> enable it. The old behaviour is that the interface will be created when
>>> one first connection is established and deleted after all connections
>>> (peers) will be disconnected. This handling is different now.
>>>
>>> The reason (in my opinion) is that the existing of such interface should
>>> not based on if there is a connection or not. At runtime of many IPv6
>>> application an interface will be removed -> I think the most userspace
>>> application can and will never handle that an IPv6 interface will be
>>> deleted and recreating during runtime.
>>>
>>> The new handling is that the interface will not removed and you can
>>> re-establish the connection to your peers.
>>>
>>> Connection to peers:
>>>
>>> Connection to peers are not binded to hci interface, it's binded to your
>>> 6LoWPAN interface. This will prepare handling for multiple 6LoWPAN
>>> interfaces on one hci (maybe also with netns support, which isn't
>>> available yet) and you can choose which peers should be handled by
>>> different BTLE 6LoWPAN interfaces. Example:
>>>
>>> L2: hci0 is connected to node A, B, C, D
>>>
>>> L3: 6lo0 and 6lo1 are two interfaces based on hci0.
>>> 6lo0 is connected to nodes: A, B, C
>>> 6lo1 is connected to nodes: D, C, B
>
> Is this is useful for what exactly? I much rather have a simple module
> that just deal with 1 connection than have to deal with complex
> topology in the beginning, so this is moving in the exact opposite
> direction.
>
Well, I learned now to not talking about crazy use-cases which you could
do with that. I need more to talk about why this is really needed. I
will do that in future.
I observed, what I explained above. There exists two "connect" mechanism,
these are L2 and L3.
With L2 I mean $FIND_OUT_RIGHT_NOT_DEPRECATED_TOOL_IN_BT_UTILITY_DSCHUNGEL
leadv/lecc stuff. This prepares somehow that you can say
l2cap_chan_connect in the upper-layer to connect to slaves.
With l2cap_chan_connect I mean L3 connection, which is the same like
what you doing in L2 for L3. It's also not be a MUST, because you can
run e.g. above example Node A, B, C, D on L2 and:
BTLE 6LoWPAN: A <-> B
L2CAP_SOCK: C <-> D
or some other mixed scenario. I think you want to still have such behaviour.
This would be the same like: A, B, C, D and 6lo0 is connected to A, B.
Forget the C, D, make with them whatever you want (_maybe_ namespace with
C, D or l2cap_sock).
At least if you want to have netns support with mutliple lowpan then
there will nothing to complex, it's just the same interface handling
like having one interface in init_net namespace. Just you can deal with
different namespaces with that (UAPI need to support namespaces for that).
It makes completely more sense to say to "lowpan interface 6lo%d connect
to slave xy".
The current way is that the user has no control of it and
l2cap_chan_connect call this "hci_get_route" function behind and with
bluetooth magic the user get one hci interface which best-fits and maybe
not that what you want.
>>> as one possible example. Handle this peer interface will have a possible
>>> more fine-granularity connection for peers.
>
> Not useful when we most likely we will not have that many peers, this
> is no Wifi or ethernet, this is for very embedded devices lets not
> over engineer here please.
>
Okay, I will not talk about any crazy use-cases which I could have. But
such setup is currently also possible but you can't control it because
bluetooth "hci_get_route" magic. On L2 you can connect to a number of Nodes
in some set. The funny thing is on L3 the "connect" command you cannot
say which interface connects now to which slave. If you have two lowpan
interfaces it will be decided by the magic "hci_get_route" function.
At last the question is not here to make such use-case the question is
here to make the "connect" command per interface. To doing that it will
not increase the number of "connect" commands which you need to use
anyway. What we remove is the bluetooth magic "hci_get_route", so now
you can "please connect from 6lo0 interface to $SLAVE, and this
interface is bounded to hci0", as exmaple.
>>> To connect to peers, there exists no "6lowpan_control" anymore. The
>>> control file is now peer interface, it's simple:
>>>
>>> echo "connect $PEER_ADDRESS 1" > /sys/kernel/debug/bluetooth/6lo0
>>>
>>> Also after connecting to peers it's still possible that another peer can
>>> to connect to the peer which has already a connected peer (complicated
>>> to describe). I saw that is somehow disabled in version before, see:
>>>
>>> http://lxr.free-electrons.com/source/net/bluetooth/6lowpan.c#L1247
>>>
>>> I am not a bluetooth expert, but I guess that is somehow if there exists
>>> limitation to devices which are not "peers" that means devices which can
>>> be run as master or slave only. So far I know such limitations for
>>> devices doesn't exists for BTLE 6LoWPAN, or?
>>>
>
> I guess that is because dual role is not specified, you either are a
> node or a router but not both at the same time.
>
Okay, then I will close the "listen" l2cap channel after somebody did a
connect. But I bet there was some graphic somewhere which had a
SLAVE.... <---- MASTER <----> MASTER ----> SLAVE...
So with that we forbid such MASTER <-> MASTER connections.
>> I think the following will explain somehow when it's a SLAVE and when
>> MASTER as my current view.
>>
>> SLAVES:
>>
>> They don't makes this echo "connect ..." foo in control files.
>> they only do "hciconfig hci0 leadv" that somebody other can connect to
>> that node (some MASTER node).
>>
>> MASTERS:
>>
>> can do the same what the SLAVES does but to be as MASTER they need to call
>> additional stuff:
>>
>> hcitool lecc $SLAVE_ADDR (one or more times)
>
> Don't use hcitool, actually never mention any of hci related
> interfaces on the patches there are deprecated, btmgmt shall be used
> to enable things like advertising, I will be working on adding
> advertising support for bluetoothctl but we actually need IPSP UUID
> support in order to make this work properly so not even btmgmt will be
> enough in the end.
>
Yes, Marcel already told me some tools are deprecated and I should not
use them. You don't want to know how long I took when I had some working
setup with my broadcom dongles. :-)
I am too lazy to switch, I will use now btmgmt. I hope it can do the
same suff up/down (I think power on/off) and leadv and lecc.
Thanks.
>> Additional for different lowpan interfaces (don't need the same addresses
>> which was used by previous one or more hcitool lecc calls):
>>
>> echo "connect SLAVE_ADDR 1" > /sys/kernel/debug/bluetooth/6lo0
>>
>> is this a correct view and can all BTLE transceivers run as SLAVE or
>> MASTER, or exists there some special transceivers outside which can be
>> run as SLAVE only?
>>
>>> Nevertheless I disabled that stuff, it's still possible to connect to
>>> some node which already run "connect ..." on his side.
>>>
>>> Otherwise I fixed the ndisc stuff and removed a lot of races/side
>>> effects. The old code had some static "lowpan_devices" list and most
>>> time there was some lookup mapping according to bdaddr to get the right
>>> 6lo netdevice struct. I removed that, also the skb->cb is used a lot in
>>> some callback which had some side-effects.
>
> Send the fixes, and the fixes only.
>
There exists fixes only, no new features. In my opinion, UAPI is broken
because you can't control what you want. Again, because "hci_get_route" magic.
I added no new features, I fixed the broken things. Maybe there are some
little small cleanups if you say changing function namens is not a
bugfix it's a cleanup.
Removing the whole implementation and adding a new one is the only way
which I would think it is possible, because we need to fix stuff in
IPHC and as well in IPv6 as well.
I know the zephyr people wants to be backwardscompatible I could say, I
add a big BROKEN_IMPLEMENTATION Kconfig switch which let's you compile
the old broken version which is still compatible with stacks outside.
btw: stacks outside I see currently pull-request in RIOT-OS which do the
same L2 address length is 8 instead 6, which means they are currently
bug compatible with Linux.
- Alex
Hi Alex,
On Wed, Jul 13, 2016 at 1:56 PM, Alexander Aring <[email protected]> wrote:
Just use a pastbin url to past the logs, that is probably not many
people interested in that.
>>> ---
>>>
>>> They simple show the same stuff, but with different addresses.
>>>
>>> I rethink about your words and I suppose you told me that tx_credits and
>>> -EAGAIN are normal that this stuff occurs.
>>>
>>> I agree, It also occurs before when I run:
>>>
>>> ping6 $IP_NODEB%6lo0 -s 60000
>>>
>>> which is normal. But what should not happen is that there is some little
>>> tiny window (race) that both tx_credits are reached to zero and then
>>> nobody transmit anything anymore, that's the tx deadlock which I hit here.
>>>
>>> And this deadlock occurs that I killed the L2CAP chan connection. I
>>> think it will work when I create a new chan (because new tx_credits).
>>> The connection isn't broken, I just can not transmit anything because tx_credits.
>>
>> If there is no calls to channel resume then yes there is a problem
>> that no tx_credits would be able to be sent, note though that the
>> command the restore credits does happen in the signalling channel not
>> in the data channel so there should be anything blocking from
>
> So these are complete separate transmission? Could it be that, because I
> do much things on the air that such "tx_credits command" stuff could not
> be reached to the other side? Then the complete mechanism can not be
> working and maybe need some recovery if no "tx_credits command reached"
> some time ago to solve this deadlock?
Well the only information about the credits I have is the rx, which is
in fact not 0 as assumed before:
rx_credits 8 -> 7
So we didn't even have to restore anything so most likely it is just
the tx_credits that goes to 0 which means there is probably a problem
with the other box.
> I am not a bluetooth expert, maybe there exists nicer solutions. :-)
>
>> receiving those, perhaps looking into what is happening the last time
>> it receives credits and the sequence of calls it generate.
>>
>
> See above, I hope that helps.
How about l2cap_le_credits, or is that not called even once? Btw, use
btmon -w <file> and just attach the file next time.
--
Luiz Augusto von Dentz
Hi Alex,
On Wed, Jul 13, 2016 at 12:19 AM, Alexander Aring <[email protected]> wrote:
>
> Hi,
>
> On 07/11/2016 09:50 PM, Alexander Aring wrote:
>> This patch adds a new btle 6lowpan implementation in version 0.2. This
>> new implementation has a new userspace API for debugfs.
>>
>> It's now possible to decide on which hci interface do you want to run
>> your 6LoWPAN interface, for that you can run:
>>
>> echo "hci0 1" > /sys/kernel/debug/bluetooth/6lowpan_enable
>>
>> to enable, or:
>>
>> echo "hci0 0" > /sys/kernel/debug/bluetooth/6lowpan_enable
No please no more designs for debugfs, we are past that and moving
towards having proper management commands, so what you did here is a
huge waste of time and you have instead just ask before hand.
>> to disable it. A cat on this file will show the current hci <-> 6lowpan
>> interface mapping.
>>
>> Additional the different is, that the interface will be created after
>> enable it. The old behaviour is that the interface will be created when
>> one first connection is established and deleted after all connections
>> (peers) will be disconnected. This handling is different now.
>>
>> The reason (in my opinion) is that the existing of such interface should
>> not based on if there is a connection or not. At runtime of many IPv6
>> application an interface will be removed -> I think the most userspace
>> application can and will never handle that an IPv6 interface will be
>> deleted and recreating during runtime.
>>
>> The new handling is that the interface will not removed and you can
>> re-establish the connection to your peers.
>>
>> Connection to peers:
>>
>> Connection to peers are not binded to hci interface, it's binded to your
>> 6LoWPAN interface. This will prepare handling for multiple 6LoWPAN
>> interfaces on one hci (maybe also with netns support, which isn't
>> available yet) and you can choose which peers should be handled by
>> different BTLE 6LoWPAN interfaces. Example:
>>
>> L2: hci0 is connected to node A, B, C, D
>>
>> L3: 6lo0 and 6lo1 are two interfaces based on hci0.
>> 6lo0 is connected to nodes: A, B, C
>> 6lo1 is connected to nodes: D, C, B
Is this is useful for what exactly? I much rather have a simple module
that just deal with 1 connection than have to deal with complex
topology in the beginning, so this is moving in the exact opposite
direction.
>> as one possible example. Handle this peer interface will have a possible
>> more fine-granularity connection for peers.
Not useful when we most likely we will not have that many peers, this
is no Wifi or ethernet, this is for very embedded devices lets not
over engineer here please.
>> To connect to peers, there exists no "6lowpan_control" anymore. The
>> control file is now peer interface, it's simple:
>>
>> echo "connect $PEER_ADDRESS 1" > /sys/kernel/debug/bluetooth/6lo0
>>
>> Also after connecting to peers it's still possible that another peer can
>> to connect to the peer which has already a connected peer (complicated
>> to describe). I saw that is somehow disabled in version before, see:
>>
>> http://lxr.free-electrons.com/source/net/bluetooth/6lowpan.c#L1247
>>
>> I am not a bluetooth expert, but I guess that is somehow if there exists
>> limitation to devices which are not "peers" that means devices which can
>> be run as master or slave only. So far I know such limitations for
>> devices doesn't exists for BTLE 6LoWPAN, or?
>>
I guess that is because dual role is not specified, you either are a
node or a router but not both at the same time.
> I think the following will explain somehow when it's a SLAVE and when
> MASTER as my current view.
>
> SLAVES:
>
> They don't makes this echo "connect ..." foo in control files.
> they only do "hciconfig hci0 leadv" that somebody other can connect to
> that node (some MASTER node).
>
> MASTERS:
>
> can do the same what the SLAVES does but to be as MASTER they need to call
> additional stuff:
>
> hcitool lecc $SLAVE_ADDR (one or more times)
Don't use hcitool, actually never mention any of hci related
interfaces on the patches there are deprecated, btmgmt shall be used
to enable things like advertising, I will be working on adding
advertising support for bluetoothctl but we actually need IPSP UUID
support in order to make this work properly so not even btmgmt will be
enough in the end.
> Additional for different lowpan interfaces (don't need the same addresses
> which was used by previous one or more hcitool lecc calls):
>
> echo "connect SLAVE_ADDR 1" > /sys/kernel/debug/bluetooth/6lo0
>
> is this a correct view and can all BTLE transceivers run as SLAVE or
> MASTER, or exists there some special transceivers outside which can be
> run as SLAVE only?
>
>> Nevertheless I disabled that stuff, it's still possible to connect to
>> some node which already run "connect ..." on his side.
>>
>> Otherwise I fixed the ndisc stuff and removed a lot of races/side
>> effects. The old code had some static "lowpan_devices" list and most
>> time there was some lookup mapping according to bdaddr to get the right
>> 6lo netdevice struct. I removed that, also the skb->cb is used a lot in
>> some callback which had some side-effects.
Send the fixes, and the fixes only.
>> Signed-off-by: Alexander Aring <[email protected]>
>> ---
>> include/net/bluetooth/hci_core.h | 10 +
>> net/bluetooth/6lowpan.c | 1015 ++++++++++++++++++++++++++++++++++++++
>> net/bluetooth/Makefile | 3 +
>> 3 files changed, 1028 insertions(+)
>> create mode 100644 net/bluetooth/6lowpan.c
>>
>> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
>> index a37027a..37a2256 100644
>> --- a/include/net/bluetooth/hci_core.h
>> +++ b/include/net/bluetooth/hci_core.h
>> @@ -399,6 +399,16 @@ struct hci_dev {
>>
>> struct led_trigger *power_led;
>>
>> +#if IS_ENABLED(CONFIG_BT_6LOWPAN)
>> + /* TODO for netns support this need to be a list
>> + * variables are protected by RTNL here
>> + */
>> + struct net_device *ldev;
>> + bool lowpan_enable;
>> + /* delete lowpan iface via debugfs workaround */
>> + bool pending_deletion;
>> +#endif
>> +
>> int (*open)(struct hci_dev *hdev);
>> int (*close)(struct hci_dev *hdev);
>> int (*flush)(struct hci_dev *hdev);
>> diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
>> new file mode 100644
>> index 0000000..a8a3410
>> --- /dev/null
>> +++ b/net/bluetooth/6lowpan.c
>> @@ -0,0 +1,1015 @@
>> +/*
>> + (C) 2016 Pengutronix, Alexander Aring <[email protected]>
>> + Copyright (c) 2013-2014 Intel Corp.
>> +
>> + This program is free software; you can redistribute it and/or modify
>> + it under the terms of the GNU General Public License version 2 and
>> + only version 2 as published by the Free Software Foundation.
>> +
>> + This program is distributed in the hope that it will be useful,
>> + but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + GNU General Public License for more details.
>> +*/
>> +
>> +#include <linux/if_arp.h>
>> +#include <linux/netdevice.h>
>> +#include <linux/etherdevice.h>
>> +#include <linux/module.h>
>> +#include <linux/debugfs.h>
>> +
>> +#include <net/ipv6.h>
>> +#include <net/ip6_route.h>
>> +#include <net/addrconf.h>
>> +
>> +#include <net/bluetooth/bluetooth.h>
>> +#include <net/bluetooth/hci_core.h>
>> +#include <net/bluetooth/l2cap.h>
>> +
>> +#include <net/6lowpan.h>
>> +
>> +#define LOWPAN_BTLE_VERSION "0.2"
>> +
>> +#define lowpan_le48_to_be48(dst, src) baswap((bdaddr_t *)dst, (bdaddr_t *)src)
>> +#define lowpan_be48_to_le48(dst, src) baswap((bdaddr_t *)dst, (bdaddr_t *)src)
>> +
>> +struct lowpan_btle_cb {
>> + struct l2cap_chan *chan;
>> +};
>> +
>> +struct lowpan_btle_dev {
>> + struct hci_dev *hdev;
>> + struct workqueue_struct *workqueue;
>> + struct l2cap_chan *listen;
>> + struct list_head peers;
>> + struct dentry *control;
>> +};
>> +
>> +struct lowpan_peer {
>> + struct l2cap_chan *chan;
>> +
>> + struct list_head list;
>> +};
>> +
>> +struct lowpan_chan_data {
>> + struct lowpan_peer peer;
>> + struct net_device *dev;
>> +};
>> +
>> +struct lowpan_xmit_work {
>> + struct work_struct work;
>> + struct l2cap_chan *chan;
>> + struct net_device *dev;
>> + unsigned int true_len;
>> + struct sk_buff *skb;
>> +};
>> +
>> +static inline struct lowpan_btle_cb *
>> +lowpan_btle_cb(const struct sk_buff *skb)
>> +{
>> + return (struct lowpan_btle_cb *)skb->cb;
>> +}
>> +
>> +static inline struct lowpan_chan_data *
>> +lowpan_chan_data(const struct l2cap_chan *chan)
>> +{
>> + return (struct lowpan_chan_data *)chan->data;
>> +}
>> +
>> +static inline struct lowpan_btle_dev *
>> +lowpan_btle_dev(const struct net_device *dev)
>> +{
>> + return (struct lowpan_btle_dev *)lowpan_dev(dev)->priv;
>> +}
>> +
>> +static inline struct lowpan_peer *
>> +lowpan_lookup_peer(struct lowpan_btle_dev *btdev, bdaddr_t *addr)
>> +{
>> + struct lowpan_peer *entry;
>> +
>> + list_for_each_entry_rcu(entry, &btdev->peers, list) {
>> + if (!bacmp(&entry->chan->dst, addr))
>> + return entry;
>> + }
>> +
>> + return NULL;
>> +}
>> +
>> +static int lowpan_give_skb_to_device(struct sk_buff *skb)
>> +{
>> + skb->protocol = htons(ETH_P_IPV6);
>> + skb->dev->stats.rx_packets++;
>> + skb->dev->stats.rx_bytes += skb->len;
>> +
>> + return netif_rx_ni(skb);
>> +}
>> +
>> +static int lowpan_rx_handlers_result(struct sk_buff *skb, lowpan_rx_result res)
>> +{
>> + switch (res) {
>> + case RX_CONTINUE:
>> + /* nobody cared about this packet */
>> + net_warn_ratelimited("%s: received unknown dispatch\n",
>> + __func__);
>
> that printout isn't corrected, for simplification I drop all non iphc
> frames at recv callback.
>
> I want to have this receive handling into "net/6lowpan" but I want to
> not doing this now in this patch-series. It will prepare it.
>
> After moving it to "net/6lowpan" unknown dispatches should be really
> dispatches which are not known in the current state of implementation.
>
> I think it's okay to leave this right there as it is, also somebody can
> trigger this warning by sending lot of frames with the right dispatch,
> not sure if that's normal that somebody from outside can trigger a lot
> of system messages which are show up in dmesg. Need to think about it,
> maybe some "ONCE" mechanism or using debug stuff.
>
>> +
>> + /* fall-through */
>> + case RX_DROP_UNUSABLE:
>> + kfree_skb(skb);
>> +
>> + /* fall-through */
>> + case RX_DROP:
>> + return NET_RX_DROP;
>> + case RX_QUEUED:
>> + return lowpan_give_skb_to_device(skb);
>> + default:
>> + break;
>> + }
>> +
>> + return NET_RX_DROP;
>> +}
>> +
>> +static lowpan_rx_result lowpan_rx_h_iphc(struct sk_buff *skb)
>> +{
>> + struct l2cap_chan *chan = lowpan_btle_cb(skb)->chan;
>> + bdaddr_t daddr, saddr;
>> + int ret;
>> +
>> + if (!lowpan_is_iphc(*skb_network_header(skb)))
>> + return RX_CONTINUE;
>> +
>> + BT_DBG("recv %s dst: %pMR type %d src: %pMR chan %p",
>> + skb->dev->name, &chan->dst, chan->dst_type, &chan->src, chan);
>> +
>> + /* bluetooth chan view is vice-versa */
>> + bacpy(&daddr, &chan->src);
>> + bacpy(&saddr, &chan->dst);
>> +
>> + ret = lowpan_header_decompress(skb, skb->dev, &daddr, &saddr);
>> + if (ret < 0)
>> + return RX_DROP_UNUSABLE;
>> +
>> + return RX_QUEUED;
>> +}
>> +
>> +static int lowpan_invoke_rx_handlers(struct sk_buff *skb)
>> +{
>> + lowpan_rx_result res;
>> +
>> +#define CALL_RXH(rxh) \
>> + do { \
>> + res = rxh(skb); \
>> + if (res != RX_CONTINUE) \
>> + goto rxh_next; \
>> + } while (0)
>> +
>> + /* likely at first */
>> + CALL_RXH(lowpan_rx_h_iphc);
>> +
>> +rxh_next:
>> + return lowpan_rx_handlers_result(skb, res);
>> +#undef CALL_RXH
>> +}
>> +
>> +static int lowpan_chan_recv(struct l2cap_chan *chan, struct sk_buff *skb)
>> +{
>> + struct lowpan_chan_data *data = lowpan_chan_data(chan);
>> + struct net_device *dev = data->dev;
>> + int ret;
>> +
>> + /* TODO handle BT_CONNECTED in bluetooth subsytem? on
>> + * when calling recv callback, I hit that case somehow
>> + */
>> + if (!netif_running(dev) || chan->state != BT_CONNECTED ||
>> + !skb->len || !lowpan_is_iphc(skb->data[0]))
>> + goto drop;
>> +
>> + /* Replacing skb->dev and followed rx handlers will manipulate skb. */
>> + skb = skb_unshare(skb, GFP_ATOMIC);
>
> can be GFP_KERNEL here.
>
>> + if (!skb)
>> + goto out;
>> +
>> + skb->dev = dev;
>> + skb_reset_network_header(skb);
>> +
>> + /* remember that one for dst bdaddr. TODO handle that as priv data for
>> + * lowpan_invoke_rx_handlers parameter. Not necessary for skb->cb.
>> + */
>
> After fixing that, skb->cb isn't needed anymore in this implementation.
> But I will do that when moving to receive handling to "net/6lowpan".
>
>> + lowpan_btle_cb(skb)->chan = chan;
>> +
>> + ret = lowpan_invoke_rx_handlers(skb);
>> + if (ret == NET_RX_DROP)
>> + BT_DBG("recv %s dropped chan %p", skb->dev->name, chan);
>> +
>> + return 0;
>> +
>> +drop:
>> + kfree_skb(skb);
>> +out:
>> + /* we handle to free skb on error, so must 0
>> + * seems that callback free the skb on error
>> + * otherwise.
>> + */
>
> btw:
>
> This is also a MUST to return 0 here always. Because netif_rx_ni will handle
> also freeing on error and we cannot change this handling. (Or we doing
> some crazy reference counting before foo, but will more confuse
> everybody).
>
>> + return 0;
>> +}
>> +
>> +static void lowpan_xmit_worker(struct work_struct *work)
>> +{
>> + struct lowpan_btle_dev *btdev;
>> + struct lowpan_xmit_work *xw;
>> + struct l2cap_chan *chan;
>> + struct net_device *dev;
>> + struct msghdr msg = { };
>> + struct kvec iv;
>> + int ret;
>> +
>> + xw = container_of(work, struct lowpan_xmit_work, work);
>> + dev = xw->dev;
>> + chan = xw->chan;
>> + btdev = lowpan_btle_dev(dev);
>> +
>> + iv.iov_base = xw->skb->data;
>> + iv.iov_len = xw->skb->len;
>> + iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, &iv, 1, xw->skb->len);
>> +
>> + BT_DBG("l2cap_chan_send %s dst: %pMR type %d src: %pMR chan %p",
>> + dev->name, &chan->dst, chan->dst_type, &chan->src, chan);
>> +
>> + l2cap_chan_lock(chan);
>> +
>> + ret = l2cap_chan_send(chan, &msg, xw->skb->len);
>> + BT_DBG("transmit return value %d", ret);
>> + if (ret < 0) {
>> + BT_DBG("send %s failed chan %p", dev->name, chan);
>> + kfree_skb(xw->skb);
>> + } else {
>> + consume_skb(xw->skb);
>> + dev->stats.tx_bytes += xw->true_len;
>> + dev->stats.tx_packets++;
>> + }
>> +
>> + l2cap_chan_unlock(chan);
>> + l2cap_chan_put(chan);
>> +
>> + kfree(xw);
>> +}
>> +
>> +static void lowpan_send_unicast_pkt(struct net_device *dev,
>> + struct l2cap_chan *chan,
>> + struct sk_buff *skb,
>> + unsigned int true_len)
>> +{
>> + struct lowpan_xmit_work *xw;
>> +
>> + /* copy to xmit work buffer */
>> + xw = kzalloc(sizeof(*xw), GFP_ATOMIC);
>> + if (!xw)
>> + return;
>> +
>> + /* chan->lock mutex need to be hold so change context to workqueue */
>> + INIT_WORK(&xw->work, lowpan_xmit_worker);
>> + xw->true_len = true_len;
>> + /* freeing protected by ifdown workqueue sync */
>> + xw->dev = dev;
>> + /* disallow freeing of skb while context switch */
>> + xw->skb = skb_get(skb);
>> + /* disallow freeing while context switch */
>> + l2cap_chan_hold(chan);
>> + xw->chan = chan;
>> +
>> + queue_work(lowpan_btle_dev(dev)->workqueue, &xw->work);
>> +}
>> +
>> +static void lowpan_send_mcast_pkt(struct net_device *dev, struct sk_buff *skb,
>> + unsigned int true_len)
>> +{
>> + struct lowpan_btle_dev *btdev = lowpan_btle_dev(dev);
>> + struct lowpan_peer *peer;
>> +
>> + rcu_read_lock();
>> +
>> + BT_DBG("xmit %s starts multicasting", dev->name);
>> +
>> + /* We need to send the packet to every device behind this
>> + * interface, because multicasting.
>> + */
>> + list_for_each_entry_rcu(peer, &btdev->peers, list)
>> + lowpan_send_unicast_pkt(dev, peer->chan, skb, true_len);
>> +
>> + rcu_read_unlock();
>> +}
>> +
>> +static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev)
>> +{
>> + struct lowpan_addr_info *info = lowpan_addr_info(skb);
>> + struct lowpan_btle_dev *btdev = lowpan_btle_dev(dev);
>> + unsigned int true_len = skb->len;
>> + struct lowpan_peer *peer;
>> + bdaddr_t daddr, saddr;
>> + int ret;
>> +
>> + /* We must take a copy of the skb before we modify/replace the ipv6
>> + * header as the header could be used elsewhere.
>> + */
>> + skb = skb_unshare(skb, GFP_ATOMIC);
>> + if (!skb) {
>> + kfree_skb(skb);
>> + goto out;
>> + }
>> +
>> + lowpan_be48_to_le48(&daddr, info->daddr);
>> + lowpan_be48_to_le48(&saddr, info->saddr);
>> +
>> + BT_DBG("xmit ndisc %s dst: %pMR src: %pMR",
>> + dev->name, &daddr, &saddr);
>> +
>> + ret = lowpan_header_compress(skb, dev, &daddr, &saddr);
>> + if (ret < 0) {
>> + kfree_skb(skb);
>> + goto out;
>> + }
>> +
>> + /* this should never be the case, otherwise iphc is broken */
>> + WARN_ON_ONCE(skb->len > dev->mtu);
>> +
>> + if (!memcmp(&daddr, dev->broadcast, dev->addr_len)) {
>> + lowpan_send_mcast_pkt(dev, skb, true_len);
>> + } else {
>> + btdev = lowpan_btle_dev(dev);
>> +
>
> btdev = can be removed here.
>
>> + rcu_read_lock();
>> +
>> + peer = lowpan_lookup_peer(btdev, &daddr);
>> + if (peer)
>> + lowpan_send_unicast_pkt(dev, peer->chan, skb,
>> + true_len);
>> +
>> + rcu_read_unlock();
>> + }
>> +
>> + consume_skb(skb);
>> +
>> +out:
>> + return NETDEV_TX_OK;
>> +}
>> +
>> +static int lowpan_stop(struct net_device *dev)
>> +{
>> + struct lowpan_btle_dev *btdev = lowpan_btle_dev(dev);
>> +
>> + /* synchronize with xmit worker */
>> + flush_workqueue(btdev->workqueue);
>> + return 0;
>> +}
>> +
>> +static struct sk_buff *lowpan_chan_alloc_skb(struct l2cap_chan *chan,
>> + unsigned long hdr_len,
>> + unsigned long len, int nb)
>> +{
>> + return bt_skb_alloc(hdr_len + len, GFP_KERNEL);
>> +}
>> +
>> +static long lowpan_chan_get_sndtimeo(struct l2cap_chan *chan)
>> +{
>> + return L2CAP_CONN_TIMEOUT;
>> +}
>> +
>> +static struct l2cap_chan *lowpan_chan_create(struct net_device *dev)
>> +{
>> + struct lowpan_chan_data *data;
>> + struct l2cap_chan *chan;
>> +
>> + chan = l2cap_chan_create();
>> + if (!chan)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + data = kmalloc(sizeof(*data), GFP_KERNEL);
>> + if (!data) {
>> + l2cap_chan_put(chan);
>> + return ERR_PTR(-ENOMEM);
>> + }
>> +
>> + l2cap_chan_set_defaults(chan);
>> + chan->chan_type = L2CAP_CHAN_CONN_ORIENTED;
>> + chan->mode = L2CAP_MODE_LE_FLOWCTL;
>> + chan->imtu = dev->mtu;
>> + chan->data = data;
>> +
>> + data->peer.chan = chan;
>> + data->dev = dev;
>> +
>> + return chan;
>> +}
>> +
>> +static struct l2cap_chan *lowpan_chan_new_conn(struct l2cap_chan *pchan)
>> +{
>> + struct lowpan_chan_data *data = lowpan_chan_data(pchan);
>> + struct l2cap_chan *chan;
>> +
>> + chan = lowpan_chan_create(data->dev);
>> + if (IS_ERR(chan))
>> + return NULL;
>> +
>> + chan->ops = pchan->ops;
>> + return chan;
>> +}
>> +
>> +static void lowpan_chan_ready(struct l2cap_chan *chan)
>> +{
>> + struct lowpan_chan_data *data = lowpan_chan_data(chan);
>> + struct lowpan_btle_dev *btdev = lowpan_btle_dev(data->dev);
>> +
>> + BT_DBG("%s chan %p ready ", data->dev->name, chan);
>> +
>> + /* make it visible for xmit */
>> + list_add_tail_rcu(&data->peer.list, &btdev->peers);
>> + synchronize_rcu();
>> +}
>> +
>> +static void lowpan_chan_close(struct l2cap_chan *chan)
>> +{
>> + struct lowpan_chan_data *data = lowpan_chan_data(chan);
>> +
>> + BT_DBG("%s chan %p closed ", data->dev->name, chan);
>> +
>> + /* make it unvisible for xmit */
>> + list_del_rcu(&data->peer.list);
>> + synchronize_rcu();
>> + kfree(data);
>
> I currently think much about to run kfree here and I think I need
> bluetooth experts to answer the correct handling here.
>
> We allocate that data on chan_create, but doing a free in chan_close
> requires that the chan state system will never run chan_ready
> afterwards. Is this correct?
>
> Correct handling for me is to do the free when the last chan_put was
> called and free everything. Maybe it could be useful to add some
> private_data _room_ to l2cap_chan_create, which sits after "struct
> l2cap_chan" as something like "u8 data[0]", to allocate/free everything
> at once.
>
> I didn't hit any issues yet that after a close, the ready callback will
> be called again so I think this can never happen.
>
> btw:
>
> previous implementation had kfree_rcu here, but we run synchronize_rcu
> before and this is not needed anymore. I think kfree_rcu will not block,
> but I feeling better to not leave that callback after running
> synchronize_rcu. :-/
>
>> +}
>> +
>> +static const struct l2cap_ops lowpan_chan_ops = {
>> + .name = "L2CAP 6LoWPAN channel",
>> + .new_connection = lowpan_chan_new_conn,
>> + .recv = lowpan_chan_recv,
>> + .close = lowpan_chan_close,
>> + .state_change = l2cap_chan_no_state_change,
>> + .ready = lowpan_chan_ready,
>> + .get_sndtimeo = lowpan_chan_get_sndtimeo,
>> + .alloc_skb = lowpan_chan_alloc_skb,
>> + .teardown = l2cap_chan_no_teardown,
>> + .defer = l2cap_chan_no_defer,
>> + .set_shutdown = l2cap_chan_no_set_shutdown,
>> + .resume = l2cap_chan_no_resume,
>> + .suspend = l2cap_chan_no_suspend,
>> +};
>> +
>> +static int lowpan_change_mtu(struct net_device *dev, int new_mtu)
>> +{
>> + struct lowpan_btle_dev *btdev = lowpan_btle_dev(dev);
>> +
>> + /* if dev is down, peers list are protected by RTNL */
>> + if (netif_running(dev) || !list_empty(&btdev->peers))
>> + return -EBUSY;
>> +
>> + if (new_mtu < IPV6_MIN_MTU)
>> + return -EINVAL;
>> +
>> + dev->mtu = new_mtu;
>> + return 0;
>> +}
>> +
>> +static const struct net_device_ops netdev_ops = {
>> + .ndo_init = lowpan_dev_init,
>> + .ndo_stop = lowpan_stop,
>> + .ndo_start_xmit = lowpan_xmit,
>> + .ndo_change_mtu = lowpan_change_mtu,
>> +};
>> +
>> +static void lowpan_free_netdev(struct net_device *dev)
>> +{
>> + struct lowpan_btle_dev *btdev = lowpan_btle_dev(dev);
>> + struct lowpan_peer *peer, *tmp;
>> + struct l2cap_chan *chan;
>> +
>> + l2cap_chan_close(btdev->listen, 0);
>> + l2cap_chan_put(btdev->listen);
>> +
>> + /* no rcu here? should be safe netdev is down */
>> + list_for_each_entry_safe(peer, tmp, &btdev->peers, list) {
>> + /* close will free peer data, so save chan here */
>> + chan = peer->chan;
>> +
>> + /* will del peer from list and and free.
>> + * should be safe by tmp pointer which has
>> + * a pointer for the next entry.
>> + */
>> + l2cap_chan_close(chan, 0);
>> + l2cap_chan_put(chan);
>> + }
>> +
>> + destroy_workqueue(btdev->workqueue);
>> + btdev->hdev->lowpan_enable = false;
>> + debugfs_remove(btdev->control);
>> + hci_dev_put(btdev->hdev);
>> +}
>> +
>> +static void lowpan_setup(struct net_device *dev)
>> +{
>> + memset(dev->broadcast, 0xff, sizeof(bdaddr_t));
>> +
>> + dev->netdev_ops = &netdev_ops;
>> + dev->destructor = lowpan_free_netdev;
>> + dev->features |= NETIF_F_NETNS_LOCAL;
>> +}
>> +
>> +static struct device_type bt_type = {
>> + .name = "bluetooth",
>> +};
>> +
>> +static struct l2cap_chan *lowpan_listen_chan_new_conn(struct l2cap_chan *pchan)
>> +{
>> + struct l2cap_chan *chan;
>> +
>> + chan = lowpan_chan_create(pchan->data);
>> + if (IS_ERR(chan))
>> + return NULL;
>> +
>> + /* change ops with more functionality than listen,
>> + * which also free chan->data stuff.
>> + */
>> + chan->ops = &lowpan_chan_ops;
>> +
>> + return chan;
>> +}
>> +
>> +static const struct l2cap_ops lowpan_listen_chan_ops = {
>> + .name = "L2CAP 6LoWPAN listen channel",
>> + .new_connection = lowpan_listen_chan_new_conn,
>> + .recv = l2cap_chan_no_recv,
>> + .close = l2cap_chan_no_close,
>> + .state_change = l2cap_chan_no_state_change,
>> + .ready = l2cap_chan_no_ready,
>> + .get_sndtimeo = l2cap_chan_no_get_sndtimeo,
>> + .alloc_skb = l2cap_chan_no_alloc_skb,
>> + .teardown = l2cap_chan_no_teardown,
>> + .defer = l2cap_chan_no_defer,
>> + .set_shutdown = l2cap_chan_no_set_shutdown,
>> + .resume = l2cap_chan_no_resume,
>> + .suspend = l2cap_chan_no_suspend,
>> +};
>> +
>> +static int lowpan_create_listen_chan(struct net_device *dev)
>> +{
>> + struct lowpan_btle_dev *btdev = lowpan_btle_dev(dev);
>> + struct l2cap_chan *chan;
>> + u8 bdaddr_type;
>> + int ret;
>> +
>> + /* don't use lowpan_chan_create here, because less functionality */
>> + chan = l2cap_chan_create();
>> + if (!chan)
>> + return -ENOMEM;
>> +
>> + chan->data = dev;
>
> I will add a comment here that this private_data of chan is really
> "struct net_device *dev" only, it's for listen stuff only.
>
> Looks wrong at the first sight.
>
>> + chan->ops = &lowpan_listen_chan_ops;
>> + hci_copy_identity_address(btdev->hdev, &chan->src, &bdaddr_type);
>> + if (bdaddr_type == ADDR_LE_DEV_PUBLIC)
>> + chan->src_type = BDADDR_LE_PUBLIC;
>> + else
>> + chan->src_type = BDADDR_LE_RANDOM;
>> +
>> + chan->state = BT_LISTEN;
>> + atomic_set(&chan->nesting, L2CAP_NESTING_PARENT);
>> +
>> + BT_DBG("chan %p src type %d", chan, chan->src_type);
>> + ret = l2cap_add_psm(chan, BDADDR_ANY, cpu_to_le16(L2CAP_PSM_IPSP));
>> + if (ret) {
>> + l2cap_chan_put(chan);
>> + BT_ERR("psm cannot be added err %d", ret);
>> + return ret;
>> + }
>> + btdev->listen = chan;
>> +
>> + return 0;
>> +}
>> +
>> +static const struct file_operations lowpan_control_fops;
>> +
>> +static struct net_device *lowpan_btle_newlink(struct hci_dev *hdev)
>> +{
>> + struct lowpan_btle_dev *btdev;
>> + struct net_device *dev;
>> + int err = -ENOMEM;
>> +
>> + dev = alloc_netdev(LOWPAN_PRIV_SIZE(sizeof(struct lowpan_btle_dev)),
>> + LOWPAN_IFNAME_TEMPLATE, NET_NAME_ENUM, lowpan_setup);
>> + if (!dev)
>> + goto out;
>> +
>> + dev->addr_assign_type = NET_ADDR_PERM;
>> + dev->addr_len = sizeof(hdev->bdaddr.b);
>> + lowpan_le48_to_be48(dev->dev_addr, &hdev->bdaddr);
>> +
>> + SET_NETDEV_DEV(dev, &hdev->dev);
>> + SET_NETDEV_DEVTYPE(dev, &bt_type);
>> +
>> + btdev = lowpan_btle_dev(dev);
>> + /* avoid freeing */
>> + btdev->hdev = hci_dev_hold(hdev);
>> + INIT_LIST_HEAD(&btdev->peers);
>> +
>> + btdev->workqueue = create_singlethread_workqueue(dev->name);
>> + if (!btdev->workqueue) {
>> + free_netdev(dev);
>> + goto out;
>> + }
>> +
>> + err = lowpan_create_listen_chan(dev);
>> + if (err < 0) {
>> + destroy_workqueue(btdev->workqueue);
>> + free_netdev(dev);
>> + goto out;
>> + }
>> +
>> + err = lowpan_register_netdevice(dev, LOWPAN_LLTYPE_BTLE);
>> + if (err < 0) {
>> + BT_ERR("register_netdev failed %d", err);
>> + l2cap_chan_close(btdev->listen, 0);
>> + l2cap_chan_put(btdev->listen);
>> + free_netdev(dev);
>> + goto out;
>> + }
>> + hdev->ldev = dev;
>> +
>> + /* ignore error handling here, we cannot call unregister anymore
>> + * It's a bug, but it's debugfs... so ignore it.
>> + */
>> + btdev->control = debugfs_create_file(dev->name, 0644,
>> + bt_debugfs, btdev,
>> + &lowpan_control_fops);
>> + if (!btdev->control)
>> + BT_ERR("debugfs error for %s, disable/enable 6lowpan again",
>> + dev->name);
>> +
>> + return dev;
>> +
>> +out:
>> + return ERR_PTR(err);
>> +}
>> +
>> +static void lowpan_btle_dellink(struct net_device *dev)
>> +{
>> + lowpan_unregister_netdevice(dev);
>> +}
>> +
>> +static int lowpan_parse_le_bdaddr(struct hci_dev *hdev, unsigned char *buf,
>> + bdaddr_t *addr, u8 *addr_type)
>> +{
>> + int n;
>> +
>> + n = sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %hhu",
>> + &addr->b[5], &addr->b[4], &addr->b[3],
>> + &addr->b[2], &addr->b[1], &addr->b[0],
>> + addr_type);
>> + if (n < 7)
>> + return -EINVAL;
>> +
>> + /* check if we handle le addresses and not own source address */
>> + if (!bdaddr_type_is_le(*addr_type) ||
>> + !bacmp(&hdev->bdaddr, addr))
>> + return -EINVAL;
>> +
>> + return n;
>> +}
>> +
>> +static ssize_t __lowpan_control_write(struct file *fp,
>> + const char __user *user_buffer,
>> + size_t count, loff_t *position)
>> +{
>> + char buf[32] = { };
>> + size_t buf_size = min(count, sizeof(buf) - 1);
>> + struct seq_file *file = fp->private_data;
>> + struct lowpan_btle_dev *btdev = file->private;
>> + struct hci_dev *hdev = btdev->hdev;
>> + struct lowpan_peer *peer;
>> + /* slave address */
>> + bdaddr_t addr;
>> + u8 addr_type;
>> + int ret;
>> +
>> + if (copy_from_user(buf, user_buffer, buf_size))
>> + return -EFAULT;
>> +
>> + if (memcmp(buf, "connect ", 8) == 0) {
>> + struct lowpan_peer *peer;
>
> peer can be removed here.
>
>> + struct l2cap_chan *chan;
>> +
>> + ret = lowpan_parse_le_bdaddr(hdev, &buf[8], &addr, &addr_type);
>> + if (ret < 0)
>> + return ret;
>> +
>> + /* check if we already know that slave */
>> + rcu_read_lock();
>> + peer = lowpan_lookup_peer(btdev, &addr);
>> + if (peer) {
>> + rcu_read_unlock();
>> + BT_DBG("6LoWPAN connection already exists");
>> + return -EEXIST;
>> + }
>> + rcu_read_unlock();
>> +
>> + chan = lowpan_chan_create(hdev->ldev);
>> + if (IS_ERR(chan))
>> + return PTR_ERR(chan);
>> + chan->ops = &lowpan_chan_ops;
>> +
>> + ret = l2cap_hdev_chan_connect(hdev, chan,
>> + cpu_to_le16(L2CAP_PSM_IPSP),
>> + 0, &addr, addr_type);
>> + if (ret < 0) {
>> + l2cap_chan_put(chan);
>> + return ret;
>> + }
>> +
>> + return count;
>> + } else if (memcmp(buf, "disconnect ", 11) == 0) {
>> + ret = lowpan_parse_le_bdaddr(hdev, &buf[11], &addr, &addr_type);
>> + if (ret < 0)
>> + return ret;
>> +
>> + /* check if we don't know that slave */
>> + rcu_read_lock();
>> + peer = lowpan_lookup_peer(btdev, &addr);
>> + if (!peer) {
>> + rcu_read_unlock();
>> + BT_DBG("6LoWPAN connection not found in peers");
>> + return -ENOENT;
>> + }
>> + rcu_read_unlock();
>> +
>> + /* first delete the peer out of list,
>> + * which makes it visiable to netdev,
>> + * will be done by close callback.
>> + */
>> + l2cap_chan_close(peer->chan, 0);
>> + l2cap_chan_put(peer->chan);
>> + } else {
>> + return -EINVAL;
>> + }
>> +
>> + return count;
>> +}
>> +
>> +static ssize_t lowpan_control_write(struct file *fp,
>> + const char __user *user_buffer,
>> + size_t count, loff_t *position)
>> +{
>> + ssize_t ret;
>> +
>> + rtnl_lock();
>> + ret = __lowpan_control_write(fp, user_buffer, count, position);
>> + rtnl_unlock();
>> +
>> + return ret;
>> +}
>> +
>> +static int lowpan_control_show(struct seq_file *f, void *ptr)
>> +{
>> + struct lowpan_btle_dev *btdev = f->private;
>> + struct hci_dev *hdev = btdev->hdev;
>> + struct lowpan_peer *peer;
>> +
>> + rtnl_lock();
>> +
>> + if (!hdev->lowpan_enable) {
>> + rtnl_unlock();
>> + return 0;
>> + }
>> +
>> + rcu_read_lock();
>> +
>> + list_for_each_entry_rcu(peer, &btdev->peers, list) {
>> + seq_printf(f, "%pMR (type %u) state: %s\n",
>> + &peer->chan->dst, peer->chan->dst_type,
>> + state_to_string(peer->chan->state));
>> + }
>> +
>> + rcu_read_unlock();
>> +
>> + rtnl_unlock();
>> + return 0;
>> +}
>> +
>> +static int lowpan_control_open(struct inode *inode, struct file *file)
>> +{
>> + return single_open(file, lowpan_control_show, inode->i_private);
>> +}
>> +
>> +static const struct file_operations lowpan_control_fops = {
>> + .open = lowpan_control_open,
>> + .read = seq_read,
>> + .write = lowpan_control_write,
>> + .llseek = seq_lseek,
>> + .release = single_release,
>> +};
>> +
>> +/* TODO remove this when add non debugfs API, it's a workaround */
>> +static struct workqueue_struct *workqueue_cleanup;
>> +
>> +struct lowpan_cleanup_work {
>> + struct work_struct work;
>> + struct net_device *dev;
>> +};
>> +
>> +static void lowpan_cleanup_worker(struct work_struct *work)
>> +{
>> + struct lowpan_cleanup_work *xw;
>> +
>> + xw = container_of(work, struct lowpan_cleanup_work, work);
>> +
>> + rtnl_lock();
>> + lowpan_btle_dellink(xw->dev);
>> + lowpan_btle_dev(xw->dev)->hdev->pending_deletion = false;
>> + rtnl_unlock();
>> +
>> + kfree(xw);
>> +}
>> +
>> +/* TODO workaround, debugfs doesn't like recursion remove while debugfs
>> + * handling, this should be removed for by using real UAPI.
>> + */
>> +static int __lowpan_btle_dellink(struct net_device *dev,
>> + struct hci_dev *hdev)
>> +{
>> + struct lowpan_cleanup_work *xw;
>> +
>> + xw = kzalloc(sizeof(*xw), GFP_KERNEL);
>> + if (!xw)
>> + return -ENOMEM;
>> +
>> + INIT_WORK(&xw->work, lowpan_cleanup_worker);
>> + xw->dev = dev;
>> +
>> + hdev->pending_deletion = true;
>> + queue_work(workqueue_cleanup, &xw->work);
>> + return 0;
>> +}
>> +
>> +static int lowpan_enable_set(struct hci_dev *hdev, bool enabled)
>> +{
>> + int ret = 0;
>> +
>> + if (hdev->lowpan_enable == enabled)
>> + goto out;
>> +
>> + if (enabled) {
>> + struct net_device *dev;
>> +
>> + /* create one interface by default */
>> + dev = lowpan_btle_newlink(hdev);
>> + if (IS_ERR(dev))
>> + return PTR_ERR(dev);
>> +
>> + /* TODO should be done by user network managers? */
>> + ret = dev_open(dev);
>> + if (ret < 0) {
>> + BT_ERR("iface %s cannot be opened %d", dev->name, ret);
>> + ret = __lowpan_btle_dellink(dev, hdev);
>> + if (ret < 0)
>> + BT_ERR("failed to remove interface %s",
>> + hdev->ldev->name);
>> +
>> + return ret;
>> + }
>> +
>> + hdev->lowpan_enable = true;
>> + } else {
>> + /* ignore pending deletions */
>> + if (hdev->pending_deletion)
>> + return 0;
>> +
>> + __lowpan_btle_dellink(hdev->ldev, hdev);
>> + }
>> +
>> +out:
>> + return ret;
>> +}
>> +
>> +static void lowpan_btle_hci_unregister(struct hci_dev *hdev)
>> +{
>> + /* no need to cleanup debugfs, will be done by hci_core */
>
> comment can be removed.
>
> Well..., I had this comment because I did some stuff before per
> "hci->debugfs". This is also the question here.
>
> Maybe "6lowpan_enable" makes no sense to move it to per hci_dev debugfs
> entry. For write, it makes sense - but read, you want some "global"
> mapping of "hci_dev <-> lowpan interface", this may easy realizied for
> netlink UAPI but for debugfs it's complicated because you need to
> iterate over all hci%d entries and making cat on "6lowpan_enable".
>
> That's the current reason why "6lowpan_enable" is global.
>
> ---
>
> The control file for btle lowpan interface could be moved per hci_dev
> and maybe I will change it again, then the above comment makes sense
> again. :-)
>
>> +
>> + /* TODO netns support with multiple lowpan interfaces */
>> + if (hdev->lowpan_enable)
>> + lowpan_btle_dellink(hdev->ldev);
>> +}
>> +
>> +static int lowpan_hci_dev_event(struct notifier_block *unused,
>> + unsigned long event, void *ptr)
>> +{
>> + struct hci_dev *hdev = ptr;
>> + int ret = NOTIFY_OK;
>> +
>> + rtnl_lock();
>> +
>> + /* bluetooth handles that event type as int,
>> + * but there is no overflow yet.
>> + */
>> + switch (event) {
>> + case HCI_DEV_UNREG:
>> + lowpan_btle_hci_unregister(hdev);
>> + ret = NOTIFY_DONE;
>> + break;
>> + default:
>> + break;
>> + }
>> +
>> + rtnl_unlock();
>> +
>> + return ret;
>> +}
>> +
>> +static struct notifier_block lowpan_hcI_dev_notifier = {
>> + .notifier_call = lowpan_hci_dev_event,
>> +};
>> +
>> +static ssize_t lowpan_enable_write(struct file *fp,
>> + const char __user *user_buffer,
>> + size_t count, loff_t *position)
>> +{
>> + char buf[32] = { };
>> + size_t buf_size = min(count, sizeof(buf) - 1);
>> + struct hci_dev *hdev;
>> + int idx, enabled, n, ret;
>> +
>> + if (copy_from_user(buf, user_buffer, buf_size))
>> + return -EFAULT;
>> +
>> + n = sscanf(buf, "hci%d %d", &idx, &enabled);
>> + if (n != 2)
>> + return -EINVAL;
>> +
>> + hdev = hci_dev_get(idx);
>> + if (!hdev)
>> + return -EINVAL;
>> +
>> + rtnl_lock();
>> + ret = lowpan_enable_set(hdev, enabled);
>> + rtnl_unlock();
>> +
>> + hci_dev_put(hdev);
>> +
>> + return count;
>> +}
>> +
>> +static int lowpan_enable_show(struct seq_file *f, void *ptr)
>> +{
>> + struct hci_dev *hdev;
>> +
>> + rtnl_lock();
>> + read_lock(&hci_dev_list_lock);
>> + list_for_each_entry(hdev, &hci_dev_list, list) {
>> + if (hdev->lowpan_enable)
>> + seq_printf(f, "hci%d -> %s\n", hdev->id,
>> + hdev->ldev->name);
>> + }
>> + read_unlock(&hci_dev_list_lock);
>> + rtnl_unlock();
>> +
>> + return 0;
>> +}
>> +
>> +static int lowpan_enable_open(struct inode *inode, struct file *file)
>> +{
>> + return single_open(file, lowpan_enable_show, inode->i_private);
>> +}
>> +
>> +static const struct file_operations lowpan_enable_fops = {
>> + .open = lowpan_enable_open,
>> + .read = seq_read,
>> + .write = lowpan_enable_write,
>> + .llseek = seq_lseek,
>> + .release = single_release,
>> +};
>> +
>> +static int __init bt_6lowpan_init(void)
>> +{
>> + int err;
>> +
>> + workqueue_cleanup = create_singlethread_workqueue("btle 6lowpan");
>> + if (!workqueue_cleanup)
>> + return -ENOMEM;
>> +
>> + debugfs_create_file("6lowpan_enable", 0644, bt_debugfs, NULL,
>> + &lowpan_enable_fops);
>> +
>> + err = register_hci_dev_notifier(&lowpan_hcI_dev_notifier);
>> + if (err < 0)
>> + destroy_workqueue(workqueue_cleanup);
>> +
>> + return err;
>> +}
>> +
>> +static void __exit bt_6lowpan_exit(void)
>> +{
>> + destroy_workqueue(workqueue_cleanup);
>> + unregister_hci_dev_notifier(&lowpan_hcI_dev_notifier);
>> +}
>> +
>> +module_init(bt_6lowpan_init);
>> +module_exit(bt_6lowpan_exit);
>> +
>> +MODULE_AUTHOR("Jukka Rissanen <[email protected]>");
>> +MODULE_DESCRIPTION("Bluetooth 6LoWPAN");
>> +MODULE_VERSION(LOWPAN_BTLE_VERSION);
>> +MODULE_LICENSE("GPL");
>> diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
>> index e347828..b3ff12e 100644
>> --- a/net/bluetooth/Makefile
>> +++ b/net/bluetooth/Makefile
>> @@ -7,6 +7,9 @@ obj-$(CONFIG_BT_RFCOMM) += rfcomm/
>> obj-$(CONFIG_BT_BNEP) += bnep/
>> obj-$(CONFIG_BT_CMTP) += cmtp/
>> obj-$(CONFIG_BT_HIDP) += hidp/
>> +obj-$(CONFIG_BT_6LOWPAN) += bluetooth_6lowpan.o
>> +
>> +bluetooth_6lowpan-y := 6lowpan.o
>>
>> bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
>> hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o lib.o \
>>
>
> - Alex
--
Luiz Augusto von Dentz
Hi,
On 07/14/2016 10:21 AM, Alexander Aring wrote:
>
> Hi,
>
> On 07/13/2016 01:15 PM, Jukka Rissanen wrote:
>> Hi Alex,
>>
>> On Tue, 2016-07-12 at 22:34 +0200, Alexander Aring wrote:
>>> Hi,
>>>
>>> On 07/11/2016 09:50 PM, Alexander Aring wrote:
>>>>
>>>> These flags should be all the same for 6LoWPAN so move it to
>>>> 6LoWPAN generic.
>>>>
>>>> Signed-off-by: Alexander Aring <[email protected]>
>>>> ---
>>>> net/6lowpan/core.c | 1 +
>>>> net/ieee802154/6lowpan/core.c | 1 -
>>>> 2 files changed, 1 insertion(+), 1 deletion(-)
>>>>
>>>> diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
>>>> index a978000..6b7de14 100644
>>>> --- a/net/6lowpan/core.c
>>>> +++ b/net/6lowpan/core.c
>>>> @@ -62,6 +62,7 @@ int lowpan_register_netdevice(struct net_device
>>>> *dev,
>>>> dev->type = ARPHRD_6LOWPAN;
>>>> dev->mtu = IPV6_MIN_MTU;
>>>> dev->priv_flags |= IFF_NO_QUEUE;
>>>> + dev->flags = IFF_BROADCAST | IFF_MULTICAST;
>>>>
>>>> dev->header_ops = &header_ops;
>>>>
>>>> diff --git a/net/ieee802154/6lowpan/core.c
>>>> b/net/ieee802154/6lowpan/core.c
>>>> index 228a711..a60abad 100644
>>>> --- a/net/ieee802154/6lowpan/core.c
>>>> +++ b/net/ieee802154/6lowpan/core.c
>>>> @@ -92,7 +92,6 @@ static void lowpan_setup(struct net_device *ldev)
>>>> memset(ldev->broadcast, 0xff, IEEE802154_ADDR_LEN);
>>>> /* We need an ipv6hdr as minimum len when calling xmit */
>>>> ldev->hard_header_len = sizeof(struct ipv6hdr);
>>> This should be moved to generic 6lowpan as well.
>>>
>>> This says at least, the skb->len at xmit callback must be at least a
>>> length of "sizeof(struct ipv6hdr)" which should be correct.
>>>
>>> BUT...
>>>
>>> I still have (more than years) the use-case that somebody sends an
>>> AF_PACKET raw socket over lowpan interface.
>>
>> According to packet(7), the "Packet sockets are used to receive or send
>> raw packets at the device driver (OSI Layer 2) level."
>> But lowpan0 interface is meant to compress IPv6 header so it is working
>> on L3 data (or more precisely between L2 and L3).
>> So I am wondering how this is suppose to work and what kind of use case
>> you have for this?
>>
>
> I think the issue is more complex, forget what the man page says here.
> We use this callback for something which isn't made for, because the
> IPv6 ndisc will tell us over this callback "what's the destination L2
> address".
>
> Sorry, I wrote something here which confused you. I meant more I think
> about the use-case that users could try to using AF_PACKET RAW sockets
> here and I think they can kill the kernel with the right parameters.
>
> Sending AF_PACKET RAW here makes completely no sense and should be
> disabled. Because we set in this callback something in the skb_headroom
> and xmit callback will use it.
>
> IMPORTANT NOTE: this handling is weird, because we except here that
> between header_create and xmit the IPv6 subsytem will not do
> skb_put/skb_push anymore. I currently think they will never do that
> because the header_create callback is normally there to put a mac header
> to the socket buffer. So IPv6 creation should be mostly done.
>
> Sending DGRAM sockets it could make sense somehow for a crazy use-case
> which maybe under 0.1% users wants. You can give the ndisc information
> from userspace over that, source and destination address.
>
> Nevertheless I would disable AF_PACKET DGRAM/RAW handling, EXCEPT RAW
> RECEIVE handling. This will already be used by wireshark/tcpdump/etc.
>
> ---
>
> Alternative half solution:
>
> I already thought about to simple make a re-lookup on ipv6 ndisc
> cache for L3 destination address. Like we do for short address handling.
>
> But this will not work for broadcast addresses, I would more use the
> normally functionality in IPv6 to tell us the L2 destination address
> which is "header_create".
>
> I need to think about the real solution to handle everything, you could
> also except that somebody sends IPv6 raw over AF_PACKET here, then
> parsing L3 daddr and do such lookup on ndisc for L2 daddr, but there
> exists this problem with broadcast. I need to look into IPv6 how we
> otherwise can detect unicast or multicast, maybe it's just when L3 is
> multicast, but I would not bet on that. I need to look into IPv6 to
> get an answer.
>
But this handling to send RAW IPv6 on AF_PACKET should not be done over
AF_PACKET, there exists sockets in AF_INET6 to do that. So I think
disable AF_PACKET (except RAW rx functionality) would be okay...
using AF_PACKET for such use-case is wrong anyway.
- Alex
Hi,
On 07/13/2016 01:15 PM, Jukka Rissanen wrote:
> Hi Alex,
>
> On Tue, 2016-07-12 at 22:34 +0200, Alexander Aring wrote:
>> Hi,
>>
>> On 07/11/2016 09:50 PM, Alexander Aring wrote:
>>>
>>> These flags should be all the same for 6LoWPAN so move it to
>>> 6LoWPAN generic.
>>>
>>> Signed-off-by: Alexander Aring <[email protected]>
>>> ---
>>> net/6lowpan/core.c | 1 +
>>> net/ieee802154/6lowpan/core.c | 1 -
>>> 2 files changed, 1 insertion(+), 1 deletion(-)
>>>
>>> diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
>>> index a978000..6b7de14 100644
>>> --- a/net/6lowpan/core.c
>>> +++ b/net/6lowpan/core.c
>>> @@ -62,6 +62,7 @@ int lowpan_register_netdevice(struct net_device
>>> *dev,
>>> dev->type = ARPHRD_6LOWPAN;
>>> dev->mtu = IPV6_MIN_MTU;
>>> dev->priv_flags |= IFF_NO_QUEUE;
>>> + dev->flags = IFF_BROADCAST | IFF_MULTICAST;
>>>
>>> dev->header_ops = &header_ops;
>>>
>>> diff --git a/net/ieee802154/6lowpan/core.c
>>> b/net/ieee802154/6lowpan/core.c
>>> index 228a711..a60abad 100644
>>> --- a/net/ieee802154/6lowpan/core.c
>>> +++ b/net/ieee802154/6lowpan/core.c
>>> @@ -92,7 +92,6 @@ static void lowpan_setup(struct net_device *ldev)
>>> memset(ldev->broadcast, 0xff, IEEE802154_ADDR_LEN);
>>> /* We need an ipv6hdr as minimum len when calling xmit */
>>> ldev->hard_header_len = sizeof(struct ipv6hdr);
>> This should be moved to generic 6lowpan as well.
>>
>> This says at least, the skb->len at xmit callback must be at least a
>> length of "sizeof(struct ipv6hdr)" which should be correct.
>>
>> BUT...
>>
>> I still have (more than years) the use-case that somebody sends an
>> AF_PACKET raw socket over lowpan interface.
>
> According to packet(7), the "Packet sockets are used to receive or send
> raw packets at the device driver (OSI Layer 2) level."
> But lowpan0 interface is meant to compress IPv6 header so it is working
> on L3 data (or more precisely between L2 and L3).
> So I am wondering how this is suppose to work and what kind of use case
> you have for this?
>
I think the issue is more complex, forget what the man page says here.
We use this callback for something which isn't made for, because the
IPv6 ndisc will tell us over this callback "what's the destination L2
address".
Sorry, I wrote something here which confused you. I meant more I think
about the use-case that users could try to using AF_PACKET RAW sockets
here and I think they can kill the kernel with the right parameters.
Sending AF_PACKET RAW here makes completely no sense and should be
disabled. Because we set in this callback something in the skb_headroom
and xmit callback will use it.
IMPORTANT NOTE: this handling is weird, because we except here that
between header_create and xmit the IPv6 subsytem will not do
skb_put/skb_push anymore. I currently think they will never do that
because the header_create callback is normally there to put a mac header
to the socket buffer. So IPv6 creation should be mostly done.
Sending DGRAM sockets it could make sense somehow for a crazy use-case
which maybe under 0.1% users wants. You can give the ndisc information
from userspace over that, source and destination address.
Nevertheless I would disable AF_PACKET DGRAM/RAW handling, EXCEPT RAW
RECEIVE handling. This will already be used by wireshark/tcpdump/etc.
---
Alternative half solution:
I already thought about to simple make a re-lookup on ipv6 ndisc
cache for L3 destination address. Like we do for short address handling.
But this will not work for broadcast addresses, I would more use the
normally functionality in IPv6 to tell us the L2 destination address
which is "header_create".
I need to think about the real solution to handle everything, you could
also except that somebody sends IPv6 raw over AF_PACKET here, then
parsing L3 daddr and do such lookup on ndisc for L2 daddr, but there
exists this problem with broadcast. I need to look into IPv6 how we
otherwise can detect unicast or multicast, maybe it's just when L3 is
multicast, but I would not bet on that. I need to look into IPv6 to
get an answer.
---
The reason why it should be removed is also below:
>>
>> I didn't test it yet, but I think this is a simple way to crash the
>> kernel on all lowpan interfaces. I currently not sure if I can break
>> something there, but I am sure it will send garbage data.
>>
>> The xmit callback needs data which is available in skb_headroom, this
>> data is set by header_create callback which will not called on
>> AF_PACKET
>> RAW sockets.
>>
>> The root of this issue is that we don't have L2 here for creating mac
>> headers. The header_create callback should do that, but we do 6LoWPAN
>> adaptation here and the header_create callback will be used by ndisc
>> to
>> say "here are the addresses, generate a mac header" and AF_PACKET
>> _DGRAM_ (for putting mac header in front of AF_PACKET payload).
>>
>> We use this callback for the first use-case of ndisc only.
>>
>> AF_PACKET RAW receive make sense, because tcpdump/wireshark needs to
>> capture data. Sending AF_PACKET RAW makes no sense and will I suppose
>> crash the kernel and I think every user can do that.
>>
>> DGRAM sockets maybe makes sense, but I would disable that also for
>> (receive and transmit). You need to use PF_INET6 socket types for
>> lowpan
>> interfaces only. That issue is somehow described at [0].
>>
- Alex
Hi Alex,
On Tue, 2016-07-12 at 22:34 +0200, Alexander Aring wrote:
> Hi,
>
> On 07/11/2016 09:50 PM, Alexander Aring wrote:
> >
> > These flags should be all the same for 6LoWPAN so move it to
> > 6LoWPAN generic.
> >
> > Signed-off-by: Alexander Aring <[email protected]>
> > ---
> >  net/6lowpan/core.c            | 1 +
> > Â net/ieee802154/6lowpan/core.c | 1 -
> > Â 2 files changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
> > index a978000..6b7de14 100644
> > --- a/net/6lowpan/core.c
> > +++ b/net/6lowpan/core.c
> > @@ -62,6 +62,7 @@ int lowpan_register_netdevice(struct net_device
> > *dev,
> > Â dev->type = ARPHRD_6LOWPAN;
> > Â dev->mtu = IPV6_MIN_MTU;
> > Â dev->priv_flags |= IFF_NO_QUEUE;
> > + dev->flags = IFF_BROADCAST | IFF_MULTICAST;
> > Â
> > Â dev->header_ops = &header_ops;
> > Â
> > diff --git a/net/ieee802154/6lowpan/core.c
> > b/net/ieee802154/6lowpan/core.c
> > index 228a711..a60abad 100644
> > --- a/net/ieee802154/6lowpan/core.c
> > +++ b/net/ieee802154/6lowpan/core.c
> > @@ -92,7 +92,6 @@ static void lowpan_setup(struct net_device *ldev)
> > Â memset(ldev->broadcast, 0xff, IEEE802154_ADDR_LEN);
> > Â /* We need an ipv6hdr as minimum len when calling xmit */
> > Â ldev->hard_header_len = sizeof(struct ipv6hdr);
> This should be moved to generic 6lowpan as well.
>
> This says at least, the skb->len at xmit callback must be at least a
> length of "sizeof(struct ipv6hdr)" which should be correct.
>
> BUT...
>
> I still have (more than years) the use-case that somebody sends an
> AF_PACKET raw socket over lowpan interface.
According to packet(7), the "Packet sockets are used to receive or send
raw packets at the device driver (OSI Layer 2) level."
But lowpan0 interface is meant to compress IPv6 header so it is working
on L3 data (or more precisely between L2 and L3).
So I am wondering how this is suppose to work and what kind of use case
you have for this?
>
> I didn't test it yet, but I think this is a simple way to crash the
> kernel on all lowpan interfaces. I currently not sure if I can break
> something there, but I am sure it will send garbage data.
>
> The xmit callback needs data which is available in skb_headroom, this
> data is set by header_create callback which will not called on
> AF_PACKET
> RAW sockets.
>
> The root of this issue is that we don't have L2 here for creating mac
> headers. The header_create callback should do that, but we do 6LoWPAN
> adaptation here and the header_create callback will be used by ndisc
> to
> say "here are the addresses, generate a mac header" and AF_PACKET
> _DGRAM_ (for putting mac header in front of AF_PACKET payload).
>
> We use this callback for the first use-case of ndisc only.
>
> AF_PACKET RAW receive make sense, because tcpdump/wireshark needs to
> capture data. Sending AF_PACKET RAW makes no sense and will I suppose
> crash the kernel and I think every user can do that.
>
> DGRAM sockets maybe makes sense, but I would disable that also for
> (receive and transmit). You need to use PF_INET6 socket types for
> lowpan
> interfaces only. That issue is somehow described at [0].
>
> >
> > - ldev->flags = IFF_BROADCAST |
> > IFF_MULTICAST;
> > Â
> > Â ldev->netdev_ops = &lowpan_netdev_ops;
> > Â ldev->destructor = free_netdev;
> >
> - Alex
>
> [0] http://lxr.free-electrons.com/source/net/ieee802154/6lowpan/tx.c#
> L43
Cheers,
Jukka
Hi,
On 07/13/2016 12:13 PM, Luiz Augusto von Dentz wrote:
> Hi Alex,
>
> On Wed, Jul 13, 2016 at 12:12 PM, Alexander Aring <[email protected]> wrote:
>>
>> Hi,
>>
>> On 07/12/2016 08:35 PM, Alexander Aring wrote:
>>>
>>> Hi,
>>>
>>> On 07/12/2016 04:51 PM, Luiz Augusto von Dentz wrote:
>>> ...
>>>>>
>>>>> HOW TO REPRODUCE:
>>>>>
>>>>> It's simple, do some high payloads which makes lot tx/rcv traffic on both sides:
>>>>>
>>>>> Node A:
>>>>>
>>>>> ping6 $IP_NODEB%6lo0 -s 60000
>>>>>
>>>>> Node B:
>>>>>
>>>>> ping6 ff02::1%6lo0
>>>>
>>>> Im not sure I understand what you are trying to do with a packet of
>>>> 60Kb, the l2cap_chan can most likely only do 1280 bytes and even with
>>>
>>> 60Kb is just some incredible high value to produce on both sides tx/rx
>>> data. The other side will normally send some "defragmentation failure"
>>> icmp messages.
>>>
>>> This value is just to produce the high traffic, not practice payload.
>>>
>>>> fragmentation this will consume the credits quicker than we can
>>>> receive more so it will eventually starting buffering until it gets
>>>> stuck. Note that it is probably still receiving credits but we
>>>
>>> This is exactly what I like to do. Produce a lot of data to kill L3
>>> layer, but this should not kill the L2 layer. Killing the L2 layer is
>>> what's happend here.
>>>
>>> If I do a lot of data on e.g. tcp traffic and the tcp connection will be
>>> closed -> that's fine for me but afterwards starting a new L3 connection
>>> should be still working. This is not the case here, I can kill L2 layer
>>> and nothing works anymore, because L2 running into deadlock.
>>>
>>>> probably have so much data buffered that all the credits are consumed
>>>> almost immediately after receiving, or you don't see -EAGAIN more than
>>>> once?
>>>>
>>>
>>> My example shows that we transmit some data on both nodes, that is
>>> important to check the l2cap_chan_send return type.
>>>
>>> When I do that above and running into the deadlock the IPv6 connection
>>> runs "NS" messages only, because it still can send on L3 layer but the
>>> L2 layer is killed -> I get -EAGAIN in l2cap_chan_send always on both
>>> nodes because tx_credits are on both nodes zero and will not be
>>> incremented again.
>>>
>>> So far I know these tx_credits will be incremented only if somebody
>>> sends something again, right? This will never be the case again and I am
>>> stucked in a deadlock which should not be there.
>>>
>>> to your question:
>>>
>>> "or you don't see -EAGAIN more than once"
>>>
>>> I see -EAGAIN always at calling l2cap_chan_send on both nodes. I think I
>>> can wait forever, after an year I still see "-EAGAIN" when calling
>>> l2cap_chan_send.
>>>
>>> Nevertheless, I will try to hit the deadlock (-EAGAIN) on both nodes so
>>> nobody transmits anything anymore. Then waiting six hours and look if
>>> still -EAGAIN is there on both nodes. I will report again here, after
>>> that time it should working again, but I don't believe that.
>>>
>>
>> I waited about ~ 6hours, deadlock was still there.
>>
>> Node A:
>>
>> transmit return value -11
>> send 6lo0 failed chan f617cc00
>> xmit ndisc 6lo0 dst: ff:ff:ff:ff:ff:ff src: 5c:f3:70:68:c1:76
>> xmit 6lo0 starts multicasting
>> l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f617cc00
>> transmit return value -11
>> send 6lo0 failed chan f617cc00
>> xmit ndisc 6lo0 dst: ff:ff:ff:ff:ff:ff src: 5c:f3:70:68:c1:76
>> xmit 6lo0 starts multicasting
>> l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f617cc00
>> transmit return value -11
>> send 6lo0 failed chan f617cc00
>> xmit ndisc 6lo0 dst: ff:ff:ff:ff:ff:ff src: 5c:f3:70:68:c1:76
>> xmit 6lo0 starts multicasting
>> l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f617cc00
>> transmit return value -11
>> send 6lo0 failed chan f617cc00
>> xmit ndisc 6lo0 dst: ff:ff:ff:ff:ff:ff src: 5c:f3:70:68:c1:76
>> xmit 6lo0 starts multicasting
>> l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f617cc00
>> transmit return value -11
>> send 6lo0 failed chan f617cc00
>>
>> Node B:
>>
>> xmit ndisc 6lo0 dst: ff:ff:ff:ff:ff:ff src: 5c:f3:70:6b:3b:1f
>> xmit 6lo0 starts multicasting
>> l2cap_chan_send 6lo0 dst: 5c:f3:70:68:c1:76 type 1 src: 5c:f3:70:6b:3b:1f chan f62aac00
>> transmit return value -11
>> send 6lo0 failed chan f62aac00
>> xmit ndisc 6lo0 dst: ff:ff:ff:ff:ff:ff src: 5c:f3:70:6b:3b:1f
>> xmit 6lo0 starts multicasting
>> l2cap_chan_send 6lo0 dst: 5c:f3:70:68:c1:76 type 1 src: 5c:f3:70:6b:3b:1f chan f62aac00
>> transmit return value -11
>> send 6lo0 failed chan f62aac00
>> xmit ndisc 6lo0 dst: ff:ff:ff:ff:ff:ff src: 5c:f3:70:6b:3b:1f
>> xmit 6lo0 starts multicasting
>> l2cap_chan_send 6lo0 dst: 5c:f3:70:68:c1:76 type 1 src: 5c:f3:70:6b:3b:1f chan f62aac00
>> transmit return value -11
>> send 6lo0 failed chan f62aac00
>
> Are there any calls to channel resume?
>
I also activate debug in l2cap_sock.c now. I captued on node which calls
the ping6 with high payload only.
The first dump shows:
- Queueing lot of IPv6 fragments, because -s 60000
- L2CAP sends first fragment "return value 1246" and drop all others.
This seems to be normal behaviour.
Shown as:
lowpan_chan_resume: chan f54c1800
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 2
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 3
...
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 50
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
chan f54c1800, msg f686be64, len 1246
chan f54c1800 len 228
chan f54c1800 len 230
chan f54c1800 len 230
chan f54c1800 len 230
chan f54c1800 len 230
chan f54c1800 len 98
chan f54c1800, skb f61bda00 len 234 priority 0
transmit return value 1246
chan f54c1800 orig refcnt 51
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 50
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
...
MANY -EAGAIN stuff until xmit is finished on L3 side.
The first fragment (IPv6 fragment!) "len 1246" was sent successful. All
others are -EAGAIN, because channel is full (tx_credits is 0).
---
Now after that L3 wants to sent another "ping request", but there is no
"resume" callback before. Seems there is also some NS/NA messages in the
middle, but should not be important.
The different here, no "resume callback" anymore and sending first
fragment (IPv6) on l2cap_chan_send will end directly in -EAGAIN, and I
cannot transmit anything anymore.
Shown in:
chan f54c1800 orig refcnt 3
conn f6183400 len 27 flags 0x2
Start: total len 69, frag len 27
conn f6183400 len 27 flags 0x1
Cont: frag len 27 (expecting 42)
conn f6183400 len 15 flags 0x1
Cont: frag len 15 (expecting 15)
len 65, cid 0x0040
chan f54c1800, len 65
rx_credits 10 -> 9
Start of new SDU. sdu_len 63 skb->len 63 imtu 1280
recv 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
conn f6183400 len 27 flags 0x2
Start: total len 77, frag len 27
conn f6183400 len 27 flags 0x1
Cont: frag len 27 (expecting 50)
conn f6183400 len 23 flags 0x1
Cont: frag len 23 (expecting 23)
len 73, cid 0x0040
chan f54c1800, len 73
rx_credits 9 -> 8
Start of new SDU. sdu_len 71 skb->len 71 imtu 1280
recv 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 2
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 3
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
...
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 50
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 51
...
MANY -EAGAIN stuff until xmit is finished on L3 side.
Here it directly starts at the first transmit with returning "-11".
---
I didn't capture now the other side. If you like I can also do that, I
think this one shows enough. In the middle of the two log snippets I run
somehow into the deadlock.
Here the complete log which shows the behaviour
- chan resume called
- lowpan xmit callback will start lot of xmit workers (maybe can be optimized,
that we don't queue stuff because queue is full... but I would try
everything and let l2cap_chan_send decide at least, this is more the
way to say "send what we can")
- Then first fragment (IPv6) will be send, after that all IPv6
fragments sends -EAGAIN.
- Next ping request will be send.
- important here, no resume callback occured
- I am stucked on this node on -EAGAIN, the other node will follow
to stuck in -EAGAIN.
:
lowpan_chan_resume: chan f54c1800
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 2
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 3
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 4
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 5
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 6
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 7
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 8
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 9
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 10
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 11
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 12
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 13
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 14
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 15
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 16
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 17
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 18
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 19
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 20
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 21
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 22
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 23
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 24
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 25
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 26
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 27
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 28
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 29
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 30
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 31
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 32
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 33
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 34
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 35
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 36
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 37
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 38
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 39
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 40
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 41
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 42
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 43
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 44
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 45
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 46
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 47
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 48
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 49
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 50
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
chan f54c1800, msg f686be64, len 1246
chan f54c1800 len 228
chan f54c1800 len 230
chan f54c1800 len 230
chan f54c1800 len 230
chan f54c1800 len 230
chan f54c1800 len 98
chan f54c1800, skb f61bda00 len 234 priority 0
transmit return value 1246
chan f54c1800 orig refcnt 51
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 50
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 49
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 48
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 47
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 46
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 45
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 44
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 43
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 42
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 41
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 40
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 39
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 38
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 37
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 36
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 35
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 34
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 33
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 32
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 31
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 30
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 29
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 28
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 27
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 26
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 25
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 24
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 23
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 22
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 21
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 20
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 19
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 18
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 17
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 16
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 15
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 14
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 13
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 12
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 11
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 10
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 9
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 8
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 7
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 6
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 5
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 4
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 3
conn f6183400 len 27 flags 0x2
Start: total len 69, frag len 27
conn f6183400 len 27 flags 0x1
Cont: frag len 27 (expecting 42)
conn f6183400 len 15 flags 0x1
Cont: frag len 15 (expecting 15)
len 65, cid 0x0040
chan f54c1800, len 65
rx_credits 10 -> 9
Start of new SDU. sdu_len 63 skb->len 63 imtu 1280
recv 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
conn f6183400 len 27 flags 0x2
Start: total len 77, frag len 27
conn f6183400 len 27 flags 0x1
Cont: frag len 27 (expecting 50)
conn f6183400 len 23 flags 0x1
Cont: frag len 23 (expecting 23)
len 73, cid 0x0040
chan f54c1800, len 73
rx_credits 9 -> 8
Start of new SDU. sdu_len 71 skb->len 71 imtu 1280
recv 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 2
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 3
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 2
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 3
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 4
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 5
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 6
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 7
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 8
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 9
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 10
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 11
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 12
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 13
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 14
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 15
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 16
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 17
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 18
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 19
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 20
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 21
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 22
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 23
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 24
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 25
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 26
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 27
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 28
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 29
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 30
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 31
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 32
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 33
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 34
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 35
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 36
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 37
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 38
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 39
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 40
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 41
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 42
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 43
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 44
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 45
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 46
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 47
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 48
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 49
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 50
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 51
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 50
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 49
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 48
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 47
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 46
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 45
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 44
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 43
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 42
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 41
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 40
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 39
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 38
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 37
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 36
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 35
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 34
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 33
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 32
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 31
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 30
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 29
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 28
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 27
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 26
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 25
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 24
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 23
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 22
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 21
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 20
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 19
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 18
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 17
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 16
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 15
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 14
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 13
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 12
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 11
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 10
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 9
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 8
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 7
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 6
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 5
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 4
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 3
conn f6183400 len 27 flags 0x2
Start: total len 77, frag len 27
conn f6183400 len 27 flags 0x1
Cont: frag len 27 (expecting 50)
conn f6183400 len 23 flags 0x1
Cont: frag len 23 (expecting 23)
len 73, cid 0x0040
chan f54c1800, len 73
rx_credits 8 -> 7
Start of new SDU. sdu_len 71 skb->len 71 imtu 1280
recv 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
chan f54c1800 orig refcnt 2
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f54c1800
transmit return value -11
send 6lo0 failed chan f54c1800
chan f54c1800 orig refcnt 3
xmit ndisc 6lo0 dst: 5c:f3:70:6b:3b:1f src: 5c:f3:70:68:c1:76
>> ---
>>
>> They simple show the same stuff, but with different addresses.
>>
>> I rethink about your words and I suppose you told me that tx_credits and
>> -EAGAIN are normal that this stuff occurs.
>>
>> I agree, It also occurs before when I run:
>>
>> ping6 $IP_NODEB%6lo0 -s 60000
>>
>> which is normal. But what should not happen is that there is some little
>> tiny window (race) that both tx_credits are reached to zero and then
>> nobody transmit anything anymore, that's the tx deadlock which I hit here.
>>
>> And this deadlock occurs that I killed the L2CAP chan connection. I
>> think it will work when I create a new chan (because new tx_credits).
>> The connection isn't broken, I just can not transmit anything because tx_credits.
>
> If there is no calls to channel resume then yes there is a problem
> that no tx_credits would be able to be sent, note though that the
> command the restore credits does happen in the signalling channel not
> in the data channel so there should be anything blocking from
So these are complete separate transmission? Could it be that, because I
do much things on the air that such "tx_credits command" stuff could not
be reached to the other side? Then the complete mechanism can not be
working and maybe need some recovery if no "tx_credits command reached"
some time ago to solve this deadlock?
I am not a bluetooth expert, maybe there exists nicer solutions. :-)
> receiving those, perhaps looking into what is happening the last time
> it receives credits and the sequence of calls it generate.
>
See above, I hope that helps.
- Alex
Hi Alex,
On Wed, Jul 13, 2016 at 12:12 PM, Alexander Aring <[email protected]> wrote:
>
> Hi,
>
> On 07/12/2016 08:35 PM, Alexander Aring wrote:
>>
>> Hi,
>>
>> On 07/12/2016 04:51 PM, Luiz Augusto von Dentz wrote:
>> ...
>>>>
>>>> HOW TO REPRODUCE:
>>>>
>>>> It's simple, do some high payloads which makes lot tx/rcv traffic on both sides:
>>>>
>>>> Node A:
>>>>
>>>> ping6 $IP_NODEB%6lo0 -s 60000
>>>>
>>>> Node B:
>>>>
>>>> ping6 ff02::1%6lo0
>>>
>>> Im not sure I understand what you are trying to do with a packet of
>>> 60Kb, the l2cap_chan can most likely only do 1280 bytes and even with
>>
>> 60Kb is just some incredible high value to produce on both sides tx/rx
>> data. The other side will normally send some "defragmentation failure"
>> icmp messages.
>>
>> This value is just to produce the high traffic, not practice payload.
>>
>>> fragmentation this will consume the credits quicker than we can
>>> receive more so it will eventually starting buffering until it gets
>>> stuck. Note that it is probably still receiving credits but we
>>
>> This is exactly what I like to do. Produce a lot of data to kill L3
>> layer, but this should not kill the L2 layer. Killing the L2 layer is
>> what's happend here.
>>
>> If I do a lot of data on e.g. tcp traffic and the tcp connection will be
>> closed -> that's fine for me but afterwards starting a new L3 connection
>> should be still working. This is not the case here, I can kill L2 layer
>> and nothing works anymore, because L2 running into deadlock.
>>
>>> probably have so much data buffered that all the credits are consumed
>>> almost immediately after receiving, or you don't see -EAGAIN more than
>>> once?
>>>
>>
>> My example shows that we transmit some data on both nodes, that is
>> important to check the l2cap_chan_send return type.
>>
>> When I do that above and running into the deadlock the IPv6 connection
>> runs "NS" messages only, because it still can send on L3 layer but the
>> L2 layer is killed -> I get -EAGAIN in l2cap_chan_send always on both
>> nodes because tx_credits are on both nodes zero and will not be
>> incremented again.
>>
>> So far I know these tx_credits will be incremented only if somebody
>> sends something again, right? This will never be the case again and I am
>> stucked in a deadlock which should not be there.
>>
>> to your question:
>>
>> "or you don't see -EAGAIN more than once"
>>
>> I see -EAGAIN always at calling l2cap_chan_send on both nodes. I think I
>> can wait forever, after an year I still see "-EAGAIN" when calling
>> l2cap_chan_send.
>>
>> Nevertheless, I will try to hit the deadlock (-EAGAIN) on both nodes so
>> nobody transmits anything anymore. Then waiting six hours and look if
>> still -EAGAIN is there on both nodes. I will report again here, after
>> that time it should working again, but I don't believe that.
>>
>
> I waited about ~ 6hours, deadlock was still there.
>
> Node A:
>
> transmit return value -11
> send 6lo0 failed chan f617cc00
> xmit ndisc 6lo0 dst: ff:ff:ff:ff:ff:ff src: 5c:f3:70:68:c1:76
> xmit 6lo0 starts multicasting
> l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f617cc00
> transmit return value -11
> send 6lo0 failed chan f617cc00
> xmit ndisc 6lo0 dst: ff:ff:ff:ff:ff:ff src: 5c:f3:70:68:c1:76
> xmit 6lo0 starts multicasting
> l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f617cc00
> transmit return value -11
> send 6lo0 failed chan f617cc00
> xmit ndisc 6lo0 dst: ff:ff:ff:ff:ff:ff src: 5c:f3:70:68:c1:76
> xmit 6lo0 starts multicasting
> l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f617cc00
> transmit return value -11
> send 6lo0 failed chan f617cc00
> xmit ndisc 6lo0 dst: ff:ff:ff:ff:ff:ff src: 5c:f3:70:68:c1:76
> xmit 6lo0 starts multicasting
> l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f617cc00
> transmit return value -11
> send 6lo0 failed chan f617cc00
>
> Node B:
>
> xmit ndisc 6lo0 dst: ff:ff:ff:ff:ff:ff src: 5c:f3:70:6b:3b:1f
> xmit 6lo0 starts multicasting
> l2cap_chan_send 6lo0 dst: 5c:f3:70:68:c1:76 type 1 src: 5c:f3:70:6b:3b:1f chan f62aac00
> transmit return value -11
> send 6lo0 failed chan f62aac00
> xmit ndisc 6lo0 dst: ff:ff:ff:ff:ff:ff src: 5c:f3:70:6b:3b:1f
> xmit 6lo0 starts multicasting
> l2cap_chan_send 6lo0 dst: 5c:f3:70:68:c1:76 type 1 src: 5c:f3:70:6b:3b:1f chan f62aac00
> transmit return value -11
> send 6lo0 failed chan f62aac00
> xmit ndisc 6lo0 dst: ff:ff:ff:ff:ff:ff src: 5c:f3:70:6b:3b:1f
> xmit 6lo0 starts multicasting
> l2cap_chan_send 6lo0 dst: 5c:f3:70:68:c1:76 type 1 src: 5c:f3:70:6b:3b:1f chan f62aac00
> transmit return value -11
> send 6lo0 failed chan f62aac00
Are there any calls to channel resume?
> ---
>
> They simple show the same stuff, but with different addresses.
>
> I rethink about your words and I suppose you told me that tx_credits and
> -EAGAIN are normal that this stuff occurs.
>
> I agree, It also occurs before when I run:
>
> ping6 $IP_NODEB%6lo0 -s 60000
>
> which is normal. But what should not happen is that there is some little
> tiny window (race) that both tx_credits are reached to zero and then
> nobody transmit anything anymore, that's the tx deadlock which I hit here.
>
> And this deadlock occurs that I killed the L2CAP chan connection. I
> think it will work when I create a new chan (because new tx_credits).
> The connection isn't broken, I just can not transmit anything because tx_credits.
If there is no calls to channel resume then yes there is a problem
that no tx_credits would be able to be sent, note though that the
command the restore credits does happen in the signalling channel not
in the data channel so there should be anything blocking from
receiving those, perhaps looking into what is happening the last time
it receives credits and the sequence of calls it generate.
--
Luiz Augusto von Dentz
Hi,
On 07/12/2016 08:35 PM, Alexander Aring wrote:
>
> Hi,
>
> On 07/12/2016 04:51 PM, Luiz Augusto von Dentz wrote:
> ...
>>>
>>> HOW TO REPRODUCE:
>>>
>>> It's simple, do some high payloads which makes lot tx/rcv traffic on both sides:
>>>
>>> Node A:
>>>
>>> ping6 $IP_NODEB%6lo0 -s 60000
>>>
>>> Node B:
>>>
>>> ping6 ff02::1%6lo0
>>
>> Im not sure I understand what you are trying to do with a packet of
>> 60Kb, the l2cap_chan can most likely only do 1280 bytes and even with
>
> 60Kb is just some incredible high value to produce on both sides tx/rx
> data. The other side will normally send some "defragmentation failure"
> icmp messages.
>
> This value is just to produce the high traffic, not practice payload.
>
>> fragmentation this will consume the credits quicker than we can
>> receive more so it will eventually starting buffering until it gets
>> stuck. Note that it is probably still receiving credits but we
>
> This is exactly what I like to do. Produce a lot of data to kill L3
> layer, but this should not kill the L2 layer. Killing the L2 layer is
> what's happend here.
>
> If I do a lot of data on e.g. tcp traffic and the tcp connection will be
> closed -> that's fine for me but afterwards starting a new L3 connection
> should be still working. This is not the case here, I can kill L2 layer
> and nothing works anymore, because L2 running into deadlock.
>
>> probably have so much data buffered that all the credits are consumed
>> almost immediately after receiving, or you don't see -EAGAIN more than
>> once?
>>
>
> My example shows that we transmit some data on both nodes, that is
> important to check the l2cap_chan_send return type.
>
> When I do that above and running into the deadlock the IPv6 connection
> runs "NS" messages only, because it still can send on L3 layer but the
> L2 layer is killed -> I get -EAGAIN in l2cap_chan_send always on both
> nodes because tx_credits are on both nodes zero and will not be
> incremented again.
>
> So far I know these tx_credits will be incremented only if somebody
> sends something again, right? This will never be the case again and I am
> stucked in a deadlock which should not be there.
>
> to your question:
>
> "or you don't see -EAGAIN more than once"
>
> I see -EAGAIN always at calling l2cap_chan_send on both nodes. I think I
> can wait forever, after an year I still see "-EAGAIN" when calling
> l2cap_chan_send.
>
> Nevertheless, I will try to hit the deadlock (-EAGAIN) on both nodes so
> nobody transmits anything anymore. Then waiting six hours and look if
> still -EAGAIN is there on both nodes. I will report again here, after
> that time it should working again, but I don't believe that.
>
I waited about ~ 6hours, deadlock was still there.
Node A:
transmit return value -11
send 6lo0 failed chan f617cc00
xmit ndisc 6lo0 dst: ff:ff:ff:ff:ff:ff src: 5c:f3:70:68:c1:76
xmit 6lo0 starts multicasting
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f617cc00
transmit return value -11
send 6lo0 failed chan f617cc00
xmit ndisc 6lo0 dst: ff:ff:ff:ff:ff:ff src: 5c:f3:70:68:c1:76
xmit 6lo0 starts multicasting
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f617cc00
transmit return value -11
send 6lo0 failed chan f617cc00
xmit ndisc 6lo0 dst: ff:ff:ff:ff:ff:ff src: 5c:f3:70:68:c1:76
xmit 6lo0 starts multicasting
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f617cc00
transmit return value -11
send 6lo0 failed chan f617cc00
xmit ndisc 6lo0 dst: ff:ff:ff:ff:ff:ff src: 5c:f3:70:68:c1:76
xmit 6lo0 starts multicasting
l2cap_chan_send 6lo0 dst: 5c:f3:70:6b:3b:1f type 1 src: 5c:f3:70:68:c1:76 chan f617cc00
transmit return value -11
send 6lo0 failed chan f617cc00
Node B:
xmit ndisc 6lo0 dst: ff:ff:ff:ff:ff:ff src: 5c:f3:70:6b:3b:1f
xmit 6lo0 starts multicasting
l2cap_chan_send 6lo0 dst: 5c:f3:70:68:c1:76 type 1 src: 5c:f3:70:6b:3b:1f chan f62aac00
transmit return value -11
send 6lo0 failed chan f62aac00
xmit ndisc 6lo0 dst: ff:ff:ff:ff:ff:ff src: 5c:f3:70:6b:3b:1f
xmit 6lo0 starts multicasting
l2cap_chan_send 6lo0 dst: 5c:f3:70:68:c1:76 type 1 src: 5c:f3:70:6b:3b:1f chan f62aac00
transmit return value -11
send 6lo0 failed chan f62aac00
xmit ndisc 6lo0 dst: ff:ff:ff:ff:ff:ff src: 5c:f3:70:6b:3b:1f
xmit 6lo0 starts multicasting
l2cap_chan_send 6lo0 dst: 5c:f3:70:68:c1:76 type 1 src: 5c:f3:70:6b:3b:1f chan f62aac00
transmit return value -11
send 6lo0 failed chan f62aac00
---
They simple show the same stuff, but with different addresses.
I rethink about your words and I suppose you told me that tx_credits and
-EAGAIN are normal that this stuff occurs.
I agree, It also occurs before when I run:
ping6 $IP_NODEB%6lo0 -s 60000
which is normal. But what should not happen is that there is some little
tiny window (race) that both tx_credits are reached to zero and then
nobody transmit anything anymore, that's the tx deadlock which I hit here.
And this deadlock occurs that I killed the L2CAP chan connection. I
think it will work when I create a new chan (because new tx_credits).
The connection isn't broken, I just can not transmit anything because tx_credits.
- Alex
Hi,
On 07/11/2016 09:50 PM, Alexander Aring wrote:
> This patch adds a new btle 6lowpan implementation in version 0.2. This
> new implementation has a new userspace API for debugfs.
>
> It's now possible to decide on which hci interface do you want to run
> your 6LoWPAN interface, for that you can run:
>
> echo "hci0 1" > /sys/kernel/debug/bluetooth/6lowpan_enable
>
> to enable, or:
>
> echo "hci0 0" > /sys/kernel/debug/bluetooth/6lowpan_enable
>
> to disable it. A cat on this file will show the current hci <-> 6lowpan
> interface mapping.
>
> Additional the different is, that the interface will be created after
> enable it. The old behaviour is that the interface will be created when
> one first connection is established and deleted after all connections
> (peers) will be disconnected. This handling is different now.
>
> The reason (in my opinion) is that the existing of such interface should
> not based on if there is a connection or not. At runtime of many IPv6
> application an interface will be removed -> I think the most userspace
> application can and will never handle that an IPv6 interface will be
> deleted and recreating during runtime.
>
> The new handling is that the interface will not removed and you can
> re-establish the connection to your peers.
>
> Connection to peers:
>
> Connection to peers are not binded to hci interface, it's binded to your
> 6LoWPAN interface. This will prepare handling for multiple 6LoWPAN
> interfaces on one hci (maybe also with netns support, which isn't
> available yet) and you can choose which peers should be handled by
> different BTLE 6LoWPAN interfaces. Example:
>
> L2: hci0 is connected to node A, B, C, D
>
> L3: 6lo0 and 6lo1 are two interfaces based on hci0.
> 6lo0 is connected to nodes: A, B, C
> 6lo1 is connected to nodes: D, C, B
>
> as one possible example. Handle this peer interface will have a possible
> more fine-granularity connection for peers.
>
> To connect to peers, there exists no "6lowpan_control" anymore. The
> control file is now peer interface, it's simple:
>
> echo "connect $PEER_ADDRESS 1" > /sys/kernel/debug/bluetooth/6lo0
>
> Also after connecting to peers it's still possible that another peer can
> to connect to the peer which has already a connected peer (complicated
> to describe). I saw that is somehow disabled in version before, see:
>
> http://lxr.free-electrons.com/source/net/bluetooth/6lowpan.c#L1247
>
> I am not a bluetooth expert, but I guess that is somehow if there exists
> limitation to devices which are not "peers" that means devices which can
> be run as master or slave only. So far I know such limitations for
> devices doesn't exists for BTLE 6LoWPAN, or?
>
I think the following will explain somehow when it's a SLAVE and when
MASTER as my current view.
SLAVES:
They don't makes this echo "connect ..." foo in control files.
they only do "hciconfig hci0 leadv" that somebody other can connect to
that node (some MASTER node).
MASTERS:
can do the same what the SLAVES does but to be as MASTER they need to call
additional stuff:
hcitool lecc $SLAVE_ADDR (one or more times)
Additional for different lowpan interfaces (don't need the same addresses
which was used by previous one or more hcitool lecc calls):
echo "connect SLAVE_ADDR 1" > /sys/kernel/debug/bluetooth/6lo0
is this a correct view and can all BTLE transceivers run as SLAVE or
MASTER, or exists there some special transceivers outside which can be
run as SLAVE only?
> Nevertheless I disabled that stuff, it's still possible to connect to
> some node which already run "connect ..." on his side.
>
> Otherwise I fixed the ndisc stuff and removed a lot of races/side
> effects. The old code had some static "lowpan_devices" list and most
> time there was some lookup mapping according to bdaddr to get the right
> 6lo netdevice struct. I removed that, also the skb->cb is used a lot in
> some callback which had some side-effects.
>
> Signed-off-by: Alexander Aring <[email protected]>
> ---
> include/net/bluetooth/hci_core.h | 10 +
> net/bluetooth/6lowpan.c | 1015 ++++++++++++++++++++++++++++++++++++++
> net/bluetooth/Makefile | 3 +
> 3 files changed, 1028 insertions(+)
> create mode 100644 net/bluetooth/6lowpan.c
>
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index a37027a..37a2256 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -399,6 +399,16 @@ struct hci_dev {
>
> struct led_trigger *power_led;
>
> +#if IS_ENABLED(CONFIG_BT_6LOWPAN)
> + /* TODO for netns support this need to be a list
> + * variables are protected by RTNL here
> + */
> + struct net_device *ldev;
> + bool lowpan_enable;
> + /* delete lowpan iface via debugfs workaround */
> + bool pending_deletion;
> +#endif
> +
> int (*open)(struct hci_dev *hdev);
> int (*close)(struct hci_dev *hdev);
> int (*flush)(struct hci_dev *hdev);
> diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
> new file mode 100644
> index 0000000..a8a3410
> --- /dev/null
> +++ b/net/bluetooth/6lowpan.c
> @@ -0,0 +1,1015 @@
> +/*
> + (C) 2016 Pengutronix, Alexander Aring <[email protected]>
> + Copyright (c) 2013-2014 Intel Corp.
> +
> + This program is free software; you can redistribute it and/or modify
> + it under the terms of the GNU General Public License version 2 and
> + only version 2 as published by the Free Software Foundation.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +*/
> +
> +#include <linux/if_arp.h>
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <linux/module.h>
> +#include <linux/debugfs.h>
> +
> +#include <net/ipv6.h>
> +#include <net/ip6_route.h>
> +#include <net/addrconf.h>
> +
> +#include <net/bluetooth/bluetooth.h>
> +#include <net/bluetooth/hci_core.h>
> +#include <net/bluetooth/l2cap.h>
> +
> +#include <net/6lowpan.h>
> +
> +#define LOWPAN_BTLE_VERSION "0.2"
> +
> +#define lowpan_le48_to_be48(dst, src) baswap((bdaddr_t *)dst, (bdaddr_t *)src)
> +#define lowpan_be48_to_le48(dst, src) baswap((bdaddr_t *)dst, (bdaddr_t *)src)
> +
> +struct lowpan_btle_cb {
> + struct l2cap_chan *chan;
> +};
> +
> +struct lowpan_btle_dev {
> + struct hci_dev *hdev;
> + struct workqueue_struct *workqueue;
> + struct l2cap_chan *listen;
> + struct list_head peers;
> + struct dentry *control;
> +};
> +
> +struct lowpan_peer {
> + struct l2cap_chan *chan;
> +
> + struct list_head list;
> +};
> +
> +struct lowpan_chan_data {
> + struct lowpan_peer peer;
> + struct net_device *dev;
> +};
> +
> +struct lowpan_xmit_work {
> + struct work_struct work;
> + struct l2cap_chan *chan;
> + struct net_device *dev;
> + unsigned int true_len;
> + struct sk_buff *skb;
> +};
> +
> +static inline struct lowpan_btle_cb *
> +lowpan_btle_cb(const struct sk_buff *skb)
> +{
> + return (struct lowpan_btle_cb *)skb->cb;
> +}
> +
> +static inline struct lowpan_chan_data *
> +lowpan_chan_data(const struct l2cap_chan *chan)
> +{
> + return (struct lowpan_chan_data *)chan->data;
> +}
> +
> +static inline struct lowpan_btle_dev *
> +lowpan_btle_dev(const struct net_device *dev)
> +{
> + return (struct lowpan_btle_dev *)lowpan_dev(dev)->priv;
> +}
> +
> +static inline struct lowpan_peer *
> +lowpan_lookup_peer(struct lowpan_btle_dev *btdev, bdaddr_t *addr)
> +{
> + struct lowpan_peer *entry;
> +
> + list_for_each_entry_rcu(entry, &btdev->peers, list) {
> + if (!bacmp(&entry->chan->dst, addr))
> + return entry;
> + }
> +
> + return NULL;
> +}
> +
> +static int lowpan_give_skb_to_device(struct sk_buff *skb)
> +{
> + skb->protocol = htons(ETH_P_IPV6);
> + skb->dev->stats.rx_packets++;
> + skb->dev->stats.rx_bytes += skb->len;
> +
> + return netif_rx_ni(skb);
> +}
> +
> +static int lowpan_rx_handlers_result(struct sk_buff *skb, lowpan_rx_result res)
> +{
> + switch (res) {
> + case RX_CONTINUE:
> + /* nobody cared about this packet */
> + net_warn_ratelimited("%s: received unknown dispatch\n",
> + __func__);
that printout isn't corrected, for simplification I drop all non iphc
frames at recv callback.
I want to have this receive handling into "net/6lowpan" but I want to
not doing this now in this patch-series. It will prepare it.
After moving it to "net/6lowpan" unknown dispatches should be really
dispatches which are not known in the current state of implementation.
I think it's okay to leave this right there as it is, also somebody can
trigger this warning by sending lot of frames with the right dispatch,
not sure if that's normal that somebody from outside can trigger a lot
of system messages which are show up in dmesg. Need to think about it,
maybe some "ONCE" mechanism or using debug stuff.
> +
> + /* fall-through */
> + case RX_DROP_UNUSABLE:
> + kfree_skb(skb);
> +
> + /* fall-through */
> + case RX_DROP:
> + return NET_RX_DROP;
> + case RX_QUEUED:
> + return lowpan_give_skb_to_device(skb);
> + default:
> + break;
> + }
> +
> + return NET_RX_DROP;
> +}
> +
> +static lowpan_rx_result lowpan_rx_h_iphc(struct sk_buff *skb)
> +{
> + struct l2cap_chan *chan = lowpan_btle_cb(skb)->chan;
> + bdaddr_t daddr, saddr;
> + int ret;
> +
> + if (!lowpan_is_iphc(*skb_network_header(skb)))
> + return RX_CONTINUE;
> +
> + BT_DBG("recv %s dst: %pMR type %d src: %pMR chan %p",
> + skb->dev->name, &chan->dst, chan->dst_type, &chan->src, chan);
> +
> + /* bluetooth chan view is vice-versa */
> + bacpy(&daddr, &chan->src);
> + bacpy(&saddr, &chan->dst);
> +
> + ret = lowpan_header_decompress(skb, skb->dev, &daddr, &saddr);
> + if (ret < 0)
> + return RX_DROP_UNUSABLE;
> +
> + return RX_QUEUED;
> +}
> +
> +static int lowpan_invoke_rx_handlers(struct sk_buff *skb)
> +{
> + lowpan_rx_result res;
> +
> +#define CALL_RXH(rxh) \
> + do { \
> + res = rxh(skb); \
> + if (res != RX_CONTINUE) \
> + goto rxh_next; \
> + } while (0)
> +
> + /* likely at first */
> + CALL_RXH(lowpan_rx_h_iphc);
> +
> +rxh_next:
> + return lowpan_rx_handlers_result(skb, res);
> +#undef CALL_RXH
> +}
> +
> +static int lowpan_chan_recv(struct l2cap_chan *chan, struct sk_buff *skb)
> +{
> + struct lowpan_chan_data *data = lowpan_chan_data(chan);
> + struct net_device *dev = data->dev;
> + int ret;
> +
> + /* TODO handle BT_CONNECTED in bluetooth subsytem? on
> + * when calling recv callback, I hit that case somehow
> + */
> + if (!netif_running(dev) || chan->state != BT_CONNECTED ||
> + !skb->len || !lowpan_is_iphc(skb->data[0]))
> + goto drop;
> +
> + /* Replacing skb->dev and followed rx handlers will manipulate skb. */
> + skb = skb_unshare(skb, GFP_ATOMIC);
can be GFP_KERNEL here.
> + if (!skb)
> + goto out;
> +
> + skb->dev = dev;
> + skb_reset_network_header(skb);
> +
> + /* remember that one for dst bdaddr. TODO handle that as priv data for
> + * lowpan_invoke_rx_handlers parameter. Not necessary for skb->cb.
> + */
After fixing that, skb->cb isn't needed anymore in this implementation.
But I will do that when moving to receive handling to "net/6lowpan".
> + lowpan_btle_cb(skb)->chan = chan;
> +
> + ret = lowpan_invoke_rx_handlers(skb);
> + if (ret == NET_RX_DROP)
> + BT_DBG("recv %s dropped chan %p", skb->dev->name, chan);
> +
> + return 0;
> +
> +drop:
> + kfree_skb(skb);
> +out:
> + /* we handle to free skb on error, so must 0
> + * seems that callback free the skb on error
> + * otherwise.
> + */
btw:
This is also a MUST to return 0 here always. Because netif_rx_ni will handle
also freeing on error and we cannot change this handling. (Or we doing
some crazy reference counting before foo, but will more confuse
everybody).
> + return 0;
> +}
> +
> +static void lowpan_xmit_worker(struct work_struct *work)
> +{
> + struct lowpan_btle_dev *btdev;
> + struct lowpan_xmit_work *xw;
> + struct l2cap_chan *chan;
> + struct net_device *dev;
> + struct msghdr msg = { };
> + struct kvec iv;
> + int ret;
> +
> + xw = container_of(work, struct lowpan_xmit_work, work);
> + dev = xw->dev;
> + chan = xw->chan;
> + btdev = lowpan_btle_dev(dev);
> +
> + iv.iov_base = xw->skb->data;
> + iv.iov_len = xw->skb->len;
> + iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, &iv, 1, xw->skb->len);
> +
> + BT_DBG("l2cap_chan_send %s dst: %pMR type %d src: %pMR chan %p",
> + dev->name, &chan->dst, chan->dst_type, &chan->src, chan);
> +
> + l2cap_chan_lock(chan);
> +
> + ret = l2cap_chan_send(chan, &msg, xw->skb->len);
> + BT_DBG("transmit return value %d", ret);
> + if (ret < 0) {
> + BT_DBG("send %s failed chan %p", dev->name, chan);
> + kfree_skb(xw->skb);
> + } else {
> + consume_skb(xw->skb);
> + dev->stats.tx_bytes += xw->true_len;
> + dev->stats.tx_packets++;
> + }
> +
> + l2cap_chan_unlock(chan);
> + l2cap_chan_put(chan);
> +
> + kfree(xw);
> +}
> +
> +static void lowpan_send_unicast_pkt(struct net_device *dev,
> + struct l2cap_chan *chan,
> + struct sk_buff *skb,
> + unsigned int true_len)
> +{
> + struct lowpan_xmit_work *xw;
> +
> + /* copy to xmit work buffer */
> + xw = kzalloc(sizeof(*xw), GFP_ATOMIC);
> + if (!xw)
> + return;
> +
> + /* chan->lock mutex need to be hold so change context to workqueue */
> + INIT_WORK(&xw->work, lowpan_xmit_worker);
> + xw->true_len = true_len;
> + /* freeing protected by ifdown workqueue sync */
> + xw->dev = dev;
> + /* disallow freeing of skb while context switch */
> + xw->skb = skb_get(skb);
> + /* disallow freeing while context switch */
> + l2cap_chan_hold(chan);
> + xw->chan = chan;
> +
> + queue_work(lowpan_btle_dev(dev)->workqueue, &xw->work);
> +}
> +
> +static void lowpan_send_mcast_pkt(struct net_device *dev, struct sk_buff *skb,
> + unsigned int true_len)
> +{
> + struct lowpan_btle_dev *btdev = lowpan_btle_dev(dev);
> + struct lowpan_peer *peer;
> +
> + rcu_read_lock();
> +
> + BT_DBG("xmit %s starts multicasting", dev->name);
> +
> + /* We need to send the packet to every device behind this
> + * interface, because multicasting.
> + */
> + list_for_each_entry_rcu(peer, &btdev->peers, list)
> + lowpan_send_unicast_pkt(dev, peer->chan, skb, true_len);
> +
> + rcu_read_unlock();
> +}
> +
> +static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> + struct lowpan_addr_info *info = lowpan_addr_info(skb);
> + struct lowpan_btle_dev *btdev = lowpan_btle_dev(dev);
> + unsigned int true_len = skb->len;
> + struct lowpan_peer *peer;
> + bdaddr_t daddr, saddr;
> + int ret;
> +
> + /* We must take a copy of the skb before we modify/replace the ipv6
> + * header as the header could be used elsewhere.
> + */
> + skb = skb_unshare(skb, GFP_ATOMIC);
> + if (!skb) {
> + kfree_skb(skb);
> + goto out;
> + }
> +
> + lowpan_be48_to_le48(&daddr, info->daddr);
> + lowpan_be48_to_le48(&saddr, info->saddr);
> +
> + BT_DBG("xmit ndisc %s dst: %pMR src: %pMR",
> + dev->name, &daddr, &saddr);
> +
> + ret = lowpan_header_compress(skb, dev, &daddr, &saddr);
> + if (ret < 0) {
> + kfree_skb(skb);
> + goto out;
> + }
> +
> + /* this should never be the case, otherwise iphc is broken */
> + WARN_ON_ONCE(skb->len > dev->mtu);
> +
> + if (!memcmp(&daddr, dev->broadcast, dev->addr_len)) {
> + lowpan_send_mcast_pkt(dev, skb, true_len);
> + } else {
> + btdev = lowpan_btle_dev(dev);
> +
btdev = can be removed here.
> + rcu_read_lock();
> +
> + peer = lowpan_lookup_peer(btdev, &daddr);
> + if (peer)
> + lowpan_send_unicast_pkt(dev, peer->chan, skb,
> + true_len);
> +
> + rcu_read_unlock();
> + }
> +
> + consume_skb(skb);
> +
> +out:
> + return NETDEV_TX_OK;
> +}
> +
> +static int lowpan_stop(struct net_device *dev)
> +{
> + struct lowpan_btle_dev *btdev = lowpan_btle_dev(dev);
> +
> + /* synchronize with xmit worker */
> + flush_workqueue(btdev->workqueue);
> + return 0;
> +}
> +
> +static struct sk_buff *lowpan_chan_alloc_skb(struct l2cap_chan *chan,
> + unsigned long hdr_len,
> + unsigned long len, int nb)
> +{
> + return bt_skb_alloc(hdr_len + len, GFP_KERNEL);
> +}
> +
> +static long lowpan_chan_get_sndtimeo(struct l2cap_chan *chan)
> +{
> + return L2CAP_CONN_TIMEOUT;
> +}
> +
> +static struct l2cap_chan *lowpan_chan_create(struct net_device *dev)
> +{
> + struct lowpan_chan_data *data;
> + struct l2cap_chan *chan;
> +
> + chan = l2cap_chan_create();
> + if (!chan)
> + return ERR_PTR(-ENOMEM);
> +
> + data = kmalloc(sizeof(*data), GFP_KERNEL);
> + if (!data) {
> + l2cap_chan_put(chan);
> + return ERR_PTR(-ENOMEM);
> + }
> +
> + l2cap_chan_set_defaults(chan);
> + chan->chan_type = L2CAP_CHAN_CONN_ORIENTED;
> + chan->mode = L2CAP_MODE_LE_FLOWCTL;
> + chan->imtu = dev->mtu;
> + chan->data = data;
> +
> + data->peer.chan = chan;
> + data->dev = dev;
> +
> + return chan;
> +}
> +
> +static struct l2cap_chan *lowpan_chan_new_conn(struct l2cap_chan *pchan)
> +{
> + struct lowpan_chan_data *data = lowpan_chan_data(pchan);
> + struct l2cap_chan *chan;
> +
> + chan = lowpan_chan_create(data->dev);
> + if (IS_ERR(chan))
> + return NULL;
> +
> + chan->ops = pchan->ops;
> + return chan;
> +}
> +
> +static void lowpan_chan_ready(struct l2cap_chan *chan)
> +{
> + struct lowpan_chan_data *data = lowpan_chan_data(chan);
> + struct lowpan_btle_dev *btdev = lowpan_btle_dev(data->dev);
> +
> + BT_DBG("%s chan %p ready ", data->dev->name, chan);
> +
> + /* make it visible for xmit */
> + list_add_tail_rcu(&data->peer.list, &btdev->peers);
> + synchronize_rcu();
> +}
> +
> +static void lowpan_chan_close(struct l2cap_chan *chan)
> +{
> + struct lowpan_chan_data *data = lowpan_chan_data(chan);
> +
> + BT_DBG("%s chan %p closed ", data->dev->name, chan);
> +
> + /* make it unvisible for xmit */
> + list_del_rcu(&data->peer.list);
> + synchronize_rcu();
> + kfree(data);
I currently think much about to run kfree here and I think I need
bluetooth experts to answer the correct handling here.
We allocate that data on chan_create, but doing a free in chan_close
requires that the chan state system will never run chan_ready
afterwards. Is this correct?
Correct handling for me is to do the free when the last chan_put was
called and free everything. Maybe it could be useful to add some
private_data _room_ to l2cap_chan_create, which sits after "struct
l2cap_chan" as something like "u8 data[0]", to allocate/free everything
at once.
I didn't hit any issues yet that after a close, the ready callback will
be called again so I think this can never happen.
btw:
previous implementation had kfree_rcu here, but we run synchronize_rcu
before and this is not needed anymore. I think kfree_rcu will not block,
but I feeling better to not leave that callback after running
synchronize_rcu. :-/
> +}
> +
> +static const struct l2cap_ops lowpan_chan_ops = {
> + .name = "L2CAP 6LoWPAN channel",
> + .new_connection = lowpan_chan_new_conn,
> + .recv = lowpan_chan_recv,
> + .close = lowpan_chan_close,
> + .state_change = l2cap_chan_no_state_change,
> + .ready = lowpan_chan_ready,
> + .get_sndtimeo = lowpan_chan_get_sndtimeo,
> + .alloc_skb = lowpan_chan_alloc_skb,
> + .teardown = l2cap_chan_no_teardown,
> + .defer = l2cap_chan_no_defer,
> + .set_shutdown = l2cap_chan_no_set_shutdown,
> + .resume = l2cap_chan_no_resume,
> + .suspend = l2cap_chan_no_suspend,
> +};
> +
> +static int lowpan_change_mtu(struct net_device *dev, int new_mtu)
> +{
> + struct lowpan_btle_dev *btdev = lowpan_btle_dev(dev);
> +
> + /* if dev is down, peers list are protected by RTNL */
> + if (netif_running(dev) || !list_empty(&btdev->peers))
> + return -EBUSY;
> +
> + if (new_mtu < IPV6_MIN_MTU)
> + return -EINVAL;
> +
> + dev->mtu = new_mtu;
> + return 0;
> +}
> +
> +static const struct net_device_ops netdev_ops = {
> + .ndo_init = lowpan_dev_init,
> + .ndo_stop = lowpan_stop,
> + .ndo_start_xmit = lowpan_xmit,
> + .ndo_change_mtu = lowpan_change_mtu,
> +};
> +
> +static void lowpan_free_netdev(struct net_device *dev)
> +{
> + struct lowpan_btle_dev *btdev = lowpan_btle_dev(dev);
> + struct lowpan_peer *peer, *tmp;
> + struct l2cap_chan *chan;
> +
> + l2cap_chan_close(btdev->listen, 0);
> + l2cap_chan_put(btdev->listen);
> +
> + /* no rcu here? should be safe netdev is down */
> + list_for_each_entry_safe(peer, tmp, &btdev->peers, list) {
> + /* close will free peer data, so save chan here */
> + chan = peer->chan;
> +
> + /* will del peer from list and and free.
> + * should be safe by tmp pointer which has
> + * a pointer for the next entry.
> + */
> + l2cap_chan_close(chan, 0);
> + l2cap_chan_put(chan);
> + }
> +
> + destroy_workqueue(btdev->workqueue);
> + btdev->hdev->lowpan_enable = false;
> + debugfs_remove(btdev->control);
> + hci_dev_put(btdev->hdev);
> +}
> +
> +static void lowpan_setup(struct net_device *dev)
> +{
> + memset(dev->broadcast, 0xff, sizeof(bdaddr_t));
> +
> + dev->netdev_ops = &netdev_ops;
> + dev->destructor = lowpan_free_netdev;
> + dev->features |= NETIF_F_NETNS_LOCAL;
> +}
> +
> +static struct device_type bt_type = {
> + .name = "bluetooth",
> +};
> +
> +static struct l2cap_chan *lowpan_listen_chan_new_conn(struct l2cap_chan *pchan)
> +{
> + struct l2cap_chan *chan;
> +
> + chan = lowpan_chan_create(pchan->data);
> + if (IS_ERR(chan))
> + return NULL;
> +
> + /* change ops with more functionality than listen,
> + * which also free chan->data stuff.
> + */
> + chan->ops = &lowpan_chan_ops;
> +
> + return chan;
> +}
> +
> +static const struct l2cap_ops lowpan_listen_chan_ops = {
> + .name = "L2CAP 6LoWPAN listen channel",
> + .new_connection = lowpan_listen_chan_new_conn,
> + .recv = l2cap_chan_no_recv,
> + .close = l2cap_chan_no_close,
> + .state_change = l2cap_chan_no_state_change,
> + .ready = l2cap_chan_no_ready,
> + .get_sndtimeo = l2cap_chan_no_get_sndtimeo,
> + .alloc_skb = l2cap_chan_no_alloc_skb,
> + .teardown = l2cap_chan_no_teardown,
> + .defer = l2cap_chan_no_defer,
> + .set_shutdown = l2cap_chan_no_set_shutdown,
> + .resume = l2cap_chan_no_resume,
> + .suspend = l2cap_chan_no_suspend,
> +};
> +
> +static int lowpan_create_listen_chan(struct net_device *dev)
> +{
> + struct lowpan_btle_dev *btdev = lowpan_btle_dev(dev);
> + struct l2cap_chan *chan;
> + u8 bdaddr_type;
> + int ret;
> +
> + /* don't use lowpan_chan_create here, because less functionality */
> + chan = l2cap_chan_create();
> + if (!chan)
> + return -ENOMEM;
> +
> + chan->data = dev;
I will add a comment here that this private_data of chan is really
"struct net_device *dev" only, it's for listen stuff only.
Looks wrong at the first sight.
> + chan->ops = &lowpan_listen_chan_ops;
> + hci_copy_identity_address(btdev->hdev, &chan->src, &bdaddr_type);
> + if (bdaddr_type == ADDR_LE_DEV_PUBLIC)
> + chan->src_type = BDADDR_LE_PUBLIC;
> + else
> + chan->src_type = BDADDR_LE_RANDOM;
> +
> + chan->state = BT_LISTEN;
> + atomic_set(&chan->nesting, L2CAP_NESTING_PARENT);
> +
> + BT_DBG("chan %p src type %d", chan, chan->src_type);
> + ret = l2cap_add_psm(chan, BDADDR_ANY, cpu_to_le16(L2CAP_PSM_IPSP));
> + if (ret) {
> + l2cap_chan_put(chan);
> + BT_ERR("psm cannot be added err %d", ret);
> + return ret;
> + }
> + btdev->listen = chan;
> +
> + return 0;
> +}
> +
> +static const struct file_operations lowpan_control_fops;
> +
> +static struct net_device *lowpan_btle_newlink(struct hci_dev *hdev)
> +{
> + struct lowpan_btle_dev *btdev;
> + struct net_device *dev;
> + int err = -ENOMEM;
> +
> + dev = alloc_netdev(LOWPAN_PRIV_SIZE(sizeof(struct lowpan_btle_dev)),
> + LOWPAN_IFNAME_TEMPLATE, NET_NAME_ENUM, lowpan_setup);
> + if (!dev)
> + goto out;
> +
> + dev->addr_assign_type = NET_ADDR_PERM;
> + dev->addr_len = sizeof(hdev->bdaddr.b);
> + lowpan_le48_to_be48(dev->dev_addr, &hdev->bdaddr);
> +
> + SET_NETDEV_DEV(dev, &hdev->dev);
> + SET_NETDEV_DEVTYPE(dev, &bt_type);
> +
> + btdev = lowpan_btle_dev(dev);
> + /* avoid freeing */
> + btdev->hdev = hci_dev_hold(hdev);
> + INIT_LIST_HEAD(&btdev->peers);
> +
> + btdev->workqueue = create_singlethread_workqueue(dev->name);
> + if (!btdev->workqueue) {
> + free_netdev(dev);
> + goto out;
> + }
> +
> + err = lowpan_create_listen_chan(dev);
> + if (err < 0) {
> + destroy_workqueue(btdev->workqueue);
> + free_netdev(dev);
> + goto out;
> + }
> +
> + err = lowpan_register_netdevice(dev, LOWPAN_LLTYPE_BTLE);
> + if (err < 0) {
> + BT_ERR("register_netdev failed %d", err);
> + l2cap_chan_close(btdev->listen, 0);
> + l2cap_chan_put(btdev->listen);
> + free_netdev(dev);
> + goto out;
> + }
> + hdev->ldev = dev;
> +
> + /* ignore error handling here, we cannot call unregister anymore
> + * It's a bug, but it's debugfs... so ignore it.
> + */
> + btdev->control = debugfs_create_file(dev->name, 0644,
> + bt_debugfs, btdev,
> + &lowpan_control_fops);
> + if (!btdev->control)
> + BT_ERR("debugfs error for %s, disable/enable 6lowpan again",
> + dev->name);
> +
> + return dev;
> +
> +out:
> + return ERR_PTR(err);
> +}
> +
> +static void lowpan_btle_dellink(struct net_device *dev)
> +{
> + lowpan_unregister_netdevice(dev);
> +}
> +
> +static int lowpan_parse_le_bdaddr(struct hci_dev *hdev, unsigned char *buf,
> + bdaddr_t *addr, u8 *addr_type)
> +{
> + int n;
> +
> + n = sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %hhu",
> + &addr->b[5], &addr->b[4], &addr->b[3],
> + &addr->b[2], &addr->b[1], &addr->b[0],
> + addr_type);
> + if (n < 7)
> + return -EINVAL;
> +
> + /* check if we handle le addresses and not own source address */
> + if (!bdaddr_type_is_le(*addr_type) ||
> + !bacmp(&hdev->bdaddr, addr))
> + return -EINVAL;
> +
> + return n;
> +}
> +
> +static ssize_t __lowpan_control_write(struct file *fp,
> + const char __user *user_buffer,
> + size_t count, loff_t *position)
> +{
> + char buf[32] = { };
> + size_t buf_size = min(count, sizeof(buf) - 1);
> + struct seq_file *file = fp->private_data;
> + struct lowpan_btle_dev *btdev = file->private;
> + struct hci_dev *hdev = btdev->hdev;
> + struct lowpan_peer *peer;
> + /* slave address */
> + bdaddr_t addr;
> + u8 addr_type;
> + int ret;
> +
> + if (copy_from_user(buf, user_buffer, buf_size))
> + return -EFAULT;
> +
> + if (memcmp(buf, "connect ", 8) == 0) {
> + struct lowpan_peer *peer;
peer can be removed here.
> + struct l2cap_chan *chan;
> +
> + ret = lowpan_parse_le_bdaddr(hdev, &buf[8], &addr, &addr_type);
> + if (ret < 0)
> + return ret;
> +
> + /* check if we already know that slave */
> + rcu_read_lock();
> + peer = lowpan_lookup_peer(btdev, &addr);
> + if (peer) {
> + rcu_read_unlock();
> + BT_DBG("6LoWPAN connection already exists");
> + return -EEXIST;
> + }
> + rcu_read_unlock();
> +
> + chan = lowpan_chan_create(hdev->ldev);
> + if (IS_ERR(chan))
> + return PTR_ERR(chan);
> + chan->ops = &lowpan_chan_ops;
> +
> + ret = l2cap_hdev_chan_connect(hdev, chan,
> + cpu_to_le16(L2CAP_PSM_IPSP),
> + 0, &addr, addr_type);
> + if (ret < 0) {
> + l2cap_chan_put(chan);
> + return ret;
> + }
> +
> + return count;
> + } else if (memcmp(buf, "disconnect ", 11) == 0) {
> + ret = lowpan_parse_le_bdaddr(hdev, &buf[11], &addr, &addr_type);
> + if (ret < 0)
> + return ret;
> +
> + /* check if we don't know that slave */
> + rcu_read_lock();
> + peer = lowpan_lookup_peer(btdev, &addr);
> + if (!peer) {
> + rcu_read_unlock();
> + BT_DBG("6LoWPAN connection not found in peers");
> + return -ENOENT;
> + }
> + rcu_read_unlock();
> +
> + /* first delete the peer out of list,
> + * which makes it visiable to netdev,
> + * will be done by close callback.
> + */
> + l2cap_chan_close(peer->chan, 0);
> + l2cap_chan_put(peer->chan);
> + } else {
> + return -EINVAL;
> + }
> +
> + return count;
> +}
> +
> +static ssize_t lowpan_control_write(struct file *fp,
> + const char __user *user_buffer,
> + size_t count, loff_t *position)
> +{
> + ssize_t ret;
> +
> + rtnl_lock();
> + ret = __lowpan_control_write(fp, user_buffer, count, position);
> + rtnl_unlock();
> +
> + return ret;
> +}
> +
> +static int lowpan_control_show(struct seq_file *f, void *ptr)
> +{
> + struct lowpan_btle_dev *btdev = f->private;
> + struct hci_dev *hdev = btdev->hdev;
> + struct lowpan_peer *peer;
> +
> + rtnl_lock();
> +
> + if (!hdev->lowpan_enable) {
> + rtnl_unlock();
> + return 0;
> + }
> +
> + rcu_read_lock();
> +
> + list_for_each_entry_rcu(peer, &btdev->peers, list) {
> + seq_printf(f, "%pMR (type %u) state: %s\n",
> + &peer->chan->dst, peer->chan->dst_type,
> + state_to_string(peer->chan->state));
> + }
> +
> + rcu_read_unlock();
> +
> + rtnl_unlock();
> + return 0;
> +}
> +
> +static int lowpan_control_open(struct inode *inode, struct file *file)
> +{
> + return single_open(file, lowpan_control_show, inode->i_private);
> +}
> +
> +static const struct file_operations lowpan_control_fops = {
> + .open = lowpan_control_open,
> + .read = seq_read,
> + .write = lowpan_control_write,
> + .llseek = seq_lseek,
> + .release = single_release,
> +};
> +
> +/* TODO remove this when add non debugfs API, it's a workaround */
> +static struct workqueue_struct *workqueue_cleanup;
> +
> +struct lowpan_cleanup_work {
> + struct work_struct work;
> + struct net_device *dev;
> +};
> +
> +static void lowpan_cleanup_worker(struct work_struct *work)
> +{
> + struct lowpan_cleanup_work *xw;
> +
> + xw = container_of(work, struct lowpan_cleanup_work, work);
> +
> + rtnl_lock();
> + lowpan_btle_dellink(xw->dev);
> + lowpan_btle_dev(xw->dev)->hdev->pending_deletion = false;
> + rtnl_unlock();
> +
> + kfree(xw);
> +}
> +
> +/* TODO workaround, debugfs doesn't like recursion remove while debugfs
> + * handling, this should be removed for by using real UAPI.
> + */
> +static int __lowpan_btle_dellink(struct net_device *dev,
> + struct hci_dev *hdev)
> +{
> + struct lowpan_cleanup_work *xw;
> +
> + xw = kzalloc(sizeof(*xw), GFP_KERNEL);
> + if (!xw)
> + return -ENOMEM;
> +
> + INIT_WORK(&xw->work, lowpan_cleanup_worker);
> + xw->dev = dev;
> +
> + hdev->pending_deletion = true;
> + queue_work(workqueue_cleanup, &xw->work);
> + return 0;
> +}
> +
> +static int lowpan_enable_set(struct hci_dev *hdev, bool enabled)
> +{
> + int ret = 0;
> +
> + if (hdev->lowpan_enable == enabled)
> + goto out;
> +
> + if (enabled) {
> + struct net_device *dev;
> +
> + /* create one interface by default */
> + dev = lowpan_btle_newlink(hdev);
> + if (IS_ERR(dev))
> + return PTR_ERR(dev);
> +
> + /* TODO should be done by user network managers? */
> + ret = dev_open(dev);
> + if (ret < 0) {
> + BT_ERR("iface %s cannot be opened %d", dev->name, ret);
> + ret = __lowpan_btle_dellink(dev, hdev);
> + if (ret < 0)
> + BT_ERR("failed to remove interface %s",
> + hdev->ldev->name);
> +
> + return ret;
> + }
> +
> + hdev->lowpan_enable = true;
> + } else {
> + /* ignore pending deletions */
> + if (hdev->pending_deletion)
> + return 0;
> +
> + __lowpan_btle_dellink(hdev->ldev, hdev);
> + }
> +
> +out:
> + return ret;
> +}
> +
> +static void lowpan_btle_hci_unregister(struct hci_dev *hdev)
> +{
> + /* no need to cleanup debugfs, will be done by hci_core */
comment can be removed.
Well..., I had this comment because I did some stuff before per
"hci->debugfs". This is also the question here.
Maybe "6lowpan_enable" makes no sense to move it to per hci_dev debugfs
entry. For write, it makes sense - but read, you want some "global"
mapping of "hci_dev <-> lowpan interface", this may easy realizied for
netlink UAPI but for debugfs it's complicated because you need to
iterate over all hci%d entries and making cat on "6lowpan_enable".
That's the current reason why "6lowpan_enable" is global.
---
The control file for btle lowpan interface could be moved per hci_dev
and maybe I will change it again, then the above comment makes sense
again. :-)
> +
> + /* TODO netns support with multiple lowpan interfaces */
> + if (hdev->lowpan_enable)
> + lowpan_btle_dellink(hdev->ldev);
> +}
> +
> +static int lowpan_hci_dev_event(struct notifier_block *unused,
> + unsigned long event, void *ptr)
> +{
> + struct hci_dev *hdev = ptr;
> + int ret = NOTIFY_OK;
> +
> + rtnl_lock();
> +
> + /* bluetooth handles that event type as int,
> + * but there is no overflow yet.
> + */
> + switch (event) {
> + case HCI_DEV_UNREG:
> + lowpan_btle_hci_unregister(hdev);
> + ret = NOTIFY_DONE;
> + break;
> + default:
> + break;
> + }
> +
> + rtnl_unlock();
> +
> + return ret;
> +}
> +
> +static struct notifier_block lowpan_hcI_dev_notifier = {
> + .notifier_call = lowpan_hci_dev_event,
> +};
> +
> +static ssize_t lowpan_enable_write(struct file *fp,
> + const char __user *user_buffer,
> + size_t count, loff_t *position)
> +{
> + char buf[32] = { };
> + size_t buf_size = min(count, sizeof(buf) - 1);
> + struct hci_dev *hdev;
> + int idx, enabled, n, ret;
> +
> + if (copy_from_user(buf, user_buffer, buf_size))
> + return -EFAULT;
> +
> + n = sscanf(buf, "hci%d %d", &idx, &enabled);
> + if (n != 2)
> + return -EINVAL;
> +
> + hdev = hci_dev_get(idx);
> + if (!hdev)
> + return -EINVAL;
> +
> + rtnl_lock();
> + ret = lowpan_enable_set(hdev, enabled);
> + rtnl_unlock();
> +
> + hci_dev_put(hdev);
> +
> + return count;
> +}
> +
> +static int lowpan_enable_show(struct seq_file *f, void *ptr)
> +{
> + struct hci_dev *hdev;
> +
> + rtnl_lock();
> + read_lock(&hci_dev_list_lock);
> + list_for_each_entry(hdev, &hci_dev_list, list) {
> + if (hdev->lowpan_enable)
> + seq_printf(f, "hci%d -> %s\n", hdev->id,
> + hdev->ldev->name);
> + }
> + read_unlock(&hci_dev_list_lock);
> + rtnl_unlock();
> +
> + return 0;
> +}
> +
> +static int lowpan_enable_open(struct inode *inode, struct file *file)
> +{
> + return single_open(file, lowpan_enable_show, inode->i_private);
> +}
> +
> +static const struct file_operations lowpan_enable_fops = {
> + .open = lowpan_enable_open,
> + .read = seq_read,
> + .write = lowpan_enable_write,
> + .llseek = seq_lseek,
> + .release = single_release,
> +};
> +
> +static int __init bt_6lowpan_init(void)
> +{
> + int err;
> +
> + workqueue_cleanup = create_singlethread_workqueue("btle 6lowpan");
> + if (!workqueue_cleanup)
> + return -ENOMEM;
> +
> + debugfs_create_file("6lowpan_enable", 0644, bt_debugfs, NULL,
> + &lowpan_enable_fops);
> +
> + err = register_hci_dev_notifier(&lowpan_hcI_dev_notifier);
> + if (err < 0)
> + destroy_workqueue(workqueue_cleanup);
> +
> + return err;
> +}
> +
> +static void __exit bt_6lowpan_exit(void)
> +{
> + destroy_workqueue(workqueue_cleanup);
> + unregister_hci_dev_notifier(&lowpan_hcI_dev_notifier);
> +}
> +
> +module_init(bt_6lowpan_init);
> +module_exit(bt_6lowpan_exit);
> +
> +MODULE_AUTHOR("Jukka Rissanen <[email protected]>");
> +MODULE_DESCRIPTION("Bluetooth 6LoWPAN");
> +MODULE_VERSION(LOWPAN_BTLE_VERSION);
> +MODULE_LICENSE("GPL");
> diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
> index e347828..b3ff12e 100644
> --- a/net/bluetooth/Makefile
> +++ b/net/bluetooth/Makefile
> @@ -7,6 +7,9 @@ obj-$(CONFIG_BT_RFCOMM) += rfcomm/
> obj-$(CONFIG_BT_BNEP) += bnep/
> obj-$(CONFIG_BT_CMTP) += cmtp/
> obj-$(CONFIG_BT_HIDP) += hidp/
> +obj-$(CONFIG_BT_6LOWPAN) += bluetooth_6lowpan.o
> +
> +bluetooth_6lowpan-y := 6lowpan.o
>
> bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
> hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o lib.o \
>
- Alex
Hi,
On 07/11/2016 09:50 PM, Alexander Aring wrote:
> These flags should be all the same for 6LoWPAN so move it to 6LoWPAN generic.
>
> Signed-off-by: Alexander Aring <[email protected]>
> ---
> net/6lowpan/core.c | 1 +
> net/ieee802154/6lowpan/core.c | 1 -
> 2 files changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
> index a978000..6b7de14 100644
> --- a/net/6lowpan/core.c
> +++ b/net/6lowpan/core.c
> @@ -62,6 +62,7 @@ int lowpan_register_netdevice(struct net_device *dev,
> dev->type = ARPHRD_6LOWPAN;
> dev->mtu = IPV6_MIN_MTU;
> dev->priv_flags |= IFF_NO_QUEUE;
> + dev->flags = IFF_BROADCAST | IFF_MULTICAST;
>
> dev->header_ops = &header_ops;
>
> diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c
> index 228a711..a60abad 100644
> --- a/net/ieee802154/6lowpan/core.c
> +++ b/net/ieee802154/6lowpan/core.c
> @@ -92,7 +92,6 @@ static void lowpan_setup(struct net_device *ldev)
> memset(ldev->broadcast, 0xff, IEEE802154_ADDR_LEN);
> /* We need an ipv6hdr as minimum len when calling xmit */
> ldev->hard_header_len = sizeof(struct ipv6hdr);
This should be moved to generic 6lowpan as well.
This says at least, the skb->len at xmit callback must be at least a
length of "sizeof(struct ipv6hdr)" which should be correct.
BUT...
I still have (more than years) the use-case that somebody sends an
AF_PACKET raw socket over lowpan interface.
I didn't test it yet, but I think this is a simple way to crash the
kernel on all lowpan interfaces. I currently not sure if I can break
something there, but I am sure it will send garbage data.
The xmit callback needs data which is available in skb_headroom, this
data is set by header_create callback which will not called on AF_PACKET
RAW sockets.
The root of this issue is that we don't have L2 here for creating mac
headers. The header_create callback should do that, but we do 6LoWPAN
adaptation here and the header_create callback will be used by ndisc to
say "here are the addresses, generate a mac header" and AF_PACKET
_DGRAM_ (for putting mac header in front of AF_PACKET payload).
We use this callback for the first use-case of ndisc only.
AF_PACKET RAW receive make sense, because tcpdump/wireshark needs to
capture data. Sending AF_PACKET RAW makes no sense and will I suppose
crash the kernel and I think every user can do that.
DGRAM sockets maybe makes sense, but I would disable that also for
(receive and transmit). You need to use PF_INET6 socket types for lowpan
interfaces only. That issue is somehow described at [0].
> - ldev->flags = IFF_BROADCAST | IFF_MULTICAST;
>
> ldev->netdev_ops = &lowpan_netdev_ops;
> ldev->destructor = free_netdev;
>
- Alex
[0] http://lxr.free-electrons.com/source/net/ieee802154/6lowpan/tx.c#L43
Hi,
own review notes to lookup for next run, otherwise I will forget them.
On 07/11/2016 09:50 PM, Alexander Aring wrote:
> This patch adds support for 48 bit 6LoWPAN address length
> autoconfiguration which is the case for BTLE 6LoWPAN.
>
> Signed-off-by: Alexander Aring <[email protected]>
> ---
> net/ipv6/addrconf.c | 19 ++++++++++++++-----
> 1 file changed, 14 insertions(+), 5 deletions(-)
>
> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index a1f6b7b..aab9b0d 100644
> --- a/net/ipv6/addrconf.c
> +++ b/net/ipv6/addrconf.c
> @@ -2007,12 +2007,21 @@ static void addrconf_leave_anycast(struct inet6_ifaddr *ifp)
> __ipv6_dev_ac_dec(ifp->idev, &addr);
> }
>
> -static int addrconf_ifid_eui64(u8 *eui, struct net_device *dev)
> +static int addrconf_ifid_6lowpan(u8 *eui, struct net_device *dev)
> {
> - if (dev->addr_len != EUI64_ADDR_LEN)
> + switch (dev->addr_len) {
> + case ETH_ALEN:
> + return addrconf_ifid_eui48(eui, dev);
> + case EUI64_ADDR_LEN:
> + if (dev->addr_len != EUI64_ADDR_LEN)
> + return -1;
this if branch should be removed.
The idea is here now to not check on "link layer type". Now we check on
address length type and do some usually stuff which all others
link-layers does which such address length. This works now for btle and
802.15.4 but maybe there will exists another 8 byte or 6 byte address
length link-layer where we cannot use the same stuff here.
If that will be the case we can still figure out the link-layer type and
do different handling.
btw: this should also be switched then in iphc code, where we evaluate
the link-layer type to do uncompress. We can switch to evaluate addr_len
there as well.
OR we introduce some callbacks which must be set from link-layer 6lowpan
implementation for compress or decompress L3 addresses (which based on
L2 address bits). I need to think about here what will be the best fit.
This callback can be used here also then.
> + memcpy(eui, dev->dev_addr, EUI64_ADDR_LEN);
> + eui[0] ^= 2;
> + break;
> + default:
> return -1;
> - memcpy(eui, dev->dev_addr, EUI64_ADDR_LEN);
> - eui[0] ^= 2;
> + }
> +
> return 0;
> }
>
> @@ -2103,7 +2112,7 @@ static int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
> case ARPHRD_IPGRE:
> return addrconf_ifid_gre(eui, dev);
> case ARPHRD_6LOWPAN:
> - return addrconf_ifid_eui64(eui, dev);
> + return addrconf_ifid_6lowpan(eui, dev);
> case ARPHRD_IEEE1394:
> return addrconf_ifid_ieee1394(eui, dev);
> case ARPHRD_TUNNEL6:
>
- Alex
Hi,
On 07/12/2016 04:51 PM, Luiz Augusto von Dentz wrote:
...
>>
>> HOW TO REPRODUCE:
>>
>> It's simple, do some high payloads which makes lot tx/rcv traffic on both sides:
>>
>> Node A:
>>
>> ping6 $IP_NODEB%6lo0 -s 60000
>>
>> Node B:
>>
>> ping6 ff02::1%6lo0
>
> Im not sure I understand what you are trying to do with a packet of
> 60Kb, the l2cap_chan can most likely only do 1280 bytes and even with
60Kb is just some incredible high value to produce on both sides tx/rx
data. The other side will normally send some "defragmentation failure"
icmp messages.
This value is just to produce the high traffic, not practice payload.
> fragmentation this will consume the credits quicker than we can
> receive more so it will eventually starting buffering until it gets
> stuck. Note that it is probably still receiving credits but we
This is exactly what I like to do. Produce a lot of data to kill L3
layer, but this should not kill the L2 layer. Killing the L2 layer is
what's happend here.
If I do a lot of data on e.g. tcp traffic and the tcp connection will be
closed -> that's fine for me but afterwards starting a new L3 connection
should be still working. This is not the case here, I can kill L2 layer
and nothing works anymore, because L2 running into deadlock.
> probably have so much data buffered that all the credits are consumed
> almost immediately after receiving, or you don't see -EAGAIN more than
> once?
>
My example shows that we transmit some data on both nodes, that is
important to check the l2cap_chan_send return type.
When I do that above and running into the deadlock the IPv6 connection
runs "NS" messages only, because it still can send on L3 layer but the
L2 layer is killed -> I get -EAGAIN in l2cap_chan_send always on both
nodes because tx_credits are on both nodes zero and will not be
incremented again.
So far I know these tx_credits will be incremented only if somebody
sends something again, right? This will never be the case again and I am
stucked in a deadlock which should not be there.
to your question:
"or you don't see -EAGAIN more than once"
I see -EAGAIN always at calling l2cap_chan_send on both nodes. I think I
can wait forever, after an year I still see "-EAGAIN" when calling
l2cap_chan_send.
Nevertheless, I will try to hit the deadlock (-EAGAIN) on both nodes so
nobody transmits anything anymore. Then waiting six hours and look if
still -EAGAIN is there on both nodes. I will report again here, after
that time it should working again, but I don't believe that.
- Alex
Hi Alex,
On Mon, Jul 11, 2016 at 10:50 PM, Alexander Aring <[email protected]> wrote:
> Hi,
>
> I want to write a short summary here, to see what I changed to the old
> implementation, please see last RFC patch.
>
> How to use it (I know some commands are deprecated and should not be used),
> I have two scripts to do that:
>
> Node A (run at first):
>
> ---
>
> #!/bin/sh
> modprobe btusb
> modprobe bluetooth_6lowpan
>
> hciconfig hci0 up
> echo "hci0 1" > /sys/kernel/debug/bluetooth/6lowpan_enable
>
> hciconfig hci0 leadv
>
> hciconfig
>
> ---
>
> hcicondif will printout the address of hci dev, remember that.
>
> Node B (run after Node A stuff):
>
> ---
>
> #!/bin/sh
>
> if [ "$#" -ne 1 ]; then
> echo "run $0 \$SLAVE_ADDRESS, where SLAVE_ADDRESS is the BTLE slave"
> exit 1
> fi
>
> modprobe btusb
> modprobe bluetooth_6lowpan
>
> hciconfig hci0 up
> echo "hci0 1" > /sys/kernel/debug/bluetooth/6lowpan_enable
>
> hcitool lecc $1
>
> echo "connect $1 1" > /sys/kernel/debug/bluetooth/6lo0
>
> ---
>
> Where $1 is the address of the Node A.
>
> Well done. Your connection should be established. Except you have crap usb
> dongles like me (broadcom) where sometimes the reset functionality seems to
> be not working 100% correctly. btw: my workaround, replug usb dongles (but
> I always need to tell the new usb bus information my qemu vm :-()
>
> KNOWN BUGS and how to reproduce that one:
>
> First, thanks to Luiz Augusto von Dentz, which tried to help me there.
> But I gave up, the BUG still exists and it's because L2CAP implementation
> or I use xmit functionality wrong.
>
> The issue is "tx credits in L2CAP (bluetooth experts know what I mean here)
> will be reach to zero at the same time on both nodes. This occurs a deadlock
> and nobody will transmit anything anymore".
>
> So far I understand these tx credits are to avoid buffer-bloating. My
> workaround to fix that was to allow buffer bloating:
>
> #define L2CAP_LE_MAX_CREDITS 10
> changed to (some high number which doesn't reach 2^16):
> #define L2CAP_LE_MAX_CREDITS 60000
>
> This will introduce buffer-bloating which I happily see when doing high payload
> pings. (I think on high traffic you will reach that issue also with this
> workaround, it's just not likely.)
>
> ---
>
> HOW TO REPRODUCE:
>
> It's simple, do some high payloads which makes lot tx/rcv traffic on both sides:
>
> Node A:
>
> ping6 $IP_NODEB%6lo0 -s 60000
>
> Node B:
>
> ping6 ff02::1%6lo0
Im not sure I understand what you are trying to do with a packet of
60Kb, the l2cap_chan can most likely only do 1280 bytes and even with
fragmentation this will consume the credits quicker than we can
receive more so it will eventually starting buffering until it gets
stuck. Note that it is probably still receiving credits but we
probably have so much data buffered that all the credits are consumed
almost immediately after receiving, or you don't see -EAGAIN more than
once?
> that's one example, but you need to produce some transmit data on both nodes to
> see that the nodes stucks in -11 (EAGAIN) of l2cap_chan_send.
>
> Doing that and activate "#define DEBUG" in "bluetooth/6lowpan.c". After some time
> Node A print outs (depends on ping6 implementation, iputils in my case):
>
> .... icmp_seq=33 Destination unreachable: Address unreachable
>
> also on Node B, there comes maybe never a responds from Node B anymore. Then it's
> likely that you hit the deadlock.
>
> Now run dmesg on both nodes, you will see each transmit (l2cap_chan_send) ends in:
>
> "transmit return value -11"
>
> if this will be printed out via dmesg on both nodes, you hit the deadlock and
> nothing will be transmitted anymore. I need bluetooth experts to solve this
> issue. :-)
>
> Otherwise I detected no issues yet, also I tried to remove my usb dongle while
> high tx/rcv bandwidth at runtime from the qemu vm without crashing the kernel
> (very important test for me).
>
> - Alex
>
> Alexander Aring (20):
> 6lowpan: ndisc: don't remove short address
> nhc: add TODO for nhc work
> ieee802154: 6lowpan: remove headroom check
> ieee802154: 6lowpan: move skb cb BUILD_BUG_ON check
> 6lowpan: remove LOWPAN_IPHC_MAX_HEADER_LEN
> 6lowpan: hold netdev while unregister
> 6lowpan: introduce generic default naming
> 6lowpan: move rx defines to generic
> bluetooth: introduce l2cap_hdev_chan_connect
> bluetooth: add hci dev notifier
> bluetooth: export functions and variables
> 6lowpan: bluetooth: remove implementation
> ieee802154: 6lowpan: move header create to 6lowpan
> 6lowpan: move dev_init to generic
> 6lowpan: iphc: override l2 packet information
> ipv6: addrconf: fix 48 bit 6lowpan autoconfiguration
> 6lowpan: iphc: add handling for btle
> 6lowpan: move multicast flags to generic
> 6lowpan: delete addr_len handling to generic
> 6lowpan: bluetooth: add new implementation
>
> include/net/6lowpan.h | 30 +-
> include/net/bluetooth/hci_core.h | 14 +
> include/net/bluetooth/l2cap.h | 3 +
> net/6lowpan/core.c | 47 +-
> net/6lowpan/iphc.c | 111 +++
> net/6lowpan/ndisc.c | 2 -
> net/6lowpan/nhc.c | 15 +
> net/bluetooth/6lowpan.c | 1788 ++++++++++++++----------------------
> net/bluetooth/hci_core.c | 26 +
> net/bluetooth/hci_sock.c | 2 +
> net/bluetooth/l2cap_core.c | 29 +-
> net/ieee802154/6lowpan/6lowpan_i.h | 9 -
> net/ieee802154/6lowpan/core.c | 23 +-
> net/ieee802154/6lowpan/tx.c | 94 +-
> net/ipv6/addrconf.c | 19 +-
> 15 files changed, 1008 insertions(+), 1204 deletions(-)
>
> --
> 2.9.0
>
--
Luiz Augusto von Dentz
I saw that unregister_netdevice will also free the netdev by running
dev_put. This may clash run debugfs exist, because it use netdevice
private data room.
Signed-off-by: Alexander Aring <[email protected]>
---
net/6lowpan/core.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
index 5945f7e..00ffab3 100644
--- a/net/6lowpan/core.c
+++ b/net/6lowpan/core.c
@@ -62,8 +62,10 @@ EXPORT_SYMBOL(lowpan_register_netdev);
void lowpan_unregister_netdevice(struct net_device *dev)
{
+ dev_hold(dev);
unregister_netdevice(dev);
lowpan_dev_debugfs_exit(dev);
+ dev_put(dev);
}
EXPORT_SYMBOL(lowpan_unregister_netdevice);
--
2.9.0
I need to react at least somehow on a hci dev unregister event to clean
all 6LoWPAN interfaces on it. This patch adds a notifier for non
socket stuff.
I also thought about to react somehow on "struct device" unregister but
I didn't found any functionality for that.
Signed-off-by: Alexander Aring <[email protected]>
---
include/net/bluetooth/hci_core.h | 4 ++++
net/bluetooth/hci_core.c | 22 ++++++++++++++++++++++
net/bluetooth/hci_sock.c | 2 ++
3 files changed, 28 insertions(+)
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index dc71473..a37027a 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -902,6 +902,10 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active);
void hci_le_conn_failed(struct hci_conn *conn, u8 status);
+int register_hci_dev_notifier(struct notifier_block *nb);
+void unregister_hci_dev_notifier(struct notifier_block *nb);
+int call_hci_dev_notifiers(unsigned long val, struct hci_dev *hdev);
+
/*
* hci_conn_get() and hci_conn_put() are used to control the life-time of an
* "hci_conn" object. They do not guarantee that the hci_conn object is running,
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 98f6c37..a75e9e7 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -57,6 +57,28 @@ DEFINE_MUTEX(hci_cb_list_lock);
/* HCI ID Numbering */
static DEFINE_IDA(hci_index_ida);
+/* HCI device notifier list */
+static RAW_NOTIFIER_HEAD(hci_dev_chain);
+
+/* ---- HCI notifiers functions ---- */
+
+int register_hci_dev_notifier(struct notifier_block *nb)
+{
+ return raw_notifier_chain_register(&hci_dev_chain, nb);
+}
+EXPORT_SYMBOL(register_hci_dev_notifier);
+
+void unregister_hci_dev_notifier(struct notifier_block *nb)
+{
+ raw_notifier_chain_unregister(&hci_dev_chain, nb);
+}
+EXPORT_SYMBOL(unregister_hci_dev_notifier);
+
+int call_hci_dev_notifiers(unsigned long val, struct hci_dev *hdev)
+{
+ return raw_notifier_call_chain(&hci_dev_chain, val, hdev);
+}
+
/* ---- HCI debugfs entries ---- */
static ssize_t dut_mode_read(struct file *file, char __user *user_buf,
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 6ef8a01..290eb1e 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -529,6 +529,8 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event)
}
read_unlock(&hci_sk_list.lock);
}
+
+ call_hci_dev_notifiers(event, hdev);
}
static struct hci_mgmt_chan *__hci_mgmt_chan_find(unsigned short channel)
--
2.9.0
This function exports some functions which are needed by the 6LoWPAN BTLE
module.
Signed-off-by: Alexander Aring <[email protected]>
---
net/bluetooth/hci_core.c | 4 ++++
net/bluetooth/l2cap_core.c | 1 +
2 files changed, 5 insertions(+)
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index a75e9e7..4935b94 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -48,7 +48,9 @@ static void hci_tx_work(struct work_struct *work);
/* HCI device list */
LIST_HEAD(hci_dev_list);
+EXPORT_SYMBOL(hci_dev_list);
DEFINE_RWLOCK(hci_dev_list_lock);
+EXPORT_SYMBOL(hci_dev_list_lock);
/* HCI callback list */
LIST_HEAD(hci_cb_list);
@@ -955,6 +957,7 @@ struct hci_dev *hci_dev_get(int index)
read_unlock(&hci_dev_list_lock);
return hdev;
}
+EXPORT_SYMBOL(hci_dev_get);
/* ---- Inquiry support ---- */
@@ -2941,6 +2944,7 @@ void hci_copy_identity_address(struct hci_dev *hdev, bdaddr_t *bdaddr,
*bdaddr_type = ADDR_LE_DEV_PUBLIC;
}
}
+EXPORT_SYMBOL(hci_copy_identity_address);
/* Alloc HCI device */
struct hci_dev *hci_alloc_dev(void)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 2ab8814..1bf534d 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -484,6 +484,7 @@ void l2cap_chan_hold(struct l2cap_chan *c)
kref_get(&c->kref);
}
+EXPORT_SYMBOL(l2cap_chan_hold);
void l2cap_chan_put(struct l2cap_chan *c)
{
--
2.9.0
A BTLE 6LoWPAN should be binded to one hci device and is not changeable
afterwards. This patch adds l2cap_hdev_chan_connect function without calling
hci_get_route which decides on bluetooth side which may be the best hci
device which should be use. The hci dev will be available by parameter.
Signed-off-by: Alexander Aring <[email protected]>
---
include/net/bluetooth/l2cap.h | 3 +++
net/bluetooth/l2cap_core.c | 28 +++++++++++++++++++++-------
2 files changed, 24 insertions(+), 7 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 5ee3c68..8a4d26e 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -928,6 +928,9 @@ struct l2cap_chan *l2cap_chan_create(void);
void l2cap_chan_close(struct l2cap_chan *chan, int reason);
int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
bdaddr_t *dst, u8 dst_type);
+int l2cap_hdev_chan_connect(struct hci_dev *hdev,
+ struct l2cap_chan *chan, __le16 psm, u16 cid,
+ bdaddr_t *dst, u8 dst_type);
int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len);
void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator);
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 54ceb1f..2ab8814 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -7041,21 +7041,17 @@ static bool is_valid_psm(u16 psm, u8 dst_type) {
return ((psm & 0x0101) == 0x0001);
}
-int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
- bdaddr_t *dst, u8 dst_type)
+int l2cap_hdev_chan_connect(struct hci_dev *hdev,
+ struct l2cap_chan *chan, __le16 psm, u16 cid,
+ bdaddr_t *dst, u8 dst_type)
{
struct l2cap_conn *conn;
struct hci_conn *hcon;
- struct hci_dev *hdev;
int err;
BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", &chan->src, dst,
dst_type, __le16_to_cpu(psm));
- hdev = hci_get_route(dst, &chan->src);
- if (!hdev)
- return -EHOSTUNREACH;
-
hci_dev_lock(hdev);
if (!is_valid_psm(__le16_to_cpu(psm), dst_type) && !cid &&
@@ -7199,7 +7195,25 @@ chan_unlock:
mutex_unlock(&conn->chan_lock);
done:
hci_dev_unlock(hdev);
+ return err;
+}
+EXPORT_SYMBOL_GPL(l2cap_hdev_chan_connect);
+
+int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
+ bdaddr_t *dst, u8 dst_type)
+{
+ struct hci_dev *hdev;
+ int err = -EHOSTUNREACH;
+
+ hdev = hci_get_route(dst, &chan->src);
+ if (!hdev)
+ goto done;
+
+ err = l2cap_hdev_chan_connect(hdev, chan, psm, cid, dst, dst_type);
+
hci_dev_put(hdev);
+
+done:
return err;
}
EXPORT_SYMBOL_GPL(l2cap_chan_connect);
--
2.9.0
The handling for the header_ops create callback should be on all 6LoWPAN
implementation the same. We move that now to generic 6LoWPAN.
Signed-off-by: Alexander Aring <[email protected]>
---
include/net/6lowpan.h | 12 +++++
net/6lowpan/core.c | 35 ++++++++++++++
net/ieee802154/6lowpan/6lowpan_i.h | 3 --
net/ieee802154/6lowpan/core.c | 5 --
net/ieee802154/6lowpan/tx.c | 93 +++++++++++++-------------------------
5 files changed, 79 insertions(+), 69 deletions(-)
diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
index 90d9d08..9a6282b 100644
--- a/include/net/6lowpan.h
+++ b/include/net/6lowpan.h
@@ -133,6 +133,18 @@ lowpan_iphc_ctx_is_compression(const struct lowpan_iphc_ctx *ctx)
return test_bit(LOWPAN_IPHC_CTX_FLAG_COMPRESSION, &ctx->flags);
}
+struct lowpan_addr_info {
+ unsigned char daddr[EUI64_ADDR_LEN];
+ unsigned char saddr[EUI64_ADDR_LEN];
+};
+
+static inline struct
+lowpan_addr_info *lowpan_addr_info(const struct sk_buff *skb)
+{
+ return (struct lowpan_addr_info *)(skb->data -
+ sizeof(struct lowpan_addr_info));
+}
+
struct lowpan_dev {
enum lowpan_lltypes lltype;
struct dentry *iface_debugfs;
diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
index 00ffab3..b01effd 100644
--- a/net/6lowpan/core.c
+++ b/net/6lowpan/core.c
@@ -18,6 +18,33 @@
#include "6lowpan_i.h"
+/* TODO I think AF_PACKET DGRAM (sending/receiving) RAW (sending) makes no
+ * sense here. We should disable it, the right use-case would be AF_INET6
+ * RAW/DGRAM sockets.
+ */
+static int lowpan_header_create(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, const void *daddr,
+ const void *saddr, unsigned int len)
+{
+ struct lowpan_addr_info *info = lowpan_addr_info(skb);
+
+ if (WARN_ON_ONCE(type != ETH_P_IPV6))
+ return -EINVAL;
+
+ memcpy(info->daddr, daddr, dev->addr_len);
+
+ if (saddr)
+ memcpy(info->saddr, saddr, dev->addr_len);
+ else
+ memcpy(info->saddr, dev->dev_addr, dev->addr_len);
+
+ return 0;
+}
+
+static struct header_ops header_ops = {
+ .create = lowpan_header_create,
+};
+
int lowpan_register_netdevice(struct net_device *dev,
enum lowpan_lltypes lltype)
{
@@ -28,6 +55,14 @@ int lowpan_register_netdevice(struct net_device *dev,
dev->mtu = IPV6_MIN_MTU;
dev->priv_flags |= IFF_NO_QUEUE;
+ dev->header_ops = &header_ops;
+
+ /* We need at least headroom for lowpan_addr_info to get necessary
+ * address information from header create to xmit callback.
+ */
+ if (dev->needed_headroom < sizeof(struct lowpan_addr_info))
+ dev->needed_headroom += sizeof(struct lowpan_addr_info);
+
lowpan_dev(dev)->lltype = lltype;
spin_lock_init(&lowpan_dev(dev)->ctx.lock);
diff --git a/net/ieee802154/6lowpan/6lowpan_i.h b/net/ieee802154/6lowpan/6lowpan_i.h
index 39b739f..709c22c 100644
--- a/net/ieee802154/6lowpan/6lowpan_i.h
+++ b/net/ieee802154/6lowpan/6lowpan_i.h
@@ -48,9 +48,6 @@ int lowpan_net_frag_init(void);
void lowpan_rx_init(void);
void lowpan_rx_exit(void);
-int lowpan_header_create(struct sk_buff *skb, struct net_device *dev,
- unsigned short type, const void *_daddr,
- const void *_saddr, unsigned int len);
netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev);
int lowpan_iphc_decompress(struct sk_buff *skb);
diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c
index f70edcc..096a194 100644
--- a/net/ieee802154/6lowpan/core.c
+++ b/net/ieee802154/6lowpan/core.c
@@ -54,10 +54,6 @@
static int open_count;
-static struct header_ops lowpan_header_ops = {
- .create = lowpan_header_create,
-};
-
static int lowpan_dev_init(struct net_device *ldev)
{
netdev_lockdep_set_classes(ldev);
@@ -106,7 +102,6 @@ static void lowpan_setup(struct net_device *ldev)
ldev->flags = IFF_BROADCAST | IFF_MULTICAST;
ldev->netdev_ops = &lowpan_netdev_ops;
- ldev->header_ops = &lowpan_header_ops;
ldev->destructor = free_netdev;
ldev->features |= NETIF_F_NETNS_LOCAL;
}
diff --git a/net/ieee802154/6lowpan/tx.c b/net/ieee802154/6lowpan/tx.c
index a7a1b12..f56b9cd 100644
--- a/net/ieee802154/6lowpan/tx.c
+++ b/net/ieee802154/6lowpan/tx.c
@@ -18,48 +18,23 @@
#define LOWPAN_FRAG1_HEAD_SIZE 0x4
#define LOWPAN_FRAGN_HEAD_SIZE 0x5
-struct lowpan_addr_info {
- struct ieee802154_addr daddr;
- struct ieee802154_addr saddr;
-};
-
-static inline struct
-lowpan_addr_info *lowpan_skb_priv(const struct sk_buff *skb)
-{
- return (struct lowpan_addr_info *)(skb->data -
- sizeof(struct lowpan_addr_info));
-}
-
-/* This callback will be called from AF_PACKET and IPv6 stack, the AF_PACKET
- * sockets gives an 8 byte array for addresses only!
- *
- * TODO I think AF_PACKET DGRAM (sending/receiving) RAW (sending) makes no
- * sense here. We should disable it, the right use-case would be AF_INET6
- * RAW/DGRAM sockets.
- */
-int lowpan_header_create(struct sk_buff *skb, struct net_device *ldev,
- unsigned short type, const void *daddr,
- const void *saddr, unsigned int len)
+static void lowpan_addr_lookup(struct net_device *ldev, struct sk_buff *skb,
+ struct ieee802154_addr *daddr,
+ struct ieee802154_addr *saddr)
{
struct wpan_dev *wpan_dev = lowpan_802154_dev(ldev)->wdev->ieee802154_ptr;
- struct lowpan_addr_info *info = lowpan_skb_priv(skb);
+ struct lowpan_addr_info *info = lowpan_addr_info(skb);
struct lowpan_802154_neigh *llneigh = NULL;
const struct ipv6hdr *hdr = ipv6_hdr(skb);
struct neighbour *n;
- /* TODO:
- * if this package isn't ipv6 one, where should it be routed?
- */
- if (type != ETH_P_IPV6)
- return 0;
-
/* intra-pan communication */
- info->saddr.pan_id = wpan_dev->pan_id;
- info->daddr.pan_id = info->saddr.pan_id;
+ saddr->pan_id = wpan_dev->pan_id;
+ daddr->pan_id = saddr->pan_id;
- if (!memcmp(daddr, ldev->broadcast, EUI64_ADDR_LEN)) {
- info->daddr.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
- info->daddr.mode = IEEE802154_ADDR_SHORT;
+ if (!memcmp(info->daddr, ldev->broadcast, EUI64_ADDR_LEN)) {
+ daddr->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
+ daddr->mode = IEEE802154_ADDR_SHORT;
} else {
__le16 short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);
@@ -73,32 +48,25 @@ int lowpan_header_create(struct sk_buff *skb, struct net_device *ldev,
if (llneigh &&
lowpan_802154_is_valid_src_short_addr(short_addr)) {
- info->daddr.short_addr = short_addr;
- info->daddr.mode = IEEE802154_ADDR_SHORT;
+ daddr->short_addr = short_addr;
+ daddr->mode = IEEE802154_ADDR_SHORT;
} else {
- info->daddr.mode = IEEE802154_ADDR_LONG;
- ieee802154_be64_to_le64(&info->daddr.extended_addr,
- daddr);
+ daddr->mode = IEEE802154_ADDR_LONG;
+ ieee802154_be64_to_le64(&daddr->extended_addr,
+ info->daddr);
}
if (n)
neigh_release(n);
}
- if (!saddr) {
- if (lowpan_802154_is_valid_src_short_addr(wpan_dev->short_addr)) {
- info->saddr.mode = IEEE802154_ADDR_SHORT;
- info->saddr.short_addr = wpan_dev->short_addr;
- } else {
- info->saddr.mode = IEEE802154_ADDR_LONG;
- info->saddr.extended_addr = wpan_dev->extended_addr;
- }
+ if (lowpan_802154_is_valid_src_short_addr(wpan_dev->short_addr)) {
+ saddr->mode = IEEE802154_ADDR_SHORT;
+ saddr->short_addr = wpan_dev->short_addr;
} else {
- info->saddr.mode = IEEE802154_ADDR_LONG;
- ieee802154_be64_to_le64(&info->saddr.extended_addr, saddr);
+ saddr->mode = IEEE802154_ADDR_LONG;
+ ieee802154_be64_to_le64(&saddr->extended_addr, info->saddr);
}
-
- return 0;
}
static struct sk_buff*
@@ -227,33 +195,33 @@ err:
}
static int lowpan_header(struct sk_buff *skb, struct net_device *ldev,
- u16 *dgram_size, u16 *dgram_offset)
+ u16 *dgram_size, u16 *dgram_offset,
+ const struct ieee802154_addr *daddr,
+ const struct ieee802154_addr *saddr)
{
struct wpan_dev *wpan_dev = lowpan_802154_dev(ldev)->wdev->ieee802154_ptr;
struct ieee802154_mac_cb *cb = mac_cb_init(skb);
- struct lowpan_addr_info info;
-
- memcpy(&info, lowpan_skb_priv(skb), sizeof(info));
*dgram_size = skb->len;
- lowpan_header_compress(skb, ldev, &info.daddr, &info.saddr);
+ lowpan_header_compress(skb, ldev, daddr, saddr);
/* dgram_offset = (saved bytes after compression) + lowpan header len */
*dgram_offset = (*dgram_size - skb->len) + skb_network_header_len(skb);
cb->type = IEEE802154_FC_TYPE_DATA;
- if (info.daddr.mode == IEEE802154_ADDR_SHORT &&
- ieee802154_is_broadcast_short_addr(info.daddr.short_addr))
+ if (daddr->mode == IEEE802154_ADDR_SHORT &&
+ ieee802154_is_broadcast_short_addr(daddr->short_addr))
cb->ackreq = false;
else
cb->ackreq = wpan_dev->ackreq;
- return wpan_dev_hard_header(skb, lowpan_802154_dev(ldev)->wdev,
- &info.daddr, &info.saddr, 0);
+ return wpan_dev_hard_header(skb, lowpan_802154_dev(ldev)->wdev, daddr,
+ saddr, 0);
}
netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *ldev)
{
+ struct ieee802154_addr daddr, saddr;
struct ieee802154_hdr wpan_hdr;
int max_single, ret;
u16 dgram_size, dgram_offset;
@@ -262,6 +230,8 @@ netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *ldev)
WARN_ON_ONCE(skb->len > IPV6_MIN_MTU);
+ lowpan_addr_lookup(ldev, skb, &daddr, &saddr);
+
/* We must take a copy of the skb before we modify/replace the ipv6
* header as the header could be used elsewhere
*/
@@ -269,7 +239,8 @@ netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *ldev)
if (!skb)
return NET_XMIT_DROP;
- ret = lowpan_header(skb, ldev, &dgram_size, &dgram_offset);
+ ret = lowpan_header(skb, ldev, &dgram_size, &dgram_offset, &daddr,
+ &saddr);
if (ret < 0) {
kfree_skb(skb);
return NET_XMIT_DROP;
--
2.9.0
This patch prepares for a new bluetooth implementation which supports
correct ndisc handling and solves some races. The get the correct ndisc
handling it's better to remove the whole implementation while
implementing this handling in different layers.
Signed-off-by: Alexander Aring <[email protected]>
---
net/bluetooth/6lowpan.c | 1413 -----------------------------------------------
net/bluetooth/Makefile | 3 -
2 files changed, 1416 deletions(-)
delete mode 100644 net/bluetooth/6lowpan.c
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
deleted file mode 100644
index d020299..0000000
--- a/net/bluetooth/6lowpan.c
+++ /dev/null
@@ -1,1413 +0,0 @@
-/*
- Copyright (c) 2013-2014 Intel Corp.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 and
- only version 2 as published by the Free Software Foundation.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-*/
-
-#include <linux/if_arp.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/module.h>
-#include <linux/debugfs.h>
-
-#include <net/ipv6.h>
-#include <net/ip6_route.h>
-#include <net/addrconf.h>
-
-#include <net/bluetooth/bluetooth.h>
-#include <net/bluetooth/hci_core.h>
-#include <net/bluetooth/l2cap.h>
-
-#include <net/6lowpan.h> /* for the compression support */
-
-#define VERSION "0.1"
-
-static struct dentry *lowpan_enable_debugfs;
-static struct dentry *lowpan_control_debugfs;
-
-#define IFACE_NAME_TEMPLATE "bt%d"
-
-struct skb_cb {
- struct in6_addr addr;
- struct in6_addr gw;
- struct l2cap_chan *chan;
- int status;
-};
-#define lowpan_cb(skb) ((struct skb_cb *)((skb)->cb))
-
-/* The devices list contains those devices that we are acting
- * as a proxy. The BT 6LoWPAN device is a virtual device that
- * connects to the Bluetooth LE device. The real connection to
- * BT device is done via l2cap layer. There exists one
- * virtual device / one BT 6LoWPAN network (=hciX device).
- * The list contains struct lowpan_dev elements.
- */
-static LIST_HEAD(bt_6lowpan_devices);
-static DEFINE_SPINLOCK(devices_lock);
-
-static bool enable_6lowpan;
-
-/* We are listening incoming connections via this channel
- */
-static struct l2cap_chan *listen_chan;
-
-struct lowpan_peer {
- struct list_head list;
- struct rcu_head rcu;
- struct l2cap_chan *chan;
-
- /* peer addresses in various formats */
- unsigned char eui64_addr[EUI64_ADDR_LEN];
- struct in6_addr peer_addr;
-};
-
-struct lowpan_btle_dev {
- struct list_head list;
-
- struct hci_dev *hdev;
- struct net_device *netdev;
- struct list_head peers;
- atomic_t peer_count; /* number of items in peers list */
-
- struct work_struct delete_netdev;
- struct delayed_work notify_peers;
-};
-
-static inline struct lowpan_btle_dev *
-lowpan_btle_dev(const struct net_device *netdev)
-{
- return (struct lowpan_btle_dev *)lowpan_dev(netdev)->priv;
-}
-
-static inline void peer_add(struct lowpan_btle_dev *dev,
- struct lowpan_peer *peer)
-{
- list_add_rcu(&peer->list, &dev->peers);
- atomic_inc(&dev->peer_count);
-}
-
-static inline bool peer_del(struct lowpan_btle_dev *dev,
- struct lowpan_peer *peer)
-{
- list_del_rcu(&peer->list);
- kfree_rcu(peer, rcu);
-
- module_put(THIS_MODULE);
-
- if (atomic_dec_and_test(&dev->peer_count)) {
- BT_DBG("last peer");
- return true;
- }
-
- return false;
-}
-
-static inline struct lowpan_peer *peer_lookup_ba(struct lowpan_btle_dev *dev,
- bdaddr_t *ba, __u8 type)
-{
- struct lowpan_peer *peer;
-
- BT_DBG("peers %d addr %pMR type %d", atomic_read(&dev->peer_count),
- ba, type);
-
- rcu_read_lock();
-
- list_for_each_entry_rcu(peer, &dev->peers, list) {
- BT_DBG("dst addr %pMR dst type %d",
- &peer->chan->dst, peer->chan->dst_type);
-
- if (bacmp(&peer->chan->dst, ba))
- continue;
-
- if (type == peer->chan->dst_type) {
- rcu_read_unlock();
- return peer;
- }
- }
-
- rcu_read_unlock();
-
- return NULL;
-}
-
-static inline struct lowpan_peer *
-__peer_lookup_chan(struct lowpan_btle_dev *dev, struct l2cap_chan *chan)
-{
- struct lowpan_peer *peer;
-
- list_for_each_entry_rcu(peer, &dev->peers, list) {
- if (peer->chan == chan)
- return peer;
- }
-
- return NULL;
-}
-
-static inline struct lowpan_peer *
-__peer_lookup_conn(struct lowpan_btle_dev *dev, struct l2cap_conn *conn)
-{
- struct lowpan_peer *peer;
-
- list_for_each_entry_rcu(peer, &dev->peers, list) {
- if (peer->chan->conn == conn)
- return peer;
- }
-
- return NULL;
-}
-
-static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_btle_dev *dev,
- struct in6_addr *daddr,
- struct sk_buff *skb)
-{
- struct lowpan_peer *peer;
- struct in6_addr *nexthop;
- struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
- int count = atomic_read(&dev->peer_count);
-
- BT_DBG("peers %d addr %pI6c rt %p", count, daddr, rt);
-
- /* If we have multiple 6lowpan peers, then check where we should
- * send the packet. If only one peer exists, then we can send the
- * packet right away.
- */
- if (count == 1) {
- rcu_read_lock();
- peer = list_first_or_null_rcu(&dev->peers, struct lowpan_peer,
- list);
- rcu_read_unlock();
- return peer;
- }
-
- if (!rt) {
- nexthop = &lowpan_cb(skb)->gw;
-
- if (ipv6_addr_any(nexthop))
- return NULL;
- } else {
- nexthop = rt6_nexthop(rt, daddr);
-
- /* We need to remember the address because it is needed
- * by bt_xmit() when sending the packet. In bt_xmit(), the
- * destination routing info is not set.
- */
- memcpy(&lowpan_cb(skb)->gw, nexthop, sizeof(struct in6_addr));
- }
-
- BT_DBG("gw %pI6c", nexthop);
-
- rcu_read_lock();
-
- list_for_each_entry_rcu(peer, &dev->peers, list) {
- BT_DBG("dst addr %pMR dst type %d ip %pI6c",
- &peer->chan->dst, peer->chan->dst_type,
- &peer->peer_addr);
-
- if (!ipv6_addr_cmp(&peer->peer_addr, nexthop)) {
- rcu_read_unlock();
- return peer;
- }
- }
-
- rcu_read_unlock();
-
- return NULL;
-}
-
-static struct lowpan_peer *lookup_peer(struct l2cap_conn *conn)
-{
- struct lowpan_btle_dev *entry;
- struct lowpan_peer *peer = NULL;
-
- rcu_read_lock();
-
- list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
- peer = __peer_lookup_conn(entry, conn);
- if (peer)
- break;
- }
-
- rcu_read_unlock();
-
- return peer;
-}
-
-static struct lowpan_btle_dev *lookup_dev(struct l2cap_conn *conn)
-{
- struct lowpan_btle_dev *entry;
- struct lowpan_btle_dev *dev = NULL;
-
- rcu_read_lock();
-
- list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
- if (conn->hcon->hdev == entry->hdev) {
- dev = entry;
- break;
- }
- }
-
- rcu_read_unlock();
-
- return dev;
-}
-
-static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev)
-{
- struct sk_buff *skb_cp;
-
- skb_cp = skb_copy(skb, GFP_ATOMIC);
- if (!skb_cp)
- return NET_RX_DROP;
-
- return netif_rx_ni(skb_cp);
-}
-
-static int iphc_decompress(struct sk_buff *skb, struct net_device *netdev,
- struct l2cap_chan *chan)
-{
- const u8 *saddr, *daddr;
- struct lowpan_btle_dev *dev;
- struct lowpan_peer *peer;
-
- dev = lowpan_btle_dev(netdev);
-
- rcu_read_lock();
- peer = __peer_lookup_chan(dev, chan);
- rcu_read_unlock();
- if (!peer)
- return -EINVAL;
-
- saddr = peer->eui64_addr;
- daddr = dev->netdev->dev_addr;
-
- return lowpan_header_decompress(skb, netdev, daddr, saddr);
-}
-
-static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
- struct l2cap_chan *chan)
-{
- struct sk_buff *local_skb;
- int ret;
-
- if (!netif_running(dev))
- goto drop;
-
- if (dev->type != ARPHRD_6LOWPAN || !skb->len)
- goto drop;
-
- skb_reset_network_header(skb);
-
- skb = skb_share_check(skb, GFP_ATOMIC);
- if (!skb)
- goto drop;
-
- /* check that it's our buffer */
- if (lowpan_is_ipv6(*skb_network_header(skb))) {
- /* Pull off the 1-byte of 6lowpan header. */
- skb_pull(skb, 1);
-
- /* Copy the packet so that the IPv6 header is
- * properly aligned.
- */
- local_skb = skb_copy_expand(skb, NET_SKB_PAD - 1,
- skb_tailroom(skb), GFP_ATOMIC);
- if (!local_skb)
- goto drop;
-
- local_skb->protocol = htons(ETH_P_IPV6);
- local_skb->pkt_type = PACKET_HOST;
- local_skb->dev = dev;
-
- skb_set_transport_header(local_skb, sizeof(struct ipv6hdr));
-
- if (give_skb_to_upper(local_skb, dev) != NET_RX_SUCCESS) {
- kfree_skb(local_skb);
- goto drop;
- }
-
- dev->stats.rx_bytes += skb->len;
- dev->stats.rx_packets++;
-
- consume_skb(local_skb);
- consume_skb(skb);
- } else if (lowpan_is_iphc(*skb_network_header(skb))) {
- local_skb = skb_clone(skb, GFP_ATOMIC);
- if (!local_skb)
- goto drop;
-
- local_skb->dev = dev;
-
- ret = iphc_decompress(local_skb, dev, chan);
- if (ret < 0) {
- kfree_skb(local_skb);
- goto drop;
- }
-
- local_skb->protocol = htons(ETH_P_IPV6);
- local_skb->pkt_type = PACKET_HOST;
-
- if (give_skb_to_upper(local_skb, dev)
- != NET_RX_SUCCESS) {
- kfree_skb(local_skb);
- goto drop;
- }
-
- dev->stats.rx_bytes += skb->len;
- dev->stats.rx_packets++;
-
- consume_skb(local_skb);
- consume_skb(skb);
- } else {
- goto drop;
- }
-
- return NET_RX_SUCCESS;
-
-drop:
- dev->stats.rx_dropped++;
- return NET_RX_DROP;
-}
-
-/* Packet from BT LE device */
-static int chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
-{
- struct lowpan_btle_dev *dev;
- struct lowpan_peer *peer;
- int err;
-
- peer = lookup_peer(chan->conn);
- if (!peer)
- return -ENOENT;
-
- dev = lookup_dev(chan->conn);
- if (!dev || !dev->netdev)
- return -ENOENT;
-
- err = recv_pkt(skb, dev->netdev, chan);
- if (err) {
- BT_DBG("recv pkt %d", err);
- err = -EAGAIN;
- }
-
- return err;
-}
-
-static u8 get_addr_type_from_eui64(u8 byte)
-{
- /* Is universal(0) or local(1) bit */
- return ((byte & 0x02) ? BDADDR_LE_RANDOM : BDADDR_LE_PUBLIC);
-}
-
-static void copy_to_bdaddr(struct in6_addr *ip6_daddr, bdaddr_t *addr)
-{
- u8 *eui64 = ip6_daddr->s6_addr + 8;
-
- addr->b[0] = eui64[7];
- addr->b[1] = eui64[6];
- addr->b[2] = eui64[5];
- addr->b[3] = eui64[2];
- addr->b[4] = eui64[1];
- addr->b[5] = eui64[0];
-}
-
-static void convert_dest_bdaddr(struct in6_addr *ip6_daddr,
- bdaddr_t *addr, u8 *addr_type)
-{
- copy_to_bdaddr(ip6_daddr, addr);
-
- /* We need to toggle the U/L bit that we got from IPv6 address
- * so that we get the proper address and type of the BD address.
- */
- addr->b[5] ^= 0x02;
-
- *addr_type = get_addr_type_from_eui64(addr->b[5]);
-}
-
-static int setup_header(struct sk_buff *skb, struct net_device *netdev,
- bdaddr_t *peer_addr, u8 *peer_addr_type)
-{
- struct in6_addr ipv6_daddr;
- struct ipv6hdr *hdr;
- struct lowpan_btle_dev *dev;
- struct lowpan_peer *peer;
- bdaddr_t addr, *any = BDADDR_ANY;
- u8 *daddr = any->b;
- int err, status = 0;
-
- hdr = ipv6_hdr(skb);
-
- dev = lowpan_btle_dev(netdev);
-
- memcpy(&ipv6_daddr, &hdr->daddr, sizeof(ipv6_daddr));
-
- if (ipv6_addr_is_multicast(&ipv6_daddr)) {
- lowpan_cb(skb)->chan = NULL;
- } else {
- u8 addr_type;
-
- /* Get destination BT device from skb.
- * If there is no such peer then discard the packet.
- */
- convert_dest_bdaddr(&ipv6_daddr, &addr, &addr_type);
-
- BT_DBG("dest addr %pMR type %d IP %pI6c", &addr,
- addr_type, &ipv6_daddr);
-
- peer = peer_lookup_ba(dev, &addr, addr_type);
- if (!peer) {
- /* The packet might be sent to 6lowpan interface
- * because of routing (either via default route
- * or user set route) so get peer according to
- * the destination address.
- */
- peer = peer_lookup_dst(dev, &ipv6_daddr, skb);
- if (!peer) {
- BT_DBG("no such peer %pMR found", &addr);
- return -ENOENT;
- }
- }
-
- daddr = peer->eui64_addr;
- *peer_addr = addr;
- *peer_addr_type = addr_type;
- lowpan_cb(skb)->chan = peer->chan;
-
- status = 1;
- }
-
- lowpan_header_compress(skb, netdev, daddr, dev->netdev->dev_addr);
-
- err = dev_hard_header(skb, netdev, ETH_P_IPV6, NULL, NULL, 0);
- if (err < 0)
- return err;
-
- return status;
-}
-
-static int header_create(struct sk_buff *skb, struct net_device *netdev,
- unsigned short type, const void *_daddr,
- const void *_saddr, unsigned int len)
-{
- if (type != ETH_P_IPV6)
- return -EINVAL;
-
- return 0;
-}
-
-/* Packet to BT LE device */
-static int send_pkt(struct l2cap_chan *chan, struct sk_buff *skb,
- struct net_device *netdev)
-{
- struct msghdr msg;
- struct kvec iv;
- int err;
-
- /* Remember the skb so that we can send EAGAIN to the caller if
- * we run out of credits.
- */
- chan->data = skb;
-
- iv.iov_base = skb->data;
- iv.iov_len = skb->len;
-
- memset(&msg, 0, sizeof(msg));
- iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, &iv, 1, skb->len);
-
- err = l2cap_chan_send(chan, &msg, skb->len);
- if (err > 0) {
- netdev->stats.tx_bytes += err;
- netdev->stats.tx_packets++;
- return 0;
- }
-
- if (!err)
- err = lowpan_cb(skb)->status;
-
- if (err < 0) {
- if (err == -EAGAIN)
- netdev->stats.tx_dropped++;
- else
- netdev->stats.tx_errors++;
- }
-
- return err;
-}
-
-static int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
-{
- struct sk_buff *local_skb;
- struct lowpan_btle_dev *entry;
- int err = 0;
-
- rcu_read_lock();
-
- list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
- struct lowpan_peer *pentry;
- struct lowpan_btle_dev *dev;
-
- if (entry->netdev != netdev)
- continue;
-
- dev = lowpan_btle_dev(entry->netdev);
-
- list_for_each_entry_rcu(pentry, &dev->peers, list) {
- int ret;
-
- local_skb = skb_clone(skb, GFP_ATOMIC);
-
- BT_DBG("xmit %s to %pMR type %d IP %pI6c chan %p",
- netdev->name,
- &pentry->chan->dst, pentry->chan->dst_type,
- &pentry->peer_addr, pentry->chan);
- ret = send_pkt(pentry->chan, local_skb, netdev);
- if (ret < 0)
- err = ret;
-
- kfree_skb(local_skb);
- }
- }
-
- rcu_read_unlock();
-
- return err;
-}
-
-static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
-{
- int err = 0;
- bdaddr_t addr;
- u8 addr_type;
-
- /* We must take a copy of the skb before we modify/replace the ipv6
- * header as the header could be used elsewhere
- */
- skb = skb_unshare(skb, GFP_ATOMIC);
- if (!skb)
- return NET_XMIT_DROP;
-
- /* Return values from setup_header()
- * <0 - error, packet is dropped
- * 0 - this is a multicast packet
- * 1 - this is unicast packet
- */
- err = setup_header(skb, netdev, &addr, &addr_type);
- if (err < 0) {
- kfree_skb(skb);
- return NET_XMIT_DROP;
- }
-
- if (err) {
- if (lowpan_cb(skb)->chan) {
- BT_DBG("xmit %s to %pMR type %d IP %pI6c chan %p",
- netdev->name, &addr, addr_type,
- &lowpan_cb(skb)->addr, lowpan_cb(skb)->chan);
- err = send_pkt(lowpan_cb(skb)->chan, skb, netdev);
- } else {
- err = -ENOENT;
- }
- } else {
- /* We need to send the packet to every device behind this
- * interface.
- */
- err = send_mcast_pkt(skb, netdev);
- }
-
- dev_kfree_skb(skb);
-
- if (err)
- BT_DBG("ERROR: xmit failed (%d)", err);
-
- return err < 0 ? NET_XMIT_DROP : err;
-}
-
-static int bt_dev_init(struct net_device *dev)
-{
- netdev_lockdep_set_classes(dev);
-
- return 0;
-}
-
-static const struct net_device_ops netdev_ops = {
- .ndo_init = bt_dev_init,
- .ndo_start_xmit = bt_xmit,
-};
-
-static struct header_ops header_ops = {
- .create = header_create,
-};
-
-static void netdev_setup(struct net_device *dev)
-{
- dev->hard_header_len = 0;
- dev->needed_tailroom = 0;
- dev->flags = IFF_RUNNING | IFF_POINTOPOINT |
- IFF_MULTICAST;
- dev->watchdog_timeo = 0;
-
- dev->netdev_ops = &netdev_ops;
- dev->header_ops = &header_ops;
- dev->destructor = free_netdev;
-}
-
-static struct device_type bt_type = {
- .name = "bluetooth",
-};
-
-static void set_addr(u8 *eui, u8 *addr, u8 addr_type)
-{
- /* addr is the BT address in little-endian format */
- eui[0] = addr[5];
- eui[1] = addr[4];
- eui[2] = addr[3];
- eui[3] = 0xFF;
- eui[4] = 0xFE;
- eui[5] = addr[2];
- eui[6] = addr[1];
- eui[7] = addr[0];
-
- /* Universal/local bit set, BT 6lowpan draft ch. 3.2.1 */
- if (addr_type == BDADDR_LE_PUBLIC)
- eui[0] &= ~0x02;
- else
- eui[0] |= 0x02;
-
- BT_DBG("type %d addr %*phC", addr_type, 8, eui);
-}
-
-static void set_dev_addr(struct net_device *netdev, bdaddr_t *addr,
- u8 addr_type)
-{
- netdev->addr_assign_type = NET_ADDR_PERM;
- set_addr(netdev->dev_addr, addr->b, addr_type);
-}
-
-static void ifup(struct net_device *netdev)
-{
- int err;
-
- rtnl_lock();
- err = dev_open(netdev);
- if (err < 0)
- BT_INFO("iface %s cannot be opened (%d)", netdev->name, err);
- rtnl_unlock();
-}
-
-static void ifdown(struct net_device *netdev)
-{
- int err;
-
- rtnl_lock();
- err = dev_close(netdev);
- if (err < 0)
- BT_INFO("iface %s cannot be closed (%d)", netdev->name, err);
- rtnl_unlock();
-}
-
-static void do_notify_peers(struct work_struct *work)
-{
- struct lowpan_btle_dev *dev = container_of(work, struct lowpan_btle_dev,
- notify_peers.work);
-
- netdev_notify_peers(dev->netdev); /* send neighbour adv at startup */
-}
-
-static bool is_bt_6lowpan(struct hci_conn *hcon)
-{
- if (hcon->type != LE_LINK)
- return false;
-
- if (!enable_6lowpan)
- return false;
-
- return true;
-}
-
-static struct l2cap_chan *chan_create(void)
-{
- struct l2cap_chan *chan;
-
- chan = l2cap_chan_create();
- if (!chan)
- return NULL;
-
- l2cap_chan_set_defaults(chan);
-
- chan->chan_type = L2CAP_CHAN_CONN_ORIENTED;
- chan->mode = L2CAP_MODE_LE_FLOWCTL;
- chan->imtu = 1280;
-
- return chan;
-}
-
-static void set_ip_addr_bits(u8 addr_type, u8 *addr)
-{
- if (addr_type == BDADDR_LE_PUBLIC)
- *addr |= 0x02;
- else
- *addr &= ~0x02;
-}
-
-static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan,
- struct lowpan_btle_dev *dev)
-{
- struct lowpan_peer *peer;
-
- peer = kzalloc(sizeof(*peer), GFP_ATOMIC);
- if (!peer)
- return NULL;
-
- peer->chan = chan;
- memset(&peer->peer_addr, 0, sizeof(struct in6_addr));
-
- /* RFC 2464 ch. 5 */
- peer->peer_addr.s6_addr[0] = 0xFE;
- peer->peer_addr.s6_addr[1] = 0x80;
- set_addr((u8 *)&peer->peer_addr.s6_addr + 8, chan->dst.b,
- chan->dst_type);
-
- memcpy(&peer->eui64_addr, (u8 *)&peer->peer_addr.s6_addr + 8,
- EUI64_ADDR_LEN);
-
- /* IPv6 address needs to have the U/L bit set properly so toggle
- * it back here.
- */
- set_ip_addr_bits(chan->dst_type, (u8 *)&peer->peer_addr.s6_addr + 8);
-
- spin_lock(&devices_lock);
- INIT_LIST_HEAD(&peer->list);
- peer_add(dev, peer);
- spin_unlock(&devices_lock);
-
- /* Notifying peers about us needs to be done without locks held */
- INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers);
- schedule_delayed_work(&dev->notify_peers, msecs_to_jiffies(100));
-
- return peer->chan;
-}
-
-static int setup_netdev(struct l2cap_chan *chan, struct lowpan_btle_dev **dev)
-{
- struct net_device *netdev;
- int err = 0;
-
- netdev = alloc_netdev(LOWPAN_PRIV_SIZE(sizeof(struct lowpan_btle_dev)),
- IFACE_NAME_TEMPLATE, NET_NAME_UNKNOWN,
- netdev_setup);
- if (!netdev)
- return -ENOMEM;
-
- set_dev_addr(netdev, &chan->src, chan->src_type);
-
- netdev->netdev_ops = &netdev_ops;
- SET_NETDEV_DEV(netdev, &chan->conn->hcon->hdev->dev);
- SET_NETDEV_DEVTYPE(netdev, &bt_type);
-
- *dev = lowpan_btle_dev(netdev);
- (*dev)->netdev = netdev;
- (*dev)->hdev = chan->conn->hcon->hdev;
- INIT_LIST_HEAD(&(*dev)->peers);
-
- spin_lock(&devices_lock);
- INIT_LIST_HEAD(&(*dev)->list);
- list_add_rcu(&(*dev)->list, &bt_6lowpan_devices);
- spin_unlock(&devices_lock);
-
- err = lowpan_register_netdev(netdev, LOWPAN_LLTYPE_BTLE);
- if (err < 0) {
- BT_INFO("register_netdev failed %d", err);
- spin_lock(&devices_lock);
- list_del_rcu(&(*dev)->list);
- spin_unlock(&devices_lock);
- free_netdev(netdev);
- goto out;
- }
-
- BT_DBG("ifindex %d peer bdaddr %pMR type %d my addr %pMR type %d",
- netdev->ifindex, &chan->dst, chan->dst_type,
- &chan->src, chan->src_type);
- set_bit(__LINK_STATE_PRESENT, &netdev->state);
-
- return 0;
-
-out:
- return err;
-}
-
-static inline void chan_ready_cb(struct l2cap_chan *chan)
-{
- struct lowpan_btle_dev *dev;
-
- dev = lookup_dev(chan->conn);
-
- BT_DBG("chan %p conn %p dev %p", chan, chan->conn, dev);
-
- if (!dev) {
- if (setup_netdev(chan, &dev) < 0) {
- l2cap_chan_del(chan, -ENOENT);
- return;
- }
- }
-
- if (!try_module_get(THIS_MODULE))
- return;
-
- add_peer_chan(chan, dev);
- ifup(dev->netdev);
-}
-
-static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan)
-{
- struct l2cap_chan *chan;
-
- chan = chan_create();
- if (!chan)
- return NULL;
-
- chan->ops = pchan->ops;
-
- BT_DBG("chan %p pchan %p", chan, pchan);
-
- return chan;
-}
-
-static void delete_netdev(struct work_struct *work)
-{
- struct lowpan_btle_dev *entry = container_of(work,
- struct lowpan_btle_dev,
- delete_netdev);
-
- lowpan_unregister_netdev(entry->netdev);
-
- /* The entry pointer is deleted by the netdev destructor. */
-}
-
-static void chan_close_cb(struct l2cap_chan *chan)
-{
- struct lowpan_btle_dev *entry;
- struct lowpan_btle_dev *dev = NULL;
- struct lowpan_peer *peer;
- int err = -ENOENT;
- bool last = false, remove = true;
-
- BT_DBG("chan %p conn %p", chan, chan->conn);
-
- if (chan->conn && chan->conn->hcon) {
- if (!is_bt_6lowpan(chan->conn->hcon))
- return;
-
- /* If conn is set, then the netdev is also there and we should
- * not remove it.
- */
- remove = false;
- }
-
- spin_lock(&devices_lock);
-
- list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
- dev = lowpan_btle_dev(entry->netdev);
- peer = __peer_lookup_chan(dev, chan);
- if (peer) {
- last = peer_del(dev, peer);
- err = 0;
-
- BT_DBG("dev %p removing %speer %p", dev,
- last ? "last " : "1 ", peer);
- BT_DBG("chan %p orig refcnt %d", chan,
- atomic_read(&chan->kref.refcount));
-
- l2cap_chan_put(chan);
- break;
- }
- }
-
- if (!err && last && dev && !atomic_read(&dev->peer_count)) {
- spin_unlock(&devices_lock);
-
- cancel_delayed_work_sync(&dev->notify_peers);
-
- ifdown(dev->netdev);
-
- if (remove) {
- INIT_WORK(&entry->delete_netdev, delete_netdev);
- schedule_work(&entry->delete_netdev);
- }
- } else {
- spin_unlock(&devices_lock);
- }
-
- return;
-}
-
-static void chan_state_change_cb(struct l2cap_chan *chan, int state, int err)
-{
- BT_DBG("chan %p conn %p state %s err %d", chan, chan->conn,
- state_to_string(state), err);
-}
-
-static struct sk_buff *chan_alloc_skb_cb(struct l2cap_chan *chan,
- unsigned long hdr_len,
- unsigned long len, int nb)
-{
- /* Note that we must allocate using GFP_ATOMIC here as
- * this function is called originally from netdev hard xmit
- * function in atomic context.
- */
- return bt_skb_alloc(hdr_len + len, GFP_ATOMIC);
-}
-
-static void chan_suspend_cb(struct l2cap_chan *chan)
-{
- struct sk_buff *skb = chan->data;
-
- BT_DBG("chan %p conn %p skb %p", chan, chan->conn, skb);
-
- if (!skb)
- return;
-
- lowpan_cb(skb)->status = -EAGAIN;
-}
-
-static void chan_resume_cb(struct l2cap_chan *chan)
-{
- struct sk_buff *skb = chan->data;
-
- BT_DBG("chan %p conn %p skb %p", chan, chan->conn, skb);
-
- if (!skb)
- return;
-
- lowpan_cb(skb)->status = 0;
-}
-
-static long chan_get_sndtimeo_cb(struct l2cap_chan *chan)
-{
- return L2CAP_CONN_TIMEOUT;
-}
-
-static const struct l2cap_ops bt_6lowpan_chan_ops = {
- .name = "L2CAP 6LoWPAN channel",
- .new_connection = chan_new_conn_cb,
- .recv = chan_recv_cb,
- .close = chan_close_cb,
- .state_change = chan_state_change_cb,
- .ready = chan_ready_cb,
- .resume = chan_resume_cb,
- .suspend = chan_suspend_cb,
- .get_sndtimeo = chan_get_sndtimeo_cb,
- .alloc_skb = chan_alloc_skb_cb,
-
- .teardown = l2cap_chan_no_teardown,
- .defer = l2cap_chan_no_defer,
- .set_shutdown = l2cap_chan_no_set_shutdown,
-};
-
-static inline __u8 bdaddr_type(__u8 type)
-{
- if (type == ADDR_LE_DEV_PUBLIC)
- return BDADDR_LE_PUBLIC;
- else
- return BDADDR_LE_RANDOM;
-}
-
-static int bt_6lowpan_connect(bdaddr_t *addr, u8 dst_type)
-{
- struct l2cap_chan *chan;
- int err;
-
- chan = chan_create();
- if (!chan)
- return -EINVAL;
-
- chan->ops = &bt_6lowpan_chan_ops;
-
- err = l2cap_chan_connect(chan, cpu_to_le16(L2CAP_PSM_IPSP), 0,
- addr, dst_type);
-
- BT_DBG("chan %p err %d", chan, err);
- if (err < 0)
- l2cap_chan_put(chan);
-
- return err;
-}
-
-static int bt_6lowpan_disconnect(struct l2cap_conn *conn, u8 dst_type)
-{
- struct lowpan_peer *peer;
-
- BT_DBG("conn %p dst type %d", conn, dst_type);
-
- peer = lookup_peer(conn);
- if (!peer)
- return -ENOENT;
-
- BT_DBG("peer %p chan %p", peer, peer->chan);
-
- l2cap_chan_close(peer->chan, ENOENT);
-
- return 0;
-}
-
-static struct l2cap_chan *bt_6lowpan_listen(void)
-{
- bdaddr_t *addr = BDADDR_ANY;
- struct l2cap_chan *chan;
- int err;
-
- if (!enable_6lowpan)
- return NULL;
-
- chan = chan_create();
- if (!chan)
- return NULL;
-
- chan->ops = &bt_6lowpan_chan_ops;
- chan->state = BT_LISTEN;
- chan->src_type = BDADDR_LE_PUBLIC;
-
- atomic_set(&chan->nesting, L2CAP_NESTING_PARENT);
-
- BT_DBG("chan %p src type %d", chan, chan->src_type);
-
- err = l2cap_add_psm(chan, addr, cpu_to_le16(L2CAP_PSM_IPSP));
- if (err) {
- l2cap_chan_put(chan);
- BT_ERR("psm cannot be added err %d", err);
- return NULL;
- }
-
- return chan;
-}
-
-static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
- struct l2cap_conn **conn)
-{
- struct hci_conn *hcon;
- struct hci_dev *hdev;
- bdaddr_t *src = BDADDR_ANY;
- int n;
-
- n = sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %hhu",
- &addr->b[5], &addr->b[4], &addr->b[3],
- &addr->b[2], &addr->b[1], &addr->b[0],
- addr_type);
-
- if (n < 7)
- return -EINVAL;
-
- hdev = hci_get_route(addr, src);
- if (!hdev)
- return -ENOENT;
-
- hci_dev_lock(hdev);
- hcon = hci_conn_hash_lookup_le(hdev, addr, *addr_type);
- hci_dev_unlock(hdev);
-
- if (!hcon)
- return -ENOENT;
-
- *conn = (struct l2cap_conn *)hcon->l2cap_data;
-
- BT_DBG("conn %p dst %pMR type %d", *conn, &hcon->dst, hcon->dst_type);
-
- return 0;
-}
-
-static void disconnect_all_peers(void)
-{
- struct lowpan_btle_dev *entry;
- struct lowpan_peer *peer, *tmp_peer, *new_peer;
- struct list_head peers;
-
- INIT_LIST_HEAD(&peers);
-
- /* We make a separate list of peers as the close_cb() will
- * modify the device peers list so it is better not to mess
- * with the same list at the same time.
- */
-
- rcu_read_lock();
-
- list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
- list_for_each_entry_rcu(peer, &entry->peers, list) {
- new_peer = kmalloc(sizeof(*new_peer), GFP_ATOMIC);
- if (!new_peer)
- break;
-
- new_peer->chan = peer->chan;
- INIT_LIST_HEAD(&new_peer->list);
-
- list_add(&new_peer->list, &peers);
- }
- }
-
- rcu_read_unlock();
-
- spin_lock(&devices_lock);
- list_for_each_entry_safe(peer, tmp_peer, &peers, list) {
- l2cap_chan_close(peer->chan, ENOENT);
-
- list_del_rcu(&peer->list);
- kfree_rcu(peer, rcu);
- }
- spin_unlock(&devices_lock);
-}
-
-struct set_enable {
- struct work_struct work;
- bool flag;
-};
-
-static void do_enable_set(struct work_struct *work)
-{
- struct set_enable *set_enable = container_of(work,
- struct set_enable, work);
-
- if (!set_enable->flag || enable_6lowpan != set_enable->flag)
- /* Disconnect existing connections if 6lowpan is
- * disabled
- */
- disconnect_all_peers();
-
- enable_6lowpan = set_enable->flag;
-
- if (listen_chan) {
- l2cap_chan_close(listen_chan, 0);
- l2cap_chan_put(listen_chan);
- }
-
- listen_chan = bt_6lowpan_listen();
-
- kfree(set_enable);
-}
-
-static int lowpan_enable_set(void *data, u64 val)
-{
- struct set_enable *set_enable;
-
- set_enable = kzalloc(sizeof(*set_enable), GFP_KERNEL);
- if (!set_enable)
- return -ENOMEM;
-
- set_enable->flag = !!val;
- INIT_WORK(&set_enable->work, do_enable_set);
-
- schedule_work(&set_enable->work);
-
- return 0;
-}
-
-static int lowpan_enable_get(void *data, u64 *val)
-{
- *val = enable_6lowpan;
- return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(lowpan_enable_fops, lowpan_enable_get,
- lowpan_enable_set, "%llu\n");
-
-static ssize_t lowpan_control_write(struct file *fp,
- const char __user *user_buffer,
- size_t count,
- loff_t *position)
-{
- char buf[32];
- size_t buf_size = min(count, sizeof(buf) - 1);
- int ret;
- bdaddr_t addr;
- u8 addr_type;
- struct l2cap_conn *conn = NULL;
-
- if (copy_from_user(buf, user_buffer, buf_size))
- return -EFAULT;
-
- buf[buf_size] = '\0';
-
- if (memcmp(buf, "connect ", 8) == 0) {
- ret = get_l2cap_conn(&buf[8], &addr, &addr_type, &conn);
- if (ret == -EINVAL)
- return ret;
-
- if (listen_chan) {
- l2cap_chan_close(listen_chan, 0);
- l2cap_chan_put(listen_chan);
- listen_chan = NULL;
- }
-
- if (conn) {
- struct lowpan_peer *peer;
-
- if (!is_bt_6lowpan(conn->hcon))
- return -EINVAL;
-
- peer = lookup_peer(conn);
- if (peer) {
- BT_DBG("6LoWPAN connection already exists");
- return -EALREADY;
- }
-
- BT_DBG("conn %p dst %pMR type %d user %d", conn,
- &conn->hcon->dst, conn->hcon->dst_type,
- addr_type);
- }
-
- ret = bt_6lowpan_connect(&addr, addr_type);
- if (ret < 0)
- return ret;
-
- return count;
- }
-
- if (memcmp(buf, "disconnect ", 11) == 0) {
- ret = get_l2cap_conn(&buf[11], &addr, &addr_type, &conn);
- if (ret < 0)
- return ret;
-
- ret = bt_6lowpan_disconnect(conn, addr_type);
- if (ret < 0)
- return ret;
-
- return count;
- }
-
- return count;
-}
-
-static int lowpan_control_show(struct seq_file *f, void *ptr)
-{
- struct lowpan_btle_dev *entry;
- struct lowpan_peer *peer;
-
- spin_lock(&devices_lock);
-
- list_for_each_entry(entry, &bt_6lowpan_devices, list) {
- list_for_each_entry(peer, &entry->peers, list)
- seq_printf(f, "%pMR (type %u)\n",
- &peer->chan->dst, peer->chan->dst_type);
- }
-
- spin_unlock(&devices_lock);
-
- return 0;
-}
-
-static int lowpan_control_open(struct inode *inode, struct file *file)
-{
- return single_open(file, lowpan_control_show, inode->i_private);
-}
-
-static const struct file_operations lowpan_control_fops = {
- .open = lowpan_control_open,
- .read = seq_read,
- .write = lowpan_control_write,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static void disconnect_devices(void)
-{
- struct lowpan_btle_dev *entry, *tmp, *new_dev;
- struct list_head devices;
-
- INIT_LIST_HEAD(&devices);
-
- /* We make a separate list of devices because the unregister_netdev()
- * will call device_event() which will also want to modify the same
- * devices list.
- */
-
- rcu_read_lock();
-
- list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
- new_dev = kmalloc(sizeof(*new_dev), GFP_ATOMIC);
- if (!new_dev)
- break;
-
- new_dev->netdev = entry->netdev;
- INIT_LIST_HEAD(&new_dev->list);
-
- list_add_rcu(&new_dev->list, &devices);
- }
-
- rcu_read_unlock();
-
- list_for_each_entry_safe(entry, tmp, &devices, list) {
- ifdown(entry->netdev);
- BT_DBG("Unregistering netdev %s %p",
- entry->netdev->name, entry->netdev);
- lowpan_unregister_netdev(entry->netdev);
- kfree(entry);
- }
-}
-
-static int device_event(struct notifier_block *unused,
- unsigned long event, void *ptr)
-{
- struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
- struct lowpan_btle_dev *entry;
-
- if (netdev->type != ARPHRD_6LOWPAN)
- return NOTIFY_DONE;
-
- switch (event) {
- case NETDEV_UNREGISTER:
- spin_lock(&devices_lock);
- list_for_each_entry(entry, &bt_6lowpan_devices, list) {
- if (entry->netdev == netdev) {
- BT_DBG("Unregistered netdev %s %p",
- netdev->name, netdev);
- list_del(&entry->list);
- break;
- }
- }
- spin_unlock(&devices_lock);
- break;
- }
-
- return NOTIFY_DONE;
-}
-
-static struct notifier_block bt_6lowpan_dev_notifier = {
- .notifier_call = device_event,
-};
-
-static int __init bt_6lowpan_init(void)
-{
- lowpan_enable_debugfs = debugfs_create_file("6lowpan_enable", 0644,
- bt_debugfs, NULL,
- &lowpan_enable_fops);
- lowpan_control_debugfs = debugfs_create_file("6lowpan_control", 0644,
- bt_debugfs, NULL,
- &lowpan_control_fops);
-
- return register_netdevice_notifier(&bt_6lowpan_dev_notifier);
-}
-
-static void __exit bt_6lowpan_exit(void)
-{
- debugfs_remove(lowpan_enable_debugfs);
- debugfs_remove(lowpan_control_debugfs);
-
- if (listen_chan) {
- l2cap_chan_close(listen_chan, 0);
- l2cap_chan_put(listen_chan);
- }
-
- disconnect_devices();
-
- unregister_netdevice_notifier(&bt_6lowpan_dev_notifier);
-}
-
-module_init(bt_6lowpan_init);
-module_exit(bt_6lowpan_exit);
-
-MODULE_AUTHOR("Jukka Rissanen <[email protected]>");
-MODULE_DESCRIPTION("Bluetooth 6LoWPAN");
-MODULE_VERSION(VERSION);
-MODULE_LICENSE("GPL");
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index b3ff12e..e347828 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -7,9 +7,6 @@ obj-$(CONFIG_BT_RFCOMM) += rfcomm/
obj-$(CONFIG_BT_BNEP) += bnep/
obj-$(CONFIG_BT_CMTP) += cmtp/
obj-$(CONFIG_BT_HIDP) += hidp/
-obj-$(CONFIG_BT_6LOWPAN) += bluetooth_6lowpan.o
-
-bluetooth_6lowpan-y := 6lowpan.o
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o lib.o \
--
2.9.0
My observations with btle 6lowpan was that such parameter doesn't
exists. IPHC will not increase the packet size of IPv6 afterwards, but
decrease it.
I thought about that LOWPAN_IPHC are the 2 bytes headers, but we will
always save two bytes because the payload_length of ipv6 will be elided.
Then I thought the CID byte will increase the payload at one byte, this
is correct but when we add the CID byte, we need to compress at
worst-case 8 bytes.
After that I thought the nhc id will be at least increase currently the
payload to one byte. This is correct, but when we set the NHC bit, then
we save one byte because ipv6 next header byte will be elided. That's
why everything should be fine until we have NHC's which have two bytes
length until or NHC compression use more bytes than raw next header
format.
Signed-off-by: Alexander Aring <[email protected]>
---
include/net/6lowpan.h | 7 -------
net/ieee802154/6lowpan/core.c | 6 ++----
2 files changed, 2 insertions(+), 11 deletions(-)
diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
index 6d56be7..7f4b63c 100644
--- a/include/net/6lowpan.h
+++ b/include/net/6lowpan.h
@@ -68,15 +68,8 @@
* possible inline data.
*/
#define LOWPAN_NHC_MAX_HDR_LEN (sizeof(struct udphdr))
-/* Max IPHC Header len without IPv6 hdr specific inline data.
- * Useful for getting the "extra" bytes we need at worst case compression.
- *
- * LOWPAN_IPHC + CID + LOWPAN_NHC_MAX_ID_LEN
- */
-#define LOWPAN_IPHC_MAX_HEADER_LEN (2 + 1 + LOWPAN_NHC_MAX_ID_LEN)
/* Maximum worst case IPHC header buffer size */
#define LOWPAN_IPHC_MAX_HC_BUF_LEN (sizeof(struct ipv6hdr) + \
- LOWPAN_IPHC_MAX_HEADER_LEN + \
LOWPAN_NHC_MAX_HDR_LEN)
/* SCI/DCI is 4 bit width, so we have maximum 16 entries */
#define LOWPAN_IPHC_CTX_TABLE_SIZE (1 << 4)
diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c
index 6afb8cd..f70edcc 100644
--- a/net/ieee802154/6lowpan/core.c
+++ b/net/ieee802154/6lowpan/core.c
@@ -152,11 +152,9 @@ static int lowpan_newlink(struct net *src_net, struct net_device *ldev,
/* We need headroom for possible wpan_dev_hard_header call and tailroom
* for encryption/fcs handling. The lowpan interface will replace
* the IPv6 header with 6LoWPAN header. At worst case the 6LoWPAN
- * header has LOWPAN_IPHC_MAX_HEADER_LEN more bytes than the IPv6
- * header.
+ * header has one byte more for possible raw IPv6 dispatch.
*/
- ldev->needed_headroom = LOWPAN_IPHC_MAX_HEADER_LEN +
- wdev->needed_headroom;
+ ldev->needed_headroom = 1 + wdev->needed_headroom;
ldev->needed_tailroom = wdev->needed_tailroom;
ldev->neigh_priv_len = sizeof(struct lowpan_802154_neigh);
--
2.9.0
The skb->pkt_type need to be set by L2, but on 6LoWPAN there exists L2
e.g. BTLE which doesn't has multicast addressing. If it's a multicast or
not is detected by IPHC headers multicast bit. The IPv6 layer will
evaluate this pkt_type, so we force set this type while uncompressing.
Should be okay for 802.15.4 as well.
Signed-off-by: Alexander Aring <[email protected]>
---
net/6lowpan/iphc.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c
index 79f1fa2..fb5f6fa 100644
--- a/net/6lowpan/iphc.c
+++ b/net/6lowpan/iphc.c
@@ -666,6 +666,8 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
switch (iphc1 & (LOWPAN_IPHC_M | LOWPAN_IPHC_DAC)) {
case LOWPAN_IPHC_M | LOWPAN_IPHC_DAC:
+ skb->pkt_type = PACKET_BROADCAST;
+
spin_lock_bh(&lowpan_dev(dev)->ctx.lock);
ci = lowpan_iphc_ctx_get_by_id(dev, LOWPAN_IPHC_CID_DCI(cid));
if (!ci) {
@@ -681,11 +683,15 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
spin_unlock_bh(&lowpan_dev(dev)->ctx.lock);
break;
case LOWPAN_IPHC_M:
+ skb->pkt_type = PACKET_BROADCAST;
+
/* multicast */
err = lowpan_uncompress_multicast_daddr(skb, &hdr.daddr,
iphc1 & LOWPAN_IPHC_DAM_MASK);
break;
case LOWPAN_IPHC_DAC:
+ skb->pkt_type = PACKET_HOST;
+
spin_lock_bh(&lowpan_dev(dev)->ctx.lock);
ci = lowpan_iphc_ctx_get_by_id(dev, LOWPAN_IPHC_CID_DCI(cid));
if (!ci) {
@@ -701,6 +707,8 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
spin_unlock_bh(&lowpan_dev(dev)->ctx.lock);
break;
default:
+ skb->pkt_type = PACKET_HOST;
+
err = lowpan_iphc_uncompress_addr(skb, dev, &hdr.daddr,
iphc1 & LOWPAN_IPHC_DAM_MASK,
daddr);
--
2.9.0
This patch adds support for 48 bit 6LoWPAN address length
autoconfiguration which is the case for BTLE 6LoWPAN.
Signed-off-by: Alexander Aring <[email protected]>
---
net/ipv6/addrconf.c | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index a1f6b7b..aab9b0d 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -2007,12 +2007,21 @@ static void addrconf_leave_anycast(struct inet6_ifaddr *ifp)
__ipv6_dev_ac_dec(ifp->idev, &addr);
}
-static int addrconf_ifid_eui64(u8 *eui, struct net_device *dev)
+static int addrconf_ifid_6lowpan(u8 *eui, struct net_device *dev)
{
- if (dev->addr_len != EUI64_ADDR_LEN)
+ switch (dev->addr_len) {
+ case ETH_ALEN:
+ return addrconf_ifid_eui48(eui, dev);
+ case EUI64_ADDR_LEN:
+ if (dev->addr_len != EUI64_ADDR_LEN)
+ return -1;
+ memcpy(eui, dev->dev_addr, EUI64_ADDR_LEN);
+ eui[0] ^= 2;
+ break;
+ default:
return -1;
- memcpy(eui, dev->dev_addr, EUI64_ADDR_LEN);
- eui[0] ^= 2;
+ }
+
return 0;
}
@@ -2103,7 +2112,7 @@ static int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
case ARPHRD_IPGRE:
return addrconf_ifid_gre(eui, dev);
case ARPHRD_6LOWPAN:
- return addrconf_ifid_eui64(eui, dev);
+ return addrconf_ifid_6lowpan(eui, dev);
case ARPHRD_IEEE1394:
return addrconf_ifid_ieee1394(eui, dev);
case ARPHRD_TUNNEL6:
--
2.9.0
These flags should be all the same for 6LoWPAN so move it to 6LoWPAN generic.
Signed-off-by: Alexander Aring <[email protected]>
---
net/6lowpan/core.c | 1 +
net/ieee802154/6lowpan/core.c | 1 -
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
index a978000..6b7de14 100644
--- a/net/6lowpan/core.c
+++ b/net/6lowpan/core.c
@@ -62,6 +62,7 @@ int lowpan_register_netdevice(struct net_device *dev,
dev->type = ARPHRD_6LOWPAN;
dev->mtu = IPV6_MIN_MTU;
dev->priv_flags |= IFF_NO_QUEUE;
+ dev->flags = IFF_BROADCAST | IFF_MULTICAST;
dev->header_ops = &header_ops;
diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c
index 228a711..a60abad 100644
--- a/net/ieee802154/6lowpan/core.c
+++ b/net/ieee802154/6lowpan/core.c
@@ -92,7 +92,6 @@ static void lowpan_setup(struct net_device *ldev)
memset(ldev->broadcast, 0xff, IEEE802154_ADDR_LEN);
/* We need an ipv6hdr as minimum len when calling xmit */
ldev->hard_header_len = sizeof(struct ipv6hdr);
- ldev->flags = IFF_BROADCAST | IFF_MULTICAST;
ldev->netdev_ops = &lowpan_netdev_ops;
ldev->destructor = free_netdev;
--
2.9.0
This patch adds right handling for uncompress/compress L3 addresses
which are compressed by BTLE L2 address.
Signed-off-by: Alexander Aring <[email protected]>
---
net/6lowpan/iphc.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 103 insertions(+)
diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c
index fb5f6fa..e93f656 100644
--- a/net/6lowpan/iphc.c
+++ b/net/6lowpan/iphc.c
@@ -50,6 +50,7 @@
#include <linux/if_arp.h>
#include <linux/netdevice.h>
+#include <net/bluetooth/bluetooth.h>
#include <net/6lowpan.h>
#include <net/ipv6.h>
@@ -186,6 +187,27 @@ lowpan_iphc_uncompress_802154_lladdr(struct in6_addr *ipaddr,
}
}
+static inline void
+lowpan_iphc_uncompress_btle_lladdr(struct in6_addr *ipaddr, const void *lladdr)
+{
+ bdaddr_t be_bdaddr;
+
+ /* ipv6 addr needs big endian form here
+ * TODO make src in baswap const?
+ */
+ baswap(&be_bdaddr, (bdaddr_t *)lladdr);
+
+ ipaddr->s6_addr[0] = 0xFE;
+ ipaddr->s6_addr[1] = 0x80;
+
+ memcpy(&ipaddr->s6_addr[8], be_bdaddr.b, 3);
+ memcpy(&ipaddr->s6_addr[13], be_bdaddr.b + 3, 3);
+
+ ipaddr->s6_addr[11] = 0xFF;
+ ipaddr->s6_addr[12] = 0xFE;
+ ipaddr->s6_addr[8] ^= 2;
+}
+
static struct lowpan_iphc_ctx *
lowpan_iphc_ctx_get_by_id(const struct net_device *dev, u8 id)
{
@@ -315,11 +337,19 @@ static int lowpan_iphc_uncompress_addr(struct sk_buff *skb,
case LOWPAN_IPHC_SAM_11:
case LOWPAN_IPHC_DAM_11:
fail = false;
+ /* TODO this can work to work as length here, ipv6 addrconf
+ * has similar functionality to generate autoconfigured
+ * addresses - use shared code here.
+ */
switch (lowpan_dev(dev)->lltype) {
case LOWPAN_LLTYPE_IEEE802154:
lowpan_iphc_uncompress_802154_lladdr(ipaddr, lladdr);
break;
+ case LOWPAN_LLTYPE_BTLE:
+ lowpan_iphc_uncompress_btle_lladdr(ipaddr, lladdr);
+ break;
default:
+ /* TODO remove? */
lowpan_iphc_uncompress_eui64_lladdr(ipaddr, lladdr);
break;
}
@@ -380,6 +410,9 @@ static int lowpan_iphc_uncompress_ctx_addr(struct sk_buff *skb,
case LOWPAN_LLTYPE_IEEE802154:
lowpan_iphc_uncompress_802154_lladdr(ipaddr, lladdr);
break;
+ case LOWPAN_LLTYPE_BTLE:
+ lowpan_iphc_uncompress_btle_lladdr(ipaddr, lladdr);
+ break;
default:
lowpan_iphc_uncompress_eui64_lladdr(ipaddr, lladdr);
break;
@@ -810,6 +843,29 @@ lowpan_iphc_compress_ctx_802154_lladdr(const struct in6_addr *ipaddr,
return lladdr_compress;
}
+static inline bool
+lowpan_iphc_compress_ctx_btle_lladdr(const struct in6_addr *ipaddr,
+ const struct lowpan_iphc_ctx *ctx,
+ const void *lladdr)
+{
+ struct in6_addr tmp = {};
+ bdaddr_t be_bdaddr;
+
+ /* ipv6 addr needs big endian form here */
+ baswap(&be_bdaddr, (bdaddr_t *)lladdr);
+
+ memcpy(&tmp.s6_addr[8], be_bdaddr.b, 3);
+ memcpy(&tmp.s6_addr[13], be_bdaddr.b + 3, 3);
+
+ tmp.s6_addr[11] = 0xFF;
+ tmp.s6_addr[12] = 0xFE;
+ tmp.s6_addr[8] ^= 2;
+
+ /* context information are always used */
+ ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
+ return ipv6_addr_equal(&tmp, ipaddr);
+}
+
static u8 lowpan_compress_ctx_addr(u8 **hc_ptr, const struct net_device *dev,
const struct in6_addr *ipaddr,
const struct lowpan_iphc_ctx *ctx,
@@ -826,6 +882,13 @@ static u8 lowpan_compress_ctx_addr(u8 **hc_ptr, const struct net_device *dev,
goto out;
}
break;
+ case LOWPAN_LLTYPE_BTLE:
+ if (lowpan_iphc_compress_ctx_btle_lladdr(ipaddr, ctx,
+ lladdr)) {
+ dam = LOWPAN_IPHC_DAM_11;
+ goto out;
+ }
+ break;
default:
/* check for SAM/DAM = 11 */
memcpy(&tmp.s6_addr[8], lladdr, EUI64_ADDR_LEN);
@@ -886,6 +949,7 @@ lowpan_iphc_compress_802154_lladdr(const struct in6_addr *ipaddr,
switch (addr->mode) {
case IEEE802154_ADDR_LONG:
ieee802154_le64_to_be64(&extended_addr, &addr->extended_addr);
+ /* TODO remove this macro and use ipv6_addr_equal */
if (is_addr_mac_addr_based(ipaddr, extended_addr))
lladdr_compress = true;
break;
@@ -914,12 +978,38 @@ lowpan_iphc_compress_802154_lladdr(const struct in6_addr *ipaddr,
return lladdr_compress;
}
+static inline bool
+lowpan_iphc_compress_btle_lladdr(const struct in6_addr *ipaddr,
+ const void *lladdr)
+{
+ struct in6_addr tmp = {};
+ bdaddr_t be_bdaddr;
+
+ /* ipv6 addr needs big endian form here */
+ baswap(&be_bdaddr, (bdaddr_t *)lladdr);
+
+ tmp.s6_addr[0] = 0xFE;
+ tmp.s6_addr[1] = 0x80;
+
+ memcpy(&tmp.s6_addr[8], be_bdaddr.b, 3);
+ memcpy(&tmp.s6_addr[13], be_bdaddr.b + 3, 3);
+
+ tmp.s6_addr[11] = 0xFF;
+ tmp.s6_addr[12] = 0xFE;
+ tmp.s6_addr[8] ^= 2;
+
+ return ipv6_addr_equal(&tmp, ipaddr);
+}
+
static u8 lowpan_compress_addr_64(u8 **hc_ptr, const struct net_device *dev,
const struct in6_addr *ipaddr,
const unsigned char *lladdr, bool sam)
{
u8 dam = LOWPAN_IPHC_DAM_01;
+ /* TODO share code with compress ctx stuff here, stateless compress
+ * is the same like stateful except the prefix must be fe80::/64
+ */
switch (lowpan_dev(dev)->lltype) {
case LOWPAN_LLTYPE_IEEE802154:
if (lowpan_iphc_compress_802154_lladdr(ipaddr, lladdr)) {
@@ -928,7 +1018,15 @@ static u8 lowpan_compress_addr_64(u8 **hc_ptr, const struct net_device *dev,
goto out;
}
break;
+ case LOWPAN_LLTYPE_BTLE:
+ if (lowpan_iphc_compress_btle_lladdr(ipaddr, lladdr)) {
+ dam = LOWPAN_IPHC_DAM_11; /* 0-bits */
+ pr_debug("address compression 0 bits\n");
+ goto out;
+ }
+ break;
default:
+ /* TODO remove? */
if (is_addr_mac_addr_based(ipaddr, lladdr)) {
dam = LOWPAN_IPHC_DAM_11; /* 0-bits */
pr_debug("address compression 0 bits\n");
@@ -1100,6 +1198,11 @@ static u8 lowpan_iphc_mcast_addr_compress(u8 **hc_ptr,
return val;
}
+/* TODO maybe handle directly saddr, daddr as big endian? unnecessary byteswaps.
+ * Neighbour cachane and dev->dev_addr is big endian saved, don't use l2 format
+ * here maybe? Cons: Will confuse everything, because lladdr are saved always
+ * in format as mac header format. Same for lowpan_header_decompress.
+ */
int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
const void *daddr, const void *saddr)
{
--
2.9.0
This patch adds a new btle 6lowpan implementation in version 0.2. This
new implementation has a new userspace API for debugfs.
It's now possible to decide on which hci interface do you want to run
your 6LoWPAN interface, for that you can run:
echo "hci0 1" > /sys/kernel/debug/bluetooth/6lowpan_enable
to enable, or:
echo "hci0 0" > /sys/kernel/debug/bluetooth/6lowpan_enable
to disable it. A cat on this file will show the current hci <-> 6lowpan
interface mapping.
Additional the different is, that the interface will be created after
enable it. The old behaviour is that the interface will be created when
one first connection is established and deleted after all connections
(peers) will be disconnected. This handling is different now.
The reason (in my opinion) is that the existing of such interface should
not based on if there is a connection or not. At runtime of many IPv6
application an interface will be removed -> I think the most userspace
application can and will never handle that an IPv6 interface will be
deleted and recreating during runtime.
The new handling is that the interface will not removed and you can
re-establish the connection to your peers.
Connection to peers:
Connection to peers are not binded to hci interface, it's binded to your
6LoWPAN interface. This will prepare handling for multiple 6LoWPAN
interfaces on one hci (maybe also with netns support, which isn't
available yet) and you can choose which peers should be handled by
different BTLE 6LoWPAN interfaces. Example:
L2: hci0 is connected to node A, B, C, D
L3: 6lo0 and 6lo1 are two interfaces based on hci0.
6lo0 is connected to nodes: A, B, C
6lo1 is connected to nodes: D, C, B
as one possible example. Handle this peer interface will have a possible
more fine-granularity connection for peers.
To connect to peers, there exists no "6lowpan_control" anymore. The
control file is now peer interface, it's simple:
echo "connect $PEER_ADDRESS 1" > /sys/kernel/debug/bluetooth/6lo0
Also after connecting to peers it's still possible that another peer can
to connect to the peer which has already a connected peer (complicated
to describe). I saw that is somehow disabled in version before, see:
http://lxr.free-electrons.com/source/net/bluetooth/6lowpan.c#L1247
I am not a bluetooth expert, but I guess that is somehow if there exists
limitation to devices which are not "peers" that means devices which can
be run as master or slave only. So far I know such limitations for
devices doesn't exists for BTLE 6LoWPAN, or?
Nevertheless I disabled that stuff, it's still possible to connect to
some node which already run "connect ..." on his side.
Otherwise I fixed the ndisc stuff and removed a lot of races/side
effects. The old code had some static "lowpan_devices" list and most
time there was some lookup mapping according to bdaddr to get the right
6lo netdevice struct. I removed that, also the skb->cb is used a lot in
some callback which had some side-effects.
Signed-off-by: Alexander Aring <[email protected]>
---
include/net/bluetooth/hci_core.h | 10 +
net/bluetooth/6lowpan.c | 1015 ++++++++++++++++++++++++++++++++++++++
net/bluetooth/Makefile | 3 +
3 files changed, 1028 insertions(+)
create mode 100644 net/bluetooth/6lowpan.c
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index a37027a..37a2256 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -399,6 +399,16 @@ struct hci_dev {
struct led_trigger *power_led;
+#if IS_ENABLED(CONFIG_BT_6LOWPAN)
+ /* TODO for netns support this need to be a list
+ * variables are protected by RTNL here
+ */
+ struct net_device *ldev;
+ bool lowpan_enable;
+ /* delete lowpan iface via debugfs workaround */
+ bool pending_deletion;
+#endif
+
int (*open)(struct hci_dev *hdev);
int (*close)(struct hci_dev *hdev);
int (*flush)(struct hci_dev *hdev);
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
new file mode 100644
index 0000000..a8a3410
--- /dev/null
+++ b/net/bluetooth/6lowpan.c
@@ -0,0 +1,1015 @@
+/*
+ (C) 2016 Pengutronix, Alexander Aring <[email protected]>
+ Copyright (c) 2013-2014 Intel Corp.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 and
+ only version 2 as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#include <linux/if_arp.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+
+#include <net/ipv6.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
+
+#include <net/6lowpan.h>
+
+#define LOWPAN_BTLE_VERSION "0.2"
+
+#define lowpan_le48_to_be48(dst, src) baswap((bdaddr_t *)dst, (bdaddr_t *)src)
+#define lowpan_be48_to_le48(dst, src) baswap((bdaddr_t *)dst, (bdaddr_t *)src)
+
+struct lowpan_btle_cb {
+ struct l2cap_chan *chan;
+};
+
+struct lowpan_btle_dev {
+ struct hci_dev *hdev;
+ struct workqueue_struct *workqueue;
+ struct l2cap_chan *listen;
+ struct list_head peers;
+ struct dentry *control;
+};
+
+struct lowpan_peer {
+ struct l2cap_chan *chan;
+
+ struct list_head list;
+};
+
+struct lowpan_chan_data {
+ struct lowpan_peer peer;
+ struct net_device *dev;
+};
+
+struct lowpan_xmit_work {
+ struct work_struct work;
+ struct l2cap_chan *chan;
+ struct net_device *dev;
+ unsigned int true_len;
+ struct sk_buff *skb;
+};
+
+static inline struct lowpan_btle_cb *
+lowpan_btle_cb(const struct sk_buff *skb)
+{
+ return (struct lowpan_btle_cb *)skb->cb;
+}
+
+static inline struct lowpan_chan_data *
+lowpan_chan_data(const struct l2cap_chan *chan)
+{
+ return (struct lowpan_chan_data *)chan->data;
+}
+
+static inline struct lowpan_btle_dev *
+lowpan_btle_dev(const struct net_device *dev)
+{
+ return (struct lowpan_btle_dev *)lowpan_dev(dev)->priv;
+}
+
+static inline struct lowpan_peer *
+lowpan_lookup_peer(struct lowpan_btle_dev *btdev, bdaddr_t *addr)
+{
+ struct lowpan_peer *entry;
+
+ list_for_each_entry_rcu(entry, &btdev->peers, list) {
+ if (!bacmp(&entry->chan->dst, addr))
+ return entry;
+ }
+
+ return NULL;
+}
+
+static int lowpan_give_skb_to_device(struct sk_buff *skb)
+{
+ skb->protocol = htons(ETH_P_IPV6);
+ skb->dev->stats.rx_packets++;
+ skb->dev->stats.rx_bytes += skb->len;
+
+ return netif_rx_ni(skb);
+}
+
+static int lowpan_rx_handlers_result(struct sk_buff *skb, lowpan_rx_result res)
+{
+ switch (res) {
+ case RX_CONTINUE:
+ /* nobody cared about this packet */
+ net_warn_ratelimited("%s: received unknown dispatch\n",
+ __func__);
+
+ /* fall-through */
+ case RX_DROP_UNUSABLE:
+ kfree_skb(skb);
+
+ /* fall-through */
+ case RX_DROP:
+ return NET_RX_DROP;
+ case RX_QUEUED:
+ return lowpan_give_skb_to_device(skb);
+ default:
+ break;
+ }
+
+ return NET_RX_DROP;
+}
+
+static lowpan_rx_result lowpan_rx_h_iphc(struct sk_buff *skb)
+{
+ struct l2cap_chan *chan = lowpan_btle_cb(skb)->chan;
+ bdaddr_t daddr, saddr;
+ int ret;
+
+ if (!lowpan_is_iphc(*skb_network_header(skb)))
+ return RX_CONTINUE;
+
+ BT_DBG("recv %s dst: %pMR type %d src: %pMR chan %p",
+ skb->dev->name, &chan->dst, chan->dst_type, &chan->src, chan);
+
+ /* bluetooth chan view is vice-versa */
+ bacpy(&daddr, &chan->src);
+ bacpy(&saddr, &chan->dst);
+
+ ret = lowpan_header_decompress(skb, skb->dev, &daddr, &saddr);
+ if (ret < 0)
+ return RX_DROP_UNUSABLE;
+
+ return RX_QUEUED;
+}
+
+static int lowpan_invoke_rx_handlers(struct sk_buff *skb)
+{
+ lowpan_rx_result res;
+
+#define CALL_RXH(rxh) \
+ do { \
+ res = rxh(skb); \
+ if (res != RX_CONTINUE) \
+ goto rxh_next; \
+ } while (0)
+
+ /* likely at first */
+ CALL_RXH(lowpan_rx_h_iphc);
+
+rxh_next:
+ return lowpan_rx_handlers_result(skb, res);
+#undef CALL_RXH
+}
+
+static int lowpan_chan_recv(struct l2cap_chan *chan, struct sk_buff *skb)
+{
+ struct lowpan_chan_data *data = lowpan_chan_data(chan);
+ struct net_device *dev = data->dev;
+ int ret;
+
+ /* TODO handle BT_CONNECTED in bluetooth subsytem? on
+ * when calling recv callback, I hit that case somehow
+ */
+ if (!netif_running(dev) || chan->state != BT_CONNECTED ||
+ !skb->len || !lowpan_is_iphc(skb->data[0]))
+ goto drop;
+
+ /* Replacing skb->dev and followed rx handlers will manipulate skb. */
+ skb = skb_unshare(skb, GFP_ATOMIC);
+ if (!skb)
+ goto out;
+
+ skb->dev = dev;
+ skb_reset_network_header(skb);
+
+ /* remember that one for dst bdaddr. TODO handle that as priv data for
+ * lowpan_invoke_rx_handlers parameter. Not necessary for skb->cb.
+ */
+ lowpan_btle_cb(skb)->chan = chan;
+
+ ret = lowpan_invoke_rx_handlers(skb);
+ if (ret == NET_RX_DROP)
+ BT_DBG("recv %s dropped chan %p", skb->dev->name, chan);
+
+ return 0;
+
+drop:
+ kfree_skb(skb);
+out:
+ /* we handle to free skb on error, so must 0
+ * seems that callback free the skb on error
+ * otherwise.
+ */
+ return 0;
+}
+
+static void lowpan_xmit_worker(struct work_struct *work)
+{
+ struct lowpan_btle_dev *btdev;
+ struct lowpan_xmit_work *xw;
+ struct l2cap_chan *chan;
+ struct net_device *dev;
+ struct msghdr msg = { };
+ struct kvec iv;
+ int ret;
+
+ xw = container_of(work, struct lowpan_xmit_work, work);
+ dev = xw->dev;
+ chan = xw->chan;
+ btdev = lowpan_btle_dev(dev);
+
+ iv.iov_base = xw->skb->data;
+ iv.iov_len = xw->skb->len;
+ iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, &iv, 1, xw->skb->len);
+
+ BT_DBG("l2cap_chan_send %s dst: %pMR type %d src: %pMR chan %p",
+ dev->name, &chan->dst, chan->dst_type, &chan->src, chan);
+
+ l2cap_chan_lock(chan);
+
+ ret = l2cap_chan_send(chan, &msg, xw->skb->len);
+ BT_DBG("transmit return value %d", ret);
+ if (ret < 0) {
+ BT_DBG("send %s failed chan %p", dev->name, chan);
+ kfree_skb(xw->skb);
+ } else {
+ consume_skb(xw->skb);
+ dev->stats.tx_bytes += xw->true_len;
+ dev->stats.tx_packets++;
+ }
+
+ l2cap_chan_unlock(chan);
+ l2cap_chan_put(chan);
+
+ kfree(xw);
+}
+
+static void lowpan_send_unicast_pkt(struct net_device *dev,
+ struct l2cap_chan *chan,
+ struct sk_buff *skb,
+ unsigned int true_len)
+{
+ struct lowpan_xmit_work *xw;
+
+ /* copy to xmit work buffer */
+ xw = kzalloc(sizeof(*xw), GFP_ATOMIC);
+ if (!xw)
+ return;
+
+ /* chan->lock mutex need to be hold so change context to workqueue */
+ INIT_WORK(&xw->work, lowpan_xmit_worker);
+ xw->true_len = true_len;
+ /* freeing protected by ifdown workqueue sync */
+ xw->dev = dev;
+ /* disallow freeing of skb while context switch */
+ xw->skb = skb_get(skb);
+ /* disallow freeing while context switch */
+ l2cap_chan_hold(chan);
+ xw->chan = chan;
+
+ queue_work(lowpan_btle_dev(dev)->workqueue, &xw->work);
+}
+
+static void lowpan_send_mcast_pkt(struct net_device *dev, struct sk_buff *skb,
+ unsigned int true_len)
+{
+ struct lowpan_btle_dev *btdev = lowpan_btle_dev(dev);
+ struct lowpan_peer *peer;
+
+ rcu_read_lock();
+
+ BT_DBG("xmit %s starts multicasting", dev->name);
+
+ /* We need to send the packet to every device behind this
+ * interface, because multicasting.
+ */
+ list_for_each_entry_rcu(peer, &btdev->peers, list)
+ lowpan_send_unicast_pkt(dev, peer->chan, skb, true_len);
+
+ rcu_read_unlock();
+}
+
+static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct lowpan_addr_info *info = lowpan_addr_info(skb);
+ struct lowpan_btle_dev *btdev = lowpan_btle_dev(dev);
+ unsigned int true_len = skb->len;
+ struct lowpan_peer *peer;
+ bdaddr_t daddr, saddr;
+ int ret;
+
+ /* We must take a copy of the skb before we modify/replace the ipv6
+ * header as the header could be used elsewhere.
+ */
+ skb = skb_unshare(skb, GFP_ATOMIC);
+ if (!skb) {
+ kfree_skb(skb);
+ goto out;
+ }
+
+ lowpan_be48_to_le48(&daddr, info->daddr);
+ lowpan_be48_to_le48(&saddr, info->saddr);
+
+ BT_DBG("xmit ndisc %s dst: %pMR src: %pMR",
+ dev->name, &daddr, &saddr);
+
+ ret = lowpan_header_compress(skb, dev, &daddr, &saddr);
+ if (ret < 0) {
+ kfree_skb(skb);
+ goto out;
+ }
+
+ /* this should never be the case, otherwise iphc is broken */
+ WARN_ON_ONCE(skb->len > dev->mtu);
+
+ if (!memcmp(&daddr, dev->broadcast, dev->addr_len)) {
+ lowpan_send_mcast_pkt(dev, skb, true_len);
+ } else {
+ btdev = lowpan_btle_dev(dev);
+
+ rcu_read_lock();
+
+ peer = lowpan_lookup_peer(btdev, &daddr);
+ if (peer)
+ lowpan_send_unicast_pkt(dev, peer->chan, skb,
+ true_len);
+
+ rcu_read_unlock();
+ }
+
+ consume_skb(skb);
+
+out:
+ return NETDEV_TX_OK;
+}
+
+static int lowpan_stop(struct net_device *dev)
+{
+ struct lowpan_btle_dev *btdev = lowpan_btle_dev(dev);
+
+ /* synchronize with xmit worker */
+ flush_workqueue(btdev->workqueue);
+ return 0;
+}
+
+static struct sk_buff *lowpan_chan_alloc_skb(struct l2cap_chan *chan,
+ unsigned long hdr_len,
+ unsigned long len, int nb)
+{
+ return bt_skb_alloc(hdr_len + len, GFP_KERNEL);
+}
+
+static long lowpan_chan_get_sndtimeo(struct l2cap_chan *chan)
+{
+ return L2CAP_CONN_TIMEOUT;
+}
+
+static struct l2cap_chan *lowpan_chan_create(struct net_device *dev)
+{
+ struct lowpan_chan_data *data;
+ struct l2cap_chan *chan;
+
+ chan = l2cap_chan_create();
+ if (!chan)
+ return ERR_PTR(-ENOMEM);
+
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ l2cap_chan_put(chan);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ l2cap_chan_set_defaults(chan);
+ chan->chan_type = L2CAP_CHAN_CONN_ORIENTED;
+ chan->mode = L2CAP_MODE_LE_FLOWCTL;
+ chan->imtu = dev->mtu;
+ chan->data = data;
+
+ data->peer.chan = chan;
+ data->dev = dev;
+
+ return chan;
+}
+
+static struct l2cap_chan *lowpan_chan_new_conn(struct l2cap_chan *pchan)
+{
+ struct lowpan_chan_data *data = lowpan_chan_data(pchan);
+ struct l2cap_chan *chan;
+
+ chan = lowpan_chan_create(data->dev);
+ if (IS_ERR(chan))
+ return NULL;
+
+ chan->ops = pchan->ops;
+ return chan;
+}
+
+static void lowpan_chan_ready(struct l2cap_chan *chan)
+{
+ struct lowpan_chan_data *data = lowpan_chan_data(chan);
+ struct lowpan_btle_dev *btdev = lowpan_btle_dev(data->dev);
+
+ BT_DBG("%s chan %p ready ", data->dev->name, chan);
+
+ /* make it visible for xmit */
+ list_add_tail_rcu(&data->peer.list, &btdev->peers);
+ synchronize_rcu();
+}
+
+static void lowpan_chan_close(struct l2cap_chan *chan)
+{
+ struct lowpan_chan_data *data = lowpan_chan_data(chan);
+
+ BT_DBG("%s chan %p closed ", data->dev->name, chan);
+
+ /* make it unvisible for xmit */
+ list_del_rcu(&data->peer.list);
+ synchronize_rcu();
+ kfree(data);
+}
+
+static const struct l2cap_ops lowpan_chan_ops = {
+ .name = "L2CAP 6LoWPAN channel",
+ .new_connection = lowpan_chan_new_conn,
+ .recv = lowpan_chan_recv,
+ .close = lowpan_chan_close,
+ .state_change = l2cap_chan_no_state_change,
+ .ready = lowpan_chan_ready,
+ .get_sndtimeo = lowpan_chan_get_sndtimeo,
+ .alloc_skb = lowpan_chan_alloc_skb,
+ .teardown = l2cap_chan_no_teardown,
+ .defer = l2cap_chan_no_defer,
+ .set_shutdown = l2cap_chan_no_set_shutdown,
+ .resume = l2cap_chan_no_resume,
+ .suspend = l2cap_chan_no_suspend,
+};
+
+static int lowpan_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct lowpan_btle_dev *btdev = lowpan_btle_dev(dev);
+
+ /* if dev is down, peers list are protected by RTNL */
+ if (netif_running(dev) || !list_empty(&btdev->peers))
+ return -EBUSY;
+
+ if (new_mtu < IPV6_MIN_MTU)
+ return -EINVAL;
+
+ dev->mtu = new_mtu;
+ return 0;
+}
+
+static const struct net_device_ops netdev_ops = {
+ .ndo_init = lowpan_dev_init,
+ .ndo_stop = lowpan_stop,
+ .ndo_start_xmit = lowpan_xmit,
+ .ndo_change_mtu = lowpan_change_mtu,
+};
+
+static void lowpan_free_netdev(struct net_device *dev)
+{
+ struct lowpan_btle_dev *btdev = lowpan_btle_dev(dev);
+ struct lowpan_peer *peer, *tmp;
+ struct l2cap_chan *chan;
+
+ l2cap_chan_close(btdev->listen, 0);
+ l2cap_chan_put(btdev->listen);
+
+ /* no rcu here? should be safe netdev is down */
+ list_for_each_entry_safe(peer, tmp, &btdev->peers, list) {
+ /* close will free peer data, so save chan here */
+ chan = peer->chan;
+
+ /* will del peer from list and and free.
+ * should be safe by tmp pointer which has
+ * a pointer for the next entry.
+ */
+ l2cap_chan_close(chan, 0);
+ l2cap_chan_put(chan);
+ }
+
+ destroy_workqueue(btdev->workqueue);
+ btdev->hdev->lowpan_enable = false;
+ debugfs_remove(btdev->control);
+ hci_dev_put(btdev->hdev);
+}
+
+static void lowpan_setup(struct net_device *dev)
+{
+ memset(dev->broadcast, 0xff, sizeof(bdaddr_t));
+
+ dev->netdev_ops = &netdev_ops;
+ dev->destructor = lowpan_free_netdev;
+ dev->features |= NETIF_F_NETNS_LOCAL;
+}
+
+static struct device_type bt_type = {
+ .name = "bluetooth",
+};
+
+static struct l2cap_chan *lowpan_listen_chan_new_conn(struct l2cap_chan *pchan)
+{
+ struct l2cap_chan *chan;
+
+ chan = lowpan_chan_create(pchan->data);
+ if (IS_ERR(chan))
+ return NULL;
+
+ /* change ops with more functionality than listen,
+ * which also free chan->data stuff.
+ */
+ chan->ops = &lowpan_chan_ops;
+
+ return chan;
+}
+
+static const struct l2cap_ops lowpan_listen_chan_ops = {
+ .name = "L2CAP 6LoWPAN listen channel",
+ .new_connection = lowpan_listen_chan_new_conn,
+ .recv = l2cap_chan_no_recv,
+ .close = l2cap_chan_no_close,
+ .state_change = l2cap_chan_no_state_change,
+ .ready = l2cap_chan_no_ready,
+ .get_sndtimeo = l2cap_chan_no_get_sndtimeo,
+ .alloc_skb = l2cap_chan_no_alloc_skb,
+ .teardown = l2cap_chan_no_teardown,
+ .defer = l2cap_chan_no_defer,
+ .set_shutdown = l2cap_chan_no_set_shutdown,
+ .resume = l2cap_chan_no_resume,
+ .suspend = l2cap_chan_no_suspend,
+};
+
+static int lowpan_create_listen_chan(struct net_device *dev)
+{
+ struct lowpan_btle_dev *btdev = lowpan_btle_dev(dev);
+ struct l2cap_chan *chan;
+ u8 bdaddr_type;
+ int ret;
+
+ /* don't use lowpan_chan_create here, because less functionality */
+ chan = l2cap_chan_create();
+ if (!chan)
+ return -ENOMEM;
+
+ chan->data = dev;
+ chan->ops = &lowpan_listen_chan_ops;
+ hci_copy_identity_address(btdev->hdev, &chan->src, &bdaddr_type);
+ if (bdaddr_type == ADDR_LE_DEV_PUBLIC)
+ chan->src_type = BDADDR_LE_PUBLIC;
+ else
+ chan->src_type = BDADDR_LE_RANDOM;
+
+ chan->state = BT_LISTEN;
+ atomic_set(&chan->nesting, L2CAP_NESTING_PARENT);
+
+ BT_DBG("chan %p src type %d", chan, chan->src_type);
+ ret = l2cap_add_psm(chan, BDADDR_ANY, cpu_to_le16(L2CAP_PSM_IPSP));
+ if (ret) {
+ l2cap_chan_put(chan);
+ BT_ERR("psm cannot be added err %d", ret);
+ return ret;
+ }
+ btdev->listen = chan;
+
+ return 0;
+}
+
+static const struct file_operations lowpan_control_fops;
+
+static struct net_device *lowpan_btle_newlink(struct hci_dev *hdev)
+{
+ struct lowpan_btle_dev *btdev;
+ struct net_device *dev;
+ int err = -ENOMEM;
+
+ dev = alloc_netdev(LOWPAN_PRIV_SIZE(sizeof(struct lowpan_btle_dev)),
+ LOWPAN_IFNAME_TEMPLATE, NET_NAME_ENUM, lowpan_setup);
+ if (!dev)
+ goto out;
+
+ dev->addr_assign_type = NET_ADDR_PERM;
+ dev->addr_len = sizeof(hdev->bdaddr.b);
+ lowpan_le48_to_be48(dev->dev_addr, &hdev->bdaddr);
+
+ SET_NETDEV_DEV(dev, &hdev->dev);
+ SET_NETDEV_DEVTYPE(dev, &bt_type);
+
+ btdev = lowpan_btle_dev(dev);
+ /* avoid freeing */
+ btdev->hdev = hci_dev_hold(hdev);
+ INIT_LIST_HEAD(&btdev->peers);
+
+ btdev->workqueue = create_singlethread_workqueue(dev->name);
+ if (!btdev->workqueue) {
+ free_netdev(dev);
+ goto out;
+ }
+
+ err = lowpan_create_listen_chan(dev);
+ if (err < 0) {
+ destroy_workqueue(btdev->workqueue);
+ free_netdev(dev);
+ goto out;
+ }
+
+ err = lowpan_register_netdevice(dev, LOWPAN_LLTYPE_BTLE);
+ if (err < 0) {
+ BT_ERR("register_netdev failed %d", err);
+ l2cap_chan_close(btdev->listen, 0);
+ l2cap_chan_put(btdev->listen);
+ free_netdev(dev);
+ goto out;
+ }
+ hdev->ldev = dev;
+
+ /* ignore error handling here, we cannot call unregister anymore
+ * It's a bug, but it's debugfs... so ignore it.
+ */
+ btdev->control = debugfs_create_file(dev->name, 0644,
+ bt_debugfs, btdev,
+ &lowpan_control_fops);
+ if (!btdev->control)
+ BT_ERR("debugfs error for %s, disable/enable 6lowpan again",
+ dev->name);
+
+ return dev;
+
+out:
+ return ERR_PTR(err);
+}
+
+static void lowpan_btle_dellink(struct net_device *dev)
+{
+ lowpan_unregister_netdevice(dev);
+}
+
+static int lowpan_parse_le_bdaddr(struct hci_dev *hdev, unsigned char *buf,
+ bdaddr_t *addr, u8 *addr_type)
+{
+ int n;
+
+ n = sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %hhu",
+ &addr->b[5], &addr->b[4], &addr->b[3],
+ &addr->b[2], &addr->b[1], &addr->b[0],
+ addr_type);
+ if (n < 7)
+ return -EINVAL;
+
+ /* check if we handle le addresses and not own source address */
+ if (!bdaddr_type_is_le(*addr_type) ||
+ !bacmp(&hdev->bdaddr, addr))
+ return -EINVAL;
+
+ return n;
+}
+
+static ssize_t __lowpan_control_write(struct file *fp,
+ const char __user *user_buffer,
+ size_t count, loff_t *position)
+{
+ char buf[32] = { };
+ size_t buf_size = min(count, sizeof(buf) - 1);
+ struct seq_file *file = fp->private_data;
+ struct lowpan_btle_dev *btdev = file->private;
+ struct hci_dev *hdev = btdev->hdev;
+ struct lowpan_peer *peer;
+ /* slave address */
+ bdaddr_t addr;
+ u8 addr_type;
+ int ret;
+
+ if (copy_from_user(buf, user_buffer, buf_size))
+ return -EFAULT;
+
+ if (memcmp(buf, "connect ", 8) == 0) {
+ struct lowpan_peer *peer;
+ struct l2cap_chan *chan;
+
+ ret = lowpan_parse_le_bdaddr(hdev, &buf[8], &addr, &addr_type);
+ if (ret < 0)
+ return ret;
+
+ /* check if we already know that slave */
+ rcu_read_lock();
+ peer = lowpan_lookup_peer(btdev, &addr);
+ if (peer) {
+ rcu_read_unlock();
+ BT_DBG("6LoWPAN connection already exists");
+ return -EEXIST;
+ }
+ rcu_read_unlock();
+
+ chan = lowpan_chan_create(hdev->ldev);
+ if (IS_ERR(chan))
+ return PTR_ERR(chan);
+ chan->ops = &lowpan_chan_ops;
+
+ ret = l2cap_hdev_chan_connect(hdev, chan,
+ cpu_to_le16(L2CAP_PSM_IPSP),
+ 0, &addr, addr_type);
+ if (ret < 0) {
+ l2cap_chan_put(chan);
+ return ret;
+ }
+
+ return count;
+ } else if (memcmp(buf, "disconnect ", 11) == 0) {
+ ret = lowpan_parse_le_bdaddr(hdev, &buf[11], &addr, &addr_type);
+ if (ret < 0)
+ return ret;
+
+ /* check if we don't know that slave */
+ rcu_read_lock();
+ peer = lowpan_lookup_peer(btdev, &addr);
+ if (!peer) {
+ rcu_read_unlock();
+ BT_DBG("6LoWPAN connection not found in peers");
+ return -ENOENT;
+ }
+ rcu_read_unlock();
+
+ /* first delete the peer out of list,
+ * which makes it visiable to netdev,
+ * will be done by close callback.
+ */
+ l2cap_chan_close(peer->chan, 0);
+ l2cap_chan_put(peer->chan);
+ } else {
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static ssize_t lowpan_control_write(struct file *fp,
+ const char __user *user_buffer,
+ size_t count, loff_t *position)
+{
+ ssize_t ret;
+
+ rtnl_lock();
+ ret = __lowpan_control_write(fp, user_buffer, count, position);
+ rtnl_unlock();
+
+ return ret;
+}
+
+static int lowpan_control_show(struct seq_file *f, void *ptr)
+{
+ struct lowpan_btle_dev *btdev = f->private;
+ struct hci_dev *hdev = btdev->hdev;
+ struct lowpan_peer *peer;
+
+ rtnl_lock();
+
+ if (!hdev->lowpan_enable) {
+ rtnl_unlock();
+ return 0;
+ }
+
+ rcu_read_lock();
+
+ list_for_each_entry_rcu(peer, &btdev->peers, list) {
+ seq_printf(f, "%pMR (type %u) state: %s\n",
+ &peer->chan->dst, peer->chan->dst_type,
+ state_to_string(peer->chan->state));
+ }
+
+ rcu_read_unlock();
+
+ rtnl_unlock();
+ return 0;
+}
+
+static int lowpan_control_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, lowpan_control_show, inode->i_private);
+}
+
+static const struct file_operations lowpan_control_fops = {
+ .open = lowpan_control_open,
+ .read = seq_read,
+ .write = lowpan_control_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/* TODO remove this when add non debugfs API, it's a workaround */
+static struct workqueue_struct *workqueue_cleanup;
+
+struct lowpan_cleanup_work {
+ struct work_struct work;
+ struct net_device *dev;
+};
+
+static void lowpan_cleanup_worker(struct work_struct *work)
+{
+ struct lowpan_cleanup_work *xw;
+
+ xw = container_of(work, struct lowpan_cleanup_work, work);
+
+ rtnl_lock();
+ lowpan_btle_dellink(xw->dev);
+ lowpan_btle_dev(xw->dev)->hdev->pending_deletion = false;
+ rtnl_unlock();
+
+ kfree(xw);
+}
+
+/* TODO workaround, debugfs doesn't like recursion remove while debugfs
+ * handling, this should be removed for by using real UAPI.
+ */
+static int __lowpan_btle_dellink(struct net_device *dev,
+ struct hci_dev *hdev)
+{
+ struct lowpan_cleanup_work *xw;
+
+ xw = kzalloc(sizeof(*xw), GFP_KERNEL);
+ if (!xw)
+ return -ENOMEM;
+
+ INIT_WORK(&xw->work, lowpan_cleanup_worker);
+ xw->dev = dev;
+
+ hdev->pending_deletion = true;
+ queue_work(workqueue_cleanup, &xw->work);
+ return 0;
+}
+
+static int lowpan_enable_set(struct hci_dev *hdev, bool enabled)
+{
+ int ret = 0;
+
+ if (hdev->lowpan_enable == enabled)
+ goto out;
+
+ if (enabled) {
+ struct net_device *dev;
+
+ /* create one interface by default */
+ dev = lowpan_btle_newlink(hdev);
+ if (IS_ERR(dev))
+ return PTR_ERR(dev);
+
+ /* TODO should be done by user network managers? */
+ ret = dev_open(dev);
+ if (ret < 0) {
+ BT_ERR("iface %s cannot be opened %d", dev->name, ret);
+ ret = __lowpan_btle_dellink(dev, hdev);
+ if (ret < 0)
+ BT_ERR("failed to remove interface %s",
+ hdev->ldev->name);
+
+ return ret;
+ }
+
+ hdev->lowpan_enable = true;
+ } else {
+ /* ignore pending deletions */
+ if (hdev->pending_deletion)
+ return 0;
+
+ __lowpan_btle_dellink(hdev->ldev, hdev);
+ }
+
+out:
+ return ret;
+}
+
+static void lowpan_btle_hci_unregister(struct hci_dev *hdev)
+{
+ /* no need to cleanup debugfs, will be done by hci_core */
+
+ /* TODO netns support with multiple lowpan interfaces */
+ if (hdev->lowpan_enable)
+ lowpan_btle_dellink(hdev->ldev);
+}
+
+static int lowpan_hci_dev_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct hci_dev *hdev = ptr;
+ int ret = NOTIFY_OK;
+
+ rtnl_lock();
+
+ /* bluetooth handles that event type as int,
+ * but there is no overflow yet.
+ */
+ switch (event) {
+ case HCI_DEV_UNREG:
+ lowpan_btle_hci_unregister(hdev);
+ ret = NOTIFY_DONE;
+ break;
+ default:
+ break;
+ }
+
+ rtnl_unlock();
+
+ return ret;
+}
+
+static struct notifier_block lowpan_hcI_dev_notifier = {
+ .notifier_call = lowpan_hci_dev_event,
+};
+
+static ssize_t lowpan_enable_write(struct file *fp,
+ const char __user *user_buffer,
+ size_t count, loff_t *position)
+{
+ char buf[32] = { };
+ size_t buf_size = min(count, sizeof(buf) - 1);
+ struct hci_dev *hdev;
+ int idx, enabled, n, ret;
+
+ if (copy_from_user(buf, user_buffer, buf_size))
+ return -EFAULT;
+
+ n = sscanf(buf, "hci%d %d", &idx, &enabled);
+ if (n != 2)
+ return -EINVAL;
+
+ hdev = hci_dev_get(idx);
+ if (!hdev)
+ return -EINVAL;
+
+ rtnl_lock();
+ ret = lowpan_enable_set(hdev, enabled);
+ rtnl_unlock();
+
+ hci_dev_put(hdev);
+
+ return count;
+}
+
+static int lowpan_enable_show(struct seq_file *f, void *ptr)
+{
+ struct hci_dev *hdev;
+
+ rtnl_lock();
+ read_lock(&hci_dev_list_lock);
+ list_for_each_entry(hdev, &hci_dev_list, list) {
+ if (hdev->lowpan_enable)
+ seq_printf(f, "hci%d -> %s\n", hdev->id,
+ hdev->ldev->name);
+ }
+ read_unlock(&hci_dev_list_lock);
+ rtnl_unlock();
+
+ return 0;
+}
+
+static int lowpan_enable_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, lowpan_enable_show, inode->i_private);
+}
+
+static const struct file_operations lowpan_enable_fops = {
+ .open = lowpan_enable_open,
+ .read = seq_read,
+ .write = lowpan_enable_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init bt_6lowpan_init(void)
+{
+ int err;
+
+ workqueue_cleanup = create_singlethread_workqueue("btle 6lowpan");
+ if (!workqueue_cleanup)
+ return -ENOMEM;
+
+ debugfs_create_file("6lowpan_enable", 0644, bt_debugfs, NULL,
+ &lowpan_enable_fops);
+
+ err = register_hci_dev_notifier(&lowpan_hcI_dev_notifier);
+ if (err < 0)
+ destroy_workqueue(workqueue_cleanup);
+
+ return err;
+}
+
+static void __exit bt_6lowpan_exit(void)
+{
+ destroy_workqueue(workqueue_cleanup);
+ unregister_hci_dev_notifier(&lowpan_hcI_dev_notifier);
+}
+
+module_init(bt_6lowpan_init);
+module_exit(bt_6lowpan_exit);
+
+MODULE_AUTHOR("Jukka Rissanen <[email protected]>");
+MODULE_DESCRIPTION("Bluetooth 6LoWPAN");
+MODULE_VERSION(LOWPAN_BTLE_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index e347828..b3ff12e 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -7,6 +7,9 @@ obj-$(CONFIG_BT_RFCOMM) += rfcomm/
obj-$(CONFIG_BT_BNEP) += bnep/
obj-$(CONFIG_BT_CMTP) += cmtp/
obj-$(CONFIG_BT_HIDP) += hidp/
+obj-$(CONFIG_BT_6LOWPAN) += bluetooth_6lowpan.o
+
+bluetooth_6lowpan-y := 6lowpan.o
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o lib.o \
--
2.9.0
This functionality need to be implemented on all 6LoWPAN interfaces so
we move this to generic 6LoWPAN.
Signed-off-by: Alexander Aring <[email protected]>
---
include/net/6lowpan.h | 2 ++
net/6lowpan/core.c | 8 ++++++++
net/ieee802154/6lowpan/core.c | 7 -------
3 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
index 9a6282b..6a60df9 100644
--- a/include/net/6lowpan.h
+++ b/include/net/6lowpan.h
@@ -286,6 +286,8 @@ int lowpan_register_netdev(struct net_device *dev,
void lowpan_unregister_netdevice(struct net_device *dev);
void lowpan_unregister_netdev(struct net_device *dev);
+int lowpan_dev_init(struct net_device *dev);
+
/**
* lowpan_header_decompress - replace 6LoWPAN header with IPv6 header
*
diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
index b01effd..a978000 100644
--- a/net/6lowpan/core.c
+++ b/net/6lowpan/core.c
@@ -45,6 +45,14 @@ static struct header_ops header_ops = {
.create = lowpan_header_create,
};
+int lowpan_dev_init(struct net_device *dev)
+{
+ netdev_lockdep_set_classes(dev);
+
+ return 0;
+}
+EXPORT_SYMBOL(lowpan_dev_init);
+
int lowpan_register_netdevice(struct net_device *dev,
enum lowpan_lltypes lltype)
{
diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c
index 096a194..228a711 100644
--- a/net/ieee802154/6lowpan/core.c
+++ b/net/ieee802154/6lowpan/core.c
@@ -54,13 +54,6 @@
static int open_count;
-static int lowpan_dev_init(struct net_device *ldev)
-{
- netdev_lockdep_set_classes(ldev);
-
- return 0;
-}
-
static int lowpan_open(struct net_device *dev)
{
if (!open_count)
--
2.9.0
The address length for BTLE 6LoWPAN is not 8 bytes, it's 6 bytes. We
need to move this setting away from generic branch.
Signed-off-by: Alexander Aring <[email protected]>
---
net/6lowpan/core.c | 1 -
net/ieee802154/6lowpan/core.c | 1 +
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
index 6b7de14..e03cecf 100644
--- a/net/6lowpan/core.c
+++ b/net/6lowpan/core.c
@@ -58,7 +58,6 @@ int lowpan_register_netdevice(struct net_device *dev,
{
int i, ret;
- dev->addr_len = EUI64_ADDR_LEN;
dev->type = ARPHRD_6LOWPAN;
dev->mtu = IPV6_MIN_MTU;
dev->priv_flags |= IFF_NO_QUEUE;
diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c
index a60abad..c97eb7a 100644
--- a/net/ieee802154/6lowpan/core.c
+++ b/net/ieee802154/6lowpan/core.c
@@ -134,6 +134,7 @@ static int lowpan_newlink(struct net *src_net, struct net_device *ldev,
}
lowpan_802154_dev(ldev)->wdev = wdev;
+ ldev->addr_len = IEEE802154_ADDR_LEN;
/* Set the lowpan hardware address to the wpan hardware address. */
memcpy(ldev->dev_addr, wdev->dev_addr, IEEE802154_ADDR_LEN);
/* We need headroom for possible wpan_dev_hard_header call and tailroom
--
2.9.0
This patch adds a default naming for all 6LoWPAN interfaces. All
userspace software should have them as example e.g. for configurations.
I see currently many examples which has different interface naming for
software which works for 6LoWPAN BTLE and 6LoWPAN 802.15.4.
We need to change also all examples outside to this naming stuff, so the
interface naming inside such examples doesn't depends on L2.
Signed-off-by: Alexander Aring <[email protected]>
---
include/net/6lowpan.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
index 7f4b63c..2570237 100644
--- a/include/net/6lowpan.h
+++ b/include/net/6lowpan.h
@@ -78,6 +78,8 @@
#define LOWPAN_DISPATCH_IPHC 0x60 /* 011xxxxx = ... */
#define LOWPAN_DISPATCH_IPHC_MASK 0xe0
+#define LOWPAN_IFNAME_TEMPLATE "6lo%d"
+
static inline bool lowpan_is_ipv6(u8 dispatch)
{
return dispatch == LOWPAN_DISPATCH_IPV6;
--
2.9.0
This patch prepares to use 802.15.4 6LoWPAN receive handling also for
BTLE 6LoWPAN.
Signed-off-by: Alexander Aring <[email protected]>
---
include/net/6lowpan.h | 6 ++++++
net/ieee802154/6lowpan/6lowpan_i.h | 6 ------
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
index 2570237..90d9d08 100644
--- a/include/net/6lowpan.h
+++ b/include/net/6lowpan.h
@@ -80,6 +80,12 @@
#define LOWPAN_IFNAME_TEMPLATE "6lo%d"
+typedef unsigned __bitwise__ lowpan_rx_result;
+#define RX_CONTINUE ((__force lowpan_rx_result) 0u)
+#define RX_DROP_UNUSABLE ((__force lowpan_rx_result) 1u)
+#define RX_DROP ((__force lowpan_rx_result) 2u)
+#define RX_QUEUED ((__force lowpan_rx_result) 3u)
+
static inline bool lowpan_is_ipv6(u8 dispatch)
{
return dispatch == LOWPAN_DISPATCH_IPV6;
diff --git a/net/ieee802154/6lowpan/6lowpan_i.h b/net/ieee802154/6lowpan/6lowpan_i.h
index 5ac7789..39b739f 100644
--- a/net/ieee802154/6lowpan/6lowpan_i.h
+++ b/net/ieee802154/6lowpan/6lowpan_i.h
@@ -7,12 +7,6 @@
#include <net/inet_frag.h>
#include <net/6lowpan.h>
-typedef unsigned __bitwise__ lowpan_rx_result;
-#define RX_CONTINUE ((__force lowpan_rx_result) 0u)
-#define RX_DROP_UNUSABLE ((__force lowpan_rx_result) 1u)
-#define RX_DROP ((__force lowpan_rx_result) 2u)
-#define RX_QUEUED ((__force lowpan_rx_result) 3u)
-
#define LOWPAN_DISPATCH_FRAG1 0xc0
#define LOWPAN_DISPATCH_FRAGN 0xe0
--
2.9.0
This patch moves the control block information BUILD_BUG_ON to the init
of module. All compile timed BUILD_BUG_ON macros should be placed there.
Signed-off-by: Alexander Aring <[email protected]>
---
include/net/6lowpan.h | 1 -
net/ieee802154/6lowpan/core.c | 3 +++
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
index 5ab4c99..6d56be7 100644
--- a/include/net/6lowpan.h
+++ b/include/net/6lowpan.h
@@ -178,7 +178,6 @@ struct lowpan_802154_cb {
static inline
struct lowpan_802154_cb *lowpan_802154_cb(const struct sk_buff *skb)
{
- BUILD_BUG_ON(sizeof(struct lowpan_802154_cb) > sizeof(skb->cb));
return (struct lowpan_802154_cb *)skb->cb;
}
diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c
index d7efbf0..6afb8cd 100644
--- a/net/ieee802154/6lowpan/core.c
+++ b/net/ieee802154/6lowpan/core.c
@@ -233,6 +233,9 @@ static int __init lowpan_init_module(void)
{
int err = 0;
+ BUILD_BUG_ON(sizeof(struct lowpan_802154_cb) >
+ FIELD_SIZEOF(struct sk_buff, cb));
+
err = lowpan_net_frag_init();
if (err < 0)
goto out;
--
2.9.0
This patch removes unnessary headroom check. We will check that value
while register net device that we have such headroom for placing address
information at the headroom of skb.
Signed-off-by: Alexander Aring <[email protected]>
---
net/ieee802154/6lowpan/tx.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/net/ieee802154/6lowpan/tx.c b/net/ieee802154/6lowpan/tx.c
index dbb476d..a7a1b12 100644
--- a/net/ieee802154/6lowpan/tx.c
+++ b/net/ieee802154/6lowpan/tx.c
@@ -26,7 +26,6 @@ struct lowpan_addr_info {
static inline struct
lowpan_addr_info *lowpan_skb_priv(const struct sk_buff *skb)
{
- WARN_ON_ONCE(skb_headroom(skb) < sizeof(struct lowpan_addr_info));
return (struct lowpan_addr_info *)(skb->data -
sizeof(struct lowpan_addr_info));
}
--
2.9.0
This patch removes handling to remove short address for a neigbour entry
if RS/RA/NS/NA doesn't contain a short address. If these messages
doesn't has any short address option, the existing short address from
ndisc cache will be used.
Signed-off-by: Alexander Aring <[email protected]>
---
net/6lowpan/ndisc.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/net/6lowpan/ndisc.c b/net/6lowpan/ndisc.c
index 86450b7..941df2f 100644
--- a/net/6lowpan/ndisc.c
+++ b/net/6lowpan/ndisc.c
@@ -101,8 +101,6 @@ static void lowpan_ndisc_802154_update(struct neighbour *n, u32 flags,
ieee802154_be16_to_le16(&neigh->short_addr, lladdr_short);
if (!lowpan_802154_is_valid_src_short_addr(neigh->short_addr))
neigh->short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);
- } else {
- neigh->short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);
}
write_unlock_bh(&n->lock);
}
--
2.9.0
This patch adds a TODO for nhc to do everything what we need inside
compress handling. Also we should check if the compression doesn't add
more bytes than sending raw next header format. We don't hit that issue
yet because NHC id is at worst case one byte long like the next header
byte in IPv6 header (which will be elided when using NHC). But on NHC
id's which are two bytes long it's likely that compressions adds more
data than sending raw next header.
The root of this issue to care about is that with running IPHC after a
IPv6 packet which is 1280 bytes long, the IPHC, NHC header with payload
should not exceed 1280 bytes. This will break bluetooth L2CAP mtu which
is 1280 bytes as well.
Signed-off-by: Alexander Aring <[email protected]>
---
net/6lowpan/nhc.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/net/6lowpan/nhc.c b/net/6lowpan/nhc.c
index 7008d53..55df7f7 100644
--- a/net/6lowpan/nhc.c
+++ b/net/6lowpan/nhc.c
@@ -94,6 +94,21 @@ static struct lowpan_nhc *lowpan_nhc_by_nhcid(const struct sk_buff *skb)
return NULL;
}
+/* TODO remove this and do everything in compress callback,
+ * this means, setting NHC bit in IPHC, check if compression
+ * make sense and doing compression. We will hit the issue
+ * when having two bytes long NHC ID's that the IPHC compression
+ * could be have more bytes than using raw next header, this
+ * will break btle 6lowpan.
+ *
+ * Marcel told to look into skb frag and linearlize functionality.
+ * This way is very ugly.
+ * Additonal change handling that sending compressed on transmit
+ * is default disabled. Use a compression flag e.g. RFC6775
+ *
+ * I see nhc in a bad state currently, this need to be fixed
+ * before adding new nhc's.
+ */
int lowpan_nhc_check_compression(struct sk_buff *skb,
const struct ipv6hdr *hdr, u8 **hc_ptr)
{
--
2.9.0
Hi,
On 08/05/2016 09:15 AM, Bakke, Glenn Ruben wrote:
> Hi Alex,
>
>> -----Original Message-----
>> From: [email protected] [mailto:linux-bluetooth-
>>
...
>> --
>> 2.9.0
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
>> the body of a message to [email protected] More majordomo
>> info at http://vger.kernel.org/majordomo-info.html
>
> I have tested the patch series with all 20 patches and verified that I can connect to devices and communicate between them over IPv6. Multicast and unicast where tested.
> The neighbors where manually added with the command "ip -6 neigh". However I did not do a long run or thoroughly test, but did the basic verification.
> I really liked the way the HCI device was manually selected for the interface, giving me flexibility to choose what connections I wanted on a given HCI device.
>
thank you for testing it.
The "ip -6 neigh" command for adding neighbours should not be needed,
why did you do that?
Also the most important information for me would be:
- Did it ever crashed your kernel?
- Do you also have the tx credits deadlock?
I have locally a RFCv2 where I put now these things which I got from
this RFC as suggestion, which are:
- try to solve that dev->dev_addr is set when first connection is
there and remove dev_addr when last connection is gone.
I solved this by creating the interface with address 0...0 at first.
If there comes a connection in then the address will be set and the
interface comes up.
If the last connection will be close (for what reason ever) then the
interface comes down and the address is 0...0.
It's not possible to set the interface up when the address is 0...0.
This behaviour is somewhat better than recreating new interface (in
my opinion).
- 6lo%d interface will be created when power on the hci interface and
deleted when power off. (That's a suggestion from Marcel, so far I
understood it).
I implemented it now that "6lowpan_enable" need to be set to "1"
before setting hci will be powered on. All hci will create an
interface then.
This change has a ?nice? effekt that we don't need to close all
connection when doing a interface unregister.
- Changed the "6lowpan_control" per hci, not per interface. Which is
more what Partrik's UAPI draft.
These three things did I change now, I just need to create new patches
and send it to the mailinglist.
But the tx credits deadlock still exists on my side.
- Alex
Hi Alex,
> -----Original Message-----
> From: [email protected] [mailto:linux-bluetooth-
> [email protected]] On Behalf Of Alexander Aring
> Sent: 11. juli 2016 21:50
> To: [email protected]
> Cc: [email protected]; [email protected]; [email protected];
> [email protected]; [email protected];
> [email protected]; Alexander Aring <[email protected]>
> Subject: [RFC bluetooth-next 00/20] bluetooth: rework 6lowpan
> implementation
>
> Hi,
>
> I want to write a short summary here, to see what I changed to the old
> implementation, please see last RFC patch.
>
> How to use it (I know some commands are deprecated and should not be
> used), I have two scripts to do that:
>
> Node A (run at first):
>
> ---
>
> #!/bin/sh
> modprobe btusb
> modprobe bluetooth_6lowpan
>
> hciconfig hci0 up
> echo "hci0 1" > /sys/kernel/debug/bluetooth/6lowpan_enable
>
> hciconfig hci0 leadv
>
> hciconfig
>
> ---
>
> hcicondif will printout the address of hci dev, remember that.
>
> Node B (run after Node A stuff):
>
> ---
>
> #!/bin/sh
>
> if [ "$#" -ne 1 ]; then
> echo "run $0 \$SLAVE_ADDRESS, where SLAVE_ADDRESS is the BTLE
> slave"
> exit 1
> fi
>
> modprobe btusb
> modprobe bluetooth_6lowpan
>
> hciconfig hci0 up
> echo "hci0 1" > /sys/kernel/debug/bluetooth/6lowpan_enable
>
> hcitool lecc $1
>
> echo "connect $1 1" > /sys/kernel/debug/bluetooth/6lo0
>
> ---
>
> Where $1 is the address of the Node A.
>
> Well done. Your connection should be established. Except you have crap usb
> dongles like me (broadcom) where sometimes the reset functionality seems
> to be not working 100% correctly. btw: my workaround, replug usb dongles
> (but I always need to tell the new usb bus information my qemu vm :-()
>
> KNOWN BUGS and how to reproduce that one:
>
> First, thanks to Luiz Augusto von Dentz, which tried to help me there.
> But I gave up, the BUG still exists and it's because L2CAP implementation or I
> use xmit functionality wrong.
>
> The issue is "tx credits in L2CAP (bluetooth experts know what I mean here)
> will be reach to zero at the same time on both nodes. This occurs a deadlock
> and nobody will transmit anything anymore".
>
> So far I understand these tx credits are to avoid buffer-bloating. My
> workaround to fix that was to allow buffer bloating:
>
> #define L2CAP_LE_MAX_CREDITS 10
> changed to (some high number which doesn't reach 2^16):
> #define L2CAP_LE_MAX_CREDITS 60000
>
> This will introduce buffer-bloating which I happily see when doing high
> payload pings. (I think on high traffic you will reach that issue also with this
> workaround, it's just not likely.)
>
> ---
>
> HOW TO REPRODUCE:
>
> It's simple, do some high payloads which makes lot tx/rcv traffic on both
> sides:
>
> Node A:
>
> ping6 $IP_NODEB%6lo0 -s 60000
>
> Node B:
>
> ping6 ff02::1%6lo0
>
> that's one example, but you need to produce some transmit data on both
> nodes to see that the nodes stucks in -11 (EAGAIN) of l2cap_chan_send.
>
> Doing that and activate "#define DEBUG" in "bluetooth/6lowpan.c". After
> some time Node A print outs (depends on ping6 implementation, iputils in
> my case):
>
> .... icmp_seq=33 Destination unreachable: Address unreachable
>
> also on Node B, there comes maybe never a responds from Node B
> anymore. Then it's likely that you hit the deadlock.
>
> Now run dmesg on both nodes, you will see each transmit
> (l2cap_chan_send) ends in:
>
> "transmit return value -11"
>
> if this will be printed out via dmesg on both nodes, you hit the deadlock and
> nothing will be transmitted anymore. I need bluetooth experts to solve this
> issue. :-)
>
> Otherwise I detected no issues yet, also I tried to remove my usb dongle
> while high tx/rcv bandwidth at runtime from the qemu vm without crashing
> the kernel (very important test for me).
>
> - Alex
>
> Alexander Aring (20):
> 6lowpan: ndisc: don't remove short address
> nhc: add TODO for nhc work
> ieee802154: 6lowpan: remove headroom check
> ieee802154: 6lowpan: move skb cb BUILD_BUG_ON check
> 6lowpan: remove LOWPAN_IPHC_MAX_HEADER_LEN
> 6lowpan: hold netdev while unregister
> 6lowpan: introduce generic default naming
> 6lowpan: move rx defines to generic
> bluetooth: introduce l2cap_hdev_chan_connect
> bluetooth: add hci dev notifier
> bluetooth: export functions and variables
> 6lowpan: bluetooth: remove implementation
> ieee802154: 6lowpan: move header create to 6lowpan
> 6lowpan: move dev_init to generic
> 6lowpan: iphc: override l2 packet information
> ipv6: addrconf: fix 48 bit 6lowpan autoconfiguration
> 6lowpan: iphc: add handling for btle
> 6lowpan: move multicast flags to generic
> 6lowpan: delete addr_len handling to generic
> 6lowpan: bluetooth: add new implementation
>
> include/net/6lowpan.h | 30 +-
> include/net/bluetooth/hci_core.h | 14 +
> include/net/bluetooth/l2cap.h | 3 +
> net/6lowpan/core.c | 47 +-
> net/6lowpan/iphc.c | 111 +++
> net/6lowpan/ndisc.c | 2 -
> net/6lowpan/nhc.c | 15 +
> net/bluetooth/6lowpan.c | 1788 ++++++++++++++----------------------
> net/bluetooth/hci_core.c | 26 +
> net/bluetooth/hci_sock.c | 2 +
> net/bluetooth/l2cap_core.c | 29 +-
> net/ieee802154/6lowpan/6lowpan_i.h | 9 -
> net/ieee802154/6lowpan/core.c | 23 +-
> net/ieee802154/6lowpan/tx.c | 94 +-
> net/ipv6/addrconf.c | 19 +-
> 15 files changed, 1008 insertions(+), 1204 deletions(-)
>
> --
> 2.9.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to [email protected] More majordomo
> info at http://vger.kernel.org/majordomo-info.html
I have tested the patch series with all 20 patches and verified that I can connect to devices and communicate between them over IPv6. Multicast and unicast where tested.
The neighbors where manually added with the command "ip -6 neigh". However I did not do a long run or thoroughly test, but did the basic verification.
I really liked the way the HCI device was manually selected for the interface, giving me flexibility to choose what connections I wanted on a given HCI device.
Thanks a lot for the effort you have put into this!
Cheers,
Glenn Ruben Bakke