1 /* This program is free software; you can redistribute it and/or modify
2 * it under the terms of the GNU General Public License version 2
3 * as published by the Free Software Foundation.
5 * This program is distributed in the hope that it will be useful,
6 * but WITHOUT ANY WARRANTY; without even the implied warranty of
7 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
8 * GNU General Public License for more details.
11 * (C) 2016 Pengutronix, Alexander Aring <aar@pengutronix.de>
14 #include <net/6lowpan.h>
15 #include <net/addrconf.h>
16 #include <net/ndisc.h>
18 #include "6lowpan_i.h"
20 static int lowpan_ndisc_is_useropt(u8 nd_opt_type
)
22 return nd_opt_type
== ND_OPT_6CO
;
25 #if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
26 #define NDISC_802154_SHORT_ADDR_LENGTH 1
27 static int lowpan_ndisc_parse_802154_options(const struct net_device
*dev
,
28 struct nd_opt_hdr
*nd_opt
,
29 struct ndisc_options
*ndopts
)
31 switch (nd_opt
->nd_opt_len
) {
32 case NDISC_802154_SHORT_ADDR_LENGTH
:
33 if (ndopts
->nd_802154_opt_array
[nd_opt
->nd_opt_type
])
35 "%s: duplicated short addr ND6 option found: type=%d\n",
36 __func__
, nd_opt
->nd_opt_type
);
38 ndopts
->nd_802154_opt_array
[nd_opt
->nd_opt_type
] = nd_opt
;
41 /* all others will be handled by ndisc IPv6 option parsing */
46 static int lowpan_ndisc_parse_options(const struct net_device
*dev
,
47 struct nd_opt_hdr
*nd_opt
,
48 struct ndisc_options
*ndopts
)
50 if (!lowpan_is_ll(dev
, LOWPAN_LLTYPE_IEEE802154
))
53 switch (nd_opt
->nd_opt_type
) {
54 case ND_OPT_SOURCE_LL_ADDR
:
55 case ND_OPT_TARGET_LL_ADDR
:
56 return lowpan_ndisc_parse_802154_options(dev
, nd_opt
, ndopts
);
62 static void lowpan_ndisc_802154_update(struct neighbour
*n
, u32 flags
,
64 const struct ndisc_options
*ndopts
)
66 struct lowpan_802154_neigh
*neigh
= lowpan_802154_neigh(neighbour_priv(n
));
67 u8
*lladdr_short
= NULL
;
70 case NDISC_ROUTER_SOLICITATION
:
71 case NDISC_ROUTER_ADVERTISEMENT
:
72 case NDISC_NEIGHBOUR_SOLICITATION
:
73 if (ndopts
->nd_802154_opts_src_lladdr
) {
74 lladdr_short
= __ndisc_opt_addr_data(ndopts
->nd_802154_opts_src_lladdr
,
75 IEEE802154_SHORT_ADDR_LEN
, 0);
78 "NA: invalid short link-layer address length\n");
84 case NDISC_NEIGHBOUR_ADVERTISEMENT
:
85 if (ndopts
->nd_802154_opts_tgt_lladdr
) {
86 lladdr_short
= __ndisc_opt_addr_data(ndopts
->nd_802154_opts_tgt_lladdr
,
87 IEEE802154_SHORT_ADDR_LEN
, 0);
90 "NA: invalid short link-layer address length\n");
99 write_lock_bh(&n
->lock
);
101 ieee802154_be16_to_le16(&neigh
->short_addr
, lladdr_short
);
102 if (!lowpan_802154_is_valid_src_short_addr(neigh
->short_addr
))
103 neigh
->short_addr
= cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC
);
105 write_unlock_bh(&n
->lock
);
108 static void lowpan_ndisc_update(const struct net_device
*dev
,
109 struct neighbour
*n
, u32 flags
, u8 icmp6_type
,
110 const struct ndisc_options
*ndopts
)
112 if (!lowpan_is_ll(dev
, LOWPAN_LLTYPE_IEEE802154
))
115 /* react on overrides only. TODO check if this is really right. */
116 if (flags
& NEIGH_UPDATE_F_OVERRIDE
)
117 lowpan_ndisc_802154_update(n
, flags
, icmp6_type
, ndopts
);
120 static int lowpan_ndisc_opt_addr_space(const struct net_device
*dev
,
121 u8 icmp6_type
, struct neighbour
*neigh
,
124 struct lowpan_802154_neigh
*n
;
125 struct wpan_dev
*wpan_dev
;
128 if (!lowpan_is_ll(dev
, LOWPAN_LLTYPE_IEEE802154
))
131 switch (icmp6_type
) {
133 n
= lowpan_802154_neigh(neighbour_priv(neigh
));
135 read_lock_bh(&neigh
->lock
);
136 if (lowpan_802154_is_valid_src_short_addr(n
->short_addr
)) {
137 memcpy(ha_buf
, &n
->short_addr
,
138 IEEE802154_SHORT_ADDR_LEN
);
139 read_unlock_bh(&neigh
->lock
);
140 addr_space
+= __ndisc_opt_addr_space(IEEE802154_SHORT_ADDR_LEN
, 0);
143 read_unlock_bh(&neigh
->lock
);
146 case NDISC_NEIGHBOUR_ADVERTISEMENT
:
147 case NDISC_NEIGHBOUR_SOLICITATION
:
148 case NDISC_ROUTER_SOLICITATION
:
149 wpan_dev
= lowpan_802154_dev(dev
)->wdev
->ieee802154_ptr
;
151 if (lowpan_802154_is_valid_src_short_addr(wpan_dev
->short_addr
))
152 addr_space
= __ndisc_opt_addr_space(IEEE802154_SHORT_ADDR_LEN
, 0);
161 static void lowpan_ndisc_fill_addr_option(const struct net_device
*dev
,
162 struct sk_buff
*skb
, u8 icmp6_type
,
165 struct wpan_dev
*wpan_dev
;
169 if (!lowpan_is_ll(dev
, LOWPAN_LLTYPE_IEEE802154
))
172 switch (icmp6_type
) {
175 ieee802154_le16_to_be16(&short_addr
, ha
);
176 __ndisc_fill_addr_option(skb
, ND_OPT_TARGET_LL_ADDR
,
178 IEEE802154_SHORT_ADDR_LEN
, 0);
181 case NDISC_NEIGHBOUR_ADVERTISEMENT
:
182 opt_type
= ND_OPT_TARGET_LL_ADDR
;
184 case NDISC_ROUTER_SOLICITATION
:
185 case NDISC_NEIGHBOUR_SOLICITATION
:
186 opt_type
= ND_OPT_SOURCE_LL_ADDR
;
192 wpan_dev
= lowpan_802154_dev(dev
)->wdev
->ieee802154_ptr
;
194 if (lowpan_802154_is_valid_src_short_addr(wpan_dev
->short_addr
)) {
195 ieee802154_le16_to_be16(&short_addr
,
196 &wpan_dev
->short_addr
);
197 __ndisc_fill_addr_option(skb
, opt_type
, &short_addr
,
198 IEEE802154_SHORT_ADDR_LEN
, 0);
202 static void lowpan_ndisc_prefix_rcv_add_addr(struct net
*net
,
203 struct net_device
*dev
,
204 const struct prefix_info
*pinfo
,
205 struct inet6_dev
*in6_dev
,
206 struct in6_addr
*addr
,
207 int addr_type
, u32 addr_flags
,
208 bool sllao
, bool tokenized
,
211 bool dev_addr_generated
)
215 /* generates short based address for RA PIO's */
216 if (lowpan_is_ll(dev
, LOWPAN_LLTYPE_IEEE802154
) && dev_addr_generated
&&
217 !addrconf_ifid_802154_6lowpan(addr
->s6_addr
+ 8, dev
)) {
218 err
= addrconf_prefix_rcv_add_addr(net
, dev
, pinfo
, in6_dev
,
219 addr
, addr_type
, addr_flags
,
220 sllao
, tokenized
, valid_lft
,
224 "RA: could not add a short address based address for prefix: %pI6c\n",
230 const struct ndisc_ops lowpan_ndisc_ops
= {
231 .is_useropt
= lowpan_ndisc_is_useropt
,
232 #if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
233 .parse_options
= lowpan_ndisc_parse_options
,
234 .update
= lowpan_ndisc_update
,
235 .opt_addr_space
= lowpan_ndisc_opt_addr_space
,
236 .fill_addr_option
= lowpan_ndisc_fill_addr_option
,
237 .prefix_rcv_add_addr
= lowpan_ndisc_prefix_rcv_add_addr
,