case OVS_ACTION_ATTR_USERSPACE: 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_PUSH_MPLS: return sizeof(struct ovs_action_push_mpls);
+ case OVS_ACTION_ATTR_POP_MPLS: return sizeof(ovs_be16);
case OVS_ACTION_ATTR_SET: return -2;
case OVS_ACTION_ATTR_SAMPLE: return -2;
case OVS_KEY_ATTR_ENCAP: return "encap";
case OVS_KEY_ATTR_PRIORITY: return "skb_priority";
case OVS_KEY_ATTR_SKB_MARK: return "skb_mark";
- case OVS_KEY_ATTR_TUN_ID: return "tun_id";
case OVS_KEY_ATTR_TUNNEL: return "tunnel";
case OVS_KEY_ATTR_IN_PORT: return "in_port";
case OVS_KEY_ATTR_ETHERNET: return "eth";
case OVS_KEY_ATTR_ICMPV6: return "icmpv6";
case OVS_KEY_ATTR_ARP: return "arp";
case OVS_KEY_ATTR_ND: return "nd";
+ case OVS_KEY_ATTR_MPLS: return "mpls";
case __OVS_KEY_ATTR_MAX:
default:
{
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 },
+ [OVS_USERSPACE_ATTR_USERDATA] = { .type = NL_A_UNSPEC,
+ .optional = true },
};
struct nlattr *a[ARRAY_SIZE(ovs_userspace_policy)];
+ const struct nlattr *userdata_attr;
if (!nl_parse_nested(attr, ovs_userspace_policy, a, ARRAY_SIZE(a))) {
ds_put_cstr(ds, "userspace(error)");
ds_put_format(ds, "userspace(pid=%"PRIu32,
nl_attr_get_u32(a[OVS_USERSPACE_ATTR_PID]));
- if (a[OVS_USERSPACE_ATTR_USERDATA]) {
+ userdata_attr = a[OVS_USERSPACE_ATTR_USERDATA];
+ if (userdata_attr && nl_attr_get_size(userdata_attr) == sizeof(uint64_t)) {
uint64_t userdata = nl_attr_get_u64(a[OVS_USERSPACE_ATTR_USERDATA]);
union user_action_cookie cookie;
ds_put_format(ds, ",userdata=0x%"PRIx64, userdata);
break;
}
+ } else if (userdata_attr) {
+ const uint8_t *userdata = nl_attr_get(userdata_attr);
+ size_t len = nl_attr_get_size(userdata_attr);
+ size_t i;
+
+ ds_put_format(ds, ",userdata(");
+ for (i = 0; i < len; i++) {
+ ds_put_format(ds, "%02x", userdata[i]);
+ }
+ ds_put_char(ds, ')');
}
ds_put_char(ds, ')');
}
}
+static void
+format_mpls_lse(struct ds *ds, ovs_be32 mpls_lse)
+{
+ ds_put_format(ds, "label=%"PRIu32",tc=%d,ttl=%d,bos=%d",
+ mpls_lse_to_label(mpls_lse),
+ mpls_lse_to_tc(mpls_lse),
+ mpls_lse_to_ttl(mpls_lse),
+ mpls_lse_to_bos(mpls_lse));
+}
+
static void
format_odp_action(struct ds *ds, const struct nlattr *a)
{
case OVS_ACTION_ATTR_POP_VLAN:
ds_put_cstr(ds, "pop_vlan");
break;
+ case OVS_ACTION_ATTR_PUSH_MPLS: {
+ const struct ovs_action_push_mpls *mpls = nl_attr_get(a);
+ ds_put_cstr(ds, "push_mpls(");
+ format_mpls_lse(ds, mpls->mpls_lse);
+ ds_put_format(ds, ",eth_type=0x%"PRIx16")", ntohs(mpls->mpls_ethertype));
+ break;
+ }
+ case OVS_ACTION_ATTR_POP_MPLS: {
+ ovs_be16 ethertype = nl_attr_get_be16(a);
+ ds_put_format(ds, "pop_mpls(eth_type=0x%"PRIx16")", ntohs(ethertype));
+ break;
+ }
case OVS_ACTION_ATTR_SAMPLE:
format_odp_sample_action(ds, a);
break;
int n = -1;
if (sscanf(s, "userspace(pid=%lli)%n", &pid, &n) > 0 && n > 0) {
- odp_put_userspace_action(pid, NULL, actions);
+ odp_put_userspace_action(pid, NULL, 0, actions);
return n;
} else if (sscanf(s, "userspace(pid=%lli,sFlow(vid=%i,"
"pcp=%i,output=%lli))%n",
cookie.type = USER_ACTION_COOKIE_SFLOW;
cookie.sflow.vlan_tci = htons(tci);
cookie.sflow.output = output;
- odp_put_userspace_action(pid, &cookie, actions);
+ odp_put_userspace_action(pid, &cookie, sizeof cookie, actions);
return n;
} else if (sscanf(s, "userspace(pid=%lli,slow_path%n", &pid, &n) > 0
&& n > 0) {
}
n++;
- odp_put_userspace_action(pid, &cookie, actions);
+ odp_put_userspace_action(pid, &cookie, sizeof cookie, actions);
return n;
} else if (sscanf(s, "userspace(pid=%lli,userdata="
"%31[x0123456789abcdefABCDEF])%n", &pid, userdata_s,
&n) > 0 && n > 0) {
- union user_action_cookie cookie;
uint64_t userdata;
userdata = strtoull(userdata_s, NULL, 0);
- memcpy(&cookie, &userdata, sizeof cookie);
- odp_put_userspace_action(pid, &cookie, actions);
+ odp_put_userspace_action(pid, &userdata, sizeof(userdata),
+ actions);
return n;
+ } else if (sscanf(s, "userspace(pid=%lli,userdata(%n", &pid, &n) > 0
+ && n > 0) {
+ struct ofpbuf buf;
+ char *end;
+
+ ofpbuf_init(&buf, 16);
+ end = ofpbuf_put_hex(&buf, &s[n], NULL);
+ if (end[0] == ')' && end[1] == ')') {
+ odp_put_userspace_action(pid, buf.data, buf.size, actions);
+ ofpbuf_uninit(&buf);
+ return (end + 2) - s;
+ }
}
}
case OVS_KEY_ATTR_ENCAP: return -2;
case OVS_KEY_ATTR_PRIORITY: return 4;
case OVS_KEY_ATTR_SKB_MARK: return 4;
- case OVS_KEY_ATTR_TUN_ID: return 8;
case OVS_KEY_ATTR_TUNNEL: return -2;
case OVS_KEY_ATTR_IN_PORT: return 4;
case OVS_KEY_ATTR_ETHERNET: return sizeof(struct ovs_key_ethernet);
case OVS_KEY_ATTR_VLAN: return sizeof(ovs_be16);
case OVS_KEY_ATTR_ETHERTYPE: return 2;
+ case OVS_KEY_ATTR_MPLS: return sizeof(struct ovs_key_mpls);
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);
ds_put_format(ds, "(%#"PRIx32")", nl_attr_get_u32(a));
break;
- case OVS_KEY_ATTR_TUN_ID:
- ds_put_format(ds, "(%#"PRIx64")", ntohll(nl_attr_get_be64(a)));
- break;
-
case OVS_KEY_ATTR_TUNNEL:
memset(&tun_key, 0, sizeof tun_key);
if (tun_key_from_attr(a, &tun_key) == ODP_FIT_ERROR) {
ds_put_char(ds, ')');
break;
+ case OVS_KEY_ATTR_MPLS: {
+ const struct ovs_key_mpls *mpls_key = nl_attr_get(a);
+ ds_put_char(ds, '(');
+ format_mpls_lse(ds, mpls_key->mpls_top_lse);
+ ds_put_char(ds, ')');
+ break;
+ }
+
case OVS_KEY_ATTR_ETHERTYPE:
ds_put_format(ds, "(0x%04"PRIx16")",
ntohs(nl_attr_get_be16(a)));
return true;
}
+static ovs_be32
+mpls_lse_from_components(int mpls_label, int mpls_tc, int mpls_ttl, int mpls_bos)
+{
+ return (htonl((mpls_label << MPLS_LABEL_SHIFT) |
+ (mpls_tc << MPLS_TC_SHIFT) |
+ (mpls_ttl << MPLS_TTL_SHIFT) |
+ (mpls_bos << MPLS_BOS_SHIFT)));
+}
+
static int
parse_odp_key_attr(const char *s, const struct simap *port_names,
struct ofpbuf *key)
}
}
- {
- char tun_id_s[32];
- int n = -1;
-
- if (sscanf(s, "tun_id(%31[x0123456789abcdefABCDEF])%n",
- tun_id_s, &n) > 0 && n > 0) {
- uint64_t tun_id = strtoull(tun_id_s, NULL, 0);
- nl_msg_put_be64(key, OVS_KEY_ATTR_TUN_ID, htonll(tun_id));
- return n;
- }
- }
-
{
char tun_id_s[32];
int tos, ttl;
}
}
+ {
+ int label, tc, ttl, bos;
+ int n = -1;
+
+ if (sscanf(s, "mpls(label=%"SCNi32",tc=%i,ttl=%i,bos=%i)%n",
+ &label, &tc, &ttl, &bos, &n) > 0 &&
+ n > 0) {
+ struct ovs_key_mpls *mpls;
+
+ mpls = nl_msg_put_unspec_uninit(key, OVS_KEY_ATTR_MPLS,
+ sizeof *mpls);
+ mpls->mpls_top_lse = mpls_lse_from_components(label, tc, ttl, bos);
+ return n;
+ }
+ }
+
{
ovs_be32 ipv4_src;
ovs_be32 ipv4_dst;
if (flow->tunnel.ip_dst) {
tun_key_to_attr(buf, &flow->tunnel);
- } else if (flow->tunnel.tun_id != htonll(0)) {
- nl_msg_put_be64(buf, OVS_KEY_ATTR_TUN_ID, flow->tunnel.tun_id);
}
if (flow->skb_mark) {
memcpy(arp_key->arp_tha, flow->arp_tha, ETH_ADDR_LEN);
}
+ if (flow->mpls_depth) {
+ struct ovs_key_mpls *mpls_key;
+
+ mpls_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_MPLS,
+ sizeof *mpls_key);
+ mpls_key->mpls_top_lse = flow->mpls_lse;
+ }
+
if (is_ip_any(flow) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
if (flow->nw_proto == IPPROTO_TCP) {
struct ovs_key_tcp *tcp_key;
uint64_t present_attrs;
size_t left;
+ BUILD_ASSERT(OVS_KEY_ATTR_MAX < CHAR_BIT * sizeof present_attrs);
present_attrs = 0;
*out_of_range_attrp = 0;
NL_ATTR_FOR_EACH (nla, left, key, key_len) {
return false;
}
- if (type >= CHAR_BIT * sizeof present_attrs) {
+ if (type > OVS_KEY_ATTR_MAX) {
*out_of_range_attrp = type;
} else {
if (present_attrs & (UINT64_C(1) << type)) {
}
static enum odp_key_fitness
-parse_l3_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
- uint64_t present_attrs, int out_of_range_attr,
- uint64_t expected_attrs, struct flow *flow,
- const struct nlattr *key, size_t key_len)
+parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
+ uint64_t present_attrs, int out_of_range_attr,
+ uint64_t expected_attrs, struct flow *flow,
+ const struct nlattr *key, size_t key_len)
{
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- if (flow->dl_type == htons(ETH_TYPE_IP)) {
+ if (eth_type_mpls(flow->dl_type)) {
+ 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]);
+ flow->mpls_depth++;
+ } else 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;
}
if (flow->nw_proto == IPPROTO_TCP
- && is_ip_any(flow)
+ && (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)) {
flow->tp_dst = tcp_key->tcp_dst;
}
} else if (flow->nw_proto == IPPROTO_UDP
- && is_ip_any(flow)
+ && (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)) {
if (!parse_ethertype(attrs, present_attrs, &expected_attrs, flow)) {
return ODP_FIT_ERROR;
}
- encap_fitness = parse_l3_onward(attrs, present_attrs, out_of_range_attr,
- expected_attrs, flow, key, key_len);
+ encap_fitness = parse_l2_5_onward(attrs, present_attrs, out_of_range_attr,
+ expected_attrs, flow, key, key_len);
/* The overall fitness is the worse of the outer and inner attributes. */
return MAX(fitness, encap_fitness);
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_SKB_MARK;
}
- if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TUN_ID)) {
- flow->tunnel.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_TUNNEL)) {
enum odp_key_fitness res;
return parse_8021q_onward(attrs, present_attrs, out_of_range_attr,
expected_attrs, flow, key, key_len);
}
- return parse_l3_onward(attrs, present_attrs, out_of_range_attr,
- expected_attrs, flow, key, key_len);
+ return parse_l2_5_onward(attrs, present_attrs, out_of_range_attr,
+ expected_attrs, flow, key, key_len);
}
/* Returns 'fitness' as a string, for use in debug messages. */
}
/* Appends an OVS_ACTION_ATTR_USERSPACE action to 'odp_actions' that specifies
- * Netlink PID 'pid'. If 'cookie' is nonnull, adds a userdata attribute whose
- * contents contains 'cookie' and returns the offset within 'odp_actions' of
- * the start of the cookie. (If 'cookie' is null, then the return value is not
- * meaningful.) */
+ * Netlink PID 'pid'. If 'userdata' is nonnull, adds a userdata attribute
+ * whose contents are the 'userdata_size' bytes at 'userdata' and returns the
+ * offset within 'odp_actions' of the start of the cookie. (If 'userdata' is
+ * null, then the return value is not meaningful.) */
size_t
-odp_put_userspace_action(uint32_t pid, const union user_action_cookie *cookie,
+odp_put_userspace_action(uint32_t pid,
+ const void *userdata, size_t userdata_size,
struct ofpbuf *odp_actions)
{
+ size_t userdata_ofs;
size_t offset;
offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_USERSPACE);
nl_msg_put_u32(odp_actions, OVS_USERSPACE_ATTR_PID, pid);
- if (cookie) {
+ if (userdata) {
+ userdata_ofs = odp_actions->size + NLA_HDRLEN;
nl_msg_put_unspec(odp_actions, OVS_USERSPACE_ATTR_USERDATA,
- cookie, sizeof *cookie);
+ userdata, userdata_size);
+ } else {
+ userdata_ofs = 0;
}
nl_msg_end_nested(odp_actions, offset);
- return cookie ? odp_actions->size - NLA_ALIGN(sizeof *cookie) : 0;
+ return userdata_ofs;
}
void
nl_msg_end_nested(odp_actions, offset);
}
+void
+odp_put_skb_mark_action(const uint32_t skb_mark,
+ struct ofpbuf *odp_actions)
+{
+ commit_set_action(odp_actions, OVS_KEY_ATTR_SKB_MARK, &skb_mark,
+ sizeof(skb_mark));
+}
+
/* If any of the flow key data that ODP actions can modify are different in
* 'base->tunnel' and 'flow->tunnel', appends a set_tunnel ODP action to
* 'odp_actions' that change the flow tunneling information in key from
/* A valid IPV4_TUNNEL must have non-zero ip_dst. */
if (flow->tunnel.ip_dst) {
odp_put_tunnel_action(&base->tunnel, odp_actions);
- } else {
- commit_set_action(odp_actions, OVS_KEY_ATTR_TUN_ID,
- &base->tunnel.tun_id, sizeof base->tunnel.tun_id);
}
}
base->vlan_tci = flow->vlan_tci;
}
+static void
+commit_mpls_action(const struct flow *flow, struct flow *base,
+ struct ofpbuf *odp_actions)
+{
+ if (flow->mpls_lse == base->mpls_lse &&
+ flow->mpls_depth == base->mpls_depth) {
+ return;
+ }
+
+ if (flow->mpls_depth < base->mpls_depth) {
+ if (base->mpls_depth - flow->mpls_depth > 1) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10);
+ VLOG_WARN_RL(&rl, "Multiple mpls_pop actions reduced to "
+ " a single mpls_pop action");
+ }
+
+ nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_POP_MPLS, flow->dl_type);
+ } else if (flow->mpls_depth > base->mpls_depth) {
+ struct ovs_action_push_mpls *mpls;
+
+ if (flow->mpls_depth - base->mpls_depth > 1) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10);
+ VLOG_WARN_RL(&rl, "Multiple mpls_push actions reduced to "
+ " a single mpls_push action");
+ }
+
+ mpls = nl_msg_put_unspec_uninit(odp_actions, OVS_ACTION_ATTR_PUSH_MPLS,
+ sizeof *mpls);
+ memset(mpls, 0, sizeof *mpls);
+ mpls->mpls_ethertype = flow->dl_type;
+ mpls->mpls_lse = flow->mpls_lse;
+ } else {
+ struct ovs_key_mpls mpls_key;
+
+ mpls_key.mpls_top_lse = flow->mpls_lse;
+ commit_set_action(odp_actions, OVS_KEY_ATTR_MPLS,
+ &mpls_key, sizeof(mpls_key));
+ }
+
+ base->dl_type = flow->dl_type;
+ base->mpls_lse = flow->mpls_lse;
+ base->mpls_depth = flow->mpls_depth;
+}
+
static void
commit_set_ipv4_action(const struct flow *flow, struct flow *base,
struct ofpbuf *odp_actions)
commit_set_port_action(const struct flow *flow, struct flow *base,
struct ofpbuf *odp_actions)
{
- if (!base->tp_src && !base->tp_dst) {
+ if (!is_ip_any(base) || (!base->tp_src && !base->tp_dst)) {
return;
}
}
base->skb_mark = flow->skb_mark;
- commit_set_action(odp_actions, OVS_KEY_ATTR_SKB_MARK,
- &base->skb_mark, sizeof(base->skb_mark));
+ odp_put_skb_mark_action(base->skb_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
commit_vlan_action(flow, base, odp_actions);
commit_set_nw_action(flow, base, odp_actions);
commit_set_port_action(flow, base, odp_actions);
+ /* Commiting MPLS actions should occur after committing nw and port
+ * actions. This is because committing MPLS actions may alter a packet so
+ * that it is no longer IP and thus nw and port actions are no longer valid.
+ */
+ commit_mpls_action(flow, base, odp_actions);
commit_set_priority_action(flow, base, odp_actions);
commit_set_skb_mark_action(flow, base, odp_actions);
}