vport-netdev.h
openvswitch_extras = \
+ README \
CAPWAP.txt
dist_sources = $(foreach module,$(dist_modules),$($(module)_sources))
--- /dev/null
+Open vSwitch datapath developer documentation
+=============================================
+
+The Open vSwitch kernel module allows flexible userspace control over
+flow-level packet processing on selected network devices. It can be
+used to implement a plain Ethernet switch, network device bonding,
+VLAN processing, network access control, flow-based network control,
+and so on.
+
+The kernel module implements multiple "datapaths" (analogous to
+bridges), each of which can have multiple "vports" (analogous to ports
+within a bridge). Each datapath also has associated with it a "flow
+table" that userspace populates with "flows" that map from keys based
+on packet headers and metadata to sets of actions. The most common
+action forwards the packet to another vport; other actions are also
+implemented.
+
+When a packet arrives on a vport, the kernel module processes it by
+extracting its flow key and looking it up in the flow table. If there
+is a matching flow, it executes the associated actions. If there is
+no match, it queues the packet to userspace for processing (as part of
+its processing, userspace will likely set up a flow to handle further
+packets of the same type entirely in-kernel).
+
+
+Flow key compatibility
+----------------------
+
+Network protocols evolve over time. New protocols become important
+and existing protocols lose their prominence. For the Open vSwitch
+kernel module to remain relevant, it must be possible for newer
+versions to parse additional protocols as part of the flow key. It
+might even be desirable, someday, to drop support for parsing
+protocols that have become obsolete. Therefore, the Netlink interface
+to Open vSwitch is designed to allow carefully written userspace
+applications to work with any version of the flow key, past or future.
+
+To support this forward and backward compatibility, whenever the
+kernel module passes a packet to userspace, it also passes along the
+flow key that it parsed from the packet. Userspace then extracts its
+own notion of a flow key from the packet and compares it against the
+kernel-provided version:
+
+ - If userspace's notion of the flow key for the packet matches the
+ kernel's, then nothing special is necessary.
+
+ - If the kernel's flow key includes more fields than the userspace
+ version of the flow key, for example if the kernel decoded IPv6
+ headers but userspace stopped at the Ethernet type (because it
+ does not understand IPv6), then again nothing special is
+ necessary. Userspace can still set up a flow in the usual way,
+ as long as it uses the kernel-provided flow key to do it.
+
+ - If the userspace flow key includes more fields than the
+ kernel's, for example if userspace decoded an IPv6 header but
+ the kernel stopped at the Ethernet type, then userspace can
+ forward the packet manually, without setting up a flow in the
+ kernel. This case is bad for performance because every packet
+ that the kernel considers part of the flow must go to userspace,
+ but the forwarding behavior is correct. (If userspace can
+ determine that the values of the extra fields would not affect
+ forwarding behavior, then it could set up a flow anyway.)
+
+How flow keys evolve over time is important to making this work, so
+the following sections go into detail.
+
+
+Flow key format
+---------------
+
+A flow key is passed over a Netlink socket as a sequence of Netlink
+attributes. Some attributes represent packet metadata, defined as any
+information about a packet that cannot be extracted from the packet
+itself, e.g. the vport on which the packet was received. Most
+attributes, however, are extracted from headers within the packet,
+e.g. source and destination addresses from Ethernet, IP, or TCP
+headers.
+
+The <linux/openvswitch.h> header file defines the exact format of the
+flow key attributes. For informal explanatory purposes here, we write
+them as comma-separated strings, with parentheses indicating arguments
+and nesting. For example, the following could represent a flow key
+corresponding to a TCP packet that arrived on vport 1:
+
+ in_port(1), eth(src=e0:91:f5:21:d0:b2, dst=00:02:e3:0f:80:a4),
+ eth_type(0x0800), ipv4(src=172.16.0.20, dst=172.18.0.52, proto=17, tos=0,
+ frag=no), tcp(src=49163, dst=80)
+
+Often we ellipsize arguments not important to the discussion, e.g.:
+
+ in_port(1), eth(...), eth_type(0x0800), ipv4(...), tcp(...)
+
+
+Basic rule for evolving flow keys
+---------------------------------
+
+Some care is needed to really maintain forward and backward
+compatibility for applications that follow the rules listed under
+"Flow key compatibility" above.
+
+The basic rule is obvious:
+
+ ------------------------------------------------------------------
+ New network protocol support must only supplement existing flow
+ key attributes. It must not change the meaning of already defined
+ flow key attributes.
+ ------------------------------------------------------------------
+
+This rule does have less-obvious consequences so it is worth working
+through a few examples. Suppose, for example, that the kernel module
+did not already implement VLAN parsing. Instead, it just interpreted
+the 802.1Q TPID (0x8100) as the Ethertype then stopped parsing the
+packet. The flow key for any packet with an 802.1Q header would look
+essentially like this, ignoring metadata:
+
+ eth(...), eth_type(0x8100)
+
+Naively, to add VLAN support, it makes sense to add a new "vlan" flow
+key attribute to contain the VLAN tag, then continue to decode the
+encapsulated headers beyond the VLAN tag using the existing field
+definitions. With this change, an TCP packet in VLAN 10 would have a
+flow key much like this:
+
+ eth(...), vlan(vid=10, pcp=0), eth_type(0x0800), ip(proto=6, ...), tcp(...)
+
+But this change would negatively affect a userspace application that
+has not been updated to understand the new "vlan" flow key attribute.
+The application could, following the flow compatibility rules above,
+ignore the "vlan" attribute that it does not understand and therefore
+assume that the flow contained IP packets. This is a bad assumption
+(the flow only contains IP packets if one parses and skips over the
+802.1Q header) and it could cause the application's behavior to change
+across kernel versions even though it follows the compatibility rules.
+
+The solution is to use a set of nested attributes. This is, for
+example, why 802.1Q support uses nested attributes. A TCP packet in
+VLAN 10 is actually expressed as:
+
+ eth(...), eth_type(0x8100), vlan(vid=10, pcp=0), encap(eth_type(0x0800),
+ ip(proto=6, ...), tcp(...)))
+
+Notice how the "eth_type", "ip", and "tcp" flow key attributes are
+nested inside the "encap" attribute. Thus, an application that does
+not understand the "vlan" key will not see either of those attributes
+and therefore will not misinterpret them. (Also, the outer eth_type
+is still 0x8100, not changed to 0x0800.)
+
+Other rules
+-----------
+
+The other rules for flow keys are much less subtle:
+
+ - Duplicate attributes are not allowed at a given nesting level.
+
+ - Ordering of attributes is not significant.
+
+ - When the kernel sends a given flow key to userspace, it always
+ composes it the same way. This allows userspace to hash and
+ compare entire flow keys that it may not be able to fully
+ interpret.
return 0;
}
-static int push_vlan(struct sk_buff *skb, const struct ovs_key_8021q *q_key)
+static int push_vlan(struct sk_buff *skb, const struct ovs_action_push_vlan *vlan)
{
if (unlikely(vlan_tx_tag_present(skb))) {
u16 current_tag;
+ ETH_HLEN, VLAN_HLEN, 0));
}
- __vlan_hwaccel_put_tag(skb, ntohs(q_key->q_tci));
+ __vlan_hwaccel_put_tag(skb, ntohs(vlan->vlan_tci));
return 0;
}
output_userspace(dp, skb, a);
break;
- case OVS_ACTION_ATTR_PUSH:
- /* Only supported push action is on vlan tag. */
- err = push_vlan(skb, nla_data(nla_data(a)));
+ case OVS_ACTION_ATTR_PUSH_VLAN:
+ err = push_vlan(skb, nla_data(a));
if (unlikely(err)) /* skb already freed. */
return err;
break;
- case OVS_ACTION_ATTR_POP:
- /* Only supported pop action is on vlan tag. */
+ case OVS_ACTION_ATTR_POP_VLAN:
err = pop_vlan(skb);
break;
return validate_actions(actions, key, depth + 1);
}
-static int validate_action_key(const struct nlattr *a,
- const struct sw_flow_key *flow_key)
+static int validate_set(const struct nlattr *a,
+ const struct sw_flow_key *flow_key)
{
- int act_type = nla_type(a);
const struct nlattr *ovs_key = nla_data(a);
int key_type = nla_type(ovs_key);
nla_len(ovs_key) != ovs_key_lens[key_type])
return -EINVAL;
-#define ACTION(act, key) (((act) << 8) | (key))
-
- switch (ACTION(act_type, key_type)) {
+ switch (key_type) {
const struct ovs_key_ipv4 *ipv4_key;
- const struct ovs_key_8021q *q_key;
-
- case ACTION(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_PRIORITY):
- case ACTION(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_TUN_ID):
- case ACTION(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_ETHERNET):
- break;
-
- case ACTION(OVS_ACTION_ATTR_PUSH, OVS_KEY_ATTR_8021Q):
- q_key = nla_data(ovs_key);
- if (q_key->q_tpid != htons(ETH_P_8021Q))
- return -EINVAL;
- if (q_key->q_tci & htons(VLAN_TAG_PRESENT))
- return -EINVAL;
+ case OVS_KEY_ATTR_PRIORITY:
+ case OVS_KEY_ATTR_TUN_ID:
+ case OVS_KEY_ATTR_ETHERNET:
break;
- case ACTION(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_IPV4):
+ case OVS_KEY_ATTR_IPV4:
if (flow_key->eth.type != htons(ETH_P_IP))
return -EINVAL;
break;
- case ACTION(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_TCP):
+ case OVS_KEY_ATTR_TCP:
if (flow_key->ip.proto != IPPROTO_TCP)
return -EINVAL;
break;
- case ACTION(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_UDP):
+ case OVS_KEY_ATTR_UDP:
if (flow_key->ip.proto != IPPROTO_UDP)
return -EINVAL;
default:
return -EINVAL;
}
-#undef ACTION
+
return 0;
}
nla_for_each_nested(a, attr, rem) {
/* Expected argument lengths, (u32)-1 for variable length. */
static const u32 action_lens[OVS_ACTION_ATTR_MAX + 1] = {
- [OVS_ACTION_ATTR_OUTPUT] = 4,
+ [OVS_ACTION_ATTR_OUTPUT] = sizeof(u32),
[OVS_ACTION_ATTR_USERSPACE] = (u32)-1,
- [OVS_ACTION_ATTR_PUSH] = (u32)-1,
- [OVS_ACTION_ATTR_POP] = 2,
+ [OVS_ACTION_ATTR_PUSH_VLAN] = sizeof(struct ovs_action_push_vlan),
+ [OVS_ACTION_ATTR_POP_VLAN] = 0,
[OVS_ACTION_ATTR_SET] = (u32)-1,
[OVS_ACTION_ATTR_SAMPLE] = (u32)-1
};
+ const struct ovs_action_push_vlan *vlan;
int type = nla_type(a);
if (type > OVS_ACTION_ATTR_MAX ||
break;
- case OVS_ACTION_ATTR_POP:
- if (nla_get_u16(a) != OVS_KEY_ATTR_8021Q)
+ case OVS_ACTION_ATTR_POP_VLAN:
+ break;
+
+ case OVS_ACTION_ATTR_PUSH_VLAN:
+ vlan = nla_data(a);
+ if (vlan->vlan_tpid != htons(ETH_P_8021Q))
+ return -EINVAL;
+ if (vlan->vlan_tci & htons(VLAN_TAG_PRESENT))
return -EINVAL;
break;
case OVS_ACTION_ATTR_SET:
- case OVS_ACTION_ATTR_PUSH:
- err = validate_action_key(a, key);
+ err = validate_set(a, key);
if (err)
return err;
break;
/* The size of the argument for each %OVS_KEY_ATTR_* Netlink attribute. */
const u32 ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
+ [OVS_KEY_ATTR_ENCAP] = 0,
[OVS_KEY_ATTR_PRIORITY] = sizeof(u32),
[OVS_KEY_ATTR_IN_PORT] = sizeof(u32),
[OVS_KEY_ATTR_ETHERNET] = sizeof(struct ovs_key_ethernet),
- [OVS_KEY_ATTR_8021Q] = sizeof(struct ovs_key_8021q),
+ [OVS_KEY_ATTR_VLAN] = sizeof(__be16),
[OVS_KEY_ATTR_ETHERTYPE] = sizeof(__be16),
[OVS_KEY_ATTR_IPV4] = sizeof(struct ovs_key_ipv4),
[OVS_KEY_ATTR_IPV6] = sizeof(struct ovs_key_ipv6),
return 0;
}
+static int parse_flow_nlattrs(const struct nlattr *attr,
+ const struct nlattr *a[], u64 *attrsp)
+{
+ const struct nlattr *nla;
+ u64 attrs;
+ int rem;
+
+ attrs = 0;
+ nla_for_each_nested(nla, attr, rem) {
+ u16 type = nla_type(nla);
+
+ if (type > OVS_KEY_ATTR_MAX || attrs & (1ULL << type) ||
+ nla_len(nla) != ovs_key_lens[type])
+ return -EINVAL;
+ attrs |= 1ULL << type;
+ a[type] = nla;
+ }
+ if (rem)
+ return -EINVAL;
+
+ *attrsp = attrs;
+ return 0;
+}
+
/**
* flow_from_nlattrs - parses Netlink attributes into a flow key.
* @swkey: receives the extracted flow key.
{
const struct nlattr *a[OVS_KEY_ATTR_MAX + 1];
const struct ovs_key_ethernet *eth_key;
- const struct nlattr *nla;
int key_len;
u64 attrs;
- int rem;
+ int err;
memset(swkey, 0, sizeof(struct sw_flow_key));
key_len = SW_FLOW_KEY_OFFSET(eth);
- attrs = 0;
- nla_for_each_nested(nla, attr, rem) {
- u16 type = nla_type(nla);
-
- if (type > OVS_KEY_ATTR_MAX || attrs & (1ULL << type) ||
- nla_len(nla) != ovs_key_lens[type])
- return -EINVAL;
- attrs |= 1ULL << type;
- a[type] = nla;
- }
- if (rem)
- return -EINVAL;
+ err = parse_flow_nlattrs(attr, a, &attrs);
+ if (err)
+ return err;
/* Metadata attributes. */
if (attrs & (1 << OVS_KEY_ATTR_PRIORITY)) {
memcpy(swkey->eth.src, eth_key->eth_src, ETH_ALEN);
memcpy(swkey->eth.dst, eth_key->eth_dst, ETH_ALEN);
- if (attrs & (1 << OVS_KEY_ATTR_8021Q)) {
- const struct ovs_key_8021q *q_key;
-
- q_key = nla_data(a[OVS_KEY_ATTR_8021Q]);
- /* Only standard 0x8100 VLANs currently supported. */
- if (q_key->q_tpid != htons(ETH_P_8021Q))
- return -EINVAL;
- if (q_key->q_tci & htons(VLAN_TAG_PRESENT))
+ if (attrs == ((1 << OVS_KEY_ATTR_VLAN) |
+ (1 << OVS_KEY_ATTR_ETHERTYPE) |
+ (1 << OVS_KEY_ATTR_ENCAP)) &&
+ nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]) == htons(ETH_P_8021Q)) {
+ swkey->eth.tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
+ if (swkey->eth.tci & htons(VLAN_TAG_PRESENT))
return -EINVAL;
- swkey->eth.tci = q_key->q_tci | htons(VLAN_TAG_PRESENT);
+ swkey->eth.tci |= htons(VLAN_TAG_PRESENT);
- attrs &= ~(1 << OVS_KEY_ATTR_8021Q);
+ err = parse_flow_nlattrs(a[OVS_KEY_ATTR_ENCAP], a, &attrs);
+ if (err)
+ return err;
}
if (attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) {
swkey->ipv4.addr.dst = ipv4_key->ipv4_dst;
if (swkey->ip.frag != OVS_FRAG_TYPE_LATER) {
- int err = ipv4_flow_from_nlattrs(swkey, &key_len, a, &attrs);
+ err = ipv4_flow_from_nlattrs(swkey, &key_len, a, &attrs);
if (err)
return err;
}
sizeof(swkey->ipv6.addr.dst));
if (swkey->ip.frag != OVS_FRAG_TYPE_LATER) {
- int err = ipv6_flow_from_nlattrs(swkey, &key_len, a, &attrs);
+ err = ipv6_flow_from_nlattrs(swkey, &key_len, a, &attrs);
if (err)
return err;
}
int flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
{
struct ovs_key_ethernet *eth_key;
- struct nlattr *nla;
+ struct nlattr *nla, *encap;
if (swkey->phy.priority)
NLA_PUT_U32(skb, OVS_KEY_ATTR_PRIORITY, swkey->phy.priority);
memcpy(eth_key->eth_dst, swkey->eth.dst, ETH_ALEN);
if (swkey->eth.tci != htons(0)) {
- struct ovs_key_8021q q_key;
-
- q_key.q_tpid = htons(ETH_P_8021Q);
- q_key.q_tci = swkey->eth.tci & ~htons(VLAN_TAG_PRESENT);
- NLA_PUT(skb, OVS_KEY_ATTR_8021Q, sizeof(q_key), &q_key);
+ NLA_PUT_BE16(skb, OVS_KEY_ATTR_ETHERTYPE, htons(ETH_P_8021Q));
+ NLA_PUT_BE16(skb, OVS_KEY_ATTR_VLAN,
+ swkey->eth.tci & ~htons(VLAN_TAG_PRESENT));
+ encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP);
+ } else {
+ encap = NULL;
}
if (swkey->eth.type == htons(ETH_P_802_2))
- return 0;
+ goto unencap;
NLA_PUT_BE16(skb, OVS_KEY_ATTR_ETHERTYPE, swkey->eth.type);
}
}
+unencap:
+ if (encap)
+ nla_nest_end(skb, encap);
+
return 0;
nla_put_failure:
enum ovs_key_attr {
OVS_KEY_ATTR_UNSPEC,
+ OVS_KEY_ATTR_ENCAP, /* Nested set of encapsulated attributes. */
OVS_KEY_ATTR_PRIORITY, /* u32 skb->priority */
OVS_KEY_ATTR_IN_PORT, /* u32 OVS dp port number */
OVS_KEY_ATTR_ETHERNET, /* struct ovs_key_ethernet */
- OVS_KEY_ATTR_8021Q, /* struct ovs_key_8021q */
+ OVS_KEY_ATTR_VLAN, /* be16 VLAN TCI */
OVS_KEY_ATTR_ETHERTYPE, /* be16 Ethernet type */
OVS_KEY_ATTR_IPV4, /* struct ovs_key_ipv4 */
OVS_KEY_ATTR_IPV6, /* struct ovs_key_ipv6 */
__u8 eth_dst[6];
};
-struct ovs_key_8021q {
- __be16 q_tpid;
- __be16 q_tci;
-};
-
struct ovs_key_ipv4 {
__be32 ipv4_src;
__be32 ipv4_dst;
#define OVS_USERSPACE_ATTR_MAX (__OVS_USERSPACE_ATTR_MAX - 1)
+/**
+ * struct ovs_action_push_vlan - %OVS_ACTION_ATTR_PUSH_VLAN action argument.
+ * @vlan_tpid: Tag protocol identifier (TPID) to push.
+ * @vlan_tci: Tag control identifier (TCI) to push. The CFI bit must not be
+ * set.
+ *
+ * The @vlan_tpid value is typically %ETH_P_8021Q. The only acceptable TPID
+ * values are those that the kernel module also parses as 802.1Q headers, to
+ * prevent %OVS_ACTION_ATTR_PUSH_VLAN followed by %OVS_ACTION_ATTR_POP_VLAN
+ * from having surprising results.
+ */
+struct ovs_action_push_vlan {
+ __be16 vlan_tpid; /* 802.1Q TPID. */
+ __be16 vlan_tci; /* 802.1Q TCI (VLAN ID and priority). */
+};
+
/**
* enum ovs_action_attr - Action types.
*
* @OVS_ACTION_ATTR_OUTPUT: Output packet to port.
* @OVS_ACTION_ATTR_USERSPACE: Send packet to userspace according to nested
* %OVS_USERSPACE_ATTR_* attributes.
- * @OVS_ACTION_ATTR_PUSH: Push header onto head of packet. The single nested
- * %OVS_KEY_ATTR_* attribute specifies a header to push and its value, e.g. a
- * nested attribute of type %OVS_KEY_ATTR_8021Q, with struct ovs_key_8021q as
- * argument, would push a VLAN header on the front of the packet.
- * @OVS_ACTION_ATTR_POP: Pop header according to %OVS_KEY_ATTR_ sent as
- * attribute data, e.g. %OVS_KEY_ATTR_8021Q as argument pops an outer VLAN
- * header.
- * @OVS_ACTION_ATTR_SET: Replaces the contents of an existing header.
- * The argument takes the same form as %OVS_ACTION_ATTR_PUSH.
+ * @OVS_ACTION_ATTR_SET: Replaces the contents of an existing header. The
+ * single nested %OVS_KEY_ATTR_* attribute specifies a header to modify and its
+ * value.
+ * @OVS_ACTION_ATTR_PUSH_VLAN: Push a new outermost 802.1Q header onto the
+ * packet.
+ * @OVS_ACTION_ATTR_POP_VLAN: Pop the outermost 802.1Q header off the packet.
* @OVS_ACTION_ATTR_SAMPLE: Probabilitically executes actions, as specified in
* the nested %OVS_SAMPLE_ATTR_* attributes.
*
- * Only a single field can be set with a single %OVS_ACTION_ATTR_{SET,PUSH}.
- * Not all fields are modifiable.
+ * Only a single header can be set with a single %OVS_ACTION_ATTR_SET. Not all
+ * fields within a header are modifiable, e.g. the IPv4 protocol and fragment
+ * type may not be changed.
*/
enum ovs_action_attr {
OVS_ACTION_ATTR_UNSPEC,
OVS_ACTION_ATTR_OUTPUT, /* u32 port number. */
OVS_ACTION_ATTR_USERSPACE, /* Nested OVS_USERSPACE_ATTR_*. */
- OVS_ACTION_ATTR_PUSH, /* One nested OVS_KEY_ATTR_*. */
- OVS_ACTION_ATTR_POP, /* u16 OVS_KEY_ATTR_*. */
OVS_ACTION_ATTR_SET, /* One nested OVS_KEY_ATTR_*. */
+ OVS_ACTION_ATTR_PUSH_VLAN, /* struct ovs_action_push_vlan. */
+ OVS_ACTION_ATTR_POP_VLAN, /* No argument. */
OVS_ACTION_ATTR_SAMPLE, /* Nested OVS_SAMPLE_ATTR_*. */
__OVS_ACTION_ATTR_MAX
};
break;
case OVS_KEY_ATTR_UNSPEC:
+ case OVS_KEY_ATTR_ENCAP:
case OVS_KEY_ATTR_ETHERTYPE:
case OVS_KEY_ATTR_IPV6:
case OVS_KEY_ATTR_IN_PORT:
- case OVS_KEY_ATTR_8021Q:
+ case OVS_KEY_ATTR_VLAN:
case OVS_KEY_ATTR_ICMP:
case OVS_KEY_ATTR_ICMPV6:
case OVS_KEY_ATTR_ARP:
unsigned int left;
NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
- const struct nlattr *nested;
- const struct ovs_key_8021q *q_key;
+ const struct ovs_action_push_vlan *vlan;
int type = nl_attr_type(a);
switch ((enum ovs_action_attr) type) {
dp_netdev_action_userspace(dp, packet, key, a);
break;
- case OVS_ACTION_ATTR_PUSH:
- nested = nl_attr_get(a);
- assert(nl_attr_type(nested) == OVS_KEY_ATTR_8021Q);
- q_key = nl_attr_get_unspec(nested, sizeof(*q_key));
- eth_push_vlan(packet, q_key->q_tci);
+ case OVS_ACTION_ATTR_PUSH_VLAN:
+ vlan = nl_attr_get(a);
+ eth_push_vlan(packet, vlan->vlan_tci);
break;
- case OVS_ACTION_ATTR_POP:
- assert(nl_attr_get_u16(a) == OVS_KEY_ATTR_8021Q);
+ case OVS_ACTION_ATTR_POP_VLAN:
dp_netdev_pop_vlan(packet);
break;
}
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);
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;
ds_put_cstr(ds, ovs_key_attr_to_string(attr));
- if (nl_attr_get_size(a) != odp_flow_key_attr_len(nl_attr_type(a))) {
+ 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)));
}
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, "(%"PRIu32")", nl_attr_get_u32(a));
break;
ETH_ADDR_ARGS(eth_key->eth_dst));
break;
- case OVS_KEY_ATTR_8021Q:
- q_key = nl_attr_get(a);
- ds_put_cstr(ds, "(");
- 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:
}
{
- 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;
}
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
return true;
}
-/* 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)
+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 *attrs[OVS_KEY_ATTR_MAX + 1];
const struct nlattr *nla;
- uint64_t expected_attrs;
uint64_t present_attrs;
- uint64_t missing_attrs;
- uint64_t extra_attrs;
size_t left;
- memset(flow, 0, sizeof *flow);
-
- memset(attrs, 0, sizeof attrs);
-
present_attrs = 0;
- expected_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) {
+ if (len != expected_len && expected_len != -2) {
if (expected_len == -1) {
VLOG_ERR_RL(&rl, "unknown attribute %"PRIu16" in flow key",
type);
return EINVAL;
}
- if (attrs[OVS_KEY_ATTR_PRIORITY]) {
+ *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)
+{
+ 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);
+
+ error = parse_flow_nlattrs(key, key_len, attrs, &present_attrs);
+ if (error) {
+ return error;
+ }
+
+ 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 (attrs[OVS_KEY_ATTR_TUN_ID]) {
+ 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 (attrs[OVS_KEY_ATTR_IN_PORT]) {
+ 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",
flow->in_port = OFPP_NONE;
}
- if (attrs[OVS_KEY_ATTR_ETHERNET]) {
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ETHERNET)) {
const struct ovs_key_ethernet *eth_key;
eth_key = nl_attr_get(attrs[OVS_KEY_ATTR_ETHERNET]);
}
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERNET;
- if (attrs[OVS_KEY_ATTR_8021Q]) {
- const struct ovs_key_8021q *q_key;
-
- q_key = nl_attr_get(attrs[OVS_KEY_ATTR_8021Q]);
- if (q_key->q_tpid != htons(ETH_TYPE_VLAN)) {
- VLOG_ERR_RL(&rl, "unsupported 802.1Q TPID %"PRIu16" in flow key",
- ntohs(q_key->q_tpid));
+ 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;
}
- if (q_key->q_tci & htons(VLAN_CFI)) {
- VLOG_ERR_RL(&rl, "invalid 802.1Q TCI %"PRIu16" in flow key",
- ntohs(q_key->q_tci));
- return EINVAL;
+ flow->vlan_tci |= htons(VLAN_CFI);
+
+ error = parse_flow_nlattrs(nl_attr_get(encap), nl_attr_get_size(encap),
+ attrs, &present_attrs);
+ if (error) {
+ return error;
}
- flow->vlan_tci = q_key->q_tci | htons(VLAN_CFI);
- expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_8021Q;
+ expected_attrs = 0;
}
- if (attrs[OVS_KEY_ATTR_ETHERTYPE]) {
+ 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",
if (flow->dl_type == htons(ETH_TYPE_IP)) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV4;
- if (attrs[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]);
}
} else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV6;
- if (attrs[OVS_KEY_ATTR_IPV6]) {
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV6)) {
const struct ovs_key_ipv6 *ipv6_key;
ipv6_key = nl_attr_get(attrs[OVS_KEY_ATTR_IPV6]);
}
} else if (flow->dl_type == htons(ETH_TYPE_ARP)) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ARP;
- if (attrs[OVS_KEY_ATTR_ARP]) {
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ARP)) {
const struct ovs_key_arp *arp_key;
arp_key = nl_attr_get(attrs[OVS_KEY_ATTR_ARP]);
flow->dl_type == htons(ETH_TYPE_IPV6))
&& !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TCP;
- if (attrs[OVS_KEY_ATTR_TCP]) {
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TCP)) {
const struct ovs_key_tcp *tcp_key;
tcp_key = nl_attr_get(attrs[OVS_KEY_ATTR_TCP]);
flow->dl_type == htons(ETH_TYPE_IPV6))
&& !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_UDP;
- if (attrs[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->dl_type == htons(ETH_TYPE_IP)
&& !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ICMP;
- if (attrs[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->dl_type == htons(ETH_TYPE_IPV6)
&& !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ICMPV6;
- if (attrs[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]);
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 (attrs[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]);
}
}
- 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;
+ return check_expectations(present_attrs, expected_attrs, key, key_len);
}
* hash bucket.) */
vlan_tci = facet->flow.vlan_tci;
NL_ATTR_FOR_EACH_UNSAFE (a, left, facet->actions, facet->actions_len) {
+ const struct ovs_action_push_vlan *vlan;
struct ofport_dpif *port;
switch (nl_attr_type(a)) {
- const struct nlattr *nested;
case OVS_ACTION_ATTR_OUTPUT:
port = get_odp_port(ofproto, nl_attr_get_u32(a));
if (port && port->bundle && port->bundle->bond) {
}
break;
- case OVS_ACTION_ATTR_POP:
- if (nl_attr_get_u16(a) == OVS_KEY_ATTR_8021Q) {
- vlan_tci = htons(0);
- }
+ case OVS_ACTION_ATTR_POP_VLAN:
+ vlan_tci = htons(0);
break;
- case OVS_ACTION_ATTR_PUSH:
- nested = nl_attr_get(a);
- if (nl_attr_type(nested) == OVS_KEY_ATTR_8021Q) {
- const struct ovs_key_8021q *q_key;
-
- q_key = nl_attr_get_unspec(nested, sizeof(*q_key));
- vlan_tci = q_key->q_tci;
- }
+ case OVS_ACTION_ATTR_PUSH_VLAN:
+ vlan = nl_attr_get(a);
+ vlan_tci = vlan->vlan_tci;
break;
}
}
}
static void
-commit_action__(struct ofpbuf *odp_actions,
- enum ovs_action_attr act_type,
- enum ovs_key_attr key_type,
- const void *key, size_t key_size)
+commit_set_action(struct ofpbuf *odp_actions, enum ovs_key_attr key_type,
+ const void *key, size_t key_size)
{
- size_t offset = nl_msg_start_nested(odp_actions, act_type);
-
+ size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET);
nl_msg_put_unspec(odp_actions, key_type, key, key_size);
nl_msg_end_nested(odp_actions, offset);
}
}
base->tun_id = flow->tun_id;
- commit_action__(odp_actions, OVS_ACTION_ATTR_SET,
- OVS_KEY_ATTR_TUN_ID, &base->tun_id, sizeof(base->tun_id));
+ commit_set_action(odp_actions, OVS_KEY_ATTR_TUN_ID,
+ &base->tun_id, sizeof(base->tun_id));
}
static void
memcpy(eth_key.eth_src, base->dl_src, ETH_ADDR_LEN);
memcpy(eth_key.eth_dst, base->dl_dst, ETH_ADDR_LEN);
- commit_action__(odp_actions, OVS_ACTION_ATTR_SET,
- OVS_KEY_ATTR_ETHERNET, ð_key, sizeof(eth_key));
+ commit_set_action(odp_actions, OVS_KEY_ATTR_ETHERNET,
+ ð_key, sizeof(eth_key));
}
static void
}
if (base->vlan_tci & htons(VLAN_CFI)) {
- nl_msg_put_u16(ctx->odp_actions, OVS_ACTION_ATTR_POP,
- OVS_KEY_ATTR_8021Q);
+ nl_msg_put_flag(ctx->odp_actions, OVS_ACTION_ATTR_POP_VLAN);
}
if (new_tci & htons(VLAN_CFI)) {
- struct ovs_key_8021q q_key;
-
- q_key.q_tpid = htons(ETH_TYPE_VLAN);
- q_key.q_tci = new_tci & ~htons(VLAN_CFI);
+ struct ovs_action_push_vlan vlan;
- commit_action__(ctx->odp_actions, OVS_ACTION_ATTR_PUSH,
- OVS_KEY_ATTR_8021Q, &q_key, sizeof(q_key));
+ vlan.vlan_tpid = htons(ETH_TYPE_VLAN);
+ vlan.vlan_tci = new_tci & ~htons(VLAN_CFI);
+ nl_msg_put_unspec(ctx->odp_actions, OVS_ACTION_ATTR_PUSH_VLAN,
+ &vlan, sizeof vlan);
}
base->vlan_tci = new_tci;
}
: base->nw_frag == FLOW_NW_FRAG_ANY
? OVS_FRAG_TYPE_FIRST : OVS_FRAG_TYPE_LATER);
- commit_action__(odp_actions, OVS_ACTION_ATTR_SET,
- OVS_KEY_ATTR_IPV4, &ipv4_key, sizeof(ipv4_key));
+ commit_set_action(odp_actions, OVS_KEY_ATTR_IPV4,
+ &ipv4_key, sizeof(ipv4_key));
}
static void
port_key.tcp_src = base->tp_src = flow->tp_src;
port_key.tcp_dst = base->tp_dst = flow->tp_dst;
- commit_action__(odp_actions, OVS_ACTION_ATTR_SET,
- OVS_KEY_ATTR_TCP, &port_key, sizeof(port_key));
+ commit_set_action(odp_actions, OVS_KEY_ATTR_TCP,
+ &port_key, sizeof(port_key));
} else if (flow->nw_proto == IPPROTO_UDP) {
struct ovs_key_udp port_key;
port_key.udp_src = base->tp_src = flow->tp_src;
port_key.udp_dst = base->tp_dst = flow->tp_dst;
- commit_action__(odp_actions, OVS_ACTION_ATTR_SET,
- OVS_KEY_ATTR_UDP, &port_key, sizeof(port_key));
+ commit_set_action(odp_actions, OVS_KEY_ATTR_UDP,
+ &port_key, sizeof(port_key));
}
}
}
base->priority = flow->priority;
- commit_action__(odp_actions, OVS_ACTION_ATTR_SET,
- OVS_KEY_ATTR_PRIORITY, &base->priority,
- sizeof(base->priority));
+ commit_set_action(odp_actions, OVS_KEY_ATTR_PRIORITY,
+ &base->priority, sizeof(base->priority));
}
static void
echo
echo '# Valid forms with VLAN header.'
- sed 's/eth([[^)]]*)/&,vlan(vid=99,pcp=7)/' odp-base.txt
+ sed 's/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/
+s/$/)/' odp-base.txt
echo
echo '# Valid forms with QoS priority.'
echo
echo '# Valid forms with tun_id and VLAN headers.'
sed 's/^/tun_id(0xfedcba9876543210),/
-s/eth([[^)]]*)/&,vlan(vid=99,pcp=7)/' odp-base.txt
+s/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/
+s/$/)/' odp-base.txt
echo
echo '# Valid forms with IP first fragment.'
"br0 none 0 drop" \
"br0 0 0 drop" \
"br0 0 1 drop" \
- "br0 10 0 p1,p5,p6,p7,p8,pop(vlan),p2" \
- "br0 10 1 p1,p5,p6,p7,p8,pop(vlan),push(vlan(vid=0,pcp=1)),p2" \
+ "br0 10 0 p1,p5,p6,p7,p8,pop_vlan,p2" \
+ "br0 10 1 p1,p5,p6,p7,p8,pop_vlan,push_vlan(vid=0,pcp=1),p2" \
"br0 11 0 p5,p7" \
"br0 11 1 p5,p7" \
- "br0 12 0 p1,p5,p6,pop(vlan),p3,p4,p7,p8" \
- "br0 12 1 p1,p5,p6,pop(vlan),push(vlan(vid=0,pcp=1)),p3,p4,p7,p8" \
+ "br0 12 0 p1,p5,p6,pop_vlan,p3,p4,p7,p8" \
+ "br0 12 1 p1,p5,p6,pop_vlan,push_vlan(vid=0,pcp=1),p3,p4,p7,p8" \
"p1 none 0 drop" \
"p1 0 0 drop" \
"p1 0 1 drop" \
- "p1 10 0 br0,p5,p6,p7,p8,pop(vlan),p2" \
- "p1 10 1 br0,p5,p6,p7,p8,pop(vlan),push(vlan(vid=0,pcp=1)),p2" \
+ "p1 10 0 br0,p5,p6,p7,p8,pop_vlan,p2" \
+ "p1 10 1 br0,p5,p6,p7,p8,pop_vlan,push_vlan(vid=0,pcp=1),p2" \
"p1 11 0 drop" \
"p1 11 1 drop" \
- "p1 12 0 br0,p5,p6,pop(vlan),p3,p4,p7,p8" \
- "p1 12 1 br0,p5,p6,pop(vlan),push(vlan(vid=0,pcp=1)),p3,p4,p7,p8" \
- "p2 none 0 push(vlan(vid=10,pcp=0)),br0,p1,p5,p6,p7,p8" \
- "p2 0 0 pop(vlan),push(vlan(vid=10,pcp=0)),br0,p1,p5,p6,p7,p8" \
- "p2 0 1 pop(vlan),push(vlan(vid=10,pcp=1)),br0,p1,p5,p6,p7,p8" \
+ "p1 12 0 br0,p5,p6,pop_vlan,p3,p4,p7,p8" \
+ "p1 12 1 br0,p5,p6,pop_vlan,push_vlan(vid=0,pcp=1),p3,p4,p7,p8" \
+ "p2 none 0 push_vlan(vid=10,pcp=0),br0,p1,p5,p6,p7,p8" \
+ "p2 0 0 pop_vlan,push_vlan(vid=10,pcp=0),br0,p1,p5,p6,p7,p8" \
+ "p2 0 1 pop_vlan,push_vlan(vid=10,pcp=1),br0,p1,p5,p6,p7,p8" \
"p2 10 0 drop" \
"p2 10 1 drop" \
"p2 11 0 drop" \
"p2 11 1 drop" \
"p2 12 0 drop" \
"p2 12 1 drop" \
- "p3 none 0 p4,p7,p8,push(vlan(vid=12,pcp=0)),br0,p1,p5,p6" \
- "p3 0 0 p4,p7,p8,pop(vlan),push(vlan(vid=12,pcp=0)),br0,p1,p5,p6" \
- "p3 0 1 p4,p7,p8,pop(vlan),push(vlan(vid=12,pcp=1)),br0,p1,p5,p6" \
+ "p3 none 0 p4,p7,p8,push_vlan(vid=12,pcp=0),br0,p1,p5,p6" \
+ "p3 0 0 p4,p7,p8,pop_vlan,push_vlan(vid=12,pcp=0),br0,p1,p5,p6" \
+ "p3 0 1 p4,p7,p8,pop_vlan,push_vlan(vid=12,pcp=1),br0,p1,p5,p6" \
"p3 10 0 drop" \
"p3 10 1 drop" \
"p3 11 0 drop" \
"p3 11 1 drop" \
"p3 12 0 drop" \
"p3 12 1 drop" \
- "p4 none 0 p3,p7,p8,push(vlan(vid=12,pcp=0)),br0,p1,p5,p6" \
- "p4 0 0 p3,p7,p8,pop(vlan),push(vlan(vid=12,pcp=0)),br0,p1,p5,p6" \
- "p4 0 1 p3,p7,p8,pop(vlan),push(vlan(vid=12,pcp=1)),br0,p1,p5,p6" \
+ "p4 none 0 p3,p7,p8,push_vlan(vid=12,pcp=0),br0,p1,p5,p6" \
+ "p4 0 0 p3,p7,p8,pop_vlan,push_vlan(vid=12,pcp=0),br0,p1,p5,p6" \
+ "p4 0 1 p3,p7,p8,pop_vlan,push_vlan(vid=12,pcp=1),br0,p1,p5,p6" \
"p4 10 0 drop" \
"p4 10 1 drop" \
"p4 11 0 drop" \
"p4 11 1 drop" \
"p4 12 0 drop" \
"p4 12 1 drop" \
- "p5 none 0 p2,push(vlan(vid=10,pcp=0)),br0,p1,p6,p7,p8" \
- "p5 0 0 p2,pop(vlan),push(vlan(vid=10,pcp=0)),br0,p1,p6,p7,p8" \
- "p5 0 1 p2,pop(vlan),push(vlan(vid=10,pcp=1)),br0,p1,p6,p7,p8" \
- "p5 10 0 br0,p1,p6,p7,p8,pop(vlan),p2" \
- "p5 10 1 br0,p1,p6,p7,p8,pop(vlan),push(vlan(vid=0,pcp=1)),p2" \
+ "p5 none 0 p2,push_vlan(vid=10,pcp=0),br0,p1,p6,p7,p8" \
+ "p5 0 0 p2,pop_vlan,push_vlan(vid=10,pcp=0),br0,p1,p6,p7,p8" \
+ "p5 0 1 p2,pop_vlan,push_vlan(vid=10,pcp=1),br0,p1,p6,p7,p8" \
+ "p5 10 0 br0,p1,p6,p7,p8,pop_vlan,p2" \
+ "p5 10 1 br0,p1,p6,p7,p8,pop_vlan,push_vlan(vid=0,pcp=1),p2" \
"p5 11 0 br0,p7" \
"p5 11 1 br0,p7" \
- "p5 12 0 br0,p1,p6,pop(vlan),p3,p4,p7,p8" \
- "p5 12 1 br0,p1,p6,pop(vlan),push(vlan(vid=0,pcp=1)),p3,p4,p7,p8" \
- "p6 none 0 p2,push(vlan(vid=10,pcp=0)),br0,p1,p5,p7,p8" \
- "p6 0 0 p2,pop(vlan),push(vlan(vid=10,pcp=0)),br0,p1,p5,p7,p8" \
- "p6 0 1 p2,pop(vlan),push(vlan(vid=10,pcp=1)),br0,p1,p5,p7,p8" \
- "p6 10 0 br0,p1,p5,p7,p8,pop(vlan),p2" \
- "p6 10 1 br0,p1,p5,p7,p8,pop(vlan),push(vlan(vid=0,pcp=1)),p2" \
+ "p5 12 0 br0,p1,p6,pop_vlan,p3,p4,p7,p8" \
+ "p5 12 1 br0,p1,p6,pop_vlan,push_vlan(vid=0,pcp=1),p3,p4,p7,p8" \
+ "p6 none 0 p2,push_vlan(vid=10,pcp=0),br0,p1,p5,p7,p8" \
+ "p6 0 0 p2,pop_vlan,push_vlan(vid=10,pcp=0),br0,p1,p5,p7,p8" \
+ "p6 0 1 p2,pop_vlan,push_vlan(vid=10,pcp=1),br0,p1,p5,p7,p8" \
+ "p6 10 0 br0,p1,p5,p7,p8,pop_vlan,p2" \
+ "p6 10 1 br0,p1,p5,p7,p8,pop_vlan,push_vlan(vid=0,pcp=1),p2" \
"p6 11 0 drop" \
"p6 11 1 drop" \
- "p6 12 0 br0,p1,p5,pop(vlan),p3,p4,p7,p8" \
- "p6 12 1 br0,p1,p5,pop(vlan),push(vlan(vid=0,pcp=1)),p3,p4,p7,p8" \
- "p7 none 0 p3,p4,p8,push(vlan(vid=12,pcp=0)),br0,p1,p5,p6" \
- "p7 0 0 p3,p4,p8,pop(vlan),push(vlan(vid=12,pcp=0)),br0,p1,p5,p6" \
- "p7 0 1 p3,p4,p8,pop(vlan),push(vlan(vid=12,pcp=1)),br0,p1,p5,p6" \
- "p7 10 0 br0,p1,p5,p6,p8,pop(vlan),p2" \
- "p7 10 1 br0,p1,p5,p6,p8,pop(vlan),push(vlan(vid=0,pcp=1)),p2" \
+ "p6 12 0 br0,p1,p5,pop_vlan,p3,p4,p7,p8" \
+ "p6 12 1 br0,p1,p5,pop_vlan,push_vlan(vid=0,pcp=1),p3,p4,p7,p8" \
+ "p7 none 0 p3,p4,p8,push_vlan(vid=12,pcp=0),br0,p1,p5,p6" \
+ "p7 0 0 p3,p4,p8,pop_vlan,push_vlan(vid=12,pcp=0),br0,p1,p5,p6" \
+ "p7 0 1 p3,p4,p8,pop_vlan,push_vlan(vid=12,pcp=1),br0,p1,p5,p6" \
+ "p7 10 0 br0,p1,p5,p6,p8,pop_vlan,p2" \
+ "p7 10 1 br0,p1,p5,p6,p8,pop_vlan,push_vlan(vid=0,pcp=1),p2" \
"p7 11 0 br0,p5" \
"p7 11 1 br0,p5" \
- "p7 12 0 br0,p1,p5,p6,pop(vlan),p3,p4,p8" \
- "p7 12 1 br0,p1,p5,p6,pop(vlan),push(vlan(vid=0,pcp=1)),p3,p4,p8" \
- "p8 none 0 p3,p4,p7,push(vlan(vid=12,pcp=0)),br0,p1,p5,p6" \
- "p8 0 0 p3,p4,p7,pop(vlan),push(vlan(vid=12,pcp=0)),br0,p1,p5,p6" \
- "p8 0 1 p3,p4,p7,pop(vlan),push(vlan(vid=12,pcp=1)),br0,p1,p5,p6" \
- "p8 10 0 br0,p1,p5,p6,p7,pop(vlan),p2" \
- "p8 10 1 br0,p1,p5,p6,p7,pop(vlan),push(vlan(vid=0,pcp=1)),p2" \
+ "p7 12 0 br0,p1,p5,p6,pop_vlan,p3,p4,p8" \
+ "p7 12 1 br0,p1,p5,p6,pop_vlan,push_vlan(vid=0,pcp=1),p3,p4,p8" \
+ "p8 none 0 p3,p4,p7,push_vlan(vid=12,pcp=0),br0,p1,p5,p6" \
+ "p8 0 0 p3,p4,p7,pop_vlan,push_vlan(vid=12,pcp=0),br0,p1,p5,p6" \
+ "p8 0 1 p3,p4,p7,pop_vlan,push_vlan(vid=12,pcp=1),br0,p1,p5,p6" \
+ "p8 10 0 br0,p1,p5,p6,p7,pop_vlan,p2" \
+ "p8 10 1 br0,p1,p5,p6,p7,pop_vlan,push_vlan(vid=0,pcp=1),p2" \
"p8 11 0 drop" \
"p8 11 1 drop" \
- "p8 12 0 br0,p1,p5,p6,pop(vlan),p3,p4,p7" \
- "p8 12 1 br0,p1,p5,p6,pop(vlan),push(vlan(vid=0,pcp=1)),p3,p4,p7"
+ "p8 12 0 br0,p1,p5,p6,pop_vlan,p3,p4,p7" \
+ "p8 12 1 br0,p1,p5,p6,pop_vlan,push_vlan(vid=0,pcp=1),p3,p4,p7"
do
set $tuple
in_port=$1
if test $vlan = none; then
flow="in_port($n_in_port),eth(src=50:54:00:00:00:01,dst=ff:ff:ff:ff:ff:ff),eth_type(0xabcd)"
else
- flow="in_port($n_in_port),eth(src=50:54:00:00:00:01,dst=ff:ff:ff:ff:ff:ff),vlan(vid=$vlan,pcp=$pcp),eth_type(0xabcd)"
+ flow="in_port($n_in_port),eth(src=50:54:00:00:00:01,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=$vlan,pcp=$pcp),encap(eth_type(0xabcd))"
fi
AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout])