diff mbox series

[v4,net-next,6/6] ila: Route notify

Message ID 20171215182800.10248-7-tom@quantonium.net
State Changes Requested, archived
Delegated to: David Miller
Headers show
Series net: ILA notification mechanism and fixes | expand

Commit Message

Tom Herbert Dec. 15, 2017, 6:28 p.m. UTC
Implement RTM notifications for ILA routers. This adds support to
ILA LWT to send a netlink RTM message when a router is uses.

THe ILA notify mechanism can be used in two contexts:

- On an ILA forwarding cache a route prefix can be configured to
  do an ILA notification. This method is used when address
  resolution needs to be done on an address.
- One an ILA router an ILA host route entry may include a
  noitification. The purpose of this is to get a notification
  to a userspace daemon to send and ILA redirect

Signed-off-by: Tom Herbert <tom@quantonium.net>
---
 include/uapi/linux/ila.h       |   2 +
 include/uapi/linux/rtnetlink.h |   8 +-
 net/ipv6/ila/ila_lwt.c         | 268 ++++++++++++++++++++++++++++-------------
 3 files changed, 193 insertions(+), 85 deletions(-)

Comments

kernel test robot Dec. 16, 2017, 10:18 p.m. UTC | #1
Hi Tom,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on net-next/master]

url:    https://github.com/0day-ci/linux/commits/Tom-Herbert/net-ILA-notification-mechanism-and-fixes/20171217-041013
reproduce:
        # apt-get install sparse
        make ARCH=x86_64 allmodconfig
        make C=1 CF=-D__CHECK_ENDIAN__


sparse warnings: (new ones prefixed by >>)


Please review and possibly fold the followup patch.

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
Roopa Prabhu Dec. 19, 2017, 4:28 p.m. UTC | #2
On Fri, Dec 15, 2017 at 10:28 AM, Tom Herbert <tom@quantonium.net> wrote:
> Implement RTM notifications for ILA routers. This adds support to
> ILA LWT to send a netlink RTM message when a router is uses.
>
> THe ILA notify mechanism can be used in two contexts:
>
> - On an ILA forwarding cache a route prefix can be configured to
>   do an ILA notification. This method is used when address
>   resolution needs to be done on an address.
> - One an ILA router an ILA host route entry may include a
>   noitification. The purpose of this is to get a notification
>   to a userspace daemon to send and ILA redirect
>
> Signed-off-by: Tom Herbert <tom@quantonium.net>
> ---
>  include/uapi/linux/ila.h       |   2 +
>  include/uapi/linux/rtnetlink.h |   8 +-
>  net/ipv6/ila/ila_lwt.c         | 268 ++++++++++++++++++++++++++++-------------
>  3 files changed, 193 insertions(+), 85 deletions(-)
>
> diff --git a/include/uapi/linux/ila.h b/include/uapi/linux/ila.h
> index db45d3e49a12..5675f3e71fac 100644
> --- a/include/uapi/linux/ila.h
> +++ b/include/uapi/linux/ila.h
> @@ -19,6 +19,8 @@ enum {
>         ILA_ATTR_CSUM_MODE,                     /* u8 */
>         ILA_ATTR_IDENT_TYPE,                    /* u8 */
>         ILA_ATTR_HOOK_TYPE,                     /* u8 */
> +       ILA_ATTR_NOTIFY_DST,                    /* flag */
> +       ILA_ATTR_NOTIFY_SRC,                    /* flag */
>
>         __ILA_ATTR_MAX,
>  };
> diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h
> index d8b5f80c2ea6..8d358a300d8a 100644
> --- a/include/uapi/linux/rtnetlink.h
> +++ b/include/uapi/linux/rtnetlink.h
> @@ -13,7 +13,8 @@
>   */
>  #define RTNL_FAMILY_IPMR               128
>  #define RTNL_FAMILY_IP6MR              129
> -#define RTNL_FAMILY_MAX                        129
> +#define RTNL_FAMILY_ILA                        130

I don't see this being used, unless I missed it.


> +#define RTNL_FAMILY_MAX                        130
>
>  /****
>   *             Routing/neighbour discovery messages.
> @@ -150,6 +151,9 @@ enum {
>         RTM_NEWCACHEREPORT = 96,
>  #define RTM_NEWCACHEREPORT RTM_NEWCACHEREPORT
>
> +       RTM_ADDR_RESOLVE = 98,
> +#define RTM_ADDR_RESOLVE RTM_ADDR_RESOLVE


Its nice that you are trying to add a generic resolver notify
format..., looks good...a few minor comments below for consistency:


your patch adds address resolve notifications with:
- generic notification msg type RTM_ADDR_RESOLVE with route msg format
- On the ILA specific RTNLGRP_ILA_NOTIFY multicast group

Other way to possibly format this for "generic" rtnl netlink resolver is:
- notification msg type RTM_NEWROUTE with route msg format (This is
for consistency: route msg format is always used with RTM_*ROUTE msg
types)
- route entry msg type RTNL_FAMILY_ILA (ie struct rtmsg-> rtm_family
to RTNL_FAMILY_ILA)
- generic address resolution netlink multicast group RTNLGRP_ADDR_RESOLVE_NOTIFY


OR   keep everything "specific" to ILA using the ILA genl channel msg
format and family


Besides that, since you are using the route msg format, you could
potentially s/ADDR_RESOLVE/ROUTE_RESOLVE/g everywhere above.



> +
>         __RTM_MAX,
>  #define RTM_MAX                (((__RTM_MAX + 3) & ~3) - 1)
>  };
> @@ -676,6 +680,8 @@ enum rtnetlink_groups {
>  #define RTNLGRP_IPV4_MROUTE_R  RTNLGRP_IPV4_MROUTE_R
>         RTNLGRP_IPV6_MROUTE_R,
>  #define RTNLGRP_IPV6_MROUTE_R  RTNLGRP_IPV6_MROUTE_R
> +       RTNLGRP_ILA_NOTIFY,
> +#define RTNLGRP_ILA_NOTIFY     RTNLGRP_ILA_NOTIFY
>         __RTNLGRP_MAX
>  };
>  #define RTNLGRP_MAX    (__RTNLGRP_MAX - 1)
> diff --git a/net/ipv6/ila/ila_lwt.c b/net/ipv6/ila/ila_lwt.c
> index 9f1e46a1468e..303c91e3bf76 100644
> --- a/net/ipv6/ila/ila_lwt.c
> +++ b/net/ipv6/ila/ila_lwt.c
> @@ -19,10 +19,15 @@
>  struct ila_lwt {
>         struct ila_params p;
>         struct dst_cache dst_cache;
> +       u8 hook_type;
>         u32 connected : 1;
> -       u32 lwt_output : 1;
> +       u32 xlat : 1;
> +       u32 notify : 2;
>  };
>
> +#define ILA_NOTIFY_DST 1
> +#define ILA_NOTIFY_SRC 2
> +
>  static inline struct ila_lwt *ila_lwt_lwtunnel(
>         struct lwtunnel_state *lwt)
>  {
> @@ -35,6 +40,67 @@ static inline struct ila_params *ila_params_lwtunnel(
>         return &ila_lwt_lwtunnel(lwt)->p;
>  }
>
> +static size_t ila_rslv_msgsize(void)
> +{
> +       size_t len =
> +               NLMSG_ALIGN(sizeof(struct rtmsg))
> +               + nla_total_size(16)     /* RTA_DST */
> +               + nla_total_size(16)     /* RTA_SRC */
> +               ;
> +
> +       return len;
> +}
> +
> +void ila_notify(struct net *net, struct sk_buff *skb, struct ila_lwt *lwt)
> +{
> +       struct ipv6hdr *ip6h = ipv6_hdr(skb);
> +       int flags = NLM_F_MULTI;
> +       struct sk_buff *nlskb;
> +       struct nlmsghdr *nlh;
> +       struct rtmsg *rtm;
> +       int err = 0;
> +
> +       /* Send ILA notification to user */
> +       nlskb = nlmsg_new(ila_rslv_msgsize(), GFP_KERNEL);
> +       if (!nlskb)
> +               return;
> +
> +       nlh = nlmsg_put(nlskb, 0, 0, RTM_ADDR_RESOLVE, sizeof(*rtm), flags);
> +       if (!nlh) {
> +               err = -EMSGSIZE;
> +               goto errout;
> +       }
> +
> +       rtm = nlmsg_data(nlh);
> +       rtm->rtm_family   = AF_INET6;
> +       rtm->rtm_dst_len  = 128;
> +       rtm->rtm_src_len  = 0;
> +       rtm->rtm_tos      = 0;
> +       rtm->rtm_table    = RT6_TABLE_UNSPEC;
> +       rtm->rtm_type     = RTN_UNICAST;
> +       rtm->rtm_scope    = RT_SCOPE_UNIVERSE;
> +
> +       if (((lwt->notify & ILA_NOTIFY_DST) &&
> +            nla_put_in6_addr(nlskb, RTA_DST, &ip6h->daddr)) ||
> +           ((lwt->notify & ILA_NOTIFY_SRC) &&
> +            nla_put_in6_addr(nlskb, RTA_SRC, &ip6h->saddr))) {
> +               nlmsg_cancel(nlskb, nlh);
> +               err = -EMSGSIZE;
> +               goto errout;
> +       }
> +
> +       nlmsg_end(nlskb, nlh);
> +
> +       rtnl_notify(nlskb, net, 0, RTNLGRP_ILA_NOTIFY, NULL, GFP_ATOMIC);
> +
> +       return;
> +
> +errout:
> +       kfree_skb(nlskb);
> +       WARN_ON(err == -EMSGSIZE);
> +       rtnl_set_sk_err(net, RTNLGRP_ILA_NOTIFY, err);
> +}
> +
>  static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb)
>  {
>         struct dst_entry *orig_dst = skb_dst(skb);
> @@ -46,11 +112,14 @@ static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb)
>         if (skb->protocol != htons(ETH_P_IPV6))
>                 goto drop;
>
> -       if (ilwt->lwt_output)
> +       if (ilwt->xlat)
>                 ila_update_ipv6_locator(skb,
>                                         ila_params_lwtunnel(orig_dst->lwtstate),
>                                         true);
>
> +       if (ilwt->notify)
> +               ila_notify(net, skb, ilwt);
> +
>         if (rt->rt6i_flags & (RTF_GATEWAY | RTF_CACHE)) {
>                 /* Already have a next hop address in route, no need for
>                  * dest cache route.
> @@ -106,11 +175,14 @@ static int ila_input(struct sk_buff *skb)
>         if (skb->protocol != htons(ETH_P_IPV6))
>                 goto drop;
>
> -       if (!ilwt->lwt_output)
> +       if (ilwt->xlat)
>                 ila_update_ipv6_locator(skb,
>                                         ila_params_lwtunnel(dst->lwtstate),
>                                         false);
>
> +       if (ilwt->notify)
> +               ila_notify(dev_net(dst->dev), skb, ilwt);
> +
>         return dst->lwtstate->orig_input(skb);
>
>  drop:
> @@ -123,6 +195,8 @@ static const struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = {
>         [ILA_ATTR_CSUM_MODE] = { .type = NLA_U8, },
>         [ILA_ATTR_IDENT_TYPE] = { .type = NLA_U8, },
>         [ILA_ATTR_HOOK_TYPE] = { .type = NLA_U8, },
> +       [ILA_ATTR_NOTIFY_DST] = { .type = NLA_FLAG },
> +       [ILA_ATTR_NOTIFY_SRC] = { .type = NLA_FLAG },
>  };
>
>  static int ila_build_state(struct net *net, struct nlattr *nla,
> @@ -130,64 +204,73 @@ static int ila_build_state(struct net *net, struct nlattr *nla,
>                            struct lwtunnel_state **ts,
>                            struct netlink_ext_ack *extack)
>  {
> -       struct ila_lwt *ilwt;
> -       struct ila_params *p;
> -       struct nlattr *tb[ILA_ATTR_MAX + 1];
> -       struct lwtunnel_state *newts;
>         const struct fib6_config *cfg6 = cfg;
> -       struct ila_addr *iaddr;
> +       struct ila_addr *iaddr = (struct ila_addr *)&cfg6->fc_dst;
>         u8 ident_type = ILA_ATYPE_USE_FORMAT;
>         u8 hook_type = ILA_HOOK_ROUTE_OUTPUT;
> +       struct nlattr *tb[ILA_ATTR_MAX + 1];
>         u8 csum_mode = ILA_CSUM_NO_ACTION;
> -       bool lwt_output = true;
> +       struct lwtunnel_state *newts;
> +       struct ila_lwt *ilwt;
> +       struct ila_params *p;
>         u8 eff_ident_type;
> -       int ret;
> +       int err;
>
>         if (family != AF_INET6)
>                 return -EINVAL;
>
> -       ret = nla_parse_nested(tb, ILA_ATTR_MAX, nla, ila_nl_policy, extack);
> -       if (ret < 0)
> -               return ret;
> +       err = nla_parse_nested(tb, ILA_ATTR_MAX, nla, ila_nl_policy, extack);
> +       if (err < 0)
> +               return err;
>
> -       if (!tb[ILA_ATTR_LOCATOR])
> -               return -EINVAL;
> +       if (tb[ILA_ATTR_LOCATOR]) {
> +               /* Doing ILA translation */
>
> -       iaddr = (struct ila_addr *)&cfg6->fc_dst;
> +               if (tb[ILA_ATTR_IDENT_TYPE])
> +                       ident_type = nla_get_u8(tb[ILA_ATTR_IDENT_TYPE]);
>
> -       if (tb[ILA_ATTR_IDENT_TYPE])
> -               ident_type = nla_get_u8(tb[ILA_ATTR_IDENT_TYPE]);
> +               if (ident_type == ILA_ATYPE_USE_FORMAT) {
> +                       /* Infer identifier type from type field in formatted
> +                        * identifier.
> +                        */
>
> -       if (ident_type == ILA_ATYPE_USE_FORMAT) {
> -               /* Infer identifier type from type field in formatted
> -                * identifier.
> -                */
> +                       if (cfg6->fc_dst_len < 8 *
> +                           sizeof(struct ila_locator) + 3) {
> +                               /* Need to have full locator and at least type
> +                                * field included in destination
> +                                */
> +                               return -EINVAL;
> +                       }
> +
> +                       eff_ident_type = iaddr->ident.type;
> +               } else {
> +                       eff_ident_type = ident_type;
> +               }
>
> -               if (cfg6->fc_dst_len < 8 * sizeof(struct ila_locator) + 3) {
> -                       /* Need to have full locator and at least type field
> -                        * included in destination
> -                        */
> +               switch (eff_ident_type) {
> +               case ILA_ATYPE_IID:
> +                       /* Don't allow ILA for IID type */
> +                       return -EINVAL;
> +               case ILA_ATYPE_LUID:
> +                       break;
> +               case ILA_ATYPE_VIRT_V4:
> +               case ILA_ATYPE_VIRT_UNI_V6:
> +               case ILA_ATYPE_VIRT_MULTI_V6:
> +               case ILA_ATYPE_NONLOCAL_ADDR:
> +                       /* These ILA formats are not supported yet. */
> +               default:
>                         return -EINVAL;
>                 }
>
> -               eff_ident_type = iaddr->ident.type;
> -       } else {
> -               eff_ident_type = ident_type;
> -       }
> +               csum_mode = nla_get_u8(tb[ILA_ATTR_CSUM_MODE]);
>
> -       switch (eff_ident_type) {
> -       case ILA_ATYPE_IID:
> -               /* Don't allow ILA for IID type */
> -               return -EINVAL;
> -       case ILA_ATYPE_LUID:
> -               break;
> -       case ILA_ATYPE_VIRT_V4:
> -       case ILA_ATYPE_VIRT_UNI_V6:
> -       case ILA_ATYPE_VIRT_MULTI_V6:
> -       case ILA_ATYPE_NONLOCAL_ADDR:
> -               /* These ILA formats are not supported yet. */
> -       default:
> -               return -EINVAL;
> +               if (csum_mode == ILA_CSUM_NEUTRAL_MAP &&
> +                   ila_csum_neutral_set(iaddr->ident)) {
> +                       /* Don't allow translation if checksum neutral bit is
> +                        * configured and it's set in the SIR address.
> +                        */
> +                       return -EINVAL;
> +               }
>         }
>
>         if (tb[ILA_ATTR_HOOK_TYPE])
> @@ -195,58 +278,62 @@ static int ila_build_state(struct net *net, struct nlattr *nla,
>
>         switch (hook_type) {
>         case ILA_HOOK_ROUTE_OUTPUT:
> -               lwt_output = true;
> -               break;
>         case ILA_HOOK_ROUTE_INPUT:
> -               lwt_output = false;
>                 break;
>         default:
>                 return -EINVAL;
>         }
>
> -       if (tb[ILA_ATTR_CSUM_MODE])
> -               csum_mode = nla_get_u8(tb[ILA_ATTR_CSUM_MODE]);
> -
> -       if (csum_mode == ILA_CSUM_NEUTRAL_MAP &&
> -           ila_csum_neutral_set(iaddr->ident)) {
> -               /* Don't allow translation if checksum neutral bit is
> -                * configured and it's set in the SIR address.
> -                */
> -               return -EINVAL;
> -       }
> -
>         newts = lwtunnel_state_alloc(sizeof(*ilwt));
>         if (!newts)
>                 return -ENOMEM;
>
>         ilwt = ila_lwt_lwtunnel(newts);
> -       ret = dst_cache_init(&ilwt->dst_cache, GFP_ATOMIC);
> -       if (ret) {
> +
> +       err = dst_cache_init(&ilwt->dst_cache, GFP_ATOMIC);
> +       if (err) {
>                 kfree(newts);
> -               return ret;
> +               return err;
>         }
>
> -       ilwt->lwt_output = !!lwt_output;
> +       newts->type = LWTUNNEL_ENCAP_ILA;
>
> -       p = ila_params_lwtunnel(newts);
> +       switch (hook_type) {
> +       case ILA_HOOK_ROUTE_OUTPUT:
> +               newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT;
> +               break;
> +       case ILA_HOOK_ROUTE_INPUT:
> +               newts->flags |= LWTUNNEL_STATE_INPUT_REDIRECT;
> +               break;
> +       }
>
> -       p->csum_mode = csum_mode;
> -       p->ident_type = ident_type;
> -       p->locator.v64 = (__force __be64)nla_get_u64(tb[ILA_ATTR_LOCATOR]);
> +       ilwt->hook_type = hook_type;
>
> -       /* Precompute checksum difference for translation since we
> -        * know both the old locator and the new one.
> -        */
> -       p->locator_match = iaddr->loc;
> +       if (tb[ILA_ATTR_NOTIFY_DST])
> +               ilwt->notify |= ILA_NOTIFY_DST;
>
> -       ila_init_saved_csum(p);
> +       if (tb[ILA_ATTR_NOTIFY_SRC])
> +               ilwt->notify |= ILA_NOTIFY_SRC;
>
> -       newts->type = LWTUNNEL_ENCAP_ILA;
> -       newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT |
> -                       LWTUNNEL_STATE_INPUT_REDIRECT;
> +       p = ila_params_lwtunnel(newts);
>
> -       if (cfg6->fc_dst_len == 8 * sizeof(struct in6_addr))
> -               ilwt->connected = 1;
> +       if (tb[ILA_ATTR_LOCATOR]) {
> +               ilwt->xlat = true;
> +               p->csum_mode = csum_mode;
> +               p->ident_type = ident_type;
> +               p->locator.v64 = (__force __be64)nla_get_u64(
> +                                                       tb[ILA_ATTR_LOCATOR]);
> +
> +               /* Precompute checksum difference for translation since we
> +                * know both the old locator and the new one.
> +                */
> +               p->locator_match = iaddr->loc;
> +
> +               ila_init_saved_csum(p);
> +
> +               if (cfg6->fc_dst_len == 8 * sizeof(struct in6_addr))
> +                       ilwt->connected = 1;
> +       }
>
>         *ts = newts;
>
> @@ -264,21 +351,32 @@ static int ila_fill_encap_info(struct sk_buff *skb,
>         struct ila_params *p = ila_params_lwtunnel(lwtstate);
>         struct ila_lwt *ilwt = ila_lwt_lwtunnel(lwtstate);
>
> -       if (nla_put_u64_64bit(skb, ILA_ATTR_LOCATOR, (__force u64)p->locator.v64,
> -                             ILA_ATTR_PAD))
> +       if (ilwt->xlat) {
> +               if (nla_put_u64_64bit(skb, ILA_ATTR_LOCATOR,
> +                                     (__force u64)p->locator.v64,
> +                                     ILA_ATTR_PAD))
>                 goto nla_put_failure;
>
> -       if (nla_put_u8(skb, ILA_ATTR_CSUM_MODE, (__force u8)p->csum_mode))
> -               goto nla_put_failure;
> +               if (nla_put_u8(skb, ILA_ATTR_CSUM_MODE,
> +                              (__force u8)p->csum_mode))
> +                       goto nla_put_failure;
>
> -       if (nla_put_u8(skb, ILA_ATTR_IDENT_TYPE, (__force u8)p->ident_type))
> -               goto nla_put_failure;
> +               if (nla_put_u8(skb, ILA_ATTR_IDENT_TYPE,
> +                              (__force u8)p->ident_type))
> +                       goto nla_put_failure;
> +       }
>
> -       if (nla_put_u8(skb, ILA_ATTR_HOOK_TYPE,
> -                      ilwt->lwt_output ? ILA_HOOK_ROUTE_OUTPUT :
> -                                         ILA_HOOK_ROUTE_INPUT))
> +       if (nla_put_u8(skb, ILA_ATTR_HOOK_TYPE, ilwt->hook_type))
>                 goto nla_put_failure;
>
> +       if (ilwt->notify & ILA_NOTIFY_DST)
> +               if (nla_put_flag(skb, ILA_ATTR_NOTIFY_DST))
> +                       goto nla_put_failure;
> +
> +       if (ilwt->notify & ILA_NOTIFY_SRC)
> +               if (nla_put_flag(skb, ILA_ATTR_NOTIFY_SRC))
> +                       goto nla_put_failure;
> +
>         return 0;
>
>  nla_put_failure:
> @@ -291,6 +389,8 @@ static int ila_encap_nlsize(struct lwtunnel_state *lwtstate)
>                nla_total_size(sizeof(u8)) +        /* ILA_ATTR_CSUM_MODE */
>                nla_total_size(sizeof(u8)) +        /* ILA_ATTR_IDENT_TYPE */
>                nla_total_size(sizeof(u8)) +        /* ILA_ATTR_HOOK_TYPE */
> +              nla_total_size(0) +                 /* ILA_ATTR_NOTIFY_DST */
> +              nla_total_size(0) +                 /* ILA_ATTR_NOTIFY_SRC */
>                0;
>  }
>
> --
> 2.11.0
>
diff mbox series

Patch

diff --git a/include/uapi/linux/ila.h b/include/uapi/linux/ila.h
index db45d3e49a12..5675f3e71fac 100644
--- a/include/uapi/linux/ila.h
+++ b/include/uapi/linux/ila.h
@@ -19,6 +19,8 @@  enum {
 	ILA_ATTR_CSUM_MODE,			/* u8 */
 	ILA_ATTR_IDENT_TYPE,			/* u8 */
 	ILA_ATTR_HOOK_TYPE,			/* u8 */
+	ILA_ATTR_NOTIFY_DST,			/* flag */
+	ILA_ATTR_NOTIFY_SRC,			/* flag */
 
 	__ILA_ATTR_MAX,
 };
diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h
index d8b5f80c2ea6..8d358a300d8a 100644
--- a/include/uapi/linux/rtnetlink.h
+++ b/include/uapi/linux/rtnetlink.h
@@ -13,7 +13,8 @@ 
  */
 #define RTNL_FAMILY_IPMR		128
 #define RTNL_FAMILY_IP6MR		129
-#define RTNL_FAMILY_MAX			129
+#define RTNL_FAMILY_ILA			130
+#define RTNL_FAMILY_MAX			130
 
 /****
  *		Routing/neighbour discovery messages.
@@ -150,6 +151,9 @@  enum {
 	RTM_NEWCACHEREPORT = 96,
 #define RTM_NEWCACHEREPORT RTM_NEWCACHEREPORT
 
+	RTM_ADDR_RESOLVE = 98,
+#define RTM_ADDR_RESOLVE RTM_ADDR_RESOLVE
+
 	__RTM_MAX,
 #define RTM_MAX		(((__RTM_MAX + 3) & ~3) - 1)
 };
@@ -676,6 +680,8 @@  enum rtnetlink_groups {
 #define RTNLGRP_IPV4_MROUTE_R	RTNLGRP_IPV4_MROUTE_R
 	RTNLGRP_IPV6_MROUTE_R,
 #define RTNLGRP_IPV6_MROUTE_R	RTNLGRP_IPV6_MROUTE_R
+	RTNLGRP_ILA_NOTIFY,
+#define RTNLGRP_ILA_NOTIFY	RTNLGRP_ILA_NOTIFY
 	__RTNLGRP_MAX
 };
 #define RTNLGRP_MAX	(__RTNLGRP_MAX - 1)
diff --git a/net/ipv6/ila/ila_lwt.c b/net/ipv6/ila/ila_lwt.c
index 9f1e46a1468e..303c91e3bf76 100644
--- a/net/ipv6/ila/ila_lwt.c
+++ b/net/ipv6/ila/ila_lwt.c
@@ -19,10 +19,15 @@ 
 struct ila_lwt {
 	struct ila_params p;
 	struct dst_cache dst_cache;
+	u8 hook_type;
 	u32 connected : 1;
-	u32 lwt_output : 1;
+	u32 xlat : 1;
+	u32 notify : 2;
 };
 
+#define ILA_NOTIFY_DST 1
+#define ILA_NOTIFY_SRC 2
+
 static inline struct ila_lwt *ila_lwt_lwtunnel(
 	struct lwtunnel_state *lwt)
 {
@@ -35,6 +40,67 @@  static inline struct ila_params *ila_params_lwtunnel(
 	return &ila_lwt_lwtunnel(lwt)->p;
 }
 
+static size_t ila_rslv_msgsize(void)
+{
+	size_t len =
+		NLMSG_ALIGN(sizeof(struct rtmsg))
+		+ nla_total_size(16)     /* RTA_DST */
+		+ nla_total_size(16)     /* RTA_SRC */
+		;
+
+	return len;
+}
+
+void ila_notify(struct net *net, struct sk_buff *skb, struct ila_lwt *lwt)
+{
+	struct ipv6hdr *ip6h = ipv6_hdr(skb);
+	int flags = NLM_F_MULTI;
+	struct sk_buff *nlskb;
+	struct nlmsghdr *nlh;
+	struct rtmsg *rtm;
+	int err = 0;
+
+	/* Send ILA notification to user */
+	nlskb = nlmsg_new(ila_rslv_msgsize(), GFP_KERNEL);
+	if (!nlskb)
+		return;
+
+	nlh = nlmsg_put(nlskb, 0, 0, RTM_ADDR_RESOLVE, sizeof(*rtm), flags);
+	if (!nlh) {
+		err = -EMSGSIZE;
+		goto errout;
+	}
+
+	rtm = nlmsg_data(nlh);
+	rtm->rtm_family   = AF_INET6;
+	rtm->rtm_dst_len  = 128;
+	rtm->rtm_src_len  = 0;
+	rtm->rtm_tos      = 0;
+	rtm->rtm_table    = RT6_TABLE_UNSPEC;
+	rtm->rtm_type     = RTN_UNICAST;
+	rtm->rtm_scope    = RT_SCOPE_UNIVERSE;
+
+	if (((lwt->notify & ILA_NOTIFY_DST) &&
+	     nla_put_in6_addr(nlskb, RTA_DST, &ip6h->daddr)) ||
+	    ((lwt->notify & ILA_NOTIFY_SRC) &&
+	     nla_put_in6_addr(nlskb, RTA_SRC, &ip6h->saddr))) {
+		nlmsg_cancel(nlskb, nlh);
+		err = -EMSGSIZE;
+		goto errout;
+	}
+
+	nlmsg_end(nlskb, nlh);
+
+	rtnl_notify(nlskb, net, 0, RTNLGRP_ILA_NOTIFY, NULL, GFP_ATOMIC);
+
+	return;
+
+errout:
+	kfree_skb(nlskb);
+	WARN_ON(err == -EMSGSIZE);
+	rtnl_set_sk_err(net, RTNLGRP_ILA_NOTIFY, err);
+}
+
 static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
 	struct dst_entry *orig_dst = skb_dst(skb);
@@ -46,11 +112,14 @@  static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 	if (skb->protocol != htons(ETH_P_IPV6))
 		goto drop;
 
-	if (ilwt->lwt_output)
+	if (ilwt->xlat)
 		ila_update_ipv6_locator(skb,
 					ila_params_lwtunnel(orig_dst->lwtstate),
 					true);
 
+	if (ilwt->notify)
+		ila_notify(net, skb, ilwt);
+
 	if (rt->rt6i_flags & (RTF_GATEWAY | RTF_CACHE)) {
 		/* Already have a next hop address in route, no need for
 		 * dest cache route.
@@ -106,11 +175,14 @@  static int ila_input(struct sk_buff *skb)
 	if (skb->protocol != htons(ETH_P_IPV6))
 		goto drop;
 
-	if (!ilwt->lwt_output)
+	if (ilwt->xlat)
 		ila_update_ipv6_locator(skb,
 					ila_params_lwtunnel(dst->lwtstate),
 					false);
 
+	if (ilwt->notify)
+		ila_notify(dev_net(dst->dev), skb, ilwt);
+
 	return dst->lwtstate->orig_input(skb);
 
 drop:
@@ -123,6 +195,8 @@  static const struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = {
 	[ILA_ATTR_CSUM_MODE] = { .type = NLA_U8, },
 	[ILA_ATTR_IDENT_TYPE] = { .type = NLA_U8, },
 	[ILA_ATTR_HOOK_TYPE] = { .type = NLA_U8, },
+	[ILA_ATTR_NOTIFY_DST] = { .type = NLA_FLAG },
+	[ILA_ATTR_NOTIFY_SRC] = { .type = NLA_FLAG },
 };
 
 static int ila_build_state(struct net *net, struct nlattr *nla,
@@ -130,64 +204,73 @@  static int ila_build_state(struct net *net, struct nlattr *nla,
 			   struct lwtunnel_state **ts,
 			   struct netlink_ext_ack *extack)
 {
-	struct ila_lwt *ilwt;
-	struct ila_params *p;
-	struct nlattr *tb[ILA_ATTR_MAX + 1];
-	struct lwtunnel_state *newts;
 	const struct fib6_config *cfg6 = cfg;
-	struct ila_addr *iaddr;
+	struct ila_addr *iaddr = (struct ila_addr *)&cfg6->fc_dst;
 	u8 ident_type = ILA_ATYPE_USE_FORMAT;
 	u8 hook_type = ILA_HOOK_ROUTE_OUTPUT;
+	struct nlattr *tb[ILA_ATTR_MAX + 1];
 	u8 csum_mode = ILA_CSUM_NO_ACTION;
-	bool lwt_output = true;
+	struct lwtunnel_state *newts;
+	struct ila_lwt *ilwt;
+	struct ila_params *p;
 	u8 eff_ident_type;
-	int ret;
+	int err;
 
 	if (family != AF_INET6)
 		return -EINVAL;
 
-	ret = nla_parse_nested(tb, ILA_ATTR_MAX, nla, ila_nl_policy, extack);
-	if (ret < 0)
-		return ret;
+	err = nla_parse_nested(tb, ILA_ATTR_MAX, nla, ila_nl_policy, extack);
+	if (err < 0)
+		return err;
 
-	if (!tb[ILA_ATTR_LOCATOR])
-		return -EINVAL;
+	if (tb[ILA_ATTR_LOCATOR]) {
+		/* Doing ILA translation */
 
-	iaddr = (struct ila_addr *)&cfg6->fc_dst;
+		if (tb[ILA_ATTR_IDENT_TYPE])
+			ident_type = nla_get_u8(tb[ILA_ATTR_IDENT_TYPE]);
 
-	if (tb[ILA_ATTR_IDENT_TYPE])
-		ident_type = nla_get_u8(tb[ILA_ATTR_IDENT_TYPE]);
+		if (ident_type == ILA_ATYPE_USE_FORMAT) {
+			/* Infer identifier type from type field in formatted
+			 * identifier.
+			 */
 
-	if (ident_type == ILA_ATYPE_USE_FORMAT) {
-		/* Infer identifier type from type field in formatted
-		 * identifier.
-		 */
+			if (cfg6->fc_dst_len < 8 *
+			    sizeof(struct ila_locator) + 3) {
+				/* Need to have full locator and at least type
+				 * field included in destination
+				 */
+				return -EINVAL;
+			}
+
+			eff_ident_type = iaddr->ident.type;
+		} else {
+			eff_ident_type = ident_type;
+		}
 
-		if (cfg6->fc_dst_len < 8 * sizeof(struct ila_locator) + 3) {
-			/* Need to have full locator and at least type field
-			 * included in destination
-			 */
+		switch (eff_ident_type) {
+		case ILA_ATYPE_IID:
+			/* Don't allow ILA for IID type */
+			return -EINVAL;
+		case ILA_ATYPE_LUID:
+			break;
+		case ILA_ATYPE_VIRT_V4:
+		case ILA_ATYPE_VIRT_UNI_V6:
+		case ILA_ATYPE_VIRT_MULTI_V6:
+		case ILA_ATYPE_NONLOCAL_ADDR:
+			/* These ILA formats are not supported yet. */
+		default:
 			return -EINVAL;
 		}
 
-		eff_ident_type = iaddr->ident.type;
-	} else {
-		eff_ident_type = ident_type;
-	}
+		csum_mode = nla_get_u8(tb[ILA_ATTR_CSUM_MODE]);
 
-	switch (eff_ident_type) {
-	case ILA_ATYPE_IID:
-		/* Don't allow ILA for IID type */
-		return -EINVAL;
-	case ILA_ATYPE_LUID:
-		break;
-	case ILA_ATYPE_VIRT_V4:
-	case ILA_ATYPE_VIRT_UNI_V6:
-	case ILA_ATYPE_VIRT_MULTI_V6:
-	case ILA_ATYPE_NONLOCAL_ADDR:
-		/* These ILA formats are not supported yet. */
-	default:
-		return -EINVAL;
+		if (csum_mode == ILA_CSUM_NEUTRAL_MAP &&
+		    ila_csum_neutral_set(iaddr->ident)) {
+			/* Don't allow translation if checksum neutral bit is
+			 * configured and it's set in the SIR address.
+			 */
+			return -EINVAL;
+		}
 	}
 
 	if (tb[ILA_ATTR_HOOK_TYPE])
@@ -195,58 +278,62 @@  static int ila_build_state(struct net *net, struct nlattr *nla,
 
 	switch (hook_type) {
 	case ILA_HOOK_ROUTE_OUTPUT:
-		lwt_output = true;
-		break;
 	case ILA_HOOK_ROUTE_INPUT:
-		lwt_output = false;
 		break;
 	default:
 		return -EINVAL;
 	}
 
-	if (tb[ILA_ATTR_CSUM_MODE])
-		csum_mode = nla_get_u8(tb[ILA_ATTR_CSUM_MODE]);
-
-	if (csum_mode == ILA_CSUM_NEUTRAL_MAP &&
-	    ila_csum_neutral_set(iaddr->ident)) {
-		/* Don't allow translation if checksum neutral bit is
-		 * configured and it's set in the SIR address.
-		 */
-		return -EINVAL;
-	}
-
 	newts = lwtunnel_state_alloc(sizeof(*ilwt));
 	if (!newts)
 		return -ENOMEM;
 
 	ilwt = ila_lwt_lwtunnel(newts);
-	ret = dst_cache_init(&ilwt->dst_cache, GFP_ATOMIC);
-	if (ret) {
+
+	err = dst_cache_init(&ilwt->dst_cache, GFP_ATOMIC);
+	if (err) {
 		kfree(newts);
-		return ret;
+		return err;
 	}
 
-	ilwt->lwt_output = !!lwt_output;
+	newts->type = LWTUNNEL_ENCAP_ILA;
 
-	p = ila_params_lwtunnel(newts);
+	switch (hook_type) {
+	case ILA_HOOK_ROUTE_OUTPUT:
+		newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT;
+		break;
+	case ILA_HOOK_ROUTE_INPUT:
+		newts->flags |= LWTUNNEL_STATE_INPUT_REDIRECT;
+		break;
+	}
 
-	p->csum_mode = csum_mode;
-	p->ident_type = ident_type;
-	p->locator.v64 = (__force __be64)nla_get_u64(tb[ILA_ATTR_LOCATOR]);
+	ilwt->hook_type = hook_type;
 
-	/* Precompute checksum difference for translation since we
-	 * know both the old locator and the new one.
-	 */
-	p->locator_match = iaddr->loc;
+	if (tb[ILA_ATTR_NOTIFY_DST])
+		ilwt->notify |= ILA_NOTIFY_DST;
 
-	ila_init_saved_csum(p);
+	if (tb[ILA_ATTR_NOTIFY_SRC])
+		ilwt->notify |= ILA_NOTIFY_SRC;
 
-	newts->type = LWTUNNEL_ENCAP_ILA;
-	newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT |
-			LWTUNNEL_STATE_INPUT_REDIRECT;
+	p = ila_params_lwtunnel(newts);
 
-	if (cfg6->fc_dst_len == 8 * sizeof(struct in6_addr))
-		ilwt->connected = 1;
+	if (tb[ILA_ATTR_LOCATOR]) {
+		ilwt->xlat = true;
+		p->csum_mode = csum_mode;
+		p->ident_type = ident_type;
+		p->locator.v64 = (__force __be64)nla_get_u64(
+							tb[ILA_ATTR_LOCATOR]);
+
+		/* Precompute checksum difference for translation since we
+		 * know both the old locator and the new one.
+		 */
+		p->locator_match = iaddr->loc;
+
+		ila_init_saved_csum(p);
+
+		if (cfg6->fc_dst_len == 8 * sizeof(struct in6_addr))
+			ilwt->connected = 1;
+	}
 
 	*ts = newts;
 
@@ -264,21 +351,32 @@  static int ila_fill_encap_info(struct sk_buff *skb,
 	struct ila_params *p = ila_params_lwtunnel(lwtstate);
 	struct ila_lwt *ilwt = ila_lwt_lwtunnel(lwtstate);
 
-	if (nla_put_u64_64bit(skb, ILA_ATTR_LOCATOR, (__force u64)p->locator.v64,
-			      ILA_ATTR_PAD))
+	if (ilwt->xlat) {
+		if (nla_put_u64_64bit(skb, ILA_ATTR_LOCATOR,
+				      (__force u64)p->locator.v64,
+				      ILA_ATTR_PAD))
 		goto nla_put_failure;
 
-	if (nla_put_u8(skb, ILA_ATTR_CSUM_MODE, (__force u8)p->csum_mode))
-		goto nla_put_failure;
+		if (nla_put_u8(skb, ILA_ATTR_CSUM_MODE,
+			       (__force u8)p->csum_mode))
+			goto nla_put_failure;
 
-	if (nla_put_u8(skb, ILA_ATTR_IDENT_TYPE, (__force u8)p->ident_type))
-		goto nla_put_failure;
+		if (nla_put_u8(skb, ILA_ATTR_IDENT_TYPE,
+			       (__force u8)p->ident_type))
+			goto nla_put_failure;
+	}
 
-	if (nla_put_u8(skb, ILA_ATTR_HOOK_TYPE,
-		       ilwt->lwt_output ? ILA_HOOK_ROUTE_OUTPUT :
-					  ILA_HOOK_ROUTE_INPUT))
+	if (nla_put_u8(skb, ILA_ATTR_HOOK_TYPE, ilwt->hook_type))
 		goto nla_put_failure;
 
+	if (ilwt->notify & ILA_NOTIFY_DST)
+		if (nla_put_flag(skb, ILA_ATTR_NOTIFY_DST))
+			goto nla_put_failure;
+
+	if (ilwt->notify & ILA_NOTIFY_SRC)
+		if (nla_put_flag(skb, ILA_ATTR_NOTIFY_SRC))
+			goto nla_put_failure;
+
 	return 0;
 
 nla_put_failure:
@@ -291,6 +389,8 @@  static int ila_encap_nlsize(struct lwtunnel_state *lwtstate)
 	       nla_total_size(sizeof(u8)) +        /* ILA_ATTR_CSUM_MODE */
 	       nla_total_size(sizeof(u8)) +        /* ILA_ATTR_IDENT_TYPE */
 	       nla_total_size(sizeof(u8)) +        /* ILA_ATTR_HOOK_TYPE */
+	       nla_total_size(0) +		   /* ILA_ATTR_NOTIFY_DST */
+	       nla_total_size(0) +		   /* ILA_ATTR_NOTIFY_SRC */
 	       0;
 }