};
#ifndef __KERNEL__
-#define TNL_PUSH_HEADER_SIZE 128
+#define TNL_PUSH_HEADER_SIZE 512
/*
* struct ovs_action_push_tnl - %OVS_ACTION_ATTR_TUNNEL_PUSH
struct flow_tnl *tnl = &md->tunnel;
struct genevehdr *gnh;
unsigned int hlen;
+ int err;
memset(md, 0, sizeof *md);
if (GENEVE_BASE_HLEN > dp_packet_size(packet)) {
return EINVAL;
}
- if (gnh->opt_len && gnh->critical) {
- VLOG_WARN_RL(&err_rl, "unknown geneve critical options: %"PRIu8" bytes\n",
- gnh->opt_len * 4);
- return EINVAL;
- }
-
if (gnh->proto_type != htons(ETH_TYPE_TEB)) {
VLOG_WARN_RL(&err_rl, "unknown geneve encapsulated protocol: %#x\n",
ntohs(gnh->proto_type));
tnl->tun_id = htonll(ntohl(get_16aligned_be32(&gnh->vni)) >> 8);
tnl->flags |= FLOW_TNL_F_KEY;
+ err = tun_metadata_from_geneve_header(gnh->options, gnh->opt_len * 4,
+ &tnl->metadata);
+ if (err) {
+ VLOG_WARN_RL(&err_rl, "invalid geneve options");
+ return err;
+ }
+
dp_packet_reset_packet(packet, hlen);
return 0;
struct netdev_vport *dev = netdev_vport_cast(netdev);
struct netdev_tunnel_config *tnl_cfg;
struct genevehdr *gnh;
+ int opt_len;
+ bool crit_opt;
/* XXX: RCUfy tnl_cfg. */
ovs_mutex_lock(&dev->mutex);
gnh = udp_build_header(tnl_cfg, tnl_flow, data);
- gnh->oam = !!(tnl_flow->tunnel.flags & FLOW_TNL_F_OAM);
- gnh->proto_type = htons(ETH_TYPE_TEB);
put_16aligned_be32(&gnh->vni, htonl(ntohll(tnl_flow->tunnel.tun_id) << 8));
ovs_mutex_unlock(&dev->mutex);
- data->header_len = GENEVE_BASE_HLEN;
+
+ opt_len = tun_metadata_to_geneve_header(&tnl_flow->tunnel.metadata,
+ gnh->options, &crit_opt);
+
+ gnh->opt_len = opt_len / 4;
+ gnh->oam = !!(tnl_flow->tunnel.flags & FLOW_TNL_F_OAM);
+ gnh->critical = crit_opt ? 1 : 0;
+ gnh->proto_type = htons(ETH_TYPE_TEB);
+
+ data->header_len = GENEVE_BASE_HLEN + opt_len;
data->tnl_type = OVS_VPORT_TYPE_GENEVE;
return 0;
}
const struct hmap *portno_names, struct ds *ds,
bool verbose);
+struct geneve_scan {
+ struct geneve_opt d[63];
+ int len;
+};
+
+static int scan_geneve(const char *s, struct geneve_scan *key,
+ struct geneve_scan *mask);
+static void format_geneve_opts(const struct geneve_opt *opt,
+ const struct geneve_opt *mask, int opts_len,
+ struct ds *, bool verbose);
+
static struct nlattr *generate_all_wildcard_mask(const struct attr_len_tbl tbl[],
int max, struct ofpbuf *,
const struct nlattr *key);
gnh = format_udp_tnl_push_header(ds, ip);
- ds_put_format(ds, "geneve(%svni=0x%"PRIx32")",
+ ds_put_format(ds, "geneve(%s%svni=0x%"PRIx32,
gnh->oam ? "oam," : "",
+ gnh->critical ? "crit," : "",
ntohl(get_16aligned_be32(&gnh->vni)) >> 8);
+
+ if (gnh->opt_len) {
+ ds_put_cstr(ds, ",options(");
+ format_geneve_opts(gnh->options, NULL, gnh->opt_len * 4,
+ ds, false);
+ ds_put_char(ds, ')');
+ }
+
+ ds_put_char(ds, ')');
} else if (data->tnl_type == OVS_VPORT_TYPE_GRE) {
const struct gre_base_hdr *greh;
ovs_16aligned_be32 *options;
struct genevehdr *gnh = (struct genevehdr *) (udp + 1);
memset(gnh, 0, sizeof *gnh);
+ header_len = sizeof *eth + sizeof *ip +
+ sizeof *udp + sizeof *gnh;
+
if (ovs_scan_len(s, &n, "oam,")) {
gnh->oam = 1;
}
- if (!ovs_scan_len(s, &n, "vni=0x%"SCNx32"))", &vni)) {
+ if (ovs_scan_len(s, &n, "crit,")) {
+ gnh->critical = 1;
+ }
+ if (!ovs_scan_len(s, &n, "vni=%"SCNi32, &vni)) {
return -EINVAL;
}
+ if (ovs_scan_len(s, &n, ",options(")) {
+ struct geneve_scan options;
+ int len;
+
+ memset(&options, 0, sizeof options);
+ len = scan_geneve(s + n, &options, NULL);
+ if (!len) {
+ return -EINVAL;
+ }
+
+ memcpy(gnh->options, options.d, options.len);
+ gnh->opt_len = options.len / 4;
+ header_len += options.len;
+
+ n += len;
+ }
+ if (!ovs_scan_len(s, &n, "))")) {
+ return -EINVAL;
+ }
+
gnh->proto_type = htons(ETH_TYPE_TEB);
put_16aligned_be32(&gnh->vni, htonl(vni << 8));
tnl_type = OVS_VPORT_TYPE_GENEVE;
- header_len = sizeof *eth + sizeof *ip +
- sizeof *udp + sizeof *gnh;
} else {
return -EINVAL;
}
#define MASK(PTR, FIELD) PTR ? &PTR->FIELD : NULL
static void
-format_odp_tun_geneve(const struct nlattr *attr,
- const struct nlattr *mask_attr, struct ds *ds,
- bool verbose)
+format_geneve_opts(const struct geneve_opt *opt,
+ const struct geneve_opt *mask, int opts_len,
+ struct ds *ds, bool verbose)
{
- int opts_len = nl_attr_get_size(attr);
- const struct geneve_opt *opt = nl_attr_get(attr);
- const struct geneve_opt *mask = mask_attr ?
- nl_attr_get(mask_attr) : NULL;
-
- if (mask && nl_attr_get_size(attr) != nl_attr_get_size(mask_attr)) {
- ds_put_format(ds, "value len %"PRIuSIZE" different from mask len %"PRIuSIZE,
- nl_attr_get_size(attr), nl_attr_get_size(mask_attr));
- return;
- }
-
while (opts_len > 0) {
unsigned int len;
uint8_t data_len, data_len_mask;
};
}
+static void
+format_odp_tun_geneve(const struct nlattr *attr,
+ const struct nlattr *mask_attr, struct ds *ds,
+ bool verbose)
+{
+ int opts_len = nl_attr_get_size(attr);
+ const struct geneve_opt *opt = nl_attr_get(attr);
+ const struct geneve_opt *mask = mask_attr ?
+ nl_attr_get(mask_attr) : NULL;
+
+ if (mask && nl_attr_get_size(attr) != nl_attr_get_size(mask_attr)) {
+ ds_put_format(ds, "value len %"PRIuSIZE" different from mask len %"PRIuSIZE,
+ nl_attr_get_size(attr), nl_attr_get_size(mask_attr));
+ return;
+ }
+
+ format_geneve_opts(opt, mask, opts_len, ds, verbose);
+}
+
static void
format_odp_tun_attr(const struct nlattr *attr, const struct nlattr *mask_attr,
struct ds *ds, bool verbose)
return 0;
}
-struct geneve_scan {
- struct geneve_opt d[63];
- int len;
-};
-
static int
scan_geneve(const char *s, struct geneve_scan *key, struct geneve_scan *mask)
{
/* Tunnel header */
#define GENEVE_MAX_OPT_SIZE 124
+#define GENEVE_TOT_OPT_SIZE 252
#define GENEVE_CRIT_OPT_TYPE (1 << 7)
memset(&entry->loc, 0, sizeof entry->loc);
}
-int
-tun_metadata_from_geneve_nlattr(const struct nlattr *attr,
- const struct nlattr *flow_attrs,
- size_t flow_attr_len,
- const struct tun_metadata *flow_metadata,
- struct tun_metadata *metadata)
+static int
+tun_metadata_from_geneve__(struct tun_table *map, const struct geneve_opt *opt,
+ const struct geneve_opt *flow_opt, int opts_len,
+ struct tun_metadata *metadata)
{
- bool is_mask = !!flow_attrs;
- struct tun_table *map;
- const struct nlattr *flow;
- int opts_len;
- const struct geneve_opt *flow_opt;
- const struct geneve_opt *opt = nl_attr_get(attr);
-
- if (!is_mask) {
- map = ovsrcu_get(struct tun_table *, &metadata_tab);
- metadata->tab = map;
- } else {
- map = flow_metadata->tab;
- }
-
if (!map) {
return 0;
}
- if (is_mask) {
- const struct nlattr *tnl_key;
- int mask_len = nl_attr_get_size(attr);
-
- tnl_key = nl_attr_find__(flow_attrs, flow_attr_len, OVS_KEY_ATTR_TUNNEL);
- if (!tnl_key) {
- return mask_len ? EINVAL : 0;
- }
-
- flow = nl_attr_find_nested(tnl_key, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS);
- if (!flow) {
- return mask_len ? EINVAL : 0;
- }
-
- if (mask_len != nl_attr_get_size(flow)) {
- return EINVAL;
- }
- } else {
- flow = attr;
- }
-
- opts_len = nl_attr_get_size(flow);
- flow_opt = nl_attr_get(flow);
-
while (opts_len > 0) {
int len;
struct tun_meta_entry *entry;
return 0;
}
-void
-tun_metadata_to_geneve_nlattr_flow(const struct tun_metadata *flow,
- struct ofpbuf *b)
+int
+tun_metadata_from_geneve_nlattr(const struct nlattr *attr,
+ const struct nlattr *flow_attrs,
+ size_t flow_attr_len,
+ const struct tun_metadata *flow_metadata,
+ struct tun_metadata *metadata)
{
struct tun_table *map;
- size_t nlattr_offset;
- int i;
+ bool is_mask = !!flow_attrs;
+ const struct nlattr *flow;
- if (!flow->opt_map) {
- return;
+ if (is_mask) {
+ const struct nlattr *tnl_key;
+ int mask_len = nl_attr_get_size(attr);
+
+ tnl_key = nl_attr_find__(flow_attrs, flow_attr_len, OVS_KEY_ATTR_TUNNEL);
+ if (!tnl_key) {
+ return mask_len ? EINVAL : 0;
+ }
+
+ flow = nl_attr_find_nested(tnl_key, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS);
+ if (!flow) {
+ return mask_len ? EINVAL : 0;
+ }
+
+ if (mask_len != nl_attr_get_size(flow)) {
+ return EINVAL;
+ }
+ } else {
+ flow = attr;
}
+ if (!is_mask) {
+ map = ovsrcu_get(struct tun_table *, &metadata_tab);
+ metadata->tab = map;
+ } else {
+ map = flow_metadata->tab;
+ }
+
+ return tun_metadata_from_geneve__(map, nl_attr_get(attr), nl_attr_get(flow),
+ nl_attr_get_size(flow), metadata);
+}
+
+int
+tun_metadata_from_geneve_header(const struct geneve_opt *opts, int opt_len,
+ struct tun_metadata *metadata)
+{
+ struct tun_table *map;
+
+ map = ovsrcu_get(struct tun_table *, &metadata_tab);
+ metadata->tab = map;
+
+ return tun_metadata_from_geneve__(map, opts, opts, opt_len, metadata);
+}
+
+static void
+tun_metadata_to_geneve__(const struct tun_metadata *flow, struct ofpbuf *b,
+ bool *crit_opt)
+{
+ struct tun_table *map;
+ int i;
+
map = flow->tab;
if (!map) {
map = ovsrcu_get(struct tun_table *, &metadata_tab);
}
- /* For all intents and purposes, the Geneve options are nested
- * attributes even if this doesn't show up directly to netlink. It's
- * similar enough that we can use the same mechanism. */
- nlattr_offset = nl_msg_start_nested(b, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS);
+ *crit_opt = false;
ULLONG_FOR_EACH_1 (i, flow->opt_map) {
struct tun_meta_entry *entry = &map->entries[i];
opt->r3 = 0;
memcpy_from_metadata(opt + 1, flow, &entry->loc);
+ *crit_opt |= !!(opt->type & GENEVE_CRIT_OPT_TYPE);
}
+}
+
+void
+tun_metadata_to_geneve_nlattr_flow(const struct tun_metadata *flow,
+ struct ofpbuf *b)
+{
+ size_t nlattr_offset;
+ bool crit_opt;
+
+ if (!flow->opt_map) {
+ return;
+ }
+
+ /* For all intents and purposes, the Geneve options are nested
+ * attributes even if this doesn't show up directly to netlink. It's
+ * similar enough that we can use the same mechanism. */
+ nlattr_offset = nl_msg_start_nested(b, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS);
+
+ tun_metadata_to_geneve__(flow, b, &crit_opt);
nl_msg_end_nested(b, nlattr_offset);
}
+int
+tun_metadata_to_geneve_header(const struct tun_metadata *flow,
+ struct geneve_opt *opts, bool *crit_opt)
+{
+ struct ofpbuf b;
+
+ ofpbuf_use_stack(&b, opts, GENEVE_TOT_OPT_SIZE);
+ tun_metadata_to_geneve__(flow, &b, crit_opt);
+
+ return b.size;
+}
+
void
tun_metadata_to_geneve_nlattr_mask(const struct ofpbuf *key,
const struct tun_metadata *mask,
struct ofputil_geneve_table_mod;
struct ofputil_geneve_table_reply;
struct tun_table;
+struct geneve_opt;
#define TUN_METADATA_NUM_OPTS 64
#define TUN_METADATA_TOT_OPT_SIZE 256
size_t flow_attr_len,
const struct tun_metadata *flow_metadata,
struct tun_metadata *metadata);
+int tun_metadata_from_geneve_header(const struct geneve_opt *, int opt_len,
+ struct tun_metadata *metadata);
+
void tun_metadata_to_geneve_nlattr_flow(const struct tun_metadata *flow,
struct ofpbuf *);
void tun_metadata_to_geneve_nlattr_mask(const struct ofpbuf *key,
const struct tun_metadata *mask,
const struct tun_metadata *flow,
struct ofpbuf *);
+int tun_metadata_to_geneve_header(const struct tun_metadata *flow,
+ struct geneve_opt *, bool *crit_opt);
+
void tun_metadata_to_nx_match(struct ofpbuf *b, enum ofp_version oxm,
const struct match *);
void tun_metadata_match_format(struct ds *, const struct match *);
tnl_push(tnl_port(4),header(size=46,type=3,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=47,tos=0,ttl=64,frag=0x40),gre((flags=0xa000,proto=0x6558),csum=0x0,key=0x1e241)),out_port(1))
tnl_push(tnl_port(6),header(size=50,type=4,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=4789,csum=0x0),vxlan(flags=0x8000000,vni=0x1c7)),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=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))
])
AT_CHECK_UNQUOTED([ovstest test-odp parse-actions < actions.txt], [0],
[Datapath actions: tnl_push(tnl_port(6081),header(size=50,type=5,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=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=6081,csum=0x0),geneve(vni=0x7b)),out_port(100))
])
+dnl Check Geneve tunnel push with options
+AT_CHECK([ovs-ofctl add-geneve-map int-br "{class=0xffff,type=0x80,len=4}->tun_metadata0"])
+AT_CHECK([ovs-ofctl add-flow int-br "actions=set_field:1.1.2.92->tun_dst,set_field:0xa->tun_metadata0,5"])
+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(6081),header(size=58,type=5,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=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=6081,csum=0x0),geneve(crit,vni=0x7b,options({class=0xffff,type=0x80,len=4,0xa}))),out_port(100))
+])
+
dnl Check decapsulation of GRE packet
AT_CHECK([ovs-appctl netdev-dummy/receive p0 '001b213cac30001b213cab6408004500007e79464000402f99080101025c0101025820006558000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
ovs-appctl time/warp 1000
port 3: rx pkts=1, bytes=98, drop=0, errs=0, frame=0, over=0, crc=0
])
+dnl Check decapsulation of Geneve packet with options
+AT_CHECK([ovs-ofctl del-flows int-br])
+AT_CHECK([ovs-ofctl add-flow int-br "tun_metadata0=0xa/0xf,actions=5"])
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 '001b213cac30001b213cab64080045000096794640004011ba630101025c01010258308817c1008200000400655800007b00ffff80010000000affff00010000000bfe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
+
+ovs-appctl time/warp 1000
+AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port 5'], [0], [dnl
+ port 5: rx pkts=1, bytes=98, drop=0, errs=0, frame=0, over=0, crc=0
+])
+AT_CHECK([ovs-appctl dpif/dump-flows int-br], [0], [dnl
+tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,ttl=64,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}),flags(-df-csum+key)),skb_mark(0),recirc_id(0),in_port(6081),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:drop
+])
+
OVS_VSWITCHD_STOP
AT_CLEANUP