diff mbox

[ovs-dev,13/15] ofproto-dpif-xlate: Support IPv6 when sending to tunnel

Message ID 1445534948-10538-14-git-send-email-cascardo@redhat.com
State Changes Requested
Headers show

Commit Message

Thadeu Lima de Souza Cascardo Oct. 22, 2015, 5:29 p.m. UTC
When doing push/pop and building tunnel header, do IPv6 route lookups and send
Neighbor Solicitations if needed.

Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@redhat.com>
---
 lib/packets.h                |  22 +++++++
 ofproto/ofproto-dpif-xlate.c | 136 +++++++++++++++++++++++++++++++++----------
 2 files changed, 126 insertions(+), 32 deletions(-)
diff mbox

Patch

diff --git a/lib/packets.h b/lib/packets.h
index 2b01d9d..9dbbe4c 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -918,6 +918,28 @@  static inline bool ipv6_addr_is_multicast(const struct in6_addr *ip) {
 }
 
 static inline void
+in6_addr_solicited_node(struct in6_addr *addr, const struct in6_addr *ip6)
+{
+    union ovs_16aligned_in6_addr *taddr = (void *) addr;
+    memset(taddr->be16, 0, sizeof(taddr->be16));
+    taddr->be16[0] = htons(0xff02);
+    taddr->be16[5] = htons(0x1);
+    taddr->be16[6] = htons(0xff00);
+    memcpy(&addr->s6_addr[13], &ip6->s6_addr[13], 3);
+}
+
+static inline void
+ipv6_multicast_to_ethernet(struct eth_addr *eth, const struct in6_addr *ip6)
+{
+    eth->ea[0] = 0x33;
+    eth->ea[1] = 0x33;
+    eth->ea[2] = ip6->s6_addr[12];
+    eth->ea[3] = ip6->s6_addr[13];
+    eth->ea[4] = ip6->s6_addr[14];
+    eth->ea[5] = ip6->s6_addr[15];
+}
+
+static inline void
 in6_addr_set_mapped_ipv4(struct in6_addr *addr, ovs_be32 ip4)
 {
     union ovs_16aligned_in6_addr *taddr = (void *) addr;
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 8552396..5b47a20 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -21,6 +21,7 @@ 
 #include <net/if.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
+#include <netinet/icmp6.h>
 
 #include "tnl-arp-cache.h"
 #include "bfd.h"
@@ -54,6 +55,7 @@ 
 #include "ofproto/ofproto-dpif-sflow.h"
 #include "ofproto/ofproto-dpif.h"
 #include "ofproto/ofproto-provider.h"
+#include "packets.h"
 #include "ovs-router.h"
 #include "tnl-ports.h"
 #include "tunnel.h"
@@ -2659,21 +2661,24 @@  process_special(struct xlate_ctx *ctx, const struct xport *xport)
 
 static int
 tnl_route_lookup_flow(const struct flow *oflow,
-                      ovs_be32 *ip, struct xport **out_port)
+                      struct in6_addr *ip, struct xport **out_port)
 {
     char out_dev[IFNAMSIZ];
     struct xbridge *xbridge;
     struct xlate_cfg *xcfg;
-    ovs_be32 gw;
+    struct in6_addr gw;
+    struct in6_addr dst;
 
-    if (!ovs_router_lookup4(oflow->tunnel.ip_dst, out_dev, &gw)) {
+    dst = flow_tnl_dst(&oflow->tunnel);
+    if (!ovs_router_lookup(&dst, out_dev, &gw)) {
         return -ENOENT;
     }
 
-    if (gw) {
+    if (ipv6_addr_is_set(&gw) &&
+        (!IN6_IS_ADDR_V4MAPPED(&gw) || in6_addr_get_mapped_ipv4(&gw))) {
         *ip = gw;
     } else {
-        *ip = oflow->tunnel.ip_dst;
+        *ip = dst;
     }
 
     xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp);
@@ -2714,6 +2719,44 @@  compose_table_xlate(struct xlate_ctx *ctx, const struct xport *out_dev,
 }
 
 static void
+tnl_send_nd_request(struct xlate_ctx *ctx, const struct xport *out_dev,
+                     const struct eth_addr eth_src,
+                     struct in6_addr * ipv6_src, struct in6_addr * ipv6_dst)
+{
+    struct dp_packet packet;
+    struct in6_addr sn_addr;
+    struct eth_addr eth_dst;
+    struct ovs_nd_msg *ns;
+    struct ovs_nd_opt *nd_opt;
+
+    in6_addr_solicited_node(&sn_addr, ipv6_dst);
+    ipv6_multicast_to_ethernet(&eth_dst, &sn_addr);
+
+    dp_packet_init(&packet, 0);
+    dp_packet_clear(&packet);
+
+    eth_compose(&packet, eth_dst, eth_src, ETH_TYPE_IPV6,
+                IPV6_HEADER_LEN + ICMP6_HEADER_LEN + ND_OPT_LEN);
+    packet_set_ipv6(&packet, IPPROTO_ICMPV6,
+                    ALIGNED_CAST(ovs_be32 *, ipv6_src->s6_addr),
+                    ALIGNED_CAST(ovs_be32 *, sn_addr.s6_addr),
+                    0, 0, 255);
+
+    ns = dp_packet_l4(&packet);
+    nd_opt = &ns->options[0];
+
+    ns->icmph.icmp6_type = ND_NEIGHBOR_SOLICIT;
+    ns->icmph.icmp6_code = 0;
+
+    nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR;
+    packet_set_nd(&packet, ALIGNED_CAST(ovs_be32 *, ipv6_dst->s6_addr),
+                  eth_src, eth_addr_zero);
+
+    compose_table_xlate(ctx, out_dev, &packet);
+    dp_packet_uninit(&packet);
+}
+
+static void
 tnl_send_arp_request(struct xlate_ctx *ctx, const struct xport *out_dev,
                      const struct eth_addr eth_src,
                      ovs_be32 ip_src, ovs_be32 ip_dst)
@@ -2734,19 +2777,25 @@  build_tunnel_send(struct xlate_ctx *ctx, const struct xport *xport,
 {
     struct ovs_action_push_tnl tnl_push_data;
     struct xport *out_dev = NULL;
-    ovs_be32 s_ip, d_ip = 0;
-    struct in6_addr s_ip6;
+    ovs_be32 s_ip = 0, d_ip = 0;
+    struct in6_addr s_ip6 = in6addr_any;
+    struct in6_addr d_ip6 = in6addr_any;
     struct eth_addr smac;
     struct eth_addr dmac;
     int err;
+    struct ds ds = DS_EMPTY_INITIALIZER;
 
-    err = tnl_route_lookup_flow(flow, &d_ip, &out_dev);
+    err = tnl_route_lookup_flow(flow, &d_ip6, &out_dev);
     if (err) {
         xlate_report(ctx, "native tunnel routing failed");
         return err;
     }
-    xlate_report(ctx, "tunneling to "IP_FMT" via %s",
-                 IP_ARGS(d_ip), netdev_get_name(out_dev->netdev));
+
+    ds_put_format(&ds, "tunneling to ");
+    print_ipv6_mapped(&ds, &d_ip6);
+    ds_put_format(&ds, " via %s", netdev_get_name(out_dev->netdev));
+    xlate_report(ctx, "%s", ds_cstr(&ds));
+    ds_destroy(&ds);
 
     /* Use mac addr of bridge port of the peer. */
     err = netdev_get_etheraddr(out_dev->netdev, &smac);
@@ -2755,34 +2804,57 @@  build_tunnel_send(struct xlate_ctx *ctx, const struct xport *xport,
         return err;
     }
 
-    err = netdev_get_in4(out_dev->netdev, (struct in_addr *) &s_ip, NULL);
-    if (err) {
-        xlate_report(ctx, "tunnel output device lacks IPv4 address");
-        return err;
-    }
+    d_ip = in6_addr_get_mapped_ipv4(&d_ip6);
+    if (d_ip) {
+        err = netdev_get_in4(out_dev->netdev, (struct in_addr *) &s_ip, NULL);
+        if (err) {
+            xlate_report(ctx, "tunnel output device lacks IPv4 address");
+            return err;
+        }
+        in6_addr_set_mapped_ipv4(&s_ip6, s_ip);
+
+        err = tnl_arp_lookup(out_dev->xbridge->name, d_ip, &dmac);
+        if (err) {
+            xlate_report(ctx, "ARP cache miss for "IP_FMT" on bridge %s, "
+                        "sending ARP request",
+                        IP_ARGS(d_ip), out_dev->xbridge->name);
+            tnl_send_arp_request(ctx, out_dev, smac, s_ip, d_ip);
+            return err;
+        }
+    } else {
+        err = netdev_get_in6(out_dev->netdev, &s_ip6);
+        if (err) {
+            xlate_report(ctx, "tunnel output device lacks IPv6 address");
+            return err;
+        }
 
-    err = tnl_arp_lookup(out_dev->xbridge->name, d_ip, &dmac);
-    if (err) {
-        xlate_report(ctx, "ARP cache miss for "IP_FMT" on bridge %s, "
-                     "sending ARP request",
-                     IP_ARGS(d_ip), out_dev->xbridge->name);
-        tnl_send_arp_request(ctx, out_dev, smac, s_ip, d_ip);
-        return err;
+        err = tnl_nd_lookup(out_dev->xbridge->name, &d_ip6, &dmac);
+        if (err) {
+            xlate_report(ctx, "ND cache miss on bridge %s, "
+                        "sending ARP request",
+                        out_dev->xbridge->name);
+            tnl_send_nd_request(ctx, out_dev, smac, &s_ip6, &d_ip6);
+            return err;
+        }
     }
+
     if (ctx->xin->xcache) {
         struct xc_entry *entry;
 
         entry = xlate_cache_add_entry(ctx->xin->xcache, XC_TNL_ARP);
         ovs_strlcpy(entry->u.tnl_arp_cache.br_name, out_dev->xbridge->name,
                     sizeof entry->u.tnl_arp_cache.br_name);
-        in6_addr_set_mapped_ipv4(&entry->u.tnl_arp_cache.d_ipv6, d_ip);
+        entry->u.tnl_arp_cache.d_ipv6 = d_ip6;
     }
 
-    xlate_report(ctx, "tunneling from "ETH_ADDR_FMT" "IP_FMT
-                 " to "ETH_ADDR_FMT" "IP_FMT,
-                 ETH_ADDR_ARGS(smac), IP_ARGS(s_ip),
-                 ETH_ADDR_ARGS(dmac), IP_ARGS(d_ip));
-    in6_addr_set_mapped_ipv4(&s_ip6, s_ip);
+    ds_init(&ds);
+    ds_put_format(&ds, "tunneling from "ETH_ADDR_FMT" ", ETH_ADDR_ARGS(smac));
+    print_ipv6_mapped(&ds, &s_ip6);
+    ds_put_format(&ds, " to "ETH_ADDR_FMT" ", ETH_ADDR_ARGS(dmac));
+    print_ipv6_mapped(&ds, &d_ip6);
+    xlate_report(ctx, "%s", ds_cstr(&ds));
+    ds_destroy(&ds);
+
     err = tnl_port_build_header(xport->ofport, flow,
                                 dmac, smac, &s_ip6, &tnl_push_data);
     if (err) {
@@ -2983,7 +3055,7 @@  compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
     }
 
     if (xport->is_tunnel) {
-        ovs_be32 dst;
+        struct in6_addr dst;
          /* Save tunnel metadata so that changes made due to
           * the Logical (tunnel) Port are not visible for any further
           * matches, while explicit set actions on tunnel metadata are.
@@ -2994,8 +3066,8 @@  compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
             xlate_report(ctx, "Tunneling decided against output");
             goto out; /* restore flow_nw_tos */
         }
-        dst = in6_addr_get_mapped_ipv4(&ctx->orig_tunnel_ipv6_dst);
-        if (flow->tunnel.ip_dst == dst) {
+        dst = flow_tnl_dst(&flow->tunnel);
+        if (ipv6_addr_equals(&dst, &ctx->orig_tunnel_ipv6_dst)) {
             xlate_report(ctx, "Not tunneling to our own address");
             goto out; /* restore flow_nw_tos */
         }
@@ -4899,6 +4971,7 @@  xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
         .xin = xin,
         .xout = xout,
         .base_flow = *flow,
+        .orig_tunnel_ipv6_dst = flow_tnl_dst(&flow->tunnel),
         .xbridge = xbridge,
         .stack = OFPBUF_STUB_INITIALIZER(stack_stub),
         .rule = xin->rule,
@@ -4928,7 +5001,6 @@  xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
         .action_set_has_group = false,
         .action_set = OFPBUF_STUB_INITIALIZER(action_set_stub),
     };
-    in6_addr_set_mapped_ipv4(&ctx.orig_tunnel_ipv6_dst, flow->tunnel.ip_dst);
 
     /* 'base_flow' reflects the packet as it came in, but we need it to reflect
      * the packet as the datapath will treat it for output actions: