2016-04-20 08:19:32

by Alexander Aring

[permalink] [raw]
Subject: [PATCHv2 bluetooth-next 00/10] 6lowpan: introduce basic 6lowpan-nd

Hi,

this patch series introduces a layer for IPv6 neighbour discovery. At first
it introduce the "ndisc_ops" to run a different handling for recv/send of
NA/NS messages. The use case for such ndisc operation is RFC 6775 [0].
Which describes a different neighbour discovery handling for 6LoWPAN networks.

I didn't implement RFC 6775 in this patch series, but introduce callback
structure for replace different functions in ndisc implementation might be
the right direction.

Another use case would be RFC 7400 [1] which describes a new option field to
getting capabilities of 6LoWPAN next header compression methods.

What I implemented is a necessary functionality to handle short address for
802.15.4 6LoWPAN networks. The L2-Layer "802.15.4" can have two different
link-layer addresses which can be used mixed at the same time inside 802.15.4
networks. To deal with such behaviour in ndisc, it is defined at RFC 4944 [2].
The bad news is, that I saw different handling of such handling. What Linux
will do is to add two source/target address information option fields, each
with different length, if short address is valid (can also not be given).
Example:

- WPAN interface address settings
- extended addr (must always be there)
- short addr (0xfffe or 0xffff -> invalid)

Will add an extended addr to source/target address information option field.
If short addr is in some valid range, then both address will be added to
the option fields. Indicated are these different address types by the length
field (extended -> length=2, short -> length=1), according to [1].

The tested 6LoWPAN implementation (RIOT-OS) allows only one source/target
option field which is short XOR extended, otherwise it will be dropped.
There is some lack of information there [2] and I don't know how do deal with
it right, maybe we need to update the implementation there if it's really
wrong.

To save such information for each neighbour we use the already implemented
neighbour private data which some casting strategy for 6LoWPAN and 6LoWPAN
link-layer specific data e.g. 802.15.4 short address handling.

Additional I implemented to add 6CO to the is_useropt callback in case of
6LoWPAN interface. The 6CO option will currently parsed in userspace which
are placed in RA-Messages.

The ndisc_ops are not finished yet, of course we need handling for RS messages
to place the 802.15.4 short address there as well and then also processing
of RA messages for the 802.15.4 SLLAO option field.

- Alex

[0] https://tools.ietf.org/html/rfc6775
[1] https://tools.ietf.org/html/rfc7400#section-3.3
[2] https://tools.ietf.org/html/rfc4944#section-8

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]>

changes since v2:
- replace #ifdef CONFIG_IPV6 to #if IS_ENABLED(...)
- replace #ifdef CONFIG_IEEE802154... to #if IS_ENABLED(...)
- add more #if IS_ENABLED(CONFIG_IPV6) in ndisc.h

Alexander Aring (10):
6lowpan: add private neighbour data
6lowpan: add 802.15.4 short addr slaac
6lowpan: remove ipv6 module request
ndisc: add addr_len parameter to ndisc_opt_addr_space
ndisc: add addr_len parameter to ndisc_opt_addr_data
ndisc: add addr_len parameter to ndisc_fill_addr_option
ipv6: introduce neighbour discovery ops
ipv6: export ndisc functions
6lowpan: introduce 6lowpan-nd
6lowpan: add support for 802.15.4 short addr handling

include/linux/netdevice.h | 6 +-
include/net/6lowpan.h | 24 ++
include/net/addrconf.h | 3 +
include/net/ndisc.h | 124 ++++++++-
net/6lowpan/6lowpan_i.h | 2 +
net/6lowpan/Makefile | 2 +-
net/6lowpan/core.c | 50 +++-
net/6lowpan/iphc.c | 167 +++++++++--
net/6lowpan/ndisc.c | 633 ++++++++++++++++++++++++++++++++++++++++++
net/bluetooth/6lowpan.c | 2 +
net/ieee802154/6lowpan/core.c | 12 +
net/ieee802154/6lowpan/tx.c | 107 ++++---
net/ipv6/addrconf.c | 7 +-
net/ipv6/ndisc.c | 132 +++++----
net/ipv6/route.c | 4 +-
15 files changed, 1117 insertions(+), 158 deletions(-)
create mode 100644 net/6lowpan/ndisc.c

--
2.8.0


2016-04-28 11:36:14

by Alexander Aring

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 00/10] 6lowpan: introduce basic 6lowpan-nd


Hi,

can I get your acks for this patch serie?

It's okay that these patches goes through bluetooth-next?

- Alex

2016-04-20 08:19:42

by Alexander Aring

[permalink] [raw]
Subject: [PATCHv2 bluetooth-next 10/10] 6lowpan: add support for 802.15.4 short addr handling

This patch adds necessary handling for use the short address for
802.15.4 6lowpan. It contains support for IPHC address compression
and new matching algorithmn to decide which link layer address will be
used for 802.15.4 frame.

Signed-off-by: Alexander Aring <[email protected]>
---
net/6lowpan/iphc.c | 167 ++++++++++++++++++++++++++++++++++++--------
net/ieee802154/6lowpan/tx.c | 107 ++++++++++++++--------------
2 files changed, 189 insertions(+), 85 deletions(-)

diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c
index 8501dd5..aca38dc 100644
--- a/net/6lowpan/iphc.c
+++ b/net/6lowpan/iphc.c
@@ -761,22 +761,75 @@ 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,
+static inline bool
+lowpan_iphc_compress_ctx_802154_lladdr(const struct in6_addr *ipaddr,
+ const struct lowpan_iphc_ctx *ctx,
+ const void *lladdr)
+{
+ const struct ieee802154_addr *addr = lladdr;
+ unsigned char extended_addr[EUI64_ADDR_LEN];
+ struct in6_addr tmp = {};
+ bool lladdr_compress = false;
+
+ switch (addr->mode) {
+ case IEEE802154_ADDR_LONG:
+ ieee802154_le64_to_be64(&extended_addr, &addr->extended_addr);
+ /* check for SAM/DAM = 11 */
+ memcpy(&tmp.s6_addr[8], &extended_addr, EUI64_ADDR_LEN);
+ /* 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))
+ lladdr_compress = true;
+ break;
+ case IEEE802154_ADDR_SHORT:
+ tmp.s6_addr[11] = 0xFF;
+ tmp.s6_addr[12] = 0xFE;
+ ieee802154_le16_to_be16(&tmp.s6_addr16[7],
+ &addr->short_addr);
+ /* context information are always used */
+ ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
+ if (ipv6_addr_equal(&tmp, ipaddr))
+ lladdr_compress = true;
+ break;
+ default:
+ /* should never handled and filtered by 802154 6lowpan */
+ WARN_ON_ONCE(1);
+ break;
+ }
+
+ return lladdr_compress;
+}
+
+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,
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;
+ switch (lowpan_dev(dev)->lltype) {
+ case LOWPAN_LLTYPE_IEEE802154:
+ if (lowpan_iphc_compress_ctx_802154_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);
+ /* 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;
+ }
+ break;
}

memset(&tmp, 0, sizeof(tmp));
@@ -813,28 +866,85 @@ out:
return dam;
}

-static u8 lowpan_compress_addr_64(u8 **hc_ptr, const struct in6_addr *ipaddr,
+static inline bool
+lowpan_iphc_compress_802154_lladdr(const struct in6_addr *ipaddr,
+ const void *lladdr)
+{
+ const struct ieee802154_addr *addr = lladdr;
+ unsigned char extended_addr[EUI64_ADDR_LEN];
+ struct in6_addr tmp = {};
+ bool lladdr_compress = false;
+
+ switch (addr->mode) {
+ case IEEE802154_ADDR_LONG:
+ ieee802154_le64_to_be64(&extended_addr, &addr->extended_addr);
+ if (is_addr_mac_addr_based(ipaddr, extended_addr))
+ lladdr_compress = true;
+ break;
+ case IEEE802154_ADDR_SHORT:
+ /* fe:80::ff:fe00:XXXX
+ * \__/
+ * short_addr
+ *
+ * Universe/Local bit is zero.
+ */
+ tmp.s6_addr[0] = 0xFE;
+ tmp.s6_addr[1] = 0x80;
+ tmp.s6_addr[11] = 0xFF;
+ tmp.s6_addr[12] = 0xFE;
+ ieee802154_le16_to_be16(&tmp.s6_addr16[7],
+ &addr->short_addr);
+ if (ipv6_addr_equal(&tmp, ipaddr))
+ lladdr_compress = true;
+ break;
+ default:
+ /* should never handled and filtered by 802154 6lowpan */
+ WARN_ON_ONCE(1);
+ break;
+ }
+
+ return lladdr_compress;
+}
+
+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_00;
+ u8 dam = LOWPAN_IPHC_DAM_01;

- if (is_addr_mac_addr_based(ipaddr, lladdr)) {
- dam = LOWPAN_IPHC_DAM_11; /* 0-bits */
- pr_debug("address compression 0 bits\n");
- } else if (lowpan_is_iid_16_bit_compressable(ipaddr)) {
+ switch (lowpan_dev(dev)->lltype) {
+ case LOWPAN_LLTYPE_IEEE802154:
+ if (lowpan_iphc_compress_802154_lladdr(ipaddr, lladdr)) {
+ dam = LOWPAN_IPHC_DAM_11; /* 0-bits */
+ pr_debug("address compression 0 bits\n");
+ goto out;
+ }
+ break;
+ default:
+ if (is_addr_mac_addr_based(ipaddr, lladdr)) {
+ dam = LOWPAN_IPHC_DAM_11; /* 0-bits */
+ pr_debug("address compression 0 bits\n");
+ goto out;
+ }
+ break;
+ }
+
+ if (lowpan_is_iid_16_bit_compressable(ipaddr)) {
/* compress IID to 16 bits xxxx::XXXX */
lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr16[7], 2);
dam = LOWPAN_IPHC_DAM_10; /* 16-bits */
raw_dump_inline(NULL, "Compressed ipv6 addr is (16 bits)",
*hc_ptr - 2, 2);
- } else {
- /* do not compress IID => xxxx::IID */
- lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr16[4], 8);
- dam = LOWPAN_IPHC_DAM_01; /* 64-bits */
- raw_dump_inline(NULL, "Compressed ipv6 addr is (64 bits)",
- *hc_ptr - 8, 8);
+ goto out;
}

+ /* do not compress IID => xxxx::IID */
+ lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr16[4], 8);
+ raw_dump_inline(NULL, "Compressed ipv6 addr is (64 bits)",
+ *hc_ptr - 8, 8);
+
+out:
+
if (sam)
return lowpan_iphc_dam_to_sam_value[dam];
else
@@ -1013,9 +1123,6 @@ int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
iphc0 = LOWPAN_DISPATCH_IPHC;
iphc1 = 0;

- 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);

@@ -1088,14 +1195,15 @@ int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
iphc1 |= LOWPAN_IPHC_SAC;
} else {
if (sci) {
- iphc1 |= lowpan_compress_ctx_addr(&hc_ptr, &hdr->saddr,
+ iphc1 |= lowpan_compress_ctx_addr(&hc_ptr, dev,
+ &hdr->saddr,
&sci_entry, saddr,
true);
iphc1 |= LOWPAN_IPHC_SAC;
} else {
if (ipv6_saddr_type & IPV6_ADDR_LINKLOCAL &&
lowpan_is_linklocal_zero_padded(hdr->saddr)) {
- iphc1 |= lowpan_compress_addr_64(&hc_ptr,
+ iphc1 |= lowpan_compress_addr_64(&hc_ptr, dev,
&hdr->saddr,
saddr, true);
pr_debug("source address unicast link-local %pI6c iphc1 0x%02x\n",
@@ -1123,14 +1231,15 @@ int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
}
} else {
if (dci) {
- iphc1 |= lowpan_compress_ctx_addr(&hc_ptr, &hdr->daddr,
+ iphc1 |= lowpan_compress_ctx_addr(&hc_ptr, dev,
+ &hdr->daddr,
&dci_entry, daddr,
false);
iphc1 |= LOWPAN_IPHC_DAC;
} else {
if (ipv6_daddr_type & IPV6_ADDR_LINKLOCAL &&
lowpan_is_linklocal_zero_padded(hdr->daddr)) {
- iphc1 |= lowpan_compress_addr_64(&hc_ptr,
+ iphc1 |= lowpan_compress_addr_64(&hc_ptr, dev,
&hdr->daddr,
daddr, false);
pr_debug("dest address unicast link-local %pI6c iphc1 0x%02x\n",
diff --git a/net/ieee802154/6lowpan/tx.c b/net/ieee802154/6lowpan/tx.c
index e459afd..88c9d16 100644
--- a/net/ieee802154/6lowpan/tx.c
+++ b/net/ieee802154/6lowpan/tx.c
@@ -9,6 +9,7 @@
*/

#include <net/6lowpan.h>
+#include <net/ndisc.h>
#include <net/ieee802154_netdev.h>
#include <net/mac802154.h>

@@ -17,19 +18,9 @@
#define LOWPAN_FRAG1_HEAD_SIZE 0x4
#define LOWPAN_FRAGN_HEAD_SIZE 0x5

-/* don't save pan id, it's intra pan */
-struct lowpan_addr {
- u8 mode;
- union {
- /* IPv6 needs big endian here */
- __be64 extended_addr;
- __be16 short_addr;
- } u;
-};
-
struct lowpan_addr_info {
- struct lowpan_addr daddr;
- struct lowpan_addr saddr;
+ struct ieee802154_addr daddr;
+ struct ieee802154_addr saddr;
};

static inline struct
@@ -48,12 +39,14 @@ lowpan_addr_info *lowpan_skb_priv(const struct sk_buff *skb)
* 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)
+ unsigned short type, const void *daddr,
+ const void *saddr, unsigned int len)
{
- const u8 *saddr = _saddr;
- const u8 *daddr = _daddr;
- struct lowpan_addr_info *info;
+ struct wpan_dev *wpan_dev = lowpan_802154_dev(ldev)->wdev->ieee802154_ptr;
+ struct lowpan_addr_info *info = lowpan_skb_priv(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?
@@ -61,21 +54,44 @@ int lowpan_header_create(struct sk_buff *skb, struct net_device *ldev,
if (type != ETH_P_IPV6)
return 0;

- if (!saddr)
- saddr = ldev->dev_addr;
+ /* intra-pan communication */
+ info->saddr.pan_id = wpan_dev->pan_id;
+ info->daddr.pan_id = info->saddr.pan_id;

- raw_dump_inline(__func__, "saddr", (unsigned char *)saddr, 8);
- raw_dump_inline(__func__, "daddr", (unsigned char *)daddr, 8);
+ if (!memcmp(daddr, ldev->broadcast, EUI64_ADDR_LEN)) {
+ info->daddr.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
+ info->daddr.mode = IEEE802154_ADDR_SHORT;
+ } else {
+ n = neigh_lookup(&nd_tbl, &hdr->daddr, ldev);
+ if (n)
+ llneigh = lowpan_802154_neigh(neighbour_priv(n));
+
+ if (llneigh &&
+ ieee802154_is_valid_src_short_addr(llneigh->short_addr)) {
+ info->daddr.mode = IEEE802154_ADDR_SHORT;
+ info->daddr.short_addr = llneigh->short_addr;
+ } else {
+ info->daddr.mode = IEEE802154_ADDR_LONG;
+ ieee802154_be64_to_le64(&info->daddr.extended_addr,
+ daddr);
+ }

- info = lowpan_skb_priv(skb);
+ if (n)
+ neigh_release(n);
+ }

- /* TODO: Currently we only support extended_addr */
- info->daddr.mode = IEEE802154_ADDR_LONG;
- memcpy(&info->daddr.u.extended_addr, daddr,
- sizeof(info->daddr.u.extended_addr));
- info->saddr.mode = IEEE802154_ADDR_LONG;
- memcpy(&info->saddr.u.extended_addr, saddr,
- sizeof(info->daddr.u.extended_addr));
+ if (!saddr) {
+ if (ieee802154_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;
+ }
+ } else {
+ info->saddr.mode = IEEE802154_ADDR_LONG;
+ ieee802154_be64_to_le64(&info->saddr.extended_addr, saddr);
+ }

return 0;
}
@@ -209,47 +225,26 @@ static int lowpan_header(struct sk_buff *skb, struct net_device *ldev,
u16 *dgram_size, u16 *dgram_offset)
{
struct wpan_dev *wpan_dev = lowpan_802154_dev(ldev)->wdev->ieee802154_ptr;
- struct ieee802154_addr sa, da;
struct ieee802154_mac_cb *cb = mac_cb_init(skb);
struct lowpan_addr_info info;
- void *daddr, *saddr;

memcpy(&info, lowpan_skb_priv(skb), sizeof(info));

- /* TODO: Currently we only support extended_addr */
- daddr = &info.daddr.u.extended_addr;
- saddr = &info.saddr.u.extended_addr;
-
*dgram_size = skb->len;
- lowpan_header_compress(skb, ldev, daddr, saddr);
+ lowpan_header_compress(skb, ldev, &info.daddr, &info.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;

- /* prepare wpan address data */
- sa.mode = IEEE802154_ADDR_LONG;
- sa.pan_id = wpan_dev->pan_id;
- sa.extended_addr = ieee802154_devaddr_from_raw(saddr);
-
- /* intra-PAN communications */
- da.pan_id = sa.pan_id;
-
- /* if the destination address is the broadcast address, use the
- * corresponding short address
- */
- if (!memcmp(daddr, ldev->broadcast, EUI64_ADDR_LEN)) {
- da.mode = IEEE802154_ADDR_SHORT;
- da.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
+ if (info.daddr.mode == IEEE802154_ADDR_SHORT &&
+ ieee802154_is_broadcast_short_addr(info.daddr.short_addr))
cb->ackreq = false;
- } else {
- da.mode = IEEE802154_ADDR_LONG;
- da.extended_addr = ieee802154_devaddr_from_raw(daddr);
+ else
cb->ackreq = wpan_dev->ackreq;
- }

- return wpan_dev_hard_header(skb, lowpan_802154_dev(ldev)->wdev, &da,
- &sa, 0);
+ return wpan_dev_hard_header(skb, lowpan_802154_dev(ldev)->wdev,
+ &info.daddr, &info.saddr, 0);
}

netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *ldev)
--
2.8.0

2016-04-20 08:19:41

by Alexander Aring

[permalink] [raw]
Subject: [PATCHv2 bluetooth-next 09/10] 6lowpan: introduce 6lowpan-nd

This patch introduce different 6lowpan handling for receive and transmit
NS/NA messages for the ipv6 neighbour discovery. The first use-case is
for supporting 802.15.4 short addresses inside the option fields and
handling for RFC6775 6CO option field as userspace option.

Future handling:
Also add RS/RA(processing) for 802.15.4 short addresses and handle
RFC6775, which requires more 6lowpan specific handling for ipv6 neighbour
discovery implementation.

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/6lowpan/6lowpan_i.h | 2 +
net/6lowpan/Makefile | 2 +-
net/6lowpan/core.c | 2 +
net/6lowpan/ndisc.c | 633 ++++++++++++++++++++++++++++++++++++++++++++++++
net/ipv6/ndisc.c | 3 +
6 files changed, 642 insertions(+), 1 deletion(-)
create mode 100644 net/6lowpan/ndisc.c

diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index 35a4396..e2ee83d 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/6lowpan/6lowpan_i.h b/net/6lowpan/6lowpan_i.h
index 97ecc27..8b01774 100644
--- a/net/6lowpan/6lowpan_i.h
+++ b/net/6lowpan/6lowpan_i.h
@@ -12,6 +12,8 @@ static inline bool lowpan_is_ll(const struct net_device *dev,
return lowpan_dev(dev)->lltype == lltype;
}

+void lowpan_register_ndisc_ops(struct net_device *dev);
+
#ifdef CONFIG_6LOWPAN_DEBUGFS
int lowpan_dev_debugfs_init(struct net_device *dev);
void lowpan_dev_debugfs_exit(struct net_device *dev);
diff --git a/net/6lowpan/Makefile b/net/6lowpan/Makefile
index e44f3bf..12d131a 100644
--- a/net/6lowpan/Makefile
+++ b/net/6lowpan/Makefile
@@ -1,6 +1,6 @@
obj-$(CONFIG_6LOWPAN) += 6lowpan.o

-6lowpan-y := core.o iphc.o nhc.o
+6lowpan-y := core.o iphc.o nhc.o ndisc.o
6lowpan-$(CONFIG_6LOWPAN_DEBUGFS) += debugfs.o

#rfc6282 nhcs
diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
index 824d1bc..e7a370e 100644
--- a/net/6lowpan/core.c
+++ b/net/6lowpan/core.c
@@ -34,6 +34,8 @@ int lowpan_register_netdevice(struct net_device *dev,
for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
lowpan_dev(dev)->ctx.table[i].id = i;

+ lowpan_register_ndisc_ops(dev);
+
ret = register_netdevice(dev);
if (ret < 0)
return ret;
diff --git a/net/6lowpan/ndisc.c b/net/6lowpan/ndisc.c
new file mode 100644
index 0000000..d088295
--- /dev/null
+++ b/net/6lowpan/ndisc.c
@@ -0,0 +1,633 @@
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <net/6lowpan.h>
+#include <net/addrconf.h>
+#include <net/ip6_route.h>
+#include <net/ndisc.h>
+
+#include "6lowpan_i.h"
+
+struct lowpan_ndisc_options {
+ struct nd_opt_hdr *nd_opt_array[ND_OPT_TARGET_LL_ADDR + 1];
+#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
+ struct nd_opt_hdr *nd_802154_opt_array[ND_OPT_TARGET_LL_ADDR + 1];
+#endif
+};
+
+#define nd_802154_opts_src_lladdr nd_802154_opt_array[ND_OPT_SOURCE_LL_ADDR]
+#define nd_802154_opts_tgt_lladdr nd_802154_opt_array[ND_OPT_TARGET_LL_ADDR]
+
+#define NDISC_802154_EXTENDED_ADDR_LENGTH 2
+#define NDISC_802154_SHORT_ADDR_LENGTH 1
+
+#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
+static void lowpan_ndisc_802154_neigh_update(struct neighbour *n, void *priv,
+ bool override)
+{
+ struct lowpan_802154_neigh *neigh = lowpan_802154_neigh(neighbour_priv(n));
+
+ if (!override)
+ return;
+
+ write_lock_bh(&n->lock);
+ if (priv)
+ ieee802154_be16_to_le16(&neigh->short_addr, priv);
+ else
+ neigh->short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);
+ write_unlock_bh(&n->lock);
+}
+
+static inline int lowpan_ndisc_802154_short_addr_space(struct net_device *dev)
+{
+ struct wpan_dev *wpan_dev;
+ int addr_space = 0;
+
+ if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154)) {
+ wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr;
+
+ if (ieee802154_is_valid_src_short_addr(wpan_dev->short_addr))
+ addr_space = ndisc_opt_addr_space(dev, IEEE802154_SHORT_ADDR_LEN);
+ }
+
+ return addr_space;
+}
+
+static inline void
+lowpan_ndisc_802154_short_addr_option(struct net_device *dev,
+ struct sk_buff *skb, int type)
+{
+ struct wpan_dev *wpan_dev;
+ __be16 short_addr;
+
+ if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154)) {
+ wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr;
+
+ if (ieee802154_is_valid_src_short_addr(wpan_dev->short_addr)) {
+ ieee802154_le16_to_be16(&short_addr,
+ &wpan_dev->short_addr);
+ ndisc_fill_addr_option(skb, type, &short_addr,
+ IEEE802154_SHORT_ADDR_LEN);
+ }
+ }
+}
+#else
+static void
+lowpan_ndisc_802154_neigh_update(struct neighbour *n, void *priv,
+ bool override) { }
+
+static inline void
+lowpan_ndisc_802154_short_addr_option(struct net_device *dev,
+ struct sk_buff *skb,
+ int type) { }
+
+static inline int lowpan_ndisc_802154_short_addr_space(struct net_device *dev)
+{
+ return 0;
+}
+#endif
+
+static void lowpan_ndisc_parse_addr_options(const struct net_device *dev,
+ struct lowpan_ndisc_options *ndopts,
+ struct nd_opt_hdr *nd_opt)
+{
+ switch (nd_opt->nd_opt_len) {
+ case NDISC_802154_EXTENDED_ADDR_LENGTH:
+ if (ndopts->nd_opt_array[nd_opt->nd_opt_type])
+ ND_PRINTK(2, warn,
+ "%s: duplicated extended addr ND6 option found: type=%d\n",
+ __func__, nd_opt->nd_opt_type);
+ else
+ ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
+ break;
+#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
+ case NDISC_802154_SHORT_ADDR_LENGTH:
+ /* only valid on 802.15.4 */
+ if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154)) {
+ ND_PRINTK(2, warn,
+ "%s: invalid length detected: type=%d\n",
+ __func__, nd_opt->nd_opt_type);
+ break;
+ }
+
+ if (ndopts->nd_802154_opt_array[nd_opt->nd_opt_type])
+ ND_PRINTK(2, warn,
+ "%s: duplicated short addr ND6 option found: type=%d\n",
+ __func__, nd_opt->nd_opt_type);
+ else
+ ndopts->nd_802154_opt_array[nd_opt->nd_opt_type] = nd_opt;
+ break;
+#endif
+ default:
+ ND_PRINTK(2, warn,
+ "%s: invalid length detected: type=%d\n",
+ __func__, nd_opt->nd_opt_type);
+ break;
+ }
+}
+
+static struct lowpan_ndisc_options *
+lowpan_ndisc_parse_options(const struct net_device *dev, u8 *opt, int opt_len,
+ struct lowpan_ndisc_options *ndopts)
+{
+ struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)opt;
+
+ if (!nd_opt || opt_len < 0 || !ndopts)
+ return NULL;
+
+ memset(ndopts, 0, sizeof(*ndopts));
+
+ while (opt_len) {
+ int l;
+
+ if (opt_len < sizeof(struct nd_opt_hdr))
+ return NULL;
+
+ l = nd_opt->nd_opt_len << 3;
+ if (opt_len < l || l == 0)
+ return NULL;
+
+ switch (nd_opt->nd_opt_type) {
+ case ND_OPT_SOURCE_LL_ADDR:
+ case ND_OPT_TARGET_LL_ADDR:
+ lowpan_ndisc_parse_addr_options(dev, ndopts, nd_opt);
+ break;
+ default:
+ /* Unknown options must be silently ignored,
+ * to accommodate future extension to the
+ * protocol.
+ */
+ ND_PRINTK(2, notice,
+ "%s: ignored unsupported option; type=%d, len=%d\n",
+ __func__,
+ nd_opt->nd_opt_type,
+ nd_opt->nd_opt_len);
+ }
+
+ opt_len -= l;
+ nd_opt = ((void *)nd_opt) + l;
+ }
+
+ return ndopts;
+}
+
+static void lowpan_ndisc_send_na(struct net_device *dev,
+ const struct in6_addr *daddr,
+ const struct in6_addr *solicited_addr,
+ bool router, bool solicited, bool override,
+ bool inc_opt)
+{
+ struct sk_buff *skb;
+ struct in6_addr tmpaddr;
+ struct inet6_ifaddr *ifp;
+ const struct in6_addr *src_addr;
+ struct nd_msg *msg;
+ int optlen = 0;
+
+ /* for anycast or proxy, solicited_addr != src_addr */
+ ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1);
+ if (ifp) {
+ src_addr = solicited_addr;
+ if (ifp->flags & IFA_F_OPTIMISTIC)
+ override = false;
+ inc_opt |= ifp->idev->cnf.force_tllao;
+ in6_ifa_put(ifp);
+ } else {
+ if (ipv6_dev_get_saddr(dev_net(dev), dev, daddr,
+ inet6_sk(dev_net(dev)->ipv6.ndisc_sk)->srcprefs,
+ &tmpaddr))
+ return;
+ src_addr = &tmpaddr;
+ }
+
+ if (!dev->addr_len)
+ inc_opt = 0;
+ if (inc_opt) {
+ optlen += ndisc_opt_addr_space(dev, dev->addr_len);
+ optlen += lowpan_ndisc_802154_short_addr_space(dev);
+ }
+
+ skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
+ if (!skb)
+ return;
+
+ msg = (struct nd_msg *)skb_put(skb, sizeof(*msg));
+ *msg = (struct nd_msg) {
+ .icmph = {
+ .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
+ .icmp6_router = router,
+ .icmp6_solicited = solicited,
+ .icmp6_override = override,
+ },
+ .target = *solicited_addr,
+ };
+
+ if (inc_opt) {
+ ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR,
+ dev->dev_addr, dev->addr_len);
+ lowpan_ndisc_802154_short_addr_option(dev, skb,
+ ND_OPT_TARGET_LL_ADDR);
+ }
+
+ ndisc_send_skb(skb, daddr, src_addr);
+}
+
+static void lowpan_ndisc_recv_na(struct sk_buff *skb)
+{
+ struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
+ struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
+ const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
+ u8 *lladdr = NULL;
+ u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) +
+ offsetof(struct nd_msg, opt));
+ struct lowpan_ndisc_options ndopts;
+ struct net_device *dev = skb->dev;
+ struct inet6_dev *idev = __in6_dev_get(dev);
+ struct inet6_ifaddr *ifp;
+ struct neighbour *neigh;
+ u8 *lladdr_short = NULL;
+
+ if (skb->len < sizeof(struct nd_msg)) {
+ ND_PRINTK(2, warn, "NA: packet too short\n");
+ return;
+ }
+
+ if (ipv6_addr_is_multicast(&msg->target)) {
+ ND_PRINTK(2, warn, "NA: target address is multicast\n");
+ return;
+ }
+
+ if (ipv6_addr_is_multicast(daddr) &&
+ msg->icmph.icmp6_solicited) {
+ ND_PRINTK(2, warn, "NA: solicited NA is multicasted\n");
+ return;
+ }
+
+ /* For some 802.11 wireless deployments (and possibly other networks),
+ * there will be a NA proxy and unsolicitd packets are attacks
+ * and thus should not be accepted.
+ */
+ if (!msg->icmph.icmp6_solicited && idev &&
+ idev->cnf.drop_unsolicited_na)
+ return;
+
+ if (!lowpan_ndisc_parse_options(dev, msg->opt, ndoptlen, &ndopts)) {
+ ND_PRINTK(2, warn, "NS: invalid ND option\n");
+ return;
+ }
+ if (ndopts.nd_opts_tgt_lladdr) {
+ lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev,
+ dev->addr_len);
+ if (!lladdr) {
+ ND_PRINTK(2, warn,
+ "NA: invalid link-layer address length\n");
+ return;
+ }
+ }
+#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
+ if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154) &&
+ ndopts.nd_802154_opts_tgt_lladdr) {
+ lladdr_short = ndisc_opt_addr_data(ndopts.nd_802154_opts_tgt_lladdr,
+ dev, IEEE802154_SHORT_ADDR_LEN);
+ if (!lladdr_short) {
+ ND_PRINTK(2, warn,
+ "NA: invalid short link-layer address length\n");
+ return;
+ }
+ }
+#endif
+ ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
+ if (ifp) {
+ if (skb->pkt_type != PACKET_LOOPBACK &&
+ (ifp->flags & IFA_F_TENTATIVE)) {
+ addrconf_dad_failure(ifp);
+ return;
+ }
+ /* What should we make now? The advertisement
+ * is invalid, but ndisc specs say nothing
+ * about it. It could be misconfiguration, or
+ * an smart proxy agent tries to help us :-)
+ *
+ * We should not print the error if NA has been
+ * received from loopback - it is just our own
+ * unsolicited advertisement.
+ */
+ if (skb->pkt_type != PACKET_LOOPBACK)
+ ND_PRINTK(1, warn,
+ "NA: someone advertises our address %pI6 on %s!\n",
+ &ifp->addr, ifp->idev->dev->name);
+ in6_ifa_put(ifp);
+ return;
+ }
+ neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
+
+ if (neigh) {
+ u8 old_flags = neigh->flags;
+ struct net *net = dev_net(dev);
+
+ if (neigh->nud_state & NUD_FAILED)
+ goto out;
+
+ /* Don't update the neighbor cache entry on a proxy NA from
+ * ourselves because either the proxied node is off link or it
+ * has already sent a NA to us.
+ */
+ if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
+ net->ipv6.devconf_all->forwarding &&
+ net->ipv6.devconf_all->proxy_ndp &&
+ pneigh_lookup(&nd_tbl, net, &msg->target, dev, 0)) {
+ /* XXX: idev->cnf.proxy_ndp */
+ goto out;
+ }
+
+ neigh_update(neigh, lladdr,
+ msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
+ NEIGH_UPDATE_F_WEAK_OVERRIDE |
+ (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0) |
+ NEIGH_UPDATE_F_OVERRIDE_ISROUTER |
+ (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0));
+
+ if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154))
+ lowpan_ndisc_802154_neigh_update(neigh, lladdr_short,
+ msg->icmph.icmp6_override);
+
+ if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
+ /* Change: router to host */
+ rt6_clean_tohost(dev_net(dev), saddr);
+ }
+
+out:
+ neigh_release(neigh);
+ }
+}
+
+static void lowpan_ndisc_send_ns(struct net_device *dev,
+ const struct in6_addr *solicit,
+ const struct in6_addr *daddr,
+ const struct in6_addr *saddr)
+{
+ struct sk_buff *skb;
+ struct in6_addr addr_buf;
+ int inc_opt = dev->addr_len;
+ int optlen = 0;
+ struct nd_msg *msg;
+
+ if (!saddr) {
+ if (ipv6_get_lladdr(dev, &addr_buf,
+ (IFA_F_TENTATIVE | IFA_F_OPTIMISTIC)))
+ return;
+ saddr = &addr_buf;
+ }
+
+ if (ipv6_addr_any(saddr))
+ inc_opt = false;
+ if (inc_opt) {
+ optlen += ndisc_opt_addr_space(dev, dev->addr_len);
+ optlen += lowpan_ndisc_802154_short_addr_space(dev);
+ }
+
+ skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
+ if (!skb)
+ return;
+
+ msg = (struct nd_msg *)skb_put(skb, sizeof(*msg));
+ *msg = (struct nd_msg) {
+ .icmph = {
+ .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,
+ },
+ .target = *solicit,
+ };
+
+ if (inc_opt) {
+ ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR,
+ dev->dev_addr, dev->addr_len);
+ lowpan_ndisc_802154_short_addr_option(dev, skb,
+ ND_OPT_SOURCE_LL_ADDR);
+ }
+
+ ndisc_send_skb(skb, daddr, saddr);
+}
+
+static void lowpan_ndisc_recv_ns(struct sk_buff *skb)
+{
+ struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
+ const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
+ const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
+ u8 *lladdr = NULL;
+ u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) +
+ offsetof(struct nd_msg, opt));
+ struct lowpan_ndisc_options ndopts;
+ struct net_device *dev = skb->dev;
+ struct inet6_ifaddr *ifp;
+ struct inet6_dev *idev = NULL;
+ struct neighbour *neigh;
+ int dad = ipv6_addr_any(saddr);
+ bool inc;
+ int is_router = -1;
+ u8 *lladdr_short = NULL;
+
+ if (skb->len < sizeof(struct nd_msg)) {
+ ND_PRINTK(2, warn, "NS: packet too short\n");
+ return;
+ }
+
+ if (ipv6_addr_is_multicast(&msg->target)) {
+ ND_PRINTK(2, warn, "NS: multicast target address\n");
+ return;
+ }
+
+ /* RFC2461 7.1.1:
+ * DAD has to be destined for solicited node multicast address.
+ */
+ if (dad && !ipv6_addr_is_solict_mult(daddr)) {
+ ND_PRINTK(2, warn, "NS: bad DAD packet (wrong destination)\n");
+ return;
+ }
+
+ if (!lowpan_ndisc_parse_options(dev, msg->opt, ndoptlen, &ndopts)) {
+ ND_PRINTK(2, warn, "NS: invalid ND options\n");
+ return;
+ }
+
+ if (ndopts.nd_opts_src_lladdr) {
+ lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev,
+ dev->addr_len);
+ if (!lladdr) {
+ ND_PRINTK(2, warn,
+ "NS: invalid link-layer address length\n");
+ return;
+ }
+
+ /* RFC2461 7.1.1:
+ * If the IP source address is the unspecified address,
+ * there MUST NOT be source link-layer address option
+ * in the message.
+ */
+ if (dad) {
+ ND_PRINTK(2, warn,
+ "NS: bad DAD packet (link-layer address option)\n");
+ return;
+ }
+ }
+
+#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
+ if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154) &&
+ ndopts.nd_802154_opts_src_lladdr) {
+ lladdr_short = ndisc_opt_addr_data(ndopts.nd_802154_opts_src_lladdr,
+ dev, IEEE802154_SHORT_ADDR_LEN);
+ if (!lladdr_short) {
+ ND_PRINTK(2, warn,
+ "NS: invalid short link-layer address length\n");
+ return;
+ }
+
+ /* RFC2461 7.1.1:
+ * If the IP source address is the unspecified address,
+ * there MUST NOT be source link-layer address option
+ * in the message.
+ */
+ if (dad) {
+ ND_PRINTK(2, warn,
+ "NS: bad DAD packet (short link-layer address option)\n");
+ return;
+ }
+ }
+#endif
+
+ inc = ipv6_addr_is_multicast(daddr);
+
+ ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
+ if (ifp) {
+have_ifp:
+ if (ifp->flags & (IFA_F_TENTATIVE | IFA_F_OPTIMISTIC)) {
+ if (dad) {
+ /* We are colliding with another node
+ * who is doing DAD
+ * so fail our DAD process
+ */
+ addrconf_dad_failure(ifp);
+ return;
+ }
+
+ /* This is not a dad solicitation.
+ * If we are an optimistic node,
+ * we should respond.
+ * Otherwise, we should ignore it.
+ */
+ if (!(ifp->flags & IFA_F_OPTIMISTIC))
+ goto out;
+ }
+
+ idev = ifp->idev;
+ } else {
+ struct net *net = dev_net(dev);
+
+ /* perhaps an address on the master device */
+ if (netif_is_l3_slave(dev)) {
+ struct net_device *mdev;
+
+ mdev = netdev_master_upper_dev_get_rcu(dev);
+ if (mdev) {
+ ifp = ipv6_get_ifaddr(net, &msg->target, mdev, 1);
+ if (ifp)
+ goto have_ifp;
+ }
+ }
+
+ idev = in6_dev_get(dev);
+ if (!idev) {
+ /* XXX: count this drop? */
+ return;
+ }
+
+ if (ipv6_chk_acast_addr(net, dev, &msg->target) ||
+ (idev->cnf.forwarding &&
+ (net->ipv6.devconf_all->proxy_ndp || idev->cnf.proxy_ndp) &&
+ (is_router = pndisc_is_router(&msg->target, dev)) >= 0)) {
+ if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) &&
+ skb->pkt_type != PACKET_HOST &&
+ inc &&
+ NEIGH_VAR(idev->nd_parms, PROXY_DELAY) != 0) {
+ /* for anycast or proxy,
+ * sender should delay its response
+ * by a random time between 0 and
+ * MAX_ANYCAST_DELAY_TIME seconds.
+ * (RFC2461) -- yoshfuji
+ */
+ struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
+
+ if (n)
+ pneigh_enqueue(&nd_tbl, idev->nd_parms,
+ n);
+ goto out;
+ }
+ } else {
+ goto out;
+ }
+ }
+
+ if (is_router < 0)
+ is_router = idev->cnf.forwarding;
+
+ if (dad) {
+ ndisc_send_na(dev, &in6addr_linklocal_allnodes, &msg->target,
+ !!is_router, false, (ifp != NULL), true);
+ goto out;
+ }
+
+ if (inc)
+ NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
+ else
+ NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
+
+ /* update / create cache entry
+ * for the source address
+ */
+ neigh = __neigh_lookup(&nd_tbl, saddr, dev,
+ !inc || lladdr || !dev->addr_len);
+ if (neigh) {
+ neigh_update(neigh, lladdr, NUD_STALE,
+ NEIGH_UPDATE_F_WEAK_OVERRIDE |
+ NEIGH_UPDATE_F_OVERRIDE);
+ if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154))
+ lowpan_ndisc_802154_neigh_update(neigh, lladdr_short,
+ true);
+ }
+ if (neigh || !dev->header_ops) {
+ ndisc_send_na(dev, saddr, &msg->target, !!is_router,
+ true, (ifp != NULL && inc), inc);
+ if (neigh)
+ neigh_release(neigh);
+ }
+
+out:
+ if (ifp)
+ in6_ifa_put(ifp);
+ else
+ in6_dev_put(idev);
+}
+
+static inline int lowpan_ndisc_is_useropt(struct nd_opt_hdr *opt)
+{
+ return __ip6_ndisc_is_useropt(opt) || opt->nd_opt_type == ND_OPT_6CO;
+}
+
+static const struct ndisc_ops lowpan_ndisc_ops = {
+ .is_useropt = lowpan_ndisc_is_useropt,
+ .send_na = lowpan_ndisc_send_na,
+ .recv_na = lowpan_ndisc_recv_na,
+ .send_ns = lowpan_ndisc_send_ns,
+ .recv_ns = lowpan_ndisc_recv_ns,
+};
+
+void lowpan_register_ndisc_ops(struct net_device *dev)
+{
+ dev->ndisc_ops = &lowpan_ndisc_ops;
+}
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index dc8bfec..9d7f228 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -1792,6 +1792,9 @@ static const struct ndisc_ops ip6_ndisc_ops = {
void ip6_register_ndisc_ops(struct net_device *dev)
{
switch (dev->type) {
+ case ARPHRD_6LOWPAN:
+ /* will be assigned while lowpan interface register */
+ break;
default:
if (dev->ndisc_ops) {
ND_PRINTK(2, warn,
--
2.8.0

2016-04-20 08:19:40

by Alexander Aring

[permalink] [raw]
Subject: [PATCHv2 bluetooth-next 08/10] ipv6: export ndisc functions

This patch exports some neighbour discovery functions which can be used
by 6lowpan neighbour discovery ops functionality then.

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 | 16 ++++++++++++++++
net/ipv6/addrconf.c | 1 +
net/ipv6/ndisc.c | 28 ++++++++++------------------
3 files changed, 27 insertions(+), 18 deletions(-)

diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index 14ed016..35a4396 100644
--- a/include/net/ndisc.h
+++ b/include/net/ndisc.h
@@ -53,6 +53,15 @@ enum {

#include <net/neighbour.h>

+/* Set to 3 to get tracing... */
+#define ND_DEBUG 1
+
+#define ND_PRINTK(val, level, fmt, ...) \
+do { \
+ if (val <= ND_DEBUG) \
+ net_##level##_ratelimited(fmt, ##__VA_ARGS__); \
+} while (0)
+
struct ctl_table;
struct inet6_dev;
struct net_device;
@@ -267,6 +276,13 @@ int ndisc_late_init(void);
void ndisc_late_cleanup(void);
void ndisc_cleanup(void);

+void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data,
+ int data_len);
+struct sk_buff *ndisc_alloc_skb(struct net_device *dev, int len);
+void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr,
+ const struct in6_addr *saddr);
+int pndisc_is_router(const void *pkey, struct net_device *dev);
+
int ndisc_rcv(struct sk_buff *skb);

void ndisc_send_rs(struct net_device *dev,
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index a2ef04b..8f05ef8 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1775,6 +1775,7 @@ struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *add

return result;
}
+EXPORT_SYMBOL(ipv6_get_ifaddr);

/* Gets referenced address, destroys ifaddr */

diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 297080a..dc8bfec 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -73,15 +73,6 @@
#include <linux/netfilter.h>
#include <linux/netfilter_ipv6.h>

-/* Set to 3 to get tracing... */
-#define ND_DEBUG 1
-
-#define ND_PRINTK(val, level, fmt, ...) \
-do { \
- if (val <= ND_DEBUG) \
- net_##level##_ratelimited(fmt, ##__VA_ARGS__); \
-} while (0)
-
static u32 ndisc_hash(const void *pkey,
const struct net_device *dev,
__u32 *hash_rnd);
@@ -150,8 +141,8 @@ struct neigh_table nd_tbl = {
};
EXPORT_SYMBOL_GPL(nd_tbl);

-static void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data,
- int data_len)
+void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data,
+ int data_len)
{
int pad = ndisc_addr_option_pad(skb->dev->type);
int space = ndisc_opt_addr_space(skb->dev, data_len);
@@ -171,6 +162,7 @@ static void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data,
if (space > 0)
memset(opt, 0, space);
}
+EXPORT_SYMBOL(ndisc_fill_addr_option);

static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
struct nd_opt_hdr *end)
@@ -378,8 +370,7 @@ static void pndisc_destructor(struct pneigh_entry *n)
ipv6_dev_mc_dec(dev, &maddr);
}

-static struct sk_buff *ndisc_alloc_skb(struct net_device *dev,
- int len)
+struct sk_buff *ndisc_alloc_skb(struct net_device *dev, int len)
{
int hlen = LL_RESERVED_SPACE(dev);
int tlen = dev->needed_tailroom;
@@ -406,6 +397,7 @@ static struct sk_buff *ndisc_alloc_skb(struct net_device *dev,

return skb;
}
+EXPORT_SYMBOL(ndisc_alloc_skb);

static void ip6_nd_hdr(struct sk_buff *skb,
const struct in6_addr *saddr,
@@ -428,9 +420,8 @@ static void ip6_nd_hdr(struct sk_buff *skb,
hdr->daddr = *daddr;
}

-static void ndisc_send_skb(struct sk_buff *skb,
- const struct in6_addr *daddr,
- const struct in6_addr *saddr)
+void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr,
+ const struct in6_addr *saddr)
{
struct dst_entry *dst = skb_dst(skb);
struct net *net = dev_net(skb->dev);
@@ -479,6 +470,7 @@ static void ndisc_send_skb(struct sk_buff *skb,

rcu_read_unlock();
}
+EXPORT_SYMBOL(ndisc_send_skb);

static void ip6_ndisc_send_na(struct net_device *dev,
const struct in6_addr *daddr,
@@ -692,8 +684,7 @@ static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
}
}

-static int pndisc_is_router(const void *pkey,
- struct net_device *dev)
+int pndisc_is_router(const void *pkey, struct net_device *dev)
{
struct pneigh_entry *n;
int ret = -1;
@@ -706,6 +697,7 @@ static int pndisc_is_router(const void *pkey,

return ret;
}
+EXPORT_SYMBOL(pndisc_is_router);

static void ip6_ndisc_recv_ns(struct sk_buff *skb)
{
--
2.8.0

2016-04-20 08:19:39

by Alexander Aring

[permalink] [raw]
Subject: [PATCHv2 bluetooth-next 07/10] ipv6: introduce neighbour discovery ops

This patch introduces neighbour discovery ops callback structure. The
structure contains at first receive and transmit handling for NS/NA and
userspace option field functionality.

These callback offers 6lowpan different handling, such as 802.15.4 short
address handling or RFC6775 (Neighbor Discovery Optimization for IPv6 over
6LoWPANs).

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/linux/netdevice.h | 3 ++
include/net/ndisc.h | 96 +++++++++++++++++++++++++++++++++++++++++++----
net/ipv6/addrconf.c | 1 +
net/ipv6/ndisc.c | 71 ++++++++++++++++++++++++-----------
net/ipv6/route.c | 2 +-
5 files changed, 144 insertions(+), 29 deletions(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 0052c42..bc60033 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1677,6 +1677,9 @@ struct net_device {
#ifdef CONFIG_NET_L3_MASTER_DEV
const struct l3mdev_ops *l3mdev_ops;
#endif
+#if IS_ENABLED(CONFIG_IPV6)
+ const struct ndisc_ops *ndisc_ops;
+#endif

const struct header_ops *header_ops;

diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index aac868e..14ed016 100644
--- a/include/net/ndisc.h
+++ b/include/net/ndisc.h
@@ -110,7 +110,8 @@ struct ndisc_options {

#define NDISC_OPT_SPACE(len) (((len)+2+7)&~7)

-struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
+struct ndisc_options *ndisc_parse_options(const struct net_device *dev,
+ u8 *opt, int opt_len,
struct ndisc_options *ndopts);

/*
@@ -173,6 +174,93 @@ static inline struct neighbour *__ipv6_neigh_lookup(struct net_device *dev, cons
return n;
}

+static inline int __ip6_ndisc_is_useropt(struct nd_opt_hdr *opt)
+{
+ return opt->nd_opt_type == ND_OPT_RDNSS ||
+ opt->nd_opt_type == ND_OPT_DNSSL;
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+struct ndisc_ops {
+ int (*is_useropt)(struct nd_opt_hdr *opt);
+ void (*send_na)(struct net_device *dev,
+ const struct in6_addr *daddr,
+ const struct in6_addr *solicited_addr,
+ bool router, bool solicited,
+ bool override, bool inc_opt);
+ void (*recv_na)(struct sk_buff *skb);
+ void (*send_ns)(struct net_device *dev,
+ const struct in6_addr *solicit,
+ const struct in6_addr *daddr,
+ const struct in6_addr *saddr);
+ void (*recv_ns)(struct sk_buff *skb);
+};
+
+static inline int ndisc_is_useropt(const struct net_device *dev,
+ struct nd_opt_hdr *opt)
+{
+ if (likely(dev->ndisc_ops->is_useropt))
+ return dev->ndisc_ops->is_useropt(opt);
+ else
+ return 0;
+}
+
+static inline void ndisc_send_na(struct net_device *dev,
+ const struct in6_addr *daddr,
+ const struct in6_addr *solicited_addr,
+ bool router, bool solicited, bool override,
+ bool inc_opt)
+{
+ if (likely(dev->ndisc_ops->send_na))
+ dev->ndisc_ops->send_na(dev, daddr, solicited_addr, router,
+ solicited, override, inc_opt);
+}
+
+static inline void ndisc_recv_na(struct sk_buff *skb)
+{
+ if (likely(skb->dev->ndisc_ops->recv_na))
+ skb->dev->ndisc_ops->recv_na(skb);
+}
+
+static inline void ndisc_send_ns(struct net_device *dev,
+ const struct in6_addr *solicit,
+ const struct in6_addr *daddr,
+ const struct in6_addr *saddr)
+{
+ if (likely(dev->ndisc_ops->send_ns))
+ dev->ndisc_ops->send_ns(dev, solicit, daddr, saddr);
+}
+
+static inline void ndisc_recv_ns(struct sk_buff *skb)
+{
+ if (likely(skb->dev->ndisc_ops->recv_ns))
+ skb->dev->ndisc_ops->recv_ns(skb);
+}
+#else
+static inline int ndisc_is_useropt(const struct net_device *dev,
+ struct nd_opt_hdr *opt)
+{
+ return 0;
+}
+
+static inline void ndisc_send_na(struct net_device *dev,
+ const struct in6_addr *daddr,
+ const struct in6_addr *solicited_addr,
+ bool router, bool solicited, bool override,
+ bool inc_opt) { }
+
+static inline void ndisc_recv_na(struct sk_buff *skb) { }
+
+static inline void ndisc_send_ns(struct net_device *dev,
+ const struct in6_addr *solicit,
+ const struct in6_addr *daddr,
+ const struct in6_addr *saddr) { }
+
+static inline void ndisc_recv_ns(struct sk_buff *skb) { }
+#endif
+
+void ip6_register_ndisc_ops(struct net_device *dev);
+
int ndisc_init(void);
int ndisc_late_init(void);

@@ -181,14 +269,8 @@ void ndisc_cleanup(void);

int ndisc_rcv(struct sk_buff *skb);

-void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
- const struct in6_addr *daddr, const struct in6_addr *saddr);
-
void ndisc_send_rs(struct net_device *dev,
const struct in6_addr *saddr, const struct in6_addr *daddr);
-void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
- const struct in6_addr *solicited_addr,
- bool router, bool solicited, bool override, bool inc_opt);

void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target);

diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 54e18c2..a2ef04b 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -3266,6 +3266,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
idev = ipv6_add_dev(dev);
if (IS_ERR(idev))
return notifier_from_errno(PTR_ERR(idev));
+ ip6_register_ndisc_ops(dev);
}
break;

diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 176c7c4..297080a 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -185,24 +185,25 @@ static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
return cur <= end && cur->nd_opt_type == type ? cur : NULL;
}

-static inline int ndisc_is_useropt(struct nd_opt_hdr *opt)
+static inline int ip6_ndisc_is_useropt(struct nd_opt_hdr *opt)
{
- return opt->nd_opt_type == ND_OPT_RDNSS ||
- opt->nd_opt_type == ND_OPT_DNSSL;
+ return __ip6_ndisc_is_useropt(opt);
}

-static struct nd_opt_hdr *ndisc_next_useropt(struct nd_opt_hdr *cur,
+static struct nd_opt_hdr *ndisc_next_useropt(const struct net_device *dev,
+ struct nd_opt_hdr *cur,
struct nd_opt_hdr *end)
{
if (!cur || !end || cur >= end)
return NULL;
do {
cur = ((void *)cur) + (cur->nd_opt_len << 3);
- } while (cur < end && !ndisc_is_useropt(cur));
- return cur <= end && ndisc_is_useropt(cur) ? cur : NULL;
+ } while (cur < end && !ndisc_is_useropt(dev, cur));
+ return cur <= end && ndisc_is_useropt(dev, cur) ? cur : NULL;
}

-struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
+struct ndisc_options *ndisc_parse_options(const struct net_device *dev,
+ u8 *opt, int opt_len,
struct ndisc_options *ndopts)
{
struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)opt;
@@ -243,7 +244,7 @@ struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
break;
#endif
default:
- if (ndisc_is_useropt(nd_opt)) {
+ if (ndisc_is_useropt(dev, nd_opt)) {
ndopts->nd_useropts_end = nd_opt;
if (!ndopts->nd_useropts)
ndopts->nd_useropts = nd_opt;
@@ -479,9 +480,11 @@ static void ndisc_send_skb(struct sk_buff *skb,
rcu_read_unlock();
}

-void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
- const struct in6_addr *solicited_addr,
- bool router, bool solicited, bool override, bool inc_opt)
+static void ip6_ndisc_send_na(struct net_device *dev,
+ const struct in6_addr *daddr,
+ const struct in6_addr *solicited_addr,
+ bool router, bool solicited, bool override,
+ bool inc_opt)
{
struct sk_buff *skb;
struct in6_addr tmpaddr;
@@ -555,8 +558,10 @@ static void ndisc_send_unsol_na(struct net_device *dev)
in6_dev_put(idev);
}

-void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
- const struct in6_addr *daddr, const struct in6_addr *saddr)
+static void ip6_ndisc_send_ns(struct net_device *dev,
+ const struct in6_addr *solicit,
+ const struct in6_addr *daddr,
+ const struct in6_addr *saddr)
{
struct sk_buff *skb;
struct in6_addr addr_buf;
@@ -702,7 +707,7 @@ static int pndisc_is_router(const void *pkey,
return ret;
}

-static void ndisc_recv_ns(struct sk_buff *skb)
+static void ip6_ndisc_recv_ns(struct sk_buff *skb)
{
struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
@@ -738,7 +743,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
return;
}

- if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
+ if (!ndisc_parse_options(dev, msg->opt, ndoptlen, &ndopts)) {
ND_PRINTK(2, warn, "NS: invalid ND options\n");
return;
}
@@ -874,7 +879,7 @@ out:
in6_dev_put(idev);
}

-static void ndisc_recv_na(struct sk_buff *skb)
+static void ip6_ndisc_recv_na(struct sk_buff *skb)
{
struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
@@ -912,7 +917,7 @@ static void ndisc_recv_na(struct sk_buff *skb)
idev->cnf.drop_unsolicited_na)
return;

- if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
+ if (!ndisc_parse_options(dev, msg->opt, ndoptlen, &ndopts)) {
ND_PRINTK(2, warn, "NS: invalid ND option\n");
return;
}
@@ -1019,7 +1024,7 @@ static void ndisc_recv_rs(struct sk_buff *skb)
goto out;

/* Parse ND options */
- if (!ndisc_parse_options(rs_msg->opt, ndoptlen, &ndopts)) {
+ if (!ndisc_parse_options(skb->dev, rs_msg->opt, ndoptlen, &ndopts)) {
ND_PRINTK(2, notice, "NS: invalid ND option, ignored\n");
goto out;
}
@@ -1137,7 +1142,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)
return;
}

- if (!ndisc_parse_options(opt, optlen, &ndopts)) {
+ if (!ndisc_parse_options(skb->dev, opt, optlen, &ndopts)) {
ND_PRINTK(2, warn, "RA: invalid ND options\n");
return;
}
@@ -1424,7 +1429,8 @@ skip_routeinfo:
struct nd_opt_hdr *p;
for (p = ndopts.nd_useropts;
p;
- p = ndisc_next_useropt(p, ndopts.nd_useropts_end)) {
+ p = ndisc_next_useropt(skb->dev, p,
+ ndopts.nd_useropts_end)) {
ndisc_ra_useropt(skb, p);
}
}
@@ -1462,7 +1468,7 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
return;
}

- if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts))
+ if (!ndisc_parse_options(skb->dev, msg->opt, ndoptlen, &ndopts))
return;

if (!ndopts.nd_opts_rh) {
@@ -1783,6 +1789,29 @@ int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, void __user *bu

#endif

+static const struct ndisc_ops ip6_ndisc_ops = {
+ .is_useropt = ip6_ndisc_is_useropt,
+ .send_na = ip6_ndisc_send_na,
+ .recv_na = ip6_ndisc_recv_na,
+ .send_ns = ip6_ndisc_send_ns,
+ .recv_ns = ip6_ndisc_recv_ns,
+};
+
+void ip6_register_ndisc_ops(struct net_device *dev)
+{
+ switch (dev->type) {
+ default:
+ if (dev->ndisc_ops) {
+ ND_PRINTK(2, warn,
+ "%s: ndisc_ops already defined for interface type=%d\n",
+ __func__, dev->type);
+ } else {
+ dev->ndisc_ops = &ip6_ndisc_ops;
+ }
+ break;
+ }
+}
+
static int __net_init ndisc_net_init(struct net *net)
{
struct ipv6_pinfo *np;
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index cc180b3..5fa276d 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -2149,7 +2149,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
* first-hop router for the specified ICMP Destination Address.
*/

- if (!ndisc_parse_options(msg->opt, optlen, &ndopts)) {
+ if (!ndisc_parse_options(skb->dev, msg->opt, optlen, &ndopts)) {
net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
return;
}
--
2.8.0

2016-04-20 08:19:38

by Alexander Aring

[permalink] [raw]
Subject: [PATCHv2 bluetooth-next 06/10] ndisc: add addr_len parameter to ndisc_fill_addr_option

This patch makes the address length as argument for the
ndisc_fill_addr_option function. This is necessary to handle addresses
which don't use dev->addr_len as address length.

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]>
---
net/ipv6/ndisc.c | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 4e91d5e..176c7c4 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -150,11 +150,11 @@ struct neigh_table nd_tbl = {
};
EXPORT_SYMBOL_GPL(nd_tbl);

-static void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data)
+static void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data,
+ int data_len)
{
int pad = ndisc_addr_option_pad(skb->dev->type);
- int data_len = skb->dev->addr_len;
- int space = ndisc_opt_addr_space(skb->dev, skb->dev->addr_len);
+ int space = ndisc_opt_addr_space(skb->dev, data_len);
u8 *opt = skb_put(skb, space);

opt[0] = type;
@@ -528,7 +528,7 @@ void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,

if (inc_opt)
ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR,
- dev->dev_addr);
+ dev->dev_addr, dev->addr_len);


ndisc_send_skb(skb, daddr, src_addr);
@@ -590,7 +590,7 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,

if (inc_opt)
ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR,
- dev->dev_addr);
+ dev->dev_addr, dev->addr_len);

ndisc_send_skb(skb, daddr, saddr);
}
@@ -641,7 +641,7 @@ void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,

if (send_sllao)
ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR,
- dev->dev_addr);
+ dev->dev_addr, dev->addr_len);

ndisc_send_skb(skb, daddr, saddr);
}
@@ -1597,7 +1597,8 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
*/

if (ha)
- ndisc_fill_addr_option(buff, ND_OPT_TARGET_LL_ADDR, ha);
+ ndisc_fill_addr_option(buff, ND_OPT_TARGET_LL_ADDR, ha,
+ dev->addr_len);

/*
* build redirect option and copy skb over to the new packet.
--
2.8.0

2016-04-20 08:19:37

by Alexander Aring

[permalink] [raw]
Subject: [PATCHv2 bluetooth-next 05/10] ndisc: add addr_len parameter to ndisc_opt_addr_data

This patch makes the address length as argument for the
ndisc_opt_addr_data function. This is necessary to handle addresses
which don't use dev->addr_len as address length.

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 | 5 +++--
net/ipv6/ndisc.c | 11 +++++++----
net/ipv6/route.c | 2 +-
3 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index ef43e88..aac868e 100644
--- a/include/net/ndisc.h
+++ b/include/net/ndisc.h
@@ -134,12 +134,13 @@ static inline int ndisc_opt_addr_space(struct net_device *dev,
}

static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,
- struct net_device *dev)
+ struct net_device *dev,
+ unsigned char addr_len)
{
u8 *lladdr = (u8 *)(p + 1);
int lladdrlen = p->nd_opt_len << 3;
int prepad = ndisc_addr_option_pad(dev->type);
- if (lladdrlen != ndisc_opt_addr_space(dev, dev->addr_len))
+ if (lladdrlen != ndisc_opt_addr_space(dev, addr_len))
return NULL;
return lladdr + prepad;
}
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 69e20e3..4e91d5e 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -744,7 +744,8 @@ static void ndisc_recv_ns(struct sk_buff *skb)
}

if (ndopts.nd_opts_src_lladdr) {
- lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
+ lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev,
+ dev->addr_len);
if (!lladdr) {
ND_PRINTK(2, warn,
"NS: invalid link-layer address length\n");
@@ -916,7 +917,8 @@ static void ndisc_recv_na(struct sk_buff *skb)
return;
}
if (ndopts.nd_opts_tgt_lladdr) {
- lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);
+ lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev,
+ dev->addr_len);
if (!lladdr) {
ND_PRINTK(2, warn,
"NA: invalid link-layer address length\n");
@@ -1024,7 +1026,7 @@ static void ndisc_recv_rs(struct sk_buff *skb)

if (ndopts.nd_opts_src_lladdr) {
lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
- skb->dev);
+ skb->dev, skb->dev->addr_len);
if (!lladdr)
goto out;
}
@@ -1322,7 +1324,8 @@ skip_linkparms:
u8 *lladdr = NULL;
if (ndopts.nd_opts_src_lladdr) {
lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
- skb->dev);
+ skb->dev,
+ skb->dev->addr_len);
if (!lladdr) {
ND_PRINTK(2, warn,
"RA: invalid link-layer address length\n");
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index ed44663..cc180b3 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -2157,7 +2157,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
lladdr = NULL;
if (ndopts.nd_opts_tgt_lladdr) {
lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
- skb->dev);
+ skb->dev, skb->dev->addr_len);
if (!lladdr) {
net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
return;
--
2.8.0

2016-04-20 08:19:36

by Alexander Aring

[permalink] [raw]
Subject: [PATCHv2 bluetooth-next 04/10] ndisc: add addr_len parameter to ndisc_opt_addr_space

This patch makes the address length as argument for the
ndisc_opt_addr_space function. This is necessary to handle addresses
which don't use dev->addr_len as address length.

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 | 8 ++++----
net/ipv6/ndisc.c | 10 +++++-----
2 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index 2d8edaa..ef43e88 100644
--- a/include/net/ndisc.h
+++ b/include/net/ndisc.h
@@ -127,10 +127,10 @@ static inline int ndisc_addr_option_pad(unsigned short type)
}
}

-static inline int ndisc_opt_addr_space(struct net_device *dev)
+static inline int ndisc_opt_addr_space(struct net_device *dev,
+ unsigned char addr_len)
{
- return NDISC_OPT_SPACE(dev->addr_len +
- ndisc_addr_option_pad(dev->type));
+ return NDISC_OPT_SPACE(addr_len + ndisc_addr_option_pad(dev->type));
}

static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,
@@ -139,7 +139,7 @@ static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,
u8 *lladdr = (u8 *)(p + 1);
int lladdrlen = p->nd_opt_len << 3;
int prepad = ndisc_addr_option_pad(dev->type);
- if (lladdrlen != ndisc_opt_addr_space(dev))
+ if (lladdrlen != ndisc_opt_addr_space(dev, dev->addr_len))
return NULL;
return lladdr + prepad;
}
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index c245895..69e20e3 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -154,7 +154,7 @@ static void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data)
{
int pad = ndisc_addr_option_pad(skb->dev->type);
int data_len = skb->dev->addr_len;
- int space = ndisc_opt_addr_space(skb->dev);
+ int space = ndisc_opt_addr_space(skb->dev, skb->dev->addr_len);
u8 *opt = skb_put(skb, space);

opt[0] = type;
@@ -509,7 +509,7 @@ void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
if (!dev->addr_len)
inc_opt = 0;
if (inc_opt)
- optlen += ndisc_opt_addr_space(dev);
+ optlen += ndisc_opt_addr_space(dev, dev->addr_len);

skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
if (!skb)
@@ -574,7 +574,7 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
if (ipv6_addr_any(saddr))
inc_opt = false;
if (inc_opt)
- optlen += ndisc_opt_addr_space(dev);
+ optlen += ndisc_opt_addr_space(dev, dev->addr_len);

skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
if (!skb)
@@ -626,7 +626,7 @@ void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
}
#endif
if (send_sllao)
- optlen += ndisc_opt_addr_space(dev);
+ optlen += ndisc_opt_addr_space(dev, dev->addr_len);

skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
if (!skb)
@@ -1563,7 +1563,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
memcpy(ha_buf, neigh->ha, dev->addr_len);
read_unlock_bh(&neigh->lock);
ha = ha_buf;
- optlen += ndisc_opt_addr_space(dev);
+ optlen += ndisc_opt_addr_space(dev, dev->addr_len);
} else
read_unlock_bh(&neigh->lock);

--
2.8.0

2016-04-20 08:19:35

by Alexander Aring

[permalink] [raw]
Subject: [PATCHv2 bluetooth-next 03/10] 6lowpan: remove ipv6 module request

Since we use exported function from ipv6 kernel module we don't need to
request the module anymore to have ipv6 functionality.

Signed-off-by: Alexander Aring <[email protected]>
---
net/6lowpan/core.c | 2 --
1 file changed, 2 deletions(-)

diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
index fbae31e..824d1bc 100644
--- a/net/6lowpan/core.c
+++ b/net/6lowpan/core.c
@@ -158,8 +158,6 @@ static int __init lowpan_module_init(void)
return ret;
}

- request_module_nowait("ipv6");
-
request_module_nowait("nhc_dest");
request_module_nowait("nhc_fragment");
request_module_nowait("nhc_hop");
--
2.8.0

2016-04-20 08:19:34

by Alexander Aring

[permalink] [raw]
Subject: [PATCHv2 bluetooth-next 02/10] 6lowpan: add 802.15.4 short addr slaac

This patch adds the autoconfiguration if a valid 802.15.4 short address
is available for 802.15.4 6LoWPAN interfaces.

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/addrconf.h | 3 +++
net/6lowpan/core.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
net/ipv6/addrconf.c | 5 +++--
3 files changed, 52 insertions(+), 2 deletions(-)

diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 730d856..b1774eb 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -94,6 +94,9 @@ int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2,
void addrconf_join_solict(struct net_device *dev, const struct in6_addr *addr);
void addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr *addr);

+void addrconf_add_linklocal(struct inet6_dev *idev,
+ const struct in6_addr *addr, u32 flags);
+
static inline int addrconf_ifid_eui48(u8 *eui, struct net_device *dev)
{
if (dev->addr_len != ETH_ALEN)
diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
index 7a240b3..fbae31e 100644
--- a/net/6lowpan/core.c
+++ b/net/6lowpan/core.c
@@ -14,6 +14,7 @@
#include <linux/module.h>

#include <net/6lowpan.h>
+#include <net/addrconf.h>

#include "6lowpan_i.h"

@@ -72,16 +73,61 @@ void lowpan_unregister_netdev(struct net_device *dev)
}
EXPORT_SYMBOL(lowpan_unregister_netdev);

+static int addrconf_ifid_802154_6lowpan(u8 *eui, struct net_device *dev)
+{
+ struct wpan_dev *wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr;
+
+ /* Set short_addr autoconfiguration if short_addr is present only */
+ if (!ieee802154_is_valid_src_short_addr(wpan_dev->short_addr))
+ return -1;
+
+ /* For either address format, all zero addresses MUST NOT be used */
+ if (wpan_dev->pan_id == cpu_to_le16(0x0000) &&
+ wpan_dev->short_addr == cpu_to_le16(0x0000))
+ return -1;
+
+ /* Alternatively, if no PAN ID is known, 16 zero bits may be used */
+ if (wpan_dev->pan_id == cpu_to_le16(IEEE802154_PAN_ID_BROADCAST))
+ memset(eui, 0, 2);
+ else
+ ieee802154_le16_to_be16(eui, &wpan_dev->pan_id);
+
+ /* The "Universal/Local" (U/L) bit shall be set to zero */
+ eui[0] &= ~2;
+ eui[2] = 0;
+ eui[3] = 0xFF;
+ eui[4] = 0xFE;
+ eui[5] = 0;
+ ieee802154_le16_to_be16(&eui[6], &wpan_dev->short_addr);
+ return 0;
+}
+
static int lowpan_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct inet6_dev *idev;
+ struct in6_addr addr;
int i;

if (dev->type != ARPHRD_6LOWPAN)
return NOTIFY_DONE;

+ idev = __in6_dev_get(dev);
+ if (!idev)
+ return NOTIFY_DONE;
+
switch (event) {
+ case NETDEV_UP:
+ case NETDEV_CHANGE:
+ /* (802.15.4 6LoWPAN short address slaac handling */
+ if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154) &&
+ addrconf_ifid_802154_6lowpan(addr.s6_addr + 8, dev) == 0) {
+ __ipv6_addr_set_half(&addr.s6_addr32[0],
+ htonl(0xFE800000), 0);
+ addrconf_add_linklocal(idev, &addr, 0);
+ }
+ break;
case NETDEV_DOWN:
for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
clear_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE,
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 27aed1a..54e18c2 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -2948,8 +2948,8 @@ static void init_loopback(struct net_device *dev)
}
}

-static void addrconf_add_linklocal(struct inet6_dev *idev,
- const struct in6_addr *addr, u32 flags)
+void addrconf_add_linklocal(struct inet6_dev *idev,
+ const struct in6_addr *addr, u32 flags)
{
struct inet6_ifaddr *ifp;
u32 addr_flags = flags | IFA_F_PERMANENT;
@@ -2968,6 +2968,7 @@ static void addrconf_add_linklocal(struct inet6_dev *idev,
in6_ifa_put(ifp);
}
}
+EXPORT_SYMBOL(addrconf_add_linklocal);

static bool ipv6_reserved_interfaceid(struct in6_addr address)
{
--
2.8.0

2016-04-20 08:19:33

by Alexander Aring

[permalink] [raw]
Subject: [PATCHv2 bluetooth-next 01/10] 6lowpan: add private neighbour data

This patch will introduce a 6lowpan neighbour private data. Like the
interface private data we handle private data for generic 6lowpan and
for link-layer specific 6lowpan.

The current first use case if to save the short address for a 802.15.4
6lowpan neighbour.

Cc: David S. Miller <[email protected]>
Signed-off-by: Alexander Aring <[email protected]>
---
include/linux/netdevice.h | 3 +--
include/net/6lowpan.h | 24 ++++++++++++++++++++++++
net/bluetooth/6lowpan.c | 2 ++
net/ieee802154/6lowpan/core.c | 12 ++++++++++++
4 files changed, 39 insertions(+), 2 deletions(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 166402a..0052c42 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1487,8 +1487,7 @@ enum netdev_priv_flags {
* @perm_addr: Permanent hw address
* @addr_assign_type: Hw address assignment type
* @addr_len: Hardware address length
- * @neigh_priv_len; Used in neigh_alloc(),
- * initialized only in atm/clip.c
+ * @neigh_priv_len; Used in neigh_alloc()
* @dev_id: Used to differentiate devices that share
* the same link layer address
* @dev_port: Used to differentiate devices that share
diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
index da84cf9..61c6517 100644
--- a/include/net/6lowpan.h
+++ b/include/net/6lowpan.h
@@ -98,6 +98,9 @@ static inline bool lowpan_is_iphc(u8 dispatch)
#define LOWPAN_PRIV_SIZE(llpriv_size) \
(sizeof(struct lowpan_dev) + llpriv_size)

+#define LOWPAN_NEIGH_PRIV_SIZE(llneigh_priv_size) \
+ (sizeof(struct lowpan_neigh) + llneigh_priv_size)
+
enum lowpan_lltypes {
LOWPAN_LLTYPE_BTLE,
LOWPAN_LLTYPE_IEEE802154,
@@ -141,6 +144,27 @@ struct lowpan_dev {
u8 priv[0] __aligned(sizeof(void *));
};

+struct lowpan_neigh {
+ /* 6LoWPAN neigh private data */
+ /* must be last */
+ u8 priv[0] __aligned(sizeof(void *));
+};
+
+struct lowpan_802154_neigh {
+ __le16 short_addr;
+};
+
+static inline struct lowpan_neigh *lowpan_neigh(void *neigh_priv)
+{
+ return neigh_priv;
+}
+
+static inline
+struct lowpan_802154_neigh *lowpan_802154_neigh(void *neigh_priv)
+{
+ return (struct lowpan_802154_neigh *)lowpan_neigh(neigh_priv)->priv;
+}
+
static inline
struct lowpan_dev *lowpan_dev(const struct net_device *dev)
{
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 38e82dd..b7c4efa 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -833,6 +833,8 @@ static int setup_netdev(struct l2cap_chan *chan, struct lowpan_btle_dev **dev)
list_add_rcu(&(*dev)->list, &bt_6lowpan_devices);
spin_unlock(&devices_lock);

+ netdev->neigh_priv_len = LOWPAN_NEIGH_PRIV_SIZE(0);
+
err = lowpan_register_netdev(netdev, LOWPAN_LLTYPE_BTLE);
if (err < 0) {
BT_INFO("register_netdev failed %d", err);
diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c
index dd085db..3162632 100644
--- a/net/ieee802154/6lowpan/core.c
+++ b/net/ieee802154/6lowpan/core.c
@@ -92,11 +92,21 @@ static int lowpan_stop(struct net_device *dev)
return 0;
}

+static int lowpan_neigh_construct(struct neighbour *n)
+{
+ struct lowpan_802154_neigh *neigh = lowpan_802154_neigh(neighbour_priv(n));
+
+ /* default no short_addr is available for a neighbour */
+ neigh->short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);
+ return 0;
+}
+
static const struct net_device_ops lowpan_netdev_ops = {
.ndo_init = lowpan_dev_init,
.ndo_start_xmit = lowpan_xmit,
.ndo_open = lowpan_open,
.ndo_stop = lowpan_stop,
+ .ndo_neigh_construct = lowpan_neigh_construct,
};

static void lowpan_setup(struct net_device *ldev)
@@ -161,6 +171,8 @@ static int lowpan_newlink(struct net *src_net, struct net_device *ldev,
wdev->needed_headroom;
ldev->needed_tailroom = wdev->needed_tailroom;

+ ldev->neigh_priv_len = LOWPAN_NEIGH_PRIV_SIZE(sizeof(struct lowpan_802154_neigh));
+
ret = lowpan_register_netdevice(ldev, LOWPAN_LLTYPE_IEEE802154);
if (ret < 0) {
dev_put(wdev);
--
2.8.0

2016-05-18 21:39:39

by Michael Richardson

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 04/10] ndisc: add addr_len parameter to ndisc_opt_addr_space


Alexander Aring <[email protected]> wrote:
> What I did in the patch series to store short address in neighbour
> private data, which makes everything 802.15.4 6lowpan specific.

FYI: there are a whole family of network types which have multiple
addresses (long/short). This includes:
1) BTLE - RFC 7668 IPv6 over BLUETOOTH(R) Low Energy
2) G.9959 - RFC 7428 Transmission of IPv6 Packets over ITU-T G.9959 Networks
3) BACnet and DECT will be defined. Some of these have 1 byte short
addresses...
4) the various LPWAN people might be able to do IPv6... we'll see.

I think that we the 802.15.4 specific stuff is fine for now, and we can
figure out it might need generalization later on.

--
] Never tell me the odds! | ipv6 mesh networks [
] Michael Richardson, Sandelman Software Works | network architect [
] [email protected] http://www.sandelman.ca/ | ruby on rails [


Attachments:
signature.asc (464.00 B)

2016-05-13 12:33:40

by Alexander Aring

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 00/10] 6lowpan: introduce basic 6lowpan-nd


Hi,

On 05/13/2016 04:59 AM, YOSHIFUJI Hideaki wrote:
> Hi,
>
> Marcel Holtmann wrote:
>> Hi Dave,
> :
>>> include/linux/netdevice.h | 6 +-
>>> include/net/6lowpan.h | 24 ++
>>> include/net/addrconf.h | 3 +
>>> include/net/ndisc.h | 124 ++++++++-
>>> net/6lowpan/6lowpan_i.h | 2 +
>>> net/6lowpan/Makefile | 2 +-
>>> net/6lowpan/core.c | 50 +++-
>>> net/6lowpan/iphc.c | 167 +++++++++--
>>> net/6lowpan/ndisc.c | 633 ++++++++++++++++++++++++++++++++++++++++++
>>> net/bluetooth/6lowpan.c | 2 +
>>> net/ieee802154/6lowpan/core.c | 12 +
>>> net/ieee802154/6lowpan/tx.c | 107 ++++---
>>> net/ipv6/addrconf.c | 7 +-
>>> net/ipv6/ndisc.c | 132 +++++----
>>> net/ipv6/route.c | 4 +-
>>> 15 files changed, 1117 insertions(+), 158 deletions(-)
>>> create mode 100644 net/6lowpan/ndisc.c
>>
>> is there a chance that we get input into this patch set? I wonder also if it would be acceptable to take this through bluetooth-next or should it better go straight into net-next?
>
> The core idea of introducing ndisc_ops is okay, but I do think this
> series of patches should be refactored; we should not make another
> "copy" of core of ndisc logic. We can introduce "ops" for each
> option, for example.
>

Thanks for your suggestion. I will try to make another patch series
which introduce ops for each option.

I also thinks it's better now. E.g. some days ago there was another
question on netdev for doing more ndp_proxy stuff inside the kernel [0].
If such feature will be added later then both implementation would be
touched (which almost introduce the same code).

- Alex

[0] http://marc.info/?l=linux-netdev&m=146278123414146

2016-05-13 02:59:33

by Hideaki Yoshifuji

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 00/10] 6lowpan: introduce basic 6lowpan-nd

Hi,

Marcel Holtmann wrote:
> Hi Dave,
:
>> include/linux/netdevice.h | 6 +-
>> include/net/6lowpan.h | 24 ++
>> include/net/addrconf.h | 3 +
>> include/net/ndisc.h | 124 ++++++++-
>> net/6lowpan/6lowpan_i.h | 2 +
>> net/6lowpan/Makefile | 2 +-
>> net/6lowpan/core.c | 50 +++-
>> net/6lowpan/iphc.c | 167 +++++++++--
>> net/6lowpan/ndisc.c | 633 ++++++++++++++++++++++++++++++++++++++++++
>> net/bluetooth/6lowpan.c | 2 +
>> net/ieee802154/6lowpan/core.c | 12 +
>> net/ieee802154/6lowpan/tx.c | 107 ++++---
>> net/ipv6/addrconf.c | 7 +-
>> net/ipv6/ndisc.c | 132 +++++----
>> net/ipv6/route.c | 4 +-
>> 15 files changed, 1117 insertions(+), 158 deletions(-)
>> create mode 100644 net/6lowpan/ndisc.c
>
> is there a chance that we get input into this patch set? I wonder also if it would be acceptable to take this through bluetooth-next or should it better go straight into net-next?

The core idea of introducing ndisc_ops is okay, but I do think this
series of patches should be refactored; we should not make another
"copy" of core of ndisc logic. We can introduce "ops" for each
option, for example.

Thank you.

--
Hideaki Yoshifuji <[email protected]>
Technical Division, MIRACLE LINUX CORPORATION

2016-05-08 10:46:27

by Alexander Aring

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 08/10] ipv6: export ndisc functions


Hi,

On 05/02/2016 09:39 PM, Hannes Frederic Sowa wrote:
> On 20.04.2016 10:19, Alexander Aring wrote:
>> This patch exports some neighbour discovery functions which can be used
>> by 6lowpan neighbour discovery ops functionality then.
>>
>> 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 | 16 ++++++++++++++++
>> net/ipv6/addrconf.c | 1 +
>> net/ipv6/ndisc.c | 28 ++++++++++------------------
>> 3 files changed, 27 insertions(+), 18 deletions(-)
>>
>> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
>> index 14ed016..35a4396 100644
>> --- a/include/net/ndisc.h
>> +++ b/include/net/ndisc.h
>> @@ -53,6 +53,15 @@ enum {
>>
>> #include <net/neighbour.h>
>>
>> +/* Set to 3 to get tracing... */
>> +#define ND_DEBUG 1
>> +
>> +#define ND_PRINTK(val, level, fmt, ...) \
>> +do { \
>> + if (val <= ND_DEBUG) \
>> + net_##level##_ratelimited(fmt, ##__VA_ARGS__); \
>> +} while (0)
>> +
>
> If the debug messages are well thought out, I think we could install
> them always on debug level.
>
> There are valid users now, so
>

Not sure if I understand you correctly here. Is this patch is fine or I
should remove ND_DEBUG, because it's handled by "net_##level##_ratelimited"
and the debug level will be handled then by other debug level settings
then?

Thanks.

- Alex

2016-05-08 10:39:40

by Alexander Aring

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 04/10] ndisc: add addr_len parameter to ndisc_opt_addr_space

Hi,

On 05/07/2016 12:23 AM, Hannes Frederic Sowa wrote:
> On 04.05.2016 14:30, Alexander Aring wrote:
>> Hi,
>>
>> On 05/02/2016 09:37 PM, Hannes Frederic Sowa wrote:
>>> On 20.04.2016 10:19, Alexander Aring wrote:
>>>> This patch makes the address length as argument for the
>>>> ndisc_opt_addr_space function. This is necessary to handle addresses
>>>> which don't use dev->addr_len as address length.
>>>
>>> Would it make sense for patch 4, 5 and 6 to add the operation to ndisc_ops?
>>>
>>
>> not sure if I understand this question right,
>>
>> We have now the ndisc_ops where we can could change the send/recv of
>> NS/NA, also is_useropt (for add 6CO RA is userspace option field).
>>
>> In case of 802.15.4 we have two MAC addresses with different length:
>>
>> - extended address - 8 bytes => EUI64
>> - short address - 2 bytes
>>
>> Now [0] describes how to make the source/target address option for
>> NS/NA/RS/RA/... to deal with both addresses.
>>
>> The short address is a special case in 802.15.4 and not always
>> available. If available we add both addresses as option field in
>> NS/NA (RS/RA will follow in future, but currently NS/NA only).
>>
>> At this point the understanding of [0] differs in 6LoWPAN
>> implementations.
>>
>> Some people handles it like:
>>
>> Handle the short address/extended address in XOR case of 6LoWPAN
>> interface. The interface has as MAC address the extended XOR short (if
>> available), depends on setting.
>>
>> Then dev->addr_len is 8 XOR 2.
>>
>> Other people (inclusive me) handle it like:
>>
>> Handle the short/extended address in case of OR, but never short address
>> alone. The interface can be accessed by extended address or short
>> address and each neighbour stores both information.
>>
>> The case "short address never alone" means that the extended address is
>> always available and MUST be there.
>>
>> Furthermore, depends on L3 addressing it could be useful to have the
>> possibility to decide if using or short OR extended address as L2 address
>> for do better compressing stuff.
>>
>> ---
>>
>> I implement it as OR case, so we add both addresses when short address
>> is available. Also we drop NS/NA when the short address is given only,
>> in theory we could also react on this and store a "dummy" 0x00..00
>> address for extended address then.
>>
>> Not sure how it need to be handled correctly, for now I implemented how
>> I understand it.
>>
>> In case of the OR case, we need to add two option fields for the
>> address, extended and short. This is why I do the calculation stuff more
>> accessible with different address lengths, so we can use 8 or 2 and not
>> dev->addr_len which stores always the 802.15.4 EUI64 address length.
>>
>> And the answer would be, no it makes no sense because we need to call
>> these functions with 8 (dev->addr_len) and 2 (if short addr is
>> available).
>
> I had to understand the usage in patch 9. It seems you are right, the
> decision cannot be done based on the protocol alone but based on the
> context, so we need to pass in different lengths based on the context.
> Thanks for your explanation.
>
> I would still suggest to not use net_device as an argument but just the
> type and length to keep the API cleaner, but this is not a strong opinion.
>

yes, I agree. I will try to change it without any dev argument.

The general question would also here, if we just not "simple" support
for general IPv6 implementation a mapping where:

L2+ (in meaning of one or more) addresses <-> one L3 address

Which means e.g. two ethernet addresses can be mapped to one L3 address.
I don't know if this makes sense somehow, but I have such use-case for
802.15.4 but it's even complicated because different address lengths.

Additional to this feature, we add also supporting of multiple L2
addresses for a net_device with different address lengths.

---

I wrote "simple" but it isn't simple, but I suppose the truly mainline
solution.

What I did in the patch series to store short address in neighbour
private data, which makes everything 802.15.4 6lowpan specific. We
currently store the short address as part of 802.15.4 interface type
in netdev_priv also.

This requires lot of changes which are not easy, also dev_hard_header
callback does not contain any length information in context when calling
this function. Btw: 0xffff (short address) in 802.15.4 is the broadcast
address, which currently works because a workaround by mapping 0xff..ff
dev->broadcast (8 bytes) to 0xffff short.

Big question is also if it's valid to make such mapping in all link-layers
e.g. ethernet, because Linux filters multiple target/source link address
option fields in NS/NA/RS/etc. Anyway could be also some flag if valid
or not.

I think this more something for future, because it's really big work. :-)

- Alex

2016-05-06 22:23:20

by Hannes Frederic Sowa

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 04/10] ndisc: add addr_len parameter to ndisc_opt_addr_space

On 04.05.2016 14:30, Alexander Aring wrote:
> Hi,
>
> On 05/02/2016 09:37 PM, Hannes Frederic Sowa wrote:
>> On 20.04.2016 10:19, Alexander Aring wrote:
>>> This patch makes the address length as argument for the
>>> ndisc_opt_addr_space function. This is necessary to handle addresses
>>> which don't use dev->addr_len as address length.
>>
>> Would it make sense for patch 4, 5 and 6 to add the operation to ndisc_ops?
>>
>
> not sure if I understand this question right,
>
> We have now the ndisc_ops where we can could change the send/recv of
> NS/NA, also is_useropt (for add 6CO RA is userspace option field).
>
> In case of 802.15.4 we have two MAC addresses with different length:
>
> - extended address - 8 bytes => EUI64
> - short address - 2 bytes
>
> Now [0] describes how to make the source/target address option for
> NS/NA/RS/RA/... to deal with both addresses.
>
> The short address is a special case in 802.15.4 and not always
> available. If available we add both addresses as option field in
> NS/NA (RS/RA will follow in future, but currently NS/NA only).
>
> At this point the understanding of [0] differs in 6LoWPAN
> implementations.
>
> Some people handles it like:
>
> Handle the short address/extended address in XOR case of 6LoWPAN
> interface. The interface has as MAC address the extended XOR short (if
> available), depends on setting.
>
> Then dev->addr_len is 8 XOR 2.
>
> Other people (inclusive me) handle it like:
>
> Handle the short/extended address in case of OR, but never short address
> alone. The interface can be accessed by extended address or short
> address and each neighbour stores both information.
>
> The case "short address never alone" means that the extended address is
> always available and MUST be there.
>
> Furthermore, depends on L3 addressing it could be useful to have the
> possibility to decide if using or short OR extended address as L2 address
> for do better compressing stuff.
>
> ---
>
> I implement it as OR case, so we add both addresses when short address
> is available. Also we drop NS/NA when the short address is given only,
> in theory we could also react on this and store a "dummy" 0x00..00
> address for extended address then.
>
> Not sure how it need to be handled correctly, for now I implemented how
> I understand it.
>
> In case of the OR case, we need to add two option fields for the
> address, extended and short. This is why I do the calculation stuff more
> accessible with different address lengths, so we can use 8 or 2 and not
> dev->addr_len which stores always the 802.15.4 EUI64 address length.
>
> And the answer would be, no it makes no sense because we need to call
> these functions with 8 (dev->addr_len) and 2 (if short addr is
> available).

I had to understand the usage in patch 9. It seems you are right, the
decision cannot be done based on the protocol alone but based on the
context, so we need to pass in different lengths based on the context.
Thanks for your explanation.

I would still suggest to not use net_device as an argument but just the
type and length to keep the API cleaner, but this is not a strong opinion.

Thanks,
Hannes

2016-05-06 10:19:14

by Alexander Aring

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 07/10] ipv6: introduce neighbour discovery ops


Hi,

On 05/06/2016 11:47 AM, Alexander Aring wrote:
>
> Hi,
>
> On 05/04/2016 02:23 PM, Stefan Schmidt wrote:
>> Hello.
>>
>> On 20/04/16 10:19, Alexander Aring wrote:
>>> This patch introduces neighbour discovery ops callback structure. The
>>> structure contains at first receive and transmit handling for NS/NA and
>>> userspace option field functionality.
>>>
>>> These callback offers 6lowpan different handling, such as 802.15.4 short
>>> address handling or RFC6775 (Neighbor Discovery Optimization for IPv6 over
>>> 6LoWPANs).
>>>
>>> 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/linux/netdevice.h | 3 ++
>>> include/net/ndisc.h | 96 +++++++++++++++++++++++++++++++++++++++++++----
>>> net/ipv6/addrconf.c | 1 +
>>> net/ipv6/ndisc.c | 71 ++++++++++++++++++++++++-----------
>>> net/ipv6/route.c | 2 +-
>>> 5 files changed, 144 insertions(+), 29 deletions(-)
>>>
>>> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
>>> index 0052c42..bc60033 100644
>>> --- a/include/linux/netdevice.h
>>> +++ b/include/linux/netdevice.h
>>> @@ -1677,6 +1677,9 @@ struct net_device {
>>> #ifdef CONFIG_NET_L3_MASTER_DEV
>>> const struct l3mdev_ops *l3mdev_ops;
>>> #endif
>>> +#if IS_ENABLED(CONFIG_IPV6)
>>> + const struct ndisc_ops *ndisc_ops;
>>> +#endif
>>> const struct header_ops *header_ops;
>>> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
>>> index aac868e..14ed016 100644
>>> --- a/include/net/ndisc.h
>>> +++ b/include/net/ndisc.h
>>> @@ -110,7 +110,8 @@ struct ndisc_options {
>>> #define NDISC_OPT_SPACE(len) (((len)+2+7)&~7)
>>> -struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
>>> +struct ndisc_options *ndisc_parse_options(const struct net_device *dev,
>>> + u8 *opt, int opt_len,
>>> struct ndisc_options *ndopts);
>>> /*
>>> @@ -173,6 +174,93 @@ static inline struct neighbour *__ipv6_neigh_lookup(struct net_device *dev, cons
>>> return n;
>>> }
>>> +static inline int __ip6_ndisc_is_useropt(struct nd_opt_hdr *opt)
>>
>> Name it __ipv6... instead of __ip6...?
>
> I had ipv6 before, but then I saw ip6... prefixes functionality in
> ndisc.c and changed it to ip6...., but both seems to be used.
>
> See "ip6_nd_hdr".
>
>>> +{
>>> + return opt->nd_opt_type == ND_OPT_RDNSS ||
>>> + opt->nd_opt_type == ND_OPT_DNSSL;
>>> +}
>>> +
>>> +#if IS_ENABLED(CONFIG_IPV6)
>>> +struct ndisc_ops {
>>> + int (*is_useropt)(struct nd_opt_hdr *opt);
>>> + void (*send_na)(struct net_device *dev,
>>> + const struct in6_addr *daddr,
>>> + const struct in6_addr *solicited_addr,
>>> + bool router, bool solicited,
>>> + bool override, bool inc_opt);
>>> + void (*recv_na)(struct sk_buff *skb);
>>> + void (*send_ns)(struct net_device *dev,
>>> + const struct in6_addr *solicit,
>>> + const struct in6_addr *daddr,
>>> + const struct in6_addr *saddr);
>>> + void (*recv_ns)(struct sk_buff *skb);
>>> +};
>>> +
>>> +static inline int ndisc_is_useropt(const struct net_device *dev,
>>> + struct nd_opt_hdr *opt)
>>> +{
>>> + if (likely(dev->ndisc_ops->is_useropt))
>>> + return dev->ndisc_ops->is_useropt(opt);
>>> + else
>>> + return 0;
>>> +}
>>> +
>>> +static inline void ndisc_send_na(struct net_device *dev,
>>> + const struct in6_addr *daddr,
>>> + const struct in6_addr *solicited_addr,
>>> + bool router, bool solicited, bool override,
>>> + bool inc_opt)
>>> +{
>>> + if (likely(dev->ndisc_ops->send_na))
>>> + dev->ndisc_ops->send_na(dev, daddr, solicited_addr, router,
>>> + solicited, override, inc_opt);
>>> +}
>>> +
>>> +static inline void ndisc_recv_na(struct sk_buff *skb)
>>> +{
>>> + if (likely(skb->dev->ndisc_ops->recv_na))
>>> + skb->dev->ndisc_ops->recv_na(skb);
>>> +}
>>> +
>>> +static inline void ndisc_send_ns(struct net_device *dev,
>>> + const struct in6_addr *solicit,
>>> + const struct in6_addr *daddr,
>>> + const struct in6_addr *saddr)
>>> +{
>>> + if (likely(dev->ndisc_ops->send_ns))
>>> + dev->ndisc_ops->send_ns(dev, solicit, daddr, saddr);
>>> +}
>>> +
>>> +static inline void ndisc_recv_ns(struct sk_buff *skb)
>>> +{
>>> + if (likely(skb->dev->ndisc_ops->recv_ns))
>>> + skb->dev->ndisc_ops->recv_ns(skb);
>>> +}
>>> +#else
>>> +static inline int ndisc_is_useropt(const struct net_device *dev,
>>> + struct nd_opt_hdr *opt)
>>> +{
>>> + return 0;
>>> +}
>>> +
>>> +static inline void ndisc_send_na(struct net_device *dev,
>>> + const struct in6_addr *daddr,
>>> + const struct in6_addr *solicited_addr,
>>> + bool router, bool solicited, bool override,
>>> + bool inc_opt) { }
>>> +
>>> +static inline void ndisc_recv_na(struct sk_buff *skb) { }
>>> +
>>> +static inline void ndisc_send_ns(struct net_device *dev,
>>> + const struct in6_addr *solicit,
>>> + const struct in6_addr *daddr,
>>> + const struct in6_addr *saddr) { }
>>> +
>>> +static inline void ndisc_recv_ns(struct sk_buff *skb) { }
>>> +#endif
>>> +
>>> +void ip6_register_ndisc_ops(struct net_device *dev);
>>> +
>>> int ndisc_init(void);
>>> int ndisc_late_init(void);
>>> @@ -181,14 +269,8 @@ void ndisc_cleanup(void);
>>> int ndisc_rcv(struct sk_buff *skb);
>>> -void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
>>> - const struct in6_addr *daddr, const struct in6_addr *saddr);
>>> -
>>> void ndisc_send_rs(struct net_device *dev,
>>> const struct in6_addr *saddr, const struct in6_addr *daddr);
>>> -void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
>>> - const struct in6_addr *solicited_addr,
>>> - bool router, bool solicited, bool override, bool inc_opt);
>>> void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target);
>>> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
>>> index 54e18c2..a2ef04b 100644
>>> --- a/net/ipv6/addrconf.c
>>> +++ b/net/ipv6/addrconf.c
>>> @@ -3266,6 +3266,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
>>> idev = ipv6_add_dev(dev);
>>> if (IS_ERR(idev))
>>> return notifier_from_errno(PTR_ERR(idev));
>>> + ip6_register_ndisc_ops(dev);
>>> }
>>> break;
>>> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
>>> index 176c7c4..297080a 100644
>>> --- a/net/ipv6/ndisc.c
>>> +++ b/net/ipv6/ndisc.c
>>> @@ -185,24 +185,25 @@ static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
>>> return cur <= end && cur->nd_opt_type == type ? cur : NULL;
>>> }
>>> -static inline int ndisc_is_useropt(struct nd_opt_hdr *opt)
>>> +static inline int ip6_ndisc_is_useropt(struct nd_opt_hdr *opt)
>>> {
>>> - return opt->nd_opt_type == ND_OPT_RDNSS ||
>>> - opt->nd_opt_type == ND_OPT_DNSSL;
>>> + return __ip6_ndisc_is_useropt(opt);
>>
>> Why putting this check into a different function? It looks like a not needed redirection.
>
> because these IPv6 userspace options are valid in 6LoWPAN-ND and IPv6-ND.
>
> In 6LoWPAN we do:
>
> static inline int lowpan_ndisc_is_useropt(struct nd_opt_hdr *opt)
> {
> return __ip6_ndisc_is_useropt(opt) || opt->nd_opt_type == ND_OPT_6CO;
> }
>
> I don't believe that _any_ IPv6 implementation in userspace can do something
> with 6CO option fields. For this reason ARPHRD_6LOWPAN should make them
> valid for userspace only. (btw: the radvd maintainer wants to add some
> patches for 6CO processing support).
>

Okay, I remove the different function and add an EXPORT_SYMBOL. Then the
6LoWPAN-ND is_useropt callback can also use this function instead using
the __ip6_ndisc_is_useropt inline function.

- Alex

2016-05-06 09:47:11

by Alexander Aring

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 07/10] ipv6: introduce neighbour discovery ops


Hi,

On 05/04/2016 02:23 PM, Stefan Schmidt wrote:
> Hello.
>
> On 20/04/16 10:19, Alexander Aring wrote:
>> This patch introduces neighbour discovery ops callback structure. The
>> structure contains at first receive and transmit handling for NS/NA and
>> userspace option field functionality.
>>
>> These callback offers 6lowpan different handling, such as 802.15.4 short
>> address handling or RFC6775 (Neighbor Discovery Optimization for IPv6 over
>> 6LoWPANs).
>>
>> 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/linux/netdevice.h | 3 ++
>> include/net/ndisc.h | 96 +++++++++++++++++++++++++++++++++++++++++++----
>> net/ipv6/addrconf.c | 1 +
>> net/ipv6/ndisc.c | 71 ++++++++++++++++++++++++-----------
>> net/ipv6/route.c | 2 +-
>> 5 files changed, 144 insertions(+), 29 deletions(-)
>>
>> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
>> index 0052c42..bc60033 100644
>> --- a/include/linux/netdevice.h
>> +++ b/include/linux/netdevice.h
>> @@ -1677,6 +1677,9 @@ struct net_device {
>> #ifdef CONFIG_NET_L3_MASTER_DEV
>> const struct l3mdev_ops *l3mdev_ops;
>> #endif
>> +#if IS_ENABLED(CONFIG_IPV6)
>> + const struct ndisc_ops *ndisc_ops;
>> +#endif
>> const struct header_ops *header_ops;
>> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
>> index aac868e..14ed016 100644
>> --- a/include/net/ndisc.h
>> +++ b/include/net/ndisc.h
>> @@ -110,7 +110,8 @@ struct ndisc_options {
>> #define NDISC_OPT_SPACE(len) (((len)+2+7)&~7)
>> -struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
>> +struct ndisc_options *ndisc_parse_options(const struct net_device *dev,
>> + u8 *opt, int opt_len,
>> struct ndisc_options *ndopts);
>> /*
>> @@ -173,6 +174,93 @@ static inline struct neighbour *__ipv6_neigh_lookup(struct net_device *dev, cons
>> return n;
>> }
>> +static inline int __ip6_ndisc_is_useropt(struct nd_opt_hdr *opt)
>
> Name it __ipv6... instead of __ip6...?

I had ipv6 before, but then I saw ip6... prefixes functionality in
ndisc.c and changed it to ip6...., but both seems to be used.

See "ip6_nd_hdr".

>> +{
>> + return opt->nd_opt_type == ND_OPT_RDNSS ||
>> + opt->nd_opt_type == ND_OPT_DNSSL;
>> +}
>> +
>> +#if IS_ENABLED(CONFIG_IPV6)
>> +struct ndisc_ops {
>> + int (*is_useropt)(struct nd_opt_hdr *opt);
>> + void (*send_na)(struct net_device *dev,
>> + const struct in6_addr *daddr,
>> + const struct in6_addr *solicited_addr,
>> + bool router, bool solicited,
>> + bool override, bool inc_opt);
>> + void (*recv_na)(struct sk_buff *skb);
>> + void (*send_ns)(struct net_device *dev,
>> + const struct in6_addr *solicit,
>> + const struct in6_addr *daddr,
>> + const struct in6_addr *saddr);
>> + void (*recv_ns)(struct sk_buff *skb);
>> +};
>> +
>> +static inline int ndisc_is_useropt(const struct net_device *dev,
>> + struct nd_opt_hdr *opt)
>> +{
>> + if (likely(dev->ndisc_ops->is_useropt))
>> + return dev->ndisc_ops->is_useropt(opt);
>> + else
>> + return 0;
>> +}
>> +
>> +static inline void ndisc_send_na(struct net_device *dev,
>> + const struct in6_addr *daddr,
>> + const struct in6_addr *solicited_addr,
>> + bool router, bool solicited, bool override,
>> + bool inc_opt)
>> +{
>> + if (likely(dev->ndisc_ops->send_na))
>> + dev->ndisc_ops->send_na(dev, daddr, solicited_addr, router,
>> + solicited, override, inc_opt);
>> +}
>> +
>> +static inline void ndisc_recv_na(struct sk_buff *skb)
>> +{
>> + if (likely(skb->dev->ndisc_ops->recv_na))
>> + skb->dev->ndisc_ops->recv_na(skb);
>> +}
>> +
>> +static inline void ndisc_send_ns(struct net_device *dev,
>> + const struct in6_addr *solicit,
>> + const struct in6_addr *daddr,
>> + const struct in6_addr *saddr)
>> +{
>> + if (likely(dev->ndisc_ops->send_ns))
>> + dev->ndisc_ops->send_ns(dev, solicit, daddr, saddr);
>> +}
>> +
>> +static inline void ndisc_recv_ns(struct sk_buff *skb)
>> +{
>> + if (likely(skb->dev->ndisc_ops->recv_ns))
>> + skb->dev->ndisc_ops->recv_ns(skb);
>> +}
>> +#else
>> +static inline int ndisc_is_useropt(const struct net_device *dev,
>> + struct nd_opt_hdr *opt)
>> +{
>> + return 0;
>> +}
>> +
>> +static inline void ndisc_send_na(struct net_device *dev,
>> + const struct in6_addr *daddr,
>> + const struct in6_addr *solicited_addr,
>> + bool router, bool solicited, bool override,
>> + bool inc_opt) { }
>> +
>> +static inline void ndisc_recv_na(struct sk_buff *skb) { }
>> +
>> +static inline void ndisc_send_ns(struct net_device *dev,
>> + const struct in6_addr *solicit,
>> + const struct in6_addr *daddr,
>> + const struct in6_addr *saddr) { }
>> +
>> +static inline void ndisc_recv_ns(struct sk_buff *skb) { }
>> +#endif
>> +
>> +void ip6_register_ndisc_ops(struct net_device *dev);
>> +
>> int ndisc_init(void);
>> int ndisc_late_init(void);
>> @@ -181,14 +269,8 @@ void ndisc_cleanup(void);
>> int ndisc_rcv(struct sk_buff *skb);
>> -void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
>> - const struct in6_addr *daddr, const struct in6_addr *saddr);
>> -
>> void ndisc_send_rs(struct net_device *dev,
>> const struct in6_addr *saddr, const struct in6_addr *daddr);
>> -void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
>> - const struct in6_addr *solicited_addr,
>> - bool router, bool solicited, bool override, bool inc_opt);
>> void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target);
>> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
>> index 54e18c2..a2ef04b 100644
>> --- a/net/ipv6/addrconf.c
>> +++ b/net/ipv6/addrconf.c
>> @@ -3266,6 +3266,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
>> idev = ipv6_add_dev(dev);
>> if (IS_ERR(idev))
>> return notifier_from_errno(PTR_ERR(idev));
>> + ip6_register_ndisc_ops(dev);
>> }
>> break;
>> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
>> index 176c7c4..297080a 100644
>> --- a/net/ipv6/ndisc.c
>> +++ b/net/ipv6/ndisc.c
>> @@ -185,24 +185,25 @@ static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
>> return cur <= end && cur->nd_opt_type == type ? cur : NULL;
>> }
>> -static inline int ndisc_is_useropt(struct nd_opt_hdr *opt)
>> +static inline int ip6_ndisc_is_useropt(struct nd_opt_hdr *opt)
>> {
>> - return opt->nd_opt_type == ND_OPT_RDNSS ||
>> - opt->nd_opt_type == ND_OPT_DNSSL;
>> + return __ip6_ndisc_is_useropt(opt);
>
> Why putting this check into a different function? It looks like a not needed redirection.

because these IPv6 userspace options are valid in 6LoWPAN-ND and IPv6-ND.

In 6LoWPAN we do:

static inline int lowpan_ndisc_is_useropt(struct nd_opt_hdr *opt)
{
return __ip6_ndisc_is_useropt(opt) || opt->nd_opt_type == ND_OPT_6CO;
}

I don't believe that _any_ IPv6 implementation in userspace can do something
with 6CO option fields. For this reason ARPHRD_6LOWPAN should make them
valid for userspace only. (btw: the radvd maintainer wants to add some
patches for 6CO processing support).

- Alex

2016-05-06 09:36:14

by Alexander Aring

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 07/10] ipv6: introduce neighbour discovery ops


Hi,

On 05/03/2016 08:17 PM, Stefan Schmidt wrote:
> Hello.
>
> On 02/05/16 21:36, Hannes Frederic Sowa wrote:
>> On 20.04.2016 10:19, Alexander Aring wrote:
>>> This patch introduces neighbour discovery ops callback structure. The
>>> structure contains at first receive and transmit handling for NS/NA and
>>> userspace option field functionality.
>>>
>>> These callback offers 6lowpan different handling, such as 802.15.4 short
>>> address handling or RFC6775 (Neighbor Discovery Optimization for IPv6 over
>>> 6LoWPANs).
>>>
>>> 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/linux/netdevice.h | 3 ++
>>> include/net/ndisc.h | 96 +++++++++++++++++++++++++++++++++++++++++++----
>>> net/ipv6/addrconf.c | 1 +
>>> net/ipv6/ndisc.c | 71 ++++++++++++++++++++++++-----------
>>> net/ipv6/route.c | 2 +-
>>> 5 files changed, 144 insertions(+), 29 deletions(-)
>>>
>>> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
>>> index 0052c42..bc60033 100644
>>> --- a/include/linux/netdevice.h
>>> +++ b/include/linux/netdevice.h
>>> @@ -1677,6 +1677,9 @@ struct net_device {
>>> #ifdef CONFIG_NET_L3_MASTER_DEV
>>> const struct l3mdev_ops *l3mdev_ops;
>>> #endif
>>> +#if IS_ENABLED(CONFIG_IPV6)
>>> + const struct ndisc_ops *ndisc_ops;
>>> +#endif
>>> const struct header_ops *header_ops;
>>> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
>>> index aac868e..14ed016 100644
>>> --- a/include/net/ndisc.h
>>> +++ b/include/net/ndisc.h
>>> @@ -110,7 +110,8 @@ struct ndisc_options {
>>> #define NDISC_OPT_SPACE(len) (((len)+2+7)&~7)
>>> -struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
>>> +struct ndisc_options *ndisc_parse_options(const struct net_device *dev,
>>> + u8 *opt, int opt_len,
>>> struct ndisc_options *ndopts);
>>> /*
>>> @@ -173,6 +174,93 @@ static inline struct neighbour *__ipv6_neigh_lookup(struct net_device *dev, cons
>>> return n;
>>> }
>>> +static inline int __ip6_ndisc_is_useropt(struct nd_opt_hdr *opt)
>>> +{
>>> + return opt->nd_opt_type == ND_OPT_RDNSS ||
>>> + opt->nd_opt_type == ND_OPT_DNSSL;
>>> +}
>>> +
>>> +#if IS_ENABLED(CONFIG_IPV6)
>>> +struct ndisc_ops {
>>> + int (*is_useropt)(struct nd_opt_hdr *opt);
>>> + void (*send_na)(struct net_device *dev,
>>> + const struct in6_addr *daddr,
>>> + const struct in6_addr *solicited_addr,
>>> + bool router, bool solicited,
>>> + bool override, bool inc_opt);
>>> + void (*recv_na)(struct sk_buff *skb);
>>> + void (*send_ns)(struct net_device *dev,
>>> + const struct in6_addr *solicit,
>>> + const struct in6_addr *daddr,
>>> + const struct in6_addr *saddr);
>>> + void (*recv_ns)(struct sk_buff *skb);
>>> +};
>>> +
>>> +static inline int ndisc_is_useropt(const struct net_device *dev,
>>> + struct nd_opt_hdr *opt)
>>> +{
>>> + if (likely(dev->ndisc_ops->is_useropt))
>>> + return dev->ndisc_ops->is_useropt(opt);
>>> + else
>>> + return 0;
>>> +}
>>> +
>>> +static inline void ndisc_send_na(struct net_device *dev,
>>> + const struct in6_addr *daddr,
>>> + const struct in6_addr *solicited_addr,
>>> + bool router, bool solicited, bool override,
>>> + bool inc_opt)
>>> +{
>>> + if (likely(dev->ndisc_ops->send_na))
>>> + dev->ndisc_ops->send_na(dev, daddr, solicited_addr, router,
>>> + solicited, override, inc_opt);
>>> +}
>>> +
>>> +static inline void ndisc_recv_na(struct sk_buff *skb)
>>> +{
>>> + if (likely(skb->dev->ndisc_ops->recv_na))
>>> + skb->dev->ndisc_ops->recv_na(skb);
>>> +}
>>> +
>>> +static inline void ndisc_send_ns(struct net_device *dev,
>>> + const struct in6_addr *solicit,
>>> + const struct in6_addr *daddr,
>>> + const struct in6_addr *saddr)
>>> +{
>>> + if (likely(dev->ndisc_ops->send_ns))
>>> + dev->ndisc_ops->send_ns(dev, solicit, daddr, saddr);
>>> +}
>>> +
>>> +static inline void ndisc_recv_ns(struct sk_buff *skb)
>>> +{
>>> + if (likely(skb->dev->ndisc_ops->recv_ns))
>>> + skb->dev->ndisc_ops->recv_ns(skb);
>>> +}
>>> +#else
>>> +static inline int ndisc_is_useropt(const struct net_device *dev,
>>> + struct nd_opt_hdr *opt)
>>> +{
>>> + return 0;
>>> +}
>>> +
>>> +static inline void ndisc_send_na(struct net_device *dev,
>>> + const struct in6_addr *daddr,
>>> + const struct in6_addr *solicited_addr,
>>> + bool router, bool solicited, bool override,
>>> + bool inc_opt) { }
>>> +
>>> +static inline void ndisc_recv_na(struct sk_buff *skb) { }
>>> +
>>> +static inline void ndisc_send_ns(struct net_device *dev,
>>> + const struct in6_addr *solicit,
>>> + const struct in6_addr *daddr,
>>> + const struct in6_addr *saddr) { }
>>> +
>>> +static inline void ndisc_recv_ns(struct sk_buff *skb) { }
>>> +#endif
>> Do those empty functions actually make sense? I wonder a bit because
>> 6lowpan strictly depends on ipv6 and they should never be called without
>> IPv6, no?
>
> Agreed. 6LoWAPN is only an adaptation layer so we know that IPv6 must be enabled here. I would also argue for removing this ifdef and the empty functions.
>

I removed the empty function, removing ifdef is not possible. There are
still somewhere net/ipv6.h includes at do_mounts/networking filesystems.
I need to leave the ifdef there otherwise compiling will fail that
"ndisc->ops" are not available.

I tested it with several randconfigs.

- Alex

2016-05-06 09:26:21

by Alexander Aring

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 02/10] 6lowpan: add 802.15.4 short addr slaac


Hi,

On 05/03/2016 08:16 PM, Stefan Schmidt wrote:
> Hello.
>
> On 20/04/16 10:19, Alexander Aring wrote:
>> This patch adds the autoconfiguration if a valid 802.15.4 short address
>> is available for 802.15.4 6LoWPAN interfaces.
>>
>> 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/addrconf.h | 3 +++
>> net/6lowpan/core.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
>> net/ipv6/addrconf.c | 5 +++--
>> 3 files changed, 52 insertions(+), 2 deletions(-)
>>
>> diff --git a/include/net/addrconf.h b/include/net/addrconf.h
>> index 730d856..b1774eb 100644
>> --- a/include/net/addrconf.h
>> +++ b/include/net/addrconf.h
>> @@ -94,6 +94,9 @@ int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2,
>> void addrconf_join_solict(struct net_device *dev, const struct in6_addr *addr);
>> void addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr *addr);
>> +void addrconf_add_linklocal(struct inet6_dev *idev,
>> + const struct in6_addr *addr, u32 flags);
>> +
>> static inline int addrconf_ifid_eui48(u8 *eui, struct net_device *dev)
>> {
>> if (dev->addr_len != ETH_ALEN)
>> diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
>> index 7a240b3..fbae31e 100644
>> --- a/net/6lowpan/core.c
>> +++ b/net/6lowpan/core.c
>> @@ -14,6 +14,7 @@
>> #include <linux/module.h>
>> #include <net/6lowpan.h>
>> +#include <net/addrconf.h>
>> #include "6lowpan_i.h"
>> @@ -72,16 +73,61 @@ void lowpan_unregister_netdev(struct net_device *dev)
>> }
>> EXPORT_SYMBOL(lowpan_unregister_netdev);
>> +static int addrconf_ifid_802154_6lowpan(u8 *eui, struct net_device *dev)
>> +{
>> + struct wpan_dev *wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr;
>> +
>> + /* Set short_addr autoconfiguration if short_addr is present only */
>> + if (!ieee802154_is_valid_src_short_addr(wpan_dev->short_addr))
>> + return -1;
>
> -EINVAL instead of -1?
>

The ipv6 implementation do that for the "dev->addr" slaac the same here.
I think the reason is because here, if this fails for any reason then
this is simple not an error, we just don't add a link-local for short
addr slaac here and don't abort completely the CHANGE/UP of interface.

IPv6 also use "-1" return value only if parsing fails, I would to see
that also for iphc stuff where we mixed a lot of "-EIO" and "-EINVAL",
anyway nobody will really care about that return value.

It just means: 0 parsing successful and -1 parsing failed.

- Alex

2016-05-04 12:30:43

by Alexander Aring

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 04/10] ndisc: add addr_len parameter to ndisc_opt_addr_space

Hi,

On 05/02/2016 09:37 PM, Hannes Frederic Sowa wrote:
> On 20.04.2016 10:19, Alexander Aring wrote:
>> This patch makes the address length as argument for the
>> ndisc_opt_addr_space function. This is necessary to handle addresses
>> which don't use dev->addr_len as address length.
>
> Would it make sense for patch 4, 5 and 6 to add the operation to ndisc_ops?
>

not sure if I understand this question right,

We have now the ndisc_ops where we can could change the send/recv of
NS/NA, also is_useropt (for add 6CO RA is userspace option field).

In case of 802.15.4 we have two MAC addresses with different length:

- extended address - 8 bytes => EUI64
- short address - 2 bytes

Now [0] describes how to make the source/target address option for
NS/NA/RS/RA/... to deal with both addresses.

The short address is a special case in 802.15.4 and not always
available. If available we add both addresses as option field in
NS/NA (RS/RA will follow in future, but currently NS/NA only).

At this point the understanding of [0] differs in 6LoWPAN
implementations.

Some people handles it like:

Handle the short address/extended address in XOR case of 6LoWPAN
interface. The interface has as MAC address the extended XOR short (if
available), depends on setting.

Then dev->addr_len is 8 XOR 2.

Other people (inclusive me) handle it like:

Handle the short/extended address in case of OR, but never short address
alone. The interface can be accessed by extended address or short
address and each neighbour stores both information.

The case "short address never alone" means that the extended address is
always available and MUST be there.

Furthermore, depends on L3 addressing it could be useful to have the
possibility to decide if using or short OR extended address as L2 address
for do better compressing stuff.

---

I implement it as OR case, so we add both addresses when short address
is available. Also we drop NS/NA when the short address is given only,
in theory we could also react on this and store a "dummy" 0x00..00
address for extended address then.

Not sure how it need to be handled correctly, for now I implemented how
I understand it.

In case of the OR case, we need to add two option fields for the
address, extended and short. This is why I do the calculation stuff more
accessible with different address lengths, so we can use 8 or 2 and not
dev->addr_len which stores always the 802.15.4 EUI64 address length.

And the answer would be, no it makes no sense because we need to call
these functions with 8 (dev->addr_len) and 2 (if short addr is
available).

- Alex

[0] https://tools.ietf.org/html/rfc4944#section-8

2016-05-04 12:23:43

by Stefan Schmidt

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 10/10] 6lowpan: add support for 802.15.4 short addr handling

Hello.

On 20/04/16 10:19, Alexander Aring wrote:
> This patch adds necessary handling for use the short address for
> 802.15.4 6lowpan. It contains support for IPHC address compression
> and new matching algorithmn to decide which link layer address will be
> used for 802.15.4 frame.
>
> Signed-off-by: Alexander Aring<[email protected]>
> ---
> net/6lowpan/iphc.c | 167 ++++++++++++++++++++++++++++++++++++--------
> net/ieee802154/6lowpan/tx.c | 107 ++++++++++++++--------------
> 2 files changed, 189 insertions(+), 85 deletions(-)
>
> diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c
> index 8501dd5..aca38dc 100644
> --- a/net/6lowpan/iphc.c
> +++ b/net/6lowpan/iphc.c
> @@ -761,22 +761,75 @@ 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,
> +static inline bool
> +lowpan_iphc_compress_ctx_802154_lladdr(const struct in6_addr *ipaddr,
> + const struct lowpan_iphc_ctx *ctx,
> + const void *lladdr)
> +{
> + const struct ieee802154_addr *addr = lladdr;
> + unsigned char extended_addr[EUI64_ADDR_LEN];
> + struct in6_addr tmp = {};
> + bool lladdr_compress = false;
> +
> + switch (addr->mode) {
> + case IEEE802154_ADDR_LONG:
> + ieee802154_le64_to_be64(&extended_addr, &addr->extended_addr);
> + /* check for SAM/DAM = 11 */
> + memcpy(&tmp.s6_addr[8], &extended_addr, EUI64_ADDR_LEN);
> + /* 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))
> + lladdr_compress = true;
> + break;
> + case IEEE802154_ADDR_SHORT:
> + tmp.s6_addr[11] = 0xFF;
> + tmp.s6_addr[12] = 0xFE;
> + ieee802154_le16_to_be16(&tmp.s6_addr16[7],
> + &addr->short_addr);
> + /* context information are always used */
> + ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
> + if (ipv6_addr_equal(&tmp, ipaddr))
> + lladdr_compress = true;
> + break;
> + default:
> + /* should never handled and filtered by 802154 6lowpan */
> + WARN_ON_ONCE(1);
> + break;
> + }
> +
> + return lladdr_compress;
> +}
> +
> +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,
> 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;
> + switch (lowpan_dev(dev)->lltype) {
> + case LOWPAN_LLTYPE_IEEE802154:
> + if (lowpan_iphc_compress_ctx_802154_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);
> + /* 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;
> + }
> + break;
> }
>
> memset(&tmp, 0, sizeof(tmp));
> @@ -813,28 +866,85 @@ out:
> return dam;
> }
>
> -static u8 lowpan_compress_addr_64(u8 **hc_ptr, const struct in6_addr *ipaddr,
> +static inline bool
> +lowpan_iphc_compress_802154_lladdr(const struct in6_addr *ipaddr,
> + const void *lladdr)
> +{
> + const struct ieee802154_addr *addr = lladdr;
> + unsigned char extended_addr[EUI64_ADDR_LEN];
> + struct in6_addr tmp = {};
> + bool lladdr_compress = false;
> +
> + switch (addr->mode) {
> + case IEEE802154_ADDR_LONG:
> + ieee802154_le64_to_be64(&extended_addr, &addr->extended_addr);
> + if (is_addr_mac_addr_based(ipaddr, extended_addr))
> + lladdr_compress = true;
> + break;
> + case IEEE802154_ADDR_SHORT:
> + /* fe:80::ff:fe00:XXXX
> + * \__/
> + * short_addr
> + *
> + * Universe/Local bit is zero.
> + */
> + tmp.s6_addr[0] = 0xFE;
> + tmp.s6_addr[1] = 0x80;
> + tmp.s6_addr[11] = 0xFF;
> + tmp.s6_addr[12] = 0xFE;
> + ieee802154_le16_to_be16(&tmp.s6_addr16[7],
> + &addr->short_addr);
> + if (ipv6_addr_equal(&tmp, ipaddr))
> + lladdr_compress = true;
> + break;
> + default:
> + /* should never handled and filtered by 802154 6lowpan */
> + WARN_ON_ONCE(1);
> + break;
> + }
> +
> + return lladdr_compress;
> +}
> +
> +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_00;
> + u8 dam = LOWPAN_IPHC_DAM_01;
>
> - if (is_addr_mac_addr_based(ipaddr, lladdr)) {
> - dam = LOWPAN_IPHC_DAM_11; /* 0-bits */
> - pr_debug("address compression 0 bits\n");
> - } else if (lowpan_is_iid_16_bit_compressable(ipaddr)) {
> + switch (lowpan_dev(dev)->lltype) {
> + case LOWPAN_LLTYPE_IEEE802154:
> + if (lowpan_iphc_compress_802154_lladdr(ipaddr, lladdr)) {
> + dam = LOWPAN_IPHC_DAM_11; /* 0-bits */
> + pr_debug("address compression 0 bits\n");
> + goto out;
> + }
> + break;
> + default:
> + if (is_addr_mac_addr_based(ipaddr, lladdr)) {
> + dam = LOWPAN_IPHC_DAM_11; /* 0-bits */
> + pr_debug("address compression 0 bits\n");
> + goto out;
> + }
> + break;
> + }
> +
> + if (lowpan_is_iid_16_bit_compressable(ipaddr)) {
> /* compress IID to 16 bits xxxx::XXXX */
> lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr16[7], 2);
> dam = LOWPAN_IPHC_DAM_10; /* 16-bits */
> raw_dump_inline(NULL, "Compressed ipv6 addr is (16 bits)",
> *hc_ptr - 2, 2);
> - } else {
> - /* do not compress IID => xxxx::IID */
> - lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr16[4], 8);
> - dam = LOWPAN_IPHC_DAM_01; /* 64-bits */
> - raw_dump_inline(NULL, "Compressed ipv6 addr is (64 bits)",
> - *hc_ptr - 8, 8);
> + goto out;
> }
>
> + /* do not compress IID => xxxx::IID */
> + lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr16[4], 8);
> + raw_dump_inline(NULL, "Compressed ipv6 addr is (64 bits)",
> + *hc_ptr - 8, 8);
> +
> +out:
> +
> if (sam)
> return lowpan_iphc_dam_to_sam_value[dam];
> else
> @@ -1013,9 +1123,6 @@ int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
> iphc0 = LOWPAN_DISPATCH_IPHC;
> iphc1 = 0;
>
> - 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);
>
> @@ -1088,14 +1195,15 @@ int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
> iphc1 |= LOWPAN_IPHC_SAC;
> } else {
> if (sci) {
> - iphc1 |= lowpan_compress_ctx_addr(&hc_ptr, &hdr->saddr,
> + iphc1 |= lowpan_compress_ctx_addr(&hc_ptr, dev,
> + &hdr->saddr,
> &sci_entry, saddr,
> true);
> iphc1 |= LOWPAN_IPHC_SAC;
> } else {
> if (ipv6_saddr_type & IPV6_ADDR_LINKLOCAL &&
> lowpan_is_linklocal_zero_padded(hdr->saddr)) {
> - iphc1 |= lowpan_compress_addr_64(&hc_ptr,
> + iphc1 |= lowpan_compress_addr_64(&hc_ptr, dev,
> &hdr->saddr,
> saddr, true);
> pr_debug("source address unicast link-local %pI6c iphc1 0x%02x\n",
> @@ -1123,14 +1231,15 @@ int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
> }
> } else {
> if (dci) {
> - iphc1 |= lowpan_compress_ctx_addr(&hc_ptr, &hdr->daddr,
> + iphc1 |= lowpan_compress_ctx_addr(&hc_ptr, dev,
> + &hdr->daddr,
> &dci_entry, daddr,
> false);
> iphc1 |= LOWPAN_IPHC_DAC;
> } else {
> if (ipv6_daddr_type & IPV6_ADDR_LINKLOCAL &&
> lowpan_is_linklocal_zero_padded(hdr->daddr)) {
> - iphc1 |= lowpan_compress_addr_64(&hc_ptr,
> + iphc1 |= lowpan_compress_addr_64(&hc_ptr, dev,
> &hdr->daddr,
> daddr, false);
> pr_debug("dest address unicast link-local %pI6c iphc1 0x%02x\n",
> diff --git a/net/ieee802154/6lowpan/tx.c b/net/ieee802154/6lowpan/tx.c
> index e459afd..88c9d16 100644
> --- a/net/ieee802154/6lowpan/tx.c
> +++ b/net/ieee802154/6lowpan/tx.c
> @@ -9,6 +9,7 @@
> */
>
> #include <net/6lowpan.h>
> +#include <net/ndisc.h>
> #include <net/ieee802154_netdev.h>
> #include <net/mac802154.h>
>
> @@ -17,19 +18,9 @@
> #define LOWPAN_FRAG1_HEAD_SIZE 0x4
> #define LOWPAN_FRAGN_HEAD_SIZE 0x5
>
> -/* don't save pan id, it's intra pan */
> -struct lowpan_addr {
> - u8 mode;
> - union {
> - /* IPv6 needs big endian here */
> - __be64 extended_addr;
> - __be16 short_addr;
> - } u;
> -};
> -
> struct lowpan_addr_info {
> - struct lowpan_addr daddr;
> - struct lowpan_addr saddr;
> + struct ieee802154_addr daddr;
> + struct ieee802154_addr saddr;
> };
>
> static inline struct
> @@ -48,12 +39,14 @@ lowpan_addr_info *lowpan_skb_priv(const struct sk_buff *skb)
> * 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)
> + unsigned short type, const void *daddr,
> + const void *saddr, unsigned int len)
> {
> - const u8 *saddr = _saddr;
> - const u8 *daddr = _daddr;
> - struct lowpan_addr_info *info;
> + struct wpan_dev *wpan_dev = lowpan_802154_dev(ldev)->wdev->ieee802154_ptr;
> + struct lowpan_addr_info *info = lowpan_skb_priv(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?
> @@ -61,21 +54,44 @@ int lowpan_header_create(struct sk_buff *skb, struct net_device *ldev,
> if (type != ETH_P_IPV6)
> return 0;
>
> - if (!saddr)
> - saddr = ldev->dev_addr;
> + /* intra-pan communication */
> + info->saddr.pan_id = wpan_dev->pan_id;
> + info->daddr.pan_id = info->saddr.pan_id;
>
> - raw_dump_inline(__func__, "saddr", (unsigned char *)saddr, 8);
> - raw_dump_inline(__func__, "daddr", (unsigned char *)daddr, 8);
> + if (!memcmp(daddr, ldev->broadcast, EUI64_ADDR_LEN)) {
> + info->daddr.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
> + info->daddr.mode = IEEE802154_ADDR_SHORT;
> + } else {
> + n = neigh_lookup(&nd_tbl, &hdr->daddr, ldev);
> + if (n)
> + llneigh = lowpan_802154_neigh(neighbour_priv(n));
> +
> + if (llneigh &&
> + ieee802154_is_valid_src_short_addr(llneigh->short_addr)) {
> + info->daddr.mode = IEEE802154_ADDR_SHORT;
> + info->daddr.short_addr = llneigh->short_addr;
> + } else {
> + info->daddr.mode = IEEE802154_ADDR_LONG;
> + ieee802154_be64_to_le64(&info->daddr.extended_addr,
> + daddr);
> + }
>
> - info = lowpan_skb_priv(skb);
> + if (n)
> + neigh_release(n);
> + }
>
> - /* TODO: Currently we only support extended_addr */
> - info->daddr.mode = IEEE802154_ADDR_LONG;
> - memcpy(&info->daddr.u.extended_addr, daddr,
> - sizeof(info->daddr.u.extended_addr));
> - info->saddr.mode = IEEE802154_ADDR_LONG;
> - memcpy(&info->saddr.u.extended_addr, saddr,
> - sizeof(info->daddr.u.extended_addr));
> + if (!saddr) {
> + if (ieee802154_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;
> + }
> + } else {
> + info->saddr.mode = IEEE802154_ADDR_LONG;
> + ieee802154_be64_to_le64(&info->saddr.extended_addr, saddr);
> + }
>
> return 0;
> }
> @@ -209,47 +225,26 @@ static int lowpan_header(struct sk_buff *skb, struct net_device *ldev,
> u16 *dgram_size, u16 *dgram_offset)
> {
> struct wpan_dev *wpan_dev = lowpan_802154_dev(ldev)->wdev->ieee802154_ptr;
> - struct ieee802154_addr sa, da;
> struct ieee802154_mac_cb *cb = mac_cb_init(skb);
> struct lowpan_addr_info info;
> - void *daddr, *saddr;
>
> memcpy(&info, lowpan_skb_priv(skb), sizeof(info));
>
> - /* TODO: Currently we only support extended_addr */
> - daddr = &info.daddr.u.extended_addr;
> - saddr = &info.saddr.u.extended_addr;
> -
> *dgram_size = skb->len;
> - lowpan_header_compress(skb, ldev, daddr, saddr);
> + lowpan_header_compress(skb, ldev, &info.daddr, &info.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;
>
> - /* prepare wpan address data */
> - sa.mode = IEEE802154_ADDR_LONG;
> - sa.pan_id = wpan_dev->pan_id;
> - sa.extended_addr = ieee802154_devaddr_from_raw(saddr);
> -
> - /* intra-PAN communications */
> - da.pan_id = sa.pan_id;
> -
> - /* if the destination address is the broadcast address, use the
> - * corresponding short address
> - */
> - if (!memcmp(daddr, ldev->broadcast, EUI64_ADDR_LEN)) {
> - da.mode = IEEE802154_ADDR_SHORT;
> - da.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
> + if (info.daddr.mode == IEEE802154_ADDR_SHORT &&
> + ieee802154_is_broadcast_short_addr(info.daddr.short_addr))
> cb->ackreq = false;
> - } else {
> - da.mode = IEEE802154_ADDR_LONG;
> - da.extended_addr = ieee802154_devaddr_from_raw(daddr);
> + else
> cb->ackreq = wpan_dev->ackreq;
> - }
>
> - return wpan_dev_hard_header(skb, lowpan_802154_dev(ldev)->wdev, &da,
> - &sa, 0);
> + return wpan_dev_hard_header(skb, lowpan_802154_dev(ldev)->wdev,
> + &info.daddr, &info.saddr, 0);
> }
>
> netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *ldev)

Reviewed-by: Stefan Schmidt<[email protected]>

regards
Stefan Schmidt

2016-05-04 12:23:33

by Stefan Schmidt

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 09/10] 6lowpan: introduce 6lowpan-nd

Hello.

On 20/04/16 10:19, Alexander Aring wrote:
> This patch introduce different 6lowpan handling for receive and transmit
> NS/NA messages for the ipv6 neighbour discovery. The first use-case is
> for supporting 802.15.4 short addresses inside the option fields and
> handling for RFC6775 6CO option field as userspace option.
>
> Future handling:
> Also add RS/RA(processing) for 802.15.4 short addresses and handle
> RFC6775, which requires more 6lowpan specific handling for ipv6 neighbour
> discovery implementation.
>
> 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/6lowpan/6lowpan_i.h | 2 +
> net/6lowpan/Makefile | 2 +-
> net/6lowpan/core.c | 2 +
> net/6lowpan/ndisc.c | 633 ++++++++++++++++++++++++++++++++++++++++++++++++
> net/ipv6/ndisc.c | 3 +
> 6 files changed, 642 insertions(+), 1 deletion(-)
> create mode 100644 net/6lowpan/ndisc.c
>
> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
> index 35a4396..e2ee83d 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/6lowpan/6lowpan_i.h b/net/6lowpan/6lowpan_i.h
> index 97ecc27..8b01774 100644
> --- a/net/6lowpan/6lowpan_i.h
> +++ b/net/6lowpan/6lowpan_i.h
> @@ -12,6 +12,8 @@ static inline bool lowpan_is_ll(const struct net_device *dev,
> return lowpan_dev(dev)->lltype == lltype;
> }
>
> +void lowpan_register_ndisc_ops(struct net_device *dev);
> +
> #ifdef CONFIG_6LOWPAN_DEBUGFS
> int lowpan_dev_debugfs_init(struct net_device *dev);
> void lowpan_dev_debugfs_exit(struct net_device *dev);
> diff --git a/net/6lowpan/Makefile b/net/6lowpan/Makefile
> index e44f3bf..12d131a 100644
> --- a/net/6lowpan/Makefile
> +++ b/net/6lowpan/Makefile
> @@ -1,6 +1,6 @@
> obj-$(CONFIG_6LOWPAN) += 6lowpan.o
>
> -6lowpan-y := core.o iphc.o nhc.o
> +6lowpan-y := core.o iphc.o nhc.o ndisc.o
> 6lowpan-$(CONFIG_6LOWPAN_DEBUGFS) += debugfs.o
>
> #rfc6282 nhcs
> diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
> index 824d1bc..e7a370e 100644
> --- a/net/6lowpan/core.c
> +++ b/net/6lowpan/core.c
> @@ -34,6 +34,8 @@ int lowpan_register_netdevice(struct net_device *dev,
> for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
> lowpan_dev(dev)->ctx.table[i].id = i;
>
> + lowpan_register_ndisc_ops(dev);
> +
> ret = register_netdevice(dev);
> if (ret < 0)
> return ret;
> diff --git a/net/6lowpan/ndisc.c b/net/6lowpan/ndisc.c
> new file mode 100644
> index 0000000..d088295
> --- /dev/null
> +++ b/net/6lowpan/ndisc.c
> @@ -0,0 +1,633 @@
> +/* This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License 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.
> + */
> +

Maybe add yourself as author for copyright and authorship here?

> +#include <net/6lowpan.h>
> +#include <net/addrconf.h>
> +#include <net/ip6_route.h>
> +#include <net/ndisc.h>
> +
> +#include "6lowpan_i.h"
> +
> +struct lowpan_ndisc_options {
> + struct nd_opt_hdr *nd_opt_array[ND_OPT_TARGET_LL_ADDR + 1];
> +#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
> + struct nd_opt_hdr *nd_802154_opt_array[ND_OPT_TARGET_LL_ADDR + 1];
> +#endif
> +};
> +

The additional one for 15.4 here is to support short and extended
addresses at the same time?


> +#define nd_802154_opts_src_lladdr nd_802154_opt_array[ND_OPT_SOURCE_LL_ADDR]
> +#define nd_802154_opts_tgt_lladdr nd_802154_opt_array[ND_OPT_TARGET_LL_ADDR]
> +
> +#define NDISC_802154_EXTENDED_ADDR_LENGTH 2
> +#define NDISC_802154_SHORT_ADDR_LENGTH 1
> +
> +#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
> +static void lowpan_ndisc_802154_neigh_update(struct neighbour *n, void *priv,
> + bool override)
> +{
> + struct lowpan_802154_neigh *neigh = lowpan_802154_neigh(neighbour_priv(n));
> +
> + if (!override)
> + return;
> +
> + write_lock_bh(&n->lock);
> + if (priv)
> + ieee802154_be16_to_le16(&neigh->short_addr, priv);
> + else
> + neigh->short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);
> + write_unlock_bh(&n->lock);
> +}
> +
> +static inline int lowpan_ndisc_802154_short_addr_space(struct net_device *dev)
> +{
> + struct wpan_dev *wpan_dev;
> + int addr_space = 0;
> +
> + if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154)) {
> + wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr;
> +
> + if (ieee802154_is_valid_src_short_addr(wpan_dev->short_addr))
> + addr_space = ndisc_opt_addr_space(dev, IEEE802154_SHORT_ADDR_LEN);
> + }
> +
> + return addr_space;
> +}
> +
> +static inline void
> +lowpan_ndisc_802154_short_addr_option(struct net_device *dev,
> + struct sk_buff *skb, int type)
> +{
> + struct wpan_dev *wpan_dev;
> + __be16 short_addr;
> +
> + if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154)) {
> + wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr;
> +
> + if (ieee802154_is_valid_src_short_addr(wpan_dev->short_addr)) {
> + ieee802154_le16_to_be16(&short_addr,
> + &wpan_dev->short_addr);
> + ndisc_fill_addr_option(skb, type, &short_addr,
> + IEEE802154_SHORT_ADDR_LEN);
> + }
> + }
> +}
> +#else
> +static void
> +lowpan_ndisc_802154_neigh_update(struct neighbour *n, void *priv,
> + bool override) { }
> +
> +static inline void
> +lowpan_ndisc_802154_short_addr_option(struct net_device *dev,
> + struct sk_buff *skb,
> + int type) { }
> +
> +static inline int lowpan_ndisc_802154_short_addr_space(struct net_device *dev)
> +{
> + return 0;
> +}
> +#endif
> +
> +static void lowpan_ndisc_parse_addr_options(const struct net_device *dev,
> + struct lowpan_ndisc_options *ndopts,
> + struct nd_opt_hdr *nd_opt)
> +{
> + switch (nd_opt->nd_opt_len) {
> + case NDISC_802154_EXTENDED_ADDR_LENGTH:
> + if (ndopts->nd_opt_array[nd_opt->nd_opt_type])
> + ND_PRINTK(2, warn,
> + "%s: duplicated extended addr ND6 option found: type=%d\n",
> + __func__, nd_opt->nd_opt_type);
> + else
> + ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
> + break;
> +#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
> + case NDISC_802154_SHORT_ADDR_LENGTH:
> + /* only valid on 802.15.4 */
> + if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154)) {
> + ND_PRINTK(2, warn,
> + "%s: invalid length detected: type=%d\n",
> + __func__, nd_opt->nd_opt_type);
> + break;
> + }
> +
> + if (ndopts->nd_802154_opt_array[nd_opt->nd_opt_type])
> + ND_PRINTK(2, warn,
> + "%s: duplicated short addr ND6 option found: type=%d\n",
> + __func__, nd_opt->nd_opt_type);
> + else
> + ndopts->nd_802154_opt_array[nd_opt->nd_opt_type] = nd_opt;
> + break;
> +#endif
> + default:
> + ND_PRINTK(2, warn,
> + "%s: invalid length detected: type=%d\n",
> + __func__, nd_opt->nd_opt_type);
> + break;
> + }
> +}
> +
> +static struct lowpan_ndisc_options *
> +lowpan_ndisc_parse_options(const struct net_device *dev, u8 *opt, int opt_len,
> + struct lowpan_ndisc_options *ndopts)
> +{
> + struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)opt;
> +
> + if (!nd_opt || opt_len < 0 || !ndopts)
> + return NULL;
> +
> + memset(ndopts, 0, sizeof(*ndopts));
> +
> + while (opt_len) {
> + int l;
> +
> + if (opt_len < sizeof(struct nd_opt_hdr))
> + return NULL;
> +
> + l = nd_opt->nd_opt_len << 3;
> + if (opt_len < l || l == 0)
> + return NULL;
> +
> + switch (nd_opt->nd_opt_type) {
> + case ND_OPT_SOURCE_LL_ADDR:
> + case ND_OPT_TARGET_LL_ADDR:
> + lowpan_ndisc_parse_addr_options(dev, ndopts, nd_opt);
> + break;
> + default:
> + /* Unknown options must be silently ignored,
> + * to accommodate future extension to the
> + * protocol.
> + */
> + ND_PRINTK(2, notice,
> + "%s: ignored unsupported option; type=%d, len=%d\n",
> + __func__,
> + nd_opt->nd_opt_type,
> + nd_opt->nd_opt_len);
> + }
> +
> + opt_len -= l;
> + nd_opt = ((void *)nd_opt) + l;
> + }
> +
> + return ndopts;
> +}
> +
> +static void lowpan_ndisc_send_na(struct net_device *dev,
> + const struct in6_addr *daddr,
> + const struct in6_addr *solicited_addr,
> + bool router, bool solicited, bool override,
> + bool inc_opt)
> +{
> + struct sk_buff *skb;
> + struct in6_addr tmpaddr;
> + struct inet6_ifaddr *ifp;
> + const struct in6_addr *src_addr;
> + struct nd_msg *msg;
> + int optlen = 0;
> +
> + /* for anycast or proxy, solicited_addr != src_addr */
> + ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1);
> + if (ifp) {
> + src_addr = solicited_addr;
> + if (ifp->flags & IFA_F_OPTIMISTIC)
> + override = false;
> + inc_opt |= ifp->idev->cnf.force_tllao;
> + in6_ifa_put(ifp);
> + } else {
> + if (ipv6_dev_get_saddr(dev_net(dev), dev, daddr,
> + inet6_sk(dev_net(dev)->ipv6.ndisc_sk)->srcprefs,
> + &tmpaddr))
> + return;
> + src_addr = &tmpaddr;
> + }
> +
> + if (!dev->addr_len)
> + inc_opt = 0;
> + if (inc_opt) {
> + optlen += ndisc_opt_addr_space(dev, dev->addr_len);
> + optlen += lowpan_ndisc_802154_short_addr_space(dev);
> + }
> +
> + skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
> + if (!skb)
> + return;
> +
> + msg = (struct nd_msg *)skb_put(skb, sizeof(*msg));
> + *msg = (struct nd_msg) {
> + .icmph = {
> + .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
> + .icmp6_router = router,
> + .icmp6_solicited = solicited,
> + .icmp6_override = override,
> + },
> + .target = *solicited_addr,
> + };
> +
> + if (inc_opt) {
> + ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR,
> + dev->dev_addr, dev->addr_len);
> + lowpan_ndisc_802154_short_addr_option(dev, skb,
> + ND_OPT_TARGET_LL_ADDR);
> + }
> +
> + ndisc_send_skb(skb, daddr, src_addr);
> +}
> +
> +static void lowpan_ndisc_recv_na(struct sk_buff *skb)
> +{
> + struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
> + struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
> + const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
> + u8 *lladdr = NULL;
> + u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) +
> + offsetof(struct nd_msg, opt));
> + struct lowpan_ndisc_options ndopts;
> + struct net_device *dev = skb->dev;
> + struct inet6_dev *idev = __in6_dev_get(dev);
> + struct inet6_ifaddr *ifp;
> + struct neighbour *neigh;
> + u8 *lladdr_short = NULL;
> +
> + if (skb->len < sizeof(struct nd_msg)) {
> + ND_PRINTK(2, warn, "NA: packet too short\n");
> + return;
> + }
> +
> + if (ipv6_addr_is_multicast(&msg->target)) {
> + ND_PRINTK(2, warn, "NA: target address is multicast\n");
> + return;
> + }
> +
> + if (ipv6_addr_is_multicast(daddr) &&
> + msg->icmph.icmp6_solicited) {
> + ND_PRINTK(2, warn, "NA: solicited NA is multicasted\n");
> + return;
> + }
> +
> + /* For some 802.11 wireless deployments (and possibly other networks),
> + * there will be a NA proxy and unsolicitd packets are attacks
> + * and thus should not be accepted.
> + */
> + if (!msg->icmph.icmp6_solicited && idev &&
> + idev->cnf.drop_unsolicited_na)
> + return;
> +
> + if (!lowpan_ndisc_parse_options(dev, msg->opt, ndoptlen, &ndopts)) {
> + ND_PRINTK(2, warn, "NS: invalid ND option\n");
> + return;
> + }
> + if (ndopts.nd_opts_tgt_lladdr) {
> + lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev,
> + dev->addr_len);
> + if (!lladdr) {
> + ND_PRINTK(2, warn,
> + "NA: invalid link-layer address length\n");
> + return;
> + }
> + }
> +#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
> + if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154) &&
> + ndopts.nd_802154_opts_tgt_lladdr) {
> + lladdr_short = ndisc_opt_addr_data(ndopts.nd_802154_opts_tgt_lladdr,
> + dev, IEEE802154_SHORT_ADDR_LEN);
> + if (!lladdr_short) {
> + ND_PRINTK(2, warn,
> + "NA: invalid short link-layer address length\n");
> + return;
> + }
> + }
> +#endif
> + ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
> + if (ifp) {
> + if (skb->pkt_type != PACKET_LOOPBACK &&
> + (ifp->flags & IFA_F_TENTATIVE)) {
> + addrconf_dad_failure(ifp);
> + return;
> + }
> + /* What should we make now? The advertisement
> + * is invalid, but ndisc specs say nothing
> + * about it. It could be misconfiguration, or
> + * an smart proxy agent tries to help us :-)
> + *
> + * We should not print the error if NA has been
> + * received from loopback - it is just our own
> + * unsolicited advertisement.
> + */
> + if (skb->pkt_type != PACKET_LOOPBACK)
> + ND_PRINTK(1, warn,
> + "NA: someone advertises our address %pI6 on %s!\n",
> + &ifp->addr, ifp->idev->dev->name);
> + in6_ifa_put(ifp);
> + return;
> + }
> + neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
> +
> + if (neigh) {
> + u8 old_flags = neigh->flags;
> + struct net *net = dev_net(dev);
> +
> + if (neigh->nud_state & NUD_FAILED)
> + goto out;
> +
> + /* Don't update the neighbor cache entry on a proxy NA from
> + * ourselves because either the proxied node is off link or it
> + * has already sent a NA to us.
> + */
> + if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
> + net->ipv6.devconf_all->forwarding &&
> + net->ipv6.devconf_all->proxy_ndp &&
> + pneigh_lookup(&nd_tbl, net, &msg->target, dev, 0)) {
> + /* XXX: idev->cnf.proxy_ndp */

What is this XXX for here? Either a real comment about something that
need to be fixed in the future or just remove it.

> + goto out;
> + }
> +
> + neigh_update(neigh, lladdr,
> + msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
> + NEIGH_UPDATE_F_WEAK_OVERRIDE |
> + (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0) |
> + NEIGH_UPDATE_F_OVERRIDE_ISROUTER |
> + (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0));
> +
> + if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154))
> + lowpan_ndisc_802154_neigh_update(neigh, lladdr_short,
> + msg->icmph.icmp6_override);
> +
> + if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
> + /* Change: router to host */
> + rt6_clean_tohost(dev_net(dev), saddr);
> + }
> +
> +out:
> + neigh_release(neigh);
> + }
> +}
> +
> +static void lowpan_ndisc_send_ns(struct net_device *dev,
> + const struct in6_addr *solicit,
> + const struct in6_addr *daddr,
> + const struct in6_addr *saddr)
> +{
> + struct sk_buff *skb;
> + struct in6_addr addr_buf;
> + int inc_opt = dev->addr_len;
> + int optlen = 0;
> + struct nd_msg *msg;
> +
> + if (!saddr) {
> + if (ipv6_get_lladdr(dev, &addr_buf,
> + (IFA_F_TENTATIVE | IFA_F_OPTIMISTIC)))
> + return;
> + saddr = &addr_buf;
> + }
> +
> + if (ipv6_addr_any(saddr))
> + inc_opt = false;
> + if (inc_opt) {
> + optlen += ndisc_opt_addr_space(dev, dev->addr_len);
> + optlen += lowpan_ndisc_802154_short_addr_space(dev);
> + }
> +
> + skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
> + if (!skb)
> + return;
> +
> + msg = (struct nd_msg *)skb_put(skb, sizeof(*msg));
> + *msg = (struct nd_msg) {
> + .icmph = {
> + .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,
> + },
> + .target = *solicit,
> + };
> +
> + if (inc_opt) {
> + ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR,
> + dev->dev_addr, dev->addr_len);
> + lowpan_ndisc_802154_short_addr_option(dev, skb,
> + ND_OPT_SOURCE_LL_ADDR);
> + }
> +
> + ndisc_send_skb(skb, daddr, saddr);
> +}
> +
> +static void lowpan_ndisc_recv_ns(struct sk_buff *skb)
> +{
> + struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
> + const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
> + const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
> + u8 *lladdr = NULL;
> + u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) +
> + offsetof(struct nd_msg, opt));
> + struct lowpan_ndisc_options ndopts;
> + struct net_device *dev = skb->dev;
> + struct inet6_ifaddr *ifp;
> + struct inet6_dev *idev = NULL;
> + struct neighbour *neigh;
> + int dad = ipv6_addr_any(saddr);
> + bool inc;
> + int is_router = -1;
> + u8 *lladdr_short = NULL;
> +
> + if (skb->len < sizeof(struct nd_msg)) {
> + ND_PRINTK(2, warn, "NS: packet too short\n");
> + return;
> + }
> +
> + if (ipv6_addr_is_multicast(&msg->target)) {
> + ND_PRINTK(2, warn, "NS: multicast target address\n");
> + return;
> + }
> +
> + /* RFC2461 7.1.1:
> + * DAD has to be destined for solicited node multicast address.
> + */
> + if (dad && !ipv6_addr_is_solict_mult(daddr)) {
> + ND_PRINTK(2, warn, "NS: bad DAD packet (wrong destination)\n");
> + return;
> + }
> +
> + if (!lowpan_ndisc_parse_options(dev, msg->opt, ndoptlen, &ndopts)) {
> + ND_PRINTK(2, warn, "NS: invalid ND options\n");
> + return;
> + }
> +
> + if (ndopts.nd_opts_src_lladdr) {
> + lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev,
> + dev->addr_len);
> + if (!lladdr) {
> + ND_PRINTK(2, warn,
> + "NS: invalid link-layer address length\n");
> + return;
> + }
> +
> + /* RFC2461 7.1.1:
> + * If the IP source address is the unspecified address,
> + * there MUST NOT be source link-layer address option
> + * in the message.
> + */
> + if (dad) {
> + ND_PRINTK(2, warn,
> + "NS: bad DAD packet (link-layer address option)\n");
> + return;
> + }
> + }
> +
> +#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
> + if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154) &&
> + ndopts.nd_802154_opts_src_lladdr) {
> + lladdr_short = ndisc_opt_addr_data(ndopts.nd_802154_opts_src_lladdr,
> + dev, IEEE802154_SHORT_ADDR_LEN);
> + if (!lladdr_short) {
> + ND_PRINTK(2, warn,
> + "NS: invalid short link-layer address length\n");
> + return;
> + }
> +
> + /* RFC2461 7.1.1:
> + * If the IP source address is the unspecified address,
> + * there MUST NOT be source link-layer address option
> + * in the message.
> + */
> + if (dad) {
> + ND_PRINTK(2, warn,
> + "NS: bad DAD packet (short link-layer address option)\n");
> + return;
> + }
> + }
> +#endif
> +
> + inc = ipv6_addr_is_multicast(daddr);
> +
> + ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
> + if (ifp) {
> +have_ifp:
> + if (ifp->flags & (IFA_F_TENTATIVE | IFA_F_OPTIMISTIC)) {
> + if (dad) {
> + /* We are colliding with another node
> + * who is doing DAD
> + * so fail our DAD process
> + */
> + addrconf_dad_failure(ifp);
> + return;
> + }
> +
> + /* This is not a dad solicitation.
> + * If we are an optimistic node,
> + * we should respond.
> + * Otherwise, we should ignore it.
> + */
> + if (!(ifp->flags & IFA_F_OPTIMISTIC))
> + goto out;
> + }
> +
> + idev = ifp->idev;
> + } else {
> + struct net *net = dev_net(dev);
> +
> + /* perhaps an address on the master device */
> + if (netif_is_l3_slave(dev)) {
> + struct net_device *mdev;
> +
> + mdev = netdev_master_upper_dev_get_rcu(dev);
> + if (mdev) {
> + ifp = ipv6_get_ifaddr(net, &msg->target, mdev, 1);
> + if (ifp)
> + goto have_ifp;
> + }
> + }
> +
> + idev = in6_dev_get(dev);
> + if (!idev) {
> + /* XXX: count this drop? */
> + return;
> + }
> +
> + if (ipv6_chk_acast_addr(net, dev, &msg->target) ||
> + (idev->cnf.forwarding &&
> + (net->ipv6.devconf_all->proxy_ndp || idev->cnf.proxy_ndp) &&
> + (is_router = pndisc_is_router(&msg->target, dev)) >= 0)) {
> + if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) &&
> + skb->pkt_type != PACKET_HOST &&
> + inc &&
> + NEIGH_VAR(idev->nd_parms, PROXY_DELAY) != 0) {
> + /* for anycast or proxy,
> + * sender should delay its response
> + * by a random time between 0 and
> + * MAX_ANYCAST_DELAY_TIME seconds.
> + * (RFC2461) -- yoshfuji
> + */
> + struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
> +
> + if (n)
> + pneigh_enqueue(&nd_tbl, idev->nd_parms,
> + n);
> + goto out;
> + }
> + } else {
> + goto out;
> + }
> + }
> +
> + if (is_router < 0)
> + is_router = idev->cnf.forwarding;
> +
> + if (dad) {
> + ndisc_send_na(dev, &in6addr_linklocal_allnodes, &msg->target,
> + !!is_router, false, (ifp != NULL), true);
> + goto out;
> + }
> +
> + if (inc)
> + NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
> + else
> + NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
> +
> + /* update / create cache entry
> + * for the source address
> + */
> + neigh = __neigh_lookup(&nd_tbl, saddr, dev,
> + !inc || lladdr || !dev->addr_len);
> + if (neigh) {
> + neigh_update(neigh, lladdr, NUD_STALE,
> + NEIGH_UPDATE_F_WEAK_OVERRIDE |
> + NEIGH_UPDATE_F_OVERRIDE);
> + if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154))
> + lowpan_ndisc_802154_neigh_update(neigh, lladdr_short,
> + true);
> + }
> + if (neigh || !dev->header_ops) {
> + ndisc_send_na(dev, saddr, &msg->target, !!is_router,
> + true, (ifp != NULL && inc), inc);
> + if (neigh)
> + neigh_release(neigh);
> + }
> +
> +out:
> + if (ifp)
> + in6_ifa_put(ifp);
> + else
> + in6_dev_put(idev);
> +}
> +
> +static inline int lowpan_ndisc_is_useropt(struct nd_opt_hdr *opt)
> +{
> + return __ip6_ndisc_is_useropt(opt) || opt->nd_opt_type == ND_OPT_6CO;
> +}
> +
> +static const struct ndisc_ops lowpan_ndisc_ops = {
> + .is_useropt = lowpan_ndisc_is_useropt,
> + .send_na = lowpan_ndisc_send_na,
> + .recv_na = lowpan_ndisc_recv_na,
> + .send_ns = lowpan_ndisc_send_ns,
> + .recv_ns = lowpan_ndisc_recv_ns,
> +};
> +
> +void lowpan_register_ndisc_ops(struct net_device *dev)
> +{
> + dev->ndisc_ops = &lowpan_ndisc_ops;
> +}
> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
> index dc8bfec..9d7f228 100644
> --- a/net/ipv6/ndisc.c
> +++ b/net/ipv6/ndisc.c
> @@ -1792,6 +1792,9 @@ static const struct ndisc_ops ip6_ndisc_ops = {
> void ip6_register_ndisc_ops(struct net_device *dev)
> {
> switch (dev->type) {
> + case ARPHRD_6LOWPAN:
> + /* will be assigned while lowpan interface register */
> + break;
> default:
> if (dev->ndisc_ops) {
> ND_PRINTK(2, warn,

Reviewed-by: Stefan Schmidt<[email protected]>

regards
Stefan Schmidt

2016-05-04 12:23:22

by Stefan Schmidt

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 08/10] ipv6: export ndisc functions

Hello.

On 20/04/16 10:19, Alexander Aring wrote:
> This patch exports some neighbour discovery functions which can be used
> by 6lowpan neighbour discovery ops functionality then.
>
> 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 | 16 ++++++++++++++++
> net/ipv6/addrconf.c | 1 +
> net/ipv6/ndisc.c | 28 ++++++++++------------------
> 3 files changed, 27 insertions(+), 18 deletions(-)
>
> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
> index 14ed016..35a4396 100644
> --- a/include/net/ndisc.h
> +++ b/include/net/ndisc.h
> @@ -53,6 +53,15 @@ enum {
>
> #include <net/neighbour.h>
>
> +/* Set to 3 to get tracing... */
> +#define ND_DEBUG 1
> +
> +#define ND_PRINTK(val, level, fmt, ...) \
> +do { \
> + if (val <= ND_DEBUG) \
> + net_##level##_ratelimited(fmt, ##__VA_ARGS__); \
> +} while (0)
> +
> struct ctl_table;
> struct inet6_dev;
> struct net_device;
> @@ -267,6 +276,13 @@ int ndisc_late_init(void);
> void ndisc_late_cleanup(void);
> void ndisc_cleanup(void);
>
> +void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data,
> + int data_len);
> +struct sk_buff *ndisc_alloc_skb(struct net_device *dev, int len);
> +void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr,
> + const struct in6_addr *saddr);
> +int pndisc_is_router(const void *pkey, struct net_device *dev);
> +
> int ndisc_rcv(struct sk_buff *skb);
>
> void ndisc_send_rs(struct net_device *dev,
> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index a2ef04b..8f05ef8 100644
> --- a/net/ipv6/addrconf.c
> +++ b/net/ipv6/addrconf.c
> @@ -1775,6 +1775,7 @@ struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *add
>
> return result;
> }
> +EXPORT_SYMBOL(ipv6_get_ifaddr);
>
> /* Gets referenced address, destroys ifaddr */
>
> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
> index 297080a..dc8bfec 100644
> --- a/net/ipv6/ndisc.c
> +++ b/net/ipv6/ndisc.c
> @@ -73,15 +73,6 @@
> #include <linux/netfilter.h>
> #include <linux/netfilter_ipv6.h>
>
> -/* Set to 3 to get tracing... */
> -#define ND_DEBUG 1
> -
> -#define ND_PRINTK(val, level, fmt, ...) \
> -do { \
> - if (val <= ND_DEBUG) \
> - net_##level##_ratelimited(fmt, ##__VA_ARGS__); \
> -} while (0)
> -
> static u32 ndisc_hash(const void *pkey,
> const struct net_device *dev,
> __u32 *hash_rnd);
> @@ -150,8 +141,8 @@ struct neigh_table nd_tbl = {
> };
> EXPORT_SYMBOL_GPL(nd_tbl);
>
> -static void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data,
> - int data_len)
> +void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data,
> + int data_len)
> {
> int pad = ndisc_addr_option_pad(skb->dev->type);
> int space = ndisc_opt_addr_space(skb->dev, data_len);
> @@ -171,6 +162,7 @@ static void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data,
> if (space > 0)
> memset(opt, 0, space);
> }
> +EXPORT_SYMBOL(ndisc_fill_addr_option);

Hmm, one export in this files uses EXPORT_SYMBOL while another one uses
EXPORT_SYMBOL_GPL. Not sure which one is preferred in this file.
>
> static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
> struct nd_opt_hdr *end)
> @@ -378,8 +370,7 @@ static void pndisc_destructor(struct pneigh_entry *n)
> ipv6_dev_mc_dec(dev, &maddr);
> }
>
> -static struct sk_buff *ndisc_alloc_skb(struct net_device *dev,
> - int len)
> +struct sk_buff *ndisc_alloc_skb(struct net_device *dev, int len)
> {
> int hlen = LL_RESERVED_SPACE(dev);
> int tlen = dev->needed_tailroom;
> @@ -406,6 +397,7 @@ static struct sk_buff *ndisc_alloc_skb(struct net_device *dev,
>
> return skb;
> }
> +EXPORT_SYMBOL(ndisc_alloc_skb);

Same here.
>
> static void ip6_nd_hdr(struct sk_buff *skb,
> const struct in6_addr *saddr,
> @@ -428,9 +420,8 @@ static void ip6_nd_hdr(struct sk_buff *skb,
> hdr->daddr = *daddr;
> }
>
> -static void ndisc_send_skb(struct sk_buff *skb,
> - const struct in6_addr *daddr,
> - const struct in6_addr *saddr)
> +void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr,
> + const struct in6_addr *saddr)
> {
> struct dst_entry *dst = skb_dst(skb);
> struct net *net = dev_net(skb->dev);
> @@ -479,6 +470,7 @@ static void ndisc_send_skb(struct sk_buff *skb,
>
> rcu_read_unlock();
> }
> +EXPORT_SYMBOL(ndisc_send_skb);

And here.
>
> static void ip6_ndisc_send_na(struct net_device *dev,
> const struct in6_addr *daddr,
> @@ -692,8 +684,7 @@ static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
> }
> }
>
> -static int pndisc_is_router(const void *pkey,
> - struct net_device *dev)
> +int pndisc_is_router(const void *pkey, struct net_device *dev)
> {
> struct pneigh_entry *n;
> int ret = -1;
> @@ -706,6 +697,7 @@ static int pndisc_is_router(const void *pkey,
>
> return ret;
> }
> +EXPORT_SYMBOL(pndisc_is_router);
>
> static void ip6_ndisc_recv_ns(struct sk_buff *skb)
> {

Reviewed-by: Stefan Schmidt<[email protected]>

regards
Stefan Schmidt

2016-05-04 12:23:07

by Stefan Schmidt

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 07/10] ipv6: introduce neighbour discovery ops

Hello.

On 20/04/16 10:19, Alexander Aring wrote:
> This patch introduces neighbour discovery ops callback structure. The
> structure contains at first receive and transmit handling for NS/NA and
> userspace option field functionality.
>
> These callback offers 6lowpan different handling, such as 802.15.4 short
> address handling or RFC6775 (Neighbor Discovery Optimization for IPv6 over
> 6LoWPANs).
>
> 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/linux/netdevice.h | 3 ++
> include/net/ndisc.h | 96 +++++++++++++++++++++++++++++++++++++++++++----
> net/ipv6/addrconf.c | 1 +
> net/ipv6/ndisc.c | 71 ++++++++++++++++++++++++-----------
> net/ipv6/route.c | 2 +-
> 5 files changed, 144 insertions(+), 29 deletions(-)
>
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index 0052c42..bc60033 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -1677,6 +1677,9 @@ struct net_device {
> #ifdef CONFIG_NET_L3_MASTER_DEV
> const struct l3mdev_ops *l3mdev_ops;
> #endif
> +#if IS_ENABLED(CONFIG_IPV6)
> + const struct ndisc_ops *ndisc_ops;
> +#endif
>
> const struct header_ops *header_ops;
>
> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
> index aac868e..14ed016 100644
> --- a/include/net/ndisc.h
> +++ b/include/net/ndisc.h
> @@ -110,7 +110,8 @@ struct ndisc_options {
>
> #define NDISC_OPT_SPACE(len) (((len)+2+7)&~7)
>
> -struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
> +struct ndisc_options *ndisc_parse_options(const struct net_device *dev,
> + u8 *opt, int opt_len,
> struct ndisc_options *ndopts);
>
> /*
> @@ -173,6 +174,93 @@ static inline struct neighbour *__ipv6_neigh_lookup(struct net_device *dev, cons
> return n;
> }
>
> +static inline int __ip6_ndisc_is_useropt(struct nd_opt_hdr *opt)

Name it __ipv6... instead of __ip6...?
> +{
> + return opt->nd_opt_type == ND_OPT_RDNSS ||
> + opt->nd_opt_type == ND_OPT_DNSSL;
> +}
> +
> +#if IS_ENABLED(CONFIG_IPV6)
> +struct ndisc_ops {
> + int (*is_useropt)(struct nd_opt_hdr *opt);
> + void (*send_na)(struct net_device *dev,
> + const struct in6_addr *daddr,
> + const struct in6_addr *solicited_addr,
> + bool router, bool solicited,
> + bool override, bool inc_opt);
> + void (*recv_na)(struct sk_buff *skb);
> + void (*send_ns)(struct net_device *dev,
> + const struct in6_addr *solicit,
> + const struct in6_addr *daddr,
> + const struct in6_addr *saddr);
> + void (*recv_ns)(struct sk_buff *skb);
> +};
> +
> +static inline int ndisc_is_useropt(const struct net_device *dev,
> + struct nd_opt_hdr *opt)
> +{
> + if (likely(dev->ndisc_ops->is_useropt))
> + return dev->ndisc_ops->is_useropt(opt);
> + else
> + return 0;
> +}
> +
> +static inline void ndisc_send_na(struct net_device *dev,
> + const struct in6_addr *daddr,
> + const struct in6_addr *solicited_addr,
> + bool router, bool solicited, bool override,
> + bool inc_opt)
> +{
> + if (likely(dev->ndisc_ops->send_na))
> + dev->ndisc_ops->send_na(dev, daddr, solicited_addr, router,
> + solicited, override, inc_opt);
> +}
> +
> +static inline void ndisc_recv_na(struct sk_buff *skb)
> +{
> + if (likely(skb->dev->ndisc_ops->recv_na))
> + skb->dev->ndisc_ops->recv_na(skb);
> +}
> +
> +static inline void ndisc_send_ns(struct net_device *dev,
> + const struct in6_addr *solicit,
> + const struct in6_addr *daddr,
> + const struct in6_addr *saddr)
> +{
> + if (likely(dev->ndisc_ops->send_ns))
> + dev->ndisc_ops->send_ns(dev, solicit, daddr, saddr);
> +}
> +
> +static inline void ndisc_recv_ns(struct sk_buff *skb)
> +{
> + if (likely(skb->dev->ndisc_ops->recv_ns))
> + skb->dev->ndisc_ops->recv_ns(skb);
> +}
> +#else
> +static inline int ndisc_is_useropt(const struct net_device *dev,
> + struct nd_opt_hdr *opt)
> +{
> + return 0;
> +}
> +
> +static inline void ndisc_send_na(struct net_device *dev,
> + const struct in6_addr *daddr,
> + const struct in6_addr *solicited_addr,
> + bool router, bool solicited, bool override,
> + bool inc_opt) { }
> +
> +static inline void ndisc_recv_na(struct sk_buff *skb) { }
> +
> +static inline void ndisc_send_ns(struct net_device *dev,
> + const struct in6_addr *solicit,
> + const struct in6_addr *daddr,
> + const struct in6_addr *saddr) { }
> +
> +static inline void ndisc_recv_ns(struct sk_buff *skb) { }
> +#endif
> +
> +void ip6_register_ndisc_ops(struct net_device *dev);
> +
> int ndisc_init(void);
> int ndisc_late_init(void);
>
> @@ -181,14 +269,8 @@ void ndisc_cleanup(void);
>
> int ndisc_rcv(struct sk_buff *skb);
>
> -void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
> - const struct in6_addr *daddr, const struct in6_addr *saddr);
> -
> void ndisc_send_rs(struct net_device *dev,
> const struct in6_addr *saddr, const struct in6_addr *daddr);
> -void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
> - const struct in6_addr *solicited_addr,
> - bool router, bool solicited, bool override, bool inc_opt);
>
> void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target);
>
> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index 54e18c2..a2ef04b 100644
> --- a/net/ipv6/addrconf.c
> +++ b/net/ipv6/addrconf.c
> @@ -3266,6 +3266,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
> idev = ipv6_add_dev(dev);
> if (IS_ERR(idev))
> return notifier_from_errno(PTR_ERR(idev));
> + ip6_register_ndisc_ops(dev);
> }
> break;
>
> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
> index 176c7c4..297080a 100644
> --- a/net/ipv6/ndisc.c
> +++ b/net/ipv6/ndisc.c
> @@ -185,24 +185,25 @@ static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
> return cur <= end && cur->nd_opt_type == type ? cur : NULL;
> }
>
> -static inline int ndisc_is_useropt(struct nd_opt_hdr *opt)
> +static inline int ip6_ndisc_is_useropt(struct nd_opt_hdr *opt)
> {
> - return opt->nd_opt_type == ND_OPT_RDNSS ||
> - opt->nd_opt_type == ND_OPT_DNSSL;
> + return __ip6_ndisc_is_useropt(opt);

Why putting this check into a different function? It looks like a not
needed redirection.
> }
>
> -static struct nd_opt_hdr *ndisc_next_useropt(struct nd_opt_hdr *cur,
> +static struct nd_opt_hdr *ndisc_next_useropt(const struct net_device *dev,
> + struct nd_opt_hdr *cur,
> struct nd_opt_hdr *end)
> {
> if (!cur || !end || cur >= end)
> return NULL;
> do {
> cur = ((void *)cur) + (cur->nd_opt_len << 3);
> - } while (cur < end && !ndisc_is_useropt(cur));
> - return cur <= end && ndisc_is_useropt(cur) ? cur : NULL;
> + } while (cur < end && !ndisc_is_useropt(dev, cur));
> + return cur <= end && ndisc_is_useropt(dev, cur) ? cur : NULL;
> }
>
> -struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
> +struct ndisc_options *ndisc_parse_options(const struct net_device *dev,
> + u8 *opt, int opt_len,
> struct ndisc_options *ndopts)
> {
> struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)opt;
> @@ -243,7 +244,7 @@ struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
> break;
> #endif
> default:
> - if (ndisc_is_useropt(nd_opt)) {
> + if (ndisc_is_useropt(dev, nd_opt)) {
> ndopts->nd_useropts_end = nd_opt;
> if (!ndopts->nd_useropts)
> ndopts->nd_useropts = nd_opt;
> @@ -479,9 +480,11 @@ static void ndisc_send_skb(struct sk_buff *skb,
> rcu_read_unlock();
> }
>
> -void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
> - const struct in6_addr *solicited_addr,
> - bool router, bool solicited, bool override, bool inc_opt)
> +static void ip6_ndisc_send_na(struct net_device *dev,
> + const struct in6_addr *daddr,
> + const struct in6_addr *solicited_addr,
> + bool router, bool solicited, bool override,
> + bool inc_opt)
> {
> struct sk_buff *skb;
> struct in6_addr tmpaddr;
> @@ -555,8 +558,10 @@ static void ndisc_send_unsol_na(struct net_device *dev)
> in6_dev_put(idev);
> }
>
> -void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
> - const struct in6_addr *daddr, const struct in6_addr *saddr)
> +static void ip6_ndisc_send_ns(struct net_device *dev,
> + const struct in6_addr *solicit,
> + const struct in6_addr *daddr,
> + const struct in6_addr *saddr)
> {
> struct sk_buff *skb;
> struct in6_addr addr_buf;
> @@ -702,7 +707,7 @@ static int pndisc_is_router(const void *pkey,
> return ret;
> }
>
> -static void ndisc_recv_ns(struct sk_buff *skb)
> +static void ip6_ndisc_recv_ns(struct sk_buff *skb)
> {
> struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
> const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
> @@ -738,7 +743,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
> return;
> }
>
> - if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
> + if (!ndisc_parse_options(dev, msg->opt, ndoptlen, &ndopts)) {
> ND_PRINTK(2, warn, "NS: invalid ND options\n");
> return;
> }
> @@ -874,7 +879,7 @@ out:
> in6_dev_put(idev);
> }
>
> -static void ndisc_recv_na(struct sk_buff *skb)
> +static void ip6_ndisc_recv_na(struct sk_buff *skb)
> {
> struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
> struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
> @@ -912,7 +917,7 @@ static void ndisc_recv_na(struct sk_buff *skb)
> idev->cnf.drop_unsolicited_na)
> return;
>
> - if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
> + if (!ndisc_parse_options(dev, msg->opt, ndoptlen, &ndopts)) {
> ND_PRINTK(2, warn, "NS: invalid ND option\n");
> return;
> }
> @@ -1019,7 +1024,7 @@ static void ndisc_recv_rs(struct sk_buff *skb)
> goto out;
>
> /* Parse ND options */
> - if (!ndisc_parse_options(rs_msg->opt, ndoptlen, &ndopts)) {
> + if (!ndisc_parse_options(skb->dev, rs_msg->opt, ndoptlen, &ndopts)) {
> ND_PRINTK(2, notice, "NS: invalid ND option, ignored\n");
> goto out;
> }
> @@ -1137,7 +1142,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)
> return;
> }
>
> - if (!ndisc_parse_options(opt, optlen, &ndopts)) {
> + if (!ndisc_parse_options(skb->dev, opt, optlen, &ndopts)) {
> ND_PRINTK(2, warn, "RA: invalid ND options\n");
> return;
> }
> @@ -1424,7 +1429,8 @@ skip_routeinfo:
> struct nd_opt_hdr *p;
> for (p = ndopts.nd_useropts;
> p;
> - p = ndisc_next_useropt(p, ndopts.nd_useropts_end)) {
> + p = ndisc_next_useropt(skb->dev, p,
> + ndopts.nd_useropts_end)) {
> ndisc_ra_useropt(skb, p);
> }
> }
> @@ -1462,7 +1468,7 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
> return;
> }
>
> - if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts))
> + if (!ndisc_parse_options(skb->dev, msg->opt, ndoptlen, &ndopts))
> return;
>
> if (!ndopts.nd_opts_rh) {
> @@ -1783,6 +1789,29 @@ int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, void __user *bu
>
> #endif
>
> +static const struct ndisc_ops ip6_ndisc_ops = {
> + .is_useropt = ip6_ndisc_is_useropt,
> + .send_na = ip6_ndisc_send_na,
> + .recv_na = ip6_ndisc_recv_na,
> + .send_ns = ip6_ndisc_send_ns,
> + .recv_ns = ip6_ndisc_recv_ns,

Here I would also think ipv6 as prefix would be nicer.

> +};
> +
> +void ip6_register_ndisc_ops(struct net_device *dev)
> +{
> + switch (dev->type) {
> + default:
> + if (dev->ndisc_ops) {
> + ND_PRINTK(2, warn,
> + "%s: ndisc_ops already defined for interface type=%d\n",
> + __func__, dev->type);
> + } else {
> + dev->ndisc_ops = &ip6_ndisc_ops;
> + }
> + break;
> + }
> +}
> +
> static int __net_init ndisc_net_init(struct net *net)
> {
> struct ipv6_pinfo *np;
> diff --git a/net/ipv6/route.c b/net/ipv6/route.c
> index cc180b3..5fa276d 100644
> --- a/net/ipv6/route.c
> +++ b/net/ipv6/route.c
> @@ -2149,7 +2149,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
> * first-hop router for the specified ICMP Destination Address.
> */
>
> - if (!ndisc_parse_options(msg->opt, optlen, &ndopts)) {
> + if (!ndisc_parse_options(skb->dev, msg->opt, optlen, &ndopts)) {
> net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
> return;
> }

Reviewed-by: Stefan Schmidt<[email protected]>

regards
Stefan Schmidt

2016-05-04 10:43:54

by Alexander Aring

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 01/10] 6lowpan: add private neighbour data


Hi,

On 05/02/2016 08:59 PM, Hannes Frederic Sowa wrote:
> Hello,
>
> On 20.04.2016 10:19, Alexander Aring wrote:
>> This patch will introduce a 6lowpan neighbour private data. Like the
>> interface private data we handle private data for generic 6lowpan and
>> for link-layer specific 6lowpan.
>>
>> The current first use case if to save the short address for a 802.15.4
>> 6lowpan neighbour.
>>
>> Cc: David S. Miller <[email protected]>
>> Signed-off-by: Alexander Aring <[email protected]>
>> ---
>> include/linux/netdevice.h | 3 +--
>> include/net/6lowpan.h | 24 ++++++++++++++++++++++++
>> net/bluetooth/6lowpan.c | 2 ++
>> net/ieee802154/6lowpan/core.c | 12 ++++++++++++
>> 4 files changed, 39 insertions(+), 2 deletions(-)
>>
>> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
>> index 166402a..0052c42 100644
>> --- a/include/linux/netdevice.h
>> +++ b/include/linux/netdevice.h
>> @@ -1487,8 +1487,7 @@ enum netdev_priv_flags {
>> * @perm_addr: Permanent hw address
>> * @addr_assign_type: Hw address assignment type
>> * @addr_len: Hardware address length
>> - * @neigh_priv_len; Used in neigh_alloc(),
>> - * initialized only in atm/clip.c
>> + * @neigh_priv_len; Used in neigh_alloc()
>> * @dev_id: Used to differentiate devices that share
>> * the same link layer address
>> * @dev_port: Used to differentiate devices that share
>> diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
>> index da84cf9..61c6517 100644
>> --- a/include/net/6lowpan.h
>> +++ b/include/net/6lowpan.h
>> @@ -98,6 +98,9 @@ static inline bool lowpan_is_iphc(u8 dispatch)
>> #define LOWPAN_PRIV_SIZE(llpriv_size) \
>> (sizeof(struct lowpan_dev) + llpriv_size)
>>
>> +#define LOWPAN_NEIGH_PRIV_SIZE(llneigh_priv_size) \
>> + (sizeof(struct lowpan_neigh) + llneigh_priv_size)
>> +
>> enum lowpan_lltypes {
>> LOWPAN_LLTYPE_BTLE,
>> LOWPAN_LLTYPE_IEEE802154,
>> @@ -141,6 +144,27 @@ struct lowpan_dev {
>> u8 priv[0] __aligned(sizeof(void *));
>> };
>>
>> +struct lowpan_neigh {
>> + /* 6LoWPAN neigh private data */
>> + /* must be last */
>> + u8 priv[0] __aligned(sizeof(void *));
>
> Are you sure this declaration is correct? You take its size above, which
> should result in zero. Looks a little bit strange. :)
>

I think yes it's correct, but I will remove it.

The basic idea here is to introduce a 6LoWPAN neighbour private data and
Link-Layer specific neighbour private data.

Example:

6LoWPAN neighbour data:

The GHC capability, 6LoWPAN contains some compression methods, also for
several Next Header (UDP, IPv6 extension headers, ...). So far I
understand each neighbour can tell which compression method it supports
by a special option field, see [0].

Such data doesn't depends on Link-Layer and is the same for all 6LoWPAN
over FOO standards.

6LoWPAN Link-Layer specific neighbour data:

Like the short address handling in case of 6LoWPAN over 802.15.4.

>> +};
>> +
>> +struct lowpan_802154_neigh {
>> + __le16 short_addr;
>> +};
>> +
>> +static inline struct lowpan_neigh *lowpan_neigh(void *neigh_priv)
>> +{
>> + return neigh_priv;
>> +}
>> +
>> +static inline
>> +struct lowpan_802154_neigh *lowpan_802154_neigh(void *neigh_priv)
>> +{
>> + return (struct lowpan_802154_neigh *)lowpan_neigh(neigh_priv)->priv;
>> +}
>
> Can't you remove lowpan_neigh completely and just use 802154_neigh at
> this point?

Yes, I remove it. I think to introduce such layer for 6LoWPAN neighbour
data, we can still introduce it later when we have an use-case. Also with
a better foo_ops structure so we have something like this:

6lowpan interface:
- .ndo_neigh_construct
- "doing 6lowpan stuff"
- .ndo_ll_neigh_construct
- "doing ll 6lowpan stuff"

and ".ndo_ll_neigh_construct" differs in case of 6lowpan ll
implementation (802.15.4 or BTLE currently).

- Alex

[0] https://tools.ietf.org/html/rfc7400#section-3.3

2016-05-03 20:07:37

by David Miller

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 00/10] 6lowpan: introduce basic 6lowpan-nd

From: Marcel Holtmann <[email protected]>
Date: Mon, 2 May 2016 16:17:41 -0700

>> My proposal would be that the IPv6 patches go via net-next to reduce
>> merge conflicts with maybe upcoming changes. If they are split up, they
>> seem very much self contained and easy to review. The rest seems to be
>> also very much self contained and can go in via bluetooth-next, then.
>> What do you think?
>
> I am actually fine with having this all go via net-next. We only
> have driver patches pending in bluetooth-next for the next merge
> window. Which means I can just pull net-next back into
> bluetooth-next at any time.

Ok, just resubmit the series explicitly targetting net-next then.

Thanks.

2016-05-03 18:17:35

by Stefan Schmidt

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 07/10] ipv6: introduce neighbour discovery ops

Hello.

On 02/05/16 21:36, Hannes Frederic Sowa wrote:
> On 20.04.2016 10:19, Alexander Aring wrote:
>> This patch introduces neighbour discovery ops callback structure. The
>> structure contains at first receive and transmit handling for NS/NA and
>> userspace option field functionality.
>>
>> These callback offers 6lowpan different handling, such as 802.15.4 short
>> address handling or RFC6775 (Neighbor Discovery Optimization for IPv6 over
>> 6LoWPANs).
>>
>> 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/linux/netdevice.h | 3 ++
>> include/net/ndisc.h | 96 +++++++++++++++++++++++++++++++++++++++++++----
>> net/ipv6/addrconf.c | 1 +
>> net/ipv6/ndisc.c | 71 ++++++++++++++++++++++++-----------
>> net/ipv6/route.c | 2 +-
>> 5 files changed, 144 insertions(+), 29 deletions(-)
>>
>> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
>> index 0052c42..bc60033 100644
>> --- a/include/linux/netdevice.h
>> +++ b/include/linux/netdevice.h
>> @@ -1677,6 +1677,9 @@ struct net_device {
>> #ifdef CONFIG_NET_L3_MASTER_DEV
>> const struct l3mdev_ops *l3mdev_ops;
>> #endif
>> +#if IS_ENABLED(CONFIG_IPV6)
>> + const struct ndisc_ops *ndisc_ops;
>> +#endif
>>
>> const struct header_ops *header_ops;
>>
>> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
>> index aac868e..14ed016 100644
>> --- a/include/net/ndisc.h
>> +++ b/include/net/ndisc.h
>> @@ -110,7 +110,8 @@ struct ndisc_options {
>>
>> #define NDISC_OPT_SPACE(len) (((len)+2+7)&~7)
>>
>> -struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
>> +struct ndisc_options *ndisc_parse_options(const struct net_device *dev,
>> + u8 *opt, int opt_len,
>> struct ndisc_options *ndopts);
>>
>> /*
>> @@ -173,6 +174,93 @@ static inline struct neighbour *__ipv6_neigh_lookup(struct net_device *dev, cons
>> return n;
>> }
>>
>> +static inline int __ip6_ndisc_is_useropt(struct nd_opt_hdr *opt)
>> +{
>> + return opt->nd_opt_type == ND_OPT_RDNSS ||
>> + opt->nd_opt_type == ND_OPT_DNSSL;
>> +}
>> +
>> +#if IS_ENABLED(CONFIG_IPV6)
>> +struct ndisc_ops {
>> + int (*is_useropt)(struct nd_opt_hdr *opt);
>> + void (*send_na)(struct net_device *dev,
>> + const struct in6_addr *daddr,
>> + const struct in6_addr *solicited_addr,
>> + bool router, bool solicited,
>> + bool override, bool inc_opt);
>> + void (*recv_na)(struct sk_buff *skb);
>> + void (*send_ns)(struct net_device *dev,
>> + const struct in6_addr *solicit,
>> + const struct in6_addr *daddr,
>> + const struct in6_addr *saddr);
>> + void (*recv_ns)(struct sk_buff *skb);
>> +};
>> +
>> +static inline int ndisc_is_useropt(const struct net_device *dev,
>> + struct nd_opt_hdr *opt)
>> +{
>> + if (likely(dev->ndisc_ops->is_useropt))
>> + return dev->ndisc_ops->is_useropt(opt);
>> + else
>> + return 0;
>> +}
>> +
>> +static inline void ndisc_send_na(struct net_device *dev,
>> + const struct in6_addr *daddr,
>> + const struct in6_addr *solicited_addr,
>> + bool router, bool solicited, bool override,
>> + bool inc_opt)
>> +{
>> + if (likely(dev->ndisc_ops->send_na))
>> + dev->ndisc_ops->send_na(dev, daddr, solicited_addr, router,
>> + solicited, override, inc_opt);
>> +}
>> +
>> +static inline void ndisc_recv_na(struct sk_buff *skb)
>> +{
>> + if (likely(skb->dev->ndisc_ops->recv_na))
>> + skb->dev->ndisc_ops->recv_na(skb);
>> +}
>> +
>> +static inline void ndisc_send_ns(struct net_device *dev,
>> + const struct in6_addr *solicit,
>> + const struct in6_addr *daddr,
>> + const struct in6_addr *saddr)
>> +{
>> + if (likely(dev->ndisc_ops->send_ns))
>> + dev->ndisc_ops->send_ns(dev, solicit, daddr, saddr);
>> +}
>> +
>> +static inline void ndisc_recv_ns(struct sk_buff *skb)
>> +{
>> + if (likely(skb->dev->ndisc_ops->recv_ns))
>> + skb->dev->ndisc_ops->recv_ns(skb);
>> +}
>> +#else
>> +static inline int ndisc_is_useropt(const struct net_device *dev,
>> + struct nd_opt_hdr *opt)
>> +{
>> + return 0;
>> +}
>> +
>> +static inline void ndisc_send_na(struct net_device *dev,
>> + const struct in6_addr *daddr,
>> + const struct in6_addr *solicited_addr,
>> + bool router, bool solicited, bool override,
>> + bool inc_opt) { }
>> +
>> +static inline void ndisc_recv_na(struct sk_buff *skb) { }
>> +
>> +static inline void ndisc_send_ns(struct net_device *dev,
>> + const struct in6_addr *solicit,
>> + const struct in6_addr *daddr,
>> + const struct in6_addr *saddr) { }
>> +
>> +static inline void ndisc_recv_ns(struct sk_buff *skb) { }
>> +#endif
> Do those empty functions actually make sense? I wonder a bit because
> 6lowpan strictly depends on ipv6 and they should never be called without
> IPv6, no?

Agreed. 6LoWAPN is only an adaptation layer so we know that IPv6 must be
enabled here. I would also argue for removing this ifdef and the empty
functions.

regards
Stefan Schmidt

2016-05-03 18:17:24

by Stefan Schmidt

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 06/10] ndisc: add addr_len parameter to ndisc_fill_addr_option

Hello.

On 20/04/16 10:19, Alexander Aring wrote:
> This patch makes the address length as argument for the
> ndisc_fill_addr_option function. This is necessary to handle addresses
> which don't use dev->addr_len as address length.
>
> 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]>
> ---
> net/ipv6/ndisc.c | 15 ++++++++-------
> 1 file changed, 8 insertions(+), 7 deletions(-)
>
> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
> index 4e91d5e..176c7c4 100644
> --- a/net/ipv6/ndisc.c
> +++ b/net/ipv6/ndisc.c
> @@ -150,11 +150,11 @@ struct neigh_table nd_tbl = {
> };
> EXPORT_SYMBOL_GPL(nd_tbl);
>
> -static void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data)
> +static void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data,
> + int data_len)
> {
> int pad = ndisc_addr_option_pad(skb->dev->type);
> - int data_len = skb->dev->addr_len;
> - int space = ndisc_opt_addr_space(skb->dev, skb->dev->addr_len);
> + int space = ndisc_opt_addr_space(skb->dev, data_len);
> u8 *opt = skb_put(skb, space);
>
> opt[0] = type;
> @@ -528,7 +528,7 @@ void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
>
> if (inc_opt)
> ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR,
> - dev->dev_addr);
> + dev->dev_addr, dev->addr_len);
>
>
> ndisc_send_skb(skb, daddr, src_addr);
> @@ -590,7 +590,7 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
>
> if (inc_opt)
> ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR,
> - dev->dev_addr);
> + dev->dev_addr, dev->addr_len);
>
> ndisc_send_skb(skb, daddr, saddr);
> }
> @@ -641,7 +641,7 @@ void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
>
> if (send_sllao)
> ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR,
> - dev->dev_addr);
> + dev->dev_addr, dev->addr_len);
>
> ndisc_send_skb(skb, daddr, saddr);
> }
> @@ -1597,7 +1597,8 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
> */
>
> if (ha)
> - ndisc_fill_addr_option(buff, ND_OPT_TARGET_LL_ADDR, ha);
> + ndisc_fill_addr_option(buff, ND_OPT_TARGET_LL_ADDR, ha,
> + dev->addr_len);
>
> /*
> * build redirect option and copy skb over to the new packet.

Reviewed-by: Stefan Schmidt<[email protected]>

regards
Stefan Schmidt

2016-05-03 18:17:14

by Stefan Schmidt

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 05/10] ndisc: add addr_len parameter to ndisc_opt_addr_data

Hello.

On 20/04/16 10:19, Alexander Aring wrote:
> This patch makes the address length as argument for the
> ndisc_opt_addr_data function. This is necessary to handle addresses
> which don't use dev->addr_len as address length.
>
> 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 | 5 +++--
> net/ipv6/ndisc.c | 11 +++++++----
> net/ipv6/route.c | 2 +-
> 3 files changed, 11 insertions(+), 7 deletions(-)
>
> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
> index ef43e88..aac868e 100644
> --- a/include/net/ndisc.h
> +++ b/include/net/ndisc.h
> @@ -134,12 +134,13 @@ static inline int ndisc_opt_addr_space(struct net_device *dev,
> }
>
> static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,
> - struct net_device *dev)
> + struct net_device *dev,
> + unsigned char addr_len)
> {
> u8 *lladdr = (u8 *)(p + 1);
> int lladdrlen = p->nd_opt_len << 3;
> int prepad = ndisc_addr_option_pad(dev->type);
> - if (lladdrlen != ndisc_opt_addr_space(dev, dev->addr_len))
> + if (lladdrlen != ndisc_opt_addr_space(dev, addr_len))
> return NULL;
> return lladdr + prepad;
> }
> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
> index 69e20e3..4e91d5e 100644
> --- a/net/ipv6/ndisc.c
> +++ b/net/ipv6/ndisc.c
> @@ -744,7 +744,8 @@ static void ndisc_recv_ns(struct sk_buff *skb)
> }
>
> if (ndopts.nd_opts_src_lladdr) {
> - lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
> + lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev,
> + dev->addr_len);
> if (!lladdr) {
> ND_PRINTK(2, warn,
> "NS: invalid link-layer address length\n");
> @@ -916,7 +917,8 @@ static void ndisc_recv_na(struct sk_buff *skb)
> return;
> }
> if (ndopts.nd_opts_tgt_lladdr) {
> - lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);
> + lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev,
> + dev->addr_len);
> if (!lladdr) {
> ND_PRINTK(2, warn,
> "NA: invalid link-layer address length\n");
> @@ -1024,7 +1026,7 @@ static void ndisc_recv_rs(struct sk_buff *skb)
>
> if (ndopts.nd_opts_src_lladdr) {
> lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
> - skb->dev);
> + skb->dev, skb->dev->addr_len);
> if (!lladdr)
> goto out;
> }
> @@ -1322,7 +1324,8 @@ skip_linkparms:
> u8 *lladdr = NULL;
> if (ndopts.nd_opts_src_lladdr) {
> lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
> - skb->dev);
> + skb->dev,
> + skb->dev->addr_len);
> if (!lladdr) {
> ND_PRINTK(2, warn,
> "RA: invalid link-layer address length\n");
> diff --git a/net/ipv6/route.c b/net/ipv6/route.c
> index ed44663..cc180b3 100644
> --- a/net/ipv6/route.c
> +++ b/net/ipv6/route.c
> @@ -2157,7 +2157,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
> lladdr = NULL;
> if (ndopts.nd_opts_tgt_lladdr) {
> lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
> - skb->dev);
> + skb->dev, skb->dev->addr_len);
> if (!lladdr) {
> net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
> return;

Reviewed-by: Stefan Schmidt<[email protected]>

regards
Stefan Schmidt

2016-05-03 18:17:03

by Stefan Schmidt

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 04/10] ndisc: add addr_len parameter to ndisc_opt_addr_space

Hello.

On 20/04/16 10:19, Alexander Aring wrote:
> This patch makes the address length as argument for the
> ndisc_opt_addr_space function. This is necessary to handle addresses
> which don't use dev->addr_len as address length.
>
> 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 | 8 ++++----
> net/ipv6/ndisc.c | 10 +++++-----
> 2 files changed, 9 insertions(+), 9 deletions(-)
>
> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
> index 2d8edaa..ef43e88 100644
> --- a/include/net/ndisc.h
> +++ b/include/net/ndisc.h
> @@ -127,10 +127,10 @@ static inline int ndisc_addr_option_pad(unsigned short type)
> }
> }
>
> -static inline int ndisc_opt_addr_space(struct net_device *dev)
> +static inline int ndisc_opt_addr_space(struct net_device *dev,
> + unsigned char addr_len)
> {
> - return NDISC_OPT_SPACE(dev->addr_len +
> - ndisc_addr_option_pad(dev->type));
> + return NDISC_OPT_SPACE(addr_len + ndisc_addr_option_pad(dev->type));
> }
>
> static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,
> @@ -139,7 +139,7 @@ static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,
> u8 *lladdr = (u8 *)(p + 1);
> int lladdrlen = p->nd_opt_len << 3;
> int prepad = ndisc_addr_option_pad(dev->type);
> - if (lladdrlen != ndisc_opt_addr_space(dev))
> + if (lladdrlen != ndisc_opt_addr_space(dev, dev->addr_len))
> return NULL;
> return lladdr + prepad;
> }
> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
> index c245895..69e20e3 100644
> --- a/net/ipv6/ndisc.c
> +++ b/net/ipv6/ndisc.c
> @@ -154,7 +154,7 @@ static void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data)
> {
> int pad = ndisc_addr_option_pad(skb->dev->type);
> int data_len = skb->dev->addr_len;
> - int space = ndisc_opt_addr_space(skb->dev);
> + int space = ndisc_opt_addr_space(skb->dev, skb->dev->addr_len);
> u8 *opt = skb_put(skb, space);
>
> opt[0] = type;
> @@ -509,7 +509,7 @@ void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
> if (!dev->addr_len)
> inc_opt = 0;
> if (inc_opt)
> - optlen += ndisc_opt_addr_space(dev);
> + optlen += ndisc_opt_addr_space(dev, dev->addr_len);
>
> skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
> if (!skb)
> @@ -574,7 +574,7 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
> if (ipv6_addr_any(saddr))
> inc_opt = false;
> if (inc_opt)
> - optlen += ndisc_opt_addr_space(dev);
> + optlen += ndisc_opt_addr_space(dev, dev->addr_len);
>
> skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
> if (!skb)
> @@ -626,7 +626,7 @@ void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
> }
> #endif
> if (send_sllao)
> - optlen += ndisc_opt_addr_space(dev);
> + optlen += ndisc_opt_addr_space(dev, dev->addr_len);
>
> skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
> if (!skb)
> @@ -1563,7 +1563,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
> memcpy(ha_buf, neigh->ha, dev->addr_len);
> read_unlock_bh(&neigh->lock);
> ha = ha_buf;
> - optlen += ndisc_opt_addr_space(dev);
> + optlen += ndisc_opt_addr_space(dev, dev->addr_len);
> } else
> read_unlock_bh(&neigh->lock);
>

Reviewed-by: Stefan Schmidt<[email protected]>

regards
Stefan Schmidt

2016-05-03 18:16:53

by Stefan Schmidt

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 03/10] 6lowpan: remove ipv6 module request

Hello.

On 20/04/16 10:19, Alexander Aring wrote:
> Since we use exported function from ipv6 kernel module we don't need to
> request the module anymore to have ipv6 functionality.
>
> Signed-off-by: Alexander Aring<[email protected]>
> ---
> net/6lowpan/core.c | 2 --
> 1 file changed, 2 deletions(-)
>
> diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
> index fbae31e..824d1bc 100644
> --- a/net/6lowpan/core.c
> +++ b/net/6lowpan/core.c
> @@ -158,8 +158,6 @@ static int __init lowpan_module_init(void)
> return ret;
> }
>
> - request_module_nowait("ipv6");
> -
> request_module_nowait("nhc_dest");
> request_module_nowait("nhc_fragment");
> request_module_nowait("nhc_hop");
Good point.

Reviewed-by: Stefan Schmidt<[email protected]>

regards
Stefan Schmidt

2016-05-03 18:16:42

by Stefan Schmidt

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 02/10] 6lowpan: add 802.15.4 short addr slaac

Hello.

On 20/04/16 10:19, Alexander Aring wrote:
> This patch adds the autoconfiguration if a valid 802.15.4 short address
> is available for 802.15.4 6LoWPAN interfaces.
>
> 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/addrconf.h | 3 +++
> net/6lowpan/core.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
> net/ipv6/addrconf.c | 5 +++--
> 3 files changed, 52 insertions(+), 2 deletions(-)
>
> diff --git a/include/net/addrconf.h b/include/net/addrconf.h
> index 730d856..b1774eb 100644
> --- a/include/net/addrconf.h
> +++ b/include/net/addrconf.h
> @@ -94,6 +94,9 @@ int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2,
> void addrconf_join_solict(struct net_device *dev, const struct in6_addr *addr);
> void addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr *addr);
>
> +void addrconf_add_linklocal(struct inet6_dev *idev,
> + const struct in6_addr *addr, u32 flags);
> +
> static inline int addrconf_ifid_eui48(u8 *eui, struct net_device *dev)
> {
> if (dev->addr_len != ETH_ALEN)
> diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
> index 7a240b3..fbae31e 100644
> --- a/net/6lowpan/core.c
> +++ b/net/6lowpan/core.c
> @@ -14,6 +14,7 @@
> #include <linux/module.h>
>
> #include <net/6lowpan.h>
> +#include <net/addrconf.h>
>
> #include "6lowpan_i.h"
>
> @@ -72,16 +73,61 @@ void lowpan_unregister_netdev(struct net_device *dev)
> }
> EXPORT_SYMBOL(lowpan_unregister_netdev);
>
> +static int addrconf_ifid_802154_6lowpan(u8 *eui, struct net_device *dev)
> +{
> + struct wpan_dev *wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr;
> +
> + /* Set short_addr autoconfiguration if short_addr is present only */
> + if (!ieee802154_is_valid_src_short_addr(wpan_dev->short_addr))
> + return -1;

-EINVAL instead of -1?

> +
> + /* For either address format, all zero addresses MUST NOT be used */
> + if (wpan_dev->pan_id == cpu_to_le16(0x0000) &&
> + wpan_dev->short_addr == cpu_to_le16(0x0000))
> + return -1;

-EINVAL instead of -1?
> +
> + /* Alternatively, if no PAN ID is known, 16 zero bits may be used */
> + if (wpan_dev->pan_id == cpu_to_le16(IEEE802154_PAN_ID_BROADCAST))
> + memset(eui, 0, 2);
> + else
> + ieee802154_le16_to_be16(eui, &wpan_dev->pan_id);
> +
> + /* The "Universal/Local" (U/L) bit shall be set to zero */
> + eui[0] &= ~2;
> + eui[2] = 0;
> + eui[3] = 0xFF;
> + eui[4] = 0xFE;
> + eui[5] = 0;
> + ieee802154_le16_to_be16(&eui[6], &wpan_dev->short_addr);
> + return 0;
> +}
> +
> static int lowpan_event(struct notifier_block *unused,
> unsigned long event, void *ptr)
> {
> struct net_device *dev = netdev_notifier_info_to_dev(ptr);
> + struct inet6_dev *idev;
> + struct in6_addr addr;
> int i;
>
> if (dev->type != ARPHRD_6LOWPAN)
> return NOTIFY_DONE;
>
> + idev = __in6_dev_get(dev);
> + if (!idev)
> + return NOTIFY_DONE;
> +
> switch (event) {
> + case NETDEV_UP:
> + case NETDEV_CHANGE:
> + /* (802.15.4 6LoWPAN short address slaac handling */
> + if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154) &&
> + addrconf_ifid_802154_6lowpan(addr.s6_addr + 8, dev) == 0) {

I normally would like to get a define here instead of the magic number
8, but given how complex this if statement already is I think its fine
to keep it.

> + __ipv6_addr_set_half(&addr.s6_addr32[0],
> + htonl(0xFE800000), 0);
> + addrconf_add_linklocal(idev, &addr, 0);
> + }
> + break;
> case NETDEV_DOWN:
> for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
> clear_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE,
> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index 27aed1a..54e18c2 100644
> --- a/net/ipv6/addrconf.c
> +++ b/net/ipv6/addrconf.c
> @@ -2948,8 +2948,8 @@ static void init_loopback(struct net_device *dev)
> }
> }
>
> -static void addrconf_add_linklocal(struct inet6_dev *idev,
> - const struct in6_addr *addr, u32 flags)
> +void addrconf_add_linklocal(struct inet6_dev *idev,
> + const struct in6_addr *addr, u32 flags)
> {
> struct inet6_ifaddr *ifp;
> u32 addr_flags = flags | IFA_F_PERMANENT;
> @@ -2968,6 +2968,7 @@ static void addrconf_add_linklocal(struct inet6_dev *idev,
> in6_ifa_put(ifp);
> }
> }
> +EXPORT_SYMBOL(addrconf_add_linklocal);
>
> static bool ipv6_reserved_interfaceid(struct in6_addr address)
> {

Reviewed-by: Stefan Schmidt<[email protected]>

regards
Stefan Schmidt

2016-05-03 18:16:32

by Stefan Schmidt

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 01/10] 6lowpan: add private neighbour data

Hello.

On 20/04/16 10:19, Alexander Aring wrote:
> This patch will introduce a 6lowpan neighbour private data. Like the
> interface private data we handle private data for generic 6lowpan and
> for link-layer specific 6lowpan.
>
> The current first use case if to save the short address for a 802.15.4
> 6lowpan neighbour.
>
> Cc: David S. Miller<[email protected]>
> Signed-off-by: Alexander Aring<[email protected]>
> ---
> include/linux/netdevice.h | 3 +--
> include/net/6lowpan.h | 24 ++++++++++++++++++++++++
> net/bluetooth/6lowpan.c | 2 ++
> net/ieee802154/6lowpan/core.c | 12 ++++++++++++
> 4 files changed, 39 insertions(+), 2 deletions(-)
>
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index 166402a..0052c42 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -1487,8 +1487,7 @@ enum netdev_priv_flags {
> * @perm_addr: Permanent hw address
> * @addr_assign_type: Hw address assignment type
> * @addr_len: Hardware address length
> - * @neigh_priv_len; Used in neigh_alloc(),
> - * initialized only in atm/clip.c
> + * @neigh_priv_len; Used in neigh_alloc()
> * @dev_id: Used to differentiate devices that share
> * the same link layer address
> * @dev_port: Used to differentiate devices that share
> diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
> index da84cf9..61c6517 100644
> --- a/include/net/6lowpan.h
> +++ b/include/net/6lowpan.h
> @@ -98,6 +98,9 @@ static inline bool lowpan_is_iphc(u8 dispatch)
> #define LOWPAN_PRIV_SIZE(llpriv_size) \
> (sizeof(struct lowpan_dev) + llpriv_size)
>
> +#define LOWPAN_NEIGH_PRIV_SIZE(llneigh_priv_size) \
> + (sizeof(struct lowpan_neigh) + llneigh_priv_size)
> +
> enum lowpan_lltypes {
> LOWPAN_LLTYPE_BTLE,
> LOWPAN_LLTYPE_IEEE802154,
> @@ -141,6 +144,27 @@ struct lowpan_dev {
> u8 priv[0] __aligned(sizeof(void *));
> };
>
> +struct lowpan_neigh {
> + /* 6LoWPAN neigh private data */
> + /* must be last */
> + u8 priv[0] __aligned(sizeof(void *));
> +};
> +
> +struct lowpan_802154_neigh {
> + __le16 short_addr;
> +};
> +
> +static inline struct lowpan_neigh *lowpan_neigh(void *neigh_priv)
> +{
> + return neigh_priv;
> +}
> +
> +static inline
> +struct lowpan_802154_neigh *lowpan_802154_neigh(void *neigh_priv)
> +{
> + return (struct lowpan_802154_neigh *)lowpan_neigh(neigh_priv)->priv;
> +}
> +
> static inline
> struct lowpan_dev *lowpan_dev(const struct net_device *dev)
> {
> diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
> index 38e82dd..b7c4efa 100644
> --- a/net/bluetooth/6lowpan.c
> +++ b/net/bluetooth/6lowpan.c
> @@ -833,6 +833,8 @@ static int setup_netdev(struct l2cap_chan *chan, struct lowpan_btle_dev **dev)
> list_add_rcu(&(*dev)->list, &bt_6lowpan_devices);
> spin_unlock(&devices_lock);
>
> + netdev->neigh_priv_len = LOWPAN_NEIGH_PRIV_SIZE(0);
> +
> err = lowpan_register_netdev(netdev, LOWPAN_LLTYPE_BTLE);
> if (err < 0) {
> BT_INFO("register_netdev failed %d", err);
> diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c
> index dd085db..3162632 100644
> --- a/net/ieee802154/6lowpan/core.c
> +++ b/net/ieee802154/6lowpan/core.c
> @@ -92,11 +92,21 @@ static int lowpan_stop(struct net_device *dev)
> return 0;
> }
>
> +static int lowpan_neigh_construct(struct neighbour *n)
> +{
> + struct lowpan_802154_neigh *neigh = lowpan_802154_neigh(neighbour_priv(n));
> +
> + /* default no short_addr is available for a neighbour */
> + neigh->short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);
> + return 0;
> +}
> +
> static const struct net_device_ops lowpan_netdev_ops = {
> .ndo_init = lowpan_dev_init,
> .ndo_start_xmit = lowpan_xmit,
> .ndo_open = lowpan_open,
> .ndo_stop = lowpan_stop,
> + .ndo_neigh_construct = lowpan_neigh_construct,
> };
>
> static void lowpan_setup(struct net_device *ldev)
> @@ -161,6 +171,8 @@ static int lowpan_newlink(struct net *src_net, struct net_device *ldev,
> wdev->needed_headroom;
> ldev->needed_tailroom = wdev->needed_tailroom;
>
> + ldev->neigh_priv_len = LOWPAN_NEIGH_PRIV_SIZE(sizeof(struct lowpan_802154_neigh));
> +
> ret = lowpan_register_netdevice(ldev, LOWPAN_LLTYPE_IEEE802154);
> if (ret < 0) {
> dev_put(wdev);

Reviewed-by: Stefan Schmidt<[email protected]>

regards
Stefan Schmidt

2016-05-02 23:17:41

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 00/10] 6lowpan: introduce basic 6lowpan-nd

Hi Hannes,

>>> this patch series introduces a layer for IPv6 neighbour discovery. At first
>>> it introduce the "ndisc_ops" to run a different handling for recv/send of
>>> NA/NS messages. The use case for such ndisc operation is RFC 6775 [0].
>>> Which describes a different neighbour discovery handling for 6LoWPAN networks.
>>>
>>> I didn't implement RFC 6775 in this patch series, but introduce callback
>>> structure for replace different functions in ndisc implementation might be
>>> the right direction.
>>>
>>> Another use case would be RFC 7400 [1] which describes a new option field to
>>> getting capabilities of 6LoWPAN next header compression methods.
>>>
>>> What I implemented is a necessary functionality to handle short address for
>>> 802.15.4 6LoWPAN networks. The L2-Layer "802.15.4" can have two different
>>> link-layer addresses which can be used mixed at the same time inside 802.15.4
>>> networks. To deal with such behaviour in ndisc, it is defined at RFC 4944 [2].
>>> The bad news is, that I saw different handling of such handling. What Linux
>>> will do is to add two source/target address information option fields, each
>>> with different length, if short address is valid (can also not be given).
>>> Example:
>>>
>>> - WPAN interface address settings
>>> - extended addr (must always be there)
>>> - short addr (0xfffe or 0xffff -> invalid)
>>>
>>> Will add an extended addr to source/target address information option field.
>>> If short addr is in some valid range, then both address will be added to
>>> the option fields. Indicated are these different address types by the length
>>> field (extended -> length=2, short -> length=1), according to [1].
>>>
>>> The tested 6LoWPAN implementation (RIOT-OS) allows only one source/target
>>> option field which is short XOR extended, otherwise it will be dropped.
>>> There is some lack of information there [2] and I don't know how do deal with
>>> it right, maybe we need to update the implementation there if it's really
>>> wrong.
>>>
>>> To save such information for each neighbour we use the already implemented
>>> neighbour private data which some casting strategy for 6LoWPAN and 6LoWPAN
>>> link-layer specific data e.g. 802.15.4 short address handling.
>>>
>>> Additional I implemented to add 6CO to the is_useropt callback in case of
>>> 6LoWPAN interface. The 6CO option will currently parsed in userspace which
>>> are placed in RA-Messages.
>>>
>>> The ndisc_ops are not finished yet, of course we need handling for RS messages
>>> to place the 802.15.4 short address there as well and then also processing
>>> of RA messages for the 802.15.4 SLLAO option field.
>>>
>>> - Alex
>>>
>>> [0] https://tools.ietf.org/html/rfc6775
>>> [1] https://tools.ietf.org/html/rfc7400#section-3.3
>>> [2] https://tools.ietf.org/html/rfc4944#section-8
>>>
>>> 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]>
>>>
>>> changes since v2:
>>> - replace #ifdef CONFIG_IPV6 to #if IS_ENABLED(...)
>>> - replace #ifdef CONFIG_IEEE802154... to #if IS_ENABLED(...)
>>> - add more #if IS_ENABLED(CONFIG_IPV6) in ndisc.h
>>>
>>> Alexander Aring (10):
>>> 6lowpan: add private neighbour data
>>> 6lowpan: add 802.15.4 short addr slaac
>>> 6lowpan: remove ipv6 module request
>>> ndisc: add addr_len parameter to ndisc_opt_addr_space
>>> ndisc: add addr_len parameter to ndisc_opt_addr_data
>>> ndisc: add addr_len parameter to ndisc_fill_addr_option
>>> ipv6: introduce neighbour discovery ops
>>> ipv6: export ndisc functions
>>> 6lowpan: introduce 6lowpan-nd
>>> 6lowpan: add support for 802.15.4 short addr handling
>>>
>>> include/linux/netdevice.h | 6 +-
>>> include/net/6lowpan.h | 24 ++
>>> include/net/addrconf.h | 3 +
>>> include/net/ndisc.h | 124 ++++++++-
>>> net/6lowpan/6lowpan_i.h | 2 +
>>> net/6lowpan/Makefile | 2 +-
>>> net/6lowpan/core.c | 50 +++-
>>> net/6lowpan/iphc.c | 167 +++++++++--
>>> net/6lowpan/ndisc.c | 633 ++++++++++++++++++++++++++++++++++++++++++
>>> net/bluetooth/6lowpan.c | 2 +
>>> net/ieee802154/6lowpan/core.c | 12 +
>>> net/ieee802154/6lowpan/tx.c | 107 ++++---
>>> net/ipv6/addrconf.c | 7 +-
>>> net/ipv6/ndisc.c | 132 +++++----
>>> net/ipv6/route.c | 4 +-
>>> 15 files changed, 1117 insertions(+), 158 deletions(-)
>>> create mode 100644 net/6lowpan/ndisc.c
>>
>> is there a chance that we get input into this patch set? I wonder also if it would be acceptable to take this through bluetooth-next or should it better go straight into net-next?
>
> My proposal would be that the IPv6 patches go via net-next to reduce
> merge conflicts with maybe upcoming changes. If they are split up, they
> seem very much self contained and easy to review. The rest seems to be
> also very much self contained and can go in via bluetooth-next, then.
> What do you think?

I am actually fine with having this all go via net-next. We only have driver patches pending in bluetooth-next for the next merge window. Which means I can just pull net-next back into bluetooth-next at any time.

Regards

Marcel


2016-05-02 19:52:09

by Hannes Frederic Sowa

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 00/10] 6lowpan: introduce basic 6lowpan-nd

On 02.05.2016 19:25, Marcel Holtmann wrote:
> Hi Dave,
>
>> this patch series introduces a layer for IPv6 neighbour discovery. At first
>> it introduce the "ndisc_ops" to run a different handling for recv/send of
>> NA/NS messages. The use case for such ndisc operation is RFC 6775 [0].
>> Which describes a different neighbour discovery handling for 6LoWPAN networks.
>>
>> I didn't implement RFC 6775 in this patch series, but introduce callback
>> structure for replace different functions in ndisc implementation might be
>> the right direction.
>>
>> Another use case would be RFC 7400 [1] which describes a new option field to
>> getting capabilities of 6LoWPAN next header compression methods.
>>
>> What I implemented is a necessary functionality to handle short address for
>> 802.15.4 6LoWPAN networks. The L2-Layer "802.15.4" can have two different
>> link-layer addresses which can be used mixed at the same time inside 802.15.4
>> networks. To deal with such behaviour in ndisc, it is defined at RFC 4944 [2].
>> The bad news is, that I saw different handling of such handling. What Linux
>> will do is to add two source/target address information option fields, each
>> with different length, if short address is valid (can also not be given).
>> Example:
>>
>> - WPAN interface address settings
>> - extended addr (must always be there)
>> - short addr (0xfffe or 0xffff -> invalid)
>>
>> Will add an extended addr to source/target address information option field.
>> If short addr is in some valid range, then both address will be added to
>> the option fields. Indicated are these different address types by the length
>> field (extended -> length=2, short -> length=1), according to [1].
>>
>> The tested 6LoWPAN implementation (RIOT-OS) allows only one source/target
>> option field which is short XOR extended, otherwise it will be dropped.
>> There is some lack of information there [2] and I don't know how do deal with
>> it right, maybe we need to update the implementation there if it's really
>> wrong.
>>
>> To save such information for each neighbour we use the already implemented
>> neighbour private data which some casting strategy for 6LoWPAN and 6LoWPAN
>> link-layer specific data e.g. 802.15.4 short address handling.
>>
>> Additional I implemented to add 6CO to the is_useropt callback in case of
>> 6LoWPAN interface. The 6CO option will currently parsed in userspace which
>> are placed in RA-Messages.
>>
>> The ndisc_ops are not finished yet, of course we need handling for RS messages
>> to place the 802.15.4 short address there as well and then also processing
>> of RA messages for the 802.15.4 SLLAO option field.
>>
>> - Alex
>>
>> [0] https://tools.ietf.org/html/rfc6775
>> [1] https://tools.ietf.org/html/rfc7400#section-3.3
>> [2] https://tools.ietf.org/html/rfc4944#section-8
>>
>> 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]>
>>
>> changes since v2:
>> - replace #ifdef CONFIG_IPV6 to #if IS_ENABLED(...)
>> - replace #ifdef CONFIG_IEEE802154... to #if IS_ENABLED(...)
>> - add more #if IS_ENABLED(CONFIG_IPV6) in ndisc.h
>>
>> Alexander Aring (10):
>> 6lowpan: add private neighbour data
>> 6lowpan: add 802.15.4 short addr slaac
>> 6lowpan: remove ipv6 module request
>> ndisc: add addr_len parameter to ndisc_opt_addr_space
>> ndisc: add addr_len parameter to ndisc_opt_addr_data
>> ndisc: add addr_len parameter to ndisc_fill_addr_option
>> ipv6: introduce neighbour discovery ops
>> ipv6: export ndisc functions
>> 6lowpan: introduce 6lowpan-nd
>> 6lowpan: add support for 802.15.4 short addr handling
>>
>> include/linux/netdevice.h | 6 +-
>> include/net/6lowpan.h | 24 ++
>> include/net/addrconf.h | 3 +
>> include/net/ndisc.h | 124 ++++++++-
>> net/6lowpan/6lowpan_i.h | 2 +
>> net/6lowpan/Makefile | 2 +-
>> net/6lowpan/core.c | 50 +++-
>> net/6lowpan/iphc.c | 167 +++++++++--
>> net/6lowpan/ndisc.c | 633 ++++++++++++++++++++++++++++++++++++++++++
>> net/bluetooth/6lowpan.c | 2 +
>> net/ieee802154/6lowpan/core.c | 12 +
>> net/ieee802154/6lowpan/tx.c | 107 ++++---
>> net/ipv6/addrconf.c | 7 +-
>> net/ipv6/ndisc.c | 132 +++++----
>> net/ipv6/route.c | 4 +-
>> 15 files changed, 1117 insertions(+), 158 deletions(-)
>> create mode 100644 net/6lowpan/ndisc.c
>
> is there a chance that we get input into this patch set? I wonder also if it would be acceptable to take this through bluetooth-next or should it better go straight into net-next?

My proposal would be that the IPv6 patches go via net-next to reduce
merge conflicts with maybe upcoming changes. If they are split up, they
seem very much self contained and easy to review. The rest seems to be
also very much self contained and can go in via bluetooth-next, then.
What do you think?

Bye,
Hannes

2016-05-02 19:39:08

by Hannes Frederic Sowa

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 08/10] ipv6: export ndisc functions

On 20.04.2016 10:19, Alexander Aring wrote:
> This patch exports some neighbour discovery functions which can be used
> by 6lowpan neighbour discovery ops functionality then.
>
> 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 | 16 ++++++++++++++++
> net/ipv6/addrconf.c | 1 +
> net/ipv6/ndisc.c | 28 ++++++++++------------------
> 3 files changed, 27 insertions(+), 18 deletions(-)
>
> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
> index 14ed016..35a4396 100644
> --- a/include/net/ndisc.h
> +++ b/include/net/ndisc.h
> @@ -53,6 +53,15 @@ enum {
>
> #include <net/neighbour.h>
>
> +/* Set to 3 to get tracing... */
> +#define ND_DEBUG 1
> +
> +#define ND_PRINTK(val, level, fmt, ...) \
> +do { \
> + if (val <= ND_DEBUG) \
> + net_##level##_ratelimited(fmt, ##__VA_ARGS__); \
> +} while (0)
> +

If the debug messages are well thought out, I think we could install
them always on debug level.

There are valid users now, so

Acked-by: Hannes Frederic Sowa <[email protected]>

2016-05-02 19:37:14

by Hannes Frederic Sowa

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 04/10] ndisc: add addr_len parameter to ndisc_opt_addr_space

On 20.04.2016 10:19, Alexander Aring wrote:
> This patch makes the address length as argument for the
> ndisc_opt_addr_space function. This is necessary to handle addresses
> which don't use dev->addr_len as address length.

Would it make sense for patch 4, 5 and 6 to add the operation to ndisc_ops?

Thanks,
Hannes

2016-05-02 19:36:13

by Hannes Frederic Sowa

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 07/10] ipv6: introduce neighbour discovery ops

On 20.04.2016 10:19, Alexander Aring wrote:
> This patch introduces neighbour discovery ops callback structure. The
> structure contains at first receive and transmit handling for NS/NA and
> userspace option field functionality.
>
> These callback offers 6lowpan different handling, such as 802.15.4 short
> address handling or RFC6775 (Neighbor Discovery Optimization for IPv6 over
> 6LoWPANs).
>
> 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/linux/netdevice.h | 3 ++
> include/net/ndisc.h | 96 +++++++++++++++++++++++++++++++++++++++++++----
> net/ipv6/addrconf.c | 1 +
> net/ipv6/ndisc.c | 71 ++++++++++++++++++++++++-----------
> net/ipv6/route.c | 2 +-
> 5 files changed, 144 insertions(+), 29 deletions(-)
>
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index 0052c42..bc60033 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -1677,6 +1677,9 @@ struct net_device {
> #ifdef CONFIG_NET_L3_MASTER_DEV
> const struct l3mdev_ops *l3mdev_ops;
> #endif
> +#if IS_ENABLED(CONFIG_IPV6)
> + const struct ndisc_ops *ndisc_ops;
> +#endif
>
> const struct header_ops *header_ops;
>
> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
> index aac868e..14ed016 100644
> --- a/include/net/ndisc.h
> +++ b/include/net/ndisc.h
> @@ -110,7 +110,8 @@ struct ndisc_options {
>
> #define NDISC_OPT_SPACE(len) (((len)+2+7)&~7)
>
> -struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
> +struct ndisc_options *ndisc_parse_options(const struct net_device *dev,
> + u8 *opt, int opt_len,
> struct ndisc_options *ndopts);
>
> /*
> @@ -173,6 +174,93 @@ static inline struct neighbour *__ipv6_neigh_lookup(struct net_device *dev, cons
> return n;
> }
>
> +static inline int __ip6_ndisc_is_useropt(struct nd_opt_hdr *opt)
> +{
> + return opt->nd_opt_type == ND_OPT_RDNSS ||
> + opt->nd_opt_type == ND_OPT_DNSSL;
> +}
> +
> +#if IS_ENABLED(CONFIG_IPV6)
> +struct ndisc_ops {
> + int (*is_useropt)(struct nd_opt_hdr *opt);
> + void (*send_na)(struct net_device *dev,
> + const struct in6_addr *daddr,
> + const struct in6_addr *solicited_addr,
> + bool router, bool solicited,
> + bool override, bool inc_opt);
> + void (*recv_na)(struct sk_buff *skb);
> + void (*send_ns)(struct net_device *dev,
> + const struct in6_addr *solicit,
> + const struct in6_addr *daddr,
> + const struct in6_addr *saddr);
> + void (*recv_ns)(struct sk_buff *skb);
> +};
> +
> +static inline int ndisc_is_useropt(const struct net_device *dev,
> + struct nd_opt_hdr *opt)
> +{
> + if (likely(dev->ndisc_ops->is_useropt))
> + return dev->ndisc_ops->is_useropt(opt);
> + else
> + return 0;
> +}
> +
> +static inline void ndisc_send_na(struct net_device *dev,
> + const struct in6_addr *daddr,
> + const struct in6_addr *solicited_addr,
> + bool router, bool solicited, bool override,
> + bool inc_opt)
> +{
> + if (likely(dev->ndisc_ops->send_na))
> + dev->ndisc_ops->send_na(dev, daddr, solicited_addr, router,
> + solicited, override, inc_opt);
> +}
> +
> +static inline void ndisc_recv_na(struct sk_buff *skb)
> +{
> + if (likely(skb->dev->ndisc_ops->recv_na))
> + skb->dev->ndisc_ops->recv_na(skb);
> +}
> +
> +static inline void ndisc_send_ns(struct net_device *dev,
> + const struct in6_addr *solicit,
> + const struct in6_addr *daddr,
> + const struct in6_addr *saddr)
> +{
> + if (likely(dev->ndisc_ops->send_ns))
> + dev->ndisc_ops->send_ns(dev, solicit, daddr, saddr);
> +}
> +
> +static inline void ndisc_recv_ns(struct sk_buff *skb)
> +{
> + if (likely(skb->dev->ndisc_ops->recv_ns))
> + skb->dev->ndisc_ops->recv_ns(skb);
> +}
> +#else
> +static inline int ndisc_is_useropt(const struct net_device *dev,
> + struct nd_opt_hdr *opt)
> +{
> + return 0;
> +}
> +
> +static inline void ndisc_send_na(struct net_device *dev,
> + const struct in6_addr *daddr,
> + const struct in6_addr *solicited_addr,
> + bool router, bool solicited, bool override,
> + bool inc_opt) { }
> +
> +static inline void ndisc_recv_na(struct sk_buff *skb) { }
> +
> +static inline void ndisc_send_ns(struct net_device *dev,
> + const struct in6_addr *solicit,
> + const struct in6_addr *daddr,
> + const struct in6_addr *saddr) { }
> +
> +static inline void ndisc_recv_ns(struct sk_buff *skb) { }
> +#endif

Do those empty functions actually make sense? I wonder a bit because
6lowpan strictly depends on ipv6 and they should never be called without
IPv6, no?

> +
> +void ip6_register_ndisc_ops(struct net_device *dev);
> +
> int ndisc_init(void);
> int ndisc_late_init(void);
>
> @@ -181,14 +269,8 @@ void ndisc_cleanup(void);
>
> int ndisc_rcv(struct sk_buff *skb);
>
> -void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
> - const struct in6_addr *daddr, const struct in6_addr *saddr);
> -
> void ndisc_send_rs(struct net_device *dev,
> const struct in6_addr *saddr, const struct in6_addr *daddr);
> -void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
> - const struct in6_addr *solicited_addr,
> - bool router, bool solicited, bool override, bool inc_opt);
>
> void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target);
>
> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index 54e18c2..a2ef04b 100644
> --- a/net/ipv6/addrconf.c
> +++ b/net/ipv6/addrconf.c
> @@ -3266,6 +3266,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
> idev = ipv6_add_dev(dev);
> if (IS_ERR(idev))
> return notifier_from_errno(PTR_ERR(idev));
> + ip6_register_ndisc_ops(dev);

Is it possible to register the ndisc options before we make the device
visible to the stack? Maybe even as a pointer to ipv6_add_dev.

> }
> break;
>
> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
> index 176c7c4..297080a 100644
> --- a/net/ipv6/ndisc.c
> +++ b/net/ipv6/ndisc.c
> @@ -185,24 +185,25 @@ static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
> return cur <= end && cur->nd_opt_type == type ? cur : NULL;
> }
>
> -static inline int ndisc_is_useropt(struct nd_opt_hdr *opt)
> +static inline int ip6_ndisc_is_useropt(struct nd_opt_hdr *opt)
> {
> - return opt->nd_opt_type == ND_OPT_RDNSS ||
> - opt->nd_opt_type == ND_OPT_DNSSL;
> + return __ip6_ndisc_is_useropt(opt);
> }

inline in C functions are not necessary.

>
> -static struct nd_opt_hdr *ndisc_next_useropt(struct nd_opt_hdr *cur,
> +static struct nd_opt_hdr *ndisc_next_useropt(const struct net_device *dev,
> + struct nd_opt_hdr *cur,
> struct nd_opt_hdr *end)
> {
> if (!cur || !end || cur >= end)
> return NULL;
> do {
> cur = ((void *)cur) + (cur->nd_opt_len << 3);
> - } while (cur < end && !ndisc_is_useropt(cur));
> - return cur <= end && ndisc_is_useropt(cur) ? cur : NULL;
> + } while (cur < end && !ndisc_is_useropt(dev, cur));
> + return cur <= end && ndisc_is_useropt(dev, cur) ? cur : NULL;
> }
>
> -struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
> +struct ndisc_options *ndisc_parse_options(const struct net_device *dev,
> + u8 *opt, int opt_len,
> struct ndisc_options *ndopts)
> {
> struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)opt;
> @@ -243,7 +244,7 @@ struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
> break;
> #endif
> default:
> - if (ndisc_is_useropt(nd_opt)) {
> + if (ndisc_is_useropt(dev, nd_opt)) {
> ndopts->nd_useropts_end = nd_opt;
> if (!ndopts->nd_useropts)
> ndopts->nd_useropts = nd_opt;
> @@ -479,9 +480,11 @@ static void ndisc_send_skb(struct sk_buff *skb,
> rcu_read_unlock();
> }
>
> -void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
> - const struct in6_addr *solicited_addr,
> - bool router, bool solicited, bool override, bool inc_opt)
> +static void ip6_ndisc_send_na(struct net_device *dev,
> + const struct in6_addr *daddr,
> + const struct in6_addr *solicited_addr,
> + bool router, bool solicited, bool override,
> + bool inc_opt)
> {
> struct sk_buff *skb;
> struct in6_addr tmpaddr;
> @@ -555,8 +558,10 @@ static void ndisc_send_unsol_na(struct net_device *dev)
> in6_dev_put(idev);
> }
>
> -void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
> - const struct in6_addr *daddr, const struct in6_addr *saddr)
> +static void ip6_ndisc_send_ns(struct net_device *dev,
> + const struct in6_addr *solicit,
> + const struct in6_addr *daddr,
> + const struct in6_addr *saddr)
> {
> struct sk_buff *skb;
> struct in6_addr addr_buf;
> @@ -702,7 +707,7 @@ static int pndisc_is_router(const void *pkey,
> return ret;
> }
>
> -static void ndisc_recv_ns(struct sk_buff *skb)
> +static void ip6_ndisc_recv_ns(struct sk_buff *skb)
> {
> struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
> const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
> @@ -738,7 +743,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
> return;
> }
>
> - if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
> + if (!ndisc_parse_options(dev, msg->opt, ndoptlen, &ndopts)) {
> ND_PRINTK(2, warn, "NS: invalid ND options\n");
> return;
> }
> @@ -874,7 +879,7 @@ out:
> in6_dev_put(idev);
> }
>
> -static void ndisc_recv_na(struct sk_buff *skb)
> +static void ip6_ndisc_recv_na(struct sk_buff *skb)
> {
> struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
> struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
> @@ -912,7 +917,7 @@ static void ndisc_recv_na(struct sk_buff *skb)
> idev->cnf.drop_unsolicited_na)
> return;
>
> - if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
> + if (!ndisc_parse_options(dev, msg->opt, ndoptlen, &ndopts)) {
> ND_PRINTK(2, warn, "NS: invalid ND option\n");
> return;
> }
> @@ -1019,7 +1024,7 @@ static void ndisc_recv_rs(struct sk_buff *skb)
> goto out;
>
> /* Parse ND options */
> - if (!ndisc_parse_options(rs_msg->opt, ndoptlen, &ndopts)) {
> + if (!ndisc_parse_options(skb->dev, rs_msg->opt, ndoptlen, &ndopts)) {
> ND_PRINTK(2, notice, "NS: invalid ND option, ignored\n");
> goto out;
> }
> @@ -1137,7 +1142,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)
> return;
> }
>
> - if (!ndisc_parse_options(opt, optlen, &ndopts)) {
> + if (!ndisc_parse_options(skb->dev, opt, optlen, &ndopts)) {
> ND_PRINTK(2, warn, "RA: invalid ND options\n");
> return;
> }
> @@ -1424,7 +1429,8 @@ skip_routeinfo:
> struct nd_opt_hdr *p;
> for (p = ndopts.nd_useropts;
> p;
> - p = ndisc_next_useropt(p, ndopts.nd_useropts_end)) {
> + p = ndisc_next_useropt(skb->dev, p,
> + ndopts.nd_useropts_end)) {
> ndisc_ra_useropt(skb, p);
> }
> }
> @@ -1462,7 +1468,7 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
> return;
> }
>
> - if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts))
> + if (!ndisc_parse_options(skb->dev, msg->opt, ndoptlen, &ndopts))
> return;
>
> if (!ndopts.nd_opts_rh) {
> @@ -1783,6 +1789,29 @@ int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, void __user *bu
>
> #endif
>
> +static const struct ndisc_ops ip6_ndisc_ops = {
> + .is_useropt = ip6_ndisc_is_useropt,
> + .send_na = ip6_ndisc_send_na,
> + .recv_na = ip6_ndisc_recv_na,
> + .send_ns = ip6_ndisc_send_ns,
> + .recv_ns = ip6_ndisc_recv_ns,
> +};
> +
> +void ip6_register_ndisc_ops(struct net_device *dev)
> +{
> + switch (dev->type) {
> + default:
> + if (dev->ndisc_ops) {
> + ND_PRINTK(2, warn,
> + "%s: ndisc_ops already defined for interface type=%d\n",
> + __func__, dev->type);
> + } else {
> + dev->ndisc_ops = &ip6_ndisc_ops;
> + }
> + break;

I would be more stricht with validation:

if (!WARN_ON(dev->ndisc_ops))
dev->ndisc_ops = &ip6_ndisc_ops;

> + }
> +}
> +
> static int __net_init ndisc_net_init(struct net *net)
> {
> struct ipv6_pinfo *np;
> diff --git a/net/ipv6/route.c b/net/ipv6/route.c
> index cc180b3..5fa276d 100644
> --- a/net/ipv6/route.c
> +++ b/net/ipv6/route.c
> @@ -2149,7 +2149,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
> * first-hop router for the specified ICMP Destination Address.
> */
>
> - if (!ndisc_parse_options(msg->opt, optlen, &ndopts)) {
> + if (!ndisc_parse_options(skb->dev, msg->opt, optlen, &ndopts)) {
> net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
> return;
> }
>

2016-05-02 19:06:59

by Hannes Frederic Sowa

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 03/10] 6lowpan: remove ipv6 module request

On 20.04.2016 10:19, Alexander Aring wrote:
> Since we use exported function from ipv6 kernel module we don't need to
> request the module anymore to have ipv6 functionality.
>
> Signed-off-by: Alexander Aring <[email protected]>

Acked-by: Hannes Frederic Sowa <[email protected]>

2016-05-02 19:06:26

by Hannes Frederic Sowa

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 02/10] 6lowpan: add 802.15.4 short addr slaac

On 20.04.2016 10:19, Alexander Aring wrote:
> This patch adds the autoconfiguration if a valid 802.15.4 short address
> is available for 802.15.4 6LoWPAN interfaces.
>
> 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]>

Acked-by: Hannes Frederic Sowa <[email protected]>

Thanks,
Hannes

2016-05-02 18:59:13

by Hannes Frederic Sowa

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 01/10] 6lowpan: add private neighbour data

Hello,

On 20.04.2016 10:19, Alexander Aring wrote:
> This patch will introduce a 6lowpan neighbour private data. Like the
> interface private data we handle private data for generic 6lowpan and
> for link-layer specific 6lowpan.
>
> The current first use case if to save the short address for a 802.15.4
> 6lowpan neighbour.
>
> Cc: David S. Miller <[email protected]>
> Signed-off-by: Alexander Aring <[email protected]>
> ---
> include/linux/netdevice.h | 3 +--
> include/net/6lowpan.h | 24 ++++++++++++++++++++++++
> net/bluetooth/6lowpan.c | 2 ++
> net/ieee802154/6lowpan/core.c | 12 ++++++++++++
> 4 files changed, 39 insertions(+), 2 deletions(-)
>
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index 166402a..0052c42 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -1487,8 +1487,7 @@ enum netdev_priv_flags {
> * @perm_addr: Permanent hw address
> * @addr_assign_type: Hw address assignment type
> * @addr_len: Hardware address length
> - * @neigh_priv_len; Used in neigh_alloc(),
> - * initialized only in atm/clip.c
> + * @neigh_priv_len; Used in neigh_alloc()
> * @dev_id: Used to differentiate devices that share
> * the same link layer address
> * @dev_port: Used to differentiate devices that share
> diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
> index da84cf9..61c6517 100644
> --- a/include/net/6lowpan.h
> +++ b/include/net/6lowpan.h
> @@ -98,6 +98,9 @@ static inline bool lowpan_is_iphc(u8 dispatch)
> #define LOWPAN_PRIV_SIZE(llpriv_size) \
> (sizeof(struct lowpan_dev) + llpriv_size)
>
> +#define LOWPAN_NEIGH_PRIV_SIZE(llneigh_priv_size) \
> + (sizeof(struct lowpan_neigh) + llneigh_priv_size)
> +
> enum lowpan_lltypes {
> LOWPAN_LLTYPE_BTLE,
> LOWPAN_LLTYPE_IEEE802154,
> @@ -141,6 +144,27 @@ struct lowpan_dev {
> u8 priv[0] __aligned(sizeof(void *));
> };
>
> +struct lowpan_neigh {
> + /* 6LoWPAN neigh private data */
> + /* must be last */
> + u8 priv[0] __aligned(sizeof(void *));

Are you sure this declaration is correct? You take its size above, which
should result in zero. Looks a little bit strange. :)

> +};
> +
> +struct lowpan_802154_neigh {
> + __le16 short_addr;
> +};
> +
> +static inline struct lowpan_neigh *lowpan_neigh(void *neigh_priv)
> +{
> + return neigh_priv;
> +}
> +
> +static inline
> +struct lowpan_802154_neigh *lowpan_802154_neigh(void *neigh_priv)
> +{
> + return (struct lowpan_802154_neigh *)lowpan_neigh(neigh_priv)->priv;
> +}

Can't you remove lowpan_neigh completely and just use 802154_neigh at
this point?

Bye,
Hannes

2016-05-02 17:25:00

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [PATCHv2 bluetooth-next 00/10] 6lowpan: introduce basic 6lowpan-nd

Hi Dave,

> this patch series introduces a layer for IPv6 neighbour discovery. At first
> it introduce the "ndisc_ops" to run a different handling for recv/send of
> NA/NS messages. The use case for such ndisc operation is RFC 6775 [0].
> Which describes a different neighbour discovery handling for 6LoWPAN networks.
>
> I didn't implement RFC 6775 in this patch series, but introduce callback
> structure for replace different functions in ndisc implementation might be
> the right direction.
>
> Another use case would be RFC 7400 [1] which describes a new option field to
> getting capabilities of 6LoWPAN next header compression methods.
>
> What I implemented is a necessary functionality to handle short address for
> 802.15.4 6LoWPAN networks. The L2-Layer "802.15.4" can have two different
> link-layer addresses which can be used mixed at the same time inside 802.15.4
> networks. To deal with such behaviour in ndisc, it is defined at RFC 4944 [2].
> The bad news is, that I saw different handling of such handling. What Linux
> will do is to add two source/target address information option fields, each
> with different length, if short address is valid (can also not be given).
> Example:
>
> - WPAN interface address settings
> - extended addr (must always be there)
> - short addr (0xfffe or 0xffff -> invalid)
>
> Will add an extended addr to source/target address information option field.
> If short addr is in some valid range, then both address will be added to
> the option fields. Indicated are these different address types by the length
> field (extended -> length=2, short -> length=1), according to [1].
>
> The tested 6LoWPAN implementation (RIOT-OS) allows only one source/target
> option field which is short XOR extended, otherwise it will be dropped.
> There is some lack of information there [2] and I don't know how do deal with
> it right, maybe we need to update the implementation there if it's really
> wrong.
>
> To save such information for each neighbour we use the already implemented
> neighbour private data which some casting strategy for 6LoWPAN and 6LoWPAN
> link-layer specific data e.g. 802.15.4 short address handling.
>
> Additional I implemented to add 6CO to the is_useropt callback in case of
> 6LoWPAN interface. The 6CO option will currently parsed in userspace which
> are placed in RA-Messages.
>
> The ndisc_ops are not finished yet, of course we need handling for RS messages
> to place the 802.15.4 short address there as well and then also processing
> of RA messages for the 802.15.4 SLLAO option field.
>
> - Alex
>
> [0] https://tools.ietf.org/html/rfc6775
> [1] https://tools.ietf.org/html/rfc7400#section-3.3
> [2] https://tools.ietf.org/html/rfc4944#section-8
>
> 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]>
>
> changes since v2:
> - replace #ifdef CONFIG_IPV6 to #if IS_ENABLED(...)
> - replace #ifdef CONFIG_IEEE802154... to #if IS_ENABLED(...)
> - add more #if IS_ENABLED(CONFIG_IPV6) in ndisc.h
>
> Alexander Aring (10):
> 6lowpan: add private neighbour data
> 6lowpan: add 802.15.4 short addr slaac
> 6lowpan: remove ipv6 module request
> ndisc: add addr_len parameter to ndisc_opt_addr_space
> ndisc: add addr_len parameter to ndisc_opt_addr_data
> ndisc: add addr_len parameter to ndisc_fill_addr_option
> ipv6: introduce neighbour discovery ops
> ipv6: export ndisc functions
> 6lowpan: introduce 6lowpan-nd
> 6lowpan: add support for 802.15.4 short addr handling
>
> include/linux/netdevice.h | 6 +-
> include/net/6lowpan.h | 24 ++
> include/net/addrconf.h | 3 +
> include/net/ndisc.h | 124 ++++++++-
> net/6lowpan/6lowpan_i.h | 2 +
> net/6lowpan/Makefile | 2 +-
> net/6lowpan/core.c | 50 +++-
> net/6lowpan/iphc.c | 167 +++++++++--
> net/6lowpan/ndisc.c | 633 ++++++++++++++++++++++++++++++++++++++++++
> net/bluetooth/6lowpan.c | 2 +
> net/ieee802154/6lowpan/core.c | 12 +
> net/ieee802154/6lowpan/tx.c | 107 ++++---
> net/ipv6/addrconf.c | 7 +-
> net/ipv6/ndisc.c | 132 +++++----
> net/ipv6/route.c | 4 +-
> 15 files changed, 1117 insertions(+), 158 deletions(-)
> create mode 100644 net/6lowpan/ndisc.c

is there a chance that we get input into this patch set? I wonder also if it would be acceptable to take this through bluetooth-next or should it better go straight into net-next?

Regards

Marcel