* interactions with the datapath.
*/
+/* Returns one the following for the action with the given OVS_ACTION_ATTR_*
+ * 'type':
+ *
+ * - For an action whose argument has a fixed length, returned that
+ * nonnegative length in bytes.
+ *
+ * - For an action with a variable-length argument, returns -2.
+ *
+ * - For an invalid 'type', returns -1. */
int
odp_action_len(uint16_t type)
{
switch ((enum ovs_action_type) type) {
case OVS_ACTION_ATTR_OUTPUT: return 4;
- case OVS_ACTION_ATTR_USERSPACE: return 8;
- case OVS_ACTION_ATTR_SET_DL_TCI: return 2;
- case OVS_ACTION_ATTR_STRIP_VLAN: return 0;
+ case OVS_ACTION_ATTR_USERSPACE: return -2;
+ case OVS_ACTION_ATTR_PUSH_VLAN: return 2;
+ case OVS_ACTION_ATTR_POP_VLAN: return 0;
case OVS_ACTION_ATTR_SET_DL_SRC: return ETH_ADDR_LEN;
case OVS_ACTION_ATTR_SET_DL_DST: return ETH_ADDR_LEN;
case OVS_ACTION_ATTR_SET_NW_SRC: return 4;
case OVS_ACTION_ATTR_SET_TUNNEL: return 8;
case OVS_ACTION_ATTR_SET_PRIORITY: return 4;
case OVS_ACTION_ATTR_POP_PRIORITY: return 0;
+ case OVS_ACTION_ATTR_SAMPLE: return -2;
case OVS_ACTION_ATTR_UNSPEC:
case __OVS_ACTION_ATTR_MAX:
}
}
+static void
+format_odp_sample_action(struct ds *ds, const struct nlattr *attr)
+{
+ static const struct nl_policy ovs_sample_policy[] = {
+ [OVS_SAMPLE_ATTR_PROBABILITY] = { .type = NL_A_U32 },
+ [OVS_SAMPLE_ATTR_ACTIONS] = { .type = NL_A_NESTED }
+ };
+ struct nlattr *a[ARRAY_SIZE(ovs_sample_policy)];
+ double percentage;
+ const struct nlattr *nla_acts;
+ int len;
+
+ ds_put_cstr(ds, "sample");
+
+ if (!nl_parse_nested(attr, ovs_sample_policy, a, ARRAY_SIZE(a))) {
+ ds_put_cstr(ds, "(error)");
+ return;
+ }
+
+ percentage = (100.0 * nl_attr_get_u32(a[OVS_SAMPLE_ATTR_PROBABILITY])) /
+ UINT32_MAX;
+
+ ds_put_format(ds, "(sample=%.1f%%,", percentage);
+
+ ds_put_cstr(ds, "actions(");
+ nla_acts = nl_attr_get(a[OVS_SAMPLE_ATTR_ACTIONS]);
+ len = nl_attr_get_size(a[OVS_SAMPLE_ATTR_ACTIONS]);
+ format_odp_actions(ds, nla_acts, len);
+ ds_put_format(ds, "))");
+}
+
+static void
+format_odp_userspace_action(struct ds *ds, const struct nlattr *attr)
+{
+ static const struct nl_policy ovs_userspace_policy[] = {
+ [OVS_USERSPACE_ATTR_PID] = { .type = NL_A_U32 },
+ [OVS_USERSPACE_ATTR_USERDATA] = { .type = NL_A_U64, .optional = true },
+ };
+ struct nlattr *a[ARRAY_SIZE(ovs_userspace_policy)];
+
+ if (!nl_parse_nested(attr, ovs_userspace_policy, a, ARRAY_SIZE(a))) {
+ ds_put_cstr(ds, "userspace(error)");
+ return;
+ }
+
+ ds_put_format(ds, "userspace(pid=%"PRIu32,
+ nl_attr_get_u32(a[OVS_USERSPACE_ATTR_PID]));
+
+ if (a[OVS_USERSPACE_ATTR_USERDATA]) {
+ uint64_t userdata = nl_attr_get_u64(a[OVS_USERSPACE_ATTR_USERDATA]);
+ struct user_action_cookie cookie;
+
+ memcpy(&cookie, &userdata, sizeof cookie);
+
+ if (cookie.type == USER_ACTION_COOKIE_CONTROLLER) {
+ ds_put_format(ds, ",controller,length=%"PRIu32,
+ cookie.data);
+ } else if (cookie.type == USER_ACTION_COOKIE_SFLOW) {
+ ds_put_format(ds, ",sFlow,n_output=%"PRIu8","
+ "vid=%"PRIu16",pcp=%"PRIu8",ifindex=%"PRIu32,
+ cookie.n_output, vlan_tci_to_vid(cookie.vlan_tci),
+ vlan_tci_to_pcp(cookie.vlan_tci), cookie.data);
+ } else {
+ ds_put_format(ds, ",userdata=0x%"PRIx64, userdata);
+ }
+ }
+
+ ds_put_char(ds, ')');
+}
+
+
void
format_odp_action(struct ds *ds, const struct nlattr *a)
{
const uint8_t *eth;
+ int expected_len;
ovs_be32 ip;
- if (nl_attr_get_size(a) != odp_action_len(nl_attr_type(a))) {
+ expected_len = odp_action_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 for: ",
- nl_attr_get_size(a), odp_action_len(nl_attr_type(a)));
+ nl_attr_get_size(a), expected_len);
format_generic_odp_action(ds, a);
return;
}
ds_put_format(ds, "%"PRIu16, nl_attr_get_u32(a));
break;
case OVS_ACTION_ATTR_USERSPACE:
- ds_put_format(ds, "userspace(%"PRIu64")", nl_attr_get_u64(a));
+ format_odp_userspace_action(ds, a);
break;
case OVS_ACTION_ATTR_SET_TUNNEL:
ds_put_format(ds, "set_tunnel(%#"PRIx64")",
ntohll(nl_attr_get_be64(a)));
break;
- case OVS_ACTION_ATTR_SET_DL_TCI:
- ds_put_format(ds, "set_tci(vid=%"PRIu16",pcp=%d)",
+ case OVS_ACTION_ATTR_PUSH_VLAN:
+ ds_put_format(ds, "push_vlan(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_ACTION_ATTR_STRIP_VLAN:
- ds_put_format(ds, "strip_vlan");
+ case OVS_ACTION_ATTR_POP_VLAN:
+ ds_put_format(ds, "pop_vlan");
break;
case OVS_ACTION_ATTR_SET_DL_SRC:
eth = nl_attr_get_unspec(a, ETH_ADDR_LEN);
case OVS_ACTION_ATTR_POP_PRIORITY:
ds_put_cstr(ds, "pop_priority");
break;
+ case OVS_ACTION_ATTR_SAMPLE:
+ format_odp_sample_action(ds, a);
+ break;
default:
format_generic_odp_action(ds, a);
break;
nl_msg_put_be64(buf, OVS_KEY_ATTR_TUN_ID, flow->tun_id);
}
- nl_msg_put_u32(buf, OVS_KEY_ATTR_IN_PORT,
- ofp_port_to_odp_port(flow->in_port));
+ if (flow->in_port != OFPP_NONE) {
+ nl_msg_put_u32(buf, OVS_KEY_ATTR_IN_PORT,
+ ofp_port_to_odp_port(flow->in_port));
+ }
eth_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ETHERNET,
sizeof *eth_key);
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) {
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_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);
break;
default:
- if (type == OVS_KEY_ATTR_UNSPEC
- || prev_type == OVS_KEY_ATTR_UNSPEC) {
- return EINVAL;
- }
return EINVAL;
}
return 0;
case OVS_KEY_ATTR_ICMPV6:
- if (flow->icmp_type == htons(ND_NEIGHBOR_SOLICIT)
- || flow->icmp_type == htons(ND_NEIGHBOR_ADVERT)) {
+ if (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT)
+ || flow->tp_src == htons(ND_NEIGHBOR_ADVERT)) {
return EINVAL;
}
return 0;