vti6: Don't unregister pernet ops twice on init errors
[deliverable/linux.git] / net / ipv6 / ip6_vti.c
index 226854a3c3928570462533fd0b030306949d6920..a51100379f4af85aa6376baf69aa34c30a284760 100644 (file)
@@ -376,41 +376,56 @@ vti6_addr_conflict(const struct ip6_tnl *t, const struct ipv6hdr *hdr)
        return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr);
 }
 
+static bool vti6_state_check(const struct xfrm_state *x,
+                            const struct in6_addr *dst,
+                            const struct in6_addr *src)
+{
+       xfrm_address_t *daddr = (xfrm_address_t *)dst;
+       xfrm_address_t *saddr = (xfrm_address_t *)src;
+
+       /* if there is no transform then this tunnel is not functional.
+        * Or if the xfrm is not mode tunnel.
+        */
+       if (!x || x->props.mode != XFRM_MODE_TUNNEL ||
+           x->props.family != AF_INET6)
+               return false;
+
+       if (ipv6_addr_any(dst))
+               return xfrm_addr_equal(saddr, &x->props.saddr, AF_INET6);
+
+       if (!xfrm_state_addr_check(x, daddr, saddr, AF_INET6))
+               return false;
+
+       return true;
+}
+
 /**
  * vti6_xmit - send a packet
  *   @skb: the outgoing socket buffer
  *   @dev: the outgoing tunnel device
+ *   @fl: the flow informations for the xfrm_lookup
  **/
-static int vti6_xmit(struct sk_buff *skb, struct net_device *dev)
+static int
+vti6_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl)
 {
        struct ip6_tnl *t = netdev_priv(dev);
        struct net_device_stats *stats = &t->dev->stats;
        struct dst_entry *dst = skb_dst(skb);
-       struct flowi fl;
-       struct ipv6hdr *ipv6h = ipv6_hdr(skb);
        struct net_device *tdev;
        int err = -1;
 
-       if ((t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) ||
-           !ip6_tnl_xmit_ctl(t) || vti6_addr_conflict(t, ipv6h))
-               return err;
-
-       memset(&fl, 0, sizeof(fl));
-       skb->mark = be32_to_cpu(t->parms.o_key);
-       xfrm_decode_session(skb, &fl, AF_INET6);
-
        if (!dst)
                goto tx_err_link_failure;
 
        dst_hold(dst);
-       dst = xfrm_lookup(t->net, dst, &fl, NULL, 0);
+       dst = xfrm_lookup(t->net, dst, fl, NULL, 0);
        if (IS_ERR(dst)) {
                err = PTR_ERR(dst);
                dst = NULL;
                goto tx_err_link_failure;
        }
 
-       if (!dst->xfrm || dst->xfrm->props.mode != XFRM_MODE_TUNNEL)
+       if (!vti6_state_check(dst->xfrm, &t->parms.raddr, &t->parms.laddr))
                goto tx_err_link_failure;
 
        tdev = dst->dev;
@@ -422,12 +437,22 @@ static int vti6_xmit(struct sk_buff *skb, struct net_device *dev)
                goto tx_err_dst_release;
        }
 
-       memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
        skb_scrub_packet(skb, !net_eq(t->net, dev_net(dev)));
        skb_dst_set(skb, dst);
        skb->dev = skb_dst(skb)->dev;
 
-       ip6tunnel_xmit(skb, dev);
+       err = dst_output(skb);
+       if (net_xmit_eval(err) == 0) {
+               struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats);
+
+               u64_stats_update_begin(&tstats->syncp);
+               tstats->tx_bytes += skb->len;
+               tstats->tx_packets++;
+               u64_stats_update_end(&tstats->syncp);
+       } else {
+               stats->tx_errors++;
+               stats->tx_aborted_errors++;
+       }
 
        return 0;
 tx_err_link_failure:
@@ -443,16 +468,33 @@ vti6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct ip6_tnl *t = netdev_priv(dev);
        struct net_device_stats *stats = &t->dev->stats;
+       struct ipv6hdr *ipv6h;
+       struct flowi fl;
        int ret;
 
+       memset(&fl, 0, sizeof(fl));
+       skb->mark = be32_to_cpu(t->parms.o_key);
+
        switch (skb->protocol) {
        case htons(ETH_P_IPV6):
-               ret = vti6_xmit(skb, dev);
+               ipv6h = ipv6_hdr(skb);
+
+               if ((t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) ||
+                   !ip6_tnl_xmit_ctl(t) || vti6_addr_conflict(t, ipv6h))
+                       goto tx_err;
+
+               xfrm_decode_session(skb, &fl, AF_INET6);
+               memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
+               break;
+       case htons(ETH_P_IP):
+               xfrm_decode_session(skb, &fl, AF_INET);
+               memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
                break;
        default:
                goto tx_err;
        }
 
+       ret = vti6_xmit(skb, dev, &fl);
        if (ret < 0)
                goto tx_err;
 
@@ -761,7 +803,6 @@ static void vti6_dev_setup(struct net_device *dev)
        t = netdev_priv(dev);
        dev->flags |= IFF_NOARP;
        dev->addr_len = sizeof(struct in6_addr);
-       dev->features |= NETIF_F_NETNS_LOCAL;
        dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
 }
 
@@ -1056,7 +1097,6 @@ static int __init vti6_tunnel_init(void)
 
        err = xfrm6_protocol_register(&vti_esp6_protocol, IPPROTO_ESP);
        if (err < 0) {
-               unregister_pernet_device(&vti6_net_ops);
                pr_err("%s: can't register vti6 protocol\n", __func__);
 
                goto out;
@@ -1065,7 +1105,6 @@ static int __init vti6_tunnel_init(void)
        err = xfrm6_protocol_register(&vti_ah6_protocol, IPPROTO_AH);
        if (err < 0) {
                xfrm6_protocol_deregister(&vti_esp6_protocol, IPPROTO_ESP);
-               unregister_pernet_device(&vti6_net_ops);
                pr_err("%s: can't register vti6 protocol\n", __func__);
 
                goto out;
@@ -1075,7 +1114,6 @@ static int __init vti6_tunnel_init(void)
        if (err < 0) {
                xfrm6_protocol_deregister(&vti_ah6_protocol, IPPROTO_AH);
                xfrm6_protocol_deregister(&vti_esp6_protocol, IPPROTO_ESP);
-               unregister_pernet_device(&vti6_net_ops);
                pr_err("%s: can't register vti6 protocol\n", __func__);
 
                goto out;
This page took 0.029832 seconds and 5 git commands to generate.