@@ -1005,6 +1005,8 @@ OvsInitTunnelVport(PVOID userContext,
case OVS_VPORT_TYPE_GRE:
status = OvsInitGreTunnel(vport);
break;
+ case OVS_VPORT_TYPE_GRE_L3:
+ break;
case OVS_VPORT_TYPE_VXLAN:
{
POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL;
@@ -1266,6 +1268,8 @@ OvsRemoveAndDeleteVport(PVOID usrParamsContext,
case OVS_VPORT_TYPE_GRE:
OvsCleanupGreTunnel(vport);
break;
+ case OVS_VPORT_TYPE_GRE_L3:
+ break;
case OVS_VPORT_TYPE_NETDEV:
if (vport->isExternal) {
if (vport->nicIndex == 0) {
@@ -230,9 +230,10 @@ enum ovs_vport_type {
OVS_VPORT_TYPE_UNSPEC,
OVS_VPORT_TYPE_NETDEV, /* network device */
OVS_VPORT_TYPE_INTERNAL, /* network device implemented by datapath */
- OVS_VPORT_TYPE_GRE, /* GRE tunnel. */
+ OVS_VPORT_TYPE_GRE, /* GRE Tap tunnel (L2 in GRE). */
OVS_VPORT_TYPE_VXLAN, /* VXLAN tunnel. */
OVS_VPORT_TYPE_GENEVE, /* Geneve tunnel. */
+ OVS_VPORT_TYPE_GRE_L3, /* GRE tunnel (L3 in GRE). */
OVS_VPORT_TYPE_LISP = 105, /* LISP tunnel */
OVS_VPORT_TYPE_STT = 106, /* STT tunnel */
__OVS_VPORT_TYPE_MAX
@@ -354,6 +355,7 @@ enum ovs_key_attr {
OVS_KEY_ATTR_CT_LABELS, /* 16-octet connection tracking labels */
OVS_KEY_ATTR_PACKET_ETHERTYPE, /* be16 Ethernet type for packet
* execution. */
+ OVS_KEY_ATTR_NEXT_BASE_LAYER, /* base layer of encapsulated packet */
#ifdef __KERNEL__
/* Only used within kernel data path. */
@@ -760,6 +760,9 @@ get_vport_type(const struct dpif_netlink_vport *vport)
case OVS_VPORT_TYPE_GRE:
return "gre";
+ case OVS_VPORT_TYPE_GRE_L3:
+ return "l3gre";
+
case OVS_VPORT_TYPE_VXLAN:
return "vxlan";
@@ -792,6 +795,8 @@ netdev_to_ovs_vport_type(const struct netdev *netdev)
return OVS_VPORT_TYPE_STT;
} else if (!strcmp(type, "geneve")) {
return OVS_VPORT_TYPE_GENEVE;
+ } else if (!strcmp(type, "l3gre")) { /* Must be before search for "gre" */
+ return OVS_VPORT_TYPE_GRE_L3;
} else if (strstr(type, "gre")) {
return OVS_VPORT_TYPE_GRE;
} else if (!strcmp(type, "vxlan")) {
@@ -821,6 +821,20 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
miniflow_push_be16(mf, tp_dst, htons(icmp->icmp6_code));
miniflow_pad_to_64(mf, tp_dst);
}
+ } else if (OVS_LIKELY(nw_proto == IPPROTO_GRE)) {
+ if (OVS_LIKELY(size >= sizeof(struct gre_base_hdr))) {
+ const struct gre_base_hdr *gre = data_pull(&data, &size,
+ sizeof *gre);
+ if (gre->protocol == htons(ETH_TYPE_TEB)) {
+ /* No need to store a zero value for next_base_layer
+ * in the miniflow which would cost an extra word of
+ * storage. */
+ BUILD_ASSERT(LAYER_2 == 0);
+ } else {
+ miniflow_push_uint8(mf, next_base_layer, LAYER_3);
+ miniflow_pad_to_64(mf, next_base_layer);
+ }
+ }
}
}
out:
@@ -1435,6 +1449,8 @@ flow_wc_map(const struct flow *flow, struct flowmap *map)
if (OVS_UNLIKELY(flow->nw_proto == IPPROTO_IGMP)) {
FLOWMAP_SET(map, igmp_group_ip4);
+ } else if (OVS_UNLIKELY(flow->nw_proto == IPPROTO_GRE)) {
+ FLOWMAP_SET(map, next_base_layer);
} else {
FLOWMAP_SET(map, tcp_flags);
FLOWMAP_SET(map, tp_src);
@@ -1453,6 +1469,8 @@ flow_wc_map(const struct flow *flow, struct flowmap *map)
FLOWMAP_SET(map, nd_target);
FLOWMAP_SET(map, arp_sha);
FLOWMAP_SET(map, arp_tha);
+ } else if (OVS_UNLIKELY(flow->nw_proto == IPPROTO_GRE)) {
+ FLOWMAP_SET(map, next_base_layer);
} else {
FLOWMAP_SET(map, tcp_flags);
FLOWMAP_SET(map, tp_src);
@@ -149,6 +149,10 @@ struct flow {
ovs_be16 tp_dst; /* TCP/UDP/SCTP destination port/ICMP code. */
ovs_be32 igmp_group_ip4; /* IGMP group IPv4 address.
* Keep last for BUILD_ASSERT_DECL below. */
+
+ uint8_t next_base_layer; /* Fields of encapsulated packet, if any,
+ * start at this layer */
+ uint8_t pad4[7];
};
BUILD_ASSERT_DECL(sizeof(struct flow) % sizeof(uint64_t) == 0);
BUILD_ASSERT_DECL(sizeof(struct flow_tnl) % sizeof(uint64_t) == 0);
@@ -1309,6 +1309,10 @@ match_format(const struct match *match, struct ds *s, int priority)
TCP_FLAGS(OVS_BE16_MAX));
}
+ if (wc->masks.next_base_layer) {
+ ds_put_format(s, "next_base_layer=%"PRIu8",", f->next_base_layer);
+ }
+
if (s->length > start_len) {
ds_chomp(s, ',');
}
@@ -5573,7 +5573,8 @@ get_etheraddr(const char *netdev_name, struct eth_addr *ea)
return error;
}
hwaddr_family = ifr.ifr_hwaddr.sa_family;
- if (hwaddr_family != AF_UNSPEC && hwaddr_family != ARPHRD_ETHER) {
+ if (hwaddr_family != AF_UNSPEC && hwaddr_family != ARPHRD_ETHER &&
+ hwaddr_family != ARPHRD_IPGRE) {
VLOG_INFO("%s device has unknown hardware address family %d",
netdev_name, hwaddr_family);
return EINVAL;
@@ -145,7 +145,7 @@ netdev_vport_is_layer3(const struct netdev *dev)
{
const char *type = netdev_get_type(dev);
- return (!strcmp("lisp", type));
+ return (!strcmp("lisp", type) || !strcmp("l3gre", type));
}
static bool
@@ -943,12 +943,17 @@ ip_extract_tnl_md(struct dp_packet *packet, struct flow_tnl *tnl,
return l4;
}
+static ovs_be16
+header_eth_type(const void *header)
+{
+ const struct eth_header *eth = header;
+ return eth->eth_type;
+}
+
static bool
is_header_ipv6(const void *header)
{
- const struct eth_header *eth;
- eth = header;
- return eth->eth_type == htons(ETH_TYPE_IPV6);
+ return header_eth_type(header) == htons(ETH_TYPE_IPV6);
}
/* Pushes the 'size' bytes of 'header' into the headroom of 'packet',
@@ -973,6 +978,9 @@ push_ip_header(struct dp_packet *packet,
memcpy(eth, header, size);
+ dp_packet_reset_offsets(packet);
+ packet->l3_ofs = sizeof (struct eth_header);
+
if (is_header_ipv6(header)) {
ip6 = ipv6_hdr(eth);
*ip_tot_size -= IPV6_HEADER_LEN;
@@ -1120,7 +1128,7 @@ gre_header_len(ovs_be16 flags)
static int
parse_gre_header(struct dp_packet *packet,
- struct flow_tnl *tnl)
+ struct flow_tnl *tnl, bool tap)
{
const struct gre_base_hdr *greh;
ovs_16aligned_be32 *options;
@@ -1136,7 +1144,8 @@ parse_gre_header(struct dp_packet *packet,
return -EINVAL;
}
- if (greh->protocol != htons(ETH_TYPE_TEB)) {
+ if ((tap && greh->protocol != htons(ETH_TYPE_TEB)) ||
+ (!tap && greh->protocol == htons(ETH_TYPE_TEB))) {
return -EINVAL;
}
@@ -1169,6 +1178,10 @@ parse_gre_header(struct dp_packet *packet,
options++;
}
+ if (!tap) {
+ packet->md.packet_ethertype = greh->protocol;
+ }
+
return hlen;
}
@@ -1182,7 +1195,7 @@ pkt_metadata_init_tnl(struct pkt_metadata *md)
}
static int
-netdev_gre_pop_header(struct dp_packet *packet)
+netdev_gre_pop_header__(struct dp_packet *packet, bool tap)
{
struct pkt_metadata *md = &packet->md;
struct flow_tnl *tnl = &md->tunnel;
@@ -1196,7 +1209,7 @@ netdev_gre_pop_header(struct dp_packet *packet)
return EINVAL;
}
- hlen = parse_gre_header(packet, tnl);
+ hlen = parse_gre_header(packet, tnl, tap);
if (hlen < 0) {
return -hlen;
}
@@ -1206,6 +1219,31 @@ netdev_gre_pop_header(struct dp_packet *packet)
return 0;
}
+static int
+netdev_gretap_pop_header(struct dp_packet *packet)
+{
+ return netdev_gre_pop_header__(packet, true);
+}
+
+static int
+netdev_gre_pop_header(struct dp_packet *packet)
+{
+ int err;
+
+ err = netdev_gre_pop_header__(packet, false);
+ if (err) {
+ return err;
+ }
+
+ if (eth_type_mpls(packet->md.packet_ethertype)) {
+ packet->l2_5_ofs = 0;
+ } else {
+ packet->l3_ofs = 0;
+ }
+
+ return 0;
+}
+
static void
netdev_gre_push_header(struct dp_packet *packet,
const struct ovs_action_push_tnl *data)
@@ -1219,12 +1257,13 @@ netdev_gre_push_header(struct dp_packet *packet,
ovs_be16 *csum_opt = (ovs_be16 *) (greh + 1);
*csum_opt = csum(greh, ip_tot_size);
}
+ packet->md.packet_ethertype = header_eth_type(data->header);
}
static int
-netdev_gre_build_header(const struct netdev *netdev,
- struct ovs_action_push_tnl *data,
- const struct flow *tnl_flow)
+netdev_gre_build_header__(const struct netdev *netdev,
+ struct ovs_action_push_tnl *data,
+ const struct flow *tnl_flow, ovs_be16 proto)
{
struct netdev_vport *dev = netdev_vport_cast(netdev);
struct netdev_tunnel_config *tnl_cfg;
@@ -1251,7 +1290,7 @@ netdev_gre_build_header(const struct netdev *netdev,
greh = (struct gre_base_hdr *) (ip + 1);
}
- greh->protocol = htons(ETH_TYPE_TEB);
+ greh->protocol = proto;
greh->flags = 0;
options = (ovs_16aligned_be32 *) (greh + 1);
@@ -1279,6 +1318,24 @@ netdev_gre_build_header(const struct netdev *netdev,
}
static int
+netdev_gretap_build_header(const struct netdev *netdev,
+ struct ovs_action_push_tnl *data,
+ const struct flow *tnl_flow)
+{
+ return netdev_gre_build_header__(netdev, data, tnl_flow,
+ htons(ETH_TYPE_TEB));
+}
+
+static int
+netdev_gre_build_header(const struct netdev *netdev,
+ struct ovs_action_push_tnl *data,
+ const struct flow *tnl_flow)
+{
+ return netdev_gre_build_header__(netdev, data, tnl_flow,
+ tnl_flow->dl_type);
+}
+
+static int
netdev_vxlan_pop_header(struct dp_packet *packet)
{
struct pkt_metadata *md = &packet->md;
@@ -1555,9 +1612,12 @@ netdev_vport_tunnel_register(void)
TUNNEL_CLASS("geneve", "genev_sys", netdev_geneve_build_header,
push_udp_header,
netdev_geneve_pop_header),
- TUNNEL_CLASS("gre", "gre_sys", netdev_gre_build_header,
+ TUNNEL_CLASS("gre", "gre_sys", netdev_gretap_build_header,
netdev_gre_push_header,
- netdev_gre_pop_header),
+ netdev_gretap_pop_header),
+ TUNNEL_CLASS("l3gre", "l3gre_sys", netdev_gre_build_header,
+ netdev_gre_push_header,
+ netdev_gre_pop_header),
TUNNEL_CLASS("ipsec_gre", "gre_sys", NULL, NULL, NULL),
TUNNEL_CLASS("vxlan", "vxlan_sys", netdev_vxlan_build_header,
push_udp_header,
@@ -20,6 +20,7 @@
#include <stdbool.h>
#include <stddef.h>
#include "compiler.h"
+#include "openvswitch/types.h"
struct dpif_netlink_vport;
struct dpif_flow_stats;
@@ -342,6 +342,7 @@ odp_execute_set_action(struct dp_packet *packet, const struct nlattr *a)
case OVS_KEY_ATTR_CT_ZONE:
case OVS_KEY_ATTR_CT_MARK:
case OVS_KEY_ATTR_CT_LABELS:
+ case OVS_KEY_ATTR_NEXT_BASE_LAYER:
case __OVS_KEY_ATTR_MAX:
default:
OVS_NOT_REACHED();
@@ -446,6 +447,7 @@ odp_execute_masked_set_action(struct dp_packet *packet,
case OVS_KEY_ATTR_ICMP:
case OVS_KEY_ATTR_ICMPV6:
case OVS_KEY_ATTR_TCP_FLAGS:
+ case OVS_KEY_ATTR_NEXT_BASE_LAYER:
case __OVS_KEY_ATTR_MAX:
default:
OVS_NOT_REACHED();
@@ -166,6 +166,7 @@ ovs_key_attr_to_string(enum ovs_key_attr attr, char *namebuf, size_t bufsize)
case OVS_KEY_ATTR_DP_HASH: return "dp_hash";
case OVS_KEY_ATTR_RECIRC_ID: return "recirc_id";
case OVS_KEY_ATTR_PACKET_ETHERTYPE: return "pkt_eth";
+ case OVS_KEY_ATTR_NEXT_BASE_LAYER: return "next_base_layer";
case __OVS_KEY_ATTR_MAX:
default:
@@ -1829,6 +1830,7 @@ static const struct attr_len_tbl ovs_flow_key_attr_lens[OVS_KEY_ATTR_MAX + 1] =
[OVS_KEY_ATTR_CT_MARK] = { .len = 4 },
[OVS_KEY_ATTR_CT_LABELS] = { .len = sizeof(struct ovs_key_ct_labels) },
[OVS_KEY_ATTR_PACKET_ETHERTYPE] = { .len = 2 },
+ [OVS_KEY_ATTR_NEXT_BASE_LAYER] = { .len = 1 },
};
/* Returns the correct length of the payload for a flow key attribute of the
@@ -2959,6 +2961,13 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
ds_chomp(ds, ',');
break;
}
+
+ case OVS_KEY_ATTR_NEXT_BASE_LAYER: {
+ const uint8_t *mask = ma ? nl_attr_get(ma) : NULL;
+ format_u8u(ds, "type", nl_attr_get_u8(a), mask, verbose);
+ break;
+ }
+
case OVS_KEY_ATTR_UNSPEC:
case __OVS_KEY_ATTR_MAX:
default:
@@ -4389,6 +4398,11 @@ odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
sctp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_SCTP,
sizeof *sctp_key);
get_tp_key(data, sctp_key);
+ } else if (flow->nw_proto == IPPROTO_GRE) {
+ if (!export_mask || data->next_base_layer == 0xff) {
+ nl_msg_put_u8(buf, OVS_KEY_ATTR_NEXT_BASE_LAYER,
+ data->next_base_layer);
+ }
} else if (flow->dl_type == htons(ETH_TYPE_IP)
&& flow->nw_proto == IPPROTO_ICMP) {
struct ovs_key_icmp *icmp_key;
@@ -4965,6 +4979,13 @@ parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
put_tp_key(sctp_key, flow);
expected_bit = OVS_KEY_ATTR_SCTP;
}
+ } else if (src_flow->nw_proto == IPPROTO_GRE
+ && (src_flow->dl_type == htons(ETH_TYPE_IP) ||
+ src_flow->dl_type == htons(ETH_TYPE_IPV6))
+ && !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) {
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_NEXT_BASE_LAYER)) {
+ flow->next_base_layer = nl_attr_get_u8(attrs[OVS_KEY_ATTR_NEXT_BASE_LAYER]);
+ }
} else if (src_flow->nw_proto == IPPROTO_ICMP
&& src_flow->dl_type == htons(ETH_TYPE_IP)
&& !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) {
@@ -27,6 +27,7 @@
#include "hash.h"
#include "list.h"
#include "netdev.h"
+#include "netdev-vport.h"
#include "ofpbuf.h"
#include "ovs-thread.h"
#include "odp-util.h"
@@ -52,6 +53,7 @@ static struct ovs_list addr_list;
struct tnl_port {
odp_port_t port;
ovs_be16 udp_port;
+ bool is_layer3;
char dev_name[IFNAMSIZ];
struct ovs_list node;
};
@@ -61,6 +63,7 @@ static struct ovs_list port_list;
struct tnl_port_in {
struct cls_rule cr;
odp_port_t portno;
+ bool match_base_layer;
struct ovs_refcount ref_cnt;
char dev_name[IFNAMSIZ];
};
@@ -82,7 +85,7 @@ tnl_port_free(struct tnl_port_in *p)
static void
tnl_port_init_flow(struct flow *flow, struct eth_addr mac,
- struct in6_addr *addr, ovs_be16 udp_port)
+ struct in6_addr *addr, ovs_be16 udp_port, bool is_layer3)
{
memset(flow, 0, sizeof *flow);
@@ -99,20 +102,21 @@ tnl_port_init_flow(struct flow *flow, struct eth_addr mac,
flow->nw_proto = IPPROTO_UDP;
} else {
flow->nw_proto = IPPROTO_GRE;
+ flow->next_base_layer = is_layer3 ? LAYER_3 : LAYER_2;
}
flow->tp_dst = udp_port;
}
static void
map_insert(odp_port_t port, struct eth_addr mac, struct in6_addr *addr,
- ovs_be16 udp_port, const char dev_name[])
+ ovs_be16 udp_port, const char dev_name[], bool is_layer3)
{
const struct cls_rule *cr;
struct tnl_port_in *p;
struct match match;
memset(&match, 0, sizeof match);
- tnl_port_init_flow(&match.flow, mac, addr, udp_port);
+ tnl_port_init_flow(&match.flow, mac, addr, udp_port, is_layer3);
do {
cr = classifier_lookup(&cls, CLS_MAX_VERSION, &match.flow, NULL);
@@ -133,6 +137,13 @@ map_insert(odp_port_t port, struct eth_addr mac, struct in6_addr *addr,
* doesn't make sense to match on UDP port numbers. */
if (udp_port) {
match.wc.masks.tp_dst = OVS_BE16_MAX;
+ } else {
+ /* Match base layer for non-UDP tunnels as it may
+ * be used to differentiate them. For UDP tunnels the
+ * port number provides differentiation.
+ */
+ match.wc.masks.next_base_layer = UINT8_MAX;
+ p->match_base_layer = true;
}
if (IN6_IS_ADDR_V4MAPPED(addr)) {
match.wc.masks.nw_dst = OVS_BE32_MAX;
@@ -151,15 +162,15 @@ map_insert(odp_port_t port, struct eth_addr mac, struct in6_addr *addr,
}
void
-tnl_port_map_insert(odp_port_t port,
- ovs_be16 udp_port, const char dev_name[])
+tnl_port_map_insert(odp_port_t port, ovs_be16 udp_port,
+ const char dev_name[], bool is_layer3)
{
struct tnl_port *p;
struct ip_device *ip_dev;
ovs_mutex_lock(&mutex);
LIST_FOR_EACH(p, node, &port_list) {
- if (udp_port == p->udp_port) {
+ if (udp_port == p->udp_port && udp_port) {
goto out;
}
}
@@ -167,6 +178,7 @@ tnl_port_map_insert(odp_port_t port,
p = xzalloc(sizeof *p);
p->port = port;
p->udp_port = udp_port;
+ p->is_layer3 = is_layer3;
ovs_strlcpy(p->dev_name, dev_name, sizeof p->dev_name);
list_insert(&port_list, &p->node);
@@ -174,11 +186,11 @@ tnl_port_map_insert(odp_port_t port,
if (ip_dev->addr4 != INADDR_ANY) {
struct in6_addr addr4 = in6_addr_mapped_ipv4(ip_dev->addr4);
map_insert(p->port, ip_dev->mac, &addr4,
- p->udp_port, p->dev_name);
+ p->udp_port, p->dev_name, is_layer3);
}
if (ipv6_addr_is_set(&ip_dev->addr6)) {
map_insert(p->port, ip_dev->mac, &ip_dev->addr6,
- p->udp_port, p->dev_name);
+ p->udp_port, p->dev_name, is_layer3);
}
}
@@ -199,19 +211,20 @@ tnl_port_unref(const struct cls_rule *cr)
}
static void
-map_delete(struct eth_addr mac, struct in6_addr *addr, ovs_be16 udp_port)
+map_delete(struct eth_addr mac, struct in6_addr *addr, ovs_be16 udp_port,
+ bool is_layer3)
{
const struct cls_rule *cr;
struct flow flow;
- tnl_port_init_flow(&flow, mac, addr, udp_port);
+ tnl_port_init_flow(&flow, mac, addr, udp_port, is_layer3);
cr = classifier_lookup(&cls, CLS_MAX_VERSION, &flow, NULL);
tnl_port_unref(cr);
}
void
-tnl_port_map_delete(ovs_be16 udp_port)
+tnl_port_map_delete(ovs_be16 udp_port, bool is_layer3)
{
struct tnl_port *p, *next;
struct ip_device *ip_dev;
@@ -232,10 +245,10 @@ tnl_port_map_delete(ovs_be16 udp_port)
LIST_FOR_EACH(ip_dev, node, &addr_list) {
if (ip_dev->addr4 != INADDR_ANY) {
struct in6_addr addr4 = in6_addr_mapped_ipv4(ip_dev->addr4);
- map_delete(ip_dev->mac, &addr4, udp_port);
+ map_delete(ip_dev->mac, &addr4, udp_port, is_layer3);
}
if (ipv6_addr_is_set(&ip_dev->addr6)) {
- map_delete(ip_dev->mac, &ip_dev->addr6, udp_port);
+ map_delete(ip_dev->mac, &ip_dev->addr6, udp_port, is_layer3);
}
}
@@ -244,15 +257,35 @@ out:
ovs_mutex_unlock(&mutex);
}
-/* 'flow' is non-const to allow for temporary modifications during the lookup.
- * Any changes are restored before returning. */
+/* 'flow' is non-const to allow for:
+ * - Temporary modifications during the lookup
+ * these are reverted before returning.
+ * - Setting matching on next_base_layer as required by the port looked up. */
odp_port_t
tnl_port_map_lookup(struct flow *flow, struct flow_wildcards *wc)
{
const struct cls_rule *cr = classifier_lookup(&cls, CLS_MAX_VERSION, flow,
wc);
+ enum base_layer next_base_layer_mask;
+ struct tnl_port_in *p;
+ odp_port_t portno;
+
+ /* next_base_layer should be matched when looking up tunnel port*/
+ next_base_layer_mask = wc->masks.base_layer;
+ wc->masks.next_base_layer = UINT8_MAX;
+
+ if (!cr) {
+ portno = ODPP_NONE;
+ } else {
+ p = tnl_port_cast(cr);
+ portno = p->portno;
+ }
+
+ if (!cr || !p->match_base_layer) {
+ wc->masks.next_base_layer = next_base_layer_mask;
+ }
- return (cr) ? tnl_port_cast(cr)->portno : ODPP_NONE;
+ return portno;
}
static void
@@ -334,11 +367,11 @@ map_insert_ipdev(struct ip_device *ip_dev)
if (ip_dev->addr4 != INADDR_ANY) {
struct in6_addr addr4 = in6_addr_mapped_ipv4(ip_dev->addr4);
map_insert(p->port, ip_dev->mac, &addr4,
- p->udp_port, p->dev_name);
+ p->udp_port, p->dev_name, p->is_layer3);
}
if (ipv6_addr_is_set(&ip_dev->addr6)) {
map_insert(p->port, ip_dev->mac, &ip_dev->addr6,
- p->udp_port, p->dev_name);
+ p->udp_port, p->dev_name, p->is_layer3);
}
}
}
@@ -386,15 +419,16 @@ insert_ipdev(const char dev_name[])
static void
delete_ipdev(struct ip_device *ip_dev)
{
+ bool is_layer3 = netdev_vport_is_layer3(ip_dev->dev);
struct tnl_port *p;
LIST_FOR_EACH(p, node, &port_list) {
if (ip_dev->addr4 != INADDR_ANY) {
struct in6_addr addr4 = in6_addr_mapped_ipv4(ip_dev->addr4);
- map_delete(ip_dev->mac, &addr4, p->udp_port);
+ map_delete(ip_dev->mac, &addr4, p->udp_port, is_layer3);
}
if (ipv6_addr_is_set(&ip_dev->addr6)) {
- map_delete(ip_dev->mac, &ip_dev->addr6, p->udp_port);
+ map_delete(ip_dev->mac, &ip_dev->addr6, p->udp_port, is_layer3);
}
}
@@ -27,9 +27,9 @@
odp_port_t tnl_port_map_lookup(struct flow *flow, struct flow_wildcards *wc);
void tnl_port_map_insert(odp_port_t port, ovs_be16 udp_port,
- const char dev_name[]);
+ const char dev_name[], bool is_layer3);
-void tnl_port_map_delete(ovs_be16 udp_port);
+void tnl_port_map_delete(ovs_be16 udp_port, bool is_layer3);
void tnl_port_map_insert_ipdev(const char dev[]);
void tnl_port_map_delete_ipdev(const char dev[]);
void tnl_port_map_run(void);
@@ -588,7 +588,7 @@ dpif_ipfix_add_tunnel_port(struct dpif_ipfix *di, struct ofport *ofport,
dip = xmalloc(sizeof *dip);
dip->ofport = ofport;
dip->odp_port = odp_port;
- if (strcmp(type, "gre") == 0) {
+ if (strcmp(type, "gre") == 0 || strcmp(type, "l3gre") == 0) {
/* 32-bit key gre */
dip->tunnel_type = DPIF_IPFIX_TUNNEL_GRE;
dip->tunnel_key_length = 4;
@@ -584,7 +584,7 @@ static enum dpif_sflow_tunnel_type
dpif_sflow_tunnel_type(struct ofport *ofport) {
const char *type = netdev_get_type(ofport->netdev);
if (type) {
- if (strcmp(type, "gre") == 0) {
+ if (strcmp(type, "gre") == 0 || strcmp(type, "l3gre") == 0) {
return DPIF_SFLOW_TUNNEL_GRE;
} else if (strcmp(type, "ipsec_gre") == 0) {
return DPIF_SFLOW_TUNNEL_IPSEC_GRE;
@@ -1035,6 +1035,7 @@ sflow_read_set_action(const struct nlattr *attr,
case OVS_KEY_ATTR_CT_MARK:
case OVS_KEY_ATTR_CT_LABELS:
case OVS_KEY_ATTR_UNSPEC:
+ case OVS_KEY_ATTR_NEXT_BASE_LAYER:
case __OVS_KEY_ATTR_MAX:
default:
break;
@@ -26,6 +26,7 @@
#include "hash.h"
#include "hmap.h"
#include "netdev.h"
+#include "netdev-vport.h"
#include "odp-util.h"
#include "ofpbuf.h"
#include "packets.h"
@@ -194,7 +195,8 @@ tnl_port_add__(const struct ofport_dpif *ofport, const struct netdev *netdev,
tnl_port_mod_log(tnl_port, "adding");
if (native_tnl) {
- tnl_port_map_insert(odp_port, cfg->dst_port, name);
+ tnl_port_map_insert(odp_port, cfg->dst_port, name,
+ netdev_vport_is_layer3(netdev));
}
return true;
}
@@ -261,7 +263,8 @@ tnl_port_del__(const struct ofport_dpif *ofport) OVS_REQ_WRLOCK(rwlock)
netdev_get_tunnel_config(tnl_port->netdev);
struct hmap **map;
- tnl_port_map_delete(cfg->dst_port);
+ tnl_port_map_delete(cfg->dst_port,
+ netdev_vport_is_layer3(tnl_port->netdev));
tnl_port_mod_log(tnl_port, "removing");
map = tnl_match_map(&tnl_port->match);
hmap_remove(*map, &tnl_port->match_node);
@@ -12,6 +12,8 @@ AT_CHECK([ovs-vsctl add-port int-br t2 -- set Interface t2 type=vxlan \
options:remote_ip=2001:cafe::93 options:out_key=flow options:csum=true ofport_request=4\
-- add-port int-br t4 -- set Interface t4 type=geneve \
options:remote_ip=flow options:key=123 ofport_request=5\
+ -- add-port int-br t5 -- set Interface t5 type=l3gre \
+ options:remote_ip=2001:cafe::92 options:key=455 ofport_request=6\
], [0])
AT_CHECK([ovs-appctl dpif/show], [0], [dnl
@@ -21,10 +23,11 @@ dummy@ovs-dummy: hit:0 missed:0
p0 1/1: (dummy)
int-br:
int-br 65534/2: (dummy)
- t1 3/3: (gre: key=456, remote_ip=2001:cafe::92)
+ t1 3/4: (gre: key=456, remote_ip=2001:cafe::92)
t2 2/4789: (vxlan: key=123, remote_ip=2001:cafe::92)
t3 4/4789: (vxlan: csum=true, out_key=flow, remote_ip=2001:cafe::93)
t4 5/6081: (geneve: key=123, remote_ip=flow)
+ t5 6/3: (l3gre: key=455, remote_ip=2001:cafe::92)
])
dnl First setup dummy interface IP address, then add the route
@@ -52,7 +55,8 @@ IP MAC Bridge
AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl
Listening ports:
genev_sys_6081 (6081)
-gre_sys (3)
+gre_sys (4)
+l3gre_sys (3)
vxlan_sys_4789 (4789)
])
@@ -65,7 +69,7 @@ AT_CHECK([tail -1 stdout], [0],
dnl Check GRE tunnel pop
AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=47,tclass=0x0,hlimit=64)'], [0], [stdout])
AT_CHECK([tail -1 stdout], [0],
- [Datapath actions: tnl_pop(3)
+ [Datapath actions: tnl_pop(4)
])
dnl Check Geneve tunnel pop
@@ -92,7 +96,7 @@ dnl Check GRE tunnel push
AT_CHECK([ovs-ofctl add-flow int-br action=3])
AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout])
AT_CHECK([tail -1 stdout], [0],
- [Datapath actions: tnl_push(tnl_port(3),header(size=62,type=3,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,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=0x1c8)),out_port(100))
+ [Datapath actions: tnl_push(tnl_port(4),header(size=62,type=3,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,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=0x1c8)),out_port(100))
])
dnl Check Geneve tunnel push
@@ -118,12 +122,12 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port 3'], [0], [dnl
port 3: rx pkts=1, bytes=98, drop=0, errs=0, frame=0, over=0, crc=0
])
-dnl Check GRE only accepts encapsulated Ethernet frames
-AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6486dd60000000006a2f402001cafe0000000000000000000000922001cafe00000000000000000000008820000800000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
+dnl Check decapsulation of L3GRE packet
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6486dd60000000005a2f402001cafe0000000000000000000000922001cafe00000000000000000000008820000800000001c745000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
ovs-appctl time/warp 1000
-AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port 3'], [0], [dnl
- port 3: rx pkts=1, bytes=98, drop=0, errs=0, frame=0, over=0, crc=0
+AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port 6'], [0], [dnl
+ port 6: rx pkts=1, bytes=84, drop=0, errs=0, frame=0, over=0, crc=0
])
dnl Check decapsulation of Geneve packet with options
@@ -12,6 +12,8 @@ AT_CHECK([ovs-vsctl add-port int-br t2 -- set Interface t2 type=vxlan \
options:remote_ip=1.1.2.93 options:out_key=flow options:csum=true ofport_request=4\
-- add-port int-br t4 -- set Interface t4 type=geneve \
options:remote_ip=flow options:key=123 ofport_request=5\
+ -- add-port int-br t5 -- set Interface t5 type=l3gre \
+ options:remote_ip=1.1.2.92 options:key=455 ofport_request=6\
], [0])
AT_CHECK([ovs-appctl dpif/show], [0], [dnl
@@ -21,10 +23,11 @@ dummy@ovs-dummy: hit:0 missed:0
p0 1/1: (dummy)
int-br:
int-br 65534/2: (dummy)
- t1 3/3: (gre: key=456, remote_ip=1.1.2.92)
+ t1 3/4: (gre: key=456, remote_ip=1.1.2.92)
t2 2/4789: (vxlan: key=123, remote_ip=1.1.2.92)
t3 4/4789: (vxlan: csum=true, out_key=flow, remote_ip=1.1.2.93)
t4 5/6081: (geneve: key=123, remote_ip=flow)
+ t5 6/3: (l3gre: key=455, remote_ip=1.1.2.92)
])
dnl First setup dummy interface IP address, then add the route
@@ -50,7 +53,8 @@ IP MAC Bridge
AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl
Listening ports:
genev_sys_6081 (6081)
-gre_sys (3)
+gre_sys (4)
+l3gre_sys (3)
vxlan_sys_4789 (4789)
])
@@ -63,7 +67,7 @@ AT_CHECK([tail -1 stdout], [0],
dnl Check GRE tunnel pop
AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout])
AT_CHECK([tail -1 stdout], [0],
- [Datapath actions: tnl_pop(3)
+ [Datapath actions: tnl_pop(4)
])
dnl Check Geneve tunnel pop
@@ -90,7 +94,14 @@ dnl Check GRE tunnel push
AT_CHECK([ovs-ofctl add-flow int-br action=3])
AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout])
AT_CHECK([tail -1 stdout], [0],
- [Datapath actions: tnl_push(tnl_port(3),header(size=42,type=3,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=47,tos=0,ttl=64,frag=0x40),gre((flags=0x2000,proto=0x6558),key=0x1c8)),out_port(100))
+ [Datapath actions: tnl_push(tnl_port(4),header(size=42,type=3,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=47,tos=0,ttl=64,frag=0x40),gre((flags=0x2000,proto=0x6558),key=0x1c8)),out_port(100))
+])
+
+dnl Check L3GRE tunnel push
+AT_CHECK([ovs-ofctl add-flow int-br action=6])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: pop_eth,tnl_push(tnl_port(3),header(size=42,type=3,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=47,tos=0,ttl=64,frag=0x40),gre((flags=0x2000,proto=0x800),key=0x1c7)),out_port(100))
])
dnl Check Geneve tunnel push
@@ -116,12 +127,20 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port 3'], [0], [dnl
port 3: rx pkts=1, bytes=98, drop=0, errs=0, frame=0, over=0, crc=0
])
-dnl Check GRE only accepts encapsulated Ethernet frames
-AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
+dnl Check decapsulation of L3GRE packet
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007079464000402fba630101025c0101025820000800000001c745000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
ovs-appctl time/warp 1000
-AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port 3'], [0], [dnl
+AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port 6'], [0], [dnl
+ port 6: rx pkts=1, bytes=84, drop=0, errs=0, frame=0, over=0, crc=0
+])
+
+dnl Check GREL3 only accepts non-fragmented packets?
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c7fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
+
+AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port [[36]]' | sort], [0], [dnl
port 3: rx pkts=1, bytes=98, drop=0, errs=0, frame=0, over=0, crc=0
+ port 6: rx pkts=1, bytes=84, drop=0, errs=0, frame=0, over=0, crc=0
])
dnl Check decapsulation of Geneve packet with options
Add support for layer 3 GRE vports (non-tap aka non-VTEP). This makes use of a separate vport type for GRE, rather than a new mode for the existing (tap/VTEP) GRE vports as this fits more naturally with the kernel where implementation of GRE and thus implementation of this feature there. In order to differentiate packets for two different types of GRE vports a new flow key attribute, OVS_KEY_ATTR_NEXT_BASE_LAYER, is used. It is intended that this attribute is only used in userspace as there appears to be no need for it to be used in the kernel datapath. It is envisaged that this attribute may be used for other non-UDP encapsulation protocols that support both layer3 and layer2 inner-packets. While for UDP encapsulation the UDP port can be used for differentiation without the need for this new attribute. One alternative approach to this new attribute, which I have not investigated in detail, would be to use a second classifier in tnl-ports.c for non-UDP layer3 tunnels; leaving the existing classifier for all other tunnels. Signed-off-by: Simon Horman <simon.horman@netronome.com> --- v9 * New patch --- datapath-windows/ovsext/Vport.c | 4 ++ datapath/linux/compat/include/linux/openvswitch.h | 4 +- lib/dpif-netlink.c | 5 ++ lib/flow.c | 18 +++++ lib/flow.h | 4 ++ lib/match.c | 4 ++ lib/netdev-linux.c | 3 +- lib/netdev-vport.c | 88 +++++++++++++++++++---- lib/netdev-vport.h | 1 + lib/odp-execute.c | 2 + lib/odp-util.c | 21 ++++++ lib/tnl-ports.c | 74 +++++++++++++------ lib/tnl-ports.h | 4 +- ofproto/ofproto-dpif-ipfix.c | 2 +- ofproto/ofproto-dpif-sflow.c | 3 +- ofproto/tunnel.c | 7 +- tests/tunnel-push-pop-ipv6.at | 20 +++--- tests/tunnel-push-pop.at | 33 +++++++-- 18 files changed, 240 insertions(+), 57 deletions(-)