diff options
| author | Ben Pfaff <blp@nicira.com> | 2011-06-01 13:39:51 -0700 |
|---|---|---|
| committer | Ben Pfaff <blp@nicira.com> | 2011-06-01 13:39:51 -0700 |
| commit | 80e5eed9c2128f04a1d7da134120d96e961dbe10 (patch) | |
| tree | a7c0b2b2c47bec4277faa5b3b140b93945161374 /datapath | |
| parent | b85145b59fab7efa236cdabd6ef0bda87f373d19 (diff) | |
| download | openvswitch-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.c | 10 | ||||
| -rw-r--r-- | datapath/flow.c | 89 | ||||
| -rw-r--r-- | datapath/flow.h | 2 |
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) { |
