#include "packets.h"
#include "timeval.h"
#include "util.h"
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(odp_util);
/* The interface between userspace and kernel uses an "OVS_*" prefix.
* Since this is fairly non-specific for the OVS userspace components,
}
switch ((enum ovs_action_attr) type) {
- case OVS_ACTION_ATTR_OUTPUT: return 4;
+ case OVS_ACTION_ATTR_OUTPUT: return sizeof(uint32_t);
case OVS_ACTION_ATTR_USERSPACE: return -2;
- case OVS_ACTION_ATTR_PUSH: return -2;
- case OVS_ACTION_ATTR_POP: return 2;
+ case OVS_ACTION_ATTR_PUSH_VLAN: return sizeof(struct ovs_action_push_vlan);
+ case OVS_ACTION_ATTR_POP_VLAN: return 0;
case OVS_ACTION_ATTR_SET: return -2;
case OVS_ACTION_ATTR_SAMPLE: return -2;
switch (attr) {
case OVS_KEY_ATTR_UNSPEC: return "unspec";
+ case OVS_KEY_ATTR_ENCAP: return "encap";
case OVS_KEY_ATTR_PRIORITY: return "priority";
case OVS_KEY_ATTR_TUN_ID: return "tun_id";
case OVS_KEY_ATTR_IN_PORT: return "in_port";
case OVS_KEY_ATTR_ETHERNET: return "eth";
- case OVS_KEY_ATTR_8021Q: return "vlan";
+ case OVS_KEY_ATTR_VLAN: return "vlan";
case OVS_KEY_ATTR_ETHERTYPE: return "eth_type";
case OVS_KEY_ATTR_IPV4: return "ipv4";
case OVS_KEY_ATTR_IPV6: return "ipv6";
{
int expected_len;
enum ovs_action_attr type = nl_attr_type(a);
+ const struct ovs_action_push_vlan *vlan;
expected_len = odp_action_len(nl_attr_type(a));
if (expected_len != -2 && nl_attr_get_size(a) != expected_len) {
}
switch (type) {
-
case OVS_ACTION_ATTR_OUTPUT:
ds_put_format(ds, "%"PRIu16, nl_attr_get_u32(a));
break;
format_odp_key_attr(nl_attr_get(a), ds);
ds_put_cstr(ds, ")");
break;
- case OVS_ACTION_ATTR_PUSH:
- ds_put_cstr(ds, "push(");
- format_odp_key_attr(nl_attr_get(a), ds);
- ds_put_cstr(ds, ")");
+ case OVS_ACTION_ATTR_PUSH_VLAN:
+ vlan = nl_attr_get(a);
+ ds_put_cstr(ds, "push_vlan(");
+ if (vlan->vlan_tpid != htons(ETH_TYPE_VLAN)) {
+ ds_put_format(ds, "tpid=0x%04"PRIx16",", ntohs(vlan->vlan_tpid));
+ }
+ ds_put_format(ds, "vid=%"PRIu16",pcp=%d)",
+ vlan_tci_to_vid(vlan->vlan_tci),
+ vlan_tci_to_pcp(vlan->vlan_tci));
break;
- case OVS_ACTION_ATTR_POP:
- ds_put_format(ds, "pop(%s)",
- ovs_key_attr_to_string(nl_attr_get_u16(a)));
+ case OVS_ACTION_ATTR_POP_VLAN:
+ ds_put_cstr(ds, "pop_vlan");
break;
case OVS_ACTION_ATTR_SAMPLE:
format_odp_sample_action(ds, a);
}
\f
/* Returns the correct length of the payload for a flow key attribute of the
- * specified 'type', or -1 if 'type' is unknown. */
+ * specified 'type', -1 if 'type' is unknown, or -2 if the attribute's payload
+ * is variable length. */
static int
odp_flow_key_attr_len(uint16_t type)
{
}
switch ((enum ovs_key_attr) type) {
+ case OVS_KEY_ATTR_ENCAP: return -2;
case OVS_KEY_ATTR_PRIORITY: return 4;
case OVS_KEY_ATTR_TUN_ID: return 8;
case OVS_KEY_ATTR_IN_PORT: return 4;
case OVS_KEY_ATTR_ETHERNET: return sizeof(struct ovs_key_ethernet);
- case OVS_KEY_ATTR_8021Q: return sizeof(struct ovs_key_8021q);
+ case OVS_KEY_ATTR_VLAN: return sizeof(ovs_be16);
case OVS_KEY_ATTR_ETHERTYPE: return 2;
case OVS_KEY_ATTR_IPV4: return sizeof(struct ovs_key_ipv4);
case OVS_KEY_ATTR_IPV6: return sizeof(struct ovs_key_ipv6);
return -1;
}
-
static void
format_generic_odp_key(const struct nlattr *a, struct ds *ds)
{
size_t len = nl_attr_get_size(a);
-
- ds_put_format(ds, "key%"PRId16, nl_attr_type(a));
if (len) {
const uint8_t *unspec;
unsigned int i;
format_odp_key_attr(const struct nlattr *a, struct ds *ds)
{
const struct ovs_key_ethernet *eth_key;
- const struct ovs_key_8021q *q_key;
const struct ovs_key_ipv4 *ipv4_key;
const struct ovs_key_ipv6 *ipv6_key;
const struct ovs_key_tcp *tcp_key;
const struct ovs_key_arp *arp_key;
const struct ovs_key_nd *nd_key;
enum ovs_key_attr attr = nl_attr_type(a);
+ int expected_len;
- if (nl_attr_get_size(a) != odp_flow_key_attr_len(nl_attr_type(a))) {
- ds_put_format(ds, "bad length %zu, expected %d for: ",
+ ds_put_cstr(ds, ovs_key_attr_to_string(attr));
+ expected_len = odp_flow_key_attr_len(nl_attr_type(a));
+ if (expected_len != -2 && nl_attr_get_size(a) != expected_len) {
+ ds_put_format(ds, "(bad length %zu, expected %d)",
nl_attr_get_size(a),
odp_flow_key_attr_len(nl_attr_type(a)));
format_generic_odp_key(a, ds);
}
switch (attr) {
+ case OVS_KEY_ATTR_ENCAP:
+ ds_put_cstr(ds, "(");
+ if (nl_attr_get_size(a)) {
+ odp_flow_key_format(nl_attr_get(a), nl_attr_get_size(a), ds);
+ }
+ ds_put_char(ds, ')');
+ break;
+
case OVS_KEY_ATTR_PRIORITY:
- ds_put_format(ds, "priority(%"PRIu32")", nl_attr_get_u32(a));
+ ds_put_format(ds, "(%"PRIu32")", nl_attr_get_u32(a));
break;
case OVS_KEY_ATTR_TUN_ID:
- ds_put_format(ds, "tun_id(%#"PRIx64")", ntohll(nl_attr_get_be64(a)));
+ ds_put_format(ds, "(%#"PRIx64")", ntohll(nl_attr_get_be64(a)));
break;
case OVS_KEY_ATTR_IN_PORT:
- ds_put_format(ds, "in_port(%"PRIu32")", nl_attr_get_u32(a));
+ ds_put_format(ds, "(%"PRIu32")", nl_attr_get_u32(a));
break;
case OVS_KEY_ATTR_ETHERNET:
eth_key = nl_attr_get(a);
- ds_put_format(ds, "eth(src="ETH_ADDR_FMT",dst="ETH_ADDR_FMT")",
+ ds_put_format(ds, "(src="ETH_ADDR_FMT",dst="ETH_ADDR_FMT")",
ETH_ADDR_ARGS(eth_key->eth_src),
ETH_ADDR_ARGS(eth_key->eth_dst));
break;
- case OVS_KEY_ATTR_8021Q:
- q_key = nl_attr_get(a);
- ds_put_cstr(ds, "vlan(");
- if (q_key->q_tpid != htons(ETH_TYPE_VLAN)) {
- ds_put_format(ds, "tpid=0x%04"PRIx16",", ntohs(q_key->q_tpid));
- }
- ds_put_format(ds, "vid=%"PRIu16",pcp=%d)",
- vlan_tci_to_vid(q_key->q_tci),
- vlan_tci_to_pcp(q_key->q_tci));
+ case OVS_KEY_ATTR_VLAN:
+ ds_put_format(ds, "(vid=%"PRIu16",pcp=%d)",
+ vlan_tci_to_vid(nl_attr_get_be16(a)),
+ vlan_tci_to_pcp(nl_attr_get_be16(a)));
break;
case OVS_KEY_ATTR_ETHERTYPE:
- ds_put_format(ds, "eth_type(0x%04"PRIx16")",
+ ds_put_format(ds, "(0x%04"PRIx16")",
ntohs(nl_attr_get_be16(a)));
break;
case OVS_KEY_ATTR_IPV4:
ipv4_key = nl_attr_get(a);
- ds_put_format(ds, "ipv4(src="IP_FMT",dst="IP_FMT",proto=%"PRIu8
+ ds_put_format(ds, "(src="IP_FMT",dst="IP_FMT",proto=%"PRIu8
",tos=%#"PRIx8",ttl=%"PRIu8",frag=%s)",
IP_ARGS(&ipv4_key->ipv4_src),
IP_ARGS(&ipv4_key->ipv4_dst),
inet_ntop(AF_INET6, ipv6_key->ipv6_src, src_str, sizeof src_str);
inet_ntop(AF_INET6, ipv6_key->ipv6_dst, dst_str, sizeof dst_str);
- ds_put_format(ds, "ipv6(src=%s,dst=%s,label=%#"PRIx32",proto=%"PRIu8
+ ds_put_format(ds, "(src=%s,dst=%s,label=%#"PRIx32",proto=%"PRIu8
",tclass=%#"PRIx8",hlimit=%"PRIu8",frag=%s)",
src_str, dst_str, ntohl(ipv6_key->ipv6_label),
ipv6_key->ipv6_proto, ipv6_key->ipv6_tclass,
case OVS_KEY_ATTR_TCP:
tcp_key = nl_attr_get(a);
- ds_put_format(ds, "tcp(src=%"PRIu16",dst=%"PRIu16")",
+ ds_put_format(ds, "(src=%"PRIu16",dst=%"PRIu16")",
ntohs(tcp_key->tcp_src), ntohs(tcp_key->tcp_dst));
break;
case OVS_KEY_ATTR_UDP:
udp_key = nl_attr_get(a);
- ds_put_format(ds, "udp(src=%"PRIu16",dst=%"PRIu16")",
+ ds_put_format(ds, "(src=%"PRIu16",dst=%"PRIu16")",
ntohs(udp_key->udp_src), ntohs(udp_key->udp_dst));
break;
case OVS_KEY_ATTR_ICMP:
icmp_key = nl_attr_get(a);
- ds_put_format(ds, "icmp(type=%"PRIu8",code=%"PRIu8")",
+ ds_put_format(ds, "(type=%"PRIu8",code=%"PRIu8")",
icmp_key->icmp_type, icmp_key->icmp_code);
break;
case OVS_KEY_ATTR_ICMPV6:
icmpv6_key = nl_attr_get(a);
- ds_put_format(ds, "icmpv6(type=%"PRIu8",code=%"PRIu8")",
+ ds_put_format(ds, "(type=%"PRIu8",code=%"PRIu8")",
icmpv6_key->icmpv6_type, icmpv6_key->icmpv6_code);
break;
case OVS_KEY_ATTR_ARP:
arp_key = nl_attr_get(a);
- ds_put_format(ds, "arp(sip="IP_FMT",tip="IP_FMT",op=%"PRIu16","
+ ds_put_format(ds, "(sip="IP_FMT",tip="IP_FMT",op=%"PRIu16","
"sha="ETH_ADDR_FMT",tha="ETH_ADDR_FMT")",
IP_ARGS(&arp_key->arp_sip), IP_ARGS(&arp_key->arp_tip),
ntohs(arp_key->arp_op), ETH_ADDR_ARGS(arp_key->arp_sha),
nd_key = nl_attr_get(a);
inet_ntop(AF_INET6, nd_key->nd_target, target, sizeof target);
- ds_put_format(ds, "nd(target=%s", target);
+ ds_put_format(ds, "(target=%s", target);
if (!eth_addr_is_zero(nd_key->nd_sll)) {
ds_put_format(ds, ",sll="ETH_ADDR_FMT,
ETH_ADDR_ARGS(nd_key->nd_sll));
}
{
- uint16_t tpid = ETH_TYPE_VLAN;
uint16_t vid;
int pcp;
int n = -1;
- if ((sscanf(s, "vlan(vid=%"SCNi16",pcp=%i)%n",
- &vid, &pcp, &n) > 0 && n > 0) ||
- (sscanf(s, "vlan(tpid=%"SCNi16",vid=%"SCNi16",pcp=%i)%n",
- &tpid, &vid, &pcp, &n) > 0 && n > 0)) {
- struct ovs_key_8021q q_key;
-
- q_key.q_tpid = htons(tpid);
- q_key.q_tci = htons((vid << VLAN_VID_SHIFT) |
- (pcp << VLAN_PCP_SHIFT));
- nl_msg_put_unspec(key, OVS_KEY_ATTR_8021Q, &q_key, sizeof q_key);
+ if ((sscanf(s, "vlan(vid=%"SCNi16",pcp=%i)%n", &vid, &pcp, &n) > 0
+ && n > 0)) {
+ nl_msg_put_be16(key, OVS_KEY_ATTR_VLAN,
+ htons((vid << VLAN_VID_SHIFT) |
+ (pcp << VLAN_PCP_SHIFT)));
return n;
}
}
}
}
+ if (!strncmp(s, "encap(", 6)) {
+ const char *start = s;
+ size_t encap;
+
+ encap = nl_msg_start_nested(key, OVS_KEY_ATTR_ENCAP);
+
+ s += 6;
+ for (;;) {
+ int retval;
+
+ s += strspn(s, ", \t\r\n");
+ if (!*s) {
+ return -EINVAL;
+ } else if (*s == ')') {
+ break;
+ }
+
+ retval = parse_odp_key_attr(s, key);
+ if (retval < 0) {
+ return retval;
+ }
+ s += retval;
+ }
+ s++;
+
+ nl_msg_end_nested(key, encap);
+
+ return s - start;
+ }
+
return -EINVAL;
}
*
* On success, the attributes appended to 'key' are individually syntactically
* valid, but they may not be valid as a sequence. 'key' might, for example,
- * be missing an "in_port" key, have duplicated keys, or have keys in the wrong
- * order. odp_flow_key_to_flow() will detect those errors. */
+ * have duplicated keys. odp_flow_key_to_flow() will detect those errors. */
int
odp_flow_key_from_string(const char *s, struct ofpbuf *key)
{
odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow)
{
struct ovs_key_ethernet *eth_key;
+ size_t encap;
if (flow->priority) {
nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, flow->priority);
memcpy(eth_key->eth_dst, flow->dl_dst, ETH_ADDR_LEN);
if (flow->vlan_tci != htons(0)) {
- struct ovs_key_8021q *q_key;
-
- q_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_8021Q,
- sizeof *q_key);
- q_key->q_tpid = htons(ETH_TYPE_VLAN);
- q_key->q_tci = flow->vlan_tci & ~htons(VLAN_CFI);
+ nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, htons(ETH_TYPE_VLAN));
+ nl_msg_put_be16(buf, OVS_KEY_ATTR_VLAN,
+ flow->vlan_tci & ~htons(VLAN_CFI));
+ encap = nl_msg_start_nested(buf, OVS_KEY_ATTR_ENCAP);
+ } else {
+ encap = 0;
}
if (ntohs(flow->dl_type) < ETH_TYPE_MIN) {
- return;
+ goto unencap;
}
nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, flow->dl_type);
}
}
}
+
+unencap:
+ if (encap) {
+ nl_msg_end_nested(buf, encap);
+ }
+}
+
+static void
+log_odp_key_attributes(struct vlog_rate_limit *rl, const char *title,
+ uint32_t attrs,
+ const struct nlattr *key, size_t key_len)
+{
+ struct ds s;
+ int i;
+
+ if (VLOG_DROP_WARN(rl)) {
+ return;
+ }
+
+ ds_init(&s);
+ ds_put_format(&s, "%s:", title);
+ for (i = 0; i < 32; i++) {
+ if (attrs & (1u << i)) {
+ ds_put_format(&s, " %s", ovs_key_attr_to_string(i));
+ }
+ }
+
+ ds_put_cstr(&s, ": ");
+ odp_flow_key_format(key, key_len, &s);
+
+ VLOG_WARN("%s", ds_cstr(&s));
+ ds_destroy(&s);
}
static bool
odp_to_ovs_frag(uint8_t odp_frag, struct flow *flow)
{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
if (odp_frag > OVS_FRAG_TYPE_LATER) {
+ VLOG_ERR_RL(&rl, "invalid frag %"PRIu8" in flow key",
+ odp_frag);
return false;
}
return true;
}
+static int
+parse_flow_nlattrs(const struct nlattr *key, size_t key_len,
+ const struct nlattr *attrs[], uint64_t *present_attrsp)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ const struct nlattr *nla;
+ uint64_t present_attrs;
+ size_t left;
+
+ present_attrs = 0;
+ NL_ATTR_FOR_EACH (nla, left, key, key_len) {
+ uint16_t type = nl_attr_type(nla);
+ size_t len = nl_attr_get_size(nla);
+ int expected_len = odp_flow_key_attr_len(type);
+
+ if (len != expected_len && expected_len != -2) {
+ if (expected_len == -1) {
+ VLOG_ERR_RL(&rl, "unknown attribute %"PRIu16" in flow key",
+ type);
+ } else {
+ VLOG_ERR_RL(&rl, "attribute %s has length %zu but should have "
+ "length %d", ovs_key_attr_to_string(type),
+ len, expected_len);
+ }
+ return EINVAL;
+ } else if (present_attrs & (UINT64_C(1) << type)) {
+ VLOG_ERR_RL(&rl, "duplicate %s attribute in flow key",
+ ovs_key_attr_to_string(type));
+ return EINVAL;
+ }
+
+ present_attrs |= UINT64_C(1) << type;
+ attrs[type] = nla;
+ }
+ if (left) {
+ VLOG_ERR_RL(&rl, "trailing garbage in flow key");
+ return EINVAL;
+ }
+
+ *present_attrsp = present_attrs;
+ return 0;
+}
+
+static int
+check_expectations(uint64_t present_attrs, uint64_t expected_attrs,
+ const struct nlattr *key, size_t key_len)
+{
+ uint64_t missing_attrs;
+ uint64_t extra_attrs;
+
+ missing_attrs = expected_attrs & ~present_attrs;
+ if (missing_attrs) {
+ static struct vlog_rate_limit miss_rl = VLOG_RATE_LIMIT_INIT(10, 10);
+ log_odp_key_attributes(&miss_rl, "expected but not present",
+ missing_attrs, key, key_len);
+ return EINVAL;
+ }
+
+ extra_attrs = present_attrs & ~expected_attrs;
+ if (extra_attrs) {
+ static struct vlog_rate_limit extra_rl = VLOG_RATE_LIMIT_INIT(10, 10);
+ log_odp_key_attributes(&extra_rl, "present but not expected",
+ extra_attrs, key, key_len);
+ return EINVAL;
+ }
+
+ return 0;
+}
+
/* Converts the 'key_len' bytes of OVS_KEY_ATTR_* attributes in 'key' to a flow
* structure in 'flow'. Returns 0 if successful, otherwise EINVAL. */
int
odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
struct flow *flow)
{
- const struct nlattr *nla;
- enum ovs_key_attr prev_type;
- size_t left;
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1];
+ uint64_t expected_attrs;
+ uint64_t present_attrs;
+ int error;
memset(flow, 0, sizeof *flow);
- flow->dl_type = htons(FLOW_DL_TYPE_NONE);
- flow->in_port = OFPP_NONE;
- prev_type = OVS_KEY_ATTR_UNSPEC;
- NL_ATTR_FOR_EACH (nla, left, key, key_len) {
- const struct ovs_key_ethernet *eth_key;
- const struct ovs_key_8021q *q_key;
- const struct ovs_key_ipv4 *ipv4_key;
- const struct ovs_key_ipv6 *ipv6_key;
- const struct ovs_key_tcp *tcp_key;
- const struct ovs_key_udp *udp_key;
- const struct ovs_key_icmp *icmp_key;
- const struct ovs_key_icmpv6 *icmpv6_key;
- const struct ovs_key_arp *arp_key;
- const struct ovs_key_nd *nd_key;
+ error = parse_flow_nlattrs(key, key_len, attrs, &present_attrs);
+ if (error) {
+ return error;
+ }
- uint16_t type = nl_attr_type(nla);
- int len = odp_flow_key_attr_len(type);
+ expected_attrs = 0;
+
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_PRIORITY)) {
+ flow->priority = nl_attr_get_u32(attrs[OVS_KEY_ATTR_PRIORITY]);
+ expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_PRIORITY;
+ }
- if (nl_attr_get_size(nla) != len && len != -1) {
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TUN_ID)) {
+ flow->tun_id = nl_attr_get_be64(attrs[OVS_KEY_ATTR_TUN_ID]);
+ expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TUN_ID;
+ }
+
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IN_PORT)) {
+ uint32_t in_port = nl_attr_get_u32(attrs[OVS_KEY_ATTR_IN_PORT]);
+ if (in_port >= UINT16_MAX || in_port >= OFPP_MAX) {
+ VLOG_ERR_RL(&rl, "in_port %"PRIu32" out of supported range",
+ in_port);
return EINVAL;
}
+ flow->in_port = odp_port_to_ofp_port(in_port);
+ expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IN_PORT;
+ } else {
+ flow->in_port = OFPP_NONE;
+ }
-#define TRANSITION(PREV_TYPE, TYPE) (((PREV_TYPE) << 16) | (TYPE))
- switch (TRANSITION(prev_type, type)) {
- case TRANSITION(OVS_KEY_ATTR_UNSPEC, OVS_KEY_ATTR_PRIORITY):
- flow->priority = nl_attr_get_u32(nla);
- break;
-
- case TRANSITION(OVS_KEY_ATTR_UNSPEC, OVS_KEY_ATTR_TUN_ID):
- case TRANSITION(OVS_KEY_ATTR_PRIORITY, OVS_KEY_ATTR_TUN_ID):
- flow->tun_id = nl_attr_get_be64(nla);
- break;
-
- case TRANSITION(OVS_KEY_ATTR_UNSPEC, OVS_KEY_ATTR_IN_PORT):
- case TRANSITION(OVS_KEY_ATTR_PRIORITY, OVS_KEY_ATTR_IN_PORT):
- case TRANSITION(OVS_KEY_ATTR_TUN_ID, OVS_KEY_ATTR_IN_PORT):
- if (nl_attr_get_u32(nla) >= UINT16_MAX) {
- return EINVAL;
- }
- flow->in_port = odp_port_to_ofp_port(nl_attr_get_u32(nla));
- break;
-
- case TRANSITION(OVS_KEY_ATTR_UNSPEC, OVS_KEY_ATTR_ETHERNET):
- case TRANSITION(OVS_KEY_ATTR_PRIORITY, OVS_KEY_ATTR_ETHERNET):
- case TRANSITION(OVS_KEY_ATTR_TUN_ID, OVS_KEY_ATTR_ETHERNET):
- case TRANSITION(OVS_KEY_ATTR_IN_PORT, OVS_KEY_ATTR_ETHERNET):
- eth_key = nl_attr_get(nla);
- memcpy(flow->dl_src, eth_key->eth_src, ETH_ADDR_LEN);
- memcpy(flow->dl_dst, eth_key->eth_dst, ETH_ADDR_LEN);
- break;
-
- case TRANSITION(OVS_KEY_ATTR_ETHERNET, OVS_KEY_ATTR_8021Q):
- q_key = nl_attr_get(nla);
- if (q_key->q_tpid != htons(ETH_TYPE_VLAN)) {
- /* Only standard 0x8100 VLANs currently supported. */
- return EINVAL;
- }
- if (q_key->q_tci & htons(VLAN_CFI)) {
- return EINVAL;
- }
- flow->vlan_tci = q_key->q_tci | htons(VLAN_CFI);
- break;
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ETHERNET)) {
+ const struct ovs_key_ethernet *eth_key;
- case TRANSITION(OVS_KEY_ATTR_8021Q, OVS_KEY_ATTR_ETHERTYPE):
- case TRANSITION(OVS_KEY_ATTR_ETHERNET, OVS_KEY_ATTR_ETHERTYPE):
- flow->dl_type = nl_attr_get_be16(nla);
- if (ntohs(flow->dl_type) < 1536) {
- return EINVAL;
- }
- break;
+ eth_key = nl_attr_get(attrs[OVS_KEY_ATTR_ETHERNET]);
+ memcpy(flow->dl_src, eth_key->eth_src, ETH_ADDR_LEN);
+ memcpy(flow->dl_dst, eth_key->eth_dst, ETH_ADDR_LEN);
+ } else {
+ VLOG_ERR_RL(&rl, "missing Ethernet attribute in flow key");
+ return EINVAL;
+ }
+ expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERNET;
+
+ if ((present_attrs & ~expected_attrs)
+ == ((UINT64_C(1) << OVS_KEY_ATTR_ETHERTYPE) |
+ (UINT64_C(1) << OVS_KEY_ATTR_VLAN) |
+ (UINT64_C(1) << OVS_KEY_ATTR_ENCAP))
+ && (nl_attr_get_be16(attrs[OVS_KEY_ATTR_ETHERTYPE])
+ == htons(ETH_TYPE_VLAN))) {
+ const struct nlattr *encap = attrs[OVS_KEY_ATTR_ENCAP];
+ const struct nlattr *vlan = attrs[OVS_KEY_ATTR_VLAN];
+
+ flow->vlan_tci = nl_attr_get_be16(vlan);
+ if (flow->vlan_tci & htons(VLAN_CFI)) {
+ return EINVAL;
+ }
+ flow->vlan_tci |= htons(VLAN_CFI);
- case TRANSITION(OVS_KEY_ATTR_ETHERTYPE, OVS_KEY_ATTR_IPV4):
- if (flow->dl_type != htons(ETH_TYPE_IP)) {
- return EINVAL;
- }
- ipv4_key = nl_attr_get(nla);
+ error = parse_flow_nlattrs(nl_attr_get(encap), nl_attr_get_size(encap),
+ attrs, &present_attrs);
+ if (error) {
+ return error;
+ }
+ expected_attrs = 0;
+ }
+
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ETHERTYPE)) {
+ flow->dl_type = nl_attr_get_be16(attrs[OVS_KEY_ATTR_ETHERTYPE]);
+ if (ntohs(flow->dl_type) < 1536) {
+ VLOG_ERR_RL(&rl, "invalid Ethertype %"PRIu16" in flow key",
+ ntohs(flow->dl_type));
+ return EINVAL;
+ }
+ expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERTYPE;
+ } else {
+ flow->dl_type = htons(FLOW_DL_TYPE_NONE);
+ }
+
+ if (flow->dl_type == htons(ETH_TYPE_IP)) {
+ expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV4;
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV4)) {
+ const struct ovs_key_ipv4 *ipv4_key;
+
+ ipv4_key = nl_attr_get(attrs[OVS_KEY_ATTR_IPV4]);
flow->nw_src = ipv4_key->ipv4_src;
flow->nw_dst = ipv4_key->ipv4_dst;
flow->nw_proto = ipv4_key->ipv4_proto;
if (!odp_to_ovs_frag(ipv4_key->ipv4_frag, flow)) {
return EINVAL;
}
- break;
+ }
+ } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
+ expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV6;
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV6)) {
+ const struct ovs_key_ipv6 *ipv6_key;
- case TRANSITION(OVS_KEY_ATTR_ETHERTYPE, OVS_KEY_ATTR_IPV6):
- if (flow->dl_type != htons(ETH_TYPE_IPV6)) {
- return EINVAL;
- }
- ipv6_key = nl_attr_get(nla);
+ ipv6_key = nl_attr_get(attrs[OVS_KEY_ATTR_IPV6]);
memcpy(&flow->ipv6_src, ipv6_key->ipv6_src, sizeof flow->ipv6_src);
memcpy(&flow->ipv6_dst, ipv6_key->ipv6_dst, sizeof flow->ipv6_dst);
flow->ipv6_label = ipv6_key->ipv6_label;
if (!odp_to_ovs_frag(ipv6_key->ipv6_frag, flow)) {
return EINVAL;
}
- break;
-
- case TRANSITION(OVS_KEY_ATTR_IPV4, OVS_KEY_ATTR_TCP):
- case TRANSITION(OVS_KEY_ATTR_IPV6, OVS_KEY_ATTR_TCP):
- if (flow->nw_proto != IPPROTO_TCP) {
- return EINVAL;
- }
- tcp_key = nl_attr_get(nla);
- flow->tp_src = tcp_key->tcp_src;
- flow->tp_dst = tcp_key->tcp_dst;
- break;
-
- case TRANSITION(OVS_KEY_ATTR_IPV4, OVS_KEY_ATTR_UDP):
- case TRANSITION(OVS_KEY_ATTR_IPV6, OVS_KEY_ATTR_UDP):
- if (flow->nw_proto != IPPROTO_UDP) {
- return EINVAL;
- }
- udp_key = nl_attr_get(nla);
- flow->tp_src = udp_key->udp_src;
- flow->tp_dst = udp_key->udp_dst;
- break;
-
- case TRANSITION(OVS_KEY_ATTR_IPV4, OVS_KEY_ATTR_ICMP):
- if (flow->nw_proto != IPPROTO_ICMP) {
- return EINVAL;
- }
- icmp_key = nl_attr_get(nla);
- flow->tp_src = htons(icmp_key->icmp_type);
- flow->tp_dst = htons(icmp_key->icmp_code);
- break;
-
- case TRANSITION(OVS_KEY_ATTR_IPV6, OVS_KEY_ATTR_ICMPV6):
- if (flow->nw_proto != IPPROTO_ICMPV6) {
- return EINVAL;
- }
- icmpv6_key = nl_attr_get(nla);
- flow->tp_src = htons(icmpv6_key->icmpv6_type);
- flow->tp_dst = htons(icmpv6_key->icmpv6_code);
- break;
+ }
+ } else if (flow->dl_type == htons(ETH_TYPE_ARP)) {
+ expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ARP;
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ARP)) {
+ const struct ovs_key_arp *arp_key;
- case TRANSITION(OVS_KEY_ATTR_ETHERTYPE, OVS_KEY_ATTR_ARP):
- if (flow->dl_type != htons(ETH_TYPE_ARP)) {
- return EINVAL;
- }
- arp_key = nl_attr_get(nla);
+ arp_key = nl_attr_get(attrs[OVS_KEY_ATTR_ARP]);
flow->nw_src = arp_key->arp_sip;
flow->nw_dst = arp_key->arp_tip;
if (arp_key->arp_op & htons(0xff00)) {
+ VLOG_ERR_RL(&rl, "unsupported ARP opcode %"PRIu16" in flow "
+ "key", ntohs(arp_key->arp_op));
return EINVAL;
}
flow->nw_proto = ntohs(arp_key->arp_op);
memcpy(flow->arp_sha, arp_key->arp_sha, ETH_ADDR_LEN);
memcpy(flow->arp_tha, arp_key->arp_tha, ETH_ADDR_LEN);
- break;
-
- case TRANSITION(OVS_KEY_ATTR_ICMPV6, OVS_KEY_ATTR_ND):
- if (flow->tp_src != htons(ND_NEIGHBOR_SOLICIT)
- && flow->tp_src != htons(ND_NEIGHBOR_ADVERT)) {
- return EINVAL;
- }
- nd_key = nl_attr_get(nla);
- memcpy(&flow->nd_target, nd_key->nd_target, sizeof flow->nd_target);
- memcpy(flow->arp_sha, nd_key->nd_sll, ETH_ADDR_LEN);
- memcpy(flow->arp_tha, nd_key->nd_tll, ETH_ADDR_LEN);
- break;
-
- default:
- return EINVAL;
}
-
- prev_type = type;
}
- if (left) {
- return EINVAL;
- }
-
- switch (prev_type) {
- case OVS_KEY_ATTR_UNSPEC:
- return EINVAL;
-
- case OVS_KEY_ATTR_PRIORITY:
- case OVS_KEY_ATTR_TUN_ID:
- case OVS_KEY_ATTR_IN_PORT:
- return EINVAL;
- case OVS_KEY_ATTR_ETHERNET:
- case OVS_KEY_ATTR_8021Q:
- return 0;
-
- case OVS_KEY_ATTR_ETHERTYPE:
- if (flow->dl_type == htons(ETH_TYPE_IP)
- || flow->dl_type == htons(ETH_TYPE_IPV6)
- || flow->dl_type == htons(ETH_TYPE_ARP)) {
- return EINVAL;
- }
- return 0;
-
- case OVS_KEY_ATTR_IPV4:
- if (flow->nw_frag & FLOW_NW_FRAG_LATER) {
- return 0;
- }
- if (flow->nw_proto == IPPROTO_TCP
- || flow->nw_proto == IPPROTO_UDP
- || flow->nw_proto == IPPROTO_ICMP) {
- return EINVAL;
- }
- return 0;
+ if (flow->nw_proto == IPPROTO_TCP
+ && (flow->dl_type == htons(ETH_TYPE_IP) ||
+ flow->dl_type == htons(ETH_TYPE_IPV6))
+ && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
+ expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TCP;
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TCP)) {
+ const struct ovs_key_tcp *tcp_key;
- case OVS_KEY_ATTR_IPV6:
- if (flow->nw_frag & FLOW_NW_FRAG_LATER) {
- return 0;
+ tcp_key = nl_attr_get(attrs[OVS_KEY_ATTR_TCP]);
+ flow->tp_src = tcp_key->tcp_src;
+ flow->tp_dst = tcp_key->tcp_dst;
}
- if (flow->nw_proto == IPPROTO_TCP
- || flow->nw_proto == IPPROTO_UDP
- || flow->nw_proto == IPPROTO_ICMPV6) {
- return EINVAL;
+ } else if (flow->nw_proto == IPPROTO_UDP
+ && (flow->dl_type == htons(ETH_TYPE_IP) ||
+ flow->dl_type == htons(ETH_TYPE_IPV6))
+ && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
+ expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_UDP;
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_UDP)) {
+ const struct ovs_key_udp *udp_key;
+
+ udp_key = nl_attr_get(attrs[OVS_KEY_ATTR_UDP]);
+ flow->tp_src = udp_key->udp_src;
+ flow->tp_dst = udp_key->udp_dst;
}
- return 0;
-
- case OVS_KEY_ATTR_ICMPV6:
- if (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT)
- || flow->tp_src == htons(ND_NEIGHBOR_ADVERT)
- || flow->nw_frag & FLOW_NW_FRAG_LATER) {
- return EINVAL;
+ } else if (flow->nw_proto == IPPROTO_ICMP
+ && flow->dl_type == htons(ETH_TYPE_IP)
+ && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
+ expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ICMP;
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ICMP)) {
+ const struct ovs_key_icmp *icmp_key;
+
+ icmp_key = nl_attr_get(attrs[OVS_KEY_ATTR_ICMP]);
+ flow->tp_src = htons(icmp_key->icmp_type);
+ flow->tp_dst = htons(icmp_key->icmp_code);
}
- return 0;
+ } else if (flow->nw_proto == IPPROTO_ICMPV6
+ && flow->dl_type == htons(ETH_TYPE_IPV6)
+ && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
+ expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ICMPV6;
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ICMPV6)) {
+ const struct ovs_key_icmpv6 *icmpv6_key;
+
+ icmpv6_key = nl_attr_get(attrs[OVS_KEY_ATTR_ICMPV6]);
+ flow->tp_src = htons(icmpv6_key->icmpv6_type);
+ flow->tp_dst = htons(icmpv6_key->icmpv6_code);
- case OVS_KEY_ATTR_TCP:
- case OVS_KEY_ATTR_UDP:
- case OVS_KEY_ATTR_ICMP:
- case OVS_KEY_ATTR_ND:
- if (flow->nw_frag & FLOW_NW_FRAG_LATER) {
- return EINVAL;
+ if (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT) ||
+ flow->tp_src == htons(ND_NEIGHBOR_ADVERT)) {
+ expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ND;
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ND)) {
+ const struct ovs_key_nd *nd_key;
+
+ nd_key = nl_attr_get(attrs[OVS_KEY_ATTR_ND]);
+ memcpy(&flow->nd_target, nd_key->nd_target,
+ sizeof flow->nd_target);
+ memcpy(flow->arp_sha, nd_key->nd_sll, ETH_ADDR_LEN);
+ memcpy(flow->arp_tha, nd_key->nd_tll, ETH_ADDR_LEN);
+ }
+ }
}
- return 0;
-
- case OVS_KEY_ATTR_ARP:
- return 0;
-
- case __OVS_KEY_ATTR_MAX:
- default:
- NOT_REACHED();
}
+
+ return check_expectations(present_attrs, expected_attrs, key, key_len);
}