Hi,
this patch series introduces the ndisc ops callback structure to add different
handling for IPv6 neighbour discovery cache functionality. It implements at first
the two following use-cases:
- 6CO handling as userspace option (For all 6LoWPAN layers, BTLE/802.15.4) [0]
- short address handling for 802.15.4 6LoWPAN only [1]
Since my last patch series, I completely changed the whole ndisc_ops callback
structure to not replace the whole ndisc functionality at recv/send level of
NS/NA/RS/RA which I send in my previous patch-series "6lowpan: introduce basic
6lowpan-nd". I changed it now to add different handling in a very low-level way
of ndisc functionality.
The ndisc_ops don't must be registered to dev->ndisc_ops anymore, if they are not
set, then no additional ipv6 ndisc handling will be done.
This patch series now introduce a complete handling of short address for
802.15.4 6LoWPAN in case of send/recv of NA/NS/RS and RA. In case of RA
(receive only) and PIO we also need a second prefix + short-address based
address.
This callback structure can be used later (I hope) for RFC 6775 [0]. This RFC
defines some new option fields and messages for 6LoWPAN-ND. This patch series
does not implement RFC 6775 (except we decide now to handle 6CO in userspace).
Additional we can use the current ops for parse/fill ndisc options for kernel
handled ndisc messages to add 6CIO, see [2].
I tested RA/NS/NA/RS messages with short address which seems to work, what I
didn't test is the redirect messages since I don't know how to generate them.
The short address for redirect messages are also some special case here, because
the short address by a L3 target address lookuped by neighbour cache need to be
added.
btw:
According to [3] sending redirect messages should be also disabled by default
on 6lowpan interfaces, but can be activated afterwards. This is maybe
something for the ipv6_devconf structure. There is a "accept_redirects" but
no "disable_redirects".
- Alex
[0] https://tools.ietf.org/html/rfc7400
[1] https://tools.ietf.org/html/rfc6775
[2] https://tools.ietf.org/html/rfc4944#section-8
changes since RFC:
- add lowlevel functions __ndisc_opt_addr_space,
__ndisc_opt_addr_data and __ndisc_fill_addr_option for corresponding
functions which doesn't requires net_device argument.
- move ndisc_ops e.g. ndisc_ops_fill_addr_option function call into the
corresponding device argument function ndisc_fill_addr_option.
(Introduced a special static inline function for redirect handling).
- fix error handling in addrconf_prefix_rcv_add_addr.
(Please see, introduce new API handling that second address registration
(in case of 802.15.4 6LoWPAN) will still be notified if failed, because
dev->addr was successful.
- add ieee802154 sub-directory in short address entry for 6lowpan UAPI.
- add lowpan_802154_is_valid_src_short_addr, because 802.15.4 6lowpan
defines the first bit as multicast (don't know how this can be working
at the end, because some hardware addresses will handle such addresses
in L2 as unicast. See:
https://www.iana.org/assignments/_6lowpan-parameters/_6lowpan-parameters.xhtml#_6lowpan-parameters-2
changes since v2:
- Introduce ndisc_ops to have our own implementation for dealing with NS/NA
which allows also to support RFC6775 (e.g. ARO).
- add handling for handling 6CO as userspace option for RA messages in
case of 6LoWPAN interfaces.
- change lowpan_is_ll to check on linklayer type only.
- added some reviewed-by's.
- move short addr slaac to net/6lowpan instead ipv6 handling.
- add handling for context based address compression in case for
short address as link-layer address.
- change strategy to use short address, a short address will always be used
when it's available.
- Handle override flag in NA messages to update short address information or
not.
Alexander Aring (12):
6lowpan: add private neighbour data
6lowpan: add 802.15.4 short addr slaac
6lowpan: remove ipv6 module request
ndisc: add __ndisc_opt_addr_space function
ndisc: add __ndisc_opt_addr_data function
ndisc: add __ndisc_fill_addr_option function
addrconf: put prefix address add in an own function
ipv6: introduce neighbour discovery ops
ipv6: export several functions
6lowpan: introduce 6lowpan-nd
6lowpan: add support for getting short address
6lowpan: add support for 802.15.4 short addr handling
include/linux/netdevice.h | 8 +-
include/net/6lowpan.h | 16 +++
include/net/addrconf.h | 10 ++
include/net/ndisc.h | 248 +++++++++++++++++++++++++++++++++++++++---
net/6lowpan/6lowpan_i.h | 4 +
net/6lowpan/Makefile | 2 +-
net/6lowpan/core.c | 50 ++++++++-
net/6lowpan/debugfs.c | 39 +++++++
net/6lowpan/iphc.c | 167 +++++++++++++++++++++++-----
net/6lowpan/ndisc.c | 234 +++++++++++++++++++++++++++++++++++++++
net/ieee802154/6lowpan/core.c | 12 ++
net/ieee802154/6lowpan/tx.c | 113 +++++++++----------
net/ipv6/addrconf.c | 218 +++++++++++++++++++++----------------
net/ipv6/ndisc.c | 123 +++++++++++++--------
net/ipv6/route.c | 8 +-
15 files changed, 1004 insertions(+), 248 deletions(-)
create mode 100644 net/6lowpan/ndisc.c
--
2.8.3
Hello.
On 14/06/16 13:52, Alexander Aring wrote:
> This patch exports some neighbour discovery functions which can be used
> by 6lowpan neighbour discovery ops functionality then.
>
> Cc: David S. Miller <[email protected]>
> Cc: Alexey Kuznetsov <[email protected]>
> Cc: James Morris <[email protected]>
> Cc: Hideaki YOSHIFUJI <[email protected]>
> Cc: Patrick McHardy <[email protected]>
> Signed-off-by: Alexander Aring <[email protected]>
> ---
> include/net/addrconf.h | 7 +++++++
> include/net/ndisc.h | 12 ++++++++++++
> net/ipv6/addrconf.c | 15 +++++++--------
> net/ipv6/ndisc.c | 14 +++-----------
> 4 files changed, 29 insertions(+), 19 deletions(-)
>
> diff --git a/include/net/addrconf.h b/include/net/addrconf.h
> index b1774eb..9826d3a 100644
> --- a/include/net/addrconf.h
> +++ b/include/net/addrconf.h
> @@ -97,6 +97,13 @@ 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);
>
> +int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
> + const struct prefix_info *pinfo,
> + struct inet6_dev *in6_dev,
> + const struct in6_addr *addr, int addr_type,
> + u32 addr_flags, bool sllao, bool tokenized,
> + __u32 valid_lft, u32 prefered_lft);
> +
> static inline int addrconf_ifid_eui48(u8 *eui, struct net_device *dev)
> {
> if (dev->addr_len != ETH_ALEN)
> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
> index a5e2767..3f0f41d 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;
> @@ -115,6 +124,9 @@ struct ndisc_options *ndisc_parse_options(const struct net_device *dev,
> u8 *opt, int opt_len,
> struct ndisc_options *ndopts);
>
> +void __ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data,
> + int data_len, int pad);
> +
> #define NDISC_OPS_REDIRECT_DATA_SPACE 2
>
> /*
> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index 2d678c0..9c7d660 100644
> --- a/net/ipv6/addrconf.c
> +++ b/net/ipv6/addrconf.c
> @@ -2333,14 +2333,12 @@ static bool is_addr_mode_generate_stable(struct inet6_dev *idev)
> idev->addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
> }
>
> -static int addrconf_prefix_rcv_add_addr(struct net *net,
> - struct net_device *dev,
> - const struct prefix_info *pinfo,
> - struct inet6_dev *in6_dev,
> - const struct in6_addr *addr,
> - int addr_type, u32 addr_flags,
> - bool sllao, bool tokenized,
> - __u32 valid_lft, u32 prefered_lft)
> +int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
> + const struct prefix_info *pinfo,
> + struct inet6_dev *in6_dev,
> + const struct in6_addr *addr, int addr_type,
> + u32 addr_flags, bool sllao, bool tokenized,
> + __u32 valid_lft, u32 prefered_lft)
> {
> struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1);
> int create = 0, update_lft = 0;
> @@ -2430,6 +2428,7 @@ static int addrconf_prefix_rcv_add_addr(struct net *net,
>
> return 0;
> }
> +EXPORT_SYMBOL_GPL(addrconf_prefix_rcv_add_addr);
>
> void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
> {
> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
> index 2f4afd1..fe65cdc 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, int pad)
> +void __ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data,
> + int data_len, int pad)
> {
> int space = __ndisc_opt_addr_space(data_len, pad);
> u8 *opt = skb_put(skb, space);
> @@ -170,6 +161,7 @@ static void __ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data,
> if (space > 0)
> memset(opt, 0, space);
> }
> +EXPORT_SYMBOL_GPL(__ndisc_fill_addr_option);
>
> static inline void ndisc_fill_addr_option(struct sk_buff *skb, int type,
> void *data, u8 icmp6_type)
Reviewed-by: Stefan Schmidt<[email protected]>
regards
Stefan Schmidt
Hello.
On 14/06/16 13:52, Alexander Aring wrote:
> This patch adds __ndisc_fill_addr_option as low-level function for
> ndisc_fill_addr_option which doesn't depend on net_device parameter.
>
> 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 | 14 ++++++++++----
> 1 file changed, 10 insertions(+), 4 deletions(-)
>
> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
> index c245895..a7b9468 100644
> --- a/net/ipv6/ndisc.c
> +++ b/net/ipv6/ndisc.c
> @@ -150,11 +150,10 @@ 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)
> {
> - 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(data_len, pad);
> u8 *opt = skb_put(skb, space);
>
> opt[0] = type;
> @@ -172,6 +171,13 @@ static void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data)
> memset(opt, 0, space);
> }
>
> +static inline void ndisc_fill_addr_option(struct sk_buff *skb, int type,
> + void *data)
> +{
> + __ndisc_fill_addr_option(skb, type, data, skb->dev->addr_len,
> + ndisc_addr_option_pad(skb->dev->type));
> +}
> +
> static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
> struct nd_opt_hdr *end)
> {
Reviewed-by: Stefan Schmidt<[email protected]>
regards
Stefan Schmidt
Hello.
On 14/06/16 13:52, Alexander Aring wrote:
> This patch adds __ndisc_opt_addr_data as low-level function for
> ndisc_opt_addr_data which doesn't depend on net_device parameter.
>
> 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 | 14 ++++++++++----
> 1 file changed, 10 insertions(+), 4 deletions(-)
>
> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
> index 4cee826..c8962ad 100644
> --- a/include/net/ndisc.h
> +++ b/include/net/ndisc.h
> @@ -138,17 +138,23 @@ static inline int ndisc_opt_addr_space(struct net_device *dev)
> ndisc_addr_option_pad(dev->type));
> }
>
> -static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,
> - struct net_device *dev)
> +static inline u8 *__ndisc_opt_addr_data(struct nd_opt_hdr *p,
> + unsigned char addr_len, int prepad)
> {
> 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(addr_len, prepad))
> return NULL;
> return lladdr + prepad;
> }
>
> +static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,
> + struct net_device *dev)
> +{
> + return __ndisc_opt_addr_data(p, dev->addr_len,
> + ndisc_addr_option_pad(dev->type));
> +}
> +
> static inline u32 ndisc_hashfn(const void *pkey, const struct net_device *dev, __u32 *hash_rnd)
> {
> const u32 *p32 = pkey;
Reviewed-by: Stefan Schmidt<[email protected]>
regards
Stefan Schmidt
Hello.
On 14/06/16 13:52, Alexander Aring wrote:
> This patch adds __ndisc_opt_addr_space as low-level function for
> ndisc_opt_addr_space which doesn't depend on net_device parameter.
>
> 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 | 9 +++++++--
> 1 file changed, 7 insertions(+), 2 deletions(-)
>
> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
> index 2d8edaa..4cee826 100644
> --- a/include/net/ndisc.h
> +++ b/include/net/ndisc.h
> @@ -127,10 +127,15 @@ static inline int ndisc_addr_option_pad(unsigned short type)
> }
> }
>
> +static inline int __ndisc_opt_addr_space(unsigned char addr_len, int pad)
> +{
> + return NDISC_OPT_SPACE(addr_len + pad);
> +}
> +
> static inline int ndisc_opt_addr_space(struct net_device *dev)
> {
> - return NDISC_OPT_SPACE(dev->addr_len +
> - ndisc_addr_option_pad(dev->type));
> + return __ndisc_opt_addr_space(dev->addr_len,
> + ndisc_addr_option_pad(dev->type));
> }
>
> static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,
Reviewed-by: Stefan Schmidt<[email protected]>
regards
Stefan Schmidt
Alexander Aring wrote:
> This patch introduce different 6lowpan handling for receive and transmit
> NS/NA messages for the ipv6 neighbour discovery. The first use-case is
> for supporting 802.15.4 short addresses inside the option fields and
> handling for RFC6775 6CO option field as userspace option.
>
> 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]>
> Reviewed-by: Stefan Schmidt <[email protected]>
> Signed-off-by: Alexander Aring <[email protected]>
Acked-by: YOSHIFUJI Hideaki <[email protected]>
> ---
> include/net/ndisc.h | 18 ++--
> net/6lowpan/6lowpan_i.h | 4 +
> net/6lowpan/Makefile | 2 +-
> net/6lowpan/core.c | 4 +-
> net/6lowpan/ndisc.c | 234 ++++++++++++++++++++++++++++++++++++++++++++++++
> 5 files changed, 254 insertions(+), 8 deletions(-)
> create mode 100644 net/6lowpan/ndisc.c
>
> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
> index 3f0f41d..be1fe228 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
> };
>
> @@ -109,14 +110,19 @@ struct ndisc_options {
> #endif
> struct nd_opt_hdr *nd_useropts;
> struct nd_opt_hdr *nd_useropts_end;
> +#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
> + struct nd_opt_hdr *nd_802154_opt_array[ND_OPT_TARGET_LL_ADDR + 1];
> +#endif
> };
>
> -#define nd_opts_src_lladdr nd_opt_array[ND_OPT_SOURCE_LL_ADDR]
> -#define nd_opts_tgt_lladdr nd_opt_array[ND_OPT_TARGET_LL_ADDR]
> -#define nd_opts_pi nd_opt_array[ND_OPT_PREFIX_INFO]
> -#define nd_opts_pi_end nd_opt_array[__ND_OPT_PREFIX_INFO_END]
> -#define nd_opts_rh nd_opt_array[ND_OPT_REDIRECT_HDR]
> -#define nd_opts_mtu nd_opt_array[ND_OPT_MTU]
> +#define nd_opts_src_lladdr nd_opt_array[ND_OPT_SOURCE_LL_ADDR]
> +#define nd_opts_tgt_lladdr nd_opt_array[ND_OPT_TARGET_LL_ADDR]
> +#define nd_opts_pi nd_opt_array[ND_OPT_PREFIX_INFO]
> +#define nd_opts_pi_end nd_opt_array[__ND_OPT_PREFIX_INFO_END]
> +#define nd_opts_rh nd_opt_array[ND_OPT_REDIRECT_HDR]
> +#define nd_opts_mtu nd_opt_array[ND_OPT_MTU]
> +#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_OPT_SPACE(len) (((len)+2+7)&~7)
>
> diff --git a/net/6lowpan/6lowpan_i.h b/net/6lowpan/6lowpan_i.h
> index 97ecc27..a67caee 100644
> --- a/net/6lowpan/6lowpan_i.h
> +++ b/net/6lowpan/6lowpan_i.h
> @@ -12,6 +12,10 @@ static inline bool lowpan_is_ll(const struct net_device *dev,
> return lowpan_dev(dev)->lltype == lltype;
> }
>
> +extern const struct ndisc_ops lowpan_ndisc_ops;
> +
> +int addrconf_ifid_802154_6lowpan(u8 *eui, 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 1c7a42b..5945f7e 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;
>
> + dev->ndisc_ops = &lowpan_ndisc_ops;
> +
> ret = register_netdevice(dev);
> if (ret < 0)
> return ret;
> @@ -73,7 +75,7 @@ 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)
> +int addrconf_ifid_802154_6lowpan(u8 *eui, struct net_device *dev)
> {
> struct wpan_dev *wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr;
>
> diff --git a/net/6lowpan/ndisc.c b/net/6lowpan/ndisc.c
> new file mode 100644
> index 0000000..ae1d419
> --- /dev/null
> +++ b/net/6lowpan/ndisc.c
> @@ -0,0 +1,234 @@
> +/* 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.
> + *
> + * Authors:
> + * (C) 2016 Pengutronix, Alexander Aring <[email protected]>
> + */
> +
> +#include <net/6lowpan.h>
> +#include <net/addrconf.h>
> +#include <net/ndisc.h>
> +
> +#include "6lowpan_i.h"
> +
> +static int lowpan_ndisc_is_useropt(u8 nd_opt_type)
> +{
> + return nd_opt_type == ND_OPT_6CO;
> +}
> +
> +#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
> +#define NDISC_802154_SHORT_ADDR_LENGTH 1
> +static int lowpan_ndisc_parse_802154_options(const struct net_device *dev,
> + struct nd_opt_hdr *nd_opt,
> + struct ndisc_options *ndopts)
> +{
> + switch (nd_opt->nd_opt_len) {
> + case NDISC_802154_SHORT_ADDR_LENGTH:
> + 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;
> + return 1;
> + default:
> + /* all others will be handled by ndisc IPv6 option parsing */
> + return 0;
> + }
> +}
> +
> +static int lowpan_ndisc_parse_options(const struct net_device *dev,
> + struct nd_opt_hdr *nd_opt,
> + struct ndisc_options *ndopts)
> +{
> + switch (nd_opt->nd_opt_type) {
> + case ND_OPT_SOURCE_LL_ADDR:
> + case ND_OPT_TARGET_LL_ADDR:
> + return lowpan_ndisc_parse_802154_options(dev, nd_opt, ndopts);
> + default:
> + return 0;
> + }
> +}
> +
> +static void lowpan_ndisc_802154_update(struct neighbour *n, u32 flags,
> + u8 icmp6_type,
> + const struct ndisc_options *ndopts)
> +{
> + struct lowpan_802154_neigh *neigh = lowpan_802154_neigh(neighbour_priv(n));
> + u8 *lladdr_short = NULL;
> +
> + switch (icmp6_type) {
> + case NDISC_ROUTER_SOLICITATION:
> + case NDISC_ROUTER_ADVERTISEMENT:
> + case NDISC_NEIGHBOUR_SOLICITATION:
> + if (ndopts->nd_802154_opts_src_lladdr) {
> + lladdr_short = __ndisc_opt_addr_data(ndopts->nd_802154_opts_src_lladdr,
> + IEEE802154_SHORT_ADDR_LEN, 0);
> + if (!lladdr_short) {
> + ND_PRINTK(2, warn,
> + "NA: invalid short link-layer address length\n");
> + return;
> + }
> + }
> + break;
> + case NDISC_REDIRECT:
> + case NDISC_NEIGHBOUR_ADVERTISEMENT:
> + if (ndopts->nd_802154_opts_tgt_lladdr) {
> + lladdr_short = __ndisc_opt_addr_data(ndopts->nd_802154_opts_tgt_lladdr,
> + IEEE802154_SHORT_ADDR_LEN, 0);
> + if (!lladdr_short) {
> + ND_PRINTK(2, warn,
> + "NA: invalid short link-layer address length\n");
> + return;
> + }
> + }
> + break;
> + default:
> + break;
> + }
> +
> + write_lock_bh(&n->lock);
> + if (lladdr_short)
> + ieee802154_be16_to_le16(&neigh->short_addr, lladdr_short);
> + else
> + neigh->short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);
> + write_unlock_bh(&n->lock);
> +}
> +
> +static void lowpan_ndisc_update(const struct net_device *dev,
> + struct neighbour *n, u32 flags, u8 icmp6_type,
> + const struct ndisc_options *ndopts)
> +{
> + if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154))
> + return;
> +
> + /* react on overrides only. TODO check if this is really right. */
> + if (flags & NEIGH_UPDATE_F_OVERRIDE)
> + lowpan_ndisc_802154_update(n, flags, icmp6_type, ndopts);
> +}
> +
> +static int lowpan_ndisc_opt_addr_space(const struct net_device *dev,
> + u8 icmp6_type, struct neighbour *neigh,
> + u8 *ha_buf, u8 **ha)
> +{
> + struct lowpan_802154_neigh *n;
> + struct wpan_dev *wpan_dev;
> + int addr_space = 0;
> +
> + if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154))
> + return 0;
> +
> + switch (icmp6_type) {
> + case NDISC_REDIRECT:
> + n = lowpan_802154_neigh(neighbour_priv(neigh));
> +
> + read_lock_bh(&neigh->lock);
> + if (lowpan_802154_is_valid_src_short_addr(n->short_addr)) {
> + memcpy(ha_buf, &n->short_addr,
> + IEEE802154_SHORT_ADDR_LEN);
> + read_unlock_bh(&neigh->lock);
> + addr_space += __ndisc_opt_addr_space(IEEE802154_SHORT_ADDR_LEN, 0);
> + *ha = ha_buf;
> + }
> + read_unlock_bh(&neigh->lock);
> + break;
> + case NDISC_NEIGHBOUR_ADVERTISEMENT:
> + case NDISC_NEIGHBOUR_SOLICITATION:
> + case NDISC_ROUTER_SOLICITATION:
> + wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr;
> +
> + if (lowpan_802154_is_valid_src_short_addr(wpan_dev->short_addr))
> + addr_space = __ndisc_opt_addr_space(IEEE802154_SHORT_ADDR_LEN, 0);
> + break;
> + default:
> + break;
> + }
> +
> + return addr_space;
> +}
> +
> +static void lowpan_ndisc_fill_addr_option(const struct net_device *dev,
> + struct sk_buff *skb, u8 icmp6_type,
> + const u8 *ha)
> +{
> + struct wpan_dev *wpan_dev;
> + __be16 short_addr;
> + u8 opt_type;
> +
> + if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154))
> + return;
> +
> + switch (icmp6_type) {
> + case NDISC_REDIRECT:
> + if (ha) {
> + ieee802154_le16_to_be16(&short_addr, ha);
> + __ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR,
> + &short_addr,
> + IEEE802154_SHORT_ADDR_LEN, 0);
> + }
> + return;
> + case NDISC_NEIGHBOUR_ADVERTISEMENT:
> + opt_type = ND_OPT_TARGET_LL_ADDR;
> + break;
> + case NDISC_ROUTER_SOLICITATION:
> + case NDISC_NEIGHBOUR_SOLICITATION:
> + opt_type = ND_OPT_SOURCE_LL_ADDR;
> + break;
> + default:
> + return;
> + }
> +
> + wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr;
> +
> + if (lowpan_802154_is_valid_src_short_addr(wpan_dev->short_addr)) {
> + ieee802154_le16_to_be16(&short_addr,
> + &wpan_dev->short_addr);
> + __ndisc_fill_addr_option(skb, opt_type, &short_addr,
> + IEEE802154_SHORT_ADDR_LEN, 0);
> + }
> +}
> +
> +static void lowpan_ndisc_prefix_rcv_add_addr(struct net *net,
> + struct net_device *dev,
> + const struct prefix_info *pinfo,
> + struct inet6_dev *in6_dev,
> + struct in6_addr *addr,
> + int addr_type, u32 addr_flags,
> + bool sllao, bool tokenized,
> + __u32 valid_lft,
> + u32 prefered_lft,
> + bool dev_addr_generated)
> +{
> + int err;
> +
> + /* generates short based address for RA PIO's */
> + if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154) && dev_addr_generated &&
> + !addrconf_ifid_802154_6lowpan(addr->s6_addr + 8, dev)) {
> + err = addrconf_prefix_rcv_add_addr(net, dev, pinfo, in6_dev,
> + addr, addr_type, addr_flags,
> + sllao, tokenized, valid_lft,
> + prefered_lft);
> + if (err)
> + ND_PRINTK(2, warn,
> + "RA: could not add a short address based address for prefix: %pI6c\n",
> + &pinfo->prefix);
> + }
> +}
> +#endif
> +
> +const struct ndisc_ops lowpan_ndisc_ops = {
> + .is_useropt = lowpan_ndisc_is_useropt,
> +#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
> + .parse_options = lowpan_ndisc_parse_options,
> + .update = lowpan_ndisc_update,
> + .opt_addr_space = lowpan_ndisc_opt_addr_space,
> + .fill_addr_option = lowpan_ndisc_fill_addr_option,
> + .prefix_rcv_add_addr = lowpan_ndisc_prefix_rcv_add_addr,
> +#endif
> +};
>
--
Hideaki Yoshifuji <[email protected]>
Technical Division, MIRACLE LINUX CORPORATION
Alexander Aring wrote:
> This patch exports some neighbour discovery functions which can be used
> by 6lowpan neighbour discovery ops functionality then.
>
> Cc: David S. Miller <[email protected]>
> Cc: Alexey Kuznetsov <[email protected]>
> Cc: James Morris <[email protected]>
> Cc: Hideaki YOSHIFUJI <[email protected]>
> Cc: Patrick McHardy <[email protected]>
> Signed-off-by: Alexander Aring <[email protected]>
Acked-by: YOSHIFUJI Hideaki <[email protected]>
> ---
> include/net/addrconf.h | 7 +++++++
> include/net/ndisc.h | 12 ++++++++++++
> net/ipv6/addrconf.c | 15 +++++++--------
> net/ipv6/ndisc.c | 14 +++-----------
> 4 files changed, 29 insertions(+), 19 deletions(-)
>
> diff --git a/include/net/addrconf.h b/include/net/addrconf.h
> index b1774eb..9826d3a 100644
> --- a/include/net/addrconf.h
> +++ b/include/net/addrconf.h
> @@ -97,6 +97,13 @@ 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);
>
> +int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
> + const struct prefix_info *pinfo,
> + struct inet6_dev *in6_dev,
> + const struct in6_addr *addr, int addr_type,
> + u32 addr_flags, bool sllao, bool tokenized,
> + __u32 valid_lft, u32 prefered_lft);
> +
> static inline int addrconf_ifid_eui48(u8 *eui, struct net_device *dev)
> {
> if (dev->addr_len != ETH_ALEN)
> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
> index a5e2767..3f0f41d 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;
> @@ -115,6 +124,9 @@ struct ndisc_options *ndisc_parse_options(const struct net_device *dev,
> u8 *opt, int opt_len,
> struct ndisc_options *ndopts);
>
> +void __ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data,
> + int data_len, int pad);
> +
> #define NDISC_OPS_REDIRECT_DATA_SPACE 2
>
> /*
> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index 2d678c0..9c7d660 100644
> --- a/net/ipv6/addrconf.c
> +++ b/net/ipv6/addrconf.c
> @@ -2333,14 +2333,12 @@ static bool is_addr_mode_generate_stable(struct inet6_dev *idev)
> idev->addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
> }
>
> -static int addrconf_prefix_rcv_add_addr(struct net *net,
> - struct net_device *dev,
> - const struct prefix_info *pinfo,
> - struct inet6_dev *in6_dev,
> - const struct in6_addr *addr,
> - int addr_type, u32 addr_flags,
> - bool sllao, bool tokenized,
> - __u32 valid_lft, u32 prefered_lft)
> +int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
> + const struct prefix_info *pinfo,
> + struct inet6_dev *in6_dev,
> + const struct in6_addr *addr, int addr_type,
> + u32 addr_flags, bool sllao, bool tokenized,
> + __u32 valid_lft, u32 prefered_lft)
> {
> struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1);
> int create = 0, update_lft = 0;
> @@ -2430,6 +2428,7 @@ static int addrconf_prefix_rcv_add_addr(struct net *net,
>
> return 0;
> }
> +EXPORT_SYMBOL_GPL(addrconf_prefix_rcv_add_addr);
>
> void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
> {
> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
> index 2f4afd1..fe65cdc 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, int pad)
> +void __ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data,
> + int data_len, int pad)
> {
> int space = __ndisc_opt_addr_space(data_len, pad);
> u8 *opt = skb_put(skb, space);
> @@ -170,6 +161,7 @@ static void __ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data,
> if (space > 0)
> memset(opt, 0, space);
> }
> +EXPORT_SYMBOL_GPL(__ndisc_fill_addr_option);
>
> static inline void ndisc_fill_addr_option(struct sk_buff *skb, int type,
> void *data, u8 icmp6_type)
>
--
Hideaki Yoshifuji <[email protected]>
Technical Division, MIRACLE LINUX CORPORATION
Alexander Aring wrote:
> This patch introduces neighbour discovery ops callback structure. The
> idea is to separate the handling for 6LoWPAN into the 6lowpan module.
>
> 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]>
Acked-by: YOSHIFUJI Hideaki <[email protected]>
> ---
> include/linux/netdevice.h | 5 ++
> include/net/ndisc.h | 197 +++++++++++++++++++++++++++++++++++++++++++++-
> net/ipv6/addrconf.c | 13 ++-
> net/ipv6/ndisc.c | 101 ++++++++++++++++--------
> net/ipv6/route.c | 8 +-
> 5 files changed, 284 insertions(+), 40 deletions(-)
>
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index 36e43bd..890158e 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -1456,6 +1456,8 @@ enum netdev_priv_flags {
> * @netdev_ops: Includes several pointers to callbacks,
> * if one wants to override the ndo_*() functions
> * @ethtool_ops: Management operations
> + * @ndisc_ops: Includes callbacks for different IPv6 neighbour
> + * discovery handling. Necessary for e.g. 6LoWPAN.
> * @header_ops: Includes callbacks for creating,parsing,caching,etc
> * of Layer 2 headers.
> *
> @@ -1672,6 +1674,9 @@ struct net_device {
> #ifdef CONFIG_NET_L3_MASTER_DEV
> const struct l3mdev_ops *l3mdev_ops;
> #endif
> +#if IS_ENABLED(CONFIG_IPV6)
> + const struct ndisc_ops *ndisc_ops;
> +#endif
>
> const struct header_ops *header_ops;
>
> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
> index c8962ad..a5e2767 100644
> --- a/include/net/ndisc.h
> +++ b/include/net/ndisc.h
> @@ -58,6 +58,7 @@ struct inet6_dev;
> struct net_device;
> struct net_proto_family;
> struct sk_buff;
> +struct prefix_info;
>
> extern struct neigh_table nd_tbl;
>
> @@ -110,9 +111,182 @@ 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);
>
> +#define NDISC_OPS_REDIRECT_DATA_SPACE 2
> +
> +/*
> + * This structure defines the hooks for IPv6 neighbour discovery.
> + * The following hooks can be defined; unless noted otherwise, they are
> + * optional and can be filled with a null pointer.
> + *
> + * int (*is_useropt)(u8 nd_opt_type):
> + * This function is called when IPv6 decide RA userspace options. if
> + * this function returns 1 then the option given by nd_opt_type will
> + * be handled as userspace option additional to the IPv6 options.
> + *
> + * int (*parse_options)(const struct net_device *dev,
> + * struct nd_opt_hdr *nd_opt,
> + * struct ndisc_options *ndopts):
> + * This function is called while parsing ndisc ops and put each position
> + * as pointer into ndopts. If this function return unequal 0, then this
> + * function took care about the ndisc option, if 0 then the IPv6 ndisc
> + * option parser will take care about that option.
> + *
> + * void (*update)(const struct net_device *dev, struct neighbour *n,
> + * u32 flags, u8 icmp6_type,
> + * const struct ndisc_options *ndopts):
> + * This function is called when IPv6 ndisc updates the neighbour cache
> + * entry. Additional options which can be updated may be previously
> + * parsed by parse_opts callback and accessible over ndopts parameter.
> + *
> + * int (*opt_addr_space)(const struct net_device *dev, u8 icmp6_type,
> + * struct neighbour *neigh, u8 *ha_buf,
> + * u8 **ha):
> + * This function is called when the necessary option space will be
> + * calculated before allocating a skb. The parameters neigh, ha_buf
> + * abd ha are available on NDISC_REDIRECT messages only.
> + *
> + * void (*fill_addr_option)(const struct net_device *dev,
> + * struct sk_buff *skb, u8 icmp6_type,
> + * const u8 *ha):
> + * This function is called when the skb will finally fill the option
> + * fields inside skb. NOTE: this callback should fill the option
> + * fields to the skb which are previously indicated by opt_space
> + * parameter. That means the decision to add such option should
> + * not lost between these two callbacks, e.g. protected by interface
> + * up state.
> + *
> + * void (*prefix_rcv_add_addr)(struct net *net, struct net_device *dev,
> + * const struct prefix_info *pinfo,
> + * struct inet6_dev *in6_dev,
> + * struct in6_addr *addr,
> + * int addr_type, u32 addr_flags,
> + * bool sllao, bool tokenized,
> + * __u32 valid_lft, u32 prefered_lft,
> + * bool dev_addr_generated):
> + * This function is called when a RA messages is received with valid
> + * PIO option fields and an IPv6 address will be added to the interface
> + * for autoconfiguration. The parameter dev_addr_generated reports about
> + * if the address was based on dev->dev_addr or not. This can be used
> + * to add a second address if link-layer operates with two link layer
> + * addresses. E.g. 802.15.4 6LoWPAN.
> + */
> +struct ndisc_ops {
> + int (*is_useropt)(u8 nd_opt_type);
> + int (*parse_options)(const struct net_device *dev,
> + struct nd_opt_hdr *nd_opt,
> + struct ndisc_options *ndopts);
> + void (*update)(const struct net_device *dev, struct neighbour *n,
> + u32 flags, u8 icmp6_type,
> + const struct ndisc_options *ndopts);
> + int (*opt_addr_space)(const struct net_device *dev, u8 icmp6_type,
> + struct neighbour *neigh, u8 *ha_buf,
> + u8 **ha);
> + void (*fill_addr_option)(const struct net_device *dev,
> + struct sk_buff *skb, u8 icmp6_type,
> + const u8 *ha);
> + void (*prefix_rcv_add_addr)(struct net *net, struct net_device *dev,
> + const struct prefix_info *pinfo,
> + struct inet6_dev *in6_dev,
> + struct in6_addr *addr,
> + int addr_type, u32 addr_flags,
> + bool sllao, bool tokenized,
> + __u32 valid_lft, u32 prefered_lft,
> + bool dev_addr_generated);
> +};
> +
> +#if IS_ENABLED(CONFIG_IPV6)
> +static inline int ndisc_ops_is_useropt(const struct net_device *dev,
> + u8 nd_opt_type)
> +{
> + if (dev->ndisc_ops && dev->ndisc_ops->is_useropt)
> + return dev->ndisc_ops->is_useropt(nd_opt_type);
> + else
> + return 0;
> +}
> +
> +static inline int ndisc_ops_parse_options(const struct net_device *dev,
> + struct nd_opt_hdr *nd_opt,
> + struct ndisc_options *ndopts)
> +{
> + if (dev->ndisc_ops && dev->ndisc_ops->parse_options)
> + return dev->ndisc_ops->parse_options(dev, nd_opt, ndopts);
> + else
> + return 0;
> +}
> +
> +static inline void ndisc_ops_update(const struct net_device *dev,
> + struct neighbour *n, u32 flags,
> + u8 icmp6_type,
> + const struct ndisc_options *ndopts)
> +{
> + if (dev->ndisc_ops && dev->ndisc_ops->update)
> + dev->ndisc_ops->update(dev, n, flags, icmp6_type, ndopts);
> +}
> +
> +static inline int ndisc_ops_opt_addr_space(const struct net_device *dev,
> + u8 icmp6_type)
> +{
> + if (dev->ndisc_ops && dev->ndisc_ops->opt_addr_space &&
> + icmp6_type != NDISC_REDIRECT)
> + return dev->ndisc_ops->opt_addr_space(dev, icmp6_type, NULL,
> + NULL, NULL);
> + else
> + return 0;
> +}
> +
> +static inline int ndisc_ops_redirect_opt_addr_space(const struct net_device *dev,
> + struct neighbour *neigh,
> + u8 *ha_buf, u8 **ha)
> +{
> + if (dev->ndisc_ops && dev->ndisc_ops->opt_addr_space)
> + return dev->ndisc_ops->opt_addr_space(dev, NDISC_REDIRECT,
> + neigh, ha_buf, ha);
> + else
> + return 0;
> +}
> +
> +static inline void ndisc_ops_fill_addr_option(const struct net_device *dev,
> + struct sk_buff *skb,
> + u8 icmp6_type)
> +{
> + if (dev->ndisc_ops && dev->ndisc_ops->fill_addr_option &&
> + icmp6_type != NDISC_REDIRECT)
> + dev->ndisc_ops->fill_addr_option(dev, skb, icmp6_type, NULL);
> +}
> +
> +static inline void ndisc_ops_fill_redirect_addr_option(const struct net_device *dev,
> + struct sk_buff *skb,
> + const u8 *ha)
> +{
> + if (dev->ndisc_ops && dev->ndisc_ops->fill_addr_option)
> + dev->ndisc_ops->fill_addr_option(dev, skb, NDISC_REDIRECT, ha);
> +}
> +
> +static inline void ndisc_ops_prefix_rcv_add_addr(struct net *net,
> + struct net_device *dev,
> + const struct prefix_info *pinfo,
> + struct inet6_dev *in6_dev,
> + struct in6_addr *addr,
> + int addr_type, u32 addr_flags,
> + bool sllao, bool tokenized,
> + __u32 valid_lft,
> + u32 prefered_lft,
> + bool dev_addr_generated)
> +{
> + if (dev->ndisc_ops && dev->ndisc_ops->prefix_rcv_add_addr)
> + dev->ndisc_ops->prefix_rcv_add_addr(net, dev, pinfo, in6_dev,
> + addr, addr_type,
> + addr_flags, sllao,
> + tokenized, valid_lft,
> + prefered_lft,
> + dev_addr_generated);
> +}
> +#endif
> +
> /*
> * Return the padding between the option length and the start of the
> * link addr. Currently only IP-over-InfiniBand needs this, although
> @@ -132,11 +306,25 @@ static inline int __ndisc_opt_addr_space(unsigned char addr_len, int pad)
> return NDISC_OPT_SPACE(addr_len + pad);
> }
>
> -static inline int ndisc_opt_addr_space(struct net_device *dev)
> +#if IS_ENABLED(CONFIG_IPV6)
> +static inline int ndisc_opt_addr_space(struct net_device *dev, u8 icmp6_type)
> +{
> + return __ndisc_opt_addr_space(dev->addr_len,
> + ndisc_addr_option_pad(dev->type)) +
> + ndisc_ops_opt_addr_space(dev, icmp6_type);
> +}
> +
> +static inline int ndisc_redirect_opt_addr_space(struct net_device *dev,
> + struct neighbour *neigh,
> + u8 *ops_data_buf,
> + u8 **ops_data)
> {
> return __ndisc_opt_addr_space(dev->addr_len,
> - ndisc_addr_option_pad(dev->type));
> + ndisc_addr_option_pad(dev->type)) +
> + ndisc_ops_redirect_opt_addr_space(dev, neigh, ops_data_buf,
> + ops_data);
> }
> +#endif
>
> static inline u8 *__ndisc_opt_addr_data(struct nd_opt_hdr *p,
> unsigned char addr_len, int prepad)
> @@ -205,6 +393,9 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target);
> int ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev,
> int dir);
>
> +void ndisc_update(const struct net_device *dev, struct neighbour *neigh,
> + const u8 *lladdr, u8 new, u32 flags, u8 icmp6_type,
> + struct ndisc_options *ndopts);
>
> /*
> * IGMP
> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index 0ca31e1..2d678c0 100644
> --- a/net/ipv6/addrconf.c
> +++ b/net/ipv6/addrconf.c
> @@ -2531,7 +2531,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
>
> if (pinfo->autoconf && in6_dev->cnf.autoconf) {
> struct in6_addr addr;
> - bool tokenized = false;
> + bool tokenized = false, dev_addr_generated = false;
>
> if (pinfo->prefix_len == 64) {
> memcpy(&addr, &pinfo->prefix, 8);
> @@ -2550,6 +2550,8 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
> } else if (ipv6_generate_eui64(addr.s6_addr + 8, dev) &&
> ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) {
> goto put;
> + } else {
> + dev_addr_generated = true;
> }
> goto ok;
> }
> @@ -2565,6 +2567,15 @@ ok:
> prefered_lft);
> if (err)
> goto put;
> +
> + /* Ignore error case here because previous prefix add addr was
> + * successful which will be notified.
> + */
> + ndisc_ops_prefix_rcv_add_addr(net, dev, pinfo, in6_dev, &addr,
> + addr_type, addr_flags, sllao,
> + tokenized, valid_lft,
> + prefered_lft,
> + dev_addr_generated);
> }
> inet6_prefix_notify(RTM_NEWPREFIX, in6_dev, pinfo);
> put:
> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
> index a7b9468..2f4afd1 100644
> --- a/net/ipv6/ndisc.c
> +++ b/net/ipv6/ndisc.c
> @@ -172,10 +172,19 @@ static void __ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data,
> }
>
> static inline void ndisc_fill_addr_option(struct sk_buff *skb, int type,
> - void *data)
> + void *data, u8 icmp6_type)
> {
> __ndisc_fill_addr_option(skb, type, data, skb->dev->addr_len,
> ndisc_addr_option_pad(skb->dev->type));
> + ndisc_ops_fill_addr_option(skb->dev, skb, icmp6_type);
> +}
> +
> +static inline void ndisc_fill_redirect_addr_option(struct sk_buff *skb,
> + void *ha,
> + const u8 *ops_data)
> +{
> + ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR, ha, NDISC_REDIRECT);
> + ndisc_ops_fill_redirect_addr_option(skb->dev, skb, ops_data);
> }
>
> static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
> @@ -191,24 +200,28 @@ 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 ndisc_is_useropt(const struct net_device *dev,
> + struct nd_opt_hdr *opt)
> {
> return opt->nd_opt_type == ND_OPT_RDNSS ||
> - opt->nd_opt_type == ND_OPT_DNSSL;
> + opt->nd_opt_type == ND_OPT_DNSSL ||
> + ndisc_ops_is_useropt(dev, opt->nd_opt_type);
> }
>
> -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;
> @@ -223,6 +236,8 @@ struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
> l = nd_opt->nd_opt_len << 3;
> if (opt_len < l || l == 0)
> return NULL;
> + if (ndisc_ops_parse_options(dev, nd_opt, ndopts))
> + goto next_opt;
> switch (nd_opt->nd_opt_type) {
> case ND_OPT_SOURCE_LL_ADDR:
> case ND_OPT_TARGET_LL_ADDR:
> @@ -249,7 +264,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;
> @@ -266,6 +281,7 @@ struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
> nd_opt->nd_opt_len);
> }
> }
> +next_opt:
> opt_len -= l;
> nd_opt = ((void *)nd_opt) + l;
> }
> @@ -515,7 +531,8 @@ 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,
> + NDISC_NEIGHBOUR_ADVERTISEMENT);
>
> skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
> if (!skb)
> @@ -534,8 +551,8 @@ 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,
> + NDISC_NEIGHBOUR_ADVERTISEMENT);
>
> ndisc_send_skb(skb, daddr, src_addr);
> }
> @@ -580,7 +597,8 @@ 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,
> + NDISC_NEIGHBOUR_SOLICITATION);
>
> skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
> if (!skb)
> @@ -596,7 +614,8 @@ 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,
> + NDISC_NEIGHBOUR_SOLICITATION);
>
> ndisc_send_skb(skb, daddr, saddr);
> }
> @@ -632,7 +651,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, NDISC_ROUTER_SOLICITATION);
>
> skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
> if (!skb)
> @@ -647,7 +666,8 @@ 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,
> + NDISC_ROUTER_SOLICITATION);
>
> ndisc_send_skb(skb, daddr, saddr);
> }
> @@ -708,6 +728,15 @@ static int pndisc_is_router(const void *pkey,
> return ret;
> }
>
> +void ndisc_update(const struct net_device *dev, struct neighbour *neigh,
> + const u8 *lladdr, u8 new, u32 flags, u8 icmp6_type,
> + struct ndisc_options *ndopts)
> +{
> + neigh_update(neigh, lladdr, new, flags);
> + /* report ndisc ops about neighbour update */
> + ndisc_ops_update(dev, neigh, flags, icmp6_type, ndopts);
> +}
> +
> static void ndisc_recv_ns(struct sk_buff *skb)
> {
> struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
> @@ -744,7 +773,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;
> }
> @@ -862,9 +891,10 @@ have_ifp:
> neigh = __neigh_lookup(&nd_tbl, saddr, dev,
> !inc || lladdr || !dev->addr_len);
> if (neigh)
> - neigh_update(neigh, lladdr, NUD_STALE,
> + ndisc_update(dev, neigh, lladdr, NUD_STALE,
> NEIGH_UPDATE_F_WEAK_OVERRIDE|
> - NEIGH_UPDATE_F_OVERRIDE);
> + NEIGH_UPDATE_F_OVERRIDE,
> + NDISC_NEIGHBOUR_SOLICITATION, &ndopts);
> if (neigh || !dev->header_ops) {
> ndisc_send_na(dev, saddr, &msg->target, !!is_router,
> true, (ifp != NULL && inc), inc);
> @@ -917,7 +947,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;
> }
> @@ -973,12 +1003,13 @@ static void ndisc_recv_na(struct sk_buff *skb)
> goto out;
> }
>
> - neigh_update(neigh, lladdr,
> + ndisc_update(dev, 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));
> + (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0),
> + NDISC_NEIGHBOUR_ADVERTISEMENT, &ndopts);
>
> if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
> /*
> @@ -1023,7 +1054,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;
> }
> @@ -1037,10 +1068,11 @@ static void ndisc_recv_rs(struct sk_buff *skb)
>
> neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1);
> if (neigh) {
> - neigh_update(neigh, lladdr, NUD_STALE,
> + ndisc_update(skb->dev, neigh, lladdr, NUD_STALE,
> NEIGH_UPDATE_F_WEAK_OVERRIDE|
> NEIGH_UPDATE_F_OVERRIDE|
> - NEIGH_UPDATE_F_OVERRIDE_ISROUTER);
> + NEIGH_UPDATE_F_OVERRIDE_ISROUTER,
> + NDISC_ROUTER_SOLICITATION, &ndopts);
> neigh_release(neigh);
> }
> out:
> @@ -1141,7 +1173,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;
> }
> @@ -1335,11 +1367,12 @@ skip_linkparms:
> goto out;
> }
> }
> - neigh_update(neigh, lladdr, NUD_STALE,
> + ndisc_update(skb->dev, neigh, lladdr, NUD_STALE,
> NEIGH_UPDATE_F_WEAK_OVERRIDE|
> NEIGH_UPDATE_F_OVERRIDE|
> NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
> - NEIGH_UPDATE_F_ISROUTER);
> + NEIGH_UPDATE_F_ISROUTER,
> + NDISC_ROUTER_ADVERTISEMENT, &ndopts);
> }
>
> if (!ipv6_accept_ra(in6_dev)) {
> @@ -1427,7 +1460,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);
> }
> }
> @@ -1465,7 +1499,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) {
> @@ -1510,7 +1544,8 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
> struct dst_entry *dst;
> struct flowi6 fl6;
> int rd_len;
> - u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
> + u8 ha_buf[MAX_ADDR_LEN], *ha = NULL,
> + ops_data_buf[NDISC_OPS_REDIRECT_DATA_SPACE], *ops_data = NULL;
> int oif = l3mdev_fib_oif(dev);
> bool ret;
>
> @@ -1569,7 +1604,9 @@ 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_redirect_opt_addr_space(dev, neigh,
> + ops_data_buf,
> + &ops_data);
> } else
> read_unlock_bh(&neigh->lock);
>
> @@ -1600,7 +1637,7 @@ 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_redirect_addr_option(buff, ha, ops_data);
>
> /*
> * build redirect option and copy skb over to the new packet.
> diff --git a/net/ipv6/route.c b/net/ipv6/route.c
> index c6ae6f9..af6f711 100644
> --- a/net/ipv6/route.c
> +++ b/net/ipv6/route.c
> @@ -2200,7 +2200,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;
> }
> @@ -2235,12 +2235,12 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
> * We have finally decided to accept it.
> */
>
> - neigh_update(neigh, lladdr, NUD_STALE,
> + ndisc_update(skb->dev, neigh, lladdr, NUD_STALE,
> NEIGH_UPDATE_F_WEAK_OVERRIDE|
> NEIGH_UPDATE_F_OVERRIDE|
> (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
> - NEIGH_UPDATE_F_ISROUTER))
> - );
> + NEIGH_UPDATE_F_ISROUTER)),
> + NDISC_REDIRECT, &ndopts);
>
> nrt = ip6_rt_cache_alloc(rt, &msg->dest, NULL);
> if (!nrt)
>
--
$B5HF#1QL@(B <[email protected]>
$B%_%i%/%k!&%j%J%C%/%93t<02q<R(B $B5;=QK\It(B $B%5%]!<%HIt(B
Alexander Aring wrote:
> This patch moves the functionality to add a RA PIO prefix generated
> address in an own function. This move prepares to add a hook for
> adding a second address for a second link-layer address. E.g. short
> address for 802.15.4 6LoWPAN.
>
> 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]>
> Reviewed-by: Stefan Schmidt <[email protected]>
> Signed-off-by: Alexander Aring <[email protected]>
Acked-by: YOSHIFUJI Hideaki <[email protected]>
> ---
> net/ipv6/addrconf.c | 203 ++++++++++++++++++++++++++++------------------------
> 1 file changed, 109 insertions(+), 94 deletions(-)
>
> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index beaad49..0ca31e1 100644
> --- a/net/ipv6/addrconf.c
> +++ b/net/ipv6/addrconf.c
> @@ -2333,12 +2333,110 @@ static bool is_addr_mode_generate_stable(struct inet6_dev *idev)
> idev->addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
> }
>
> +static int addrconf_prefix_rcv_add_addr(struct net *net,
> + struct net_device *dev,
> + const struct prefix_info *pinfo,
> + struct inet6_dev *in6_dev,
> + const struct in6_addr *addr,
> + int addr_type, u32 addr_flags,
> + bool sllao, bool tokenized,
> + __u32 valid_lft, u32 prefered_lft)
> +{
> + struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1);
> + int create = 0, update_lft = 0;
> +
> + if (!ifp && valid_lft) {
> + int max_addresses = in6_dev->cnf.max_addresses;
> +
> +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
> + if (in6_dev->cnf.optimistic_dad &&
> + !net->ipv6.devconf_all->forwarding && sllao)
> + addr_flags |= IFA_F_OPTIMISTIC;
> +#endif
> +
> + /* Do not allow to create too much of autoconfigured
> + * addresses; this would be too easy way to crash kernel.
> + */
> + if (!max_addresses ||
> + ipv6_count_addresses(in6_dev) < max_addresses)
> + ifp = ipv6_add_addr(in6_dev, addr, NULL,
> + pinfo->prefix_len,
> + addr_type&IPV6_ADDR_SCOPE_MASK,
> + addr_flags, valid_lft,
> + prefered_lft);
> +
> + if (IS_ERR_OR_NULL(ifp))
> + return -1;
> +
> + update_lft = 0;
> + create = 1;
> + spin_lock_bh(&ifp->lock);
> + ifp->flags |= IFA_F_MANAGETEMPADDR;
> + ifp->cstamp = jiffies;
> + ifp->tokenized = tokenized;
> + spin_unlock_bh(&ifp->lock);
> + addrconf_dad_start(ifp);
> + }
> +
> + if (ifp) {
> + u32 flags;
> + unsigned long now;
> + u32 stored_lft;
> +
> + /* update lifetime (RFC2462 5.5.3 e) */
> + spin_lock_bh(&ifp->lock);
> + now = jiffies;
> + if (ifp->valid_lft > (now - ifp->tstamp) / HZ)
> + stored_lft = ifp->valid_lft - (now - ifp->tstamp) / HZ;
> + else
> + stored_lft = 0;
> + if (!update_lft && !create && stored_lft) {
> + const u32 minimum_lft = min_t(u32,
> + stored_lft, MIN_VALID_LIFETIME);
> + valid_lft = max(valid_lft, minimum_lft);
> +
> + /* RFC4862 Section 5.5.3e:
> + * "Note that the preferred lifetime of the
> + * corresponding address is always reset to
> + * the Preferred Lifetime in the received
> + * Prefix Information option, regardless of
> + * whether the valid lifetime is also reset or
> + * ignored."
> + *
> + * So we should always update prefered_lft here.
> + */
> + update_lft = 1;
> + }
> +
> + if (update_lft) {
> + ifp->valid_lft = valid_lft;
> + ifp->prefered_lft = prefered_lft;
> + ifp->tstamp = now;
> + flags = ifp->flags;
> + ifp->flags &= ~IFA_F_DEPRECATED;
> + spin_unlock_bh(&ifp->lock);
> +
> + if (!(flags&IFA_F_TENTATIVE))
> + ipv6_ifa_notify(0, ifp);
> + } else
> + spin_unlock_bh(&ifp->lock);
> +
> + manage_tempaddrs(in6_dev, ifp, valid_lft, prefered_lft,
> + create, now);
> +
> + in6_ifa_put(ifp);
> + addrconf_verify();
> + }
> +
> + return 0;
> +}
> +
> void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
> {
> struct prefix_info *pinfo;
> __u32 valid_lft;
> __u32 prefered_lft;
> - int addr_type;
> + int addr_type, err;
> u32 addr_flags = 0;
> struct inet6_dev *in6_dev;
> struct net *net = dev_net(dev);
> @@ -2432,9 +2530,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
> /* Try to figure out our local address for this prefix */
>
> if (pinfo->autoconf && in6_dev->cnf.autoconf) {
> - struct inet6_ifaddr *ifp;
> struct in6_addr addr;
> - int create = 0, update_lft = 0;
> bool tokenized = false;
>
> if (pinfo->prefix_len == 64) {
> @@ -2453,106 +2549,25 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
> goto ok;
> } else if (ipv6_generate_eui64(addr.s6_addr + 8, dev) &&
> ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) {
> - in6_dev_put(in6_dev);
> - return;
> + goto put;
> }
> goto ok;
> }
> net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n",
> pinfo->prefix_len);
> - in6_dev_put(in6_dev);
> - return;
> + goto put;
>
> ok:
> -
> - ifp = ipv6_get_ifaddr(net, &addr, dev, 1);
> -
> - if (!ifp && valid_lft) {
> - int max_addresses = in6_dev->cnf.max_addresses;
> -
> -#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
> - if (in6_dev->cnf.optimistic_dad &&
> - !net->ipv6.devconf_all->forwarding && sllao)
> - addr_flags |= IFA_F_OPTIMISTIC;
> -#endif
> -
> - /* Do not allow to create too much of autoconfigured
> - * addresses; this would be too easy way to crash kernel.
> - */
> - if (!max_addresses ||
> - ipv6_count_addresses(in6_dev) < max_addresses)
> - ifp = ipv6_add_addr(in6_dev, &addr, NULL,
> - pinfo->prefix_len,
> - addr_type&IPV6_ADDR_SCOPE_MASK,
> - addr_flags, valid_lft,
> - prefered_lft);
> -
> - if (IS_ERR_OR_NULL(ifp)) {
> - in6_dev_put(in6_dev);
> - return;
> - }
> -
> - update_lft = 0;
> - create = 1;
> - spin_lock_bh(&ifp->lock);
> - ifp->flags |= IFA_F_MANAGETEMPADDR;
> - ifp->cstamp = jiffies;
> - ifp->tokenized = tokenized;
> - spin_unlock_bh(&ifp->lock);
> - addrconf_dad_start(ifp);
> - }
> -
> - if (ifp) {
> - u32 flags;
> - unsigned long now;
> - u32 stored_lft;
> -
> - /* update lifetime (RFC2462 5.5.3 e) */
> - spin_lock_bh(&ifp->lock);
> - now = jiffies;
> - if (ifp->valid_lft > (now - ifp->tstamp) / HZ)
> - stored_lft = ifp->valid_lft - (now - ifp->tstamp) / HZ;
> - else
> - stored_lft = 0;
> - if (!update_lft && !create && stored_lft) {
> - const u32 minimum_lft = min_t(u32,
> - stored_lft, MIN_VALID_LIFETIME);
> - valid_lft = max(valid_lft, minimum_lft);
> -
> - /* RFC4862 Section 5.5.3e:
> - * "Note that the preferred lifetime of the
> - * corresponding address is always reset to
> - * the Preferred Lifetime in the received
> - * Prefix Information option, regardless of
> - * whether the valid lifetime is also reset or
> - * ignored."
> - *
> - * So we should always update prefered_lft here.
> - */
> - update_lft = 1;
> - }
> -
> - if (update_lft) {
> - ifp->valid_lft = valid_lft;
> - ifp->prefered_lft = prefered_lft;
> - ifp->tstamp = now;
> - flags = ifp->flags;
> - ifp->flags &= ~IFA_F_DEPRECATED;
> - spin_unlock_bh(&ifp->lock);
> -
> - if (!(flags&IFA_F_TENTATIVE))
> - ipv6_ifa_notify(0, ifp);
> - } else
> - spin_unlock_bh(&ifp->lock);
> -
> - manage_tempaddrs(in6_dev, ifp, valid_lft, prefered_lft,
> - create, now);
> -
> - in6_ifa_put(ifp);
> - addrconf_verify();
> - }
> + err = addrconf_prefix_rcv_add_addr(net, dev, pinfo, in6_dev,
> + &addr, addr_type,
> + addr_flags, sllao,
> + tokenized, valid_lft,
> + prefered_lft);
> + if (err)
> + goto put;
> }
> inet6_prefix_notify(RTM_NEWPREFIX, in6_dev, pinfo);
> +put:
> in6_dev_put(in6_dev);
> }
>
>
--
Hideaki Yoshifuji <[email protected]>
Technical Division, MIRACLE LINUX CORPORATION
Alexander Aring wrote:
> This patch adds __ndisc_fill_addr_option as low-level function for
> ndisc_fill_addr_option which doesn't depend on net_device parameter.
>
> Cc: David S. Miller <[email protected]>
> Cc: Alexey Kuznetsov <[email protected]>
> Cc: James Morris <[email protected]>
> Cc: Hideaki YOSHIFUJI <[email protected]>
> Cc: Patrick McHardy <[email protected]>
> Signed-off-by: Alexander Aring <[email protected]>
Acked-by: YOSHIFUJI Hideaki <[email protected]>
> ---
> net/ipv6/ndisc.c | 14 ++++++++++----
> 1 file changed, 10 insertions(+), 4 deletions(-)
>
> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
> index c245895..a7b9468 100644
> --- a/net/ipv6/ndisc.c
> +++ b/net/ipv6/ndisc.c
> @@ -150,11 +150,10 @@ 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)
> {
> - 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(data_len, pad);
> u8 *opt = skb_put(skb, space);
>
> opt[0] = type;
> @@ -172,6 +171,13 @@ static void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data)
> memset(opt, 0, space);
> }
>
> +static inline void ndisc_fill_addr_option(struct sk_buff *skb, int type,
> + void *data)
> +{
> + __ndisc_fill_addr_option(skb, type, data, skb->dev->addr_len,
> + ndisc_addr_option_pad(skb->dev->type));
> +}
> +
> static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
> struct nd_opt_hdr *end)
> {
>
--
Hideaki Yoshifuji <[email protected]>
Technical Division, MIRACLE LINUX CORPORATION
Alexander Aring wrote:
> This patch adds __ndisc_opt_addr_data as low-level function for
> ndisc_opt_addr_data which doesn't depend on net_device parameter.
>
> Cc: David S. Miller <[email protected]>
> Cc: Alexey Kuznetsov <[email protected]>
> Cc: James Morris <[email protected]>
> Cc: Hideaki YOSHIFUJI <[email protected]>
> Cc: Patrick McHardy <[email protected]>
> Signed-off-by: Alexander Aring <[email protected]>
Acked-by: YOSHIFUJI Hideaki <[email protected]>
> ---
> include/net/ndisc.h | 14 ++++++++++----
> 1 file changed, 10 insertions(+), 4 deletions(-)
>
> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
> index 4cee826..c8962ad 100644
> --- a/include/net/ndisc.h
> +++ b/include/net/ndisc.h
> @@ -138,17 +138,23 @@ static inline int ndisc_opt_addr_space(struct net_device *dev)
> ndisc_addr_option_pad(dev->type));
> }
>
> -static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,
> - struct net_device *dev)
> +static inline u8 *__ndisc_opt_addr_data(struct nd_opt_hdr *p,
> + unsigned char addr_len, int prepad)
> {
> 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(addr_len, prepad))
> return NULL;
> return lladdr + prepad;
> }
>
> +static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,
> + struct net_device *dev)
> +{
> + return __ndisc_opt_addr_data(p, dev->addr_len,
> + ndisc_addr_option_pad(dev->type));
> +}
> +
> static inline u32 ndisc_hashfn(const void *pkey, const struct net_device *dev, __u32 *hash_rnd)
> {
> const u32 *p32 = pkey;
>
--
Hideaki Yoshifuji <[email protected]>
Technical Division, MIRACLE LINUX CORPORATION
Alexander Aring wrote:
> This patch adds __ndisc_opt_addr_space as low-level function for
> ndisc_opt_addr_space which doesn't depend on net_device parameter.
>
> Cc: David S. Miller <[email protected]>
> Cc: Alexey Kuznetsov <[email protected]>
> Cc: James Morris <[email protected]>
> Cc: Hideaki YOSHIFUJI <[email protected]>
> Cc: Patrick McHardy <[email protected]>
> Signed-off-by: Alexander Aring <[email protected]>
Acked-by: YOSHIFUJI Hideaki <[email protected]>
> ---
> include/net/ndisc.h | 9 +++++++--
> 1 file changed, 7 insertions(+), 2 deletions(-)
>
> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
> index 2d8edaa..4cee826 100644
> --- a/include/net/ndisc.h
> +++ b/include/net/ndisc.h
> @@ -127,10 +127,15 @@ static inline int ndisc_addr_option_pad(unsigned short type)
> }
> }
>
> +static inline int __ndisc_opt_addr_space(unsigned char addr_len, int pad)
> +{
> + return NDISC_OPT_SPACE(addr_len + pad);
> +}
> +
> static inline int ndisc_opt_addr_space(struct net_device *dev)
> {
> - return NDISC_OPT_SPACE(dev->addr_len +
> - ndisc_addr_option_pad(dev->type));
> + return __ndisc_opt_addr_space(dev->addr_len,
> + ndisc_addr_option_pad(dev->type));
> }
>
> static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,
>
--
Hideaki Yoshifuji <[email protected]>
Technical Division, MIRACLE LINUX CORPORATION
Alexander Aring wrote:
> This patch will introduce a 6lowpan neighbour private data. Like the
> interface private data we handle private data for generic 6lowpan and
> for link-layer specific 6lowpan.
>
> The current first use case if to save the short address for a 802.15.4
> 6lowpan neighbour.
>
> Cc: David S. Miller <[email protected]>
> Reviewed-by: Stefan Schmidt <[email protected]>
> Signed-off-by: Alexander Aring <[email protected]>
Acked-by: YOSHIFUJI Hideaki <[email protected]>
> ---
> include/linux/netdevice.h | 3 +--
> include/net/6lowpan.h | 10 ++++++++++
> net/ieee802154/6lowpan/core.c | 12 ++++++++++++
> 3 files changed, 23 insertions(+), 2 deletions(-)
>
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index d101e4d..36e43bd 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -1483,8 +1483,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..2d9b9d3 100644
> --- a/include/net/6lowpan.h
> +++ b/include/net/6lowpan.h
> @@ -141,6 +141,16 @@ struct lowpan_dev {
> u8 priv[0] __aligned(sizeof(void *));
> };
>
> +struct lowpan_802154_neigh {
> + __le16 short_addr;
> +};
> +
> +static inline
> +struct lowpan_802154_neigh *lowpan_802154_neigh(void *neigh_priv)
> +{
> + return neigh_priv;
> +}
> +
> static inline
> struct lowpan_dev *lowpan_dev(const struct net_device *dev)
> {
> diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c
> index 4e2b308..8c004a0 100644
> --- a/net/ieee802154/6lowpan/core.c
> +++ b/net/ieee802154/6lowpan/core.c
> @@ -81,11 +81,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)
> @@ -150,6 +160,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 = sizeof(struct lowpan_802154_neigh);
> +
> ret = lowpan_register_netdevice(ldev, LOWPAN_LLTYPE_IEEE802154);
> if (ret < 0) {
> dev_put(wdev);
>
--
Hideaki Yoshifuji <[email protected]>
Technical Division, MIRACLE LINUX CORPORATION
Hi,
Alexander Aring wrote:
> Alexander Aring (12):
> 6lowpan: add private neighbour data
> 6lowpan: add 802.15.4 short addr slaac
> 6lowpan: remove ipv6 module request
> ndisc: add __ndisc_opt_addr_space function
> ndisc: add __ndisc_opt_addr_data function
> ndisc: add __ndisc_fill_addr_option function
> addrconf: put prefix address add in an own function
> ipv6: introduce neighbour discovery ops
> ipv6: export several functions
> 6lowpan: introduce 6lowpan-nd
> 6lowpan: add support for getting short address
> 6lowpan: add support for 802.15.4 short addr handling
>
> include/linux/netdevice.h | 8 +-
> include/net/6lowpan.h | 16 +++
> include/net/addrconf.h | 10 ++
> include/net/ndisc.h | 248 +++++++++++++++++++++++++++++++++++++++---
> net/6lowpan/6lowpan_i.h | 4 +
> net/6lowpan/Makefile | 2 +-
> net/6lowpan/core.c | 50 ++++++++-
> net/6lowpan/debugfs.c | 39 +++++++
> net/6lowpan/iphc.c | 167 +++++++++++++++++++++++-----
> net/6lowpan/ndisc.c | 234 +++++++++++++++++++++++++++++++++++++++
> net/ieee802154/6lowpan/core.c | 12 ++
> net/ieee802154/6lowpan/tx.c | 113 +++++++++----------
> net/ipv6/addrconf.c | 218 +++++++++++++++++++++----------------
> net/ipv6/ndisc.c | 123 +++++++++++++--------
> net/ipv6/route.c | 8 +-
> 15 files changed, 1004 insertions(+), 248 deletions(-)
> create mode 100644 net/6lowpan/ndisc.c
>
Looks good to me.
Acked-by: YOSHIFUJI Hideaki <[email protected]>
--
Hideaki Yoshifuji <[email protected]>
Technical Division, MIRACLE LINUX 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.
Reviewed-by: Stefan Schmidt <[email protected]>
Signed-off-by: Alexander Aring <[email protected]>
---
net/6lowpan/iphc.c | 167 ++++++++++++++++++++++++++++++++++++--------
net/ieee802154/6lowpan/tx.c | 113 +++++++++++++++---------------
2 files changed, 195 insertions(+), 85 deletions(-)
diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c
index 8501dd5..79f1fa2 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];
+ bool lladdr_compress = false;
+ struct in6_addr tmp = {};
+
+ 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];
+ bool lladdr_compress = false;
+ struct in6_addr tmp = {};
+
+ 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..dbb476d 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,50 @@ 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 {
+ __le16 short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);
+
+ n = neigh_lookup(&nd_tbl, &hdr->daddr, ldev);
+ if (n) {
+ llneigh = lowpan_802154_neigh(neighbour_priv(n));
+ read_lock_bh(&n->lock);
+ short_addr = llneigh->short_addr;
+ read_unlock_bh(&n->lock);
+ }
- info = lowpan_skb_priv(skb);
+ if (llneigh &&
+ lowpan_802154_is_valid_src_short_addr(short_addr)) {
+ info->daddr.short_addr = short_addr;
+ info->daddr.mode = IEEE802154_ADDR_SHORT;
+ } else {
+ info->daddr.mode = IEEE802154_ADDR_LONG;
+ ieee802154_be64_to_le64(&info->daddr.extended_addr,
+ daddr);
+ }
- /* 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 (n)
+ neigh_release(n);
+ }
+
+ if (!saddr) {
+ if (lowpan_802154_is_valid_src_short_addr(wpan_dev->short_addr)) {
+ info->saddr.mode = IEEE802154_ADDR_SHORT;
+ info->saddr.short_addr = wpan_dev->short_addr;
+ } else {
+ info->saddr.mode = IEEE802154_ADDR_LONG;
+ info->saddr.extended_addr = wpan_dev->extended_addr;
+ }
+ } else {
+ info->saddr.mode = IEEE802154_ADDR_LONG;
+ ieee802154_be64_to_le64(&info->saddr.extended_addr, saddr);
+ }
return 0;
}
@@ -209,47 +231,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.3
In case of sending RA messages we need some way to get the short address
from an 802.15.4 6LoWPAN interface. This patch will add a temporary
debugfs entry for experimental userspace api.
Reviewed-by: Stefan Schmidt <[email protected]>
Signed-off-by: Alexander Aring <[email protected]>
---
net/6lowpan/debugfs.c | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/net/6lowpan/debugfs.c b/net/6lowpan/debugfs.c
index acbaa3d..24915e0 100644
--- a/net/6lowpan/debugfs.c
+++ b/net/6lowpan/debugfs.c
@@ -245,6 +245,41 @@ static const struct file_operations lowpan_context_fops = {
.release = single_release,
};
+static int lowpan_short_addr_get(void *data, u64 *val)
+{
+ struct wpan_dev *wdev = data;
+
+ rtnl_lock();
+ *val = le16_to_cpu(wdev->short_addr);
+ rtnl_unlock();
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(lowpan_short_addr_fops, lowpan_short_addr_get,
+ NULL, "0x%04llx\n");
+
+static int lowpan_dev_debugfs_802154_init(const struct net_device *dev,
+ struct lowpan_dev *ldev)
+{
+ struct dentry *dentry, *root;
+
+ if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154))
+ return 0;
+
+ root = debugfs_create_dir("ieee802154", ldev->iface_debugfs);
+ if (!root)
+ return -EINVAL;
+
+ dentry = debugfs_create_file("short_addr", 0444, root,
+ lowpan_802154_dev(dev)->wdev->ieee802154_ptr,
+ &lowpan_short_addr_fops);
+ if (!dentry)
+ return -EINVAL;
+
+ return 0;
+}
+
int lowpan_dev_debugfs_init(struct net_device *dev)
{
struct lowpan_dev *ldev = lowpan_dev(dev);
@@ -272,6 +307,10 @@ int lowpan_dev_debugfs_init(struct net_device *dev)
goto remove_root;
}
+ ret = lowpan_dev_debugfs_802154_init(dev, ldev);
+ if (ret < 0)
+ goto remove_root;
+
return 0;
remove_root:
--
2.8.3
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.
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]>
Reviewed-by: Stefan Schmidt <[email protected]>
Signed-off-by: Alexander Aring <[email protected]>
---
include/net/ndisc.h | 18 ++--
net/6lowpan/6lowpan_i.h | 4 +
net/6lowpan/Makefile | 2 +-
net/6lowpan/core.c | 4 +-
net/6lowpan/ndisc.c | 234 ++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 254 insertions(+), 8 deletions(-)
create mode 100644 net/6lowpan/ndisc.c
diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index 3f0f41d..be1fe228 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
};
@@ -109,14 +110,19 @@ struct ndisc_options {
#endif
struct nd_opt_hdr *nd_useropts;
struct nd_opt_hdr *nd_useropts_end;
+#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
+ struct nd_opt_hdr *nd_802154_opt_array[ND_OPT_TARGET_LL_ADDR + 1];
+#endif
};
-#define nd_opts_src_lladdr nd_opt_array[ND_OPT_SOURCE_LL_ADDR]
-#define nd_opts_tgt_lladdr nd_opt_array[ND_OPT_TARGET_LL_ADDR]
-#define nd_opts_pi nd_opt_array[ND_OPT_PREFIX_INFO]
-#define nd_opts_pi_end nd_opt_array[__ND_OPT_PREFIX_INFO_END]
-#define nd_opts_rh nd_opt_array[ND_OPT_REDIRECT_HDR]
-#define nd_opts_mtu nd_opt_array[ND_OPT_MTU]
+#define nd_opts_src_lladdr nd_opt_array[ND_OPT_SOURCE_LL_ADDR]
+#define nd_opts_tgt_lladdr nd_opt_array[ND_OPT_TARGET_LL_ADDR]
+#define nd_opts_pi nd_opt_array[ND_OPT_PREFIX_INFO]
+#define nd_opts_pi_end nd_opt_array[__ND_OPT_PREFIX_INFO_END]
+#define nd_opts_rh nd_opt_array[ND_OPT_REDIRECT_HDR]
+#define nd_opts_mtu nd_opt_array[ND_OPT_MTU]
+#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_OPT_SPACE(len) (((len)+2+7)&~7)
diff --git a/net/6lowpan/6lowpan_i.h b/net/6lowpan/6lowpan_i.h
index 97ecc27..a67caee 100644
--- a/net/6lowpan/6lowpan_i.h
+++ b/net/6lowpan/6lowpan_i.h
@@ -12,6 +12,10 @@ static inline bool lowpan_is_ll(const struct net_device *dev,
return lowpan_dev(dev)->lltype == lltype;
}
+extern const struct ndisc_ops lowpan_ndisc_ops;
+
+int addrconf_ifid_802154_6lowpan(u8 *eui, 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 1c7a42b..5945f7e 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;
+ dev->ndisc_ops = &lowpan_ndisc_ops;
+
ret = register_netdevice(dev);
if (ret < 0)
return ret;
@@ -73,7 +75,7 @@ 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)
+int addrconf_ifid_802154_6lowpan(u8 *eui, struct net_device *dev)
{
struct wpan_dev *wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr;
diff --git a/net/6lowpan/ndisc.c b/net/6lowpan/ndisc.c
new file mode 100644
index 0000000..ae1d419
--- /dev/null
+++ b/net/6lowpan/ndisc.c
@@ -0,0 +1,234 @@
+/* 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.
+ *
+ * Authors:
+ * (C) 2016 Pengutronix, Alexander Aring <[email protected]>
+ */
+
+#include <net/6lowpan.h>
+#include <net/addrconf.h>
+#include <net/ndisc.h>
+
+#include "6lowpan_i.h"
+
+static int lowpan_ndisc_is_useropt(u8 nd_opt_type)
+{
+ return nd_opt_type == ND_OPT_6CO;
+}
+
+#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
+#define NDISC_802154_SHORT_ADDR_LENGTH 1
+static int lowpan_ndisc_parse_802154_options(const struct net_device *dev,
+ struct nd_opt_hdr *nd_opt,
+ struct ndisc_options *ndopts)
+{
+ switch (nd_opt->nd_opt_len) {
+ case NDISC_802154_SHORT_ADDR_LENGTH:
+ 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;
+ return 1;
+ default:
+ /* all others will be handled by ndisc IPv6 option parsing */
+ return 0;
+ }
+}
+
+static int lowpan_ndisc_parse_options(const struct net_device *dev,
+ struct nd_opt_hdr *nd_opt,
+ struct ndisc_options *ndopts)
+{
+ switch (nd_opt->nd_opt_type) {
+ case ND_OPT_SOURCE_LL_ADDR:
+ case ND_OPT_TARGET_LL_ADDR:
+ return lowpan_ndisc_parse_802154_options(dev, nd_opt, ndopts);
+ default:
+ return 0;
+ }
+}
+
+static void lowpan_ndisc_802154_update(struct neighbour *n, u32 flags,
+ u8 icmp6_type,
+ const struct ndisc_options *ndopts)
+{
+ struct lowpan_802154_neigh *neigh = lowpan_802154_neigh(neighbour_priv(n));
+ u8 *lladdr_short = NULL;
+
+ switch (icmp6_type) {
+ case NDISC_ROUTER_SOLICITATION:
+ case NDISC_ROUTER_ADVERTISEMENT:
+ case NDISC_NEIGHBOUR_SOLICITATION:
+ if (ndopts->nd_802154_opts_src_lladdr) {
+ lladdr_short = __ndisc_opt_addr_data(ndopts->nd_802154_opts_src_lladdr,
+ IEEE802154_SHORT_ADDR_LEN, 0);
+ if (!lladdr_short) {
+ ND_PRINTK(2, warn,
+ "NA: invalid short link-layer address length\n");
+ return;
+ }
+ }
+ break;
+ case NDISC_REDIRECT:
+ case NDISC_NEIGHBOUR_ADVERTISEMENT:
+ if (ndopts->nd_802154_opts_tgt_lladdr) {
+ lladdr_short = __ndisc_opt_addr_data(ndopts->nd_802154_opts_tgt_lladdr,
+ IEEE802154_SHORT_ADDR_LEN, 0);
+ if (!lladdr_short) {
+ ND_PRINTK(2, warn,
+ "NA: invalid short link-layer address length\n");
+ return;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ write_lock_bh(&n->lock);
+ if (lladdr_short)
+ ieee802154_be16_to_le16(&neigh->short_addr, lladdr_short);
+ else
+ neigh->short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);
+ write_unlock_bh(&n->lock);
+}
+
+static void lowpan_ndisc_update(const struct net_device *dev,
+ struct neighbour *n, u32 flags, u8 icmp6_type,
+ const struct ndisc_options *ndopts)
+{
+ if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154))
+ return;
+
+ /* react on overrides only. TODO check if this is really right. */
+ if (flags & NEIGH_UPDATE_F_OVERRIDE)
+ lowpan_ndisc_802154_update(n, flags, icmp6_type, ndopts);
+}
+
+static int lowpan_ndisc_opt_addr_space(const struct net_device *dev,
+ u8 icmp6_type, struct neighbour *neigh,
+ u8 *ha_buf, u8 **ha)
+{
+ struct lowpan_802154_neigh *n;
+ struct wpan_dev *wpan_dev;
+ int addr_space = 0;
+
+ if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154))
+ return 0;
+
+ switch (icmp6_type) {
+ case NDISC_REDIRECT:
+ n = lowpan_802154_neigh(neighbour_priv(neigh));
+
+ read_lock_bh(&neigh->lock);
+ if (lowpan_802154_is_valid_src_short_addr(n->short_addr)) {
+ memcpy(ha_buf, &n->short_addr,
+ IEEE802154_SHORT_ADDR_LEN);
+ read_unlock_bh(&neigh->lock);
+ addr_space += __ndisc_opt_addr_space(IEEE802154_SHORT_ADDR_LEN, 0);
+ *ha = ha_buf;
+ }
+ read_unlock_bh(&neigh->lock);
+ break;
+ case NDISC_NEIGHBOUR_ADVERTISEMENT:
+ case NDISC_NEIGHBOUR_SOLICITATION:
+ case NDISC_ROUTER_SOLICITATION:
+ wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr;
+
+ if (lowpan_802154_is_valid_src_short_addr(wpan_dev->short_addr))
+ addr_space = __ndisc_opt_addr_space(IEEE802154_SHORT_ADDR_LEN, 0);
+ break;
+ default:
+ break;
+ }
+
+ return addr_space;
+}
+
+static void lowpan_ndisc_fill_addr_option(const struct net_device *dev,
+ struct sk_buff *skb, u8 icmp6_type,
+ const u8 *ha)
+{
+ struct wpan_dev *wpan_dev;
+ __be16 short_addr;
+ u8 opt_type;
+
+ if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154))
+ return;
+
+ switch (icmp6_type) {
+ case NDISC_REDIRECT:
+ if (ha) {
+ ieee802154_le16_to_be16(&short_addr, ha);
+ __ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR,
+ &short_addr,
+ IEEE802154_SHORT_ADDR_LEN, 0);
+ }
+ return;
+ case NDISC_NEIGHBOUR_ADVERTISEMENT:
+ opt_type = ND_OPT_TARGET_LL_ADDR;
+ break;
+ case NDISC_ROUTER_SOLICITATION:
+ case NDISC_NEIGHBOUR_SOLICITATION:
+ opt_type = ND_OPT_SOURCE_LL_ADDR;
+ break;
+ default:
+ return;
+ }
+
+ wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr;
+
+ if (lowpan_802154_is_valid_src_short_addr(wpan_dev->short_addr)) {
+ ieee802154_le16_to_be16(&short_addr,
+ &wpan_dev->short_addr);
+ __ndisc_fill_addr_option(skb, opt_type, &short_addr,
+ IEEE802154_SHORT_ADDR_LEN, 0);
+ }
+}
+
+static void lowpan_ndisc_prefix_rcv_add_addr(struct net *net,
+ struct net_device *dev,
+ const struct prefix_info *pinfo,
+ struct inet6_dev *in6_dev,
+ struct in6_addr *addr,
+ int addr_type, u32 addr_flags,
+ bool sllao, bool tokenized,
+ __u32 valid_lft,
+ u32 prefered_lft,
+ bool dev_addr_generated)
+{
+ int err;
+
+ /* generates short based address for RA PIO's */
+ if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154) && dev_addr_generated &&
+ !addrconf_ifid_802154_6lowpan(addr->s6_addr + 8, dev)) {
+ err = addrconf_prefix_rcv_add_addr(net, dev, pinfo, in6_dev,
+ addr, addr_type, addr_flags,
+ sllao, tokenized, valid_lft,
+ prefered_lft);
+ if (err)
+ ND_PRINTK(2, warn,
+ "RA: could not add a short address based address for prefix: %pI6c\n",
+ &pinfo->prefix);
+ }
+}
+#endif
+
+const struct ndisc_ops lowpan_ndisc_ops = {
+ .is_useropt = lowpan_ndisc_is_useropt,
+#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
+ .parse_options = lowpan_ndisc_parse_options,
+ .update = lowpan_ndisc_update,
+ .opt_addr_space = lowpan_ndisc_opt_addr_space,
+ .fill_addr_option = lowpan_ndisc_fill_addr_option,
+ .prefix_rcv_add_addr = lowpan_ndisc_prefix_rcv_add_addr,
+#endif
+};
--
2.8.3
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/addrconf.h | 7 +++++++
include/net/ndisc.h | 12 ++++++++++++
net/ipv6/addrconf.c | 15 +++++++--------
net/ipv6/ndisc.c | 14 +++-----------
4 files changed, 29 insertions(+), 19 deletions(-)
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index b1774eb..9826d3a 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -97,6 +97,13 @@ 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);
+int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
+ const struct prefix_info *pinfo,
+ struct inet6_dev *in6_dev,
+ const struct in6_addr *addr, int addr_type,
+ u32 addr_flags, bool sllao, bool tokenized,
+ __u32 valid_lft, u32 prefered_lft);
+
static inline int addrconf_ifid_eui48(u8 *eui, struct net_device *dev)
{
if (dev->addr_len != ETH_ALEN)
diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index a5e2767..3f0f41d 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;
@@ -115,6 +124,9 @@ struct ndisc_options *ndisc_parse_options(const struct net_device *dev,
u8 *opt, int opt_len,
struct ndisc_options *ndopts);
+void __ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data,
+ int data_len, int pad);
+
#define NDISC_OPS_REDIRECT_DATA_SPACE 2
/*
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 2d678c0..9c7d660 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -2333,14 +2333,12 @@ static bool is_addr_mode_generate_stable(struct inet6_dev *idev)
idev->addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
}
-static int addrconf_prefix_rcv_add_addr(struct net *net,
- struct net_device *dev,
- const struct prefix_info *pinfo,
- struct inet6_dev *in6_dev,
- const struct in6_addr *addr,
- int addr_type, u32 addr_flags,
- bool sllao, bool tokenized,
- __u32 valid_lft, u32 prefered_lft)
+int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
+ const struct prefix_info *pinfo,
+ struct inet6_dev *in6_dev,
+ const struct in6_addr *addr, int addr_type,
+ u32 addr_flags, bool sllao, bool tokenized,
+ __u32 valid_lft, u32 prefered_lft)
{
struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1);
int create = 0, update_lft = 0;
@@ -2430,6 +2428,7 @@ static int addrconf_prefix_rcv_add_addr(struct net *net,
return 0;
}
+EXPORT_SYMBOL_GPL(addrconf_prefix_rcv_add_addr);
void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
{
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 2f4afd1..fe65cdc 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, int pad)
+void __ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data,
+ int data_len, int pad)
{
int space = __ndisc_opt_addr_space(data_len, pad);
u8 *opt = skb_put(skb, space);
@@ -170,6 +161,7 @@ static void __ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data,
if (space > 0)
memset(opt, 0, space);
}
+EXPORT_SYMBOL_GPL(__ndisc_fill_addr_option);
static inline void ndisc_fill_addr_option(struct sk_buff *skb, int type,
void *data, u8 icmp6_type)
--
2.8.3
This patch introduces neighbour discovery ops callback structure. The
idea is to separate the handling for 6LoWPAN into the 6lowpan module.
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 | 5 ++
include/net/ndisc.h | 197 +++++++++++++++++++++++++++++++++++++++++++++-
net/ipv6/addrconf.c | 13 ++-
net/ipv6/ndisc.c | 101 ++++++++++++++++--------
net/ipv6/route.c | 8 +-
5 files changed, 284 insertions(+), 40 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 36e43bd..890158e 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1456,6 +1456,8 @@ enum netdev_priv_flags {
* @netdev_ops: Includes several pointers to callbacks,
* if one wants to override the ndo_*() functions
* @ethtool_ops: Management operations
+ * @ndisc_ops: Includes callbacks for different IPv6 neighbour
+ * discovery handling. Necessary for e.g. 6LoWPAN.
* @header_ops: Includes callbacks for creating,parsing,caching,etc
* of Layer 2 headers.
*
@@ -1672,6 +1674,9 @@ struct net_device {
#ifdef CONFIG_NET_L3_MASTER_DEV
const struct l3mdev_ops *l3mdev_ops;
#endif
+#if IS_ENABLED(CONFIG_IPV6)
+ const struct ndisc_ops *ndisc_ops;
+#endif
const struct header_ops *header_ops;
diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index c8962ad..a5e2767 100644
--- a/include/net/ndisc.h
+++ b/include/net/ndisc.h
@@ -58,6 +58,7 @@ struct inet6_dev;
struct net_device;
struct net_proto_family;
struct sk_buff;
+struct prefix_info;
extern struct neigh_table nd_tbl;
@@ -110,9 +111,182 @@ 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);
+#define NDISC_OPS_REDIRECT_DATA_SPACE 2
+
+/*
+ * This structure defines the hooks for IPv6 neighbour discovery.
+ * The following hooks can be defined; unless noted otherwise, they are
+ * optional and can be filled with a null pointer.
+ *
+ * int (*is_useropt)(u8 nd_opt_type):
+ * This function is called when IPv6 decide RA userspace options. if
+ * this function returns 1 then the option given by nd_opt_type will
+ * be handled as userspace option additional to the IPv6 options.
+ *
+ * int (*parse_options)(const struct net_device *dev,
+ * struct nd_opt_hdr *nd_opt,
+ * struct ndisc_options *ndopts):
+ * This function is called while parsing ndisc ops and put each position
+ * as pointer into ndopts. If this function return unequal 0, then this
+ * function took care about the ndisc option, if 0 then the IPv6 ndisc
+ * option parser will take care about that option.
+ *
+ * void (*update)(const struct net_device *dev, struct neighbour *n,
+ * u32 flags, u8 icmp6_type,
+ * const struct ndisc_options *ndopts):
+ * This function is called when IPv6 ndisc updates the neighbour cache
+ * entry. Additional options which can be updated may be previously
+ * parsed by parse_opts callback and accessible over ndopts parameter.
+ *
+ * int (*opt_addr_space)(const struct net_device *dev, u8 icmp6_type,
+ * struct neighbour *neigh, u8 *ha_buf,
+ * u8 **ha):
+ * This function is called when the necessary option space will be
+ * calculated before allocating a skb. The parameters neigh, ha_buf
+ * abd ha are available on NDISC_REDIRECT messages only.
+ *
+ * void (*fill_addr_option)(const struct net_device *dev,
+ * struct sk_buff *skb, u8 icmp6_type,
+ * const u8 *ha):
+ * This function is called when the skb will finally fill the option
+ * fields inside skb. NOTE: this callback should fill the option
+ * fields to the skb which are previously indicated by opt_space
+ * parameter. That means the decision to add such option should
+ * not lost between these two callbacks, e.g. protected by interface
+ * up state.
+ *
+ * void (*prefix_rcv_add_addr)(struct net *net, struct net_device *dev,
+ * const struct prefix_info *pinfo,
+ * struct inet6_dev *in6_dev,
+ * struct in6_addr *addr,
+ * int addr_type, u32 addr_flags,
+ * bool sllao, bool tokenized,
+ * __u32 valid_lft, u32 prefered_lft,
+ * bool dev_addr_generated):
+ * This function is called when a RA messages is received with valid
+ * PIO option fields and an IPv6 address will be added to the interface
+ * for autoconfiguration. The parameter dev_addr_generated reports about
+ * if the address was based on dev->dev_addr or not. This can be used
+ * to add a second address if link-layer operates with two link layer
+ * addresses. E.g. 802.15.4 6LoWPAN.
+ */
+struct ndisc_ops {
+ int (*is_useropt)(u8 nd_opt_type);
+ int (*parse_options)(const struct net_device *dev,
+ struct nd_opt_hdr *nd_opt,
+ struct ndisc_options *ndopts);
+ void (*update)(const struct net_device *dev, struct neighbour *n,
+ u32 flags, u8 icmp6_type,
+ const struct ndisc_options *ndopts);
+ int (*opt_addr_space)(const struct net_device *dev, u8 icmp6_type,
+ struct neighbour *neigh, u8 *ha_buf,
+ u8 **ha);
+ void (*fill_addr_option)(const struct net_device *dev,
+ struct sk_buff *skb, u8 icmp6_type,
+ const u8 *ha);
+ void (*prefix_rcv_add_addr)(struct net *net, struct net_device *dev,
+ const struct prefix_info *pinfo,
+ struct inet6_dev *in6_dev,
+ struct in6_addr *addr,
+ int addr_type, u32 addr_flags,
+ bool sllao, bool tokenized,
+ __u32 valid_lft, u32 prefered_lft,
+ bool dev_addr_generated);
+};
+
+#if IS_ENABLED(CONFIG_IPV6)
+static inline int ndisc_ops_is_useropt(const struct net_device *dev,
+ u8 nd_opt_type)
+{
+ if (dev->ndisc_ops && dev->ndisc_ops->is_useropt)
+ return dev->ndisc_ops->is_useropt(nd_opt_type);
+ else
+ return 0;
+}
+
+static inline int ndisc_ops_parse_options(const struct net_device *dev,
+ struct nd_opt_hdr *nd_opt,
+ struct ndisc_options *ndopts)
+{
+ if (dev->ndisc_ops && dev->ndisc_ops->parse_options)
+ return dev->ndisc_ops->parse_options(dev, nd_opt, ndopts);
+ else
+ return 0;
+}
+
+static inline void ndisc_ops_update(const struct net_device *dev,
+ struct neighbour *n, u32 flags,
+ u8 icmp6_type,
+ const struct ndisc_options *ndopts)
+{
+ if (dev->ndisc_ops && dev->ndisc_ops->update)
+ dev->ndisc_ops->update(dev, n, flags, icmp6_type, ndopts);
+}
+
+static inline int ndisc_ops_opt_addr_space(const struct net_device *dev,
+ u8 icmp6_type)
+{
+ if (dev->ndisc_ops && dev->ndisc_ops->opt_addr_space &&
+ icmp6_type != NDISC_REDIRECT)
+ return dev->ndisc_ops->opt_addr_space(dev, icmp6_type, NULL,
+ NULL, NULL);
+ else
+ return 0;
+}
+
+static inline int ndisc_ops_redirect_opt_addr_space(const struct net_device *dev,
+ struct neighbour *neigh,
+ u8 *ha_buf, u8 **ha)
+{
+ if (dev->ndisc_ops && dev->ndisc_ops->opt_addr_space)
+ return dev->ndisc_ops->opt_addr_space(dev, NDISC_REDIRECT,
+ neigh, ha_buf, ha);
+ else
+ return 0;
+}
+
+static inline void ndisc_ops_fill_addr_option(const struct net_device *dev,
+ struct sk_buff *skb,
+ u8 icmp6_type)
+{
+ if (dev->ndisc_ops && dev->ndisc_ops->fill_addr_option &&
+ icmp6_type != NDISC_REDIRECT)
+ dev->ndisc_ops->fill_addr_option(dev, skb, icmp6_type, NULL);
+}
+
+static inline void ndisc_ops_fill_redirect_addr_option(const struct net_device *dev,
+ struct sk_buff *skb,
+ const u8 *ha)
+{
+ if (dev->ndisc_ops && dev->ndisc_ops->fill_addr_option)
+ dev->ndisc_ops->fill_addr_option(dev, skb, NDISC_REDIRECT, ha);
+}
+
+static inline void ndisc_ops_prefix_rcv_add_addr(struct net *net,
+ struct net_device *dev,
+ const struct prefix_info *pinfo,
+ struct inet6_dev *in6_dev,
+ struct in6_addr *addr,
+ int addr_type, u32 addr_flags,
+ bool sllao, bool tokenized,
+ __u32 valid_lft,
+ u32 prefered_lft,
+ bool dev_addr_generated)
+{
+ if (dev->ndisc_ops && dev->ndisc_ops->prefix_rcv_add_addr)
+ dev->ndisc_ops->prefix_rcv_add_addr(net, dev, pinfo, in6_dev,
+ addr, addr_type,
+ addr_flags, sllao,
+ tokenized, valid_lft,
+ prefered_lft,
+ dev_addr_generated);
+}
+#endif
+
/*
* Return the padding between the option length and the start of the
* link addr. Currently only IP-over-InfiniBand needs this, although
@@ -132,11 +306,25 @@ static inline int __ndisc_opt_addr_space(unsigned char addr_len, int pad)
return NDISC_OPT_SPACE(addr_len + pad);
}
-static inline int ndisc_opt_addr_space(struct net_device *dev)
+#if IS_ENABLED(CONFIG_IPV6)
+static inline int ndisc_opt_addr_space(struct net_device *dev, u8 icmp6_type)
+{
+ return __ndisc_opt_addr_space(dev->addr_len,
+ ndisc_addr_option_pad(dev->type)) +
+ ndisc_ops_opt_addr_space(dev, icmp6_type);
+}
+
+static inline int ndisc_redirect_opt_addr_space(struct net_device *dev,
+ struct neighbour *neigh,
+ u8 *ops_data_buf,
+ u8 **ops_data)
{
return __ndisc_opt_addr_space(dev->addr_len,
- ndisc_addr_option_pad(dev->type));
+ ndisc_addr_option_pad(dev->type)) +
+ ndisc_ops_redirect_opt_addr_space(dev, neigh, ops_data_buf,
+ ops_data);
}
+#endif
static inline u8 *__ndisc_opt_addr_data(struct nd_opt_hdr *p,
unsigned char addr_len, int prepad)
@@ -205,6 +393,9 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target);
int ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev,
int dir);
+void ndisc_update(const struct net_device *dev, struct neighbour *neigh,
+ const u8 *lladdr, u8 new, u32 flags, u8 icmp6_type,
+ struct ndisc_options *ndopts);
/*
* IGMP
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 0ca31e1..2d678c0 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -2531,7 +2531,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
if (pinfo->autoconf && in6_dev->cnf.autoconf) {
struct in6_addr addr;
- bool tokenized = false;
+ bool tokenized = false, dev_addr_generated = false;
if (pinfo->prefix_len == 64) {
memcpy(&addr, &pinfo->prefix, 8);
@@ -2550,6 +2550,8 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
} else if (ipv6_generate_eui64(addr.s6_addr + 8, dev) &&
ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) {
goto put;
+ } else {
+ dev_addr_generated = true;
}
goto ok;
}
@@ -2565,6 +2567,15 @@ ok:
prefered_lft);
if (err)
goto put;
+
+ /* Ignore error case here because previous prefix add addr was
+ * successful which will be notified.
+ */
+ ndisc_ops_prefix_rcv_add_addr(net, dev, pinfo, in6_dev, &addr,
+ addr_type, addr_flags, sllao,
+ tokenized, valid_lft,
+ prefered_lft,
+ dev_addr_generated);
}
inet6_prefix_notify(RTM_NEWPREFIX, in6_dev, pinfo);
put:
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index a7b9468..2f4afd1 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -172,10 +172,19 @@ static void __ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data,
}
static inline void ndisc_fill_addr_option(struct sk_buff *skb, int type,
- void *data)
+ void *data, u8 icmp6_type)
{
__ndisc_fill_addr_option(skb, type, data, skb->dev->addr_len,
ndisc_addr_option_pad(skb->dev->type));
+ ndisc_ops_fill_addr_option(skb->dev, skb, icmp6_type);
+}
+
+static inline void ndisc_fill_redirect_addr_option(struct sk_buff *skb,
+ void *ha,
+ const u8 *ops_data)
+{
+ ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR, ha, NDISC_REDIRECT);
+ ndisc_ops_fill_redirect_addr_option(skb->dev, skb, ops_data);
}
static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
@@ -191,24 +200,28 @@ 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 ndisc_is_useropt(const struct net_device *dev,
+ struct nd_opt_hdr *opt)
{
return opt->nd_opt_type == ND_OPT_RDNSS ||
- opt->nd_opt_type == ND_OPT_DNSSL;
+ opt->nd_opt_type == ND_OPT_DNSSL ||
+ ndisc_ops_is_useropt(dev, opt->nd_opt_type);
}
-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;
@@ -223,6 +236,8 @@ struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
l = nd_opt->nd_opt_len << 3;
if (opt_len < l || l == 0)
return NULL;
+ if (ndisc_ops_parse_options(dev, nd_opt, ndopts))
+ goto next_opt;
switch (nd_opt->nd_opt_type) {
case ND_OPT_SOURCE_LL_ADDR:
case ND_OPT_TARGET_LL_ADDR:
@@ -249,7 +264,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;
@@ -266,6 +281,7 @@ struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
nd_opt->nd_opt_len);
}
}
+next_opt:
opt_len -= l;
nd_opt = ((void *)nd_opt) + l;
}
@@ -515,7 +531,8 @@ 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,
+ NDISC_NEIGHBOUR_ADVERTISEMENT);
skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
if (!skb)
@@ -534,8 +551,8 @@ 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,
+ NDISC_NEIGHBOUR_ADVERTISEMENT);
ndisc_send_skb(skb, daddr, src_addr);
}
@@ -580,7 +597,8 @@ 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,
+ NDISC_NEIGHBOUR_SOLICITATION);
skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
if (!skb)
@@ -596,7 +614,8 @@ 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,
+ NDISC_NEIGHBOUR_SOLICITATION);
ndisc_send_skb(skb, daddr, saddr);
}
@@ -632,7 +651,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, NDISC_ROUTER_SOLICITATION);
skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
if (!skb)
@@ -647,7 +666,8 @@ 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,
+ NDISC_ROUTER_SOLICITATION);
ndisc_send_skb(skb, daddr, saddr);
}
@@ -708,6 +728,15 @@ static int pndisc_is_router(const void *pkey,
return ret;
}
+void ndisc_update(const struct net_device *dev, struct neighbour *neigh,
+ const u8 *lladdr, u8 new, u32 flags, u8 icmp6_type,
+ struct ndisc_options *ndopts)
+{
+ neigh_update(neigh, lladdr, new, flags);
+ /* report ndisc ops about neighbour update */
+ ndisc_ops_update(dev, neigh, flags, icmp6_type, ndopts);
+}
+
static void ndisc_recv_ns(struct sk_buff *skb)
{
struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
@@ -744,7 +773,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;
}
@@ -862,9 +891,10 @@ have_ifp:
neigh = __neigh_lookup(&nd_tbl, saddr, dev,
!inc || lladdr || !dev->addr_len);
if (neigh)
- neigh_update(neigh, lladdr, NUD_STALE,
+ ndisc_update(dev, neigh, lladdr, NUD_STALE,
NEIGH_UPDATE_F_WEAK_OVERRIDE|
- NEIGH_UPDATE_F_OVERRIDE);
+ NEIGH_UPDATE_F_OVERRIDE,
+ NDISC_NEIGHBOUR_SOLICITATION, &ndopts);
if (neigh || !dev->header_ops) {
ndisc_send_na(dev, saddr, &msg->target, !!is_router,
true, (ifp != NULL && inc), inc);
@@ -917,7 +947,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;
}
@@ -973,12 +1003,13 @@ static void ndisc_recv_na(struct sk_buff *skb)
goto out;
}
- neigh_update(neigh, lladdr,
+ ndisc_update(dev, 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));
+ (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0),
+ NDISC_NEIGHBOUR_ADVERTISEMENT, &ndopts);
if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
/*
@@ -1023,7 +1054,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;
}
@@ -1037,10 +1068,11 @@ static void ndisc_recv_rs(struct sk_buff *skb)
neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1);
if (neigh) {
- neigh_update(neigh, lladdr, NUD_STALE,
+ ndisc_update(skb->dev, neigh, lladdr, NUD_STALE,
NEIGH_UPDATE_F_WEAK_OVERRIDE|
NEIGH_UPDATE_F_OVERRIDE|
- NEIGH_UPDATE_F_OVERRIDE_ISROUTER);
+ NEIGH_UPDATE_F_OVERRIDE_ISROUTER,
+ NDISC_ROUTER_SOLICITATION, &ndopts);
neigh_release(neigh);
}
out:
@@ -1141,7 +1173,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;
}
@@ -1335,11 +1367,12 @@ skip_linkparms:
goto out;
}
}
- neigh_update(neigh, lladdr, NUD_STALE,
+ ndisc_update(skb->dev, neigh, lladdr, NUD_STALE,
NEIGH_UPDATE_F_WEAK_OVERRIDE|
NEIGH_UPDATE_F_OVERRIDE|
NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
- NEIGH_UPDATE_F_ISROUTER);
+ NEIGH_UPDATE_F_ISROUTER,
+ NDISC_ROUTER_ADVERTISEMENT, &ndopts);
}
if (!ipv6_accept_ra(in6_dev)) {
@@ -1427,7 +1460,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);
}
}
@@ -1465,7 +1499,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) {
@@ -1510,7 +1544,8 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
struct dst_entry *dst;
struct flowi6 fl6;
int rd_len;
- u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
+ u8 ha_buf[MAX_ADDR_LEN], *ha = NULL,
+ ops_data_buf[NDISC_OPS_REDIRECT_DATA_SPACE], *ops_data = NULL;
int oif = l3mdev_fib_oif(dev);
bool ret;
@@ -1569,7 +1604,9 @@ 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_redirect_opt_addr_space(dev, neigh,
+ ops_data_buf,
+ &ops_data);
} else
read_unlock_bh(&neigh->lock);
@@ -1600,7 +1637,7 @@ 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_redirect_addr_option(buff, ha, ops_data);
/*
* build redirect option and copy skb over to the new packet.
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index c6ae6f9..af6f711 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -2200,7 +2200,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;
}
@@ -2235,12 +2235,12 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
* We have finally decided to accept it.
*/
- neigh_update(neigh, lladdr, NUD_STALE,
+ ndisc_update(skb->dev, neigh, lladdr, NUD_STALE,
NEIGH_UPDATE_F_WEAK_OVERRIDE|
NEIGH_UPDATE_F_OVERRIDE|
(on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
- NEIGH_UPDATE_F_ISROUTER))
- );
+ NEIGH_UPDATE_F_ISROUTER)),
+ NDISC_REDIRECT, &ndopts);
nrt = ip6_rt_cache_alloc(rt, &msg->dest, NULL);
if (!nrt)
--
2.8.3
This patch moves the functionality to add a RA PIO prefix generated
address in an own function. This move prepares to add a hook for
adding a second address for a second link-layer address. E.g. short
address for 802.15.4 6LoWPAN.
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]>
Reviewed-by: Stefan Schmidt <[email protected]>
Signed-off-by: Alexander Aring <[email protected]>
---
net/ipv6/addrconf.c | 203 ++++++++++++++++++++++++++++------------------------
1 file changed, 109 insertions(+), 94 deletions(-)
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index beaad49..0ca31e1 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -2333,12 +2333,110 @@ static bool is_addr_mode_generate_stable(struct inet6_dev *idev)
idev->addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
}
+static int addrconf_prefix_rcv_add_addr(struct net *net,
+ struct net_device *dev,
+ const struct prefix_info *pinfo,
+ struct inet6_dev *in6_dev,
+ const struct in6_addr *addr,
+ int addr_type, u32 addr_flags,
+ bool sllao, bool tokenized,
+ __u32 valid_lft, u32 prefered_lft)
+{
+ struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1);
+ int create = 0, update_lft = 0;
+
+ if (!ifp && valid_lft) {
+ int max_addresses = in6_dev->cnf.max_addresses;
+
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+ if (in6_dev->cnf.optimistic_dad &&
+ !net->ipv6.devconf_all->forwarding && sllao)
+ addr_flags |= IFA_F_OPTIMISTIC;
+#endif
+
+ /* Do not allow to create too much of autoconfigured
+ * addresses; this would be too easy way to crash kernel.
+ */
+ if (!max_addresses ||
+ ipv6_count_addresses(in6_dev) < max_addresses)
+ ifp = ipv6_add_addr(in6_dev, addr, NULL,
+ pinfo->prefix_len,
+ addr_type&IPV6_ADDR_SCOPE_MASK,
+ addr_flags, valid_lft,
+ prefered_lft);
+
+ if (IS_ERR_OR_NULL(ifp))
+ return -1;
+
+ update_lft = 0;
+ create = 1;
+ spin_lock_bh(&ifp->lock);
+ ifp->flags |= IFA_F_MANAGETEMPADDR;
+ ifp->cstamp = jiffies;
+ ifp->tokenized = tokenized;
+ spin_unlock_bh(&ifp->lock);
+ addrconf_dad_start(ifp);
+ }
+
+ if (ifp) {
+ u32 flags;
+ unsigned long now;
+ u32 stored_lft;
+
+ /* update lifetime (RFC2462 5.5.3 e) */
+ spin_lock_bh(&ifp->lock);
+ now = jiffies;
+ if (ifp->valid_lft > (now - ifp->tstamp) / HZ)
+ stored_lft = ifp->valid_lft - (now - ifp->tstamp) / HZ;
+ else
+ stored_lft = 0;
+ if (!update_lft && !create && stored_lft) {
+ const u32 minimum_lft = min_t(u32,
+ stored_lft, MIN_VALID_LIFETIME);
+ valid_lft = max(valid_lft, minimum_lft);
+
+ /* RFC4862 Section 5.5.3e:
+ * "Note that the preferred lifetime of the
+ * corresponding address is always reset to
+ * the Preferred Lifetime in the received
+ * Prefix Information option, regardless of
+ * whether the valid lifetime is also reset or
+ * ignored."
+ *
+ * So we should always update prefered_lft here.
+ */
+ update_lft = 1;
+ }
+
+ if (update_lft) {
+ ifp->valid_lft = valid_lft;
+ ifp->prefered_lft = prefered_lft;
+ ifp->tstamp = now;
+ flags = ifp->flags;
+ ifp->flags &= ~IFA_F_DEPRECATED;
+ spin_unlock_bh(&ifp->lock);
+
+ if (!(flags&IFA_F_TENTATIVE))
+ ipv6_ifa_notify(0, ifp);
+ } else
+ spin_unlock_bh(&ifp->lock);
+
+ manage_tempaddrs(in6_dev, ifp, valid_lft, prefered_lft,
+ create, now);
+
+ in6_ifa_put(ifp);
+ addrconf_verify();
+ }
+
+ return 0;
+}
+
void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
{
struct prefix_info *pinfo;
__u32 valid_lft;
__u32 prefered_lft;
- int addr_type;
+ int addr_type, err;
u32 addr_flags = 0;
struct inet6_dev *in6_dev;
struct net *net = dev_net(dev);
@@ -2432,9 +2530,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
/* Try to figure out our local address for this prefix */
if (pinfo->autoconf && in6_dev->cnf.autoconf) {
- struct inet6_ifaddr *ifp;
struct in6_addr addr;
- int create = 0, update_lft = 0;
bool tokenized = false;
if (pinfo->prefix_len == 64) {
@@ -2453,106 +2549,25 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
goto ok;
} else if (ipv6_generate_eui64(addr.s6_addr + 8, dev) &&
ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) {
- in6_dev_put(in6_dev);
- return;
+ goto put;
}
goto ok;
}
net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n",
pinfo->prefix_len);
- in6_dev_put(in6_dev);
- return;
+ goto put;
ok:
-
- ifp = ipv6_get_ifaddr(net, &addr, dev, 1);
-
- if (!ifp && valid_lft) {
- int max_addresses = in6_dev->cnf.max_addresses;
-
-#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
- if (in6_dev->cnf.optimistic_dad &&
- !net->ipv6.devconf_all->forwarding && sllao)
- addr_flags |= IFA_F_OPTIMISTIC;
-#endif
-
- /* Do not allow to create too much of autoconfigured
- * addresses; this would be too easy way to crash kernel.
- */
- if (!max_addresses ||
- ipv6_count_addresses(in6_dev) < max_addresses)
- ifp = ipv6_add_addr(in6_dev, &addr, NULL,
- pinfo->prefix_len,
- addr_type&IPV6_ADDR_SCOPE_MASK,
- addr_flags, valid_lft,
- prefered_lft);
-
- if (IS_ERR_OR_NULL(ifp)) {
- in6_dev_put(in6_dev);
- return;
- }
-
- update_lft = 0;
- create = 1;
- spin_lock_bh(&ifp->lock);
- ifp->flags |= IFA_F_MANAGETEMPADDR;
- ifp->cstamp = jiffies;
- ifp->tokenized = tokenized;
- spin_unlock_bh(&ifp->lock);
- addrconf_dad_start(ifp);
- }
-
- if (ifp) {
- u32 flags;
- unsigned long now;
- u32 stored_lft;
-
- /* update lifetime (RFC2462 5.5.3 e) */
- spin_lock_bh(&ifp->lock);
- now = jiffies;
- if (ifp->valid_lft > (now - ifp->tstamp) / HZ)
- stored_lft = ifp->valid_lft - (now - ifp->tstamp) / HZ;
- else
- stored_lft = 0;
- if (!update_lft && !create && stored_lft) {
- const u32 minimum_lft = min_t(u32,
- stored_lft, MIN_VALID_LIFETIME);
- valid_lft = max(valid_lft, minimum_lft);
-
- /* RFC4862 Section 5.5.3e:
- * "Note that the preferred lifetime of the
- * corresponding address is always reset to
- * the Preferred Lifetime in the received
- * Prefix Information option, regardless of
- * whether the valid lifetime is also reset or
- * ignored."
- *
- * So we should always update prefered_lft here.
- */
- update_lft = 1;
- }
-
- if (update_lft) {
- ifp->valid_lft = valid_lft;
- ifp->prefered_lft = prefered_lft;
- ifp->tstamp = now;
- flags = ifp->flags;
- ifp->flags &= ~IFA_F_DEPRECATED;
- spin_unlock_bh(&ifp->lock);
-
- if (!(flags&IFA_F_TENTATIVE))
- ipv6_ifa_notify(0, ifp);
- } else
- spin_unlock_bh(&ifp->lock);
-
- manage_tempaddrs(in6_dev, ifp, valid_lft, prefered_lft,
- create, now);
-
- in6_ifa_put(ifp);
- addrconf_verify();
- }
+ err = addrconf_prefix_rcv_add_addr(net, dev, pinfo, in6_dev,
+ &addr, addr_type,
+ addr_flags, sllao,
+ tokenized, valid_lft,
+ prefered_lft);
+ if (err)
+ goto put;
}
inet6_prefix_notify(RTM_NEWPREFIX, in6_dev, pinfo);
+put:
in6_dev_put(in6_dev);
}
--
2.8.3
This patch adds __ndisc_fill_addr_option as low-level function for
ndisc_fill_addr_option which doesn't depend on net_device parameter.
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 | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index c245895..a7b9468 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -150,11 +150,10 @@ 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)
{
- 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(data_len, pad);
u8 *opt = skb_put(skb, space);
opt[0] = type;
@@ -172,6 +171,13 @@ static void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data)
memset(opt, 0, space);
}
+static inline void ndisc_fill_addr_option(struct sk_buff *skb, int type,
+ void *data)
+{
+ __ndisc_fill_addr_option(skb, type, data, skb->dev->addr_len,
+ ndisc_addr_option_pad(skb->dev->type));
+}
+
static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
struct nd_opt_hdr *end)
{
--
2.8.3
This patch adds __ndisc_opt_addr_data as low-level function for
ndisc_opt_addr_data which doesn't depend on net_device parameter.
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 | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index 4cee826..c8962ad 100644
--- a/include/net/ndisc.h
+++ b/include/net/ndisc.h
@@ -138,17 +138,23 @@ static inline int ndisc_opt_addr_space(struct net_device *dev)
ndisc_addr_option_pad(dev->type));
}
-static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,
- struct net_device *dev)
+static inline u8 *__ndisc_opt_addr_data(struct nd_opt_hdr *p,
+ unsigned char addr_len, int prepad)
{
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(addr_len, prepad))
return NULL;
return lladdr + prepad;
}
+static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,
+ struct net_device *dev)
+{
+ return __ndisc_opt_addr_data(p, dev->addr_len,
+ ndisc_addr_option_pad(dev->type));
+}
+
static inline u32 ndisc_hashfn(const void *pkey, const struct net_device *dev, __u32 *hash_rnd)
{
const u32 *p32 = pkey;
--
2.8.3
This patch adds __ndisc_opt_addr_space as low-level function for
ndisc_opt_addr_space which doesn't depend on net_device parameter.
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 | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index 2d8edaa..4cee826 100644
--- a/include/net/ndisc.h
+++ b/include/net/ndisc.h
@@ -127,10 +127,15 @@ static inline int ndisc_addr_option_pad(unsigned short type)
}
}
+static inline int __ndisc_opt_addr_space(unsigned char addr_len, int pad)
+{
+ return NDISC_OPT_SPACE(addr_len + pad);
+}
+
static inline int ndisc_opt_addr_space(struct net_device *dev)
{
- return NDISC_OPT_SPACE(dev->addr_len +
- ndisc_addr_option_pad(dev->type));
+ return __ndisc_opt_addr_space(dev->addr_len,
+ ndisc_addr_option_pad(dev->type));
}
static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,
--
2.8.3
Since we use exported function from ipv6 kernel module we don't need to
request the module anymore to have ipv6 functionality.
Acked-by: Hannes Frederic Sowa <[email protected]>
Reviewed-by: Stefan Schmidt <[email protected]>
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 801404c..1c7a42b 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.3
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]>
Acked-by: Hannes Frederic Sowa <[email protected]>
Reviewed-by: Stefan Schmidt <[email protected]>
Signed-off-by: Alexander Aring <[email protected]>
---
include/net/6lowpan.h | 6 ++++++
include/net/addrconf.h | 3 +++
net/6lowpan/core.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
net/ipv6/addrconf.c | 5 +++--
4 files changed, 58 insertions(+), 2 deletions(-)
diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
index 2d9b9d3..5ab4c99 100644
--- a/include/net/6lowpan.h
+++ b/include/net/6lowpan.h
@@ -254,6 +254,12 @@ static inline bool lowpan_fetch_skb(struct sk_buff *skb, void *data,
return false;
}
+static inline bool lowpan_802154_is_valid_src_short_addr(__le16 addr)
+{
+ /* First bit of addr is multicast, reserved or 802.15.4 specific */
+ return !(addr & cpu_to_le16(0x8000));
+}
+
static inline void lowpan_push_hc_data(u8 **hc_ptr, const void *data,
const size_t len)
{
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..801404c 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 (!lowpan_802154_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 47f837a..beaad49 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -2947,8 +2947,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;
@@ -2967,6 +2967,7 @@ static void addrconf_add_linklocal(struct inet6_dev *idev,
in6_ifa_put(ifp);
}
}
+EXPORT_SYMBOL_GPL(addrconf_add_linklocal);
static bool ipv6_reserved_interfaceid(struct in6_addr address)
{
--
2.8.3
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]>
Reviewed-by: Stefan Schmidt <[email protected]>
Signed-off-by: Alexander Aring <[email protected]>
---
include/linux/netdevice.h | 3 +--
include/net/6lowpan.h | 10 ++++++++++
net/ieee802154/6lowpan/core.c | 12 ++++++++++++
3 files changed, 23 insertions(+), 2 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index d101e4d..36e43bd 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1483,8 +1483,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..2d9b9d3 100644
--- a/include/net/6lowpan.h
+++ b/include/net/6lowpan.h
@@ -141,6 +141,16 @@ struct lowpan_dev {
u8 priv[0] __aligned(sizeof(void *));
};
+struct lowpan_802154_neigh {
+ __le16 short_addr;
+};
+
+static inline
+struct lowpan_802154_neigh *lowpan_802154_neigh(void *neigh_priv)
+{
+ return neigh_priv;
+}
+
static inline
struct lowpan_dev *lowpan_dev(const struct net_device *dev)
{
diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c
index 4e2b308..8c004a0 100644
--- a/net/ieee802154/6lowpan/core.c
+++ b/net/ieee802154/6lowpan/core.c
@@ -81,11 +81,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)
@@ -150,6 +160,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 = sizeof(struct lowpan_802154_neigh);
+
ret = lowpan_register_netdevice(ldev, LOWPAN_LLTYPE_IEEE802154);
if (ret < 0) {
dev_put(wdev);
--
2.8.3