Skip to content

Commit

Permalink
userspace: L3 tunnel support for GRE and LISP
Browse files Browse the repository at this point in the history
Add a boolean "layer3" configuration option for tunnel vports.
The layer3 option defaults to false for all ports except LISP.
GRE ports accept both true and false for "layer3".

A tunnel vport configured with layer3=true receives L3 packets.
which are then converted to Ethernet packets by pushing a dummy
Ethernet heder at the ingress of the OpenFlow pipeline. The
Ethernet header of a packet is stripped before sending to a
layer3 tunnel vport.

Presently a single GRE vport cannot carry both L2 and L3 packets.
But it is possible to create two GRE vports representing the same
GRE tunel, one with layer3=false, the other with layer3=true.
L2 packet from the tunnel are received on the first vport, L3
packets on the second. The controller must send packets to the
layer3 GRE vport to tunnel them without their Ethernet header.

Units tests have been added to check the L3 tunnel handling.

LISP tunnels are not yet supported by the netdev userspace datapath.

Signed-off-by: Simon Horman <[email protected]>
Signed-off-by: Jiri Benc <[email protected]>
Signed-off-by: Yi Yang <[email protected]>
Signed-off-by: Jan Scheurich <[email protected]>
Co-authored-by: Zoltan Balogh <[email protected]>
Signed-off-by: Ben Pfaff <[email protected]>
  • Loading branch information
2 people authored and blp committed Jun 2, 2017
1 parent beb75a4 commit 63171f0
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 22 deletions.
5 changes: 5 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ Post-v2.7.0
- Fedora Packaging:
* OVN services are no longer restarted automatically after upgrade.
- Add --cleanup option to command 'ovs-appctl exit' (see ovs-vswitchd(8)).
- L3 tunneling:
* Add "layer3" options for tunnel ports that support non-Ethernet (L3)
payload (GRE).
* Transparently pop and push Ethernet headers at transmit/reception
of packets to/from L3 tunnels.

v2.7.0 - 21 Feb 2017
---------------------
Expand Down
28 changes: 23 additions & 5 deletions lib/netdev-native-tnl.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ netdev_tnl_push_ip_header(struct dp_packet *packet,
*ip_tot_size = dp_packet_size(packet) - sizeof (struct eth_header);

memcpy(eth, header, size);
/* The encapsulated packet has type Ethernet. Adjust dp_packet. */
packet->packet_type = htonl(PT_ETH);
dp_packet_reset_offsets(packet);
packet->l3_ofs = sizeof (struct eth_header);

if (netdev_tnl_is_header_ipv6(header)) {
ip6 = netdev_tnl_ipv6_hdr(eth);
Expand Down Expand Up @@ -345,6 +349,7 @@ parse_gre_header(struct dp_packet *packet,
ovs_16aligned_be32 *options;
int hlen;
unsigned int ulen;
uint16_t greh_protocol;

greh = netdev_tnl_ip_extract_tnl_md(packet, tnl, &ulen);
if (!greh) {
Expand All @@ -355,10 +360,6 @@ parse_gre_header(struct dp_packet *packet,
return -EINVAL;
}

if (greh->protocol != htons(ETH_TYPE_TEB)) {
return -EINVAL;
}

hlen = ulen + gre_header_len(greh->flags);
if (hlen > dp_packet_size(packet)) {
return -EINVAL;
Expand Down Expand Up @@ -388,6 +389,17 @@ parse_gre_header(struct dp_packet *packet,
options++;
}

/* Set the new packet type depending on the GRE protocol field. */
greh_protocol = ntohs(greh->protocol);
if (greh_protocol == ETH_TYPE_TEB) {
packet->packet_type = htonl(PT_ETH);
} else if (greh_protocol >= ETH_TYPE_MIN) {
/* Allow all GRE protocol values above 0x5ff as Ethertypes. */
packet->packet_type = PACKET_TYPE_BE(OFPHTN_ETHERTYPE, greh_protocol);
} else {
return -EINVAL;
}

return hlen;
}

Expand Down Expand Up @@ -451,7 +463,11 @@ netdev_gre_build_header(const struct netdev *netdev,

greh = netdev_tnl_ip_build_header(data, params, IPPROTO_GRE);

greh->protocol = htons(ETH_TYPE_TEB);
if (tnl_cfg->is_layer3) {
greh->protocol = params->flow->dl_type;
} else {
greh->protocol = htons(ETH_TYPE_TEB);
}
greh->flags = 0;

options = (ovs_16aligned_be32 *) (greh + 1);
Expand Down Expand Up @@ -504,6 +520,7 @@ netdev_vxlan_pop_header(struct dp_packet *packet)
tnl->tun_id = htonll(ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8);
tnl->flags |= FLOW_TNL_F_KEY;

packet->packet_type = htonl(PT_ETH);
dp_packet_reset_packet(packet, hlen + VXLAN_HLEN);

return packet;
Expand Down Expand Up @@ -583,6 +600,7 @@ netdev_geneve_pop_header(struct dp_packet *packet)
tnl->metadata.present.len = opts_len;
tnl->flags |= FLOW_TNL_F_UDPIF;

packet->packet_type = htonl(PT_ETH);
dp_packet_reset_packet(packet, hlen);

return packet;
Expand Down
14 changes: 12 additions & 2 deletions lib/netdev-vport.c
Original file line number Diff line number Diff line change
Expand Up @@ -410,14 +410,15 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp)
const char *name = netdev_get_name(dev_);
const char *type = netdev_get_type(dev_);
struct ds errors = DS_EMPTY_INITIALIZER;
bool needs_dst_port, has_csum;
bool needs_dst_port, has_csum, optional_layer3;
uint16_t dst_proto = 0, src_proto = 0;
struct netdev_tunnel_config tnl_cfg;
struct smap_node *node;
int err;

has_csum = strstr(type, "gre") || strstr(type, "geneve") ||
strstr(type, "stt") || strstr(type, "vxlan");
optional_layer3 = !strcmp(type, "gre");
memset(&tnl_cfg, 0, sizeof tnl_cfg);

/* Add a default destination port for tunnel ports if none specified. */
Expand All @@ -431,6 +432,7 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp)

if (!strcmp(type, "lisp")) {
tnl_cfg.dst_port = htons(LISP_DST_PORT);
tnl_cfg.is_layer3 = true;
}

if (!strcmp(type, "stt")) {
Expand Down Expand Up @@ -518,6 +520,10 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp)
} else if (!strcmp(node->key, "egress_pkt_mark")) {
tnl_cfg.egress_pkt_mark = strtoul(node->value, NULL, 10);
tnl_cfg.set_egress_pkt_mark = true;
} else if (!strcmp(node->key, "layer3") && optional_layer3) {
if (!strcmp(node->value, "true")) {
tnl_cfg.is_layer3 = true;
}
} else {
ds_put_format(&errors, "%s: unknown %s argument '%s'\n",
name, type, node->key);
Expand Down Expand Up @@ -587,6 +593,7 @@ static int
get_tunnel_config(const struct netdev *dev, struct smap *args)
{
struct netdev_vport *netdev = netdev_vport_cast(dev);
const char *type = netdev_get_type(dev);
struct netdev_tunnel_config tnl_cfg;

ovs_mutex_lock(&netdev->mutex);
Expand Down Expand Up @@ -640,7 +647,6 @@ get_tunnel_config(const struct netdev *dev, struct smap *args)

if (tnl_cfg.dst_port) {
uint16_t dst_port = ntohs(tnl_cfg.dst_port);
const char *type = netdev_get_type(dev);

if ((!strcmp("geneve", type) && dst_port != GENEVE_DST_PORT) ||
(!strcmp("vxlan", type) && dst_port != VXLAN_DST_PORT) ||
Expand All @@ -654,6 +660,10 @@ get_tunnel_config(const struct netdev *dev, struct smap *args)
smap_add(args, "csum", "true");
}

if (tnl_cfg.is_layer3 && !strcmp("gre", type)) {
smap_add(args, "layer3", "true");
}

if (!tnl_cfg.dont_fragment) {
smap_add(args, "df_default", "false");
}
Expand Down
14 changes: 11 additions & 3 deletions ofproto/tunnel.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "openvswitch/vlog.h"
#include "unaligned.h"
#include "ofproto-dpif.h"
#include "netdev-vport.h"

VLOG_DEFINE_THIS_MODULE(tunnel);

Expand All @@ -49,6 +50,7 @@ struct tnl_match {
bool in_key_flow;
bool ip_src_flow;
bool ip_dst_flow;
bool is_layer3;
};

struct tnl_port {
Expand Down Expand Up @@ -162,6 +164,7 @@ tnl_port_add__(const struct ofport_dpif *ofport, const struct netdev *netdev,
tnl_port->match.ip_dst_flow = cfg->ip_dst_flow;
tnl_port->match.in_key_flow = cfg->in_key_flow;
tnl_port->match.odp_port = odp_port;
tnl_port->match.is_layer3 = netdev_vport_is_layer3(netdev);

map = tnl_match_map(&tnl_port->match);
existing_port = tnl_find_exact(&tnl_port->match, *map);
Expand Down Expand Up @@ -205,7 +208,8 @@ tnl_port_add__(const struct ofport_dpif *ofport, const struct netdev *netdev,
* Returns 0 if successful, otherwise a positive errno value. */
int
tnl_port_add(const struct ofport_dpif *ofport, const struct netdev *netdev,
odp_port_t odp_port, bool native_tnl, const char name[]) OVS_EXCLUDED(rwlock)
odp_port_t odp_port, bool native_tnl, const char name[])
OVS_EXCLUDED(rwlock)
{
bool ok;

Expand All @@ -232,7 +236,8 @@ tnl_port_reconfigure(const struct ofport_dpif *ofport,
fat_rwlock_wrlock(&rwlock);
tnl_port = tnl_find_ofport(ofport);
if (!tnl_port) {
changed = tnl_port_add__(ofport, netdev, odp_port, false, native_tnl, name);
changed = tnl_port_add__(ofport, netdev, odp_port, false, native_tnl,
name);
} else if (tnl_port->netdev != netdev
|| tnl_port->match.odp_port != odp_port
|| tnl_port->change_seq != netdev_get_change_seq(tnl_port->netdev)) {
Expand Down Expand Up @@ -383,7 +388,6 @@ tnl_wc_init(struct flow *flow, struct flow_wildcards *wc)
}
/* Match on packet_type for tunneled packets.*/
wc->masks.packet_type = OVS_BE32_MAX;

}
}

Expand Down Expand Up @@ -562,6 +566,7 @@ tnl_find(const struct flow *flow) OVS_REQ_RDLOCK(rwlock)
match.in_key_flow = in_key_flow;
match.ip_dst_flow = ip_dst_flow;
match.ip_src_flow = ip_src == IP_SRC_FLOW;
match.is_layer3 = flow->packet_type != htonl(PT_ETH);

tnl_port = tnl_find_exact(&match, map);
if (tnl_port) {
Expand Down Expand Up @@ -611,6 +616,9 @@ tnl_match_fmt(const struct tnl_match *match, struct ds *ds)
} else {
ds_put_format(ds, ", key=%#"PRIx64, ntohll(match->in_key));
}
if (match->is_layer3) {
ds_put_cstr(ds, ", layer3");
}

ds_put_format(ds, ", dp port=%"PRIu32, match->odp_port);
}
Expand Down
16 changes: 11 additions & 5 deletions tests/tunnel-push-pop-ipv6.at
Original file line number Diff line number Diff line change
Expand Up @@ -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=gre \
options:remote_ip=2001:cafe::92 options:key=455 options:layer3=true ofport_request=6\
], [0])

AT_CHECK([ovs-appctl dpif/show], [0], [dnl
Expand All @@ -25,6 +27,7 @@ dummy@ovs-dummy: hit:0 missed:0
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: (gre: key=455, layer3=true, remote_ip=2001:cafe::92)
])

dnl First setup dummy interface IP address, then add the route
Expand Down Expand Up @@ -124,18 +127,21 @@ AT_CHECK([tail -1 stdout], [0],

dnl Check decapsulation of GRE packet
AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6486dd60000000006a2f402001cafe0000000000000000000000922001cafe00000000000000000000008820006558000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6486dd60000000006a2f402001cafe0000000000000000000000922001cafe00000000000000000000008820006558000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
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=?, errs=?, frame=?, over=?, crc=?
port 3: rx pkts=2, bytes=196, drop=?, errs=?, frame=?, over=?, crc=?
])

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'])
AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6486dd60000000005a2f402001cafe0000000000000000000000922001cafe00000000000000000000008820000800000001c745000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
ovs-appctl time/warp 1000
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=?, errs=?, frame=?, over=?, crc=?
AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port 6'], [0], [dnl
port 6: rx pkts=2, bytes=168, drop=?, errs=?, frame=?, over=?, crc=?
])

dnl Check decapsulation of Geneve packet with options
Expand Down
33 changes: 28 additions & 5 deletions tests/tunnel-push-pop.at
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ AT_CHECK([ovs-vsctl add-port int-br t2 -- set Interface t2 type=vxlan \
options:remote_ip=flow options:key=123 ofport_request=5\
-- add-port int-br t5 -- set Interface t5 type=geneve \
options:remote_ip=1.1.2.93 options:out_key=flow options:egress_pkt_mark=1234 ofport_request=6\
-- add-port int-br t6 -- set Interface t6 type=gre \
options:remote_ip=1.1.2.92 options:key=456 options:layer3=true ofport_request=7\
], [0])

AT_CHECK([ovs-appctl dpif/show], [0], [dnl
Expand All @@ -28,6 +30,7 @@ dummy@ovs-dummy: hit:0 missed:0
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/6081: (geneve: egress_pkt_mark=1234, out_key=flow, remote_ip=1.1.2.93)
t6 7/3: (gre: key=456, layer3=true, remote_ip=1.1.2.92)
])

dnl First setup dummy interface IP address, then add the route
Expand Down Expand Up @@ -124,6 +127,13 @@ 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=0x4000),gre((flags=0x2000,proto=0x6558),key=0x1c8)),out_port(100))
])

dnl Check L3GRE tunnel push
AT_CHECK([ovs-ofctl add-flow int-br action=7])
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:01),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=0x4000),gre((flags=0x2000,proto=0x800),key=0x1c8)),out_port(100))
])

dnl Check Geneve tunnel push
AT_CHECK([ovs-ofctl add-flow int-br "actions=set_field:1.1.2.92->tun_dst,5"])
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])
Expand All @@ -148,18 +158,31 @@ AT_CHECK([tail -1 stdout], [0],

dnl Check decapsulation of GRE packet
AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820006558000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820006558000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820006558000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
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=?, errs=?, frame=?, over=?, crc=?
port 3: rx pkts=3, bytes=294, drop=?, errs=?, frame=?, over=?, crc=?
])

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 'aa55aa550000001b213cab6408004500007079464000402fba630101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007079464000402fba630101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007079464000402fba630101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
ovs-appctl time/warp 1000
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=?, errs=?, frame=?, over=?, crc=?
AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port 7'], [0], [dnl
port 7: rx pkts=3, bytes=252, drop=?, errs=?, frame=?, over=?, crc=?
])

dnl Check GREL3 only accepts non-fragmented packets?
AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])

AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port [[37]]' | sort], [0], [dnl
port 3: rx pkts=3, bytes=294, drop=?, errs=?, frame=?, over=?, crc=?
port 7: rx pkts=3, bytes=252, drop=?, errs=?, frame=?, over=?, crc=?
])

dnl Check decapsulation of Geneve packet with options
Expand Down
24 changes: 22 additions & 2 deletions vswitchd/vswitch.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2135,8 +2135,8 @@

<dt><code>gre</code></dt>
<dd>
An Ethernet over RFC 2890 Generic Routing Encapsulation over IPv4/IPv6
tunnel.
Generic Routing Encapsulation (GRE) over IPv4/IPv6 tunnel,
configurable to encapsulate layer 2 or layer 3 traffic.
</dd>

<dt><code>vxlan</code></dt>
Expand Down Expand Up @@ -2385,6 +2385,26 @@

</group>

<group title="Tunnel Options: gre only">
<p>
<code>gre</code> interfaces support these options.
</p>

<column name="options" key="layer3" type='{"type": "boolean"}'>
<p>
By default, or if set to false, the tunnel carries L2 packets (with
an Ethernet header). If set to true, the tunnel carries L3 packets
(without an Ethernet header present).
</p>

<p>
A single GRE tunnel cannot carry both L2 and L3 packets, but the
same effect can be realized by creating two tunnels with different
<code>layer3</code> settings and otherwise the same configuration.
</p>
</column>
</group>

<group title="Tunnel Options: gre, geneve, and vxlan">
<p>
<code>gre</code>, <code>geneve</code>, and
Expand Down

0 comments on commit 63171f0

Please sign in to comment.