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 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/rfc6775
[1] https://tools.ietf.org/html/rfc4944#section-8
[2] https://tools.ietf.org/html/rfc7400#section-3.3
[3] https://tools.ietf.org/html/rfc6775#section-6.1
Cc: David S. Miller <[email protected]>
Cc: Alexey Kuznetsov <[email protected]>
Cc: James Morris <[email protected]>
Cc: Hideaki YOSHIFUJI <[email protected]>
Cc: Patrick McHardy <[email protected]>
changes since "PATCHv2 6lowpan: introduce basic 6lowpan-nd":
- change EXPORT_SYMBOL to EXPORT_SYMBOL_GPL
- rework ndisc_ops to be very low-level (not replacing
full ndisc handling) and add full short address handling
now.
- added comments for ndisc_ops.
- make ndisc_opt_addr_space, ndisc_opt_addr_data and
ndisc_fill_addr_option completely independent from
net_device dev argument. Instead add ndisc_dev helpers
for do it for the normal case.
- remove lowpan_neigh and add a 802.15.4 6lowpan private data
for now only.
- remove empty ndisc_ops inline functions
- add tmp debugfs UAPI to get 6lowpan short address, necessary
for e.g. radvd to put short address as sllao in RA.
Alexander Aring (12):
6lowpan: add private neighbour data
6lowpan: add 802.15.4 short addr slaac
6lowpan: remove ipv6 module request
ndisc: get rid off dev parameter in ndisc_opt_addr_space
ndisc: get rid off dev parameter in ndisc_opt_addr_data
ndisc: get rid off dev parameter in ndisc_fill_addr_option
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 | 10 ++
include/net/addrconf.h | 10 ++
include/net/ndisc.h | 229 +++++++++++++++++++++++++++++++++++++++---
net/6lowpan/6lowpan_i.h | 4 +
net/6lowpan/Makefile | 2 +-
net/6lowpan/core.c | 50 ++++++++-
net/6lowpan/debugfs.c | 35 +++++++
net/6lowpan/iphc.c | 167 ++++++++++++++++++++++++------
net/6lowpan/ndisc.c | 223 ++++++++++++++++++++++++++++++++++++++++
net/ieee802154/6lowpan/core.c | 12 +++
net/ieee802154/6lowpan/tx.c | 113 ++++++++++-----------
net/ipv6/addrconf.c | 204 ++++++++++++++++++++-----------------
net/ipv6/ndisc.c | 178 +++++++++++++++++++-------------
net/ipv6/route.c | 18 ++--
15 files changed, 990 insertions(+), 273 deletions(-)
create mode 100644 net/6lowpan/ndisc.c
--
2.8.2
Hi Hannes,
On 05/27/2016 06:56 PM, Hannes Frederic Sowa wrote:
> On 25.05.2016 07:15, YOSHIFUJI Hideaki wrote:
>>
>>
>> Alexander Aring wrote:
>>> This patch removes the net_device parameter from ndisc_opt_addr_space
>>> function. This can be useful for calling such functionality which
>>> doesn't depends on dev parameter. For current existing functionality
>>> which depends on dev parameter, we introduce ndisc_dev_opt_addr_space to have
>>> an easy replacement for the ndisc_opt_addr_space function.
>>>
>>> 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 | 13 +++++++++----
>>> net/ipv6/ndisc.c | 12 ++++++------
>>> 2 files changed, 15 insertions(+), 10 deletions(-)
>>>
>>> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
>>> index 2d8edaa..dbc8d01 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(struct net_device *dev)
>>> +static inline int ndisc_opt_addr_space(unsigned char addr_len, int pad)
>>> {
>>> - return NDISC_OPT_SPACE(dev->addr_len +
>>> - ndisc_addr_option_pad(dev->type));
>>> + return NDISC_OPT_SPACE(addr_len + pad);
>>> +}
>>> +
>>> +static inline int ndisc_dev_opt_addr_space(const struct net_device *dev)
>>> +{
>>> + return ndisc_opt_addr_space(dev->addr_len,
>>> + ndisc_addr_option_pad(dev->type));
>>> }
>>>
>>
>> I prefer not to change existing functions such as ndisc_opt_addr_space(),
>> and name new function __ndisc_opt_addr_space() etc.
>>
>> Plus, my original thought (when I implement these functions) was to
>> have per-net_device ndisc_opt_addr_spece(), ndisc_opt_adr_data() etc.
>>
>> What do you think of that?
>
> As I understood it 6lowpan devices need to handle both, non-compressed
> and compressed options/addresses. Probably one can make them
> per-interface, but a change to the arguments has still to happen.
>
Yes, we need to handle both addresses at the same time.
I think you mixed some keywords here: "non-compressed/compressed", it
should be extended(sometimes also named long) and short address.
But the short address is an optional address, the extended address is
always available (that's why we handle them as dev->addr).
I suppose Hideaki suggest here to don't rename the function, what I have
now is:
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,
--
which removes a lot the renaming stuff in "ndisc.c". I can still use
"__ndisc_opt_addr_space" as a function which doesn't require net_device
argument. Example:
addr_space = __ndisc_opt_addr_space(IEEE802154_SHORT_ADDR_LEN, 0);
Looks better now.
- Alex
On 25.05.2016 07:15, YOSHIFUJI Hideaki wrote:
>
>
> Alexander Aring wrote:
>> This patch removes the net_device parameter from ndisc_opt_addr_space
>> function. This can be useful for calling such functionality which
>> doesn't depends on dev parameter. For current existing functionality
>> which depends on dev parameter, we introduce ndisc_dev_opt_addr_space to have
>> an easy replacement for the ndisc_opt_addr_space function.
>>
>> 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 | 13 +++++++++----
>> net/ipv6/ndisc.c | 12 ++++++------
>> 2 files changed, 15 insertions(+), 10 deletions(-)
>>
>> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
>> index 2d8edaa..dbc8d01 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(struct net_device *dev)
>> +static inline int ndisc_opt_addr_space(unsigned char addr_len, int pad)
>> {
>> - return NDISC_OPT_SPACE(dev->addr_len +
>> - ndisc_addr_option_pad(dev->type));
>> + return NDISC_OPT_SPACE(addr_len + pad);
>> +}
>> +
>> +static inline int ndisc_dev_opt_addr_space(const struct net_device *dev)
>> +{
>> + return ndisc_opt_addr_space(dev->addr_len,
>> + ndisc_addr_option_pad(dev->type));
>> }
>>
>
> I prefer not to change existing functions such as ndisc_opt_addr_space(),
> and name new function __ndisc_opt_addr_space() etc.
>
> Plus, my original thought (when I implement these functions) was to
> have per-net_device ndisc_opt_addr_spece(), ndisc_opt_adr_data() etc.
>
> What do you think of that?
As I understood it 6lowpan devices need to handle both, non-compressed
and compressed options/addresses. Probably one can make them
per-interface, but a change to the arguments has still to happen.
Alex?
Thanks,
Hannes
Hello.
On 27/05/16 13:03, Alexander Aring wrote:
> Hi,
>
> On 05/27/2016 12:05 PM, Stefan Schmidt wrote:
>> Hello.
>>
>> On 23/05/16 21:22, Alexander Aring wrote:
>>> 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.
>>>
>>> Signed-off-by: Alexander Aring<[email protected]>
>>> ---
>>> net/6lowpan/debugfs.c | 35 +++++++++++++++++++++++++++++++++++
>>> 1 file changed, 35 insertions(+)
>>>
>>> diff --git a/net/6lowpan/debugfs.c b/net/6lowpan/debugfs.c
>>> index acbaa3d..638ae59 100644
>>> --- a/net/6lowpan/debugfs.c
>>> +++ b/net/6lowpan/debugfs.c
>>> @@ -245,6 +245,37 @@ 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;
>>> +
>>> + if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154))
>>> + return 0;
>>> +
>>> + dentry = debugfs_create_file("short_addr", 0444, ldev->iface_debugfs,
>>> + 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 +303,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:
>> Reviewed-by: Stefan Schmidt<[email protected]>
>>
> grml, I changed this patch now to have an 802154/short_addr directory
> inside the root of 6lowpan debugfs entry.
>
> This is because Michael Richardson wrote that other L2 has also
> short_addr (any kind of second L2 address type), see [0].
The question is if we ever will have support for them in Linux. But I
agree that it might be better to have an architecture that would fit for
those cases as well.
> Each of them has their own "constraints" e.g. 802.15.4 2-byte length, or
> sometimes 1-byte. This constraint depends on L2, so I added 802154
> subdir.
OK
regards
Stefan Schmidt
Hello.
On 27/05/16 13:41, Alexander Aring wrote:
> Hi,
>
> On 05/27/2016 11:45 AM, Stefan Schmidt wrote:
>> Hello.
>>
> sorry, again I will change something on this patch which is buggy.
>
>> On 23/05/16 21:22, 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]>
>>> Signed-off-by: Alexander Aring<[email protected]>
>>> ---
>>> net/ipv6/addrconf.c | 191 ++++++++++++++++++++++++++++------------------------
>>> 1 file changed, 102 insertions(+), 89 deletions(-)
>>>
>>> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
>>> index beaad49..393cdbf 100644
>>> --- a/net/ipv6/addrconf.c
>>> +++ b/net/ipv6/addrconf.c
>>> @@ -2333,6 +2333,104 @@ static bool is_addr_mode_generate_stable(struct inet6_dev *idev)
>>> idev->addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
>>> }
>>> +static void addrconf_prefix_rcv_add_addr(struct net *net,
> will change to return int here to indicates errors.
>
>>> + 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)) {
>>> + in6_dev_put(in6_dev);
>>> + return;
> return -1; and remove in6_dev_put stuff.
>
>>> + }
>>> +
>>> + 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();
>>> + }
>>> +}
>>> +
>>> void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
>>> {
>>> struct prefix_info *pinfo;
>>> @@ -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) {
>>> @@ -2464,93 +2560,10 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
>>> return;
>>> 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();
>>> - }
>>> + addrconf_prefix_rcv_add_addr(net, dev, pinfo, in6_dev, &addr,
>>> + addr_type, addr_flags, sllao,
>>> + tokenized, valid_lft,
>>> + prefered_lft);
> check error here and goto in6_dev_put stuff. Oderwise
> inet6_prefix_notify will call on error.
>
>>> }
>>> inet6_prefix_notify(RTM_NEWPREFIX, in6_dev, pinfo);
>>> in6_dev_put(in6_dev);
>> Reviewed-by: Stefan Schmidt<[email protected]>
>>
> I hope this fits to your review by tag, I simple add it then in RFCv2.
You can do the change you described here and still keep my reviewed by.
regards
Stefan Schmidt
Hi,
On 05/27/2016 11:45 AM, Stefan Schmidt wrote:
> Hello.
>
sorry, again I will change something on this patch which is buggy.
> On 23/05/16 21:22, 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]>
>> Signed-off-by: Alexander Aring<[email protected]>
>> ---
>> net/ipv6/addrconf.c | 191 ++++++++++++++++++++++++++++------------------------
>> 1 file changed, 102 insertions(+), 89 deletions(-)
>>
>> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
>> index beaad49..393cdbf 100644
>> --- a/net/ipv6/addrconf.c
>> +++ b/net/ipv6/addrconf.c
>> @@ -2333,6 +2333,104 @@ static bool is_addr_mode_generate_stable(struct inet6_dev *idev)
>> idev->addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
>> }
>> +static void addrconf_prefix_rcv_add_addr(struct net *net,
will change to return int here to indicates errors.
>> + 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)) {
>> + in6_dev_put(in6_dev);
>> + return;
return -1; and remove in6_dev_put stuff.
>> + }
>> +
>> + 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();
>> + }
>> +}
>> +
>> void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
>> {
>> struct prefix_info *pinfo;
>> @@ -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) {
>> @@ -2464,93 +2560,10 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
>> return;
>> 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();
>> - }
>> + addrconf_prefix_rcv_add_addr(net, dev, pinfo, in6_dev, &addr,
>> + addr_type, addr_flags, sllao,
>> + tokenized, valid_lft,
>> + prefered_lft);
check error here and goto in6_dev_put stuff. Oderwise
inet6_prefix_notify will call on error.
>> }
>> inet6_prefix_notify(RTM_NEWPREFIX, in6_dev, pinfo);
>> in6_dev_put(in6_dev);
>
> Reviewed-by: Stefan Schmidt<[email protected]>
>
I hope this fits to your review by tag, I simple add it then in RFCv2.
- Alex
Hi,
On 05/27/2016 12:05 PM, Stefan Schmidt wrote:
> Hello.
>
> On 23/05/16 21:22, Alexander Aring wrote:
>> 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.
>>
>> Signed-off-by: Alexander Aring<[email protected]>
>> ---
>> net/6lowpan/debugfs.c | 35 +++++++++++++++++++++++++++++++++++
>> 1 file changed, 35 insertions(+)
>>
>> diff --git a/net/6lowpan/debugfs.c b/net/6lowpan/debugfs.c
>> index acbaa3d..638ae59 100644
>> --- a/net/6lowpan/debugfs.c
>> +++ b/net/6lowpan/debugfs.c
>> @@ -245,6 +245,37 @@ 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;
>> +
>> + if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154))
>> + return 0;
>> +
>> + dentry = debugfs_create_file("short_addr", 0444, ldev->iface_debugfs,
>> + 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 +303,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:
>
> Reviewed-by: Stefan Schmidt<[email protected]>
>
grml, I changed this patch now to have an 802154/short_addr directory
inside the root of 6lowpan debugfs entry.
This is because Michael Richardson wrote that other L2 has also
short_addr (any kind of second L2 address type), see [0].
Each of them has their own "constraints" e.g. 802.15.4 2-byte length, or
sometimes 1-byte. This constraint depends on L2, so I added 802154
subdir.
Nevertheless it's debugfs and we can still change it when introduce real
UAPI, but it will remind me to introduce it as setting which has some
kind of L2 dependency naming.
- Alex
[0] http://marc.info/?l=linux-netdev&m=146360758922131&w=4
Hello.
On 23/05/16 21:22, 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]>
> 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 | 223 ++++++++++++++++++++++++++++++++++++++++++++++++
> 5 files changed, 243 insertions(+), 8 deletions(-)
> create mode 100644 net/6lowpan/ndisc.c
>
> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
> index c92ebdb..5299b52 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
Later on we want to make this a option that can get selected because not
all networks using 6lowpan are also using the ndisc optimizations for it.
For now this is fine as is though.
> 6lowpan-$(CONFIG_6LOWPAN_DEBUGFS) += debugfs.o
>
> #rfc6282 nhcs
> diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
> index 824d1bc..3de7fd9 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..f67967b
> --- /dev/null
> +++ b/net/6lowpan/ndisc.c
> @@ -0,0 +1,223 @@
> +/* This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
Maybe add your copyright here?
> +#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_addr_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,
The ND_PRINTK macro is used for consistency with the rest of the ndisc
code here?
> + "%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_opts(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_addr_options(dev, nd_opt, ndopts);
> + default:
> + return 0;
> + }
> +}
> +
> +static void lowpan_ndisc_802154_neigh_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_neigh_update(const struct net_device *dev,
> + struct neighbour *n, u32 flags,
> + u8 icmp6_type,
> + const struct ndisc_options *ndopts)
> +{
> + /* react on overrides only. TODO check if this is really right. */
> + if (!lowpan_is_ll(dev, lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154)) ||
> + ~flags & NEIGH_UPDATE_F_OVERRIDE)
> + return;
> +
> + lowpan_ndisc_802154_neigh_update(n, flags, icmp6_type, ndopts);
> +}
> +
> +static int lowpan_ndisc_opt_space(const struct net_device *dev, u8 icmp6_type,
> + const 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));
> +
> + if (ieee802154_is_valid_src_short_addr(n->short_addr)) {
> + memcpy(ha_buf, &n->short_addr,
> + IEEE802154_SHORT_ADDR_LEN);
> + addr_space += ndisc_opt_addr_space(IEEE802154_SHORT_ADDR_LEN, 0);
> + *ha = ha_buf;
> + }
> + break;
> + case NDISC_NEIGHBOUR_ADVERTISEMENT:
> + case NDISC_NEIGHBOUR_SOLICITATION:
> + case NDISC_ROUTER_SOLICITATION:
> + wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr;
> +
> + if (ieee802154_is_valid_src_short_addr(wpan_dev->short_addr))
> + addr_space = ndisc_opt_addr_space(IEEE802154_SHORT_ADDR_LEN, 0);
> + break;
> + default:
> + break;
> + }
> +
> + return addr_space;
> +}
> +
> +static void lowpan_ndisc_fill_opts(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 (ieee802154_is_valid_src_short_addr(wpan_dev->short_addr)) {
> + ieee802154_le16_to_be16(&short_addr,
> + &wpan_dev->short_addr);
> + ndisc_fill_addr_option(skb, 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)
> +{
> + /* 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)) {
> + addrconf_prefix_rcv_add_addr(net, dev, pinfo, in6_dev, addr,
> + addr_type, addr_flags, sllao,
> + tokenized, valid_lft,
> + prefered_lft);
> + }
> +}
> +#endif
> +
> +const struct ndisc_ops lowpan_ndisc_ops = {
> + .is_useropt = lowpan_ndisc_is_useropt,
> +#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
> + .parse_opts = lowpan_ndisc_parse_opts,
> + .neigh_update = lowpan_ndisc_neigh_update,
> + .opt_space = lowpan_ndisc_opt_space,
> + .fill_opts = lowpan_ndisc_fill_opts,
> + .prefix_rcv_add_addr = lowpan_ndisc_prefix_rcv_add_addr,
> +#endif
> +};
Besides the changes YOSHIFUJI request regarding the ndisc naming this
looks good from the 6lowpan side.
Reviewed-by: Stefan Schmidt<[email protected]>
regards
Stefan Schmidt
Hello.
On 23/05/16 21:22, Alexander Aring wrote:
> 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.
>
> Signed-off-by: Alexander Aring<[email protected]>
> ---
> net/6lowpan/debugfs.c | 35 +++++++++++++++++++++++++++++++++++
> 1 file changed, 35 insertions(+)
>
> diff --git a/net/6lowpan/debugfs.c b/net/6lowpan/debugfs.c
> index acbaa3d..638ae59 100644
> --- a/net/6lowpan/debugfs.c
> +++ b/net/6lowpan/debugfs.c
> @@ -245,6 +245,37 @@ 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;
> +
> + if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154))
> + return 0;
> +
> + dentry = debugfs_create_file("short_addr", 0444, ldev->iface_debugfs,
> + 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 +303,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:
Reviewed-by: Stefan Schmidt<[email protected]>
regards
Stefan Schmidt
Hello.
On 23/05/16 21:22, 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..f5d2230 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);
>
> +void 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 e4a711e..c92ebdb 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);
> +
> /*
> * Return the padding between the option length and the start of the
> * link addr. Currently only IP-over-InfiniBand needs this, although
> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index 4506cac..aeea54e 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 void 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)
> +void 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 void addrconf_prefix_rcv_add_addr(struct net *net,
> addrconf_verify();
> }
> }
> +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 99fd53c..d30f241 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_dev_fill_addr_option(struct sk_buff *skb, int type,
> const struct net_device *dev)
Reviewed-by: Stefan Schmidt<[email protected]>
regards
Stefan Schmidt
Hello.
On 23/05/16 21:22, 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]>
> Signed-off-by: Alexander Aring<[email protected]>
> ---
> net/ipv6/addrconf.c | 191 ++++++++++++++++++++++++++++------------------------
> 1 file changed, 102 insertions(+), 89 deletions(-)
>
> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index beaad49..393cdbf 100644
> --- a/net/ipv6/addrconf.c
> +++ b/net/ipv6/addrconf.c
> @@ -2333,6 +2333,104 @@ static bool is_addr_mode_generate_stable(struct inet6_dev *idev)
> idev->addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
> }
>
> +static void 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)) {
> + 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();
> + }
> +}
> +
> void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
> {
> struct prefix_info *pinfo;
> @@ -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) {
> @@ -2464,93 +2560,10 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
> return;
>
> 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();
> - }
> + addrconf_prefix_rcv_add_addr(net, dev, pinfo, in6_dev, &addr,
> + addr_type, addr_flags, sllao,
> + tokenized, valid_lft,
> + prefered_lft);
> }
> inet6_prefix_notify(RTM_NEWPREFIX, in6_dev, pinfo);
> in6_dev_put(in6_dev);
Reviewed-by: Stefan Schmidt<[email protected]>
regards
Stefan Schmidt
Hi,
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]>
> ---
> include/linux/netdevice.h | 5 ++
> include/net/ndisc.h | 176 +++++++++++++++++++++++++++++++++++++++++++++-
> net/ipv6/addrconf.c | 9 ++-
> net/ipv6/ndisc.c | 119 +++++++++++++++++++++----------
> net/ipv6/route.c | 14 ++--
> 5 files changed, 275 insertions(+), 48 deletions(-)
>
> @@ -205,6 +376,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_neigh_update(const struct net_device *dev, struct neighbour *neigh,
> + const u8 *lladdr, u8 new, u32 flags, u8 icmp6_type,
> + struct ndisc_options *ndopts);
>
I prefer ndisc_update().
> /*
> * IGMP
> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index 393cdbf..4506cac 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);
> @@ -2551,6 +2551,8 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
> ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) {
> in6_dev_put(in6_dev);
> return;
> + } else {
> + dev_addr_generated = true;
> }
> goto ok;
> }
> @@ -2564,6 +2566,11 @@ ok:
> addr_type, addr_flags, sllao,
> tokenized, valid_lft,
> prefered_lft);
> + 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);
> in6_dev_put(in6_dev);
> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
> index d794d64..99fd53c 100644
> --- a/net/ipv6/ndisc.c
> +++ b/net/ipv6/ndisc.c
> @@ -191,24 +191,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 +227,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_opts(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 +255,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 +272,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;
> }
> @@ -514,8 +521,11 @@ void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
>
> if (!dev->addr_len)
> inc_opt = 0;
> - if (inc_opt)
> + if (inc_opt) {
> optlen += ndisc_dev_opt_addr_space(dev);
> + optlen += ndisc_ops_opt_space(dev,
> + NDISC_NEIGHBOUR_ADVERTISEMENT);
> + }
>
I think we could call per-device space handler inside
ndisc_dev_opt_addr_space(dev) (or ndisc_opt_addr_space,
in my opinion as I mentioned in 04/12) here by having
extra arugment for it.
> skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
> if (!skb)
> @@ -532,8 +542,10 @@ void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
> .target = *solicited_addr,
> };
>
> - if (inc_opt)
> + if (inc_opt) {
> ndisc_dev_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR, dev);
> + ndisc_ops_fill_opts(dev, skb, NDISC_NEIGHBOUR_ADVERTISEMENT);
> + }
>
likewise. And ndisc_ops_fill_addr_option, maybe?
>
> ndisc_send_skb(skb, daddr, src_addr);
> @@ -578,8 +590,11 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
>
> if (ipv6_addr_any(saddr))
> inc_opt = false;
> - if (inc_opt)
> + if (inc_opt) {
> optlen += ndisc_dev_opt_addr_space(dev);
> + optlen += ndisc_ops_opt_space(dev,
> + NDISC_NEIGHBOUR_SOLICITATION);
> + }
>
> skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
> if (!skb)
> @@ -593,8 +608,10 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
> .target = *solicit,
> };
>
> - if (inc_opt)
> + if (inc_opt) {
> ndisc_dev_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR, dev);
> + ndisc_ops_fill_opts(dev, skb, NDISC_NEIGHBOUR_SOLICITATION);
> + }
>
> ndisc_send_skb(skb, daddr, saddr);
> }
> @@ -629,8 +646,10 @@ void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
> }
> }
> #endif
> - if (send_sllao)
> + if (send_sllao) {
> optlen += ndisc_dev_opt_addr_space(dev);
> + optlen += ndisc_ops_opt_space(dev, NDISC_ROUTER_SOLICITATION);
> + }
>
> skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
> if (!skb)
> @@ -643,8 +662,10 @@ void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
> },
> };
>
> - if (send_sllao)
> + if (send_sllao) {
> ndisc_dev_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR, dev);
> + ndisc_ops_fill_opts(dev, skb, NDISC_ROUTER_SOLICITATION);
> + }
>
> ndisc_send_skb(skb, daddr, saddr);
> }
> @@ -705,6 +726,15 @@ static int pndisc_is_router(const void *pkey,
> return ret;
> }
>
> +void ndisc_neigh_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_neigh_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);
> @@ -741,7 +771,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;
> }
> @@ -860,9 +890,10 @@ have_ifp:
> neigh = __neigh_lookup(&nd_tbl, saddr, dev,
> !inc || lladdr || !dev->addr_len);
> if (neigh)
> - neigh_update(neigh, lladdr, NUD_STALE,
> - NEIGH_UPDATE_F_WEAK_OVERRIDE|
> - NEIGH_UPDATE_F_OVERRIDE);
> + ndisc_neigh_update(dev, neigh, lladdr, NUD_STALE,
> + NEIGH_UPDATE_F_WEAK_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);
> @@ -915,7 +946,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;
> }
> @@ -972,12 +1003,13 @@ static void ndisc_recv_na(struct sk_buff *skb)
> goto out;
> }
>
> - neigh_update(neigh, lladdr,
> - msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
> - NEIGH_UPDATE_F_WEAK_OVERRIDE|
> - (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0)|
> - NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
> - (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0));
> + ndisc_neigh_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),
> + NDISC_NEIGHBOUR_ADVERTISEMENT, &ndopts);
>
> if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
> /*
> @@ -1022,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;
> }
> @@ -1036,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,
> - NEIGH_UPDATE_F_WEAK_OVERRIDE|
> - NEIGH_UPDATE_F_OVERRIDE|
> - NEIGH_UPDATE_F_OVERRIDE_ISROUTER);
> + ndisc_neigh_update(skb->dev, neigh, lladdr, NUD_STALE,
> + NEIGH_UPDATE_F_WEAK_OVERRIDE|
> + NEIGH_UPDATE_F_OVERRIDE|
> + NEIGH_UPDATE_F_OVERRIDE_ISROUTER,
> + NDISC_ROUTER_SOLICITATION, &ndopts);
> neigh_release(neigh);
> }
> out:
> @@ -1140,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;
> }
> @@ -1334,11 +1367,12 @@ skip_linkparms:
> goto out;
> }
> }
> - neigh_update(neigh, lladdr, NUD_STALE,
> - NEIGH_UPDATE_F_WEAK_OVERRIDE|
> - NEIGH_UPDATE_F_OVERRIDE|
> - NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
> - NEIGH_UPDATE_F_ISROUTER);
> + ndisc_neigh_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,
> + NDISC_ROUTER_ADVERTISEMENT, &ndopts);
> }
>
> if (!ipv6_accept_ra(in6_dev)) {
> @@ -1426,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);
> }
> }
> @@ -1464,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) {
> @@ -1509,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,6 +1605,9 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
> read_unlock_bh(&neigh->lock);
> ha = ha_buf;
> optlen += ndisc_dev_opt_addr_space(dev);
> + optlen += ndisc_ops_redirect_opt_space(dev, neigh,
> + ops_data_buf,
> + &ops_data);
> } else
> read_unlock_bh(&neigh->lock);
>
> @@ -1598,10 +1637,12 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
> * include target_address option
> */
>
> - if (ha)
> + if (ha) {
> ndisc_fill_addr_option(buff, ND_OPT_TARGET_LL_ADDR, ha,
> dev->addr_len,
> ndisc_addr_option_pad(dev->type));
> + ndisc_ops_redirect_fill_addr_option(dev, buff, 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 d662cd8..0800a776 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,
> - NEIGH_UPDATE_F_WEAK_OVERRIDE|
> - NEIGH_UPDATE_F_OVERRIDE|
> - (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
> - NEIGH_UPDATE_F_ISROUTER))
> - );
> + ndisc_neigh_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)),
> + NDISC_REDIRECT, &ndopts);
>
> nrt = ip6_rt_cache_alloc(rt, &msg->dest, NULL);
> if (!nrt)
>
--
Hideaki Yoshifuji <[email protected]>
Technical Division, MIRACLE LINUX CORPORATION
Alexander Aring wrote:
> This patch removes the net_device parameter from ndisc_opt_addr_space
> function. This can be useful for calling such functionality which
> doesn't depends on dev parameter. For current existing functionality
> which depends on dev parameter, we introduce ndisc_dev_opt_addr_space to have
> an easy replacement for the ndisc_opt_addr_space function.
>
> 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 | 13 +++++++++----
> net/ipv6/ndisc.c | 12 ++++++------
> 2 files changed, 15 insertions(+), 10 deletions(-)
>
> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
> index 2d8edaa..dbc8d01 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(struct net_device *dev)
> +static inline int ndisc_opt_addr_space(unsigned char addr_len, int pad)
> {
> - return NDISC_OPT_SPACE(dev->addr_len +
> - ndisc_addr_option_pad(dev->type));
> + return NDISC_OPT_SPACE(addr_len + pad);
> +}
> +
> +static inline int ndisc_dev_opt_addr_space(const struct net_device *dev)
> +{
> + return ndisc_opt_addr_space(dev->addr_len,
> + ndisc_addr_option_pad(dev->type));
> }
>
I prefer not to change existing functions such as ndisc_opt_addr_space(),
and name new function __ndisc_opt_addr_space() etc.
Plus, my original thought (when I implement these functions) was to
have per-net_device ndisc_opt_addr_spece(), ndisc_opt_adr_data() etc.
What do you think of that?
> static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,
> @@ -139,7 +144,7 @@ static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,
> u8 *lladdr = (u8 *)(p + 1);
> int lladdrlen = p->nd_opt_len << 3;
> int prepad = ndisc_addr_option_pad(dev->type);
> - if (lladdrlen != ndisc_opt_addr_space(dev))
> + if (lladdrlen != ndisc_opt_addr_space(dev->addr_len, prepad))
> return NULL;
> return lladdr + prepad;
> }
> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
> index c245895..2241f06 100644
> --- a/net/ipv6/ndisc.c
> +++ b/net/ipv6/ndisc.c
> @@ -152,9 +152,9 @@ EXPORT_SYMBOL_GPL(nd_tbl);
>
> static void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data)
> {
> - int pad = ndisc_addr_option_pad(skb->dev->type);
> + 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;
> @@ -509,7 +509,7 @@ void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
> if (!dev->addr_len)
> inc_opt = 0;
> if (inc_opt)
> - optlen += ndisc_opt_addr_space(dev);
> + optlen += ndisc_dev_opt_addr_space(dev);
>
> skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
> if (!skb)
> @@ -574,7 +574,7 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
> if (ipv6_addr_any(saddr))
> inc_opt = false;
> if (inc_opt)
> - optlen += ndisc_opt_addr_space(dev);
> + optlen += ndisc_dev_opt_addr_space(dev);
>
> skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
> if (!skb)
> @@ -626,7 +626,7 @@ void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
> }
> #endif
> if (send_sllao)
> - optlen += ndisc_opt_addr_space(dev);
> + optlen += ndisc_dev_opt_addr_space(dev);
>
> skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
> if (!skb)
> @@ -1563,7 +1563,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
> memcpy(ha_buf, neigh->ha, dev->addr_len);
> read_unlock_bh(&neigh->lock);
> ha = ha_buf;
> - optlen += ndisc_opt_addr_space(dev);
> + optlen += ndisc_dev_opt_addr_space(dev);
> } else
> read_unlock_bh(&neigh->lock);
>
>
--
Hideaki Yoshifuji <[email protected]>
Technical Division, MIRACLE LINUX CORPORATION
Hi.
Alexander Aring wrote:
> 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.
Thank you for your work! It looks much better now.
Some comments will follow.
--
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..4646be4 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 &&
+ ieee802154_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 (ieee802154_is_valid_src_short_addr(wpan_dev->short_addr)) {
+ info->saddr.mode = IEEE802154_ADDR_SHORT;
+ info->saddr.short_addr = wpan_dev->short_addr;
+ } else {
+ info->saddr.mode = IEEE802154_ADDR_LONG;
+ info->saddr.extended_addr = wpan_dev->extended_addr;
+ }
+ } else {
+ info->saddr.mode = IEEE802154_ADDR_LONG;
+ ieee802154_be64_to_le64(&info->saddr.extended_addr, saddr);
+ }
return 0;
}
@@ -209,47 +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.2
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.
Signed-off-by: Alexander Aring <[email protected]>
---
net/6lowpan/debugfs.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/net/6lowpan/debugfs.c b/net/6lowpan/debugfs.c
index acbaa3d..638ae59 100644
--- a/net/6lowpan/debugfs.c
+++ b/net/6lowpan/debugfs.c
@@ -245,6 +245,37 @@ 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;
+
+ if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154))
+ return 0;
+
+ dentry = debugfs_create_file("short_addr", 0444, ldev->iface_debugfs,
+ 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 +303,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.2
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]>
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 | 223 ++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 243 insertions(+), 8 deletions(-)
create mode 100644 net/6lowpan/ndisc.c
diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index c92ebdb..5299b52 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 824d1bc..3de7fd9 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..f67967b
--- /dev/null
+++ b/net/6lowpan/ndisc.c
@@ -0,0 +1,223 @@
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <net/6lowpan.h>
+#include <net/addrconf.h>
+#include <net/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_addr_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_opts(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_addr_options(dev, nd_opt, ndopts);
+ default:
+ return 0;
+ }
+}
+
+static void lowpan_ndisc_802154_neigh_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_neigh_update(const struct net_device *dev,
+ struct neighbour *n, u32 flags,
+ u8 icmp6_type,
+ const struct ndisc_options *ndopts)
+{
+ /* react on overrides only. TODO check if this is really right. */
+ if (!lowpan_is_ll(dev, lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154)) ||
+ ~flags & NEIGH_UPDATE_F_OVERRIDE)
+ return;
+
+ lowpan_ndisc_802154_neigh_update(n, flags, icmp6_type, ndopts);
+}
+
+static int lowpan_ndisc_opt_space(const struct net_device *dev, u8 icmp6_type,
+ const 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));
+
+ if (ieee802154_is_valid_src_short_addr(n->short_addr)) {
+ memcpy(ha_buf, &n->short_addr,
+ IEEE802154_SHORT_ADDR_LEN);
+ addr_space += ndisc_opt_addr_space(IEEE802154_SHORT_ADDR_LEN, 0);
+ *ha = ha_buf;
+ }
+ break;
+ case NDISC_NEIGHBOUR_ADVERTISEMENT:
+ case NDISC_NEIGHBOUR_SOLICITATION:
+ case NDISC_ROUTER_SOLICITATION:
+ wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr;
+
+ if (ieee802154_is_valid_src_short_addr(wpan_dev->short_addr))
+ addr_space = ndisc_opt_addr_space(IEEE802154_SHORT_ADDR_LEN, 0);
+ break;
+ default:
+ break;
+ }
+
+ return addr_space;
+}
+
+static void lowpan_ndisc_fill_opts(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 (ieee802154_is_valid_src_short_addr(wpan_dev->short_addr)) {
+ ieee802154_le16_to_be16(&short_addr,
+ &wpan_dev->short_addr);
+ ndisc_fill_addr_option(skb, 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)
+{
+ /* 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)) {
+ addrconf_prefix_rcv_add_addr(net, dev, pinfo, in6_dev, addr,
+ addr_type, addr_flags, sllao,
+ tokenized, valid_lft,
+ prefered_lft);
+ }
+}
+#endif
+
+const struct ndisc_ops lowpan_ndisc_ops = {
+ .is_useropt = lowpan_ndisc_is_useropt,
+#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
+ .parse_opts = lowpan_ndisc_parse_opts,
+ .neigh_update = lowpan_ndisc_neigh_update,
+ .opt_space = lowpan_ndisc_opt_space,
+ .fill_opts = lowpan_ndisc_fill_opts,
+ .prefix_rcv_add_addr = lowpan_ndisc_prefix_rcv_add_addr,
+#endif
+};
--
2.8.2
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..f5d2230 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);
+void 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 e4a711e..c92ebdb 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);
+
/*
* Return the padding between the option length and the start of the
* link addr. Currently only IP-over-InfiniBand needs this, although
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 4506cac..aeea54e 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 void 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)
+void 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 void addrconf_prefix_rcv_add_addr(struct net *net,
addrconf_verify();
}
}
+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 99fd53c..d30f241 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_dev_fill_addr_option(struct sk_buff *skb, int type,
const struct net_device *dev)
--
2.8.2
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 | 176 +++++++++++++++++++++++++++++++++++++++++++++-
net/ipv6/addrconf.c | 9 ++-
net/ipv6/ndisc.c | 119 +++++++++++++++++++++----------
net/ipv6/route.c | 14 ++--
5 files changed, 275 insertions(+), 48 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 42737c4..328c150 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1457,6 +1457,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 7fa74b0..e4a711e 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,7 +111,8 @@ struct ndisc_options {
#define NDISC_OPT_SPACE(len) (((len)+2+7)&~7)
-struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
+struct ndisc_options *ndisc_parse_options(const struct net_device *dev,
+ u8 *opt, int opt_len,
struct ndisc_options *ndopts);
/*
@@ -183,6 +185,175 @@ static inline struct neighbour *__ipv6_neigh_lookup(struct net_device *dev, cons
return n;
}
+#if IS_ENABLED(CONFIG_IPV6)
+#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.
+ *
+ * void (*parse_opts)(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.
+ *
+ * int (*neigh_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_space)(const struct net_device *dev, u8 icmp6_type,
+ * const 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. In case
+ * of NDISC_REDIRECT the neigh->lock spinlock will be held.
+ *
+ * int (*fill_opts)(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.
+ *
+ * int (*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_opts)(const struct net_device *dev,
+ struct nd_opt_hdr *nd_opt,
+ struct ndisc_options *ndopts);
+ void (*neigh_update)(const struct net_device *dev,
+ struct neighbour *n, u32 flags, u8 icmp6_type,
+ const struct ndisc_options *ndopts);
+ int (*opt_space)(const struct net_device *dev, u8 icmp6_type,
+ const struct neighbour *neigh, u8 *ha_buf,
+ u8 **ha);
+ void (*fill_opts)(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);
+};
+
+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_opts(const struct net_device *dev,
+ struct nd_opt_hdr *nd_opt,
+ struct ndisc_options *ndopts)
+{
+ if (dev->ndisc_ops && dev->ndisc_ops->parse_opts)
+ return dev->ndisc_ops->parse_opts(dev, nd_opt, ndopts);
+ else
+ return 0;
+}
+
+static inline void ndisc_ops_neigh_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->neigh_update)
+ dev->ndisc_ops->neigh_update(dev, n, flags, icmp6_type,
+ ndopts);
+}
+
+static inline int ndisc_ops_opt_space(const struct net_device *dev,
+ u8 icmp6_type)
+{
+ if (dev->ndisc_ops && dev->ndisc_ops->opt_space)
+ return dev->ndisc_ops->opt_space(dev, icmp6_type, NULL, NULL,
+ NULL);
+ else
+ return 0;
+}
+
+static inline int ndisc_ops_redirect_opt_space(const struct net_device *dev,
+ const struct neighbour *neigh,
+ u8 *ha_buf, u8 **ha)
+{
+ if (dev->ndisc_ops && dev->ndisc_ops->opt_space)
+ return dev->ndisc_ops->opt_space(dev, NDISC_REDIRECT,
+ neigh, ha_buf, ha);
+ else
+ return 0;
+}
+
+static inline void ndisc_ops_fill_opts(const struct net_device *dev,
+ struct sk_buff *skb, u8 icmp6_type)
+{
+ if (dev->ndisc_ops && dev->ndisc_ops->fill_opts)
+ dev->ndisc_ops->fill_opts(dev, skb, icmp6_type, NULL);
+}
+
+static inline void ndisc_ops_redirect_fill_addr_option(const struct net_device *dev,
+ struct sk_buff *skb,
+ const u8 *ha)
+{
+ if (dev->ndisc_ops && dev->ndisc_ops->fill_opts)
+ dev->ndisc_ops->fill_opts(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
+
int ndisc_init(void);
int ndisc_late_init(void);
@@ -205,6 +376,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_neigh_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 393cdbf..4506cac 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);
@@ -2551,6 +2551,8 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) {
in6_dev_put(in6_dev);
return;
+ } else {
+ dev_addr_generated = true;
}
goto ok;
}
@@ -2564,6 +2566,11 @@ ok:
addr_type, addr_flags, sllao,
tokenized, valid_lft,
prefered_lft);
+ 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);
in6_dev_put(in6_dev);
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index d794d64..99fd53c 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -191,24 +191,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 +227,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_opts(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 +255,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 +272,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;
}
@@ -514,8 +521,11 @@ void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
if (!dev->addr_len)
inc_opt = 0;
- if (inc_opt)
+ if (inc_opt) {
optlen += ndisc_dev_opt_addr_space(dev);
+ optlen += ndisc_ops_opt_space(dev,
+ NDISC_NEIGHBOUR_ADVERTISEMENT);
+ }
skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
if (!skb)
@@ -532,8 +542,10 @@ void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
.target = *solicited_addr,
};
- if (inc_opt)
+ if (inc_opt) {
ndisc_dev_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR, dev);
+ ndisc_ops_fill_opts(dev, skb, NDISC_NEIGHBOUR_ADVERTISEMENT);
+ }
ndisc_send_skb(skb, daddr, src_addr);
@@ -578,8 +590,11 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
if (ipv6_addr_any(saddr))
inc_opt = false;
- if (inc_opt)
+ if (inc_opt) {
optlen += ndisc_dev_opt_addr_space(dev);
+ optlen += ndisc_ops_opt_space(dev,
+ NDISC_NEIGHBOUR_SOLICITATION);
+ }
skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
if (!skb)
@@ -593,8 +608,10 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
.target = *solicit,
};
- if (inc_opt)
+ if (inc_opt) {
ndisc_dev_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR, dev);
+ ndisc_ops_fill_opts(dev, skb, NDISC_NEIGHBOUR_SOLICITATION);
+ }
ndisc_send_skb(skb, daddr, saddr);
}
@@ -629,8 +646,10 @@ void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
}
}
#endif
- if (send_sllao)
+ if (send_sllao) {
optlen += ndisc_dev_opt_addr_space(dev);
+ optlen += ndisc_ops_opt_space(dev, NDISC_ROUTER_SOLICITATION);
+ }
skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
if (!skb)
@@ -643,8 +662,10 @@ void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
},
};
- if (send_sllao)
+ if (send_sllao) {
ndisc_dev_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR, dev);
+ ndisc_ops_fill_opts(dev, skb, NDISC_ROUTER_SOLICITATION);
+ }
ndisc_send_skb(skb, daddr, saddr);
}
@@ -705,6 +726,15 @@ static int pndisc_is_router(const void *pkey,
return ret;
}
+void ndisc_neigh_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_neigh_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);
@@ -741,7 +771,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;
}
@@ -860,9 +890,10 @@ have_ifp:
neigh = __neigh_lookup(&nd_tbl, saddr, dev,
!inc || lladdr || !dev->addr_len);
if (neigh)
- neigh_update(neigh, lladdr, NUD_STALE,
- NEIGH_UPDATE_F_WEAK_OVERRIDE|
- NEIGH_UPDATE_F_OVERRIDE);
+ ndisc_neigh_update(dev, neigh, lladdr, NUD_STALE,
+ NEIGH_UPDATE_F_WEAK_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);
@@ -915,7 +946,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;
}
@@ -972,12 +1003,13 @@ static void ndisc_recv_na(struct sk_buff *skb)
goto out;
}
- neigh_update(neigh, lladdr,
- msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
- NEIGH_UPDATE_F_WEAK_OVERRIDE|
- (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0)|
- NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
- (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0));
+ ndisc_neigh_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),
+ NDISC_NEIGHBOUR_ADVERTISEMENT, &ndopts);
if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
/*
@@ -1022,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;
}
@@ -1036,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,
- NEIGH_UPDATE_F_WEAK_OVERRIDE|
- NEIGH_UPDATE_F_OVERRIDE|
- NEIGH_UPDATE_F_OVERRIDE_ISROUTER);
+ ndisc_neigh_update(skb->dev, neigh, lladdr, NUD_STALE,
+ NEIGH_UPDATE_F_WEAK_OVERRIDE|
+ NEIGH_UPDATE_F_OVERRIDE|
+ NEIGH_UPDATE_F_OVERRIDE_ISROUTER,
+ NDISC_ROUTER_SOLICITATION, &ndopts);
neigh_release(neigh);
}
out:
@@ -1140,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;
}
@@ -1334,11 +1367,12 @@ skip_linkparms:
goto out;
}
}
- neigh_update(neigh, lladdr, NUD_STALE,
- NEIGH_UPDATE_F_WEAK_OVERRIDE|
- NEIGH_UPDATE_F_OVERRIDE|
- NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
- NEIGH_UPDATE_F_ISROUTER);
+ ndisc_neigh_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,
+ NDISC_ROUTER_ADVERTISEMENT, &ndopts);
}
if (!ipv6_accept_ra(in6_dev)) {
@@ -1426,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);
}
}
@@ -1464,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) {
@@ -1509,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,6 +1605,9 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
read_unlock_bh(&neigh->lock);
ha = ha_buf;
optlen += ndisc_dev_opt_addr_space(dev);
+ optlen += ndisc_ops_redirect_opt_space(dev, neigh,
+ ops_data_buf,
+ &ops_data);
} else
read_unlock_bh(&neigh->lock);
@@ -1598,10 +1637,12 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
* include target_address option
*/
- if (ha)
+ if (ha) {
ndisc_fill_addr_option(buff, ND_OPT_TARGET_LL_ADDR, ha,
dev->addr_len,
ndisc_addr_option_pad(dev->type));
+ ndisc_ops_redirect_fill_addr_option(dev, buff, 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 d662cd8..0800a776 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,
- NEIGH_UPDATE_F_WEAK_OVERRIDE|
- NEIGH_UPDATE_F_OVERRIDE|
- (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
- NEIGH_UPDATE_F_ISROUTER))
- );
+ ndisc_neigh_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)),
+ NDISC_REDIRECT, &ndopts);
nrt = ip6_rt_cache_alloc(rt, &msg->dest, NULL);
if (!nrt)
--
2.8.2
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]>
Signed-off-by: Alexander Aring <[email protected]>
---
net/ipv6/addrconf.c | 191 ++++++++++++++++++++++++++++------------------------
1 file changed, 102 insertions(+), 89 deletions(-)
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index beaad49..393cdbf 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -2333,6 +2333,104 @@ static bool is_addr_mode_generate_stable(struct inet6_dev *idev)
idev->addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
}
+static void 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)) {
+ 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();
+ }
+}
+
void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
{
struct prefix_info *pinfo;
@@ -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) {
@@ -2464,93 +2560,10 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
return;
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();
- }
+ addrconf_prefix_rcv_add_addr(net, dev, pinfo, in6_dev, &addr,
+ addr_type, addr_flags, sllao,
+ tokenized, valid_lft,
+ prefered_lft);
}
inet6_prefix_notify(RTM_NEWPREFIX, in6_dev, pinfo);
in6_dev_put(in6_dev);
--
2.8.2
This patch removes the net_device parameter from ndisc_fill_addr_option
function. This can be useful for calling such functionality which
doesn't depends on dev parameter. For current existing functionality
which depends on dev parameter, we introduce ndisc_dev_fill_addr_option to have
an easy replacement for the ndisc_fill_addr_option function.
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 | 25 +++++++++++++++----------
1 file changed, 15 insertions(+), 10 deletions(-)
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index d6f28d5..d794d64 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -150,10 +150,9 @@ 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(data_len, pad);
u8 *opt = skb_put(skb, space);
@@ -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_dev_fill_addr_option(struct sk_buff *skb, int type,
+ const struct net_device *dev)
+{
+ ndisc_fill_addr_option(skb, type, dev->dev_addr, dev->addr_len,
+ ndisc_addr_option_pad(dev->type));
+}
+
static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
struct nd_opt_hdr *end)
{
@@ -527,8 +533,7 @@ void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
};
if (inc_opt)
- ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR,
- dev->dev_addr);
+ ndisc_dev_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR, dev);
ndisc_send_skb(skb, daddr, src_addr);
@@ -589,8 +594,7 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
};
if (inc_opt)
- ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR,
- dev->dev_addr);
+ ndisc_dev_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR, dev);
ndisc_send_skb(skb, daddr, saddr);
}
@@ -640,8 +644,7 @@ void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
};
if (send_sllao)
- ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR,
- dev->dev_addr);
+ ndisc_dev_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR, dev);
ndisc_send_skb(skb, daddr, saddr);
}
@@ -1596,7 +1599,9 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
*/
if (ha)
- ndisc_fill_addr_option(buff, ND_OPT_TARGET_LL_ADDR, ha);
+ ndisc_fill_addr_option(buff, ND_OPT_TARGET_LL_ADDR, ha,
+ dev->addr_len,
+ ndisc_addr_option_pad(dev->type));
/*
* build redirect option and copy skb over to the new packet.
--
2.8.2
This patch removes the net_device parameter from ndisc_opt_addr_data
function. This can be useful for calling such functionality which
doesn't depends on dev parameter. For current existing functionality
which depends on dev parameter, we introduce ndisc_dev_opt_addr_data to have
an easy replacement for the ndisc_opt_addr_data function.
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 | 12 +++++++++---
net/ipv6/ndisc.c | 14 ++++++++------
net/ipv6/route.c | 4 ++--
3 files changed, 19 insertions(+), 11 deletions(-)
diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index dbc8d01..7fa74b0 100644
--- a/include/net/ndisc.h
+++ b/include/net/ndisc.h
@@ -139,16 +139,22 @@ static inline int ndisc_dev_opt_addr_space(const struct net_device *dev)
}
static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,
- struct net_device *dev)
+ 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->addr_len, prepad))
+ if (lladdrlen != ndisc_opt_addr_space(addr_len, prepad))
return NULL;
return lladdr + prepad;
}
+static inline u8 *ndisc_dev_opt_addr_data(struct nd_opt_hdr *p,
+ const 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;
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 2241f06..d6f28d5 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -744,7 +744,8 @@ static void ndisc_recv_ns(struct sk_buff *skb)
}
if (ndopts.nd_opts_src_lladdr) {
- lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
+ lladdr = ndisc_dev_opt_addr_data(ndopts.nd_opts_src_lladdr,
+ dev);
if (!lladdr) {
ND_PRINTK(2, warn,
"NS: invalid link-layer address length\n");
@@ -916,7 +917,8 @@ static void ndisc_recv_na(struct sk_buff *skb)
return;
}
if (ndopts.nd_opts_tgt_lladdr) {
- lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);
+ lladdr = ndisc_dev_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
+ dev);
if (!lladdr) {
ND_PRINTK(2, warn,
"NA: invalid link-layer address length\n");
@@ -1023,8 +1025,8 @@ static void ndisc_recv_rs(struct sk_buff *skb)
}
if (ndopts.nd_opts_src_lladdr) {
- lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
- skb->dev);
+ lladdr = ndisc_dev_opt_addr_data(ndopts.nd_opts_src_lladdr,
+ skb->dev);
if (!lladdr)
goto out;
}
@@ -1321,8 +1323,8 @@ skip_linkparms:
if (neigh) {
u8 *lladdr = NULL;
if (ndopts.nd_opts_src_lladdr) {
- lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
- skb->dev);
+ lladdr = ndisc_dev_opt_addr_data(ndopts.nd_opts_src_lladdr,
+ skb->dev);
if (!lladdr) {
ND_PRINTK(2, warn,
"RA: invalid link-layer address length\n");
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 969913d..d662cd8 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -2207,8 +2207,8 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
lladdr = NULL;
if (ndopts.nd_opts_tgt_lladdr) {
- lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
- skb->dev);
+ lladdr = ndisc_dev_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
+ skb->dev);
if (!lladdr) {
net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
return;
--
2.8.2
This patch removes the net_device parameter from ndisc_opt_addr_space
function. This can be useful for calling such functionality which
doesn't depends on dev parameter. For current existing functionality
which depends on dev parameter, we introduce ndisc_dev_opt_addr_space to have
an easy replacement for the ndisc_opt_addr_space function.
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 | 13 +++++++++----
net/ipv6/ndisc.c | 12 ++++++------
2 files changed, 15 insertions(+), 10 deletions(-)
diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index 2d8edaa..dbc8d01 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(struct net_device *dev)
+static inline int ndisc_opt_addr_space(unsigned char addr_len, int pad)
{
- return NDISC_OPT_SPACE(dev->addr_len +
- ndisc_addr_option_pad(dev->type));
+ return NDISC_OPT_SPACE(addr_len + pad);
+}
+
+static inline int ndisc_dev_opt_addr_space(const struct net_device *dev)
+{
+ 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,
@@ -139,7 +144,7 @@ static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,
u8 *lladdr = (u8 *)(p + 1);
int lladdrlen = p->nd_opt_len << 3;
int prepad = ndisc_addr_option_pad(dev->type);
- if (lladdrlen != ndisc_opt_addr_space(dev))
+ if (lladdrlen != ndisc_opt_addr_space(dev->addr_len, prepad))
return NULL;
return lladdr + prepad;
}
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index c245895..2241f06 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -152,9 +152,9 @@ EXPORT_SYMBOL_GPL(nd_tbl);
static void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data)
{
- int pad = ndisc_addr_option_pad(skb->dev->type);
+ 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;
@@ -509,7 +509,7 @@ void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
if (!dev->addr_len)
inc_opt = 0;
if (inc_opt)
- optlen += ndisc_opt_addr_space(dev);
+ optlen += ndisc_dev_opt_addr_space(dev);
skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
if (!skb)
@@ -574,7 +574,7 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
if (ipv6_addr_any(saddr))
inc_opt = false;
if (inc_opt)
- optlen += ndisc_opt_addr_space(dev);
+ optlen += ndisc_dev_opt_addr_space(dev);
skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
if (!skb)
@@ -626,7 +626,7 @@ void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
}
#endif
if (send_sllao)
- optlen += ndisc_opt_addr_space(dev);
+ optlen += ndisc_dev_opt_addr_space(dev);
skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
if (!skb)
@@ -1563,7 +1563,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
memcpy(ha_buf, neigh->ha, dev->addr_len);
read_unlock_bh(&neigh->lock);
ha = ha_buf;
- optlen += ndisc_opt_addr_space(dev);
+ optlen += ndisc_dev_opt_addr_space(dev);
} else
read_unlock_bh(&neigh->lock);
--
2.8.2
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 fbae31e..824d1bc 100644
--- a/net/6lowpan/core.c
+++ b/net/6lowpan/core.c
@@ -158,8 +158,6 @@ static int __init lowpan_module_init(void)
return ret;
}
- request_module_nowait("ipv6");
-
request_module_nowait("nhc_dest");
request_module_nowait("nhc_fragment");
request_module_nowait("nhc_hop");
--
2.8.2
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/addrconf.h | 3 +++
net/6lowpan/core.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
net/ipv6/addrconf.c | 5 +++--
3 files changed, 52 insertions(+), 2 deletions(-)
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 730d856..b1774eb 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -94,6 +94,9 @@ int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2,
void addrconf_join_solict(struct net_device *dev, const struct in6_addr *addr);
void addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr *addr);
+void addrconf_add_linklocal(struct inet6_dev *idev,
+ const struct in6_addr *addr, u32 flags);
+
static inline int addrconf_ifid_eui48(u8 *eui, struct net_device *dev)
{
if (dev->addr_len != ETH_ALEN)
diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
index 7a240b3..fbae31e 100644
--- a/net/6lowpan/core.c
+++ b/net/6lowpan/core.c
@@ -14,6 +14,7 @@
#include <linux/module.h>
#include <net/6lowpan.h>
+#include <net/addrconf.h>
#include "6lowpan_i.h"
@@ -72,16 +73,61 @@ void lowpan_unregister_netdev(struct net_device *dev)
}
EXPORT_SYMBOL(lowpan_unregister_netdev);
+static int addrconf_ifid_802154_6lowpan(u8 *eui, struct net_device *dev)
+{
+ struct wpan_dev *wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr;
+
+ /* Set short_addr autoconfiguration if short_addr is present only */
+ if (!ieee802154_is_valid_src_short_addr(wpan_dev->short_addr))
+ return -1;
+
+ /* For either address format, all zero addresses MUST NOT be used */
+ if (wpan_dev->pan_id == cpu_to_le16(0x0000) &&
+ wpan_dev->short_addr == cpu_to_le16(0x0000))
+ return -1;
+
+ /* Alternatively, if no PAN ID is known, 16 zero bits may be used */
+ if (wpan_dev->pan_id == cpu_to_le16(IEEE802154_PAN_ID_BROADCAST))
+ memset(eui, 0, 2);
+ else
+ ieee802154_le16_to_be16(eui, &wpan_dev->pan_id);
+
+ /* The "Universal/Local" (U/L) bit shall be set to zero */
+ eui[0] &= ~2;
+ eui[2] = 0;
+ eui[3] = 0xFF;
+ eui[4] = 0xFE;
+ eui[5] = 0;
+ ieee802154_le16_to_be16(&eui[6], &wpan_dev->short_addr);
+ return 0;
+}
+
static int lowpan_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct inet6_dev *idev;
+ struct in6_addr addr;
int i;
if (dev->type != ARPHRD_6LOWPAN)
return NOTIFY_DONE;
+ idev = __in6_dev_get(dev);
+ if (!idev)
+ return NOTIFY_DONE;
+
switch (event) {
+ case NETDEV_UP:
+ case NETDEV_CHANGE:
+ /* (802.15.4 6LoWPAN short address slaac handling */
+ if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154) &&
+ addrconf_ifid_802154_6lowpan(addr.s6_addr + 8, dev) == 0) {
+ __ipv6_addr_set_half(&addr.s6_addr32[0],
+ htonl(0xFE800000), 0);
+ addrconf_add_linklocal(idev, &addr, 0);
+ }
+ break;
case NETDEV_DOWN:
for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
clear_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE,
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 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.2
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 c148edf..42737c4 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1484,8 +1484,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 dd085db..dcc0d59 100644
--- a/net/ieee802154/6lowpan/core.c
+++ b/net/ieee802154/6lowpan/core.c
@@ -92,11 +92,21 @@ static int lowpan_stop(struct net_device *dev)
return 0;
}
+static int lowpan_neigh_construct(struct neighbour *n)
+{
+ struct lowpan_802154_neigh *neigh = lowpan_802154_neigh(neighbour_priv(n));
+
+ /* default no short_addr is available for a neighbour */
+ neigh->short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);
+ return 0;
+}
+
static const struct net_device_ops lowpan_netdev_ops = {
.ndo_init = lowpan_dev_init,
.ndo_start_xmit = lowpan_xmit,
.ndo_open = lowpan_open,
.ndo_stop = lowpan_stop,
+ .ndo_neigh_construct = lowpan_neigh_construct,
};
static void lowpan_setup(struct net_device *ldev)
@@ -161,6 +171,8 @@ static int lowpan_newlink(struct net *src_net, struct net_device *ldev,
wdev->needed_headroom;
ldev->needed_tailroom = wdev->needed_tailroom;
+ ldev->neigh_priv_len = sizeof(struct lowpan_802154_neigh);
+
ret = lowpan_register_netdevice(ldev, LOWPAN_LLTYPE_IEEE802154);
if (ret < 0) {
dev_put(wdev);
--
2.8.2