#include "openvswitch/vlog.h"
#include "openvswitch/match.h"
#include "odp-netlink-macros.h"
+#include "csum.h"
VLOG_DEFINE_THIS_MODULE(odp_util);
static int parse_odp_key_mask_attr(struct parse_odp_context *, const char *,
struct ofpbuf *, struct ofpbuf *);
+
+static int parse_odp_key_mask_attr__(struct parse_odp_context *, const char *,
+ struct ofpbuf *, struct ofpbuf *);
+
static void format_odp_key_attr(const struct nlattr *a,
const struct nlattr *ma,
const struct hmap *portno_names, struct ds *ds,
const ovs_32aligned_u128 *mask, bool verbose);
static int scan_u128(const char *s, ovs_u128 *value, ovs_u128 *mask);
-static int parse_odp_action(const char *s, const struct simap *port_names,
+static int parse_odp_action(struct parse_odp_context *context, const char *s,
+ struct ofpbuf *actions);
+
+static int parse_odp_action__(struct parse_odp_context *context, const char *s,
struct ofpbuf *actions);
/* Returns one the following for the action with the given OVS_ACTION_ATTR_*
switch ((enum ovs_action_attr) type) {
case OVS_ACTION_ATTR_OUTPUT: return sizeof(uint32_t);
+ case OVS_ACTION_ATTR_LB_OUTPUT: return sizeof(uint32_t);
case OVS_ACTION_ATTR_TRUNC: return sizeof(struct ovs_action_trunc);
case OVS_ACTION_ATTR_TUNNEL_PUSH: return ATTR_LEN_VARIABLE;
case OVS_ACTION_ATTR_TUNNEL_POP: return sizeof(uint32_t);
case OVS_ACTION_ATTR_CLONE: return ATTR_LEN_VARIABLE;
case OVS_ACTION_ATTR_PUSH_NSH: return ATTR_LEN_VARIABLE;
case OVS_ACTION_ATTR_POP_NSH: return 0;
+ case OVS_ACTION_ATTR_CHECK_PKT_LEN: return ATTR_LEN_VARIABLE;
+ case OVS_ACTION_ATTR_DROP: return sizeof(uint32_t);
case OVS_ACTION_ATTR_UNSPEC:
case __OVS_ACTION_ATTR_MAX:
format_nsh_key(struct ds *ds, const struct ovs_key_nsh *key)
{
ds_put_format(ds, "flags=%d", key->flags);
- ds_put_format(ds, "ttl=%d", key->ttl);
+ ds_put_format(ds, ",ttl=%d", key->ttl);
ds_put_format(ds, ",mdtype=%d", key->mdtype);
ds_put_format(ds, ",np=%d", key->np);
ds_put_format(ds, ",spi=0x%x",
} else {
VLOG_WARN("%s Invalid ERSPAN version %d\n", __func__, ersh->ver);
}
+ } else if (data->tnl_type == OVS_VPORT_TYPE_GTPU) {
+ const struct gtpuhdr *gtph;
+
+ gtph = format_udp_tnl_push_header(ds, udp);
+
+ ds_put_format(ds, "gtpu(flags=0x%"PRIx8
+ ",msgtype=%"PRIu8",teid=0x%"PRIx32")",
+ gtph->md.flags, gtph->md.msgtype,
+ ntohl(get_16aligned_be32(>ph->teid)));
}
+
ds_put_format(ds, ")");
}
[OVS_CT_ATTR_HELPER] = { .type = NL_A_STRING, .optional = true,
.min_len = 1, .max_len = 16 },
[OVS_CT_ATTR_NAT] = { .type = NL_A_UNSPEC, .optional = true },
+ [OVS_CT_ATTR_TIMEOUT] = { .type = NL_A_STRING, .optional = true,
+ .min_len = 1, .max_len = 32 },
};
static void
ovs_32aligned_u128 mask;
} *label;
const uint32_t *mark;
- const char *helper;
+ const char *helper, *timeout;
uint16_t zone;
bool commit, force;
const struct nlattr *nat;
mark = a[OVS_CT_ATTR_MARK] ? nl_attr_get(a[OVS_CT_ATTR_MARK]) : NULL;
label = a[OVS_CT_ATTR_LABELS] ? nl_attr_get(a[OVS_CT_ATTR_LABELS]): NULL;
helper = a[OVS_CT_ATTR_HELPER] ? nl_attr_get(a[OVS_CT_ATTR_HELPER]) : NULL;
+ timeout = a[OVS_CT_ATTR_TIMEOUT] ?
+ nl_attr_get(a[OVS_CT_ATTR_TIMEOUT]) : NULL;
nat = a[OVS_CT_ATTR_NAT];
ds_put_format(ds, "ct");
- if (commit || force || zone || mark || label || helper || nat) {
+ if (commit || force || zone || mark || label || helper || timeout || nat) {
ds_put_cstr(ds, "(");
if (commit) {
ds_put_format(ds, "commit,");
if (helper) {
ds_put_format(ds, "helper=%s,", helper);
}
+ if (timeout) {
+ ds_put_format(ds, "timeout=%s", timeout);
+ }
if (nat) {
format_odp_ct_nat(ds, nat);
}
ds_put_cstr(ds, "))");
}
+static void
+format_odp_check_pkt_len_action(struct ds *ds, const struct nlattr *attr,
+ const struct hmap *portno_names OVS_UNUSED)
+{
+ static const struct nl_policy ovs_cpl_policy[] = {
+ [OVS_CHECK_PKT_LEN_ATTR_PKT_LEN] = { .type = NL_A_U16 },
+ [OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER] = { .type = NL_A_NESTED },
+ [OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL]
+ = { .type = NL_A_NESTED },
+ };
+ struct nlattr *a[ARRAY_SIZE(ovs_cpl_policy)];
+ ds_put_cstr(ds, "check_pkt_len");
+ if (!nl_parse_nested(attr, ovs_cpl_policy, a, ARRAY_SIZE(a))) {
+ ds_put_cstr(ds, "(error)");
+ return;
+ }
+
+ if (!a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER] ||
+ !a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL]) {
+ ds_put_cstr(ds, "(error)");
+ return;
+ }
+
+ uint16_t pkt_len = nl_attr_get_u16(a[OVS_CHECK_PKT_LEN_ATTR_PKT_LEN]);
+ ds_put_format(ds, "(size=%u,gt(", pkt_len);
+ const struct nlattr *acts;
+ acts = a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER];
+ format_odp_actions(ds, nl_attr_get(acts), nl_attr_get_size(acts),
+ portno_names);
+
+ ds_put_cstr(ds, "),le(");
+ acts = a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL];
+ format_odp_actions(ds, nl_attr_get(acts), nl_attr_get_size(acts),
+ portno_names);
+ ds_put_cstr(ds, "))");
+}
static void
format_odp_action(struct ds *ds, const struct nlattr *a,
case OVS_ACTION_ATTR_OUTPUT:
odp_portno_name_format(portno_names, nl_attr_get_odp_port(a), ds);
break;
+ case OVS_ACTION_ATTR_LB_OUTPUT:
+ ds_put_format(ds, "lb_output(%"PRIu32")", nl_attr_get_u32(a));
+ break;
case OVS_ACTION_ATTR_TRUNC: {
const struct ovs_action_trunc *trunc =
nl_attr_get_unspec(a, sizeof *trunc);
case OVS_ACTION_ATTR_POP_NSH:
ds_put_cstr(ds, "pop_nsh()");
break;
+ case OVS_ACTION_ATTR_CHECK_PKT_LEN:
+ format_odp_check_pkt_len_action(ds, a, portno_names);
+ break;
+ case OVS_ACTION_ATTR_DROP:
+ ds_put_cstr(ds, "drop");
+ break;
case OVS_ACTION_ATTR_UNSPEC:
case __OVS_ACTION_ATTR_MAX:
default:
struct gre_base_hdr *greh;
struct erspan_base_hdr *ersh;
struct erspan_md2 *md2;
- uint16_t gre_proto, gre_flags, dl_type, udp_src, udp_dst, csum, sid;
+ uint16_t gre_proto, gre_flags, dl_type, udp_src, udp_dst, udp_csum, sid;
ovs_be32 sip, dip;
uint32_t tnl_type = 0, header_len = 0, ip_len = 0, erspan_idx = 0;
void *l3, *l4;
int n = 0;
uint8_t hwid, dir;
+ uint32_t teid;
+ uint8_t gtpu_flags, gtpu_msgtype;
if (!ovs_scan_len(s, &n, "tnl_push(tnl_port(%"SCNi32"),", &data->tnl_port)) {
return -EINVAL;
if (eth->eth_type == htons(ETH_TYPE_IP)) {
/* IPv4 */
uint16_t ip_frag_off;
+ memset(ip, 0, sizeof(*ip));
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),
put_16aligned_be32(&ip->ip_src, sip);
put_16aligned_be32(&ip->ip_dst, dip);
ip->ip_frag_off = htons(ip_frag_off);
+ ip->ip_ihl_ver = IP_IHL_VER(5, 4);
ip_len = sizeof *ip;
+ ip->ip_csum = csum(ip, ip_len);
} else {
char sip6_s[IPV6_SCAN_LEN + 1];
char dip6_s[IPV6_SCAN_LEN + 1];
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"),",
- &udp_src, &udp_dst, &csum)) {
+ &udp_src, &udp_dst, &udp_csum)) {
uint32_t vx_flags, vni;
udp->udp_src = htons(udp_src);
udp->udp_dst = htons(udp_dst);
udp->udp_len = 0;
- udp->udp_csum = htons(csum);
+ udp->udp_csum = htons(udp_csum);
if (ovs_scan_len(s, &n, "vxlan(flags=0x%"SCNx32",vni=0x%"SCNx32"))",
&vx_flags, &vni)) {
ovs_16aligned_be32 *options = (ovs_16aligned_be32 *) (greh + 1);
if (greh->flags & htons(GRE_CSUM)) {
+ uint16_t csum;
if (!ovs_scan_len(s, &n, ",csum=0x%"SCNx16, &csum)) {
return -EINVAL;
}
header_len = sizeof *eth + ip_len + ERSPAN_GREHDR_LEN +
sizeof *ersh + ERSPAN_V2_MDSIZE;
+
+ } else if (ovs_scan_len(s, &n, "gtpu(flags=%"SCNi8",msgtype=%"
+ SCNu8",teid=0x%"SCNx32"))",
+ >pu_flags, >pu_msgtype, &teid)) {
+ struct gtpuhdr *gtph = (struct gtpuhdr *) (udp + 1);
+
+ gtph->md.flags = gtpu_flags;
+ gtph->md.msgtype = gtpu_msgtype;
+ put_16aligned_be32(>ph->teid, htonl(teid));
+ tnl_type = OVS_VPORT_TYPE_GTPU;
+ header_len = sizeof *eth + ip_len +
+ sizeof *udp + sizeof *gtph;
} else {
return -EINVAL;
}
const char *s = s_;
if (ovs_scan(s, "ct")) {
- const char *helper = NULL;
- size_t helper_len = 0;
+ const char *helper = NULL, *timeout = NULL;
+ size_t helper_len = 0, timeout_len = 0;
bool commit = false;
bool force_commit = false;
uint16_t zone = 0;
s += helper_len;
continue;
}
+ if (ovs_scan(s, "timeout=%n", &n)) {
+ s += n;
+ timeout_len = strcspn(s, delimiters_end);
+ if (!timeout_len || timeout_len > 31) {
+ return -EINVAL;
+ }
+ timeout = s;
+ s += timeout_len;
+ continue;
+ }
n = scan_ct_nat(s, &nat_params);
if (n > 0) {
nl_msg_put_string__(actions, OVS_CT_ATTR_HELPER, helper,
helper_len);
}
+ if (timeout) {
+ nl_msg_put_string__(actions, OVS_CT_ATTR_TIMEOUT, timeout,
+ timeout_len);
+ }
if (have_nat) {
nl_msg_put_ct_nat(&nat_params, actions);
}
}
static int
-parse_action_list(const char *s, const struct simap *port_names,
+parse_action_list(struct parse_odp_context *context, const char *s,
struct ofpbuf *actions)
{
int n = 0;
if (s[n] == ')') {
break;
}
- retval = parse_odp_action(s + n, port_names, actions);
+ retval = parse_odp_action(context, s + n, actions);
if (retval < 0) {
return retval;
}
return n;
}
+
static int
-parse_odp_action(const char *s, const struct simap *port_names,
+parse_odp_action(struct parse_odp_context *context, const char *s,
struct ofpbuf *actions)
+{
+ int retval;
+
+ context->depth++;
+
+ if (context->depth == MAX_ODP_NESTED) {
+ retval = -EINVAL;
+ } else {
+ retval = parse_odp_action__(context, s, actions);
+ }
+
+ context->depth--;
+
+ return retval;
+}
+
+
+static int
+parse_odp_action__(struct parse_odp_context *context, const char *s,
+ struct ofpbuf *actions)
{
{
uint32_t port;
}
}
+ {
+ uint32_t bond_id;
+ int n;
+
+ if (ovs_scan(s, "lb_output(%"PRIu32")%n", &bond_id, &n)) {
+ nl_msg_put_u32(actions, OVS_ACTION_ATTR_LB_OUTPUT, bond_id);
+ return n;
+ }
+ }
+
{
uint32_t max_len;
int n;
}
}
- if (port_names) {
+ if (context->port_names) {
int len = strcspn(s, delimiters);
struct simap_node *node;
- node = simap_find_len(port_names, s, len);
+ node = simap_find_len(context->port_names, s, len);
if (node) {
nl_msg_put_u32(actions, OVS_ACTION_ATTR_OUTPUT, node->data);
return len;
struct ofpbuf maskbuf = OFPBUF_STUB_INITIALIZER(mask);
struct nlattr *nested, *key;
size_t size;
- struct parse_odp_context context = (struct parse_odp_context) {
- .port_names = port_names,
- };
start_ofs = nl_msg_start_nested(actions, OVS_ACTION_ATTR_SET);
- retval = parse_odp_key_mask_attr(&context, s + 4, actions, &maskbuf);
+ retval = parse_odp_key_mask_attr(context, s + 4, actions, &maskbuf);
if (retval < 0) {
ofpbuf_uninit(&maskbuf);
return retval;
actions_ofs = nl_msg_start_nested(actions,
OVS_SAMPLE_ATTR_ACTIONS);
- int retval = parse_action_list(s + n, port_names, actions);
- if (retval < 0)
+ int retval = parse_action_list(context, s + n, actions);
+ if (retval < 0) {
return retval;
+ }
+
n += retval;
nl_msg_end_nested(actions, actions_ofs);
int n = 6;
actions_ofs = nl_msg_start_nested(actions, OVS_ACTION_ATTR_CLONE);
- int retval = parse_action_list(s + n, port_names, actions);
+ int retval = parse_action_list(context, s + n, actions);
if (retval < 0) {
return retval;
}
}
}
+ {
+ uint16_t pkt_len;
+ int n = -1;
+ if (ovs_scan(s, "check_pkt_len(size=%"SCNi16",gt(%n", &pkt_len, &n)) {
+ size_t cpl_ofs, actions_ofs;
+ cpl_ofs = nl_msg_start_nested(actions,
+ OVS_ACTION_ATTR_CHECK_PKT_LEN);
+ nl_msg_put_u16(actions, OVS_CHECK_PKT_LEN_ATTR_PKT_LEN, pkt_len);
+ actions_ofs = nl_msg_start_nested(
+ actions, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER);
+
+ int retval;
+ if (!strncasecmp(s + n, "drop", 4)) {
+ n += 4;
+ } else {
+ retval = parse_action_list(context, s + n, actions);
+ if (retval < 0) {
+ return retval;
+ }
+
+ n += retval;
+ }
+ nl_msg_end_nested(actions, actions_ofs);
+ retval = -1;
+ if (!ovs_scan(s + n, "),le(%n", &retval)) {
+ return -EINVAL;
+ }
+ n += retval;
+
+ actions_ofs = nl_msg_start_nested(
+ actions, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL);
+ if (!strncasecmp(s + n, "drop", 4)) {
+ n += 4;
+ } else {
+ retval = parse_action_list(context, s + n, actions);
+ if (retval < 0) {
+ return retval;
+ }
+ n += retval;
+ }
+ nl_msg_end_nested(actions, actions_ofs);
+ nl_msg_end_nested(actions, cpl_ofs);
+ return s[n + 1] == ')' ? n + 2 : -EINVAL;
+ }
+ }
+
{
int retval;
return n;
}
}
+
return -EINVAL;
}
size_t old_size;
if (!strcasecmp(s, "drop")) {
+ nl_msg_put_u32(actions, OVS_ACTION_ATTR_DROP, XLATE_OK);
return 0;
}
+ struct parse_odp_context context = (struct parse_odp_context) {
+ .port_names = port_names,
+ };
+
old_size = actions->size;
for (;;) {
int retval;
return 0;
}
- retval = parse_odp_action(s, port_names, actions);
+ retval = parse_odp_action(&context, s, actions);
+
if (retval < 0 || !strchr(delimiters, s[retval])) {
actions->size = old_size;
return -retval;
[OVS_TUNNEL_KEY_ATTR_IPV6_SRC] = { .len = 16 },
[OVS_TUNNEL_KEY_ATTR_IPV6_DST] = { .len = 16 },
[OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS] = { .len = ATTR_LEN_VARIABLE },
+ [OVS_TUNNEL_KEY_ATTR_GTPU_OPTS] = { .len = ATTR_LEN_VARIABLE },
};
const struct attr_len_tbl ovs_flow_key_attr_lens[OVS_KEY_ATTR_MAX + 1] = {
}
/* Parses OVS_KEY_ATTR_NSH attribute 'attr' into 'nsh' and 'nsh_mask' and
- * returns fitness. If 'errorp' is nonnull and the function returns
- * ODP_FIT_ERROR, stores a malloc()'d error message in '*errorp'. */
-enum odp_key_fitness
-odp_nsh_key_from_attr(const struct nlattr *attr, struct ovs_key_nsh *nsh,
- struct ovs_key_nsh *nsh_mask, char **errorp)
+ * returns fitness. If the attribute is a key, 'is_mask' should be false;
+ * if it is a mask, 'is_mask' should be true. If 'errorp' is nonnull and the
+ * function returns ODP_FIT_ERROR, stores a malloc()'d error message in
+ * '*errorp'. */
+static enum odp_key_fitness
+odp_nsh_key_from_attr__(const struct nlattr *attr, bool is_mask,
+ struct ovs_key_nsh *nsh, struct ovs_key_nsh *nsh_mask,
+ char **errorp)
{
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
if (errorp) {
return ODP_FIT_TOO_MUCH;
}
- if (has_md1 && nsh->mdtype != NSH_M_TYPE1 && !nsh_mask) {
+ if (!is_mask && has_md1 && nsh->mdtype != NSH_M_TYPE1 && !nsh_mask) {
odp_parse_error(&rl, errorp, "OVS_NSH_KEY_ATTR_MD1 present but "
"declared mdtype %"PRIu8" is not %d (NSH_M_TYPE1)",
nsh->mdtype, NSH_M_TYPE1);
return ODP_FIT_PERFECT;
}
+/* Parses OVS_KEY_ATTR_NSH attribute 'attr' into 'nsh' and 'nsh_mask' and
+ * returns fitness. The attribute should be a key (not a mask). If 'errorp'
+ * is nonnull and the function returns ODP_FIT_ERROR, stores a malloc()'d error
+ * message in '*errorp'. */
+enum odp_key_fitness
+odp_nsh_key_from_attr(const struct nlattr *attr, struct ovs_key_nsh *nsh,
+ struct ovs_key_nsh *nsh_mask, char **errorp)
+{
+ return odp_nsh_key_from_attr__(attr, false, nsh, nsh_mask, errorp);
+}
+
/* Parses OVS_KEY_ATTR_TUNNEL attribute 'attr' into 'tun' and returns fitness.
* If the attribute is a key, 'is_mask' should be false; if it is a mask,
* 'is_mask' should be true. If 'errorp' is nonnull and the function returns
}
break;
}
+ case OVS_TUNNEL_KEY_ATTR_GTPU_OPTS: {
+ const struct gtpu_metadata *opts = nl_attr_get(a);
+
+ tun->gtpu_flags = opts->flags;
+ tun->gtpu_msgtype = opts->msgtype;
+ break;
+ }
default:
/* Allow this to show up as unexpected, if there are unknown
&opts, sizeof(opts));
}
+ if ((!tnl_type || !strcmp(tnl_type, "gtpu")) &&
+ (tun_key->gtpu_flags && tun_key->gtpu_msgtype)) {
+ struct gtpu_metadata opts;
+
+ opts.flags = tun_key->gtpu_flags;
+ opts.msgtype = tun_key->gtpu_msgtype;
+ nl_msg_put_unspec(a, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS,
+ &opts, sizeof(opts));
+ }
nl_msg_end_nested(a, tun_key_ofs);
}
static void
format_odp_tun_erspan_opt(const struct nlattr *attr,
- const struct nlattr *mask_attr, struct ds *ds,
- bool verbose)
+ const struct nlattr *mask_attr, struct ds *ds,
+ bool verbose)
{
const struct erspan_metadata *opts, *mask;
uint8_t ver, ver_ma, dir, dir_ma, hwid, hwid_ma;
ds_chomp(ds, ',');
}
+static void
+format_odp_tun_gtpu_opt(const struct nlattr *attr,
+ const struct nlattr *mask_attr, struct ds *ds,
+ bool verbose)
+{
+ const struct gtpu_metadata *opts, *mask;
+
+ opts = nl_attr_get(attr);
+ mask = mask_attr ? nl_attr_get(mask_attr) : NULL;
+
+ format_u8x(ds, "flags", opts->flags, mask ? &mask->flags : NULL, verbose);
+ format_u8u(ds, "msgtype", opts->msgtype, mask ? &mask->msgtype : NULL,
+ verbose);
+ ds_chomp(ds, ',');
+}
+
#define MASK(PTR, FIELD) PTR ? &PTR->FIELD : NULL
static void
format_odp_tun_erspan_opt(a, ma, ds, verbose);
ds_put_cstr(ds, "),");
break;
+ case OVS_TUNNEL_KEY_ATTR_GTPU_OPTS:
+ ds_put_cstr(ds, "gtpu(");
+ format_odp_tun_gtpu_opt(a, ma, ds, verbose);
+ ds_put_cstr(ds, ")");
+ break;
case __OVS_TUNNEL_KEY_ATTR_MAX:
default:
format_unknown_key(ds, a, ma);
return 0;
}
+static int
+scan_gtpu_metadata(const char *s,
+ struct gtpu_metadata *key,
+ struct gtpu_metadata *mask)
+{
+ const char *s_base = s;
+ uint8_t flags, flags_ma;
+ uint8_t msgtype, msgtype_ma;
+ int len;
+
+ if (!strncmp(s, "flags=", 6)) {
+ s += 6;
+ len = scan_u8(s, &flags, mask ? &flags_ma : NULL);
+ if (len == 0) {
+ return 0;
+ }
+ s += len;
+ }
+
+ if (s[0] == ',') {
+ s++;
+ }
+
+ if (!strncmp(s, "msgtype=", 8)) {
+ s += 8;
+ len = scan_u8(s, &msgtype, mask ? &msgtype_ma : NULL);
+ if (len == 0) {
+ return 0;
+ }
+ s += len;
+ }
+
+ if (!strncmp(s, ")", 1)) {
+ s += 1;
+ key->flags = flags;
+ key->msgtype = msgtype;
+ if (mask) {
+ mask->flags = flags_ma;
+ mask->msgtype = msgtype_ma;
+ }
+ }
+ return s - s_base;
+}
+
static int
scan_erspan_metadata(const char *s,
struct erspan_metadata *key,
sizeof *md);
}
+static void
+gtpu_to_attr(struct ofpbuf *a, const void *data_)
+{
+ const struct gtpu_metadata *md = data_;
+
+ nl_msg_put_unspec(a, OVS_TUNNEL_KEY_ATTR_GTPU_OPTS, md,
+ sizeof *md);
+}
+
#define SCAN_PUT_ATTR(BUF, ATTR, DATA, FUNC) \
{ \
unsigned long call_fn = (unsigned long)FUNC; \
static int
parse_odp_key_mask_attr(struct parse_odp_context *context, const char *s,
struct ofpbuf *key, struct ofpbuf *mask)
+{
+ int retval;
+
+ context->depth++;
+
+ if (context->depth == MAX_ODP_NESTED) {
+ retval = -EINVAL;
+ } else {
+ retval = parse_odp_key_mask_attr__(context, s, key, mask);
+ }
+
+ context->depth--;
+
+ return retval;
+}
+
+static int
+parse_odp_key_mask_attr__(struct parse_odp_context *context, const char *s,
+ struct ofpbuf *key, struct ofpbuf *mask)
{
SCAN_SINGLE("skb_priority(", uint32_t, u32, OVS_KEY_ATTR_PRIORITY);
SCAN_SINGLE("skb_mark(", uint32_t, u32, OVS_KEY_ATTR_SKB_MARK);
SCAN_FIELD_NESTED_FUNC("vxlan(gbp(", uint32_t, vxlan_gbp, vxlan_gbp_to_attr);
SCAN_FIELD_NESTED_FUNC("geneve(", struct geneve_scan, geneve,
geneve_to_attr);
+ SCAN_FIELD_NESTED_FUNC("gtpu(", struct gtpu_metadata, gtpu_metadata,
+ gtpu_to_attr);
SCAN_FIELD_NESTED_FUNC("flags(", uint16_t, tun_flags, tun_flags_to_attr);
} SCAN_END_NESTED();
/* nsh is nested, it needs special process */
int ret = parse_odp_nsh_key_mask_attr(s, key, mask);
if (ret < 0) {
- return ret;
+ return ret;
} else {
- s += ret;
+ s += ret;
}
/* Encap open-coded. */
const char *start = s;
size_t encap, encap_mask = 0;
- if (context->depth + 1 == MAX_ODP_NESTED) {
- return -EINVAL;
- }
- context->depth++;
-
encap = nl_msg_start_nested(key, OVS_KEY_ATTR_ENCAP);
if (mask) {
encap_mask = nl_msg_start_nested(mask, OVS_KEY_ATTR_ENCAP);
s += strspn(s, delimiters);
if (!*s) {
- context->depth--;
return -EINVAL;
} else if (*s == ')') {
break;
retval = parse_odp_key_mask_attr(context, s, key, mask);
if (retval < 0) {
- context->depth--;
return retval;
}
if (mask) {
nl_msg_end_nested(mask, encap_mask);
}
- context->depth--;
return s - start;
}
odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
bool export_mask, struct ofpbuf *buf)
{
+ /* New "struct flow" fields that are visible to the datapath (including all
+ * data fields) should be translated into equivalent datapath flow fields
+ * here (you will have to add a OVS_KEY_ATTR_* for them). */
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
+
struct ovs_key_ethernet *eth_key;
size_t encap[FLOW_MAX_VLAN_HEADERS] = {0};
size_t max_vlans;
nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, data->skb_priority);
- if (flow_tnl_dst_is_set(&flow->tunnel) || export_mask) {
+ if (flow_tnl_dst_is_set(&flow->tunnel) ||
+ flow_tnl_src_is_set(&flow->tunnel) || export_mask) {
tun_key_to_attr(buf, &data->tunnel, &parms->flow->tunnel,
parms->key_buf, NULL);
}
if (flow->ct_nw_proto) {
if (parms->support.ct_orig_tuple
&& flow->dl_type == htons(ETH_TYPE_IP)) {
- struct ovs_key_ct_tuple_ipv4 ct = {
- data->ct_nw_src,
- data->ct_nw_dst,
- data->ct_tp_src,
- data->ct_tp_dst,
- data->ct_nw_proto,
- };
- nl_msg_put_unspec(buf, OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4, &ct,
- sizeof ct);
+ struct ovs_key_ct_tuple_ipv4 *ct;
+
+ /* 'struct ovs_key_ct_tuple_ipv4' has padding, clear it. */
+ ct = nl_msg_put_unspec_zero(buf, OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4,
+ sizeof *ct);
+ ct->ipv4_src = data->ct_nw_src;
+ ct->ipv4_dst = data->ct_nw_dst;
+ ct->src_port = data->ct_tp_src;
+ ct->dst_port = data->ct_tp_dst;
+ ct->ipv4_proto = data->ct_nw_proto;
} else if (parms->support.ct_orig_tuple6
&& flow->dl_type == htons(ETH_TYPE_IPV6)) {
- struct ovs_key_ct_tuple_ipv6 ct = {
- data->ct_ipv6_src,
- data->ct_ipv6_dst,
- data->ct_tp_src,
- data->ct_tp_dst,
- data->ct_nw_proto,
- };
- nl_msg_put_unspec(buf, OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6, &ct,
- sizeof ct);
+ struct ovs_key_ct_tuple_ipv6 *ct;
+
+ /* 'struct ovs_key_ct_tuple_ipv6' has padding, clear it. */
+ ct = nl_msg_put_unspec_zero(buf, OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6,
+ sizeof *ct);
+ ct->ipv6_src = data->ct_ipv6_src;
+ ct->ipv6_dst = data->ct_ipv6_dst;
+ ct->src_port = data->ct_tp_src;
+ ct->dst_port = data->ct_tp_dst;
+ ct->ipv6_proto = data->ct_nw_proto;
}
}
if (parms->support.recirc) {
&& (!export_mask || (data->tp_src == htons(0xff)
&& data->tp_dst == htons(0xff)))) {
struct ovs_key_nd *nd_key;
- struct ovs_key_nd_extensions *nd_ext_key;
nd_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ND,
sizeof *nd_key);
nd_key->nd_target = data->nd_target;
nd_key->nd_sll = data->arp_sha;
nd_key->nd_tll = data->arp_tha;
- /* Add ND Extensions Attr only if reserved field
+ /* Add ND Extensions Attr only if supported and reserved field
* or options type is set. */
- if (data->igmp_group_ip4 != 0 ||
- data->tcp_flags != 0) {
- nd_ext_key =
- nl_msg_put_unspec_uninit(buf,
+ if (parms->support.nd_ext) {
+ struct ovs_key_nd_extensions *nd_ext_key;
+
+ if (data->igmp_group_ip4 != 0 || data->tcp_flags != 0) {
+ /* 'struct ovs_key_nd_extensions' has padding,
+ * clear it. */
+ nd_ext_key = nl_msg_put_unspec_zero(buf,
OVS_KEY_ATTR_ND_EXTENSIONS,
sizeof *nd_ext_key);
- nd_ext_key->nd_reserved = data->igmp_group_ip4;
- nd_ext_key->nd_options_type = ntohs(data->tcp_flags);
+ nd_ext_key->nd_reserved = data->igmp_group_ip4;
+ nd_ext_key->nd_options_type = ntohs(data->tcp_flags);
+ }
}
}
}
nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, md->skb_priority);
+ if (md->dp_hash) {
+ nl_msg_put_u32(buf, OVS_KEY_ATTR_DP_HASH, md->dp_hash);
+ }
+
if (flow_tnl_dst_is_set(&md->tunnel)) {
tun_key_to_attr(buf, &md->tunnel, &md->tunnel, NULL, NULL);
}
}
}
-uint32_t
-odp_flow_key_hash(const struct nlattr *key, size_t key_len)
+/* Places the hash of the 'key_len' bytes starting at 'key' into '*hash'.
+ * Generated value has format of random UUID. */
+void
+odp_flow_key_hash(const void *key, size_t key_len, ovs_u128 *hash)
{
- BUILD_ASSERT_DECL(!(NLA_ALIGNTO % sizeof(uint32_t)));
- return hash_bytes32(ALIGNED_CAST(const uint32_t *, key), key_len, 0);
+ static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+ static uint32_t secret;
+
+ if (ovsthread_once_start(&once)) {
+ secret = random_uint32();
+ ovsthread_once_done(&once);
+ }
+ hash_bytes128(key, key_len, secret, hash);
+ uuid_set_bits_v4((struct uuid *)hash);
}
static void
*expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_NSH;
}
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_NSH)) {
- if (odp_nsh_key_from_attr(attrs[OVS_KEY_ATTR_NSH], &flow->nsh,
- NULL, errorp) == ODP_FIT_ERROR) {
+ if (odp_nsh_key_from_attr__(attrs[OVS_KEY_ATTR_NSH],
+ is_mask, &flow->nsh,
+ NULL, errorp) == ODP_FIT_ERROR) {
return ODP_FIT_ERROR;
}
if (is_mask) {
struct flow *flow, const struct flow *src_flow,
char **errorp)
{
+ /* New "struct flow" fields that are visible to the datapath (including all
+ * data fields) should be translated from equivalent datapath flow fields
+ * here (you will have to add a OVS_KEY_ATTR_* for them). */
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
+
enum odp_key_fitness fitness = ODP_FIT_ERROR;
if (errorp) {
*errorp = NULL;
int size;
};
+
+/* Performs bitwise OR over the fields in 'dst_' and 'src_' specified in
+ * 'offsetof_sizeof_arr' array. Result is stored in 'dst_'. */
+static void
+or_masks(void *dst_, const void *src_,
+ struct offsetof_sizeof *offsetof_sizeof_arr)
+{
+ int field, size, offset;
+ const uint8_t *src = src_;
+ uint8_t *dst = dst_;
+
+ for (field = 0; ; field++) {
+ size = offsetof_sizeof_arr[field].size;
+ offset = offsetof_sizeof_arr[field].offset;
+
+ if (!size) {
+ return;
+ }
+ or_bytes(dst + offset, src + offset, size);
+ }
+}
+
/* Compares each of the fields in 'key0' and 'key1'. The fields are specified
* in 'offsetof_sizeof_arr', which is an array terminated by a 0-size field.
* Returns true if all of the fields are equal, false if at least one differs.
struct flow_wildcards *wc,
bool use_masked)
{
- struct ovs_key_ethernet key, base, mask;
+ struct ovs_key_ethernet key, base, mask, orig_mask;
struct offsetof_sizeof ovs_key_ethernet_offsetof_sizeof_arr[] =
OVS_KEY_ETHERNET_OFFSETOF_SIZEOF_ARR;
+
if (flow->packet_type != htonl(PT_ETH)) {
return;
}
get_ethernet_key(flow, &key);
get_ethernet_key(base_flow, &base);
get_ethernet_key(&wc->masks, &mask);
+ memcpy(&orig_mask, &mask, sizeof mask);
if (commit(OVS_KEY_ATTR_ETHERNET, use_masked,
&key, &base, &mask, sizeof key,
ovs_key_ethernet_offsetof_sizeof_arr, odp_actions)) {
put_ethernet_key(&base, base_flow);
+ or_masks(&mask, &orig_mask, ovs_key_ethernet_offsetof_sizeof_arr);
put_ethernet_key(&mask, &wc->masks);
}
}
struct ofpbuf *odp_actions, struct flow_wildcards *wc,
bool use_masked)
{
- struct ovs_key_ipv4 key, mask, base;
+ struct ovs_key_ipv4 key, mask, orig_mask, base;
struct offsetof_sizeof ovs_key_ipv4_offsetof_sizeof_arr[] =
OVS_KEY_IPV4_OFFSETOF_SIZEOF_ARR;
get_ipv4_key(flow, &key, false);
get_ipv4_key(base_flow, &base, false);
get_ipv4_key(&wc->masks, &mask, true);
+ memcpy(&orig_mask, &mask, sizeof mask);
mask.ipv4_proto = 0; /* Not writeable. */
mask.ipv4_frag = 0; /* Not writable. */
if (commit(OVS_KEY_ATTR_IPV4, use_masked, &key, &base, &mask, sizeof key,
ovs_key_ipv4_offsetof_sizeof_arr, odp_actions)) {
put_ipv4_key(&base, base_flow, false);
- if (mask.ipv4_proto != 0) { /* Mask was changed by commit(). */
- put_ipv4_key(&mask, &wc->masks, true);
- }
+ or_masks(&mask, &orig_mask, ovs_key_ipv4_offsetof_sizeof_arr);
+ put_ipv4_key(&mask, &wc->masks, true);
}
}
struct ofpbuf *odp_actions, struct flow_wildcards *wc,
bool use_masked)
{
- struct ovs_key_ipv6 key, mask, base;
+ struct ovs_key_ipv6 key, mask, orig_mask, base;
struct offsetof_sizeof ovs_key_ipv6_offsetof_sizeof_arr[] =
OVS_KEY_IPV6_OFFSETOF_SIZEOF_ARR;
get_ipv6_key(flow, &key, false);
get_ipv6_key(base_flow, &base, false);
get_ipv6_key(&wc->masks, &mask, true);
+ memcpy(&orig_mask, &mask, sizeof mask);
mask.ipv6_proto = 0; /* Not writeable. */
mask.ipv6_frag = 0; /* Not writable. */
mask.ipv6_label &= htonl(IPV6_LABEL_MASK); /* Not writable. */
if (commit(OVS_KEY_ATTR_IPV6, use_masked, &key, &base, &mask, sizeof key,
ovs_key_ipv6_offsetof_sizeof_arr, odp_actions)) {
put_ipv6_key(&base, base_flow, false);
- if (mask.ipv6_proto != 0) { /* Mask was changed by commit(). */
- put_ipv6_key(&mask, &wc->masks, true);
- }
+ or_masks(&mask, &orig_mask, ovs_key_ipv6_offsetof_sizeof_arr);
+ put_ipv6_key(&mask, &wc->masks, true);
}
}
arp->arp_sip = flow->nw_src;
arp->arp_tip = flow->nw_dst;
- arp->arp_op = htons(flow->nw_proto);
+ arp->arp_op = flow->nw_proto == UINT8_MAX ?
+ OVS_BE16_MAX : htons(flow->nw_proto);
arp->arp_sha = flow->arp_sha;
arp->arp_tha = flow->arp_tha;
}
commit_set_arp_action(const struct flow *flow, struct flow *base_flow,
struct ofpbuf *odp_actions, struct flow_wildcards *wc)
{
- struct ovs_key_arp key, mask, base;
+ struct ovs_key_arp key, mask, orig_mask, base;
struct offsetof_sizeof ovs_key_arp_offsetof_sizeof_arr[] =
OVS_KEY_ARP_OFFSETOF_SIZEOF_ARR;
get_arp_key(flow, &key);
get_arp_key(base_flow, &base);
get_arp_key(&wc->masks, &mask);
+ memcpy(&orig_mask, &mask, sizeof mask);
if (commit(OVS_KEY_ATTR_ARP, true, &key, &base, &mask, sizeof key,
ovs_key_arp_offsetof_sizeof_arr, odp_actions)) {
put_arp_key(&base, base_flow);
+ or_masks(&mask, &orig_mask, ovs_key_arp_offsetof_sizeof_arr);
put_arp_key(&mask, &wc->masks);
return SLOW_ACTION;
}
commit_set_icmp_action(const struct flow *flow, struct flow *base_flow,
struct ofpbuf *odp_actions, struct flow_wildcards *wc)
{
- struct ovs_key_icmp key, mask, base;
+ struct ovs_key_icmp key, mask, orig_mask, base;
struct offsetof_sizeof ovs_key_icmp_offsetof_sizeof_arr[] =
OVS_KEY_ICMP_OFFSETOF_SIZEOF_ARR;
enum ovs_key_attr attr;
get_icmp_key(flow, &key);
get_icmp_key(base_flow, &base);
get_icmp_key(&wc->masks, &mask);
+ memcpy(&orig_mask, &mask, sizeof mask);
if (commit(attr, false, &key, &base, &mask, sizeof key,
ovs_key_icmp_offsetof_sizeof_arr, odp_actions)) {
put_icmp_key(&base, base_flow);
+ or_masks(&mask, &orig_mask, ovs_key_icmp_offsetof_sizeof_arr);
put_icmp_key(&mask, &wc->masks);
return SLOW_ACTION;
}
struct ofpbuf *odp_actions,
struct flow_wildcards *wc, bool use_masked)
{
- struct ovs_key_nd key, mask, base;
+ struct ovs_key_nd key, mask, orig_mask, base;
struct offsetof_sizeof ovs_key_nd_offsetof_sizeof_arr[] =
OVS_KEY_ND_OFFSETOF_SIZEOF_ARR;
get_nd_key(flow, &key);
get_nd_key(base_flow, &base);
get_nd_key(&wc->masks, &mask);
+ memcpy(&orig_mask, &mask, sizeof mask);
if (commit(OVS_KEY_ATTR_ND, use_masked, &key, &base, &mask, sizeof key,
ovs_key_nd_offsetof_sizeof_arr, odp_actions)) {
put_nd_key(&base, base_flow);
+ or_masks(&mask, &orig_mask, ovs_key_nd_offsetof_sizeof_arr);
put_nd_key(&mask, &wc->masks);
return SLOW_ACTION;
}
struct ofpbuf *odp_actions,
struct flow_wildcards *wc, bool use_masked)
{
- struct ovs_key_nd_extensions key, mask, base;
+ struct ovs_key_nd_extensions key, mask, orig_mask, base;
struct offsetof_sizeof ovs_key_nd_extensions_offsetof_sizeof_arr[] =
OVS_KEY_ND_EXTENSIONS_OFFSETOF_SIZEOF_ARR;
get_nd_extensions_key(flow, &key);
get_nd_extensions_key(base_flow, &base);
get_nd_extensions_key(&wc->masks, &mask);
+ memcpy(&orig_mask, &mask, sizeof mask);
if (commit(OVS_KEY_ATTR_ND_EXTENSIONS, use_masked, &key, &base, &mask,
sizeof key, ovs_key_nd_extensions_offsetof_sizeof_arr,
odp_actions)) {
put_nd_extensions_key(&base, base_flow);
+ or_masks(&mask, &orig_mask, ovs_key_nd_extensions_offsetof_sizeof_arr);
put_nd_extensions_key(&mask, &wc->masks);
return SLOW_ACTION;
}
bool use_masked)
{
enum ovs_key_attr key_type;
- union ovs_key_tp key, mask, base;
+ union ovs_key_tp key, mask, orig_mask, base;
struct offsetof_sizeof ovs_key_tp_offsetof_sizeof_arr[] =
OVS_KEY_TCP_OFFSETOF_SIZEOF_ARR;
get_tp_key(flow, &key);
get_tp_key(base_flow, &base);
get_tp_key(&wc->masks, &mask);
+ memcpy(&orig_mask, &mask, sizeof mask);
if (commit(key_type, use_masked, &key, &base, &mask, sizeof key,
ovs_key_tp_offsetof_sizeof_arr, odp_actions)) {
put_tp_key(&base, base_flow);
+ or_masks(&mask, &orig_mask, ovs_key_tp_offsetof_sizeof_arr);
put_tp_key(&mask, &wc->masks);
}
}
if (commit(OVS_KEY_ATTR_PRIORITY, use_masked, &key, &base, &mask,
sizeof key, ovs_key_prio_offsetof_sizeof_arr, odp_actions)) {
base_flow->skb_priority = base;
- wc->masks.skb_priority = mask;
+ wc->masks.skb_priority |= mask;
}
}
sizeof key, ovs_key_pkt_mark_offsetof_sizeof_arr,
odp_actions)) {
base_flow->pkt_mark = base;
- wc->masks.pkt_mark = mask;
+ wc->masks.pkt_mark |= mask;
}
}
/* If you add a field that OpenFlow actions can change, and that is visible
* to the datapath (including all data fields), then you should also add
* code here to commit changes to the field. */
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
enum slow_path_reason slow1, slow2;
bool mpls_done = false;