Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[platform/kernel/linux-rpi.git] / net / ipv6 / route.c
index d155864..e476f01 100644 (file)
 #include <net/tcp.h>
 #include <linux/rtnetlink.h>
 #include <net/dst.h>
+#include <net/dst_metadata.h>
 #include <net/xfrm.h>
 #include <net/netevent.h>
 #include <net/netlink.h>
 #include <net/nexthop.h>
+#include <net/lwtunnel.h>
+#include <net/ip_tunnels.h>
 
 #include <asm/uaccess.h>
 
@@ -535,13 +538,14 @@ static void rt6_probe_deferred(struct work_struct *w)
                container_of(w, struct __rt6_probe_work, work);
 
        addrconf_addr_solict_mult(&work->target, &mcaddr);
-       ndisc_send_ns(work->dev, NULL, &work->target, &mcaddr, NULL);
+       ndisc_send_ns(work->dev, NULL, &work->target, &mcaddr, NULL, NULL);
        dev_put(work->dev);
        kfree(work);
 }
 
 static void rt6_probe(struct rt6_info *rt)
 {
+       struct __rt6_probe_work *work;
        struct neighbour *neigh;
        /*
         * Okay, this does not seem to be appropriate
@@ -556,34 +560,33 @@ static void rt6_probe(struct rt6_info *rt)
        rcu_read_lock_bh();
        neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
        if (neigh) {
-               write_lock(&neigh->lock);
                if (neigh->nud_state & NUD_VALID)
                        goto out;
-       }
-
-       if (!neigh ||
-           time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
-               struct __rt6_probe_work *work;
 
+               work = NULL;
+               write_lock(&neigh->lock);
+               if (!(neigh->nud_state & NUD_VALID) &&
+                   time_after(jiffies,
+                              neigh->updated +
+                              rt->rt6i_idev->cnf.rtr_probe_interval)) {
+                       work = kmalloc(sizeof(*work), GFP_ATOMIC);
+                       if (work)
+                               __neigh_set_probe_once(neigh);
+               }
+               write_unlock(&neigh->lock);
+       } else {
                work = kmalloc(sizeof(*work), GFP_ATOMIC);
+       }
 
-               if (neigh && work)
-                       __neigh_set_probe_once(neigh);
-
-               if (neigh)
-                       write_unlock(&neigh->lock);
+       if (work) {
+               INIT_WORK(&work->work, rt6_probe_deferred);
+               work->target = rt->rt6i_gateway;
+               dev_hold(rt->dst.dev);
+               work->dev = rt->dst.dev;
+               schedule_work(&work->work);
+       }
 
-               if (work) {
-                       INIT_WORK(&work->work, rt6_probe_deferred);
-                       work->target = rt->rt6i_gateway;
-                       dev_hold(rt->dst.dev);
-                       work->dev = rt->dst.dev;
-                       schedule_work(&work->work);
-               }
-       } else {
 out:
-               write_unlock(&neigh->lock);
-       }
        rcu_read_unlock_bh();
 }
 #else
@@ -662,6 +665,12 @@ static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
 {
        int m;
        bool match_do_rr = false;
+       struct inet6_dev *idev = rt->rt6i_idev;
+       struct net_device *dev = rt->dst.dev;
+
+       if (dev && !netif_carrier_ok(dev) &&
+           idev->cnf.ignore_routes_with_linkdown)
+               goto out;
 
        if (rt6_check_expired(rt))
                goto out;
@@ -1154,6 +1163,7 @@ void ip6_route_input(struct sk_buff *skb)
        const struct ipv6hdr *iph = ipv6_hdr(skb);
        struct net *net = dev_net(skb->dev);
        int flags = RT6_LOOKUP_F_HAS_SADDR;
+       struct ip_tunnel_info *tun_info;
        struct flowi6 fl6 = {
                .flowi6_iif = skb->dev->ifindex,
                .daddr = iph->daddr,
@@ -1163,6 +1173,10 @@ void ip6_route_input(struct sk_buff *skb)
                .flowi6_proto = iph->nexthdr,
        };
 
+       tun_info = skb_tunnel_info(skb);
+       if (tun_info && tun_info->mode == IP_TUNNEL_INFO_RX)
+               fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
+       skb_dst_drop(skb);
        skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
 }
 
@@ -1801,6 +1815,24 @@ int ip6_route_add(struct fib6_config *cfg)
 
        rt->dst.output = ip6_output;
 
+       if (cfg->fc_encap) {
+               struct lwtunnel_state *lwtstate;
+
+               err = lwtunnel_build_state(dev, cfg->fc_encap_type,
+                                          cfg->fc_encap, &lwtstate);
+               if (err)
+                       goto out;
+               rt->dst.lwtstate = lwtstate_get(lwtstate);
+               if (lwtunnel_output_redirect(rt->dst.lwtstate)) {
+                       rt->dst.lwtstate->orig_output = rt->dst.output;
+                       rt->dst.output = lwtunnel_output;
+               }
+               if (lwtunnel_input_redirect(rt->dst.lwtstate)) {
+                       rt->dst.lwtstate->orig_input = rt->dst.input;
+                       rt->dst.input = lwtunnel_input;
+               }
+       }
+
        ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
        rt->rt6i_dst.plen = cfg->fc_dst_len;
        if (rt->rt6i_dst.plen == 128)
@@ -2180,6 +2212,7 @@ static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort)
 #endif
        rt->rt6i_prefsrc = ort->rt6i_prefsrc;
        rt->rt6i_table = ort->rt6i_table;
+       rt->dst.lwtstate = lwtstate_get(ort->dst.lwtstate);
 }
 
 #ifdef CONFIG_IPV6_ROUTE_INFO
@@ -2628,6 +2661,8 @@ static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
        [RTA_METRICS]           = { .type = NLA_NESTED },
        [RTA_MULTIPATH]         = { .len = sizeof(struct rtnexthop) },
        [RTA_PREF]              = { .type = NLA_U8 },
+       [RTA_ENCAP_TYPE]        = { .type = NLA_U16 },
+       [RTA_ENCAP]             = { .type = NLA_NESTED },
 };
 
 static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
@@ -2722,6 +2757,12 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
                cfg->fc_flags |= RTF_PREF(pref);
        }
 
+       if (tb[RTA_ENCAP])
+               cfg->fc_encap = tb[RTA_ENCAP];
+
+       if (tb[RTA_ENCAP_TYPE])
+               cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
+
        err = 0;
 errout:
        return err;
@@ -2754,6 +2795,10 @@ beginning:
                                r_cfg.fc_gateway = nla_get_in6_addr(nla);
                                r_cfg.fc_flags |= RTF_GATEWAY;
                        }
+                       r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP);
+                       nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
+                       if (nla)
+                               r_cfg.fc_encap_type = nla_get_u16(nla);
                }
                err = add ? ip6_route_add(&r_cfg) : ip6_route_del(&r_cfg);
                if (err) {
@@ -2816,7 +2861,7 @@ static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh)
                return ip6_route_add(&cfg);
 }
 
-static inline size_t rt6_nlmsg_size(void)
+static inline size_t rt6_nlmsg_size(struct rt6_info *rt)
 {
        return NLMSG_ALIGN(sizeof(struct rtmsg))
               + nla_total_size(16) /* RTA_SRC */
@@ -2830,7 +2875,8 @@ static inline size_t rt6_nlmsg_size(void)
               + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
               + nla_total_size(sizeof(struct rta_cacheinfo))
               + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
-              + nla_total_size(1); /* RTA_PREF */
+              + nla_total_size(1) /* RTA_PREF */
+              + lwtunnel_get_encap_size(rt->dst.lwtstate);
 }
 
 static int rt6_fill_node(struct net *net,
@@ -2891,6 +2937,11 @@ static int rt6_fill_node(struct net *net,
        else
                rtm->rtm_type = RTN_UNICAST;
        rtm->rtm_flags = 0;
+       if (!netif_carrier_ok(rt->dst.dev)) {
+               rtm->rtm_flags |= RTNH_F_LINKDOWN;
+               if (rt->rt6i_idev->cnf.ignore_routes_with_linkdown)
+                       rtm->rtm_flags |= RTNH_F_DEAD;
+       }
        rtm->rtm_scope = RT_SCOPE_UNIVERSE;
        rtm->rtm_protocol = rt->rt6i_protocol;
        if (rt->rt6i_flags & RTF_DYNAMIC)
@@ -2978,6 +3029,8 @@ static int rt6_fill_node(struct net *net,
        if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags)))
                goto nla_put_failure;
 
+       lwtunnel_fill_encap(skb, rt->dst.lwtstate);
+
        nlmsg_end(skb, nlh);
        return 0;
 
@@ -3104,7 +3157,7 @@ void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
        err = -ENOBUFS;
        seq = info->nlh ? info->nlh->nlmsg_seq : 0;
 
-       skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
+       skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
        if (!skb)
                goto errout;