@@ -235,6 +235,52 @@ static void ndisc_802154_parse_addr_options(struct ndisc_options *ndopts,
break;
}
}
+
+static void ndisc_802154_neigh_update(struct neighbour *n, void *priv)
+{
+ struct lowpan_802154_neigh *neigh = lowpan_802154_neigh(neighbour_priv(n));
+
+ write_lock_bh(&n->lock);
+ if (priv)
+ ieee802154_be16_to_le16(&neigh->short_addr, priv);
+ else
+ neigh->short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);
+ write_unlock_bh(&n->lock);
+}
+
+static inline int ndisc_802154_short_addr_space(struct net_device *dev)
+{
+ struct wpan_dev *wpan_dev;
+ int addr_space = 0;
+
+ if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154)) {
+ wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr;
+
+ if (ieee802154_is_valid_src_short_addr(wpan_dev->short_addr))
+ addr_space = ndisc_opt_addr_space(dev, IEEE802154_SHORT_ADDR_LEN);
+ }
+
+ return addr_space;
+}
+
+static inline void ndisc_802154_short_addr_option(struct net_device *dev,
+ struct sk_buff *skb,
+ int type)
+{
+ struct wpan_dev *wpan_dev;
+ __be16 short_addr;
+
+ if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154)) {
+ wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr;
+
+ if (ieee802154_is_valid_src_short_addr(wpan_dev->short_addr)) {
+ ieee802154_le16_to_be16(&short_addr,
+ &wpan_dev->short_addr);
+ ndisc_fill_addr_option(skb, type, &short_addr,
+ IEEE802154_SHORT_ADDR_LEN);
+ }
+ }
+}
#endif
struct ndisc_options *ndisc_parse_options(const struct net_device *dev,
@@ -550,8 +596,12 @@ 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_opt_addr_space(dev, dev->addr_len);
+#ifdef CONFIG_IEEE802154_6LOWPAN
+ optlen += ndisc_802154_short_addr_space(dev);
+#endif
+ }
skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
if (!skb)
@@ -568,9 +618,14 @@ void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
.target = *solicited_addr,
};
- if (inc_opt)
+ if (inc_opt) {
ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR,
dev->dev_addr, dev->addr_len);
+#ifdef CONFIG_IEEE802154_6LOWPAN
+ ndisc_802154_short_addr_option(dev, skb,
+ ND_OPT_TARGET_LL_ADDR);
+#endif
+ }
ndisc_send_skb(skb, daddr, src_addr);
@@ -615,8 +670,12 @@ 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_opt_addr_space(dev, dev->addr_len);
+#ifdef CONFIG_IEEE802154_6LOWPAN
+ optlen += ndisc_802154_short_addr_space(dev);
+#endif
+ }
skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
if (!skb)
@@ -630,9 +689,14 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
.target = *solicit,
};
- if (inc_opt)
+ if (inc_opt) {
ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR,
dev->dev_addr, dev->addr_len);
+#ifdef CONFIG_IEEE802154_6LOWPAN
+ ndisc_802154_short_addr_option(dev, skb,
+ ND_OPT_SOURCE_LL_ADDR);
+#endif
+ }
ndisc_send_skb(skb, daddr, saddr);
}
@@ -760,6 +824,9 @@ static void ndisc_recv_ns(struct sk_buff *skb)
int dad = ipv6_addr_any(saddr);
bool inc;
int is_router = -1;
+#ifdef CONFIG_IEEE802154_6LOWPAN
+ u8 *lladdr_short = NULL;
+#endif
if (skb->len < sizeof(struct nd_msg)) {
ND_PRINTK(2, warn, "NS: packet too short\n");
@@ -806,6 +873,30 @@ static void ndisc_recv_ns(struct sk_buff *skb)
}
}
+#ifdef CONFIG_IEEE802154_6LOWPAN
+ if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154) &&
+ ndopts.nd_802154_opts_src_lladdr) {
+ lladdr_short = ndisc_opt_addr_data(ndopts.nd_802154_opts_src_lladdr,
+ dev, IEEE802154_SHORT_ADDR_LEN);
+ if (!lladdr_short) {
+ ND_PRINTK(2, warn,
+ "NS: invalid short link-layer address length\n");
+ return;
+ }
+
+ /* RFC2461 7.1.1:
+ * If the IP source address is the unspecified address,
+ * there MUST NOT be source link-layer address option
+ * in the message.
+ */
+ if (dad) {
+ ND_PRINTK(2, warn,
+ "NS: bad DAD packet (short link-layer address option)\n");
+ return;
+ }
+ }
+#endif
+
inc = ipv6_addr_is_multicast(daddr);
ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
@@ -898,10 +989,15 @@ have_ifp:
*/
neigh = __neigh_lookup(&nd_tbl, saddr, dev,
!inc || lladdr || !dev->addr_len);
- if (neigh)
+ if (neigh) {
neigh_update(neigh, lladdr, NUD_STALE,
NEIGH_UPDATE_F_WEAK_OVERRIDE|
NEIGH_UPDATE_F_OVERRIDE);
+#ifdef CONFIG_IEEE802154_6LOWPAN
+ if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154))
+ ndisc_802154_neigh_update(neigh, lladdr_short);
+#endif
+ }
if (neigh || !dev->header_ops) {
ndisc_send_na(dev, saddr, &msg->target, !!is_router,
true, (ifp != NULL && inc), inc);
@@ -929,6 +1025,9 @@ static void ndisc_recv_na(struct sk_buff *skb)
struct inet6_dev *idev = __in6_dev_get(dev);
struct inet6_ifaddr *ifp;
struct neighbour *neigh;
+#ifdef CONFIG_IEEE802154_6LOWPAN
+ u8 *lladdr_short = NULL;
+#endif
if (skb->len < sizeof(struct nd_msg)) {
ND_PRINTK(2, warn, "NA: packet too short\n");
@@ -967,6 +1066,18 @@ static void ndisc_recv_na(struct sk_buff *skb)
return;
}
}
+#ifdef CONFIG_IEEE802154_6LOWPAN
+ if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154) &&
+ ndopts.nd_802154_opts_tgt_lladdr) {
+ lladdr_short = ndisc_opt_addr_data(ndopts.nd_802154_opts_tgt_lladdr,
+ dev, IEEE802154_SHORT_ADDR_LEN);
+ if (!lladdr_short) {
+ ND_PRINTK(2, warn,
+ "NA: invalid short link-layer address length\n");
+ return;
+ }
+ }
+#endif
ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
if (ifp) {
if (skb->pkt_type != PACKET_LOOPBACK
@@ -1018,6 +1129,11 @@ static void ndisc_recv_na(struct sk_buff *skb)
NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
(msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0));
+#ifdef CONFIG_IEEE802154_6LOWPAN
+ if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154))
+ ndisc_802154_neigh_update(neigh, lladdr_short);
+#endif
+
if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
/*
* Change: router to host
This patch will add 802.15.4 6lowpan handling when parsing NS and NA messages. The 802.15.4 6lowpan neighbour has a parivate data room for storing an optional address which is available via link layer address option fields. This option field will currently always be written if a neighbour was returned by neighbour lookup functionality. Signed-off-by: Alexander Aring <aar@pengutronix.de> --- net/ipv6/ndisc.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 121 insertions(+), 5 deletions(-)