vti4: Support inter address family tunneling.
authorSteffen Klassert <steffen.klassert@secunet.com>
Fri, 21 Feb 2014 07:41:10 +0000 (08:41 +0100)
committerSteffen Klassert <steffen.klassert@secunet.com>
Tue, 25 Feb 2014 06:04:19 +0000 (07:04 +0100)
With this patch we can tunnel ipv6 traffic via a vti4
interface. A vti4 interface can now have an ipv6 address
and ipv6 traffic can be routed via a vti4 interface.
The resulting traffic is xfrm transformed and tunneled
throuhg ipv4 if matching IPsec policies and states are
present.

Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
net/ipv4/ip_vti.c

index 0dc341ddd1aa1170855cd55c46d3fe2ee1c239e0..9369b7c03f1c3ff1053e5bfe2f1728fd46d16886 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/init.h>
 #include <linux/netfilter_ipv4.h>
 #include <linux/if_ether.h>
+#include <linux/icmpv6.h>
 
 #include <net/sock.h>
 #include <net/ip.h>
@@ -122,31 +123,21 @@ static int vti_rcv_cb(struct sk_buff *skb, int err)
        return 0;
 }
 
-/* This function assumes it is being called from dev_queue_xmit()
- * and that skb is filled properly by that function.
- */
-static netdev_tx_t vti_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev,
+                           struct flowi *fl)
 {
        struct ip_tunnel *tunnel = netdev_priv(dev);
        struct dst_entry *dst = skb_dst(skb);
        struct net_device *tdev;        /* Device to other host */
-       struct flowi fl;
        int err;
 
-       if (skb->protocol != htons(ETH_P_IP))
-               goto tx_error;
-
-       memset(&fl, 0, sizeof(fl));
-       skb->mark = be32_to_cpu(tunnel->parms.o_key);
-       xfrm_decode_session(skb, &fl, AF_INET);
-
        if (!dst) {
                dev->stats.tx_carrier_errors++;
                goto tx_error_icmp;
        }
 
        dst_hold(dst);
-       dst = xfrm_lookup(tunnel->net, dst, &fl, NULL, 0);
+       dst = xfrm_lookup(tunnel->net, dst, fl, NULL, 0);
        if (IS_ERR(dst)) {
                dev->stats.tx_carrier_errors++;
                goto tx_error_icmp;
@@ -178,7 +169,6 @@ static netdev_tx_t vti_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
                        tunnel->err_count = 0;
        }
 
-       memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
        skb_scrub_packet(skb, !net_eq(tunnel->net, dev_net(dev)));
        skb_dst_set(skb, dst);
        skb->dev = skb_dst(skb)->dev;
@@ -197,6 +187,36 @@ tx_error:
        return NETDEV_TX_OK;
 }
 
+/* This function assumes it is being called from dev_queue_xmit()
+ * and that skb is filled properly by that function.
+ */
+static netdev_tx_t vti_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct ip_tunnel *tunnel = netdev_priv(dev);
+       struct flowi fl;
+
+       memset(&fl, 0, sizeof(fl));
+
+       skb->mark = be32_to_cpu(tunnel->parms.o_key);
+
+       switch (skb->protocol) {
+       case htons(ETH_P_IP):
+               xfrm_decode_session(skb, &fl, AF_INET);
+               memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
+               break;
+       case htons(ETH_P_IPV6):
+               xfrm_decode_session(skb, &fl, AF_INET6);
+               memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
+               break;
+       default:
+               dev->stats.tx_errors++;
+               dev_kfree_skb(skb);
+               return NETDEV_TX_OK;
+       }
+
+       return vti_xmit(skb, dev, &fl);
+}
+
 static int vti4_err(struct sk_buff *skb, u32 info)
 {
        __be32 spi;