@@ -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;
@@ -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(ð_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:
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(-)