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]>
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 | 101 ++++++-
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, 1094 insertions(+), 158 deletions(-)
create mode 100644 net/6lowpan/ndisc.c
--
2.8.0
Hi Alexander,
[auto build test ERROR on bluetooth-next/master]
url: https://github.com/0day-ci/linux/commits/Alexander-Aring/6lowpan-introduce-basic-6lowpan-nd/20160418-191825
base: https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git master
config: i386-randconfig-a0-04181351 (attached as .config)
reproduce:
# save the attached .config to linux build tree
make ARCH=i386
All errors (new ones prefixed by >>):
>> ERROR: "ipv6_get_lladdr" [net/6lowpan/6lowpan.ko] undefined!
>> ERROR: "inet6_ifa_finish_destroy" [net/6lowpan/6lowpan.ko] undefined!
>> ERROR: "rt6_clean_tohost" [net/6lowpan/6lowpan.ko] undefined!
>> ERROR: "ipv6_chk_acast_addr" [net/6lowpan/6lowpan.ko] undefined!
>> ERROR: "addrconf_dad_failure" [net/6lowpan/6lowpan.ko] undefined!
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Hi Alexander,
[auto build test WARNING on bluetooth-next/master]
url: https://github.com/0day-ci/linux/commits/Alexander-Aring/6lowpan-introduce-basic-6lowpan-nd/20160418-191825
base: https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git master
config: x86_64-randconfig-i0-04181247 (attached as .config)
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64
All warnings (new ones prefixed by >>):
In file included from include/uapi/linux/stddef.h:1:0,
from include/linux/stddef.h:4,
from include/uapi/linux/posix_types.h:4,
from include/uapi/linux/types.h:13,
from include/linux/types.h:5,
from include/uapi/linux/capability.h:16,
from include/linux/capability.h:15,
from net/appletalk/ddp.c:54:
include/net/ndisc.h: In function 'ndisc_is_useropt':
include/net/ndisc.h:201:16: error: 'const struct net_device' has no member named 'ndisc_ops'
if (likely(dev->ndisc_ops->is_useropt))
^
include/linux/compiler.h:138:43: note: in definition of macro 'likely'
# define likely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 1))
^
include/net/ndisc.h:201:16: error: 'const struct net_device' has no member named 'ndisc_ops'
if (likely(dev->ndisc_ops->is_useropt))
^
include/linux/compiler.h:138:51: note: in definition of macro 'likely'
# define likely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 1))
^
include/net/ndisc.h:201:16: error: 'const struct net_device' has no member named 'ndisc_ops'
if (likely(dev->ndisc_ops->is_useropt))
^
include/linux/compiler.h:114:47: note: in definition of macro 'likely_notrace'
#define likely_notrace(x) __builtin_expect(!!(x), 1)
^
include/linux/compiler.h:138:56: note: in expansion of macro '__branch_check__'
# define likely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 1))
^
>> include/net/ndisc.h:201:6: note: in expansion of macro 'likely'
if (likely(dev->ndisc_ops->is_useropt))
^
In file included from include/net/ipv6.h:20:0,
from include/net/inetpeer.h:15,
from include/net/route.h:28,
from net/appletalk/ddp.c:64:
include/net/ndisc.h:202:13: error: 'const struct net_device' has no member named 'ndisc_ops'
return dev->ndisc_ops->is_useropt(opt);
^
In file included from include/uapi/linux/stddef.h:1:0,
from include/linux/stddef.h:4,
from include/uapi/linux/posix_types.h:4,
from include/uapi/linux/types.h:13,
from include/linux/types.h:5,
from include/uapi/linux/capability.h:16,
from include/linux/capability.h:15,
from net/appletalk/ddp.c:54:
include/net/ndisc.h: In function 'ndisc_send_na':
include/net/ndisc.h:213:16: error: 'struct net_device' has no member named 'ndisc_ops'
if (likely(dev->ndisc_ops->send_na))
^
include/linux/compiler.h:138:43: note: in definition of macro 'likely'
# define likely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 1))
^
include/net/ndisc.h:213:16: error: 'struct net_device' has no member named 'ndisc_ops'
if (likely(dev->ndisc_ops->send_na))
^
include/linux/compiler.h:138:51: note: in definition of macro 'likely'
# define likely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 1))
^
include/net/ndisc.h:213:16: error: 'struct net_device' has no member named 'ndisc_ops'
if (likely(dev->ndisc_ops->send_na))
^
include/linux/compiler.h:114:47: note: in definition of macro 'likely_notrace'
#define likely_notrace(x) __builtin_expect(!!(x), 1)
^
include/linux/compiler.h:138:56: note: in expansion of macro '__branch_check__'
# define likely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 1))
^
include/net/ndisc.h:213:6: note: in expansion of macro 'likely'
if (likely(dev->ndisc_ops->send_na))
^
In file included from include/net/ipv6.h:20:0,
from include/net/inetpeer.h:15,
from include/net/route.h:28,
from net/appletalk/ddp.c:64:
include/net/ndisc.h:214:6: error: 'struct net_device' has no member named 'ndisc_ops'
dev->ndisc_ops->send_na(dev, daddr, solicited_addr, router,
^
In file included from include/uapi/linux/stddef.h:1:0,
from include/linux/stddef.h:4,
from include/uapi/linux/posix_types.h:4,
from include/uapi/linux/types.h:13,
from include/linux/types.h:5,
from include/uapi/linux/capability.h:16,
from include/linux/capability.h:15,
from net/appletalk/ddp.c:54:
include/net/ndisc.h: In function 'ndisc_recv_na':
include/net/ndisc.h:220:21: error: 'struct net_device' has no member named 'ndisc_ops'
if (likely(skb->dev->ndisc_ops->recv_na))
^
include/linux/compiler.h:138:43: note: in definition of macro 'likely'
# define likely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 1))
^
include/net/ndisc.h:220:21: error: 'struct net_device' has no member named 'ndisc_ops'
if (likely(skb->dev->ndisc_ops->recv_na))
^
include/linux/compiler.h:138:51: note: in definition of macro 'likely'
# define likely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 1))
^
include/net/ndisc.h:220:21: error: 'struct net_device' has no member named 'ndisc_ops'
if (likely(skb->dev->ndisc_ops->recv_na))
^
include/linux/compiler.h:114:47: note: in definition of macro 'likely_notrace'
#define likely_notrace(x) __builtin_expect(!!(x), 1)
^
include/linux/compiler.h:138:56: note: in expansion of macro '__branch_check__'
# define likely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 1))
^
include/net/ndisc.h:220:6: note: in expansion of macro 'likely'
if (likely(skb->dev->ndisc_ops->recv_na))
^
In file included from include/net/ipv6.h:20:0,
from include/net/inetpeer.h:15,
from include/net/route.h:28,
from net/appletalk/ddp.c:64:
include/net/ndisc.h:221:11: error: 'struct net_device' has no member named 'ndisc_ops'
skb->dev->ndisc_ops->recv_na(skb);
^
In file included from include/uapi/linux/stddef.h:1:0,
from include/linux/stddef.h:4,
from include/uapi/linux/posix_types.h:4,
from include/uapi/linux/types.h:13,
from include/linux/types.h:5,
from include/uapi/linux/capability.h:16,
from include/linux/capability.h:15,
from net/appletalk/ddp.c:54:
include/net/ndisc.h: In function 'ndisc_send_ns':
include/net/ndisc.h:229:16: error: 'struct net_device' has no member named 'ndisc_ops'
if (likely(dev->ndisc_ops->send_ns))
vim +/likely +201 include/net/ndisc.h
185 void (*send_na)(struct net_device *dev,
186 const struct in6_addr *daddr,
187 const struct in6_addr *solicited_addr,
188 bool router, bool solicited,
189 bool override, bool inc_opt);
190 void (*recv_na)(struct sk_buff *skb);
191 void (*send_ns)(struct net_device *dev,
192 const struct in6_addr *solicit,
193 const struct in6_addr *daddr,
194 const struct in6_addr *saddr);
195 void (*recv_ns)(struct sk_buff *skb);
196 };
197
198 static inline int ndisc_is_useropt(const struct net_device *dev,
199 struct nd_opt_hdr *opt)
200 {
> 201 if (likely(dev->ndisc_ops->is_useropt))
202 return dev->ndisc_ops->is_useropt(opt);
203 else
204 return 0;
205 }
206
207 static inline void ndisc_send_na(struct net_device *dev,
208 const struct in6_addr *daddr,
209 const struct in6_addr *solicited_addr,
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Hi,
Am 04/18/2016 um 12:58 PM schrieb Alexander Aring:
> 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 | 73 ++++++++++++++++++++++++++++++++++++++++++-----
> net/ipv6/addrconf.c | 1 +
> net/ipv6/ndisc.c | 71 +++++++++++++++++++++++++++++++--------------
> net/ipv6/route.c | 2 +-
> 5 files changed, 121 insertions(+), 29 deletions(-)
>
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index 0052c42..4f1b3f2 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
> +#ifdef CONFIG_IPV6
> + const struct ndisc_ops *ndisc_ops;
> +#endif
Need to change it to:
#if IS_ENABLED(CONFIG_IPV6)
as well also on other configs which can be builded as tristate.
Sorry for the noise, I will fix that in v2. :-)
- Alex
Hi Alexander,
[auto build test ERROR on bluetooth-next/master]
url: https://github.com/0day-ci/linux/commits/Alexander-Aring/6lowpan-introduce-basic-6lowpan-nd/20160418-191825
base: https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git master
config: x86_64-allmodconfig (attached as .config)
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64
All errors (new ones prefixed by >>):
In file included from include/linux/linkage.h:4:0,
from include/linux/fs.h:4,
from include/linux/debugfs.h:18,
from include/net/6lowpan.h:56,
from net/6lowpan/ndisc.c:11:
include/net/ndisc.h: In function 'ndisc_is_useropt':
include/net/ndisc.h:211:16: error: 'const struct net_device' has no member named 'ndisc_ops'
if (likely(dev->ndisc_ops->is_useropt))
^
include/linux/compiler.h:169:40: note: in definition of macro 'likely'
# define likely(x) __builtin_expect(!!(x), 1)
^
In file included from include/net/ipv6.h:20:0,
from include/net/6lowpan.h:58,
from net/6lowpan/ndisc.c:11:
include/net/ndisc.h:212:13: error: 'const struct net_device' has no member named 'ndisc_ops'
return dev->ndisc_ops->is_useropt(opt);
^
In file included from include/linux/linkage.h:4:0,
from include/linux/fs.h:4,
from include/linux/debugfs.h:18,
from include/net/6lowpan.h:56,
from net/6lowpan/ndisc.c:11:
include/net/ndisc.h: In function 'ndisc_send_na':
include/net/ndisc.h:223:16: error: 'struct net_device' has no member named 'ndisc_ops'
if (likely(dev->ndisc_ops->send_na))
^
include/linux/compiler.h:169:40: note: in definition of macro 'likely'
# define likely(x) __builtin_expect(!!(x), 1)
^
In file included from include/net/ipv6.h:20:0,
from include/net/6lowpan.h:58,
from net/6lowpan/ndisc.c:11:
include/net/ndisc.h:224:6: error: 'struct net_device' has no member named 'ndisc_ops'
dev->ndisc_ops->send_na(dev, daddr, solicited_addr, router,
^
In file included from include/linux/linkage.h:4:0,
from include/linux/fs.h:4,
from include/linux/debugfs.h:18,
from include/net/6lowpan.h:56,
from net/6lowpan/ndisc.c:11:
include/net/ndisc.h: In function 'ndisc_recv_na':
include/net/ndisc.h:230:21: error: 'struct net_device' has no member named 'ndisc_ops'
if (likely(skb->dev->ndisc_ops->recv_na))
^
include/linux/compiler.h:169:40: note: in definition of macro 'likely'
# define likely(x) __builtin_expect(!!(x), 1)
^
In file included from include/net/ipv6.h:20:0,
from include/net/6lowpan.h:58,
from net/6lowpan/ndisc.c:11:
include/net/ndisc.h:231:11: error: 'struct net_device' has no member named 'ndisc_ops'
skb->dev->ndisc_ops->recv_na(skb);
^
In file included from include/linux/linkage.h:4:0,
from include/linux/fs.h:4,
from include/linux/debugfs.h:18,
from include/net/6lowpan.h:56,
from net/6lowpan/ndisc.c:11:
include/net/ndisc.h: In function 'ndisc_send_ns':
include/net/ndisc.h:239:16: error: 'struct net_device' has no member named 'ndisc_ops'
if (likely(dev->ndisc_ops->send_ns))
^
include/linux/compiler.h:169:40: note: in definition of macro 'likely'
# define likely(x) __builtin_expect(!!(x), 1)
^
In file included from include/net/ipv6.h:20:0,
from include/net/6lowpan.h:58,
from net/6lowpan/ndisc.c:11:
include/net/ndisc.h:240:6: error: 'struct net_device' has no member named 'ndisc_ops'
dev->ndisc_ops->send_ns(dev, solicit, daddr, saddr);
^
In file included from include/linux/linkage.h:4:0,
from include/linux/fs.h:4,
from include/linux/debugfs.h:18,
from include/net/6lowpan.h:56,
from net/6lowpan/ndisc.c:11:
include/net/ndisc.h: In function 'ndisc_recv_ns':
include/net/ndisc.h:245:21: error: 'struct net_device' has no member named 'ndisc_ops'
if (likely(skb->dev->ndisc_ops->recv_ns))
^
include/linux/compiler.h:169:40: note: in definition of macro 'likely'
# define likely(x) __builtin_expect(!!(x), 1)
^
In file included from include/net/ipv6.h:20:0,
from include/net/6lowpan.h:58,
from net/6lowpan/ndisc.c:11:
include/net/ndisc.h:246:11: error: 'struct net_device' has no member named 'ndisc_ops'
skb->dev->ndisc_ops->recv_ns(skb);
^
net/6lowpan/ndisc.c: In function 'lowpan_register_ndisc_ops':
>> net/6lowpan/ndisc.c:632:5: error: 'struct net_device' has no member named 'ndisc_ops'
dev->ndisc_ops = &lowpan_ndisc_ops;
^
vim +632 net/6lowpan/ndisc.c
626 .send_ns = lowpan_ndisc_send_ns,
627 .recv_ns = lowpan_ndisc_recv_ns,
628 };
629
630 void lowpan_register_ndisc_ops(struct net_device *dev)
631 {
> 632 dev->ndisc_ops = &lowpan_ndisc_ops;
633 }
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Hi Alexander,
[auto build test ERROR on bluetooth-next/master]
url: https://github.com/0day-ci/linux/commits/Alexander-Aring/6lowpan-introduce-basic-6lowpan-nd/20160418-191825
base: https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git master
config: x86_64-allmodconfig (attached as .config)
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64
All error/warnings (new ones prefixed by >>):
In file included from include/uapi/linux/stddef.h:1:0,
from include/linux/stddef.h:4,
from include/uapi/linux/posix_types.h:4,
from include/uapi/linux/types.h:13,
from include/linux/types.h:5,
from include/linux/list.h:4,
from include/linux/module.h:9,
from drivers/net/ethernet/atheros/alx/main.c:35:
include/net/ndisc.h: In function 'ndisc_is_useropt':
>> include/net/ndisc.h:201:16: error: 'const struct net_device' has no member named 'ndisc_ops'
if (likely(dev->ndisc_ops->is_useropt))
^
include/linux/compiler.h:169:40: note: in definition of macro 'likely'
# define likely(x) __builtin_expect(!!(x), 1)
^
In file included from include/net/ipv6.h:20:0,
from include/net/inetpeer.h:15,
from include/net/route.h:28,
from include/net/ip.h:31,
from include/net/ip6_checksum.h:31,
from drivers/net/ethernet/atheros/alx/main.c:46:
include/net/ndisc.h:202:13: error: 'const struct net_device' has no member named 'ndisc_ops'
return dev->ndisc_ops->is_useropt(opt);
^
In file included from include/uapi/linux/stddef.h:1:0,
from include/linux/stddef.h:4,
from include/uapi/linux/posix_types.h:4,
from include/uapi/linux/types.h:13,
from include/linux/types.h:5,
from include/linux/list.h:4,
from include/linux/module.h:9,
from drivers/net/ethernet/atheros/alx/main.c:35:
include/net/ndisc.h: In function 'ndisc_send_na':
>> include/net/ndisc.h:213:16: error: 'struct net_device' has no member named 'ndisc_ops'
if (likely(dev->ndisc_ops->send_na))
^
include/linux/compiler.h:169:40: note: in definition of macro 'likely'
# define likely(x) __builtin_expect(!!(x), 1)
^
In file included from include/net/ipv6.h:20:0,
from include/net/inetpeer.h:15,
from include/net/route.h:28,
from include/net/ip.h:31,
from include/net/ip6_checksum.h:31,
from drivers/net/ethernet/atheros/alx/main.c:46:
include/net/ndisc.h:214:6: error: 'struct net_device' has no member named 'ndisc_ops'
dev->ndisc_ops->send_na(dev, daddr, solicited_addr, router,
^
In file included from include/uapi/linux/stddef.h:1:0,
from include/linux/stddef.h:4,
from include/uapi/linux/posix_types.h:4,
from include/uapi/linux/types.h:13,
from include/linux/types.h:5,
from include/linux/list.h:4,
from include/linux/module.h:9,
from drivers/net/ethernet/atheros/alx/main.c:35:
include/net/ndisc.h: In function 'ndisc_recv_na':
include/net/ndisc.h:220:21: error: 'struct net_device' has no member named 'ndisc_ops'
if (likely(skb->dev->ndisc_ops->recv_na))
^
include/linux/compiler.h:169:40: note: in definition of macro 'likely'
# define likely(x) __builtin_expect(!!(x), 1)
^
In file included from include/net/ipv6.h:20:0,
from include/net/inetpeer.h:15,
from include/net/route.h:28,
from include/net/ip.h:31,
from include/net/ip6_checksum.h:31,
from drivers/net/ethernet/atheros/alx/main.c:46:
include/net/ndisc.h:221:11: error: 'struct net_device' has no member named 'ndisc_ops'
skb->dev->ndisc_ops->recv_na(skb);
^
In file included from include/uapi/linux/stddef.h:1:0,
from include/linux/stddef.h:4,
from include/uapi/linux/posix_types.h:4,
from include/uapi/linux/types.h:13,
from include/linux/types.h:5,
from include/linux/list.h:4,
from include/linux/module.h:9,
from drivers/net/ethernet/atheros/alx/main.c:35:
include/net/ndisc.h: In function 'ndisc_send_ns':
include/net/ndisc.h:229:16: error: 'struct net_device' has no member named 'ndisc_ops'
if (likely(dev->ndisc_ops->send_ns))
^
include/linux/compiler.h:169:40: note: in definition of macro 'likely'
# define likely(x) __builtin_expect(!!(x), 1)
^
In file included from include/net/ipv6.h:20:0,
from include/net/inetpeer.h:15,
from include/net/route.h:28,
from include/net/ip.h:31,
from include/net/ip6_checksum.h:31,
from drivers/net/ethernet/atheros/alx/main.c:46:
include/net/ndisc.h:230:6: error: 'struct net_device' has no member named 'ndisc_ops'
dev->ndisc_ops->send_ns(dev, solicit, daddr, saddr);
^
In file included from include/uapi/linux/stddef.h:1:0,
from include/linux/stddef.h:4,
from include/uapi/linux/posix_types.h:4,
from include/uapi/linux/types.h:13,
from include/linux/types.h:5,
from include/linux/list.h:4,
from include/linux/module.h:9,
from drivers/net/ethernet/atheros/alx/main.c:35:
include/net/ndisc.h: In function 'ndisc_recv_ns':
include/net/ndisc.h:235:21: error: 'struct net_device' has no member named 'ndisc_ops'
if (likely(skb->dev->ndisc_ops->recv_ns))
^
include/linux/compiler.h:169:40: note: in definition of macro 'likely'
# define likely(x) __builtin_expect(!!(x), 1)
^
In file included from include/net/ipv6.h:20:0,
from include/net/inetpeer.h:15,
from include/net/route.h:28,
from include/net/ip.h:31,
from include/net/ip6_checksum.h:31,
from drivers/net/ethernet/atheros/alx/main.c:46:
include/net/ndisc.h:236:11: error: 'struct net_device' has no member named 'ndisc_ops'
skb->dev->ndisc_ops->recv_ns(skb);
^
--
In file included from include/uapi/linux/stddef.h:1:0,
from include/linux/stddef.h:4,
from include/uapi/linux/posix_types.h:4,
from include/uapi/linux/types.h:13,
from include/linux/types.h:5,
from include/linux/list.h:4,
from include/linux/module.h:9,
from net/ipv6/ndisc.c:32:
include/net/ndisc.h: In function 'ndisc_is_useropt':
>> include/net/ndisc.h:201:16: error: 'const struct net_device' has no member named 'ndisc_ops'
if (likely(dev->ndisc_ops->is_useropt))
^
include/linux/compiler.h:169:40: note: in definition of macro 'likely'
# define likely(x) __builtin_expect(!!(x), 1)
^
In file included from include/net/ipv6.h:20:0,
from net/ipv6/ndisc.c:57:
include/net/ndisc.h:202:13: error: 'const struct net_device' has no member named 'ndisc_ops'
return dev->ndisc_ops->is_useropt(opt);
^
In file included from include/uapi/linux/stddef.h:1:0,
from include/linux/stddef.h:4,
from include/uapi/linux/posix_types.h:4,
from include/uapi/linux/types.h:13,
from include/linux/types.h:5,
from include/linux/list.h:4,
from include/linux/module.h:9,
from net/ipv6/ndisc.c:32:
include/net/ndisc.h: In function 'ndisc_send_na':
>> include/net/ndisc.h:213:16: error: 'struct net_device' has no member named 'ndisc_ops'
if (likely(dev->ndisc_ops->send_na))
^
include/linux/compiler.h:169:40: note: in definition of macro 'likely'
# define likely(x) __builtin_expect(!!(x), 1)
^
In file included from include/net/ipv6.h:20:0,
from net/ipv6/ndisc.c:57:
include/net/ndisc.h:214:6: error: 'struct net_device' has no member named 'ndisc_ops'
dev->ndisc_ops->send_na(dev, daddr, solicited_addr, router,
^
In file included from include/uapi/linux/stddef.h:1:0,
from include/linux/stddef.h:4,
from include/uapi/linux/posix_types.h:4,
from include/uapi/linux/types.h:13,
from include/linux/types.h:5,
from include/linux/list.h:4,
from include/linux/module.h:9,
from net/ipv6/ndisc.c:32:
include/net/ndisc.h: In function 'ndisc_recv_na':
include/net/ndisc.h:220:21: error: 'struct net_device' has no member named 'ndisc_ops'
if (likely(skb->dev->ndisc_ops->recv_na))
^
include/linux/compiler.h:169:40: note: in definition of macro 'likely'
# define likely(x) __builtin_expect(!!(x), 1)
^
In file included from include/net/ipv6.h:20:0,
from net/ipv6/ndisc.c:57:
include/net/ndisc.h:221:11: error: 'struct net_device' has no member named 'ndisc_ops'
skb->dev->ndisc_ops->recv_na(skb);
^
In file included from include/uapi/linux/stddef.h:1:0,
from include/linux/stddef.h:4,
from include/uapi/linux/posix_types.h:4,
from include/uapi/linux/types.h:13,
from include/linux/types.h:5,
from include/linux/list.h:4,
from include/linux/module.h:9,
from net/ipv6/ndisc.c:32:
include/net/ndisc.h: In function 'ndisc_send_ns':
include/net/ndisc.h:229:16: error: 'struct net_device' has no member named 'ndisc_ops'
if (likely(dev->ndisc_ops->send_ns))
^
include/linux/compiler.h:169:40: note: in definition of macro 'likely'
# define likely(x) __builtin_expect(!!(x), 1)
^
In file included from include/net/ipv6.h:20:0,
from net/ipv6/ndisc.c:57:
include/net/ndisc.h:230:6: error: 'struct net_device' has no member named 'ndisc_ops'
dev->ndisc_ops->send_ns(dev, solicit, daddr, saddr);
^
In file included from include/uapi/linux/stddef.h:1:0,
from include/linux/stddef.h:4,
from include/uapi/linux/posix_types.h:4,
from include/uapi/linux/types.h:13,
from include/linux/types.h:5,
from include/linux/list.h:4,
from include/linux/module.h:9,
from net/ipv6/ndisc.c:32:
include/net/ndisc.h: In function 'ndisc_recv_ns':
include/net/ndisc.h:235:21: error: 'struct net_device' has no member named 'ndisc_ops'
if (likely(skb->dev->ndisc_ops->recv_ns))
^
include/linux/compiler.h:169:40: note: in definition of macro 'likely'
# define likely(x) __builtin_expect(!!(x), 1)
^
In file included from include/net/ipv6.h:20:0,
from net/ipv6/ndisc.c:57:
include/net/ndisc.h:236:11: error: 'struct net_device' has no member named 'ndisc_ops'
skb->dev->ndisc_ops->recv_ns(skb);
^
net/ipv6/ndisc.c: In function 'ip6_register_ndisc_ops':
>> net/ipv6/ndisc.c:1804:10: error: 'struct net_device' has no member named 'ndisc_ops'
if (dev->ndisc_ops) {
^
net/ipv6/ndisc.c:1809:7: error: 'struct net_device' has no member named 'ndisc_ops'
dev->ndisc_ops = &ip6_ndisc_ops;
^
In file included from include/net/ipv6.h:20:0,
from net/ipv6/ndisc.c:57:
include/net/ndisc.h: In function 'ndisc_is_useropt':
>> include/net/ndisc.h:205:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^
vim +201 include/net/ndisc.h
195 void (*recv_ns)(struct sk_buff *skb);
196 };
197
198 static inline int ndisc_is_useropt(const struct net_device *dev,
199 struct nd_opt_hdr *opt)
200 {
> 201 if (likely(dev->ndisc_ops->is_useropt))
202 return dev->ndisc_ops->is_useropt(opt);
203 else
204 return 0;
> 205 }
206
207 static inline void ndisc_send_na(struct net_device *dev,
208 const struct in6_addr *daddr,
209 const struct in6_addr *solicited_addr,
210 bool router, bool solicited, bool override,
211 bool inc_opt)
212 {
> 213 if (likely(dev->ndisc_ops->send_na))
214 dev->ndisc_ops->send_na(dev, daddr, solicited_addr, router,
215 solicited, override, inc_opt);
216 }
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
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
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 25ce092..a884c81 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..b6dc244
--- /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];
+#ifdef 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
+
+#ifdef 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;
+#ifdef 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;
+ }
+ }
+#ifdef 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;
+ }
+ }
+
+#ifdef 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
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 5346625..25ce092 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;
@@ -244,6 +253,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
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 | 73 ++++++++++++++++++++++++++++++++++++++++++-----
net/ipv6/addrconf.c | 1 +
net/ipv6/ndisc.c | 71 +++++++++++++++++++++++++++++++--------------
net/ipv6/route.c | 2 +-
5 files changed, 121 insertions(+), 29 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 0052c42..4f1b3f2 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
+#ifdef 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..5346625 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,70 @@ 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;
+}
+
+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);
+}
+
+void ip6_register_ndisc_ops(struct net_device *dev);
+
int ndisc_init(void);
int ndisc_late_init(void);
@@ -181,14 +246,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
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
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
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
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
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
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