ipv6: sr: add support for ip4ip6 encapsulation
authorDavid Lebrun <david.lebrun@uclouvain.be>
Fri, 25 Aug 2017 07:56:44 +0000 (09:56 +0200)
committerDavid S. Miller <davem@davemloft.net>
Sat, 26 Aug 2017 00:10:23 +0000 (17:10 -0700)
This patch enables the SRv6 encapsulation mode to carry an IPv4 payload.
All the infrastructure was already present, I just had to add a parameter
to seg6_do_srh_encap() to specify the inner packet protocol, and perform
some additional checks.

Usage example:
ip route add 1.2.3.4 encap seg6 mode encap segs fc00::1,fc00::2 dev eth0

Signed-off-by: David Lebrun <david.lebrun@uclouvain.be>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/seg6.h
net/ipv6/seg6_iptunnel.c
net/ipv6/seg6_local.c

index 5379f550f521d841de956ad4d3a8c5cc4285e275..099bad59dc90150457f02e64d87b1354140ef0f3 100644 (file)
@@ -60,7 +60,8 @@ extern int seg6_local_init(void);
 extern void seg6_local_exit(void);
 
 extern bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len);
-extern int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh);
+extern int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh,
+                            int proto);
 extern int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh);
 
 #endif
index 5012330405702939c728fd546ce83022cc787ca5..5bec7817a7b91f99b2e9bb7b7e05b4281d192e8b 100644 (file)
@@ -91,7 +91,7 @@ static void set_tun_src(struct net *net, struct net_device *dev,
 }
 
 /* encapsulate an IPv6 packet within an outer IPv6 header with a given SRH */
-int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh)
+int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, int proto)
 {
        struct net *net = dev_net(skb_dst(skb)->dev);
        struct ipv6hdr *hdr, *inner_hdr;
@@ -116,15 +116,22 @@ int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh)
         * hlim will be decremented in ip6_forward() afterwards and
         * decapsulation will overwrite inner hlim with outer hlim
         */
-       ip6_flow_hdr(hdr, ip6_tclass(ip6_flowinfo(inner_hdr)),
-                    ip6_flowlabel(inner_hdr));
-       hdr->hop_limit = inner_hdr->hop_limit;
+
+       if (skb->protocol == htons(ETH_P_IPV6)) {
+               ip6_flow_hdr(hdr, ip6_tclass(ip6_flowinfo(inner_hdr)),
+                            ip6_flowlabel(inner_hdr));
+               hdr->hop_limit = inner_hdr->hop_limit;
+       } else {
+               ip6_flow_hdr(hdr, 0, 0);
+               hdr->hop_limit = ip6_dst_hoplimit(skb_dst(skb));
+       }
+
        hdr->nexthdr = NEXTHDR_ROUTING;
 
        isrh = (void *)hdr + sizeof(*hdr);
        memcpy(isrh, osrh, hdrlen);
 
-       isrh->nexthdr = NEXTHDR_IPV6;
+       isrh->nexthdr = proto;
 
        hdr->daddr = isrh->segments[isrh->first_segment];
        set_tun_src(net, skb->dev, &hdr->daddr, &hdr->saddr);
@@ -199,7 +206,7 @@ static int seg6_do_srh(struct sk_buff *skb)
 {
        struct dst_entry *dst = skb_dst(skb);
        struct seg6_iptunnel_encap *tinfo;
-       int err = 0;
+       int proto, err = 0;
 
        tinfo = seg6_encap_lwtunnel(dst->lwtstate);
 
@@ -210,17 +217,31 @@ static int seg6_do_srh(struct sk_buff *skb)
 
        switch (tinfo->mode) {
        case SEG6_IPTUN_MODE_INLINE:
+               if (skb->protocol != htons(ETH_P_IPV6))
+                       return -EINVAL;
+
                err = seg6_do_srh_inline(skb, tinfo->srh);
+               if (err)
+                       return err;
+
                skb_reset_inner_headers(skb);
                break;
        case SEG6_IPTUN_MODE_ENCAP:
-               err = seg6_do_srh_encap(skb, tinfo->srh);
+               if (skb->protocol == htons(ETH_P_IPV6))
+                       proto = IPPROTO_IPV6;
+               else if (skb->protocol == htons(ETH_P_IP))
+                       proto = IPPROTO_IPIP;
+               else
+                       return -EINVAL;
+
+               err = seg6_do_srh_encap(skb, tinfo->srh, proto);
+               if (err)
+                       return err;
+
+               skb->protocol = htons(ETH_P_IPV6);
                break;
        }
 
-       if (err)
-               return err;
-
        ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
        skb_set_transport_header(skb, sizeof(struct ipv6hdr));
 
@@ -334,6 +355,9 @@ static int seg6_build_state(struct nlattr *nla,
        struct seg6_lwt *slwt;
        int err;
 
+       if (family != AF_INET && family != AF_INET6)
+               return -EINVAL;
+
        err = nla_parse_nested(tb, SEG6_IPTUNNEL_MAX, nla,
                               seg6_iptunnel_policy, extack);
 
@@ -356,6 +380,9 @@ static int seg6_build_state(struct nlattr *nla,
 
        switch (tuninfo->mode) {
        case SEG6_IPTUN_MODE_INLINE:
+               if (family != AF_INET6)
+                       return -EINVAL;
+
                break;
        case SEG6_IPTUN_MODE_ENCAP:
                break;
index 147680e7a00c8f9dda7edd9e9262d57f1a78f078..609b94e970de2015c2078b217a3875e2c47ef8d8 100644 (file)
@@ -290,7 +290,7 @@ static int input_action_end_b6_encap(struct sk_buff *skb,
        skb_reset_inner_headers(skb);
        skb->encapsulation = 1;
 
-       err = seg6_do_srh_encap(skb, slwt->srh);
+       err = seg6_do_srh_encap(skb, slwt->srh, IPPROTO_IPV6);
        if (err)
                goto drop;