summaryrefslogtreecommitdiff
path: root/datapath
diff options
context:
space:
mode:
authorBen Pfaff <blp@nicira.com>2011-06-01 13:39:51 -0700
committerBen Pfaff <blp@nicira.com>2011-06-01 13:39:51 -0700
commit80e5eed9c2128f04a1d7da134120d96e961dbe10 (patch)
treea7c0b2b2c47bec4277faa5b3b140b93945161374 /datapath
parentb85145b59fab7efa236cdabd6ef0bda87f373d19 (diff)
downloadopenvswitch-80e5eed9c2128f04a1d7da134120d96e961dbe10.tar.gz
datapath: Get packet metadata from userspace in odp_packet_cmd_execute().
Until now, the tun_id and in_port have been lost when a packet is sent from the kernel to userspace and then back to the kernel. I didn't think that this was a problem, but recent behavior made me look closer and see that it makes a difference if sFlow is turned on or if an ODP_ATTR_ACTION_CONTROLLER action is present. We could possibly kluge around those, but for future-proofing it seems better to pass the packet metadata from userspace to the kernel. That is what this commit does. This commit introduces a user-kernel protocol break. We could avoid that, if it is desirable, by making ODP_PACKET_ATTR_KEY optional for ODP_PACKET_CMD_EXECUTE commands. Signed-off-by: Ben Pfaff <blp@nicira.com> Acked-by: Jesse Gross <jesse@nicira.com>
Diffstat (limited to 'datapath')
-rw-r--r--datapath/datapath.c10
-rw-r--r--datapath/flow.c89
-rw-r--r--datapath/flow.h2
3 files changed, 84 insertions, 17 deletions
diff --git a/datapath/datapath.c b/datapath/datapath.c
index 728012249..d607315ed 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -655,7 +655,8 @@ static int odp_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
int key_len;
err = -EINVAL;
- if (!a[ODP_PACKET_ATTR_PACKET] || !a[ODP_PACKET_ATTR_ACTIONS] ||
+ if (!a[ODP_PACKET_ATTR_PACKET] || !a[ODP_PACKET_ATTR_KEY] ||
+ !a[ODP_PACKET_ATTR_ACTIONS] ||
nla_len(a[ODP_PACKET_ATTR_PACKET]) < ETH_HLEN)
goto err;
@@ -694,6 +695,12 @@ static int odp_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
goto err_flow_put;
flow->tbl_node.hash = flow_hash(&flow->key, key_len);
+ err = flow_metadata_from_nlattrs(&flow->key.eth.in_port,
+ &flow->key.eth.tun_id,
+ a[ODP_PACKET_ATTR_KEY]);
+ if (err)
+ goto err_flow_put;
+
acts = flow_actions_alloc(a[ODP_PACKET_ATTR_ACTIONS]);
err = PTR_ERR(acts);
if (IS_ERR(acts))
@@ -725,6 +732,7 @@ err:
static const struct nla_policy packet_policy[ODP_PACKET_ATTR_MAX + 1] = {
[ODP_PACKET_ATTR_PACKET] = { .type = NLA_UNSPEC },
+ [ODP_PACKET_ATTR_KEY] = { .type = NLA_NESTED },
[ODP_PACKET_ATTR_ACTIONS] = { .type = NLA_NESTED },
};
diff --git a/datapath/flow.c b/datapath/flow.c
index 2b80c6d82..d181cdefb 100644
--- a/datapath/flow.c
+++ b/datapath/flow.c
@@ -597,6 +597,23 @@ int flow_cmp(const struct tbl_node *node, void *key2_, int len)
return !memcmp(key1, key2, len);
}
+/* The size of the argument for each %ODP_KEY_ATTR_* Netlink attribute. */
+static const u32 key_lens[ODP_KEY_ATTR_MAX + 1] = {
+ [ODP_KEY_ATTR_TUN_ID] = 8,
+ [ODP_KEY_ATTR_IN_PORT] = 4,
+ [ODP_KEY_ATTR_ETHERNET] = sizeof(struct odp_key_ethernet),
+ [ODP_KEY_ATTR_8021Q] = sizeof(struct odp_key_8021q),
+ [ODP_KEY_ATTR_ETHERTYPE] = 2,
+ [ODP_KEY_ATTR_IPV4] = sizeof(struct odp_key_ipv4),
+ [ODP_KEY_ATTR_IPV6] = sizeof(struct odp_key_ipv6),
+ [ODP_KEY_ATTR_TCP] = sizeof(struct odp_key_tcp),
+ [ODP_KEY_ATTR_UDP] = sizeof(struct odp_key_udp),
+ [ODP_KEY_ATTR_ICMP] = sizeof(struct odp_key_icmp),
+ [ODP_KEY_ATTR_ICMPV6] = sizeof(struct odp_key_icmpv6),
+ [ODP_KEY_ATTR_ARP] = sizeof(struct odp_key_arp),
+ [ODP_KEY_ATTR_ND] = sizeof(struct odp_key_nd),
+};
+
/**
* flow_from_nlattrs - parses Netlink attributes into a flow key.
* @swkey: receives the extracted flow key.
@@ -625,22 +642,6 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
prev_type = ODP_KEY_ATTR_UNSPEC;
nla_for_each_nested(nla, attr, rem) {
- static const u32 key_lens[ODP_KEY_ATTR_MAX + 1] = {
- [ODP_KEY_ATTR_TUN_ID] = 8,
- [ODP_KEY_ATTR_IN_PORT] = 4,
- [ODP_KEY_ATTR_ETHERNET] = sizeof(struct odp_key_ethernet),
- [ODP_KEY_ATTR_8021Q] = sizeof(struct odp_key_8021q),
- [ODP_KEY_ATTR_ETHERTYPE] = 2,
- [ODP_KEY_ATTR_IPV4] = sizeof(struct odp_key_ipv4),
- [ODP_KEY_ATTR_IPV6] = sizeof(struct odp_key_ipv6),
- [ODP_KEY_ATTR_TCP] = sizeof(struct odp_key_tcp),
- [ODP_KEY_ATTR_UDP] = sizeof(struct odp_key_udp),
- [ODP_KEY_ATTR_ICMP] = sizeof(struct odp_key_icmp),
- [ODP_KEY_ATTR_ICMPV6] = sizeof(struct odp_key_icmpv6),
- [ODP_KEY_ATTR_ARP] = sizeof(struct odp_key_arp),
- [ODP_KEY_ATTR_ND] = sizeof(struct odp_key_nd),
- };
-
const struct odp_key_ethernet *eth_key;
const struct odp_key_8021q *q_key;
const struct odp_key_ipv4 *ipv4_key;
@@ -868,6 +869,62 @@ ok:
return error;
}
+/**
+ * flow_metadata_from_nlattrs - parses Netlink attributes into a flow key.
+ * @in_port: receives the extracted input port.
+ * @tun_id: receives the extracted tunnel ID.
+ * @key: Netlink attribute holding nested %ODP_KEY_ATTR_* Netlink attribute
+ * sequence.
+ *
+ * This parses a series of Netlink attributes that form a flow key, which must
+ * take the same form accepted by flow_from_nlattrs(), but only enough of it to
+ * get the metadata, that is, the parts of the flow key that cannot be
+ * extracted from the packet itself.
+ */
+int flow_metadata_from_nlattrs(u16 *in_port, __be64 *tun_id,
+ const struct nlattr *attr)
+{
+ const struct nlattr *nla;
+ u16 prev_type;
+ int rem;
+
+ *tun_id = 0;
+
+ prev_type = ODP_KEY_ATTR_UNSPEC;
+ nla_for_each_nested(nla, attr, rem) {
+ int type = nla_type(nla);
+
+ if (type > ODP_KEY_ATTR_MAX || nla_len(nla) != key_lens[type])
+ return -EINVAL;
+
+ switch (TRANSITION(prev_type, type)) {
+ case TRANSITION(ODP_KEY_ATTR_UNSPEC, ODP_KEY_ATTR_TUN_ID):
+ *tun_id = nla_get_be64(nla);
+ break;
+
+ case TRANSITION(ODP_KEY_ATTR_UNSPEC, ODP_KEY_ATTR_IN_PORT):
+ case TRANSITION(ODP_KEY_ATTR_TUN_ID, ODP_KEY_ATTR_IN_PORT):
+ if (nla_get_u32(nla) >= DP_MAX_PORTS)
+ return -EINVAL;
+ *in_port = nla_get_u32(nla);
+ break;
+
+ default:
+ goto done;
+ }
+
+ prev_type = type;
+ }
+ if (rem)
+ return -EINVAL;
+
+done:
+ if (prev_type == ODP_KEY_ATTR_UNSPEC ||
+ prev_type == ODP_KEY_ATTR_TUN_ID)
+ return -EINVAL;
+ return 0;
+}
+
int flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
{
struct odp_key_ethernet *eth_key;
diff --git a/datapath/flow.h b/datapath/flow.h
index 1d75a0188..3cda5967c 100644
--- a/datapath/flow.h
+++ b/datapath/flow.h
@@ -152,6 +152,8 @@ int flow_cmp(const struct tbl_node *, void *target, int len);
int flow_to_nlattrs(const struct sw_flow_key *, struct sk_buff *);
int flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
const struct nlattr *);
+int flow_metadata_from_nlattrs(u16 *in_port, __be64 *tun_id,
+ const struct nlattr *);
static inline struct sw_flow *flow_cast(const struct tbl_node *node)
{