Skip to content

Commit

Permalink
netdev-vport: Add IPv6 support for build/push/pop tunnel header
Browse files Browse the repository at this point in the history
This includes VXLAN, GRE and Geneve.

Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@redhat.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
  • Loading branch information
Thadeu Lima de Souza Cascardo authored and blp committed Dec 4, 2015
1 parent 213cc05 commit aee642c
Show file tree
Hide file tree
Showing 8 changed files with 298 additions and 123 deletions.
230 changes: 153 additions & 77 deletions lib/netdev-vport.c

Large diffs are not rendered by default.

107 changes: 74 additions & 33 deletions lib/odp-util.c
Expand Up @@ -22,6 +22,7 @@
#include <math.h>
#include <netinet/in.h>
#include <netinet/icmp6.h>
#include <netinet/ip6.h>
#include <stdlib.h>
#include <string.h>

Expand Down Expand Up @@ -435,11 +436,8 @@ format_odp_hash_action(struct ds *ds, const struct ovs_action_hash *hash_act)
}

static const void *
format_udp_tnl_push_header(struct ds *ds, const struct ip_header *ip)
format_udp_tnl_push_header(struct ds *ds, const struct udp_header *udp)
{
const struct udp_header *udp;

udp = (const struct udp_header *) (ip + 1);
ds_put_format(ds, "udp(src=%"PRIu16",dst=%"PRIu16",csum=0x%"PRIx16"),",
ntohs(udp->udp_src), ntohs(udp->udp_dst),
ntohs(udp->udp_csum));
Expand All @@ -451,13 +449,13 @@ static void
format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
{
const struct eth_header *eth;
const struct ip_header *ip;
const void *l3;
const void *l4;
const struct udp_header *udp;

eth = (const struct eth_header *)data->header;

l3 = eth + 1;
ip = (const struct ip_header *)l3;

/* Ethernet */
ds_put_format(ds, "header(size=%"PRIu8",type=%"PRIu8",eth(dst=",
Expand All @@ -467,27 +465,46 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_src));
ds_put_format(ds, ",dl_type=0x%04"PRIx16"),", ntohs(eth->eth_type));

/* IPv4 */
ds_put_format(ds, "ipv4(src="IP_FMT",dst="IP_FMT",proto=%"PRIu8
",tos=%#"PRIx8",ttl=%"PRIu8",frag=0x%"PRIx16"),",
IP_ARGS(get_16aligned_be32(&ip->ip_src)),
IP_ARGS(get_16aligned_be32(&ip->ip_dst)),
ip->ip_proto, ip->ip_tos,
ip->ip_ttl,
ip->ip_frag_off);
if (eth->eth_type == htons(ETH_TYPE_IP)) {
/* IPv4 */
const struct ip_header *ip;
ip = (const struct ip_header *) l3;
ds_put_format(ds, "ipv4(src="IP_FMT",dst="IP_FMT",proto=%"PRIu8
",tos=%#"PRIx8",ttl=%"PRIu8",frag=0x%"PRIx16"),",
IP_ARGS(get_16aligned_be32(&ip->ip_src)),
IP_ARGS(get_16aligned_be32(&ip->ip_dst)),
ip->ip_proto, ip->ip_tos,
ip->ip_ttl,
ip->ip_frag_off);
l4 = (ip + 1);
} else {
const struct ip6_hdr *ip6;
ip6 = (const struct ip6_hdr *) l3;
ds_put_format(ds, "ipv6(src=");
ipv6_format_addr(&ip6->ip6_src, ds);
ds_put_format(ds, ",dst=");
ipv6_format_addr(&ip6->ip6_dst, ds);
ds_put_format(ds, ",label=%i,proto=%"PRIu8",tclass=0x%"PRIx8
",hlimit=%"PRIu8"),",
ntohl(ip6->ip6_flow) & IPV6_LABEL_MASK, ip6->ip6_nxt,
(ntohl(ip6->ip6_flow) >> 20) & 0xff, ip6->ip6_hlim);
l4 = (ip6 + 1);
}

udp = (const struct udp_header *) l4;

if (data->tnl_type == OVS_VPORT_TYPE_VXLAN) {
const struct vxlanhdr *vxh;

vxh = format_udp_tnl_push_header(ds, ip);
vxh = format_udp_tnl_push_header(ds, udp);

ds_put_format(ds, "vxlan(flags=0x%"PRIx32",vni=0x%"PRIx32")",
ntohl(get_16aligned_be32(&vxh->vx_flags)),
ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8);
} else if (data->tnl_type == OVS_VPORT_TYPE_GENEVE) {
const struct genevehdr *gnh;

gnh = format_udp_tnl_push_header(ds, ip);
gnh = format_udp_tnl_push_header(ds, udp);

ds_put_format(ds, "geneve(%s%svni=0x%"PRIx32,
gnh->oam ? "oam," : "",
Expand All @@ -505,9 +522,7 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
} else if (data->tnl_type == OVS_VPORT_TYPE_GRE) {
const struct gre_base_hdr *greh;
ovs_16aligned_be32 *options;
void *l4;

l4 = ((uint8_t *)l3 + sizeof(struct ip_header));
greh = (const struct gre_base_hdr *) l4;

ds_put_format(ds, "gre((flags=0x%"PRIx16",proto=0x%"PRIx16")",
Expand Down Expand Up @@ -860,11 +875,12 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
{
struct eth_header *eth;
struct ip_header *ip;
struct ovs_16aligned_ip6_hdr *ip6;
struct udp_header *udp;
struct gre_base_hdr *greh;
uint16_t gre_proto, gre_flags, dl_type, udp_src, udp_dst, csum;
ovs_be32 sip, dip;
uint32_t tnl_type = 0, header_len = 0;
uint32_t tnl_type = 0, header_len = 0, ip_len = 0;
void *l3, *l4;
int n = 0;

Expand All @@ -873,8 +889,8 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
}
eth = (struct eth_header *) data->header;
l3 = (data->header + sizeof *eth);
l4 = ((uint8_t *) l3 + sizeof (struct ip_header));
ip = (struct ip_header *) l3;
ip6 = (struct ovs_16aligned_ip6_hdr *) l3;
if (!ovs_scan_len(s, &n, "header(size=%"SCNi32",type=%"SCNi32","
"eth(dst="ETH_ADDR_SCAN_FMT",",
&data->header_len,
Expand All @@ -892,19 +908,44 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
}
eth->eth_type = htons(dl_type);

/* IPv4 */
if (!ovs_scan_len(s, &n, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT",proto=%"SCNi8
",tos=%"SCNi8",ttl=%"SCNi8",frag=0x%"SCNx16"),",
IP_SCAN_ARGS(&sip),
IP_SCAN_ARGS(&dip),
&ip->ip_proto, &ip->ip_tos,
&ip->ip_ttl, &ip->ip_frag_off)) {
return -EINVAL;
if (eth->eth_type == htons(ETH_TYPE_IP)) {
/* IPv4 */
if (!ovs_scan_len(s, &n, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT",proto=%"SCNi8
",tos=%"SCNi8",ttl=%"SCNi8",frag=0x%"SCNx16"),",
IP_SCAN_ARGS(&sip),
IP_SCAN_ARGS(&dip),
&ip->ip_proto, &ip->ip_tos,
&ip->ip_ttl, &ip->ip_frag_off)) {
return -EINVAL;
}
put_16aligned_be32(&ip->ip_src, sip);
put_16aligned_be32(&ip->ip_dst, dip);
ip_len = sizeof *ip;
} else {
char sip6_s[IPV6_SCAN_LEN + 1];
char dip6_s[IPV6_SCAN_LEN + 1];
struct in6_addr sip6, dip6;
uint8_t tclass;
uint32_t label;
if (!ovs_scan_len(s, &n, "ipv6(src="IPV6_SCAN_FMT",dst="IPV6_SCAN_FMT
",label=%i,proto=%"SCNi8",tclass=0x%"SCNx8
",hlimit=%"SCNi8"),",
sip6_s, dip6_s, &label, &ip6->ip6_nxt,
&tclass, &ip6->ip6_hlim)
|| (label & ~IPV6_LABEL_MASK) != 0
|| inet_pton(AF_INET6, sip6_s, &sip6) != 1
|| inet_pton(AF_INET6, dip6_s, &dip6) != 1) {
return -EINVAL;
}
put_16aligned_be32(&ip6->ip6_flow, htonl(6 << 28) |
htonl(tclass << 20) | htonl(label));
memcpy(&ip6->ip6_src, &sip6, sizeof(ip6->ip6_src));
memcpy(&ip6->ip6_dst, &dip6, sizeof(ip6->ip6_dst));
ip_len = sizeof *ip6;
}
put_16aligned_be32(&ip->ip_src, sip);
put_16aligned_be32(&ip->ip_dst, dip);

/* Tunnel header */
l4 = ((uint8_t *) l3 + ip_len);
udp = (struct udp_header *) l4;
greh = (struct gre_base_hdr *) l4;
if (ovs_scan_len(s, &n, "udp(src=%"SCNi16",dst=%"SCNi16",csum=0x%"SCNx16"),",
Expand All @@ -923,13 +964,13 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
put_16aligned_be32(&vxh->vx_flags, htonl(vx_flags));
put_16aligned_be32(&vxh->vx_vni, htonl(vni << 8));
tnl_type = OVS_VPORT_TYPE_VXLAN;
header_len = sizeof *eth + sizeof *ip +
header_len = sizeof *eth + ip_len +
sizeof *udp + sizeof *vxh;
} else if (ovs_scan_len(s, &n, "geneve(")) {
struct genevehdr *gnh = (struct genevehdr *) (udp + 1);

memset(gnh, 0, sizeof *gnh);
header_len = sizeof *eth + sizeof *ip +
header_len = sizeof *eth + ip_len +
sizeof *udp + sizeof *gnh;

if (ovs_scan_len(s, &n, "oam,")) {
Expand Down Expand Up @@ -1008,7 +1049,7 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
return -EINVAL;
}

header_len = sizeof *eth + sizeof *ip +
header_len = sizeof *eth + ip_len +
((uint8_t *) options - (uint8_t *) greh);
} else {
return -EINVAL;
Expand Down
23 changes: 23 additions & 0 deletions lib/packets.c
Expand Up @@ -1166,3 +1166,26 @@ packet_csum_pseudoheader(const struct ip_header *ip)
return partial;
}

#ifndef __CHECKER__
uint32_t
packet_csum_pseudoheader6(const struct ovs_16aligned_ip6_hdr *ip6)
{
uint32_t partial = 0;

partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_src.be32[0])));
partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_src.be32[1])));
partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_src.be32[2])));
partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_src.be32[3])));
partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_dst.be32[0])));
partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_dst.be32[1])));
partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_dst.be32[2])));
partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_dst.be32[3])));

partial = csum_add16(partial, 0);
partial = csum_add16(partial, ip6->ip6_plen);
partial = csum_add16(partial, 0);
partial = csum_add16(partial, ip6->ip6_nxt);

return partial;
}
#endif
4 changes: 4 additions & 0 deletions lib/packets.h
Expand Up @@ -768,6 +768,8 @@ struct arp_eth_header {
};
BUILD_ASSERT_DECL(ARP_ETH_HEADER_LEN == sizeof(struct arp_eth_header));

#define IPV6_HEADER_LEN 40

/* Like struct in6_addr, but whereas that struct requires 32-bit alignment on
* most implementations, this one only requires 16-bit alignment. */
union ovs_16aligned_in6_addr {
Expand Down Expand Up @@ -808,6 +810,8 @@ struct icmp6_header {
};
BUILD_ASSERT_DECL(ICMP6_HEADER_LEN == sizeof(struct icmp6_header));

uint32_t packet_csum_pseudoheader6(const struct ovs_16aligned_ip6_hdr *);

/* Neighbor Discovery option field.
* ND options are always a multiple of 8 bytes in size. */
#define ND_OPT_LEN 8
Expand Down
4 changes: 3 additions & 1 deletion ofproto/ofproto-dpif-xlate.c
Expand Up @@ -2771,6 +2771,7 @@ 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;
struct eth_addr smac;
struct eth_addr dmac;
int err;
Expand Down Expand Up @@ -2817,8 +2818,9 @@ build_tunnel_send(struct xlate_ctx *ctx, const struct xport *xport,
" 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);
err = tnl_port_build_header(xport->ofport, flow,
dmac, smac, s_ip, &tnl_push_data);
dmac, smac, &s_ip6, &tnl_push_data);
if (err) {
return err;
}
Expand Down
44 changes: 33 additions & 11 deletions ofproto/tunnel.c
Expand Up @@ -685,40 +685,62 @@ tnl_port_build_header(const struct ofport_dpif *ofport,
const struct flow *tnl_flow,
const struct eth_addr dmac,
const struct eth_addr smac,
ovs_be32 ip_src, struct ovs_action_push_tnl *data)
const struct in6_addr * ipv6_src,
struct ovs_action_push_tnl *data)
{
struct tnl_port *tnl_port;
struct eth_header *eth;
struct ip_header *ip;
struct ovs_16aligned_ip6_hdr *ip6;
void *l3;
int res;
ovs_be32 ip_src;

fat_rwlock_rdlock(&rwlock);
tnl_port = tnl_find_ofport(ofport);
ovs_assert(tnl_port);

ip_src = in6_addr_get_mapped_ipv4(ipv6_src);

/* Build Ethernet and IP headers. */
memset(data->header, 0, sizeof data->header);

eth = (struct eth_header *)data->header;
eth->eth_dst = dmac;
eth->eth_src = smac;
eth->eth_type = htons(ETH_TYPE_IP);
eth->eth_type = ip_src ? htons(ETH_TYPE_IP) : htons(ETH_TYPE_IPV6);

l3 = (eth + 1);
ip = (struct ip_header *) l3;

ip->ip_ihl_ver = IP_IHL_VER(5, 4);
ip->ip_tos = tnl_flow->tunnel.ip_tos;
ip->ip_ttl = tnl_flow->tunnel.ip_ttl;
ip->ip_frag_off = (tnl_flow->tunnel.flags & FLOW_TNL_F_DONT_FRAGMENT) ?
htons(IP_DF) : 0;
if (ip_src) {
ip = (struct ip_header *) l3;

ip->ip_ihl_ver = IP_IHL_VER(5, 4);
ip->ip_tos = tnl_flow->tunnel.ip_tos;
ip->ip_ttl = tnl_flow->tunnel.ip_ttl;
ip->ip_frag_off = (tnl_flow->tunnel.flags & FLOW_TNL_F_DONT_FRAGMENT) ?
htons(IP_DF) : 0;

put_16aligned_be32(&ip->ip_src, ip_src);
put_16aligned_be32(&ip->ip_dst, tnl_flow->tunnel.ip_dst);
} else {
ip6 = (struct ovs_16aligned_ip6_hdr *) l3;

ip6->ip6_vfc = 0x60;
ip6->ip6_hlim = tnl_flow->tunnel.ip_ttl;

put_16aligned_be32(&ip->ip_src, ip_src);
put_16aligned_be32(&ip->ip_dst, tnl_flow->tunnel.ip_dst);
/* next header, plen - at netdev_build_header? */

memcpy(&ip6->ip6_src, ipv6_src, sizeof(ovs_be32[4]));
memcpy(&ip6->ip6_dst, &tnl_flow->tunnel.ipv6_dst, sizeof(ovs_be32[4]));
}

res = netdev_build_header(tnl_port->netdev, data, tnl_flow);
ip->ip_csum = csum(ip, sizeof *ip);

if (ip_src) {
ip->ip_csum = csum(ip, sizeof *ip);
}

fat_rwlock_unlock(&rwlock);

return res;
Expand Down
3 changes: 2 additions & 1 deletion ofproto/tunnel.h
Expand Up @@ -54,6 +54,7 @@ int tnl_port_build_header(const struct ofport_dpif *ofport,
const struct flow *tnl_flow,
const struct eth_addr dmac,
const struct eth_addr smac,
ovs_be32 ip_src, struct ovs_action_push_tnl *data);
const struct in6_addr *ipv6_src,
struct ovs_action_push_tnl *data);

#endif /* tunnel.h */
6 changes: 6 additions & 0 deletions tests/odp.at
Expand Up @@ -306,6 +306,12 @@ tnl_push(tnl_port(6),header(size=50,type=4,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:1
tnl_push(tnl_port(6),header(size=50,type=5,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=6081,csum=0x0),geneve(oam,vni=0x1c7)),out_port(1))
tnl_push(tnl_port(6),header(size=58,type=5,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=6081,csum=0x0),geneve(crit,vni=0x1c7,options({class=0xffff,type=0x80,len=4,0xa}))),out_port(1))
tnl_push(tnl_port(6),header(size=50,type=5,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=6081,csum=0xffff),geneve(vni=0x1c7)),out_port(1))
tnl_push(tnl_port(4),header(size=62,type=3,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=47,tclass=0x0,hlimit=64),gre((flags=0x2000,proto=0x6558),key=0x1e241)),out_port(1))
tnl_push(tnl_port(4),header(size=66,type=3,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=47,tclass=0x0,hlimit=64),gre((flags=0xa000,proto=0x6558),csum=0x0,key=0x1e241)),out_port(1))
tnl_push(tnl_port(6),header(size=70,type=4,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0x8000000,vni=0x1c7)),out_port(1))
tnl_push(tnl_port(6),header(size=70,type=5,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=0,dst=6081,csum=0x0),geneve(oam,vni=0x1c7)),out_port(1))
tnl_push(tnl_port(6),header(size=78,type=5,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=0,dst=6081,csum=0x0),geneve(crit,vni=0x1c7,options({class=0xffff,type=0x80,len=4,0xa}))),out_port(1))
tnl_push(tnl_port(6),header(size=70,type=5,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=0,dst=6081,csum=0xffff),geneve(vni=0x1c7)),out_port(1))
ct
ct(commit)
ct(commit,zone=5)
Expand Down

0 comments on commit aee642c

Please sign in to comment.