2015-12-14 14:00:52

by Alexander Aring

[permalink] [raw]
Subject: [RFCv4 bluetooth-next 0/2] 6lowpan: 6co and stateful compression support

Hi,

this patch series adds stateful compression support and add 6co option as a new
userspace option for processing RA messages inside userspace.

I am not sure if "6CO" handling inside userspace is the best option here.

I will send also "radvd" patches which introduce a very "basic" support for
processing(non 6LBR)/manage(6LBR) 6CO option fields. These patches doesn't
support lifetime handling of contexts. There exists the question as well if
we should handle the lifetime handling inside userspace or kernelspace.

I am currently follow this approach:
If we doesn't need it inside the kernelspace, then we should handle it in
userspace.

It's difficult to figure out if we really can it handle inside userspace only.

RFC6775 describes some different roles inside the network:

- 6LN (6LoWPAN Node)
- 6LR (Router inside 6LoWPAN network)
- 6LBR ($IP_NETWORK <-> 6LoWPAN network)

Processing ICMPv6 (RA/RS, NA/NS) messages may be different for each role. I
currently have not the full overlook inside RFC6775 and sometimes (as example
of ABRO field, another Option-Field for 6LoWPAN) says:

8.1.3. Routers Processing Router Advertisements
Note: (I suppose this is for 6LR only!)
If a received RA does not contain an ABRO, then the RA MUST be silently
ignored.

---

For my knowledge such handling need to be inside kernelspace. This is filter
functionality only, processing can be handled inside userspace (which needs ABRO
also as userspace option at first), but then the kernel need to know which "role
(6LN, 6LR, 6LBR)" the interface has.

- Alex

changes since v4:
- remove patches for adding debugfs which are already upstream.
- add "ipv6: add 6co as icmpv6 userspace option"
- fix transmit check on (cid) instead (sci || dci) for adding CID inline
data. If CID is zero it will be compressed.
- remove "dci_table, sci_table, mcast_table" we have "ctx_table" only.
- Change enabled with "u32 flags" since we need more information than
"enabled" only. We handle also "compression flag" now.

Alexander Aring (2):
6lowpan: iphc: add support for stateful compression
ipv6: add 6co as icmpv6 userspace option

include/net/6lowpan.h | 31 ++++
include/net/ndisc.h | 1 +
net/6lowpan/core.c | 6 +-
net/6lowpan/debugfs.c | 97 ++++++++++++
net/6lowpan/iphc.c | 420 +++++++++++++++++++++++++++++++++++++++++++-------
net/ipv6/ndisc.c | 3 +-
6 files changed, 499 insertions(+), 59 deletions(-)

--
2.6.1



2015-12-17 13:41:05

by Alexander Aring

[permalink] [raw]
Subject: Re: [RFCv4 bluetooth-next 1/2] 6lowpan: iphc: add support for stateful compression

On Thu, Dec 17, 2015 at 12:26:37PM +0000, Duda, Lukasz wrote:
> Hi Alex!
>
> > -----Original Message-----
> > From: Alexander Aring [mailto:[email protected]]
> > Sent: Tuesday, December 15, 2015 12:09
> > To: Duda, Lukasz
> > Cc: [email protected]; [email protected];
> > [email protected]; [email protected]; [email protected];
> > [email protected]
> > Subject: Re: [RFCv4 bluetooth-next 1/2] 6lowpan: iphc: add support for
> > stateful compression
> >
> > On Tue, Dec 15, 2015 at 10:29:51AM +0000, Duda, Lukasz wrote:
> > > First of all great work for your series of patches on 6lowpan improvements
> > and
> > > stateful compression!
> > >
> > > I have just done some testing of this patch (without RADVD modifications),
> > and
> > > I can share my experiments using 6LoWPAN over BT-LE by sending simple
> > ICMPv6
> > > messages. Contexts for BTLE device has been added manually.
> > >
> >
> > did you test that with linux <-> linux? Or linux <->
> > $SOME_OTHER_6LOWPAN_BTLE_STACK.
> >
> > I tested it on my side with RIOT, it has 802.15.4 6LoWPAN support and
> > also manipulate manually the context table.
> >
>
> I have tested it with nRF52 BTLE device from Nordic Semiconductor
> with IoT SDK, and Linux Ubuntu with BTLE Dongle that acts as Router/Master.
>
> > > Experiment 1:
> > >
> > > Router: 2001:db8::1/64 BTLE: 2001:db8::211:22FF:FE33:4455/64
> > > CID 1: 2001:db8::/64
> > >
> > > Works fine, I see that CID 1 is used for both addresses. Router has 64 bits of
> > > IID inline and BTLE node has 0.
> > >
> > > Experiment 2:
> > >
> > > Router: 2001:db8::1/64 BTLE: 2001:db8::211:22FF:FE33:4455/64
> > > CID 3: 2001:db8::/64 CID 5: 2001:db8::1/128
> > >
> > > Works also fine, I see that both CID 3 and 5 are used, as well as both sides
> > > compress its IID in the best possible way. So the patch appears to work fine
> > on
> > > 6LoWPAN over BT-LE.
> > >
> >
> > ok.
> >
> > >
> > > However, I notice that the folder created in the sys/kernel/debug/6lowpan/
> > for
> > > my bluetooth network interface is called "bt%d". And I would imagine this
> > > should be "bt0", "bt1", ... and not the template?
> > >
> >
> > urgh, this should not happen. I use "dev->name" for that and dev is the
> > netdevice structure. This should be an _unique_ interface name,
> > otherwise you will getting trouble if you have two btle 6lowpan
> > interfaces.
> >
> > I didn't realized it because I create my interface with:
> >
> > ip link add link wpan0 name lowpan0 type lowpan
> >
> > but should change it into:
> >
> > ip link add link wpan0 name lowpan%d type lowpan
> >
> > I realized that the dev->name will be changed from template into "real"
> > name after registering. This should do the job:
> >
> > diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
> > index c7f06f5..faf65ba 100644
> > --- a/net/6lowpan/core.c
> > +++ b/net/6lowpan/core.c
> > @@ -29,13 +29,13 @@ int lowpan_register_netdevice(struct net_device
> > *dev,
> >
> > lowpan_priv(dev)->lltype = lltype;
> >
> > - ret = lowpan_dev_debugfs_init(dev);
> > + ret = register_netdevice(dev);
> > if (ret < 0)
> > return ret;
> >
> > - ret = register_netdevice(dev);
> > + ret = lowpan_dev_debugfs_init(dev);
> > if (ret < 0)
> > - lowpan_dev_debugfs_exit(dev);
> > + unregister_netdevice(dev);
> >
> > return ret;
> > }
> >
> > I think it should be safe to do that after registering because we held the
> > RTNL lock. And the interface isn't up after registering.
> >
>
> Thanks! Your patch helped, and I acked it in separate mail thread.
>
> > > Also, I notice that the compression for Flow Control and Traffic Label in IPv6
> > > header has been modified, these fields are no longer compressed in any
> > packets
> > > (0b11 value) that comes from Linux Kernel (e.g. ICMP Echo Request,
> > > Router Advertisement), instead I get three extra bytes (0b01 value).
> > > I would like to understand reason for this modification a little better.
> >
> > 0b11 means that traffic class and flow label are zero. Are you sure that
> > these fields are zero inside the IPv6 header when you transmit
> > "e.g. ICMP Echo Request, RA"?
> >
> > Can you verify this by running tcpdump/wireshark? Or instruments some
> > printk's at [0] for hdr->flow_lbl array and hdr->priority?
> >
> > - Alex
> >
> > [0] http://lxr.free-electrons.com/source/net/6lowpan/iphc.c#L428
>
> I did some more linux debugging, and indeed, its IPv6 stack that already gives ip6hdr
> with flow label set to some strange number. Do you know maybe the reason of this?
> On Kernel version < 4.2 that field was always set to 0, thus better compression can
> be applied.
>

I think it depends on ping6 implementation. There exists some of them
outside, I using "iputils" [0].

Look inside the manpage of iputils:

-F flow label
ping6 only. Allocate and set 20 bit flow label (in hex) on echo
request packets. If value is zero, kernel allocates random flow label.

This is for "echo request" only and if set it to zero then a random
one will be used. Anyway you can also try to set some -F "0xdead" and
look in wireshark if it's (6LoWPAN adaptation/transmitting) was
correctly.

What I believe when I read "If value is zero, kernel allocates random
flow label.", then you can't set it to zero from userspace, but I am not
100% sure, may depends on socket type (which depends on ping6
implementation).

At least:

It could be that the compression never worked correctly before and
simple set always flow_lbl compression to zero? Depends on how you test
it at "Kernel version < 4.2".

Please verify the testing with "Kernel version < 4.2" and check if
flow_lbl is zero before running 6LoWPAN adaptation with same ping6
implementation (but may also differs if they do some runtime checks on
what kernel supports).

- Alex

[0] https://github.com/iputils/iputils

2015-12-17 12:26:37

by Lukasz Duda

[permalink] [raw]
Subject: RE: [RFCv4 bluetooth-next 1/2] 6lowpan: iphc: add support for stateful compression

SGkgQWxleCENCg0KPiAtLS0tLU9yaWdpbmFsIE1lc3NhZ2UtLS0tLQ0KPiBGcm9tOiBBbGV4YW5k
ZXIgQXJpbmcgW21haWx0bzphbGV4LmFyaW5nQGdtYWlsLmNvbV0NCj4gU2VudDogVHVlc2RheSwg
RGVjZW1iZXIgMTUsIDIwMTUgMTI6MDkNCj4gVG86IER1ZGEsIEx1a2Fzeg0KPiBDYzogbGludXgt
d3BhbkB2Z2VyLmtlcm5lbC5vcmc7IGxpbnV4LWJsdWV0b290aEB2Z2VyLmtlcm5lbC5vcmc7DQo+
IG5ldGRldkB2Z2VyLmtlcm5lbC5vcmc7IGtlcm5lbEBwZW5ndXRyb25peC5kZTsgbWNyQHNhbmRl
bG1hbi5jYTsNCj4gbWFydGluLmdlcmdlbGVpdEBocy1ybS5kZQ0KPiBTdWJqZWN0OiBSZTogW1JG
Q3Y0IGJsdWV0b290aC1uZXh0IDEvMl0gNmxvd3BhbjogaXBoYzogYWRkIHN1cHBvcnQgZm9yDQo+
IHN0YXRlZnVsIGNvbXByZXNzaW9uDQo+IA0KPiBPbiBUdWUsIERlYyAxNSwgMjAxNSBhdCAxMDoy
OTo1MUFNICswMDAwLCBEdWRhLCBMdWthc3ogd3JvdGU6DQo+ID4gRmlyc3Qgb2YgYWxsIGdyZWF0
IHdvcmsgZm9yIHlvdXIgc2VyaWVzIG9mIHBhdGNoZXMgb24gNmxvd3BhbiBpbXByb3ZlbWVudHMN
Cj4gYW5kDQo+ID4gc3RhdGVmdWwgY29tcHJlc3Npb24hDQo+ID4NCj4gPiBJIGhhdmUganVzdCBk
b25lIHNvbWUgdGVzdGluZyBvZiB0aGlzIHBhdGNoICh3aXRob3V0IFJBRFZEIG1vZGlmaWNhdGlv
bnMpLA0KPiBhbmQNCj4gPiBJIGNhbiBzaGFyZSBteSBleHBlcmltZW50cyB1c2luZyA2TG9XUEFO
IG92ZXIgQlQtTEUgYnkgc2VuZGluZyBzaW1wbGUNCj4gSUNNUHY2DQo+ID4gbWVzc2FnZXMuIENv
bnRleHRzIGZvciBCVExFIGRldmljZSBoYXMgYmVlbiBhZGRlZCBtYW51YWxseS4NCj4gPg0KPiAN
Cj4gZGlkIHlvdSB0ZXN0IHRoYXQgd2l0aCBsaW51eCA8LT4gbGludXg/IE9yIGxpbnV4IDwtPg0K
PiAkU09NRV9PVEhFUl82TE9XUEFOX0JUTEVfU1RBQ0suDQo+IA0KPiBJIHRlc3RlZCBpdCBvbiBt
eSBzaWRlIHdpdGggUklPVCwgaXQgaGFzIDgwMi4xNS40IDZMb1dQQU4gc3VwcG9ydCBhbmQNCj4g
YWxzbyBtYW5pcHVsYXRlIG1hbnVhbGx5IHRoZSBjb250ZXh0IHRhYmxlLg0KPiANCg0KSSBoYXZl
IHRlc3RlZCBpdCB3aXRoIG5SRjUyIEJUTEUgZGV2aWNlIGZyb20gTm9yZGljIFNlbWljb25kdWN0
b3IgDQp3aXRoIElvVCBTREssIGFuZCBMaW51eCBVYnVudHUgd2l0aCBCVExFIERvbmdsZSB0aGF0
IGFjdHMgYXMgUm91dGVyL01hc3Rlci4NCg0KPiA+IEV4cGVyaW1lbnQgMToNCj4gPg0KPiA+IFJv
dXRlcjogMjAwMTpkYjg6OjEvNjQgQlRMRTogMjAwMTpkYjg6OjIxMToyMkZGOkZFMzM6NDQ1NS82
NA0KPiA+IENJRCAxOiAyMDAxOmRiODo6LzY0DQo+ID4NCj4gPiBXb3JrcyBmaW5lLCBJIHNlZSB0
aGF0IENJRCAxIGlzIHVzZWQgZm9yIGJvdGggYWRkcmVzc2VzLiBSb3V0ZXIgaGFzIDY0IGJpdHMg
b2YNCj4gPiBJSUQgaW5saW5lIGFuZCBCVExFIG5vZGUgaGFzIDAuDQo+ID4NCj4gPiBFeHBlcmlt
ZW50IDI6DQo+ID4NCj4gPiBSb3V0ZXI6IDIwMDE6ZGI4OjoxLzY0IEJUTEU6IDIwMDE6ZGI4Ojoy
MTE6MjJGRjpGRTMzOjQ0NTUvNjQNCj4gPiBDSUQgMzogMjAwMTpkYjg6Oi82NCBDSUQgNTogMjAw
MTpkYjg6OjEvMTI4DQo+ID4NCj4gPiBXb3JrcyBhbHNvIGZpbmUsIEkgc2VlIHRoYXQgYm90aCBD
SUQgMyBhbmQgNSBhcmUgdXNlZCwgYXMgd2VsbCBhcyBib3RoIHNpZGVzDQo+ID4gY29tcHJlc3Mg
aXRzIElJRCBpbiB0aGUgYmVzdCBwb3NzaWJsZSB3YXkuIFNvIHRoZSBwYXRjaCBhcHBlYXJzIHRv
IHdvcmsgZmluZQ0KPiBvbg0KPiA+IDZMb1dQQU4gb3ZlciBCVC1MRS4NCj4gPg0KPiANCj4gb2su
DQo+IA0KPiA+DQo+ID4gSG93ZXZlciwgSSBub3RpY2UgdGhhdCB0aGUgZm9sZGVyIGNyZWF0ZWQg
aW4gdGhlIHN5cy9rZXJuZWwvZGVidWcvNmxvd3Bhbi8NCj4gZm9yDQo+ID4gbXkgYmx1ZXRvb3Ro
IG5ldHdvcmsgaW50ZXJmYWNlIGlzIGNhbGxlZCAiYnQlZCIuIEFuZCBJIHdvdWxkIGltYWdpbmUg
dGhpcw0KPiA+IHNob3VsZCBiZSAiYnQwIiwgImJ0MSIsIC4uLiBhbmQgbm90IHRoZSB0ZW1wbGF0
ZT8NCj4gPg0KPiANCj4gdXJnaCwgdGhpcyBzaG91bGQgbm90IGhhcHBlbi4gSSB1c2UgImRldi0+
bmFtZSIgZm9yIHRoYXQgYW5kIGRldiBpcyB0aGUNCj4gbmV0ZGV2aWNlIHN0cnVjdHVyZS4gVGhp
cyBzaG91bGQgYmUgYW4gX3VuaXF1ZV8gaW50ZXJmYWNlIG5hbWUsDQo+IG90aGVyd2lzZSB5b3Ug
d2lsbCBnZXR0aW5nIHRyb3VibGUgaWYgeW91IGhhdmUgdHdvIGJ0bGUgNmxvd3Bhbg0KPiBpbnRl
cmZhY2VzLg0KPiANCj4gSSBkaWRuJ3QgcmVhbGl6ZWQgaXQgYmVjYXVzZSBJIGNyZWF0ZSBteSBp
bnRlcmZhY2Ugd2l0aDoNCj4gDQo+IGlwIGxpbmsgYWRkIGxpbmsgd3BhbjAgbmFtZSBsb3dwYW4w
IHR5cGUgbG93cGFuDQo+IA0KPiBidXQgc2hvdWxkIGNoYW5nZSBpdCBpbnRvOg0KPiANCj4gaXAg
bGluayBhZGQgbGluayB3cGFuMCBuYW1lIGxvd3BhbiVkIHR5cGUgbG93cGFuDQo+IA0KPiBJIHJl
YWxpemVkIHRoYXQgdGhlIGRldi0+bmFtZSB3aWxsIGJlIGNoYW5nZWQgZnJvbSB0ZW1wbGF0ZSBp
bnRvICJyZWFsIg0KPiBuYW1lIGFmdGVyIHJlZ2lzdGVyaW5nLiBUaGlzIHNob3VsZCBkbyB0aGUg
am9iOg0KPiANCj4gZGlmZiAtLWdpdCBhL25ldC82bG93cGFuL2NvcmUuYyBiL25ldC82bG93cGFu
L2NvcmUuYw0KPiBpbmRleCBjN2YwNmY1Li5mYWY2NWJhIDEwMDY0NA0KPiAtLS0gYS9uZXQvNmxv
d3Bhbi9jb3JlLmMNCj4gKysrIGIvbmV0LzZsb3dwYW4vY29yZS5jDQo+IEBAIC0yOSwxMyArMjks
MTMgQEAgaW50IGxvd3Bhbl9yZWdpc3Rlcl9uZXRkZXZpY2Uoc3RydWN0IG5ldF9kZXZpY2UNCj4g
KmRldiwNCj4gDQo+ICAgICAgICAgbG93cGFuX3ByaXYoZGV2KS0+bGx0eXBlID0gbGx0eXBlOw0K
PiANCj4gLSAgICAgICByZXQgPSBsb3dwYW5fZGV2X2RlYnVnZnNfaW5pdChkZXYpOw0KPiArICAg
ICAgIHJldCA9IHJlZ2lzdGVyX25ldGRldmljZShkZXYpOw0KPiAgICAgICAgIGlmIChyZXQgPCAw
KQ0KPiAgICAgICAgICAgICAgICAgcmV0dXJuIHJldDsNCj4gDQo+IC0gICAgICAgcmV0ID0gcmVn
aXN0ZXJfbmV0ZGV2aWNlKGRldik7DQo+ICsgICAgICAgcmV0ID0gbG93cGFuX2Rldl9kZWJ1Z2Zz
X2luaXQoZGV2KTsNCj4gICAgICAgICBpZiAocmV0IDwgMCkNCj4gLSAgICAgICAgICAgICAgIGxv
d3Bhbl9kZXZfZGVidWdmc19leGl0KGRldik7DQo+ICsgICAgICAgICAgICAgICB1bnJlZ2lzdGVy
X25ldGRldmljZShkZXYpOw0KPiANCj4gICAgICAgICByZXR1cm4gcmV0Ow0KPiAgfQ0KPiANCj4g
SSB0aGluayBpdCBzaG91bGQgYmUgc2FmZSB0byBkbyB0aGF0IGFmdGVyIHJlZ2lzdGVyaW5nIGJl
Y2F1c2Ugd2UgaGVsZCB0aGUNCj4gUlROTCBsb2NrLiBBbmQgdGhlIGludGVyZmFjZSBpc24ndCB1
cCBhZnRlciByZWdpc3RlcmluZy4NCj4gDQoNClRoYW5rcyEgWW91ciBwYXRjaCBoZWxwZWQsIGFu
ZCBJIGFja2VkIGl0IGluIHNlcGFyYXRlIG1haWwgdGhyZWFkLg0KDQo+ID4gQWxzbywgSSBub3Rp
Y2UgdGhhdCB0aGUgY29tcHJlc3Npb24gZm9yIEZsb3cgQ29udHJvbCBhbmQgVHJhZmZpYyBMYWJl
bCBpbiBJUHY2DQo+ID4gaGVhZGVyIGhhcyBiZWVuIG1vZGlmaWVkLCB0aGVzZSBmaWVsZHMgYXJl
IG5vIGxvbmdlciBjb21wcmVzc2VkIGluIGFueQ0KPiBwYWNrZXRzDQo+ID4gKDBiMTEgdmFsdWUp
IHRoYXQgY29tZXMgZnJvbSBMaW51eCBLZXJuZWwgKGUuZy4gSUNNUCBFY2hvIFJlcXVlc3QsDQo+
ID4gUm91dGVyIEFkdmVydGlzZW1lbnQpLCBpbnN0ZWFkIEkgZ2V0IHRocmVlIGV4dHJhIGJ5dGVz
ICgwYjAxIHZhbHVlKS4NCj4gPiBJIHdvdWxkIGxpa2UgdG8gdW5kZXJzdGFuZCByZWFzb24gZm9y
IHRoaXMgbW9kaWZpY2F0aW9uIGEgbGl0dGxlIGJldHRlci4NCj4gDQo+IDBiMTEgbWVhbnMgdGhh
dCB0cmFmZmljIGNsYXNzIGFuZCBmbG93IGxhYmVsIGFyZSB6ZXJvLiBBcmUgeW91IHN1cmUgdGhh
dA0KPiB0aGVzZSBmaWVsZHMgYXJlIHplcm8gaW5zaWRlIHRoZSBJUHY2IGhlYWRlciB3aGVuIHlv
dSB0cmFuc21pdA0KPiAiZS5nLiBJQ01QIEVjaG8gUmVxdWVzdCwgUkEiPw0KPiANCj4gQ2FuIHlv
dSB2ZXJpZnkgdGhpcyBieSBydW5uaW5nIHRjcGR1bXAvd2lyZXNoYXJrPyBPciBpbnN0cnVtZW50
cyBzb21lDQo+IHByaW50aydzIGF0IFswXSBmb3IgaGRyLT5mbG93X2xibCBhcnJheSBhbmQgaGRy
LT5wcmlvcml0eT8NCj4gDQo+IC0gQWxleA0KPiANCj4gWzBdIGh0dHA6Ly9seHIuZnJlZS1lbGVj
dHJvbnMuY29tL3NvdXJjZS9uZXQvNmxvd3Bhbi9pcGhjLmMjTDQyOA0KDQpJIGRpZCBzb21lIG1v
cmUgbGludXggZGVidWdnaW5nLCBhbmQgaW5kZWVkLCBpdHMgSVB2NiBzdGFjayB0aGF0IGFscmVh
ZHkgZ2l2ZXMgaXA2aGRyDQp3aXRoIGZsb3cgbGFiZWwgc2V0IHRvIHNvbWUgc3RyYW5nZSBudW1i
ZXIuIERvIHlvdSBrbm93IG1heWJlIHRoZSByZWFzb24gb2YgdGhpcz8NCk9uIEtlcm5lbCB2ZXJz
aW9uIDwgNC4yIHRoYXQgZmllbGQgd2FzIGFsd2F5cyBzZXQgdG8gMCwgdGh1cyBiZXR0ZXIgY29t
cHJlc3Npb24gY2FuDQpiZSBhcHBsaWVkLg0KDQpCZXN0IHJlZ2FyZHMsDQrFgXVrYXN6DQo=

2015-12-15 11:08:41

by Alexander Aring

[permalink] [raw]
Subject: Re: [RFCv4 bluetooth-next 1/2] 6lowpan: iphc: add support for stateful compression

On Tue, Dec 15, 2015 at 10:29:51AM +0000, Duda, Lukasz wrote:
> First of all great work for your series of patches on 6lowpan improvements and
> stateful compression!
>
> I have just done some testing of this patch (without RADVD modifications), and
> I can share my experiments using 6LoWPAN over BT-LE by sending simple ICMPv6
> messages. Contexts for BTLE device has been added manually.
>

did you test that with linux <-> linux? Or linux <->
$SOME_OTHER_6LOWPAN_BTLE_STACK.

I tested it on my side with RIOT, it has 802.15.4 6LoWPAN support and
also manipulate manually the context table.

> Experiment 1:
>
> Router: 2001:db8::1/64 BTLE: 2001:db8::211:22FF:FE33:4455/64
> CID 1: 2001:db8::/64
>
> Works fine, I see that CID 1 is used for both addresses. Router has 64 bits of
> IID inline and BTLE node has 0.
>
> Experiment 2:
>
> Router: 2001:db8::1/64 BTLE: 2001:db8::211:22FF:FE33:4455/64
> CID 3: 2001:db8::/64 CID 5: 2001:db8::1/128
>
> Works also fine, I see that both CID 3 and 5 are used, as well as both sides
> compress its IID in the best possible way. So the patch appears to work fine on
> 6LoWPAN over BT-LE.
>

ok.

>
> However, I notice that the folder created in the sys/kernel/debug/6lowpan/ for
> my bluetooth network interface is called "bt%d". And I would imagine this
> should be "bt0", "bt1", ... and not the template?
>

urgh, this should not happen. I use "dev->name" for that and dev is the
netdevice structure. This should be an _unique_ interface name,
otherwise you will getting trouble if you have two btle 6lowpan
interfaces.

I didn't realized it because I create my interface with:

ip link add link wpan0 name lowpan0 type lowpan

but should change it into:

ip link add link wpan0 name lowpan%d type lowpan

I realized that the dev->name will be changed from template into "real"
name after registering. This should do the job:

diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
index c7f06f5..faf65ba 100644
--- a/net/6lowpan/core.c
+++ b/net/6lowpan/core.c
@@ -29,13 +29,13 @@ int lowpan_register_netdevice(struct net_device *dev,

lowpan_priv(dev)->lltype = lltype;

- ret = lowpan_dev_debugfs_init(dev);
+ ret = register_netdevice(dev);
if (ret < 0)
return ret;

- ret = register_netdevice(dev);
+ ret = lowpan_dev_debugfs_init(dev);
if (ret < 0)
- lowpan_dev_debugfs_exit(dev);
+ unregister_netdevice(dev);

return ret;
}

I think it should be safe to do that after registering because we held the
RTNL lock. And the interface isn't up after registering.

> Also, I notice that the compression for Flow Control and Traffic Label in IPv6
> header has been modified, these fields are no longer compressed in any packets
> (0b11 value) that comes from Linux Kernel (e.g. ICMP Echo Request,
> Router Advertisement), instead I get three extra bytes (0b01 value).
> I would like to understand reason for this modification a little better.

0b11 means that traffic class and flow label are zero. Are you sure that
these fields are zero inside the IPv6 header when you transmit
"e.g. ICMP Echo Request, RA"?

Can you verify this by running tcpdump/wireshark? Or instruments some
printk's at [0] for hdr->flow_lbl array and hdr->priority?

- Alex

[0] http://lxr.free-electrons.com/source/net/6lowpan/iphc.c#L428

2015-12-15 10:29:51

by Lukasz Duda

[permalink] [raw]
Subject: RE: [RFCv4 bluetooth-next 1/2] 6lowpan: iphc: add support for stateful compression

Hi Alex,

> -----Original Message-----
> From: Alexander Aring [mailto:[email protected]]
> Sent: Monday, December 14, 2015 15:01
> To: [email protected]
> Cc: [email protected]; [email protected];
> [email protected]; [email protected]; Duda, Lukasz;
> [email protected]; Alexander Aring
> Subject: [RFCv4 bluetooth-next 1/2] 6lowpan: iphc: add support for stateful
> compression
>
> This patch introduce support for IPHC stateful address compression. It will
> offer the context table via one debugfs entry.
>
> Example to setup a context id:
>
> A "cat /sys/kernel/debug/6lowpan/lowpan0/ctx_table" will display all
> contexts which are available. Example:
>
> ID ipv6-address/prefix-length flags
> 0 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 1 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 2 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 3 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 4 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 5 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 6 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 7 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 8 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 9 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 10 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 11 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 12 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 13 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 14 0000:0000:0000:0000:0000:0000:0000:0000/0 0
> 15 0000:0000:0000:0000:0000:0000:0000:0000/0 0
>
> For setting a context e.g. context id 0, context 2001::, prefix-length 64.
>
> Hint: Simple copy one line and then maniuplate it.
>
> echo "0 2001:0000:0000:0000:0000:0000:0000:0000/64 3" >
> /sys/kernel/debug/6lowpan/lowpan0/ctx_table
>
> The flags are currently two:
>
> - BIT(0) - active: entry is added or deleted to the ctx_table.
> - BIT(1) - c: compression flag according rfc6775.
>
> On transmit side:
>
> The IPHC code will automatically search for a context which would be match
> for the address. Then it will be use the context with the best compression
> method. Means the longest prefix which match will be used.
>
> Example:
>
> 2001::/126 vs 2001::/127 - the 2001::/127 can be full compressed if the last
> bit of the address which has the prefix 2001::/127 is the same like the IID
> from the Encapsulating Header. A context ID can also be a 2001::1/128, which
> is then a full ipv6 address.
>
> On Receive side:
>
> If there is a context defined (when CID not available then it's the default
> context 0) then it will be used, if the header doesn't set SAC or DAC bit thens,
> it will be dropped.
>
> Signed-off-by: Alexander Aring <[email protected]>
> ---
> include/net/6lowpan.h | 31 ++++
> net/6lowpan/core.c | 6 +-
> net/6lowpan/debugfs.c | 97 ++++++++++++
> net/6lowpan/iphc.c | 420
> +++++++++++++++++++++++++++++++++++++++++++-------
> 4 files changed, 496 insertions(+), 58 deletions(-)
>
> diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h index
> 2f6a3f2..db636c8 100644
> --- a/include/net/6lowpan.h
> +++ b/include/net/6lowpan.h
> @@ -75,6 +75,8 @@
> #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_CI_TABLE_SIZE (1 << 4)
>
> #define LOWPAN_DISPATCH_IPV6 0x41 /* 01000001 = 65 */
> #define LOWPAN_DISPATCH_IPHC 0x60 /* 011xxxxx = ... */
> @@ -98,9 +100,38 @@ enum lowpan_lltypes {
> LOWPAN_LLTYPE_IEEE802154,
> };
>
> +enum lowpan_iphc_ctx_flags {
> + LOWPAN_IPHC_CTX_FLAG_ACTIVE = BIT(0),
> + LOWPAN_IPHC_CTX_FLAG_C = BIT(1),
> +};
> +
> +struct lowpan_iphc_ctx {
> + u8 id;
> + struct in6_addr pfx;
> + u8 plen;
> + u32 flags;
> +};
> +
> +struct lowpan_iphc_ctx_table {
> + spinlock_t lock;
> + const struct lowpan_iphc_ctx_ops *ops;
> + struct lowpan_iphc_ctx table[LOWPAN_IPHC_CI_TABLE_SIZE]; };
> +
> +static inline bool lowpan_iphc_ctx_is_active(const struct
> +lowpan_iphc_ctx *ctx) {
> + return ctx->flags & LOWPAN_IPHC_CTX_FLAG_ACTIVE; }
> +
> +static inline bool lowpan_iphc_ctx_is_c(const struct lowpan_iphc_ctx
> +*ctx) {
> + return ctx->flags & LOWPAN_IPHC_CTX_FLAG_C; }
> +
> struct lowpan_priv {
> enum lowpan_lltypes lltype;
> struct dentry *iface_debugfs;
> + struct lowpan_iphc_ctx_table ctx;
>
> /* must be last */
> u8 priv[0] __aligned(sizeof(void *));
> diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c index c7f06f5..772f51c
> 100644
> --- a/net/6lowpan/core.c
> +++ b/net/6lowpan/core.c
> @@ -20,7 +20,7 @@
> int lowpan_register_netdevice(struct net_device *dev,
> enum lowpan_lltypes lltype)
> {
> - int ret;
> + int i, ret;
>
> dev->addr_len = EUI64_ADDR_LEN;
> dev->type = ARPHRD_6LOWPAN;
> @@ -29,6 +29,10 @@ int lowpan_register_netdevice(struct net_device
> *dev,
>
> lowpan_priv(dev)->lltype = lltype;
>
> + spin_lock_init(&lowpan_priv(dev)->ctx.lock);
> + for (i = 0; i < LOWPAN_IPHC_CI_TABLE_SIZE; i++)
> + lowpan_priv(dev)->ctx.table[i].id = i;
> +
> ret = lowpan_dev_debugfs_init(dev);
> if (ret < 0)
> return ret;
> diff --git a/net/6lowpan/debugfs.c b/net/6lowpan/debugfs.c index
> 88eef84..5270fa1 100644
> --- a/net/6lowpan/debugfs.c
> +++ b/net/6lowpan/debugfs.c
> @@ -16,19 +16,116 @@
>
> #include "6lowpan_i.h"
>
> +#define LOWPAN_DEBUGFS_CTX_NUM_ARGS 11
> +
> static struct dentry *lowpan_debugfs;
>
> +static int lowpan_context_show(struct seq_file *file, void *offset) {
> + struct lowpan_iphc_ctx_table *t = file->private;
> + int i;
> +
> + seq_printf(file, "%-2s %-43s %s\n", "ID", "ipv6-address/prefix-length",
> + "flags");
> +
> + spin_lock_bh(&t->lock);
> + for (i = 0; i < LOWPAN_IPHC_CI_TABLE_SIZE; i++)
> + seq_printf(file,
> + "%-2d
> %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x/%-3d %x\n",
> + t->table[i].id,
> + be16_to_cpu(t->table[i].pfx.s6_addr16[0]),
> + be16_to_cpu(t->table[i].pfx.s6_addr16[1]),
> + be16_to_cpu(t->table[i].pfx.s6_addr16[2]),
> + be16_to_cpu(t->table[i].pfx.s6_addr16[3]),
> + be16_to_cpu(t->table[i].pfx.s6_addr16[4]),
> + be16_to_cpu(t->table[i].pfx.s6_addr16[5]),
> + be16_to_cpu(t->table[i].pfx.s6_addr16[6]),
> + be16_to_cpu(t->table[i].pfx.s6_addr16[7]),
> + t->table[i].plen, t->table[i].flags);
> + spin_unlock_bh(&t->lock);
> +
> + return 0;
> +}
> +
> +static int lowpan_context_dbgfs_open(struct inode *inode, struct file
> +*file) {
> + return single_open(file, lowpan_context_show, inode->i_private); }
> +
> +static ssize_t lowpan_context_dbgfs_write(struct file *fp,
> + const char __user *user_buf,
> + size_t count, loff_t *ppos)
> +{
> + char buf[128] = {};
> + struct seq_file *file = fp->private_data;
> + struct lowpan_iphc_ctx_table *t = file->private;
> + struct lowpan_iphc_ctx ctx;
> + int status = count, n, id, i, plen;
> + unsigned int addr[8], flags;
> +
> + if (copy_from_user(&buf, user_buf, min_t(size_t, sizeof(buf) - 1,
> + count))) {
> + status = -EFAULT;
> + goto out;
> + }
> +
> + n = sscanf(buf, "%d
> %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x/%d %x",
> + &id, &addr[0], &addr[1], &addr[2], &addr[3], &addr[4],
> + &addr[5], &addr[6], &addr[7], &plen, &flags);
> + if (n != LOWPAN_DEBUGFS_CTX_NUM_ARGS) {
> + status = -EIO;
> + goto out;
> + }
> +
> + if (id > LOWPAN_IPHC_CI_TABLE_SIZE - 1 || plen > 128) {
> + status = -EINVAL;
> + goto out;
> + }
> +
> + ctx.id = id;
> + ctx.plen = plen;
> + ctx.flags = flags & (LOWPAN_IPHC_CTX_FLAG_ACTIVE |
> + LOWPAN_IPHC_CTX_FLAG_C);
> +
> + for (i = 0; i < 8; i++)
> + ctx.pfx.s6_addr16[i] = cpu_to_be16(addr[i] & 0xffff);
> +
> + spin_lock_bh(&t->lock);
> + memcpy(&t->table[ctx.id], &ctx, sizeof(ctx));
> + spin_unlock_bh(&t->lock);
> +
> +out:
> + return status;
> +}
> +
> +const struct file_operations lowpan_context_fops = {
> + .open = lowpan_context_dbgfs_open,
> + .read = seq_read,
> + .write = lowpan_context_dbgfs_write,
> + .llseek = seq_lseek,
> + .release = single_release,
> +};
> +
> int lowpan_dev_debugfs_init(struct net_device *dev) {
> struct lowpan_priv *lpriv = lowpan_priv(dev);
> + static struct dentry *dentry;
>
> /* creating the root */
> lpriv->iface_debugfs = debugfs_create_dir(dev->name,
> lowpan_debugfs);
> if (!lpriv->iface_debugfs)
> goto fail;
>
> + dentry = debugfs_create_file("ctx_table", 0644, lpriv->iface_debugfs,
> + &lowpan_priv(dev)->ctx,
> + &lowpan_context_fops);
> + if (!dentry)
> + goto remove_root;
> +
> return 0;
>
> +remove_root:
> + lowpan_dev_debugfs_exit(dev);
> fail:
> return -EINVAL;
> }
> diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c index
> 346b5c1..909e0e8 100644
> --- a/net/6lowpan/iphc.c
> +++ b/net/6lowpan/iphc.c
> @@ -56,6 +56,7 @@
> /* special link-layer handling */
> #include <net/mac802154.h>
>
> +#include "6lowpan_i.h"
> #include "nhc.h"
>
> /* Values of fields within the IPHC encoding first byte */ @@ -147,6 +148,9
> @@
> (((a)->s6_addr16[6]) == 0) && \
> (((a)->s6_addr[14]) == 0))
>
> +#define LOWPAN_IPHC_CID_DCI(cid) (cid & 0x0f)
> +#define LOWPAN_IPHC_CID_SCI(cid) ((cid & 0xf0) >> 4)
> +
> static inline void iphc_uncompress_eui64_lladdr(struct in6_addr *ipaddr,
> const void *lladdr)
> {
> @@ -195,6 +199,104 @@ static inline void
> iphc_uncompress_802154_lladdr(struct in6_addr *ipaddr,
> }
> }
>
> +static struct lowpan_iphc_ctx *
> +lowpan_iphc_ctx_get_by_id(const struct net_device *dev, u8 id) {
> + struct lowpan_iphc_ctx *ret = &lowpan_priv(dev)->ctx.table[id];
> +
> + WARN_ON_ONCE(id > LOWPAN_IPHC_CI_TABLE_SIZE);
> +
> + if (!lowpan_iphc_ctx_is_active(ret))
> + return NULL;
> +
> + /* Don't need to check the compression flag here. Context SHOULD
> be used
> + * in decompression case another compressor has not yet received
> the
> + * updated context information.
> + */
> + return ret;
> +}
> +
> +static struct lowpan_iphc_ctx *
> +lowpan_iphc_ctx_get_by_addr(const struct net_device *dev,
> + const struct in6_addr *addr)
> +{
> + struct lowpan_iphc_ctx *table = lowpan_priv(dev)->ctx.table;
> + struct lowpan_iphc_ctx *ret = NULL;
> + struct in6_addr addr_pfx;
> + u8 addr_plen;
> + int i;
> +
> + for (i = 0; i < LOWPAN_IPHC_CI_TABLE_SIZE; i++) {
> + /* Check if context is valid. A context that is not valid
> + * MUST NOT be used for compression.
> + */
> + if (!lowpan_iphc_ctx_is_active(&table[i]) ||
> + !lowpan_iphc_ctx_is_c(&table[i]))
> + continue;
> +
> + ipv6_addr_prefix(&addr_pfx, addr, table[i].plen);
> +
> + /* if prefix len < 64, the remaining bits until 64th bit is
> + * zero. Otherwise we use table[i]->plen.
> + */
> + if (table[i].plen < 64)
> + addr_plen = 64;
> + else
> + addr_plen = table[i].plen;
> +
> + if (ipv6_prefix_equal(&addr_pfx, &table[i].pfx, addr_plen)) {
> + /* remember first match */
> + if (!ret) {
> + ret = &table[i];
> + continue;
> + }
> +
> + /* get the context with longest prefix len */
> + if (table[i].plen > ret->plen)
> + ret = &table[i];
> + }
> + }
> +
> + return ret;
> +}
> +
> +static struct lowpan_iphc_ctx *
> +lowpan_iphc_ctx_get_by_mcast_addr(const struct net_device *dev,
> + const struct in6_addr *addr)
> +{
> + struct lowpan_iphc_ctx *table = lowpan_priv(dev)->ctx.table;
> + struct lowpan_iphc_ctx *ret = NULL;
> + struct in6_addr addr_mcast, network_pfx = {};
> + int i;
> +
> + /* init mcast address with */
> + memcpy(&addr_mcast, addr, sizeof(*addr));
> +
> + for (i = 0; i < LOWPAN_IPHC_CI_TABLE_SIZE; i++) {
> + /* Check if context is valid. A context that is not valid
> + * MUST NOT be used for compression.
> + */
> + if (!lowpan_iphc_ctx_is_active(&table[i]) ||
> + !lowpan_iphc_ctx_is_c(&table[i]))
> + continue;
> +
> + /* setting plen */
> + addr_mcast.s6_addr[3] = table[i].plen;
> + /* get network prefix to copy into multicast address */
> + ipv6_addr_prefix(&network_pfx, &table[i].pfx,
> + table[i].plen);
> + /* setting network prefix */
> + memcpy(&addr_mcast.s6_addr[4], &network_pfx, 8);
> +
> + if (ipv6_addr_equal(addr, &addr_mcast)) {
> + ret = &table[i];
> + break;
> + }
> + }
> +
> + return ret;
> +}
> +
> /* Uncompress address function for source and
> * destination address(non-multicast).
> *
> @@ -259,30 +361,59 @@ static int uncompress_addr(struct sk_buff *skb,
> const struct net_device *dev,
> /* Uncompress address function for source context
> * based address(non-multicast).
> */
> -static int uncompress_context_based_src_addr(struct sk_buff *skb,
> - struct in6_addr *ipaddr,
> - u8 address_mode)
> +static int uncompress_ctx_addr(struct sk_buff *skb,
> + const struct net_device *dev,
> + const struct lowpan_iphc_ctx *ctx,
> + struct in6_addr *ipaddr, u8 address_mode,
> + const void *lladdr)
> {
> + bool fail;
> +
> switch (address_mode) {
> - case LOWPAN_IPHC_SAM_00:
> - /* unspec address ::
> + /* SAM and DAM are the same here */
> + case LOWPAN_IPHC_DAM_00:
> + fail = false;
> + /* SAM_00 -> unspec address ::
> * Do nothing, address is already ::
> + *
> + * DAM 00 -> reserved should never occur.
> */
> break;
> case LOWPAN_IPHC_SAM_01:
> - /* TODO */
> + case LOWPAN_IPHC_DAM_01:
> + fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[8], 8);
> + ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen);
> + break;
> case LOWPAN_IPHC_SAM_10:
> - /* TODO */
> + case LOWPAN_IPHC_DAM_10:
> + ipaddr->s6_addr[11] = 0xFF;
> + ipaddr->s6_addr[12] = 0xFE;
> + fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[14], 2);
> + ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen);
> + break;
> case LOWPAN_IPHC_SAM_11:
> - /* TODO */
> - netdev_warn(skb->dev, "SAM value 0x%x not supported\n",
> - address_mode);
> - return -EINVAL;
> + case LOWPAN_IPHC_DAM_11:
> + fail = false;
> + switch (lowpan_priv(dev)->lltype) {
> + case LOWPAN_LLTYPE_IEEE802154:
> + iphc_uncompress_802154_lladdr(ipaddr, lladdr);
> + break;
> + default:
> + iphc_uncompress_eui64_lladdr(ipaddr, lladdr);
> + break;
> + }
> + ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen);
> + break;
> default:
> pr_debug("Invalid sam value: 0x%x\n", address_mode);
> return -EINVAL;
> }
>
> + if (fail) {
> + pr_debug("Failed to fetch skb data\n");
> + return -EIO;
> + }
> +
> raw_dump_inline(NULL,
> "Reconstructed context based ipv6 src addr is",
> ipaddr->s6_addr, 16);
> @@ -346,6 +477,30 @@ static int
> lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
> return 0;
> }
>
> +static int lowpan_uncompress_multicast_ctx_daddr(struct sk_buff *skb,
> + struct lowpan_iphc_ctx *ctx,
> + struct in6_addr *ipaddr,
> + u8 address_mode)
> +{
> + struct in6_addr network_pfx = {};
> + bool fail;
> +
> + ipaddr->s6_addr[0] = 0xFF;
> + fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 2);
> + fail |= lowpan_fetch_skb(skb, &ipaddr->s6_addr[12], 4);
> + /* take prefix_len and network prefix from the context */
> + ipaddr->s6_addr[3] = ctx->plen;
> + /* get network prefix to copy into multicast address */
> + ipv6_addr_prefix(&network_pfx, &ctx->pfx, ctx->plen);
> + /* setting network prefix */
> + memcpy(&ipaddr->s6_addr[4], &network_pfx, 8);
> +
> + if (fail < 0)
> + return -EIO;
> +
> + return 0;
> +}
> +
> /* get the ecn values from iphc tf format and set it to ipv6hdr */ static inline
> void lowpan_iphc_tf_set_ecn(struct ipv6hdr *hdr, const u8 *tf) { @@ -459,7
> +614,8 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct
> net_device *dev,
> const void *daddr, const void *saddr) {
> struct ipv6hdr hdr = {};
> - u8 iphc0, iphc1;
> + struct lowpan_iphc_ctx *ci;
> + u8 iphc0, iphc1, cid = 0;
> int err;
>
> raw_dump_table(__func__, "raw skb data dump uncompressed", @@
> -469,12 +625,14 @@ int lowpan_header_decompress(struct sk_buff *skb,
> const struct net_device *dev,
> lowpan_fetch_skb(skb, &iphc1, sizeof(iphc1)))
> return -EINVAL;
>
> - /* another if the CID flag is set */
> - if (iphc1 & LOWPAN_IPHC_CID)
> - return -ENOTSUPP;
> -
> hdr.version = 6;
>
> + /* default CID = 0, another if the CID flag is set */
> + if (iphc1 & LOWPAN_IPHC_CID) {
> + if (lowpan_fetch_skb(skb, &cid, sizeof(cid)))
> + return -EINVAL;
> + }
> +
> err = lowpan_iphc_tf_decompress(skb, &hdr,
> iphc0 & LOWPAN_IPHC_TF_MASK);
> if (err < 0)
> @@ -500,10 +658,17 @@ int lowpan_header_decompress(struct sk_buff
> *skb, const struct net_device *dev,
> }
>
> if (iphc1 & LOWPAN_IPHC_SAC) {
> - /* Source address context based uncompression */
> + spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
> + ci = lowpan_iphc_ctx_get_by_id(dev,
> LOWPAN_IPHC_CID_SCI(cid));
> + if (!ci) {
> + spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
> + return -EINVAL;
> + }
> +
> pr_debug("SAC bit is set. Handle context based source
> address.\n");
> - err = uncompress_context_based_src_addr(skb, &hdr.saddr,
> - iphc1 &
> LOWPAN_IPHC_SAM_MASK);
> + err = uncompress_ctx_addr(skb, dev, ci, &hdr.saddr,
> + iphc1 &
> LOWPAN_IPHC_SAM_MASK, saddr);
> + spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
> } else {
> /* Source address uncompression */
> pr_debug("source address stateless compression\n"); @@ -
> 515,27 +680,52 @@ int lowpan_header_decompress(struct sk_buff *skb,
> const struct net_device *dev,
> if (err)
> return -EINVAL;
>
> - /* check for Multicast Compression */
> - if (iphc1 & LOWPAN_IPHC_M) {
> - if (iphc1 & LOWPAN_IPHC_DAC) {
> - pr_debug("dest: context-based mcast
> compression\n");
> - /* TODO: implement this */
> - } else {
> - err = lowpan_uncompress_multicast_daddr(skb,
> &hdr.daddr,
> - iphc1 &
> LOWPAN_IPHC_DAM_MASK);
> + switch (iphc1 & (LOWPAN_IPHC_M | LOWPAN_IPHC_DAC)) {
> + case LOWPAN_IPHC_M | LOWPAN_IPHC_DAC:
> + spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
> + ci = lowpan_iphc_ctx_get_by_id(dev,
> LOWPAN_IPHC_CID_DCI(cid));
> + if (!ci) {
> + spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
> + return -EINVAL;
> + }
>
> - if (err)
> - return -EINVAL;
> + /* multicast with context */
> + pr_debug("dest: context-based mcast compression\n");
> + err = lowpan_uncompress_multicast_ctx_daddr(skb, ci,
> + &hdr.daddr,
> + iphc1 &
> LOWPAN_IPHC_DAM_MASK);
> + spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
> + break;
> + case LOWPAN_IPHC_M:
> + /* multicast */
> + err = lowpan_uncompress_multicast_daddr(skb, &hdr.daddr,
> + iphc1 &
> LOWPAN_IPHC_DAM_MASK);
> + break;
> + case LOWPAN_IPHC_DAC:
> + spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
> + ci = lowpan_iphc_ctx_get_by_id(dev,
> LOWPAN_IPHC_CID_DCI(cid));
> + if (!ci) {
> + spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
> + return -EINVAL;
> }
> - } else {
> +
> + /* Destination address context based uncompression */
> + pr_debug("DAC bit is set. Handle context based destination
> address.\n");
> + err = uncompress_ctx_addr(skb, dev, ci, &hdr.daddr,
> + iphc1 &
> LOWPAN_IPHC_DAM_MASK, daddr);
> + spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
> + break;
> + default:
> err = uncompress_addr(skb, dev, &hdr.daddr,
> iphc1 & LOWPAN_IPHC_DAM_MASK,
> daddr);
> pr_debug("dest: stateless compression mode %d dest
> %pI6c\n",
> iphc1 & LOWPAN_IPHC_DAM_MASK, &hdr.daddr);
> - if (err)
> - return -EINVAL;
> + break;
> }
>
> + if (err)
> + return -EINVAL;
> +
> /* Next header data uncompression */
> if (iphc0 & LOWPAN_IPHC_NH) {
> err = lowpan_nhc_do_uncompression(skb, dev, &hdr); @@ -
> 585,6 +775,58 @@ static const u8 lowpan_iphc_dam_to_sam_value[] = {
> [LOWPAN_IPHC_DAM_11] = LOWPAN_IPHC_SAM_11, };
>
> +static u8 lowpan_compress_ctx_addr(u8 **hc_ptr, const struct in6_addr
> *ipaddr,
> + const struct lowpan_iphc_ctx *ctx,
> + const unsigned char *lladdr, bool sam) {
> + struct in6_addr tmp = {};
> + u8 dam;
> +
> + /* check for SAM/DAM = 11 */
> + memcpy(&tmp.s6_addr[8], lladdr, 8);
> + /* second bit-flip (Universe/Local) is done according RFC2464 */
> + tmp.s6_addr[8] ^= 0x02;
> + /* context information are always used */
> + ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
> + if (ipv6_addr_equal(&tmp, ipaddr)) {
> + dam = LOWPAN_IPHC_DAM_11;
> + goto out;
> + }
> +
> + memset(&tmp, 0, sizeof(tmp));
> + /* check for SAM/DAM = 01 */
> + tmp.s6_addr[11] = 0xFF;
> + tmp.s6_addr[12] = 0xFE;
> + memcpy(&tmp.s6_addr[14], &ipaddr->s6_addr[14], 2);
> + /* context information are always used */
> + ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
> + if (ipv6_addr_equal(&tmp, ipaddr)) {
> + lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[14], 2);
> + dam = LOWPAN_IPHC_DAM_10;
> + goto out;
> + }
> +
> + memset(&tmp, 0, sizeof(tmp));
> + /* check for SAM/DAM = 10, should always match */
> + memcpy(&tmp.s6_addr[8], &ipaddr->s6_addr[8], 8);
> + /* context information are always used */
> + ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
> + if (ipv6_addr_equal(&tmp, ipaddr)) {
> + lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[8], 8);
> + dam = LOWPAN_IPHC_DAM_01;
> + goto out;
> + }
> +
> + WARN_ON_ONCE("context found but no address mode matched\n");
> + return -EINVAL;
> +out:
> +
> + if (sam)
> + return lowpan_iphc_dam_to_sam_value[dam];
> + else
> + return dam;
> +}
> +
> static u8 lowpan_compress_addr_64(u8 **hc_ptr, const struct in6_addr
> *ipaddr,
> const unsigned char *lladdr, bool sam) {
> @@ -708,6 +950,21 @@ static u8 lowpan_iphc_tf_compress(u8 **hc_ptr,
> const struct ipv6hdr *hdr)
> return val;
> }
>
> +static u8 lowpan_iphc_mcast_ctx_addr_compress(u8 **hc_ptr,
> + const struct lowpan_iphc_ctx *ctx,
> + const struct in6_addr *ipaddr) {
> + u8 data[6];
> +
> + /* flags/scope, reserved (RIID) */
> + memcpy(data, &ipaddr->s6_addr[1], 2);
> + /* group ID */
> + memcpy(&data[1], &ipaddr->s6_addr[11], 4);
> + lowpan_push_hc_data(hc_ptr, data, 6);
> +
> + return LOWPAN_IPHC_DAM_00;
> +}
> +
> static u8 lowpan_iphc_mcast_addr_compress(u8 **hc_ptr,
> const struct in6_addr *ipaddr)
> {
> @@ -742,10 +999,11 @@ static u8 lowpan_iphc_mcast_addr_compress(u8
> **hc_ptr, int lowpan_header_compress(struct sk_buff *skb, const struct
> net_device *dev,
> const void *daddr, const void *saddr) {
> - u8 iphc0, iphc1, *hc_ptr;
> + u8 iphc0, iphc1, *hc_ptr, cid = 0;
> struct ipv6hdr *hdr;
> u8 head[LOWPAN_IPHC_MAX_HC_BUF_LEN] = {};
> - int ret, addr_type;
> + struct lowpan_iphc_ctx *dci, *sci, dci_entry, sci_entry;
> + int ret, ipv6_daddr_type, ipv6_saddr_type;
>
> if (skb->protocol != htons(ETH_P_IPV6))
> return -EINVAL;
> @@ -769,14 +1027,38 @@ int lowpan_header_compress(struct sk_buff
> *skb, const struct net_device *dev,
> iphc0 = LOWPAN_DISPATCH_IPHC;
> iphc1 = 0;
>
> - /* TODO: context lookup */
> -
> raw_dump_inline(__func__, "saddr", saddr, EUI64_ADDR_LEN);
> raw_dump_inline(__func__, "daddr", daddr, EUI64_ADDR_LEN);
>
> raw_dump_table(__func__, "sending raw skb network uncompressed
> packet",
> skb->data, skb->len);
>
> + ipv6_daddr_type = ipv6_addr_type(&hdr->daddr);
> + spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
> + if (ipv6_daddr_type & IPV6_ADDR_MULTICAST)
> + dci = lowpan_iphc_ctx_get_by_mcast_addr(dev, &hdr-
> >daddr);
> + else
> + dci = lowpan_iphc_ctx_get_by_addr(dev, &hdr->daddr);
> + if (dci) {
> + memcpy(&dci_entry, dci, sizeof(*dci));
> + cid |= dci->id;
> + }
> + spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
> +
> + spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
> + sci = lowpan_iphc_ctx_get_by_addr(dev, &hdr->saddr);
> + if (sci) {
> + memcpy(&sci_entry, sci, sizeof(*sci));
> + cid |= (sci->id << 4);
> + }
> + spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
> +
> + /* if cid is zero it will be compressed */
> + if (cid) {
> + iphc1 |= LOWPAN_IPHC_CID;
> + lowpan_push_hc_data(&hc_ptr, &cid, sizeof(cid));
> + }
> +
> /* Traffic Class, Flow Label compression */
> iphc0 |= lowpan_iphc_tf_compress(&hc_ptr, hdr);
>
> @@ -813,39 +1095,63 @@ int lowpan_header_compress(struct sk_buff
> *skb, const struct net_device *dev,
> sizeof(hdr->hop_limit));
> }
>
> - addr_type = ipv6_addr_type(&hdr->saddr);
> + ipv6_saddr_type = ipv6_addr_type(&hdr->saddr);
> /* source address compression */
> - if (addr_type == IPV6_ADDR_ANY) {
> + if (ipv6_saddr_type == IPV6_ADDR_ANY) {
> pr_debug("source address is unspecified, setting SAC\n");
> iphc1 |= LOWPAN_IPHC_SAC;
> } else {
> - if (addr_type & IPV6_ADDR_LINKLOCAL) {
> - iphc1 |= lowpan_compress_addr_64(&hc_ptr, &hdr-
> >saddr,
> - saddr, true);
> - pr_debug("source address unicast link-local %pI6c
> iphc1 0x%02x\n",
> - &hdr->saddr, iphc1);
> + if (sci) {
> + iphc1 |= lowpan_compress_ctx_addr(&hc_ptr, &hdr-
> >saddr,
> + &sci_entry, saddr,
> + true);
> + iphc1 |= LOWPAN_IPHC_SAC;
> } else {
> - pr_debug("send the full source address\n");
> - lowpan_push_hc_data(&hc_ptr, hdr->saddr.s6_addr,
> 16);
> + if (ipv6_saddr_type & IPV6_ADDR_LINKLOCAL) {
> + iphc1 |=
> lowpan_compress_addr_64(&hc_ptr,
> + &hdr->saddr,
> + saddr, true);
> + pr_debug("source address unicast link-local
> %pI6c iphc1 0x%02x\n",
> + &hdr->saddr, iphc1);
> + } else {
> + pr_debug("send the full source address\n");
> + lowpan_push_hc_data(&hc_ptr,
> + hdr->saddr.s6_addr, 16);
> + }
> }
> }
>
> - addr_type = ipv6_addr_type(&hdr->daddr);
> /* destination address compression */
> - if (addr_type & IPV6_ADDR_MULTICAST) {
> + if (ipv6_daddr_type & IPV6_ADDR_MULTICAST) {
> pr_debug("destination address is multicast: ");
> - iphc1 |= LOWPAN_IPHC_M;
> - iphc1 |= lowpan_iphc_mcast_addr_compress(&hc_ptr, &hdr-
> >daddr);
> + if (dci) {
> + iphc1 |=
> lowpan_iphc_mcast_ctx_addr_compress(&hc_ptr,
> +
> &dci_entry,
> + &hdr-
> >daddr);
> + } else {
> + iphc1 |= LOWPAN_IPHC_M;
> + iphc1 |=
> lowpan_iphc_mcast_addr_compress(&hc_ptr,
> + &hdr-
> >daddr);
> + }
> } else {
> - if (addr_type & IPV6_ADDR_LINKLOCAL) {
> - /* TODO: context lookup */
> - iphc1 |= lowpan_compress_addr_64(&hc_ptr, &hdr-
> >daddr,
> - daddr, false);
> - pr_debug("dest address unicast link-local %pI6c "
> - "iphc1 0x%02x\n", &hdr->daddr, iphc1);
> + if (dci) {
> + iphc1 |= lowpan_compress_ctx_addr(&hc_ptr, &hdr-
> >daddr,
> + &dci_entry, daddr,
> + false);
> + iphc1 |= LOWPAN_IPHC_DAC;
> } else {
> - pr_debug("dest address unicast %pI6c\n", &hdr-
> >daddr);
> - lowpan_push_hc_data(&hc_ptr, hdr->daddr.s6_addr,
> 16);
> + if (ipv6_daddr_type & IPV6_ADDR_LINKLOCAL) {
> + iphc1 |=
> lowpan_compress_addr_64(&hc_ptr,
> + &hdr->daddr,
> + daddr, false);
> + pr_debug("dest address unicast link-local
> %pI6c iphc1 0x%02x\n",
> + &hdr->daddr, iphc1);
> + } else {
> + pr_debug("dest address unicast %pI6c\n",
> + &hdr->daddr);
> + lowpan_push_hc_data(&hc_ptr,
> + hdr->daddr.s6_addr, 16);
> + }
> }
> }
>
> --
> 2.6.1

First of all great work for your series of patches on 6lowpan improvements and
stateful compression!

I have just done some testing of this patch (without RADVD modifications), and
I can share my experiments using 6LoWPAN over BT-LE by sending simple ICMPv6
messages. Contexts for BTLE device has been added manually.

Experiment 1:

Router: 2001:db8::1/64 BTLE: 2001:db8::211:22FF:FE33:4455/64
CID 1: 2001:db8::/64

Works fine, I see that CID 1 is used for both addresses. Router has 64 bits of
IID inline and BTLE node has 0.

Experiment 2:

Router: 2001:db8::1/64 BTLE: 2001:db8::211:22FF:FE33:4455/64
CID 3: 2001:db8::/64 CID 5: 2001:db8::1/128

Works also fine, I see that both CID 3 and 5 are used, as well as both sides
compress its IID in the best possible way. So the patch appears to work fine on
6LoWPAN over BT-LE.


However, I notice that the folder created in the sys/kernel/debug/6lowpan/ for
my bluetooth network interface is called "bt%d". And I would imagine this
should be "bt0", "bt1", ... and not the template?

Also, I notice that the compression for Flow Control and Traffic Label in IPv6
header has been modified, these fields are no longer compressed in any packets
(0b11 value) that comes from Linux Kernel (e.g. ICMP Echo Request,
Router Advertisement), instead I get three extra bytes (0b01 value).
I would like to understand reason for this modification a little better.

Best regards,
?ukasz Duda

2015-12-14 14:00:53

by Alexander Aring

[permalink] [raw]
Subject: [RFCv4 bluetooth-next 1/2] 6lowpan: iphc: add support for stateful compression

This patch introduce support for IPHC stateful address compression. It
will offer the context table via one debugfs entry.

Example to setup a context id:

A "cat /sys/kernel/debug/6lowpan/lowpan0/ctx_table" will display all
contexts which are available. Example:

ID ipv6-address/prefix-length flags
0 0000:0000:0000:0000:0000:0000:0000:0000/0 0
1 0000:0000:0000:0000:0000:0000:0000:0000/0 0
2 0000:0000:0000:0000:0000:0000:0000:0000/0 0
3 0000:0000:0000:0000:0000:0000:0000:0000/0 0
4 0000:0000:0000:0000:0000:0000:0000:0000/0 0
5 0000:0000:0000:0000:0000:0000:0000:0000/0 0
6 0000:0000:0000:0000:0000:0000:0000:0000/0 0
7 0000:0000:0000:0000:0000:0000:0000:0000/0 0
8 0000:0000:0000:0000:0000:0000:0000:0000/0 0
9 0000:0000:0000:0000:0000:0000:0000:0000/0 0
10 0000:0000:0000:0000:0000:0000:0000:0000/0 0
11 0000:0000:0000:0000:0000:0000:0000:0000/0 0
12 0000:0000:0000:0000:0000:0000:0000:0000/0 0
13 0000:0000:0000:0000:0000:0000:0000:0000/0 0
14 0000:0000:0000:0000:0000:0000:0000:0000/0 0
15 0000:0000:0000:0000:0000:0000:0000:0000/0 0

For setting a context e.g. context id 0, context 2001::, prefix-length
64.

Hint: Simple copy one line and then maniuplate it.

echo "0 2001:0000:0000:0000:0000:0000:0000:0000/64 3" >
/sys/kernel/debug/6lowpan/lowpan0/ctx_table

The flags are currently two:

- BIT(0) - active: entry is added or deleted to the ctx_table.
- BIT(1) - c: compression flag according rfc6775.

On transmit side:

The IPHC code will automatically search for a context which would be
match for the address. Then it will be use the context with the
best compression method. Means the longest prefix which match will be
used.

Example:

2001::/126 vs 2001::/127 - the 2001::/127 can be full compressed if the
last bit of the address which has the prefix 2001::/127 is the same like
the IID from the Encapsulating Header. A context ID can also be a
2001::1/128, which is then a full ipv6 address.

On Receive side:

If there is a context defined (when CID not available then it's the
default context 0) then it will be used, if the header doesn't set
SAC or DAC bit thens, it will be dropped.

Signed-off-by: Alexander Aring <[email protected]>
---
include/net/6lowpan.h | 31 ++++
net/6lowpan/core.c | 6 +-
net/6lowpan/debugfs.c | 97 ++++++++++++
net/6lowpan/iphc.c | 420 +++++++++++++++++++++++++++++++++++++++++++-------
4 files changed, 496 insertions(+), 58 deletions(-)

diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
index 2f6a3f2..db636c8 100644
--- a/include/net/6lowpan.h
+++ b/include/net/6lowpan.h
@@ -75,6 +75,8 @@
#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_CI_TABLE_SIZE (1 << 4)

#define LOWPAN_DISPATCH_IPV6 0x41 /* 01000001 = 65 */
#define LOWPAN_DISPATCH_IPHC 0x60 /* 011xxxxx = ... */
@@ -98,9 +100,38 @@ enum lowpan_lltypes {
LOWPAN_LLTYPE_IEEE802154,
};

+enum lowpan_iphc_ctx_flags {
+ LOWPAN_IPHC_CTX_FLAG_ACTIVE = BIT(0),
+ LOWPAN_IPHC_CTX_FLAG_C = BIT(1),
+};
+
+struct lowpan_iphc_ctx {
+ u8 id;
+ struct in6_addr pfx;
+ u8 plen;
+ u32 flags;
+};
+
+struct lowpan_iphc_ctx_table {
+ spinlock_t lock;
+ const struct lowpan_iphc_ctx_ops *ops;
+ struct lowpan_iphc_ctx table[LOWPAN_IPHC_CI_TABLE_SIZE];
+};
+
+static inline bool lowpan_iphc_ctx_is_active(const struct lowpan_iphc_ctx *ctx)
+{
+ return ctx->flags & LOWPAN_IPHC_CTX_FLAG_ACTIVE;
+}
+
+static inline bool lowpan_iphc_ctx_is_c(const struct lowpan_iphc_ctx *ctx)
+{
+ return ctx->flags & LOWPAN_IPHC_CTX_FLAG_C;
+}
+
struct lowpan_priv {
enum lowpan_lltypes lltype;
struct dentry *iface_debugfs;
+ struct lowpan_iphc_ctx_table ctx;

/* must be last */
u8 priv[0] __aligned(sizeof(void *));
diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
index c7f06f5..772f51c 100644
--- a/net/6lowpan/core.c
+++ b/net/6lowpan/core.c
@@ -20,7 +20,7 @@
int lowpan_register_netdevice(struct net_device *dev,
enum lowpan_lltypes lltype)
{
- int ret;
+ int i, ret;

dev->addr_len = EUI64_ADDR_LEN;
dev->type = ARPHRD_6LOWPAN;
@@ -29,6 +29,10 @@ int lowpan_register_netdevice(struct net_device *dev,

lowpan_priv(dev)->lltype = lltype;

+ spin_lock_init(&lowpan_priv(dev)->ctx.lock);
+ for (i = 0; i < LOWPAN_IPHC_CI_TABLE_SIZE; i++)
+ lowpan_priv(dev)->ctx.table[i].id = i;
+
ret = lowpan_dev_debugfs_init(dev);
if (ret < 0)
return ret;
diff --git a/net/6lowpan/debugfs.c b/net/6lowpan/debugfs.c
index 88eef84..5270fa1 100644
--- a/net/6lowpan/debugfs.c
+++ b/net/6lowpan/debugfs.c
@@ -16,19 +16,116 @@

#include "6lowpan_i.h"

+#define LOWPAN_DEBUGFS_CTX_NUM_ARGS 11
+
static struct dentry *lowpan_debugfs;

+static int lowpan_context_show(struct seq_file *file, void *offset)
+{
+ struct lowpan_iphc_ctx_table *t = file->private;
+ int i;
+
+ seq_printf(file, "%-2s %-43s %s\n", "ID", "ipv6-address/prefix-length",
+ "flags");
+
+ spin_lock_bh(&t->lock);
+ for (i = 0; i < LOWPAN_IPHC_CI_TABLE_SIZE; i++)
+ seq_printf(file,
+ "%-2d %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x/%-3d %x\n",
+ t->table[i].id,
+ be16_to_cpu(t->table[i].pfx.s6_addr16[0]),
+ be16_to_cpu(t->table[i].pfx.s6_addr16[1]),
+ be16_to_cpu(t->table[i].pfx.s6_addr16[2]),
+ be16_to_cpu(t->table[i].pfx.s6_addr16[3]),
+ be16_to_cpu(t->table[i].pfx.s6_addr16[4]),
+ be16_to_cpu(t->table[i].pfx.s6_addr16[5]),
+ be16_to_cpu(t->table[i].pfx.s6_addr16[6]),
+ be16_to_cpu(t->table[i].pfx.s6_addr16[7]),
+ t->table[i].plen, t->table[i].flags);
+ spin_unlock_bh(&t->lock);
+
+ return 0;
+}
+
+static int lowpan_context_dbgfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, lowpan_context_show, inode->i_private);
+}
+
+static ssize_t lowpan_context_dbgfs_write(struct file *fp,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buf[128] = {};
+ struct seq_file *file = fp->private_data;
+ struct lowpan_iphc_ctx_table *t = file->private;
+ struct lowpan_iphc_ctx ctx;
+ int status = count, n, id, i, plen;
+ unsigned int addr[8], flags;
+
+ if (copy_from_user(&buf, user_buf, min_t(size_t, sizeof(buf) - 1,
+ count))) {
+ status = -EFAULT;
+ goto out;
+ }
+
+ n = sscanf(buf, "%d %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x/%d %x",
+ &id, &addr[0], &addr[1], &addr[2], &addr[3], &addr[4],
+ &addr[5], &addr[6], &addr[7], &plen, &flags);
+ if (n != LOWPAN_DEBUGFS_CTX_NUM_ARGS) {
+ status = -EIO;
+ goto out;
+ }
+
+ if (id > LOWPAN_IPHC_CI_TABLE_SIZE - 1 || plen > 128) {
+ status = -EINVAL;
+ goto out;
+ }
+
+ ctx.id = id;
+ ctx.plen = plen;
+ ctx.flags = flags & (LOWPAN_IPHC_CTX_FLAG_ACTIVE |
+ LOWPAN_IPHC_CTX_FLAG_C);
+
+ for (i = 0; i < 8; i++)
+ ctx.pfx.s6_addr16[i] = cpu_to_be16(addr[i] & 0xffff);
+
+ spin_lock_bh(&t->lock);
+ memcpy(&t->table[ctx.id], &ctx, sizeof(ctx));
+ spin_unlock_bh(&t->lock);
+
+out:
+ return status;
+}
+
+const struct file_operations lowpan_context_fops = {
+ .open = lowpan_context_dbgfs_open,
+ .read = seq_read,
+ .write = lowpan_context_dbgfs_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
int lowpan_dev_debugfs_init(struct net_device *dev)
{
struct lowpan_priv *lpriv = lowpan_priv(dev);
+ static struct dentry *dentry;

/* creating the root */
lpriv->iface_debugfs = debugfs_create_dir(dev->name, lowpan_debugfs);
if (!lpriv->iface_debugfs)
goto fail;

+ dentry = debugfs_create_file("ctx_table", 0644, lpriv->iface_debugfs,
+ &lowpan_priv(dev)->ctx,
+ &lowpan_context_fops);
+ if (!dentry)
+ goto remove_root;
+
return 0;

+remove_root:
+ lowpan_dev_debugfs_exit(dev);
fail:
return -EINVAL;
}
diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c
index 346b5c1..909e0e8 100644
--- a/net/6lowpan/iphc.c
+++ b/net/6lowpan/iphc.c
@@ -56,6 +56,7 @@
/* special link-layer handling */
#include <net/mac802154.h>

+#include "6lowpan_i.h"
#include "nhc.h"

/* Values of fields within the IPHC encoding first byte */
@@ -147,6 +148,9 @@
(((a)->s6_addr16[6]) == 0) && \
(((a)->s6_addr[14]) == 0))

+#define LOWPAN_IPHC_CID_DCI(cid) (cid & 0x0f)
+#define LOWPAN_IPHC_CID_SCI(cid) ((cid & 0xf0) >> 4)
+
static inline void iphc_uncompress_eui64_lladdr(struct in6_addr *ipaddr,
const void *lladdr)
{
@@ -195,6 +199,104 @@ static inline void iphc_uncompress_802154_lladdr(struct in6_addr *ipaddr,
}
}

+static struct lowpan_iphc_ctx *
+lowpan_iphc_ctx_get_by_id(const struct net_device *dev, u8 id)
+{
+ struct lowpan_iphc_ctx *ret = &lowpan_priv(dev)->ctx.table[id];
+
+ WARN_ON_ONCE(id > LOWPAN_IPHC_CI_TABLE_SIZE);
+
+ if (!lowpan_iphc_ctx_is_active(ret))
+ return NULL;
+
+ /* Don't need to check the compression flag here. Context SHOULD be used
+ * in decompression case another compressor has not yet received the
+ * updated context information.
+ */
+ return ret;
+}
+
+static struct lowpan_iphc_ctx *
+lowpan_iphc_ctx_get_by_addr(const struct net_device *dev,
+ const struct in6_addr *addr)
+{
+ struct lowpan_iphc_ctx *table = lowpan_priv(dev)->ctx.table;
+ struct lowpan_iphc_ctx *ret = NULL;
+ struct in6_addr addr_pfx;
+ u8 addr_plen;
+ int i;
+
+ for (i = 0; i < LOWPAN_IPHC_CI_TABLE_SIZE; i++) {
+ /* Check if context is valid. A context that is not valid
+ * MUST NOT be used for compression.
+ */
+ if (!lowpan_iphc_ctx_is_active(&table[i]) ||
+ !lowpan_iphc_ctx_is_c(&table[i]))
+ continue;
+
+ ipv6_addr_prefix(&addr_pfx, addr, table[i].plen);
+
+ /* if prefix len < 64, the remaining bits until 64th bit is
+ * zero. Otherwise we use table[i]->plen.
+ */
+ if (table[i].plen < 64)
+ addr_plen = 64;
+ else
+ addr_plen = table[i].plen;
+
+ if (ipv6_prefix_equal(&addr_pfx, &table[i].pfx, addr_plen)) {
+ /* remember first match */
+ if (!ret) {
+ ret = &table[i];
+ continue;
+ }
+
+ /* get the context with longest prefix len */
+ if (table[i].plen > ret->plen)
+ ret = &table[i];
+ }
+ }
+
+ return ret;
+}
+
+static struct lowpan_iphc_ctx *
+lowpan_iphc_ctx_get_by_mcast_addr(const struct net_device *dev,
+ const struct in6_addr *addr)
+{
+ struct lowpan_iphc_ctx *table = lowpan_priv(dev)->ctx.table;
+ struct lowpan_iphc_ctx *ret = NULL;
+ struct in6_addr addr_mcast, network_pfx = {};
+ int i;
+
+ /* init mcast address with */
+ memcpy(&addr_mcast, addr, sizeof(*addr));
+
+ for (i = 0; i < LOWPAN_IPHC_CI_TABLE_SIZE; i++) {
+ /* Check if context is valid. A context that is not valid
+ * MUST NOT be used for compression.
+ */
+ if (!lowpan_iphc_ctx_is_active(&table[i]) ||
+ !lowpan_iphc_ctx_is_c(&table[i]))
+ continue;
+
+ /* setting plen */
+ addr_mcast.s6_addr[3] = table[i].plen;
+ /* get network prefix to copy into multicast address */
+ ipv6_addr_prefix(&network_pfx, &table[i].pfx,
+ table[i].plen);
+ /* setting network prefix */
+ memcpy(&addr_mcast.s6_addr[4], &network_pfx, 8);
+
+ if (ipv6_addr_equal(addr, &addr_mcast)) {
+ ret = &table[i];
+ break;
+ }
+ }
+
+ return ret;
+}
+
/* Uncompress address function for source and
* destination address(non-multicast).
*
@@ -259,30 +361,59 @@ static int uncompress_addr(struct sk_buff *skb, const struct net_device *dev,
/* Uncompress address function for source context
* based address(non-multicast).
*/
-static int uncompress_context_based_src_addr(struct sk_buff *skb,
- struct in6_addr *ipaddr,
- u8 address_mode)
+static int uncompress_ctx_addr(struct sk_buff *skb,
+ const struct net_device *dev,
+ const struct lowpan_iphc_ctx *ctx,
+ struct in6_addr *ipaddr, u8 address_mode,
+ const void *lladdr)
{
+ bool fail;
+
switch (address_mode) {
- case LOWPAN_IPHC_SAM_00:
- /* unspec address ::
+ /* SAM and DAM are the same here */
+ case LOWPAN_IPHC_DAM_00:
+ fail = false;
+ /* SAM_00 -> unspec address ::
* Do nothing, address is already ::
+ *
+ * DAM 00 -> reserved should never occur.
*/
break;
case LOWPAN_IPHC_SAM_01:
- /* TODO */
+ case LOWPAN_IPHC_DAM_01:
+ fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[8], 8);
+ ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen);
+ break;
case LOWPAN_IPHC_SAM_10:
- /* TODO */
+ case LOWPAN_IPHC_DAM_10:
+ ipaddr->s6_addr[11] = 0xFF;
+ ipaddr->s6_addr[12] = 0xFE;
+ fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[14], 2);
+ ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen);
+ break;
case LOWPAN_IPHC_SAM_11:
- /* TODO */
- netdev_warn(skb->dev, "SAM value 0x%x not supported\n",
- address_mode);
- return -EINVAL;
+ case LOWPAN_IPHC_DAM_11:
+ fail = false;
+ switch (lowpan_priv(dev)->lltype) {
+ case LOWPAN_LLTYPE_IEEE802154:
+ iphc_uncompress_802154_lladdr(ipaddr, lladdr);
+ break;
+ default:
+ iphc_uncompress_eui64_lladdr(ipaddr, lladdr);
+ break;
+ }
+ ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen);
+ break;
default:
pr_debug("Invalid sam value: 0x%x\n", address_mode);
return -EINVAL;
}

+ if (fail) {
+ pr_debug("Failed to fetch skb data\n");
+ return -EIO;
+ }
+
raw_dump_inline(NULL,
"Reconstructed context based ipv6 src addr is",
ipaddr->s6_addr, 16);
@@ -346,6 +477,30 @@ static int lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
return 0;
}

+static int lowpan_uncompress_multicast_ctx_daddr(struct sk_buff *skb,
+ struct lowpan_iphc_ctx *ctx,
+ struct in6_addr *ipaddr,
+ u8 address_mode)
+{
+ struct in6_addr network_pfx = {};
+ bool fail;
+
+ ipaddr->s6_addr[0] = 0xFF;
+ fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 2);
+ fail |= lowpan_fetch_skb(skb, &ipaddr->s6_addr[12], 4);
+ /* take prefix_len and network prefix from the context */
+ ipaddr->s6_addr[3] = ctx->plen;
+ /* get network prefix to copy into multicast address */
+ ipv6_addr_prefix(&network_pfx, &ctx->pfx, ctx->plen);
+ /* setting network prefix */
+ memcpy(&ipaddr->s6_addr[4], &network_pfx, 8);
+
+ if (fail < 0)
+ return -EIO;
+
+ return 0;
+}
+
/* get the ecn values from iphc tf format and set it to ipv6hdr */
static inline void lowpan_iphc_tf_set_ecn(struct ipv6hdr *hdr, const u8 *tf)
{
@@ -459,7 +614,8 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
const void *daddr, const void *saddr)
{
struct ipv6hdr hdr = {};
- u8 iphc0, iphc1;
+ struct lowpan_iphc_ctx *ci;
+ u8 iphc0, iphc1, cid = 0;
int err;

raw_dump_table(__func__, "raw skb data dump uncompressed",
@@ -469,12 +625,14 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
lowpan_fetch_skb(skb, &iphc1, sizeof(iphc1)))
return -EINVAL;

- /* another if the CID flag is set */
- if (iphc1 & LOWPAN_IPHC_CID)
- return -ENOTSUPP;
-
hdr.version = 6;

+ /* default CID = 0, another if the CID flag is set */
+ if (iphc1 & LOWPAN_IPHC_CID) {
+ if (lowpan_fetch_skb(skb, &cid, sizeof(cid)))
+ return -EINVAL;
+ }
+
err = lowpan_iphc_tf_decompress(skb, &hdr,
iphc0 & LOWPAN_IPHC_TF_MASK);
if (err < 0)
@@ -500,10 +658,17 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
}

if (iphc1 & LOWPAN_IPHC_SAC) {
- /* Source address context based uncompression */
+ spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
+ ci = lowpan_iphc_ctx_get_by_id(dev, LOWPAN_IPHC_CID_SCI(cid));
+ if (!ci) {
+ spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
+ return -EINVAL;
+ }
+
pr_debug("SAC bit is set. Handle context based source address.\n");
- err = uncompress_context_based_src_addr(skb, &hdr.saddr,
- iphc1 & LOWPAN_IPHC_SAM_MASK);
+ err = uncompress_ctx_addr(skb, dev, ci, &hdr.saddr,
+ iphc1 & LOWPAN_IPHC_SAM_MASK, saddr);
+ spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
} else {
/* Source address uncompression */
pr_debug("source address stateless compression\n");
@@ -515,27 +680,52 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
if (err)
return -EINVAL;

- /* check for Multicast Compression */
- if (iphc1 & LOWPAN_IPHC_M) {
- if (iphc1 & LOWPAN_IPHC_DAC) {
- pr_debug("dest: context-based mcast compression\n");
- /* TODO: implement this */
- } else {
- err = lowpan_uncompress_multicast_daddr(skb, &hdr.daddr,
- iphc1 & LOWPAN_IPHC_DAM_MASK);
+ switch (iphc1 & (LOWPAN_IPHC_M | LOWPAN_IPHC_DAC)) {
+ case LOWPAN_IPHC_M | LOWPAN_IPHC_DAC:
+ spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
+ ci = lowpan_iphc_ctx_get_by_id(dev, LOWPAN_IPHC_CID_DCI(cid));
+ if (!ci) {
+ spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
+ return -EINVAL;
+ }

- if (err)
- return -EINVAL;
+ /* multicast with context */
+ pr_debug("dest: context-based mcast compression\n");
+ err = lowpan_uncompress_multicast_ctx_daddr(skb, ci,
+ &hdr.daddr,
+ iphc1 & LOWPAN_IPHC_DAM_MASK);
+ spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
+ break;
+ case LOWPAN_IPHC_M:
+ /* multicast */
+ err = lowpan_uncompress_multicast_daddr(skb, &hdr.daddr,
+ iphc1 & LOWPAN_IPHC_DAM_MASK);
+ break;
+ case LOWPAN_IPHC_DAC:
+ spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
+ ci = lowpan_iphc_ctx_get_by_id(dev, LOWPAN_IPHC_CID_DCI(cid));
+ if (!ci) {
+ spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
+ return -EINVAL;
}
- } else {
+
+ /* Destination address context based uncompression */
+ pr_debug("DAC bit is set. Handle context based destination address.\n");
+ err = uncompress_ctx_addr(skb, dev, ci, &hdr.daddr,
+ iphc1 & LOWPAN_IPHC_DAM_MASK, daddr);
+ spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
+ break;
+ default:
err = uncompress_addr(skb, dev, &hdr.daddr,
iphc1 & LOWPAN_IPHC_DAM_MASK, daddr);
pr_debug("dest: stateless compression mode %d dest %pI6c\n",
iphc1 & LOWPAN_IPHC_DAM_MASK, &hdr.daddr);
- if (err)
- return -EINVAL;
+ break;
}

+ if (err)
+ return -EINVAL;
+
/* Next header data uncompression */
if (iphc0 & LOWPAN_IPHC_NH) {
err = lowpan_nhc_do_uncompression(skb, dev, &hdr);
@@ -585,6 +775,58 @@ static const u8 lowpan_iphc_dam_to_sam_value[] = {
[LOWPAN_IPHC_DAM_11] = LOWPAN_IPHC_SAM_11,
};

+static u8 lowpan_compress_ctx_addr(u8 **hc_ptr, const struct in6_addr *ipaddr,
+ const struct lowpan_iphc_ctx *ctx,
+ const unsigned char *lladdr, bool sam)
+{
+ struct in6_addr tmp = {};
+ u8 dam;
+
+ /* check for SAM/DAM = 11 */
+ memcpy(&tmp.s6_addr[8], lladdr, 8);
+ /* second bit-flip (Universe/Local) is done according RFC2464 */
+ tmp.s6_addr[8] ^= 0x02;
+ /* context information are always used */
+ ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
+ if (ipv6_addr_equal(&tmp, ipaddr)) {
+ dam = LOWPAN_IPHC_DAM_11;
+ goto out;
+ }
+
+ memset(&tmp, 0, sizeof(tmp));
+ /* check for SAM/DAM = 01 */
+ tmp.s6_addr[11] = 0xFF;
+ tmp.s6_addr[12] = 0xFE;
+ memcpy(&tmp.s6_addr[14], &ipaddr->s6_addr[14], 2);
+ /* context information are always used */
+ ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
+ if (ipv6_addr_equal(&tmp, ipaddr)) {
+ lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[14], 2);
+ dam = LOWPAN_IPHC_DAM_10;
+ goto out;
+ }
+
+ memset(&tmp, 0, sizeof(tmp));
+ /* check for SAM/DAM = 10, should always match */
+ memcpy(&tmp.s6_addr[8], &ipaddr->s6_addr[8], 8);
+ /* context information are always used */
+ ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
+ if (ipv6_addr_equal(&tmp, ipaddr)) {
+ lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[8], 8);
+ dam = LOWPAN_IPHC_DAM_01;
+ goto out;
+ }
+
+ WARN_ON_ONCE("context found but no address mode matched\n");
+ return -EINVAL;
+out:
+
+ if (sam)
+ return lowpan_iphc_dam_to_sam_value[dam];
+ else
+ return dam;
+}
+
static u8 lowpan_compress_addr_64(u8 **hc_ptr, const struct in6_addr *ipaddr,
const unsigned char *lladdr, bool sam)
{
@@ -708,6 +950,21 @@ static u8 lowpan_iphc_tf_compress(u8 **hc_ptr, const struct ipv6hdr *hdr)
return val;
}

+static u8 lowpan_iphc_mcast_ctx_addr_compress(u8 **hc_ptr,
+ const struct lowpan_iphc_ctx *ctx,
+ const struct in6_addr *ipaddr)
+{
+ u8 data[6];
+
+ /* flags/scope, reserved (RIID) */
+ memcpy(data, &ipaddr->s6_addr[1], 2);
+ /* group ID */
+ memcpy(&data[1], &ipaddr->s6_addr[11], 4);
+ lowpan_push_hc_data(hc_ptr, data, 6);
+
+ return LOWPAN_IPHC_DAM_00;
+}
+
static u8 lowpan_iphc_mcast_addr_compress(u8 **hc_ptr,
const struct in6_addr *ipaddr)
{
@@ -742,10 +999,11 @@ static u8 lowpan_iphc_mcast_addr_compress(u8 **hc_ptr,
int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
const void *daddr, const void *saddr)
{
- u8 iphc0, iphc1, *hc_ptr;
+ u8 iphc0, iphc1, *hc_ptr, cid = 0;
struct ipv6hdr *hdr;
u8 head[LOWPAN_IPHC_MAX_HC_BUF_LEN] = {};
- int ret, addr_type;
+ struct lowpan_iphc_ctx *dci, *sci, dci_entry, sci_entry;
+ int ret, ipv6_daddr_type, ipv6_saddr_type;

if (skb->protocol != htons(ETH_P_IPV6))
return -EINVAL;
@@ -769,14 +1027,38 @@ int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
iphc0 = LOWPAN_DISPATCH_IPHC;
iphc1 = 0;

- /* TODO: context lookup */
-
raw_dump_inline(__func__, "saddr", saddr, EUI64_ADDR_LEN);
raw_dump_inline(__func__, "daddr", daddr, EUI64_ADDR_LEN);

raw_dump_table(__func__, "sending raw skb network uncompressed packet",
skb->data, skb->len);

+ ipv6_daddr_type = ipv6_addr_type(&hdr->daddr);
+ spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
+ if (ipv6_daddr_type & IPV6_ADDR_MULTICAST)
+ dci = lowpan_iphc_ctx_get_by_mcast_addr(dev, &hdr->daddr);
+ else
+ dci = lowpan_iphc_ctx_get_by_addr(dev, &hdr->daddr);
+ if (dci) {
+ memcpy(&dci_entry, dci, sizeof(*dci));
+ cid |= dci->id;
+ }
+ spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
+
+ spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
+ sci = lowpan_iphc_ctx_get_by_addr(dev, &hdr->saddr);
+ if (sci) {
+ memcpy(&sci_entry, sci, sizeof(*sci));
+ cid |= (sci->id << 4);
+ }
+ spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
+
+ /* if cid is zero it will be compressed */
+ if (cid) {
+ iphc1 |= LOWPAN_IPHC_CID;
+ lowpan_push_hc_data(&hc_ptr, &cid, sizeof(cid));
+ }
+
/* Traffic Class, Flow Label compression */
iphc0 |= lowpan_iphc_tf_compress(&hc_ptr, hdr);

@@ -813,39 +1095,63 @@ int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
sizeof(hdr->hop_limit));
}

- addr_type = ipv6_addr_type(&hdr->saddr);
+ ipv6_saddr_type = ipv6_addr_type(&hdr->saddr);
/* source address compression */
- if (addr_type == IPV6_ADDR_ANY) {
+ if (ipv6_saddr_type == IPV6_ADDR_ANY) {
pr_debug("source address is unspecified, setting SAC\n");
iphc1 |= LOWPAN_IPHC_SAC;
} else {
- if (addr_type & IPV6_ADDR_LINKLOCAL) {
- iphc1 |= lowpan_compress_addr_64(&hc_ptr, &hdr->saddr,
- saddr, true);
- pr_debug("source address unicast link-local %pI6c iphc1 0x%02x\n",
- &hdr->saddr, iphc1);
+ if (sci) {
+ iphc1 |= lowpan_compress_ctx_addr(&hc_ptr, &hdr->saddr,
+ &sci_entry, saddr,
+ true);
+ iphc1 |= LOWPAN_IPHC_SAC;
} else {
- pr_debug("send the full source address\n");
- lowpan_push_hc_data(&hc_ptr, hdr->saddr.s6_addr, 16);
+ if (ipv6_saddr_type & IPV6_ADDR_LINKLOCAL) {
+ iphc1 |= lowpan_compress_addr_64(&hc_ptr,
+ &hdr->saddr,
+ saddr, true);
+ pr_debug("source address unicast link-local %pI6c iphc1 0x%02x\n",
+ &hdr->saddr, iphc1);
+ } else {
+ pr_debug("send the full source address\n");
+ lowpan_push_hc_data(&hc_ptr,
+ hdr->saddr.s6_addr, 16);
+ }
}
}

- addr_type = ipv6_addr_type(&hdr->daddr);
/* destination address compression */
- if (addr_type & IPV6_ADDR_MULTICAST) {
+ if (ipv6_daddr_type & IPV6_ADDR_MULTICAST) {
pr_debug("destination address is multicast: ");
- iphc1 |= LOWPAN_IPHC_M;
- iphc1 |= lowpan_iphc_mcast_addr_compress(&hc_ptr, &hdr->daddr);
+ if (dci) {
+ iphc1 |= lowpan_iphc_mcast_ctx_addr_compress(&hc_ptr,
+ &dci_entry,
+ &hdr->daddr);
+ } else {
+ iphc1 |= LOWPAN_IPHC_M;
+ iphc1 |= lowpan_iphc_mcast_addr_compress(&hc_ptr,
+ &hdr->daddr);
+ }
} else {
- if (addr_type & IPV6_ADDR_LINKLOCAL) {
- /* TODO: context lookup */
- iphc1 |= lowpan_compress_addr_64(&hc_ptr, &hdr->daddr,
- daddr, false);
- pr_debug("dest address unicast link-local %pI6c "
- "iphc1 0x%02x\n", &hdr->daddr, iphc1);
+ if (dci) {
+ iphc1 |= lowpan_compress_ctx_addr(&hc_ptr, &hdr->daddr,
+ &dci_entry, daddr,
+ false);
+ iphc1 |= LOWPAN_IPHC_DAC;
} else {
- pr_debug("dest address unicast %pI6c\n", &hdr->daddr);
- lowpan_push_hc_data(&hc_ptr, hdr->daddr.s6_addr, 16);
+ if (ipv6_daddr_type & IPV6_ADDR_LINKLOCAL) {
+ iphc1 |= lowpan_compress_addr_64(&hc_ptr,
+ &hdr->daddr,
+ daddr, false);
+ pr_debug("dest address unicast link-local %pI6c iphc1 0x%02x\n",
+ &hdr->daddr, iphc1);
+ } else {
+ pr_debug("dest address unicast %pI6c\n",
+ &hdr->daddr);
+ lowpan_push_hc_data(&hc_ptr,
+ hdr->daddr.s6_addr, 16);
+ }
}
}

--
2.6.1


2015-12-14 14:00:54

by Alexander Aring

[permalink] [raw]
Subject: [RFCv4 bluetooth-next 2/2] ipv6: add 6co as icmpv6 userspace option

This patch adds the 6LoWPAN Context Option (6CO) as userspace option to
processing such options inside RA messages in userspace.

Cc: David S. Miller <[email protected]>
Cc: Alexey Kuznetsov <[email protected]>
Cc: James Morris <[email protected]>
Cc: Hideaki YOSHIFUJI <[email protected]>
Cc: Patrick McHardy <[email protected]>
Signed-off-by: Alexander Aring <[email protected]>
---
include/net/ndisc.h | 1 +
net/ipv6/ndisc.c | 3 ++-
2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index 2d8edaa..944258d 100644
--- a/include/net/ndisc.h
+++ b/include/net/ndisc.h
@@ -35,6 +35,7 @@ enum {
ND_OPT_ROUTE_INFO = 24, /* RFC4191 */
ND_OPT_RDNSS = 25, /* RFC5006 */
ND_OPT_DNSSL = 31, /* RFC6106 */
+ ND_OPT_6CO = 34, /* RFC6775 */
__ND_OPT_MAX
};

diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index d6161e1..bed154e 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -188,7 +188,8 @@ static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
static inline int ndisc_is_useropt(struct nd_opt_hdr *opt)
{
return opt->nd_opt_type == ND_OPT_RDNSS ||
- opt->nd_opt_type == ND_OPT_DNSSL;
+ opt->nd_opt_type == ND_OPT_DNSSL ||
+ opt->nd_opt_type == ND_OPT_6CO;
}

static struct nd_opt_hdr *ndisc_next_useropt(struct nd_opt_hdr *cur,
--
2.6.1