From c2b878e048e969c14a29c5f0e3972f7a9d9c5bf1 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Fri, 4 Dec 2015 12:36:48 -0200 Subject: [PATCH] ofproto-dpif-xlate: Support IPv6 when sending to tunnel 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 Cc: Flavio Leitner Signed-off-by: Ben Pfaff --- lib/packets.c | 30 ++++++++++++ lib/packets.h | 24 +++++++++ ofproto/ofproto-dpif-xlate.c | 95 +++++++++++++++++++++++++----------- 3 files changed, 120 insertions(+), 29 deletions(-) diff --git a/lib/packets.c b/lib/packets.c index c89fbd205..0ad5073d5 100644 --- a/lib/packets.c +++ b/lib/packets.c @@ -1167,6 +1167,36 @@ compose_arp(struct dp_packet *b, uint16_t arp_op, dp_packet_set_l3(b, arp); } +void +compose_nd(struct dp_packet *b, const struct eth_addr eth_src, + struct in6_addr * ipv6_src, struct in6_addr * ipv6_dst) +{ + 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); + + eth_compose(b, eth_dst, eth_src, ETH_TYPE_IPV6, + IPV6_HEADER_LEN + ICMP6_HEADER_LEN + ND_OPT_LEN); + packet_set_ipv6(b, 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(b); + 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(b, ALIGNED_CAST(ovs_be32 *, ipv6_dst->s6_addr), + eth_src, eth_addr_zero); +} + uint32_t packet_csum_pseudoheader(const struct ip_header *ip) { diff --git a/lib/packets.h b/lib/packets.h index 892d2eb41..edf140b9e 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -945,6 +945,28 @@ in6_addr_get_mapped_ipv4(const struct in6_addr *addr) } } +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 bool dl_type_is_ip_any(ovs_be16 dl_type) { return dl_type == htons(ETH_TYPE_IP) @@ -1015,6 +1037,8 @@ void compose_arp(struct dp_packet *, uint16_t arp_op, const struct eth_addr arp_sha, const struct eth_addr arp_tha, bool broadcast, ovs_be32 arp_spa, ovs_be32 arp_tpa); +void compose_nd(struct dp_packet *, const struct eth_addr eth_src, + struct in6_addr *, struct in6_addr *); uint32_t packet_csum_pseudoheader(const struct ip_header *); #endif /* packets.h */ diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index 2d0a1086a..cf184e405 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -54,6 +54,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" @@ -2698,21 +2699,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); @@ -2752,6 +2756,19 @@ compose_table_xlate(struct xlate_ctx *ctx, const struct xport *out_dev, ctx->recurse, ctx->resubmits, packet); } +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; + + dp_packet_init(&packet, 0); + compose_nd(&packet, eth_src, ipv6_src, ipv6_dst); + 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, @@ -2773,19 +2790,24 @@ 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; + char buf_sip6[INET6_ADDRSTRLEN]; + char buf_dip6[INET6_ADDRSTRLEN]; - 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)); + + xlate_report(ctx, "tunneling to %s via %s", + ipv6_string_mapped(buf_dip6, &d_ip6), + netdev_get_name(out_dev->netdev)); /* Use mac addr of bridge port of the peer. */ err = netdev_get_etheraddr(out_dev->netdev, &smac); @@ -2794,34 +2816,49 @@ 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); + } 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); + err = tnl_neigh_lookup(out_dev->xbridge->name, &d_ip6, &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); + xlate_report(ctx, "neighbor cache miss for %s on bridge %s, " + "sending %s request", + buf_dip6, out_dev->xbridge->name, d_ip ? "ARP" : "ND"); + if (d_ip) { + tnl_send_arp_request(ctx, out_dev, smac, s_ip, d_ip); + } else { + 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_NEIGH); ovs_strlcpy(entry->u.tnl_neigh_cache.br_name, out_dev->xbridge->name, sizeof entry->u.tnl_neigh_cache.br_name); - in6_addr_set_mapped_ipv4(&entry->u.tnl_neigh_cache.d_ipv6, d_ip); + entry->u.tnl_neigh_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); + xlate_report(ctx, "tunneling from "ETH_ADDR_FMT" %s" + " to "ETH_ADDR_FMT" %s", + ETH_ADDR_ARGS(smac), ipv6_string_mapped(buf_sip6, &s_ip6), + ETH_ADDR_ARGS(dmac), buf_dip6); + err = tnl_port_build_header(xport->ofport, flow, dmac, smac, &s_ip6, &tnl_push_data); if (err) { @@ -3025,7 +3062,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. @@ -3036,8 +3073,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 */ } @@ -5019,7 +5056,7 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout) .xin = xin, .xout = xout, .base_flow = *flow, - .orig_tunnel_ipv6_dst = in6_addr_mapped_ipv4(flow->tunnel.ip_dst), + .orig_tunnel_ipv6_dst = flow_tnl_dst(&flow->tunnel), .xbridge = xbridge, .stack = OFPBUF_STUB_INITIALIZER(stack_stub), .rule = xin->rule, -- 2.20.1