Return-Path: Subject: Re: [PATCHv3 net-next 08/12] ipv6: introduce neighbour discovery ops To: Alexander Aring , linux-wpan@vger.kernel.org References: <20160614115239.17788-1-aar@pengutronix.de> <20160614115239.17788-9-aar@pengutronix.de> Cc: hideaki.yoshifuji@miraclelinux.com, kernel@pengutronix.de, marcel@holtmann.org, jukka.rissanen@linux.intel.com, hannes@stressinduktion.org, stefan@osg.samsung.com, mcr@sandelman.ca, werner@almesberger.net, linux-bluetooth@vger.kernel.org, netdev@vger.kernel.org, "David S . Miller" , Alexey Kuznetsov , James Morris , Hideaki YOSHIFUJI , Patrick McHardy From: =?UTF-8?B?WU9TSElGVUpJIEhpZGVha2kv5ZCJ6Jek6Iux5piO?= Message-ID: <5761379D.6030700@miraclelinux.com> Date: Wed, 15 Jun 2016 20:10:21 +0900 MIME-Version: 1.0 In-Reply-To: <20160614115239.17788-9-aar@pengutronix.de> Content-Type: text/plain; charset=iso-2022-jp List-ID: 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 > Cc: Alexey Kuznetsov > Cc: James Morris > Cc: Hideaki YOSHIFUJI > Cc: Patrick McHardy > Signed-off-by: Alexander Aring Acked-by: YOSHIFUJI Hideaki > --- > include/linux/netdevice.h | 5 ++ > include/net/ndisc.h | 197 +++++++++++++++++++++++++++++++++++++++++++++- > net/ipv6/addrconf.c | 13 ++- > net/ipv6/ndisc.c | 101 ++++++++++++++++-------- > net/ipv6/route.c | 8 +- > 5 files changed, 284 insertions(+), 40 deletions(-) > > diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h > index 36e43bd..890158e 100644 > --- a/include/linux/netdevice.h > +++ b/include/linux/netdevice.h > @@ -1456,6 +1456,8 @@ enum netdev_priv_flags { > * @netdev_ops: Includes several pointers to callbacks, > * if one wants to override the ndo_*() functions > * @ethtool_ops: Management operations > + * @ndisc_ops: Includes callbacks for different IPv6 neighbour > + * discovery handling. Necessary for e.g. 6LoWPAN. > * @header_ops: Includes callbacks for creating,parsing,caching,etc > * of Layer 2 headers. > * > @@ -1672,6 +1674,9 @@ struct net_device { > #ifdef CONFIG_NET_L3_MASTER_DEV > const struct l3mdev_ops *l3mdev_ops; > #endif > +#if IS_ENABLED(CONFIG_IPV6) > + const struct ndisc_ops *ndisc_ops; > +#endif > > const struct header_ops *header_ops; > > diff --git a/include/net/ndisc.h b/include/net/ndisc.h > index c8962ad..a5e2767 100644 > --- a/include/net/ndisc.h > +++ b/include/net/ndisc.h > @@ -58,6 +58,7 @@ struct inet6_dev; > struct net_device; > struct net_proto_family; > struct sk_buff; > +struct prefix_info; > > extern struct neigh_table nd_tbl; > > @@ -110,9 +111,182 @@ struct ndisc_options { > > #define NDISC_OPT_SPACE(len) (((len)+2+7)&~7) > > -struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len, > +struct ndisc_options *ndisc_parse_options(const struct net_device *dev, > + u8 *opt, int opt_len, > struct ndisc_options *ndopts); > > +#define NDISC_OPS_REDIRECT_DATA_SPACE 2 > + > +/* > + * This structure defines the hooks for IPv6 neighbour discovery. > + * The following hooks can be defined; unless noted otherwise, they are > + * optional and can be filled with a null pointer. > + * > + * int (*is_useropt)(u8 nd_opt_type): > + * This function is called when IPv6 decide RA userspace options. if > + * this function returns 1 then the option given by nd_opt_type will > + * be handled as userspace option additional to the IPv6 options. > + * > + * int (*parse_options)(const struct net_device *dev, > + * struct nd_opt_hdr *nd_opt, > + * struct ndisc_options *ndopts): > + * This function is called while parsing ndisc ops and put each position > + * as pointer into ndopts. If this function return unequal 0, then this > + * function took care about the ndisc option, if 0 then the IPv6 ndisc > + * option parser will take care about that option. > + * > + * void (*update)(const struct net_device *dev, struct neighbour *n, > + * u32 flags, u8 icmp6_type, > + * const struct ndisc_options *ndopts): > + * This function is called when IPv6 ndisc updates the neighbour cache > + * entry. Additional options which can be updated may be previously > + * parsed by parse_opts callback and accessible over ndopts parameter. > + * > + * int (*opt_addr_space)(const struct net_device *dev, u8 icmp6_type, > + * struct neighbour *neigh, u8 *ha_buf, > + * u8 **ha): > + * This function is called when the necessary option space will be > + * calculated before allocating a skb. The parameters neigh, ha_buf > + * abd ha are available on NDISC_REDIRECT messages only. > + * > + * void (*fill_addr_option)(const struct net_device *dev, > + * struct sk_buff *skb, u8 icmp6_type, > + * const u8 *ha): > + * This function is called when the skb will finally fill the option > + * fields inside skb. NOTE: this callback should fill the option > + * fields to the skb which are previously indicated by opt_space > + * parameter. That means the decision to add such option should > + * not lost between these two callbacks, e.g. protected by interface > + * up state. > + * > + * void (*prefix_rcv_add_addr)(struct net *net, struct net_device *dev, > + * const struct prefix_info *pinfo, > + * struct inet6_dev *in6_dev, > + * struct in6_addr *addr, > + * int addr_type, u32 addr_flags, > + * bool sllao, bool tokenized, > + * __u32 valid_lft, u32 prefered_lft, > + * bool dev_addr_generated): > + * This function is called when a RA messages is received with valid > + * PIO option fields and an IPv6 address will be added to the interface > + * for autoconfiguration. The parameter dev_addr_generated reports about > + * if the address was based on dev->dev_addr or not. This can be used > + * to add a second address if link-layer operates with two link layer > + * addresses. E.g. 802.15.4 6LoWPAN. > + */ > +struct ndisc_ops { > + int (*is_useropt)(u8 nd_opt_type); > + int (*parse_options)(const struct net_device *dev, > + struct nd_opt_hdr *nd_opt, > + struct ndisc_options *ndopts); > + void (*update)(const struct net_device *dev, struct neighbour *n, > + u32 flags, u8 icmp6_type, > + const struct ndisc_options *ndopts); > + int (*opt_addr_space)(const struct net_device *dev, u8 icmp6_type, > + struct neighbour *neigh, u8 *ha_buf, > + u8 **ha); > + void (*fill_addr_option)(const struct net_device *dev, > + struct sk_buff *skb, u8 icmp6_type, > + const u8 *ha); > + void (*prefix_rcv_add_addr)(struct net *net, struct net_device *dev, > + const struct prefix_info *pinfo, > + struct inet6_dev *in6_dev, > + struct in6_addr *addr, > + int addr_type, u32 addr_flags, > + bool sllao, bool tokenized, > + __u32 valid_lft, u32 prefered_lft, > + bool dev_addr_generated); > +}; > + > +#if IS_ENABLED(CONFIG_IPV6) > +static inline int ndisc_ops_is_useropt(const struct net_device *dev, > + u8 nd_opt_type) > +{ > + if (dev->ndisc_ops && dev->ndisc_ops->is_useropt) > + return dev->ndisc_ops->is_useropt(nd_opt_type); > + else > + return 0; > +} > + > +static inline int ndisc_ops_parse_options(const struct net_device *dev, > + struct nd_opt_hdr *nd_opt, > + struct ndisc_options *ndopts) > +{ > + if (dev->ndisc_ops && dev->ndisc_ops->parse_options) > + return dev->ndisc_ops->parse_options(dev, nd_opt, ndopts); > + else > + return 0; > +} > + > +static inline void ndisc_ops_update(const struct net_device *dev, > + struct neighbour *n, u32 flags, > + u8 icmp6_type, > + const struct ndisc_options *ndopts) > +{ > + if (dev->ndisc_ops && dev->ndisc_ops->update) > + dev->ndisc_ops->update(dev, n, flags, icmp6_type, ndopts); > +} > + > +static inline int ndisc_ops_opt_addr_space(const struct net_device *dev, > + u8 icmp6_type) > +{ > + if (dev->ndisc_ops && dev->ndisc_ops->opt_addr_space && > + icmp6_type != NDISC_REDIRECT) > + return dev->ndisc_ops->opt_addr_space(dev, icmp6_type, NULL, > + NULL, NULL); > + else > + return 0; > +} > + > +static inline int ndisc_ops_redirect_opt_addr_space(const struct net_device *dev, > + struct neighbour *neigh, > + u8 *ha_buf, u8 **ha) > +{ > + if (dev->ndisc_ops && dev->ndisc_ops->opt_addr_space) > + return dev->ndisc_ops->opt_addr_space(dev, NDISC_REDIRECT, > + neigh, ha_buf, ha); > + else > + return 0; > +} > + > +static inline void ndisc_ops_fill_addr_option(const struct net_device *dev, > + struct sk_buff *skb, > + u8 icmp6_type) > +{ > + if (dev->ndisc_ops && dev->ndisc_ops->fill_addr_option && > + icmp6_type != NDISC_REDIRECT) > + dev->ndisc_ops->fill_addr_option(dev, skb, icmp6_type, NULL); > +} > + > +static inline void ndisc_ops_fill_redirect_addr_option(const struct net_device *dev, > + struct sk_buff *skb, > + const u8 *ha) > +{ > + if (dev->ndisc_ops && dev->ndisc_ops->fill_addr_option) > + dev->ndisc_ops->fill_addr_option(dev, skb, NDISC_REDIRECT, ha); > +} > + > +static inline void ndisc_ops_prefix_rcv_add_addr(struct net *net, > + struct net_device *dev, > + const struct prefix_info *pinfo, > + struct inet6_dev *in6_dev, > + struct in6_addr *addr, > + int addr_type, u32 addr_flags, > + bool sllao, bool tokenized, > + __u32 valid_lft, > + u32 prefered_lft, > + bool dev_addr_generated) > +{ > + if (dev->ndisc_ops && dev->ndisc_ops->prefix_rcv_add_addr) > + dev->ndisc_ops->prefix_rcv_add_addr(net, dev, pinfo, in6_dev, > + addr, addr_type, > + addr_flags, sllao, > + tokenized, valid_lft, > + prefered_lft, > + dev_addr_generated); > +} > +#endif > + > /* > * Return the padding between the option length and the start of the > * link addr. Currently only IP-over-InfiniBand needs this, although > @@ -132,11 +306,25 @@ static inline int __ndisc_opt_addr_space(unsigned char addr_len, int pad) > return NDISC_OPT_SPACE(addr_len + pad); > } > > -static inline int ndisc_opt_addr_space(struct net_device *dev) > +#if IS_ENABLED(CONFIG_IPV6) > +static inline int ndisc_opt_addr_space(struct net_device *dev, u8 icmp6_type) > +{ > + return __ndisc_opt_addr_space(dev->addr_len, > + ndisc_addr_option_pad(dev->type)) + > + ndisc_ops_opt_addr_space(dev, icmp6_type); > +} > + > +static inline int ndisc_redirect_opt_addr_space(struct net_device *dev, > + struct neighbour *neigh, > + u8 *ops_data_buf, > + u8 **ops_data) > { > return __ndisc_opt_addr_space(dev->addr_len, > - ndisc_addr_option_pad(dev->type)); > + ndisc_addr_option_pad(dev->type)) + > + ndisc_ops_redirect_opt_addr_space(dev, neigh, ops_data_buf, > + ops_data); > } > +#endif > > static inline u8 *__ndisc_opt_addr_data(struct nd_opt_hdr *p, > unsigned char addr_len, int prepad) > @@ -205,6 +393,9 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target); > int ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev, > int dir); > > +void ndisc_update(const struct net_device *dev, struct neighbour *neigh, > + const u8 *lladdr, u8 new, u32 flags, u8 icmp6_type, > + struct ndisc_options *ndopts); > > /* > * IGMP > diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c > index 0ca31e1..2d678c0 100644 > --- a/net/ipv6/addrconf.c > +++ b/net/ipv6/addrconf.c > @@ -2531,7 +2531,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao) > > if (pinfo->autoconf && in6_dev->cnf.autoconf) { > struct in6_addr addr; > - bool tokenized = false; > + bool tokenized = false, dev_addr_generated = false; > > if (pinfo->prefix_len == 64) { > memcpy(&addr, &pinfo->prefix, 8); > @@ -2550,6 +2550,8 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao) > } else if (ipv6_generate_eui64(addr.s6_addr + 8, dev) && > ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) { > goto put; > + } else { > + dev_addr_generated = true; > } > goto ok; > } > @@ -2565,6 +2567,15 @@ ok: > prefered_lft); > if (err) > goto put; > + > + /* Ignore error case here because previous prefix add addr was > + * successful which will be notified. > + */ > + ndisc_ops_prefix_rcv_add_addr(net, dev, pinfo, in6_dev, &addr, > + addr_type, addr_flags, sllao, > + tokenized, valid_lft, > + prefered_lft, > + dev_addr_generated); > } > inet6_prefix_notify(RTM_NEWPREFIX, in6_dev, pinfo); > put: > diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c > index a7b9468..2f4afd1 100644 > --- a/net/ipv6/ndisc.c > +++ b/net/ipv6/ndisc.c > @@ -172,10 +172,19 @@ static void __ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data, > } > > static inline void ndisc_fill_addr_option(struct sk_buff *skb, int type, > - void *data) > + void *data, u8 icmp6_type) > { > __ndisc_fill_addr_option(skb, type, data, skb->dev->addr_len, > ndisc_addr_option_pad(skb->dev->type)); > + ndisc_ops_fill_addr_option(skb->dev, skb, icmp6_type); > +} > + > +static inline void ndisc_fill_redirect_addr_option(struct sk_buff *skb, > + void *ha, > + const u8 *ops_data) > +{ > + ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR, ha, NDISC_REDIRECT); > + ndisc_ops_fill_redirect_addr_option(skb->dev, skb, ops_data); > } > > static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur, > @@ -191,24 +200,28 @@ static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur, > return cur <= end && cur->nd_opt_type == type ? cur : NULL; > } > > -static inline int ndisc_is_useropt(struct nd_opt_hdr *opt) > +static inline int ndisc_is_useropt(const struct net_device *dev, > + struct nd_opt_hdr *opt) > { > return opt->nd_opt_type == ND_OPT_RDNSS || > - opt->nd_opt_type == ND_OPT_DNSSL; > + opt->nd_opt_type == ND_OPT_DNSSL || > + ndisc_ops_is_useropt(dev, opt->nd_opt_type); > } > > -static struct nd_opt_hdr *ndisc_next_useropt(struct nd_opt_hdr *cur, > +static struct nd_opt_hdr *ndisc_next_useropt(const struct net_device *dev, > + struct nd_opt_hdr *cur, > struct nd_opt_hdr *end) > { > if (!cur || !end || cur >= end) > return NULL; > do { > cur = ((void *)cur) + (cur->nd_opt_len << 3); > - } while (cur < end && !ndisc_is_useropt(cur)); > - return cur <= end && ndisc_is_useropt(cur) ? cur : NULL; > + } while (cur < end && !ndisc_is_useropt(dev, cur)); > + return cur <= end && ndisc_is_useropt(dev, cur) ? cur : NULL; > } > > -struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len, > +struct ndisc_options *ndisc_parse_options(const struct net_device *dev, > + u8 *opt, int opt_len, > struct ndisc_options *ndopts) > { > struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)opt; > @@ -223,6 +236,8 @@ struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len, > l = nd_opt->nd_opt_len << 3; > if (opt_len < l || l == 0) > return NULL; > + if (ndisc_ops_parse_options(dev, nd_opt, ndopts)) > + goto next_opt; > switch (nd_opt->nd_opt_type) { > case ND_OPT_SOURCE_LL_ADDR: > case ND_OPT_TARGET_LL_ADDR: > @@ -249,7 +264,7 @@ struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len, > break; > #endif > default: > - if (ndisc_is_useropt(nd_opt)) { > + if (ndisc_is_useropt(dev, nd_opt)) { > ndopts->nd_useropts_end = nd_opt; > if (!ndopts->nd_useropts) > ndopts->nd_useropts = nd_opt; > @@ -266,6 +281,7 @@ struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len, > nd_opt->nd_opt_len); > } > } > +next_opt: > opt_len -= l; > nd_opt = ((void *)nd_opt) + l; > } > @@ -515,7 +531,8 @@ void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr, > if (!dev->addr_len) > inc_opt = 0; > if (inc_opt) > - optlen += ndisc_opt_addr_space(dev); > + optlen += ndisc_opt_addr_space(dev, > + NDISC_NEIGHBOUR_ADVERTISEMENT); > > skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen); > if (!skb) > @@ -534,8 +551,8 @@ void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr, > > if (inc_opt) > ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR, > - dev->dev_addr); > - > + dev->dev_addr, > + NDISC_NEIGHBOUR_ADVERTISEMENT); > > ndisc_send_skb(skb, daddr, src_addr); > } > @@ -580,7 +597,8 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit, > if (ipv6_addr_any(saddr)) > inc_opt = false; > if (inc_opt) > - optlen += ndisc_opt_addr_space(dev); > + optlen += ndisc_opt_addr_space(dev, > + NDISC_NEIGHBOUR_SOLICITATION); > > skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen); > if (!skb) > @@ -596,7 +614,8 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit, > > if (inc_opt) > ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR, > - dev->dev_addr); > + dev->dev_addr, > + NDISC_NEIGHBOUR_SOLICITATION); > > ndisc_send_skb(skb, daddr, saddr); > } > @@ -632,7 +651,7 @@ void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, > } > #endif > if (send_sllao) > - optlen += ndisc_opt_addr_space(dev); > + optlen += ndisc_opt_addr_space(dev, NDISC_ROUTER_SOLICITATION); > > skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen); > if (!skb) > @@ -647,7 +666,8 @@ void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, > > if (send_sllao) > ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR, > - dev->dev_addr); > + dev->dev_addr, > + NDISC_ROUTER_SOLICITATION); > > ndisc_send_skb(skb, daddr, saddr); > } > @@ -708,6 +728,15 @@ static int pndisc_is_router(const void *pkey, > return ret; > } > > +void ndisc_update(const struct net_device *dev, struct neighbour *neigh, > + const u8 *lladdr, u8 new, u32 flags, u8 icmp6_type, > + struct ndisc_options *ndopts) > +{ > + neigh_update(neigh, lladdr, new, flags); > + /* report ndisc ops about neighbour update */ > + ndisc_ops_update(dev, neigh, flags, icmp6_type, ndopts); > +} > + > static void ndisc_recv_ns(struct sk_buff *skb) > { > struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb); > @@ -744,7 +773,7 @@ static void ndisc_recv_ns(struct sk_buff *skb) > return; > } > > - if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) { > + if (!ndisc_parse_options(dev, msg->opt, ndoptlen, &ndopts)) { > ND_PRINTK(2, warn, "NS: invalid ND options\n"); > return; > } > @@ -862,9 +891,10 @@ have_ifp: > neigh = __neigh_lookup(&nd_tbl, saddr, dev, > !inc || lladdr || !dev->addr_len); > if (neigh) > - neigh_update(neigh, lladdr, NUD_STALE, > + ndisc_update(dev, neigh, lladdr, NUD_STALE, > NEIGH_UPDATE_F_WEAK_OVERRIDE| > - NEIGH_UPDATE_F_OVERRIDE); > + NEIGH_UPDATE_F_OVERRIDE, > + NDISC_NEIGHBOUR_SOLICITATION, &ndopts); > if (neigh || !dev->header_ops) { > ndisc_send_na(dev, saddr, &msg->target, !!is_router, > true, (ifp != NULL && inc), inc); > @@ -917,7 +947,7 @@ static void ndisc_recv_na(struct sk_buff *skb) > idev->cnf.drop_unsolicited_na) > return; > > - if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) { > + if (!ndisc_parse_options(dev, msg->opt, ndoptlen, &ndopts)) { > ND_PRINTK(2, warn, "NS: invalid ND option\n"); > return; > } > @@ -973,12 +1003,13 @@ static void ndisc_recv_na(struct sk_buff *skb) > goto out; > } > > - neigh_update(neigh, lladdr, > + ndisc_update(dev, neigh, lladdr, > msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE, > NEIGH_UPDATE_F_WEAK_OVERRIDE| > (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0)| > NEIGH_UPDATE_F_OVERRIDE_ISROUTER| > - (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0)); > + (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0), > + NDISC_NEIGHBOUR_ADVERTISEMENT, &ndopts); > > if ((old_flags & ~neigh->flags) & NTF_ROUTER) { > /* > @@ -1023,7 +1054,7 @@ static void ndisc_recv_rs(struct sk_buff *skb) > goto out; > > /* Parse ND options */ > - if (!ndisc_parse_options(rs_msg->opt, ndoptlen, &ndopts)) { > + if (!ndisc_parse_options(skb->dev, rs_msg->opt, ndoptlen, &ndopts)) { > ND_PRINTK(2, notice, "NS: invalid ND option, ignored\n"); > goto out; > } > @@ -1037,10 +1068,11 @@ static void ndisc_recv_rs(struct sk_buff *skb) > > neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1); > if (neigh) { > - neigh_update(neigh, lladdr, NUD_STALE, > + ndisc_update(skb->dev, neigh, lladdr, NUD_STALE, > NEIGH_UPDATE_F_WEAK_OVERRIDE| > NEIGH_UPDATE_F_OVERRIDE| > - NEIGH_UPDATE_F_OVERRIDE_ISROUTER); > + NEIGH_UPDATE_F_OVERRIDE_ISROUTER, > + NDISC_ROUTER_SOLICITATION, &ndopts); > neigh_release(neigh); > } > out: > @@ -1141,7 +1173,7 @@ static void ndisc_router_discovery(struct sk_buff *skb) > return; > } > > - if (!ndisc_parse_options(opt, optlen, &ndopts)) { > + if (!ndisc_parse_options(skb->dev, opt, optlen, &ndopts)) { > ND_PRINTK(2, warn, "RA: invalid ND options\n"); > return; > } > @@ -1335,11 +1367,12 @@ skip_linkparms: > goto out; > } > } > - neigh_update(neigh, lladdr, NUD_STALE, > + ndisc_update(skb->dev, neigh, lladdr, NUD_STALE, > NEIGH_UPDATE_F_WEAK_OVERRIDE| > NEIGH_UPDATE_F_OVERRIDE| > NEIGH_UPDATE_F_OVERRIDE_ISROUTER| > - NEIGH_UPDATE_F_ISROUTER); > + NEIGH_UPDATE_F_ISROUTER, > + NDISC_ROUTER_ADVERTISEMENT, &ndopts); > } > > if (!ipv6_accept_ra(in6_dev)) { > @@ -1427,7 +1460,8 @@ skip_routeinfo: > struct nd_opt_hdr *p; > for (p = ndopts.nd_useropts; > p; > - p = ndisc_next_useropt(p, ndopts.nd_useropts_end)) { > + p = ndisc_next_useropt(skb->dev, p, > + ndopts.nd_useropts_end)) { > ndisc_ra_useropt(skb, p); > } > } > @@ -1465,7 +1499,7 @@ static void ndisc_redirect_rcv(struct sk_buff *skb) > return; > } > > - if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) > + if (!ndisc_parse_options(skb->dev, msg->opt, ndoptlen, &ndopts)) > return; > > if (!ndopts.nd_opts_rh) { > @@ -1510,7 +1544,8 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) > struct dst_entry *dst; > struct flowi6 fl6; > int rd_len; > - u8 ha_buf[MAX_ADDR_LEN], *ha = NULL; > + u8 ha_buf[MAX_ADDR_LEN], *ha = NULL, > + ops_data_buf[NDISC_OPS_REDIRECT_DATA_SPACE], *ops_data = NULL; > int oif = l3mdev_fib_oif(dev); > bool ret; > > @@ -1569,7 +1604,9 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) > memcpy(ha_buf, neigh->ha, dev->addr_len); > read_unlock_bh(&neigh->lock); > ha = ha_buf; > - optlen += ndisc_opt_addr_space(dev); > + optlen += ndisc_redirect_opt_addr_space(dev, neigh, > + ops_data_buf, > + &ops_data); > } else > read_unlock_bh(&neigh->lock); > > @@ -1600,7 +1637,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) > */ > > if (ha) > - ndisc_fill_addr_option(buff, ND_OPT_TARGET_LL_ADDR, ha); > + ndisc_fill_redirect_addr_option(buff, ha, ops_data); > > /* > * build redirect option and copy skb over to the new packet. > diff --git a/net/ipv6/route.c b/net/ipv6/route.c > index c6ae6f9..af6f711 100644 > --- a/net/ipv6/route.c > +++ b/net/ipv6/route.c > @@ -2200,7 +2200,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu > * first-hop router for the specified ICMP Destination Address. > */ > > - if (!ndisc_parse_options(msg->opt, optlen, &ndopts)) { > + if (!ndisc_parse_options(skb->dev, msg->opt, optlen, &ndopts)) { > net_dbg_ratelimited("rt6_redirect: invalid ND options\n"); > return; > } > @@ -2235,12 +2235,12 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu > * We have finally decided to accept it. > */ > > - neigh_update(neigh, lladdr, NUD_STALE, > + ndisc_update(skb->dev, neigh, lladdr, NUD_STALE, > NEIGH_UPDATE_F_WEAK_OVERRIDE| > NEIGH_UPDATE_F_OVERRIDE| > (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER| > - NEIGH_UPDATE_F_ISROUTER)) > - ); > + NEIGH_UPDATE_F_ISROUTER)), > + NDISC_REDIRECT, &ndopts); > > nrt = ip6_rt_cache_alloc(rt, &msg->dest, NULL); > if (!nrt) > -- 吉藤英明 ミラクル・リナックス株式会社 技術本部 サポート部