case OVS_KEY_ATTR_IPV4: return "ipv4";
case OVS_KEY_ATTR_IPV6: return "ipv6";
case OVS_KEY_ATTR_TCP: return "tcp";
+ case OVS_KEY_ATTR_TCP_FLAGS: return "tcp_flags";
case OVS_KEY_ATTR_UDP: return "udp";
case OVS_KEY_ATTR_SCTP: return "sctp";
case OVS_KEY_ATTR_ICMP: return "icmp";
}
static const char *
-slow_path_reason_to_string(enum slow_path_reason reason)
+slow_path_reason_to_string(uint32_t reason)
{
- switch (reason) {
- case SLOW_CFM:
- return "cfm";
- case SLOW_LACP:
- return "lacp";
- case SLOW_STP:
- return "stp";
- case SLOW_BFD:
- return "bfd";
- case SLOW_CONTROLLER:
- return "controller";
- case __SLOW_MAX:
- default:
- return NULL;
+ switch ((enum slow_path_reason) reason) {
+#define SPR(ENUM, STRING, EXPLANATION) case ENUM: return STRING;
+ SLOW_PATH_REASONS
+#undef SPR
}
+
+ return NULL;
}
-static enum slow_path_reason
-string_to_slow_path_reason(const char *string)
+const char *
+slow_path_reason_to_explanation(enum slow_path_reason reason)
{
- enum slow_path_reason i;
-
- for (i = 1; i < __SLOW_MAX; i++) {
- if (!strcmp(string, slow_path_reason_to_string(i))) {
- return i;
- }
+ switch (reason) {
+#define SPR(ENUM, STRING, EXPLANATION) case ENUM: return EXPLANATION;
+ SLOW_PATH_REASONS
+#undef SPR
}
- return 0;
+ return "<unknown>";
}
static int
cookie.sflow.output);
} else if (userdata_len == sizeof cookie.slow_path
&& cookie.type == USER_ACTION_COOKIE_SLOW_PATH) {
- const char *reason;
- reason = slow_path_reason_to_string(cookie.slow_path.reason);
- reason = reason ? reason : "";
- ds_put_format(ds, ",slow_path(%s)", reason);
+ ds_put_cstr(ds, ",slow_path(");
+ format_flags(ds, slow_path_reason_to_string,
+ cookie.slow_path.reason, ',');
+ ds_put_format(ds, ")");
} else if (userdata_len == sizeof cookie.flow_sample
&& cookie.type == USER_ACTION_COOKIE_FLOW_SAMPLE) {
ds_put_format(ds, ",flow_sample(probability=%"PRIu16
odp_put_userspace_action(pid, &cookie, sizeof cookie.sflow,
actions);
return n;
- } else if (sscanf(s, "userspace(pid=%lli,slow_path(%n", &pid, &n) > 0
+ } else if (sscanf(s, "userspace(pid=%lli,slow_path%n", &pid, &n) > 0
&& n > 0) {
union user_action_cookie cookie;
- char reason[32];
-
- if (s[n] == ')' && s[n + 1] == ')') {
- reason[0] = '\0';
- n += 2;
- } else if (sscanf(s + n, "%31[^)]))", reason) > 0) {
- n += strlen(reason) + 2;
- } else {
- return -EINVAL;
- }
+ int res;
cookie.type = USER_ACTION_COOKIE_SLOW_PATH;
cookie.slow_path.unused = 0;
- cookie.slow_path.reason = string_to_slow_path_reason(reason);
+ cookie.slow_path.reason = 0;
- if (reason[0] && !cookie.slow_path.reason) {
+ res = parse_flags(&s[n], slow_path_reason_to_string,
+ &cookie.slow_path.reason);
+ if (res < 0) {
+ return res;
+ }
+ n += res;
+ if (s[n] != ')') {
return -EINVAL;
}
+ n++;
odp_put_userspace_action(pid, &cookie, sizeof cookie.slow_path,
actions);
case OVS_KEY_ATTR_IPV4: return sizeof(struct ovs_key_ipv4);
case OVS_KEY_ATTR_IPV6: return sizeof(struct ovs_key_ipv6);
case OVS_KEY_ATTR_TCP: return sizeof(struct ovs_key_tcp);
+ case OVS_KEY_ATTR_TCP_FLAGS: return 2;
case OVS_KEY_ATTR_UDP: return sizeof(struct ovs_key_udp);
case OVS_KEY_ATTR_SCTP: return sizeof(struct ovs_key_sctp);
case OVS_KEY_ATTR_ICMP: return sizeof(struct ovs_key_icmp);
}
break;
+ case OVS_KEY_ATTR_TCP_FLAGS:
+ ds_put_format(ds, "0x%03"PRIx16, ntohs(nl_attr_get_be16(a)));
+ if (!is_exact) {
+ ds_put_format(ds, "/0x%03"PRIx16, ntohs(nl_attr_get_be16(ma)));
+ }
+ break;
+
case OVS_KEY_ATTR_UDP:
if (!is_exact) {
const struct ovs_key_udp *udp_mask = nl_attr_get(ma);
int size = nl_attr_get_size(key);
if (odp_flow_key_attr_len(type) >=0) {
- memset(nl_msg_put_unspec_uninit(ofp, type, size), 0, size);
+ nl_msg_put_unspec_zero(ofp, type, size);
} else {
size_t nested_mask;
/* Appends to 'ds' a string representation of the 'key_len' bytes of
* OVS_KEY_ATTR_* attributes in 'key'. If non-null, additionally formats the
- * 'mask_len' bytes of 'mask' which apply to 'key'. */
+ * 'mask_len' bytes of 'mask' which apply to 'key'. If 'portno_names' is
+ * non-null and 'verbose' is true, translates odp port number to its name. */
void
odp_flow_format(const struct nlattr *key, size_t key_len,
const struct nlattr *mask, size_t mask_len,
}
}
+ {
+ uint16_t tcp_flags, tcp_flags_mask;
+ int n = -1;
+
+ if (mask && sscanf(s, "tcp_flags(%"SCNi16"/%"SCNi16")%n",
+ &tcp_flags, &tcp_flags_mask, &n) > 0 && n > 0) {
+ nl_msg_put_be16(key, OVS_KEY_ATTR_TCP_FLAGS, htons(tcp_flags));
+ nl_msg_put_be16(mask, OVS_KEY_ATTR_TCP_FLAGS, htons(tcp_flags_mask));
+ return n;
+ } else if (sscanf(s, "tcp_flags(%"SCNi16")%n", &tcp_flags, &n) > 0 && n > 0) {
+ nl_msg_put_be16(key, OVS_KEY_ATTR_TCP_FLAGS, htons(tcp_flags));
+ if (mask) {
+ nl_msg_put_be16(mask, OVS_KEY_ATTR_TCP_FLAGS,
+ htons(UINT16_MAX));
+ }
+ return n;
+ }
+ }
+
{
int udp_src;
int udp_dst;
flow->dl_type == htons(ETH_TYPE_RARP)) {
struct ovs_key_arp *arp_key;
- arp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ARP,
- sizeof *arp_key);
- memset(arp_key, 0, sizeof *arp_key);
+ arp_key = nl_msg_put_unspec_zero(buf, OVS_KEY_ATTR_ARP,
+ sizeof *arp_key);
arp_key->arp_sip = data->nw_src;
arp_key->arp_tip = data->nw_dst;
arp_key->arp_op = htons(data->nw_proto);
sizeof *tcp_key);
tcp_key->tcp_src = data->tp_src;
tcp_key->tcp_dst = data->tp_dst;
+
+ if (data->tcp_flags) {
+ nl_msg_put_be16(buf, OVS_KEY_ATTR_TCP_FLAGS, data->tcp_flags);
+ }
} else if (flow->nw_proto == IPPROTO_UDP) {
struct ovs_key_udp *udp_key;
uint64_t present_attrs, int out_of_range_attr,
uint64_t expected_attrs, struct flow *flow,
const struct nlattr *key, size_t key_len,
- const struct flow *src_flow)
+ const struct flow *src_flow)
{
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
bool is_mask = src_flow != flow;
enum ovs_key_attr expected_bit = 0xff;
if (eth_type_mpls(src_flow->dl_type)) {
- if (!is_mask) {
- expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_MPLS);
+ if (!is_mask) {
+ expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_MPLS);
- if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS))) {
- return ODP_FIT_TOO_LITTLE;
- }
- flow->mpls_lse = nl_attr_get_be32(attrs[OVS_KEY_ATTR_MPLS]);
+ if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS))) {
+ return ODP_FIT_TOO_LITTLE;
+ }
+ flow->mpls_lse = nl_attr_get_be32(attrs[OVS_KEY_ATTR_MPLS]);
} else if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS)) {
flow->mpls_lse = nl_attr_get_be32(attrs[OVS_KEY_ATTR_MPLS]);
flow->nw_tos = ipv4_key->ipv4_tos;
flow->nw_ttl = ipv4_key->ipv4_ttl;
if (is_mask) {
- flow->nw_frag = ipv4_key->ipv4_frag;
+ flow->nw_frag = ipv4_key->ipv4_frag;
check_start = ipv4_key;
check_len = sizeof *ipv4_key;
expected_bit = OVS_KEY_ATTR_IPV4;
flow->nw_tos = ipv6_key->ipv6_tclass;
flow->nw_ttl = ipv6_key->ipv6_hlimit;
if (is_mask) {
- flow->nw_frag = ipv6_key->ipv6_frag;
+ flow->nw_frag = ipv6_key->ipv6_frag;
check_start = ipv6_key;
check_len = sizeof *ipv6_key;
expected_bit = OVS_KEY_ATTR_IPV6;
flow->tp_dst = tcp_key->tcp_dst;
expected_bit = OVS_KEY_ATTR_TCP;
}
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TCP_FLAGS)) {
+ expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TCP_FLAGS;
+ flow->tcp_flags = nl_attr_get_be16(attrs[OVS_KEY_ATTR_TCP_FLAGS]);
+ }
} else if (src_flow->nw_proto == IPPROTO_UDP
&& (src_flow->dl_type == htons(ETH_TYPE_IP) ||
src_flow->dl_type == htons(ETH_TYPE_IPV6))
flow->tp_dst = udp_key->udp_dst;
expected_bit = OVS_KEY_ATTR_UDP;
}
- } else if (flow->nw_proto == IPPROTO_SCTP
- && (flow->dl_type == htons(ETH_TYPE_IP) ||
- flow->dl_type == htons(ETH_TYPE_IPV6))
- && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
+ } else if (src_flow->nw_proto == IPPROTO_SCTP
+ && (src_flow->dl_type == htons(ETH_TYPE_IP) ||
+ src_flow->dl_type == htons(ETH_TYPE_IPV6))
+ && !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) {
if (!is_mask) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_SCTP;
}
} else {
tci = nl_attr_get_be16(attrs[OVS_KEY_ATTR_VLAN]);
if (!is_mask) {
- if (tci == htons(0)) {
+ if (tci == htons(0)) {
/* Corner case for a truncated 802.1Q header. */
- if (fitness == ODP_FIT_PERFECT && nl_attr_get_size(encap)) {
- return ODP_FIT_TOO_MUCH;
- }
- return fitness;
- } else if (!(tci & htons(VLAN_CFI))) {
- VLOG_ERR_RL(&rl, "OVS_KEY_ATTR_VLAN 0x%04"PRIx16" is nonzero "
- "but CFI bit is not set", ntohs(tci));
- return ODP_FIT_ERROR;
- }
+ if (fitness == ODP_FIT_PERFECT && nl_attr_get_size(encap)) {
+ return ODP_FIT_TOO_MUCH;
+ }
+ return fitness;
+ } else if (!(tci & htons(VLAN_CFI))) {
+ VLOG_ERR_RL(&rl, "OVS_KEY_ATTR_VLAN 0x%04"PRIx16" is nonzero "
+ "but CFI bit is not set", ntohs(tci));
+ return ODP_FIT_ERROR;
+ }
}
/* Set vlan_tci.
* Remove the TPID from dl_type since it's not the real Ethertype. */
case 1: {
struct ovs_action_push_mpls *mpls;
- mpls = nl_msg_put_unspec_uninit(odp_actions, OVS_ACTION_ATTR_PUSH_MPLS,
- sizeof *mpls);
- memset(mpls, 0, sizeof *mpls);
+ mpls = nl_msg_put_unspec_zero(odp_actions, OVS_ACTION_ATTR_PUSH_MPLS,
+ sizeof *mpls);
mpls->mpls_ethertype = flow->dl_type;
mpls->mpls_lse = flow->mpls_lse;
break;
&ipv6_key, sizeof(ipv6_key));
}
-static void
+static enum slow_path_reason
+commit_set_arp_action(const struct flow *flow, struct flow *base,
+ struct ofpbuf *odp_actions, struct flow_wildcards *wc)
+{
+ struct ovs_key_arp arp_key;
+
+ if (base->nw_src == flow->nw_src &&
+ base->nw_dst == flow->nw_dst &&
+ base->nw_proto == flow->nw_proto &&
+ eth_addr_equals(base->arp_sha, flow->arp_sha) &&
+ eth_addr_equals(base->arp_tha, flow->arp_tha)) {
+ return 0;
+ }
+
+ memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src);
+ memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst);
+ memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
+ memset(&wc->masks.arp_sha, 0xff, sizeof wc->masks.arp_sha);
+ memset(&wc->masks.arp_tha, 0xff, sizeof wc->masks.arp_tha);
+
+ base->nw_src = flow->nw_src;
+ base->nw_dst = flow->nw_dst;
+ base->nw_proto = flow->nw_proto;
+ memcpy(base->arp_sha, flow->arp_sha, ETH_ADDR_LEN);
+ memcpy(base->arp_tha, flow->arp_tha, ETH_ADDR_LEN);
+
+ arp_key.arp_sip = base->nw_src;
+ arp_key.arp_tip = base->nw_dst;
+ arp_key.arp_op = htons(base->nw_proto);
+ memcpy(arp_key.arp_sha, flow->arp_sha, ETH_ADDR_LEN);
+ memcpy(arp_key.arp_tha, flow->arp_tha, ETH_ADDR_LEN);
+
+ commit_set_action(odp_actions, OVS_KEY_ATTR_ARP, &arp_key, sizeof arp_key);
+
+ return SLOW_ACTION;
+}
+
+static enum slow_path_reason
commit_set_nw_action(const struct flow *flow, struct flow *base,
struct ofpbuf *odp_actions, struct flow_wildcards *wc)
{
- /* Check if flow really have an IP header. */
+ /* Check if 'flow' really has an L3 header. */
if (!flow->nw_proto) {
- return;
+ return 0;
}
- if (base->dl_type == htons(ETH_TYPE_IP)) {
+ switch (ntohs(base->dl_type)) {
+ case ETH_TYPE_IP:
commit_set_ipv4_action(flow, base, odp_actions, wc);
- } else if (base->dl_type == htons(ETH_TYPE_IPV6)) {
+ break;
+
+ case ETH_TYPE_IPV6:
commit_set_ipv6_action(flow, base, odp_actions, wc);
+ break;
+
+ case ETH_TYPE_ARP:
+ return commit_set_arp_action(flow, base, odp_actions, wc);
}
+
+ return 0;
}
static void
odp_put_pkt_mark_action(base->pkt_mark, odp_actions);
}
+
/* If any of the flow key data that ODP actions can modify are different in
* 'base' and 'flow', appends ODP actions to 'odp_actions' that change the flow
* key from 'base' into 'flow', and then changes 'base' the same way. Does not
* commit set_tunnel actions. Users should call commit_odp_tunnel_action()
* in addition to this function if needed. Sets fields in 'wc' that are
- * used as part of the action. */
-void
+ * used as part of the action.
+ *
+ * Returns a reason to force processing the flow's packets into the userspace
+ * slow path, if there is one, otherwise 0. */
+enum slow_path_reason
commit_odp_actions(const struct flow *flow, struct flow *base,
struct ofpbuf *odp_actions, struct flow_wildcards *wc,
int *mpls_depth_delta)
{
+ enum slow_path_reason slow;
+
commit_set_ether_addr_action(flow, base, odp_actions, wc);
commit_vlan_action(flow->vlan_tci, base, odp_actions, wc);
- commit_set_nw_action(flow, base, odp_actions, wc);
+ slow = commit_set_nw_action(flow, base, odp_actions, wc);
commit_set_port_action(flow, base, odp_actions, wc);
/* Committing MPLS actions should occur after committing nw and port
* actions. This is because committing MPLS actions may alter a packet so
commit_mpls_action(flow, base, odp_actions, wc, mpls_depth_delta);
commit_set_priority_action(flow, base, odp_actions, wc);
commit_set_pkt_mark_action(flow, base, odp_actions, wc);
+
+ return slow;
}