From 9b405f1aa8d175dc63ad3ffe5d0fe05d5ee09162 Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Fri, 18 Jan 2013 18:10:59 -0800 Subject: [PATCH] datapath: More flexible kernel/userspace tunneling attribute. Following patch breaks down single ipv4_tunnel netlink attribute into individual member attributes. It will help when we extend tunneling parameters in future. Signed-off-by: Pravin B Shelar Acked-by: Jesse Gross Bug #14611 --- datapath/datapath.c | 331 ++++++++++++++++++++++++++++++------ datapath/flow.c | 173 ++++++++++++++----- datapath/flow.h | 20 ++- datapath/tunnel.h | 1 - include/linux/openvswitch.h | 32 ++-- lib/dpif-netdev.c | 2 +- lib/odp-util.c | 242 +++++++++++++++----------- tests/odp.at | 11 +- 8 files changed, 591 insertions(+), 221 deletions(-) diff --git a/datapath/datapath.c b/datapath/datapath.c index b2854bc0f..b8e9d1889 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -423,16 +423,89 @@ static int flush_flows(struct datapath *dp) return 0; } -static int validate_actions(const struct nlattr *attr, - const struct sw_flow_key *key, int depth); +static struct nlattr *reserve_sfa_size(struct sw_flow_actions **sfa, int attr_len) +{ + + struct sw_flow_actions *acts; + int new_acts_size; + int req_size = NLA_ALIGN(attr_len); + int next_offset = offsetof(struct sw_flow_actions, actions) + + (*sfa)->actions_len; + + if (req_size <= (ksize(*sfa) - next_offset)) + goto out; + + new_acts_size = ksize(*sfa) * 2; + + if (new_acts_size > MAX_ACTIONS_BUFSIZE) { + if ((MAX_ACTIONS_BUFSIZE - next_offset) < req_size) + return ERR_PTR(-EMSGSIZE); + new_acts_size = MAX_ACTIONS_BUFSIZE; + } + + acts = ovs_flow_actions_alloc(new_acts_size); + if (IS_ERR(acts)) + return (void *)acts; -static int validate_sample(const struct nlattr *attr, - const struct sw_flow_key *key, int depth) + memcpy(acts->actions, (*sfa)->actions, (*sfa)->actions_len); + acts->actions_len = (*sfa)->actions_len; + kfree(*sfa); + *sfa = acts; + +out: + (*sfa)->actions_len += req_size; + return (struct nlattr *) ((unsigned char *)(*sfa) + next_offset); +} + +static int add_action(struct sw_flow_actions **sfa, int attrtype, void *data, int len) +{ + struct nlattr *a; + + a = reserve_sfa_size(sfa, nla_attr_size(len)); + if (IS_ERR(a)) + return PTR_ERR(a); + + a->nla_type = attrtype; + a->nla_len = nla_attr_size(len); + + if (data) + memcpy(nla_data(a), data, len); + memset((unsigned char *) a + a->nla_len, 0, nla_padlen(len)); + + return 0; +} + +static inline int add_nested_action_start(struct sw_flow_actions **sfa, int attrtype) +{ + int used = (*sfa)->actions_len; + int err; + + err = add_action(sfa, attrtype, NULL, 0); + if (err) + return err; + + return used; +} + +static inline void add_nested_action_end(struct sw_flow_actions *sfa, int st_offset) +{ + struct nlattr *a = (struct nlattr *) ((unsigned char *)sfa->actions + st_offset); + + a->nla_len = sfa->actions_len - st_offset; +} + +static int validate_and_copy_actions(const struct nlattr *attr, + const struct sw_flow_key *key, int depth, + struct sw_flow_actions **sfa); + +static int validate_and_copy_sample(const struct nlattr *attr, + const struct sw_flow_key *key, int depth, + struct sw_flow_actions **sfa) { const struct nlattr *attrs[OVS_SAMPLE_ATTR_MAX + 1]; const struct nlattr *probability, *actions; const struct nlattr *a; - int rem; + int rem, start, err, st_acts; memset(attrs, 0, sizeof(attrs)); nla_for_each_nested(a, attr, rem) { @@ -451,7 +524,26 @@ static int validate_sample(const struct nlattr *attr, actions = attrs[OVS_SAMPLE_ATTR_ACTIONS]; if (!actions || (nla_len(actions) && nla_len(actions) < NLA_HDRLEN)) return -EINVAL; - return validate_actions(actions, key, depth + 1); + + /* validation done, copy sample action. */ + start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SAMPLE); + if (start < 0) + return start; + err = add_action(sfa, OVS_SAMPLE_ATTR_PROBABILITY, nla_data(probability), sizeof(u32)); + if (err) + return err; + st_acts = add_nested_action_start(sfa, OVS_SAMPLE_ATTR_ACTIONS); + if (st_acts < 0) + return st_acts; + + err = validate_and_copy_actions(actions, key, depth + 1, sfa); + if (err) + return err; + + add_nested_action_end(*sfa, st_acts); + add_nested_action_end(*sfa, start); + + return 0; } static int validate_tp_port(const struct sw_flow_key *flow_key) @@ -467,8 +559,30 @@ static int validate_tp_port(const struct sw_flow_key *flow_key) return -EINVAL; } +static int validate_and_copy_set_tun(const struct nlattr *attr, + struct sw_flow_actions **sfa) +{ + struct ovs_key_ipv4_tunnel tun_key; + int err, start; + + err = ipv4_tun_from_nlattr(nla_data(attr), &tun_key); + if (err) + return err; + + start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SET); + if (start < 0) + return start; + + err = add_action(sfa, OVS_KEY_ATTR_IPV4_TUNNEL, &tun_key, sizeof(tun_key)); + add_nested_action_end(*sfa, start); + + return err; +} + static int validate_set(const struct nlattr *a, - const struct sw_flow_key *flow_key) + const struct sw_flow_key *flow_key, + struct sw_flow_actions **sfa, + bool *set_tun) { const struct nlattr *ovs_key = nla_data(a); int key_type = nla_type(ovs_key); @@ -478,13 +592,14 @@ static int validate_set(const struct nlattr *a, return -EINVAL; if (key_type > OVS_KEY_ATTR_MAX || - nla_len(ovs_key) != ovs_key_lens[key_type]) + (ovs_key_lens[key_type] != nla_len(ovs_key) && + ovs_key_lens[key_type] != -1)) return -EINVAL; switch (key_type) { const struct ovs_key_ipv4 *ipv4_key; - const struct ovs_key_ipv4_tunnel *tun_key; const struct ovs_key_ipv6 *ipv6_key; + int err; case OVS_KEY_ATTR_PRIORITY: case OVS_KEY_ATTR_TUN_ID: @@ -498,10 +613,11 @@ static int validate_set(const struct nlattr *a, #endif break; - case OVS_KEY_ATTR_IPV4_TUNNEL: - tun_key = nla_data(ovs_key); - if (!tun_key->ipv4_dst) - return -EINVAL; + case OVS_KEY_ATTR_TUNNEL: + *set_tun = true; + err = validate_and_copy_set_tun(a, sfa); + if (err) + return err; break; case OVS_KEY_ATTR_IPV4: @@ -579,8 +695,24 @@ static int validate_userspace(const struct nlattr *attr) return 0; } -static int validate_actions(const struct nlattr *attr, - const struct sw_flow_key *key, int depth) +static int copy_action(const struct nlattr *from, + struct sw_flow_actions **sfa) +{ + int totlen = NLA_ALIGN(from->nla_len); + struct nlattr *to; + + to = reserve_sfa_size(sfa, from->nla_len); + if (IS_ERR(to)) + return PTR_ERR(to); + + memcpy(to, from, totlen); + return 0; +} + +static int validate_and_copy_actions(const struct nlattr *attr, + const struct sw_flow_key *key, + int depth, + struct sw_flow_actions **sfa) { const struct nlattr *a; int rem, err; @@ -600,12 +732,14 @@ static int validate_actions(const struct nlattr *attr, }; const struct ovs_action_push_vlan *vlan; int type = nla_type(a); + bool skip_copy; if (type > OVS_ACTION_ATTR_MAX || (action_lens[type] != nla_len(a) && action_lens[type] != (u32)-1)) return -EINVAL; + skip_copy = false; switch (type) { case OVS_ACTION_ATTR_UNSPEC: return -EINVAL; @@ -634,20 +768,26 @@ static int validate_actions(const struct nlattr *attr, break; case OVS_ACTION_ATTR_SET: - err = validate_set(a, key); + err = validate_set(a, key, sfa, &skip_copy); if (err) return err; break; case OVS_ACTION_ATTR_SAMPLE: - err = validate_sample(a, key, depth); + err = validate_and_copy_sample(a, key, depth, sfa); if (err) return err; + skip_copy = true; break; default: return -EINVAL; } + if (!skip_copy) { + err = copy_action(a, sfa); + if (err) + return err; + } } if (rem > 0) @@ -716,16 +856,15 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) err = ovs_flow_metadata_from_nlattrs(flow, key_len, a[OVS_PACKET_ATTR_KEY]); if (err) goto err_flow_free; - - err = validate_actions(a[OVS_PACKET_ATTR_ACTIONS], &flow->key, 0); - if (err) - goto err_flow_free; - - acts = ovs_flow_actions_alloc(a[OVS_PACKET_ATTR_ACTIONS]); + acts = ovs_flow_actions_alloc(nla_len(a[OVS_PACKET_ATTR_ACTIONS])); err = PTR_ERR(acts); if (IS_ERR(acts)) goto err_flow_free; + + err = validate_and_copy_actions(a[OVS_PACKET_ATTR_ACTIONS], &flow->key, 0, &acts); rcu_assign_pointer(flow->sf_acts, acts); + if (err) + goto err_flow_free; OVS_CB(packet)->flow = flow; packet->priority = flow->key.phy.priority; @@ -814,6 +953,99 @@ static struct genl_multicast_group ovs_dp_flow_multicast_group = { .name = OVS_FLOW_MCGROUP }; +static int actions_to_attr(const struct nlattr *attr, int len, struct sk_buff *skb); +static int sample_action_to_attr(const struct nlattr *attr, struct sk_buff *skb) +{ + const struct nlattr *a; + struct nlattr *start; + int err = 0, rem; + + start = nla_nest_start(skb, OVS_ACTION_ATTR_SAMPLE); + if (!start) + return -EMSGSIZE; + + nla_for_each_nested(a, attr, rem) { + int type = nla_type(a); + struct nlattr *st_sample; + + switch (type) { + case OVS_SAMPLE_ATTR_PROBABILITY: + if (nla_put(skb, OVS_SAMPLE_ATTR_PROBABILITY, sizeof(u32), nla_data(a))) + return -EMSGSIZE; + break; + case OVS_SAMPLE_ATTR_ACTIONS: + st_sample = nla_nest_start(skb, OVS_SAMPLE_ATTR_ACTIONS); + if (!st_sample) + return -EMSGSIZE; + err = actions_to_attr(nla_data(a), nla_len(a), skb); + if (err) + return err; + nla_nest_end(skb, st_sample); + break; + } + } + + nla_nest_end(skb, start); + return err; +} + +static int set_action_to_attr(const struct nlattr *a, struct sk_buff *skb) +{ + const struct nlattr *ovs_key = nla_data(a); + int key_type = nla_type(ovs_key); + struct nlattr *start; + int err; + + switch (key_type) { + case OVS_KEY_ATTR_IPV4_TUNNEL: + start = nla_nest_start(skb, OVS_ACTION_ATTR_SET); + if (!start) + return -EMSGSIZE; + + err = ipv4_tun_to_nlattr(skb, nla_data(ovs_key)); + if (err) + return err; + nla_nest_end(skb, start); + break; + default: + if (nla_put(skb, OVS_ACTION_ATTR_SET, nla_len(a), ovs_key)) + return -EMSGSIZE; + break; + } + + return 0; +} + +static int actions_to_attr(const struct nlattr *attr, int len, struct sk_buff *skb) +{ + const struct nlattr *a; + int rem, err; + + nla_for_each_attr(a, attr, len, rem) { + int type = nla_type(a); + + switch (type) { + case OVS_ACTION_ATTR_SET: + err = set_action_to_attr(a, skb); + if (err) + return err; + break; + + case OVS_ACTION_ATTR_SAMPLE: + err = sample_action_to_attr(a, skb); + if (err) + return err; + break; + default: + if (nla_put(skb, type, nla_len(a), nla_data(a))) + return -EMSGSIZE; + break; + } + } + + return 0; +} + /* Called with genl_lock. */ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp, struct sk_buff *skb, u32 portid, @@ -821,6 +1053,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp, { const int skb_orig_len = skb->len; const struct sw_flow_actions *sf_acts; + struct nlattr *start; struct ovs_flow_stats stats; struct ovs_header *ovs_header; struct nlattr *nla; @@ -875,10 +1108,11 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp, * This can only fail for dump operations because the skb is always * properly sized for single flows. */ - err = nla_put(skb, OVS_FLOW_ATTR_ACTIONS, sf_acts->actions_len, - sf_acts->actions); + start = nla_nest_start(skb, OVS_FLOW_ATTR_ACTIONS); + err = actions_to_attr(sf_acts->actions, sf_acts->actions_len, skb); if (err < 0 && skb_orig_len) goto error; + nla_nest_end(skb, start); return genlmsg_end(skb, ovs_header); @@ -938,6 +1172,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) struct sk_buff *reply; struct datapath *dp; struct flow_table *table; + struct sw_flow_actions *acts = NULL; int error; int key_len; @@ -951,9 +1186,14 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) /* Validate actions. */ if (a[OVS_FLOW_ATTR_ACTIONS]) { - error = validate_actions(a[OVS_FLOW_ATTR_ACTIONS], &key, 0); - if (error) + acts = ovs_flow_actions_alloc(nla_len(a[OVS_FLOW_ATTR_ACTIONS])); + error = PTR_ERR(acts); + if (IS_ERR(acts)) goto error; + + error = validate_and_copy_actions(a[OVS_FLOW_ATTR_ACTIONS], &key, 0, &acts); + if (error) + goto err_kfree; } else if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW) { error = -EINVAL; goto error; @@ -962,17 +1202,15 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); error = -ENODEV; if (!dp) - goto error; + goto err_kfree; table = genl_dereference(dp->table); flow = ovs_flow_tbl_lookup(table, &key, key_len); if (!flow) { - struct sw_flow_actions *acts; - /* Bail out if we're not allowed to create a new flow. */ error = -ENOENT; if (info->genlhdr->cmd == OVS_FLOW_CMD_SET) - goto error; + goto err_kfree; /* Expand table, if necessary, to make room. */ if (ovs_flow_tbl_need_to_expand(table)) { @@ -990,15 +1228,10 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) flow = ovs_flow_alloc(); if (IS_ERR(flow)) { error = PTR_ERR(flow); - goto error; + goto err_kfree; } clear_stats(flow); - /* Obtain actions. */ - acts = ovs_flow_actions_alloc(a[OVS_FLOW_ATTR_ACTIONS]); - error = PTR_ERR(acts); - if (IS_ERR(acts)) - goto error_free_flow; rcu_assign_pointer(flow->sf_acts, acts); /* Put flow in bucket. */ @@ -1010,7 +1243,6 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) } else { /* We found a matching flow. */ struct sw_flow_actions *old_acts; - struct nlattr *acts_attrs; /* Bail out if we're not allowed to modify an existing flow. * We accept NLM_F_CREATE in place of the intended NLM_F_EXCL @@ -1021,26 +1253,13 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) error = -EEXIST; if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW && info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) - goto error; + goto err_kfree; /* Update actions. */ old_acts = rcu_dereference_protected(flow->sf_acts, lockdep_genl_is_held()); - acts_attrs = a[OVS_FLOW_ATTR_ACTIONS]; - if (acts_attrs && - (old_acts->actions_len != nla_len(acts_attrs) || - memcmp(old_acts->actions, nla_data(acts_attrs), - old_acts->actions_len))) { - struct sw_flow_actions *new_acts; - - new_acts = ovs_flow_actions_alloc(acts_attrs); - error = PTR_ERR(new_acts); - if (IS_ERR(new_acts)) - goto error; - - rcu_assign_pointer(flow->sf_acts, new_acts); - ovs_flow_deferred_free_acts(old_acts); - } + rcu_assign_pointer(flow->sf_acts, acts); + ovs_flow_deferred_free_acts(old_acts); reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid, info->snd_seq, OVS_FLOW_CMD_NEW); @@ -1062,8 +1281,8 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) ovs_dp_flow_multicast_group.id, PTR_ERR(reply)); return 0; -error_free_flow: - ovs_flow_free(flow); +err_kfree: + kfree(acts); error: return error; } diff --git a/datapath/flow.c b/datapath/flow.c index 63eef77ad..fad9e1977 100644 --- a/datapath/flow.c +++ b/datapath/flow.c @@ -200,20 +200,18 @@ void ovs_flow_used(struct sw_flow *flow, struct sk_buff *skb) spin_unlock(&flow->lock); } -struct sw_flow_actions *ovs_flow_actions_alloc(const struct nlattr *actions) +struct sw_flow_actions *ovs_flow_actions_alloc(int size) { - int actions_len = nla_len(actions); struct sw_flow_actions *sfa; - if (actions_len > MAX_ACTIONS_BUFSIZE) + if (size > MAX_ACTIONS_BUFSIZE) return ERR_PTR(-EINVAL); - sfa = kmalloc(sizeof(*sfa) + actions_len, GFP_KERNEL); + sfa = kmalloc(sizeof(*sfa) + size, GFP_KERNEL); if (!sfa) return ERR_PTR(-ENOMEM); - sfa->actions_len = actions_len; - memcpy(sfa->actions, nla_data(actions), actions_len); + sfa->actions_len = 0; return sfa; } @@ -848,7 +846,7 @@ const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = { [OVS_KEY_ATTR_ICMPV6] = sizeof(struct ovs_key_icmpv6), [OVS_KEY_ATTR_ARP] = sizeof(struct ovs_key_arp), [OVS_KEY_ATTR_ND] = sizeof(struct ovs_key_nd), - [OVS_KEY_ATTR_IPV4_TUNNEL] = sizeof(struct ovs_key_ipv4_tunnel), + [OVS_KEY_ATTR_TUNNEL] = -1, /* Not upstream. */ [OVS_KEY_ATTR_TUN_ID] = sizeof(__be64), @@ -989,6 +987,105 @@ static int parse_flow_nlattrs(const struct nlattr *attr, return 0; } +int ipv4_tun_from_nlattr(const struct nlattr *attr, + struct ovs_key_ipv4_tunnel *tun_key) +{ + struct nlattr *a; + int rem; + bool ttl = false; + + memset(tun_key, 0, sizeof(*tun_key)); + + nla_for_each_nested(a, attr, rem) { + int type = nla_type(a); + static const u32 ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1] = { + [OVS_TUNNEL_KEY_ATTR_ID] = sizeof(u64), + [OVS_TUNNEL_KEY_ATTR_IPV4_SRC] = sizeof(u32), + [OVS_TUNNEL_KEY_ATTR_IPV4_DST] = sizeof(u32), + [OVS_TUNNEL_KEY_ATTR_TOS] = 1, + [OVS_TUNNEL_KEY_ATTR_TTL] = 1, + [OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT] = 0, + [OVS_TUNNEL_KEY_ATTR_CSUM] = 0, + }; + + if (type > OVS_TUNNEL_KEY_ATTR_MAX || + ovs_tunnel_key_lens[type] != nla_len(a)) + return -EINVAL; + + switch (type) { + case OVS_TUNNEL_KEY_ATTR_ID: + tun_key->tun_id = nla_get_be64(a); + tun_key->tun_flags |= OVS_TNL_F_KEY; + break; + case OVS_TUNNEL_KEY_ATTR_IPV4_SRC: + tun_key->ipv4_src = nla_get_be32(a); + break; + case OVS_TUNNEL_KEY_ATTR_IPV4_DST: + tun_key->ipv4_dst = nla_get_be32(a); + break; + case OVS_TUNNEL_KEY_ATTR_TOS: + tun_key->ipv4_tos = nla_get_u8(a); + break; + case OVS_TUNNEL_KEY_ATTR_TTL: + tun_key->ipv4_ttl = nla_get_u8(a); + ttl = true; + break; + case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT: + tun_key->tun_flags |= OVS_TNL_F_DONT_FRAGMENT; + break; + case OVS_TUNNEL_KEY_ATTR_CSUM: + tun_key->tun_flags |= OVS_TNL_F_CSUM; + break; + default: + return -EINVAL; + + } + } + if (rem > 0) + return -EINVAL; + + if (!tun_key->ipv4_dst) + return -EINVAL; + + if (!ttl) + return -EINVAL; + + return 0; +} + +int ipv4_tun_to_nlattr(struct sk_buff *skb, + const struct ovs_key_ipv4_tunnel *tun_key) +{ + struct nlattr *nla; + + nla = nla_nest_start(skb, OVS_KEY_ATTR_TUNNEL); + if (!nla) + return -EMSGSIZE; + + if (tun_key->tun_flags & OVS_TNL_F_KEY && + nla_put_be64(skb, OVS_TUNNEL_KEY_ATTR_ID, tun_key->tun_id)) + return -EMSGSIZE; + if (tun_key->ipv4_src && + nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, tun_key->ipv4_src)) + return -EMSGSIZE; + if (nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_DST, tun_key->ipv4_dst)) + return -EMSGSIZE; + if (tun_key->ipv4_tos && + nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TOS, tun_key->ipv4_tos)) + return -EMSGSIZE; + if (nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TTL, tun_key->ipv4_ttl)) + return -EMSGSIZE; + if ((tun_key->tun_flags & OVS_TNL_F_DONT_FRAGMENT) && + nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT)) + return -EMSGSIZE; + if ((tun_key->tun_flags & OVS_TNL_F_CSUM) && + nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_CSUM)) + return -EMSGSIZE; + + nla_nest_end(skb, nla); + return 0; +} + /** * ovs_flow_from_nlattrs - parses Netlink attributes into a flow key. * @swkey: receives the extracted flow key. @@ -1037,37 +1134,29 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp, } if (attrs & (1ULL << OVS_KEY_ATTR_TUN_ID) && - attrs & (1ULL << OVS_KEY_ATTR_IPV4_TUNNEL)) { - struct ovs_key_ipv4_tunnel *tun_key; + attrs & (1ULL << OVS_KEY_ATTR_TUNNEL)) { __be64 tun_id; - tun_key = nla_data(a[OVS_KEY_ATTR_IPV4_TUNNEL]); + err = ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], &swkey->tun_key); + if (err) + return err; - if (!tun_key->ipv4_dst) - return -EINVAL; - if (!(tun_key->tun_flags & OVS_TNL_F_KEY)) + if (!(swkey->tun_key.tun_flags & OVS_TNL_F_KEY)) return -EINVAL; tun_id = nla_get_be64(a[OVS_KEY_ATTR_TUN_ID]); - if (tun_id != tun_key->tun_id) + if (tun_id != swkey->tun_key.tun_id) return -EINVAL; - memcpy(&swkey->tun_key, tun_key, sizeof(swkey->tun_key)); - memset(swkey->tun_key.pad, 0, sizeof(swkey->tun_key.pad)); - attrs &= ~(1ULL << OVS_KEY_ATTR_TUN_ID); - attrs &= ~(1ULL << OVS_KEY_ATTR_IPV4_TUNNEL); - } else if (attrs & (1ULL << OVS_KEY_ATTR_IPV4_TUNNEL)) { - struct ovs_key_ipv4_tunnel *tun_key; - tun_key = nla_data(a[OVS_KEY_ATTR_IPV4_TUNNEL]); - - if (!tun_key->ipv4_dst) - return -EINVAL; + attrs &= ~(1ULL << OVS_KEY_ATTR_TUNNEL); + } else if (attrs & (1ULL << OVS_KEY_ATTR_TUNNEL)) { - memcpy(&swkey->tun_key, tun_key, sizeof(swkey->tun_key)); - memset(swkey->tun_key.pad, 0, sizeof(swkey->tun_key.pad)); + err = ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], &swkey->tun_key); + if (err) + return err; - attrs &= ~(1ULL << OVS_KEY_ATTR_IPV4_TUNNEL); + attrs &= ~(1ULL << OVS_KEY_ATTR_TUNNEL); } /* Data attributes. */ @@ -1223,6 +1312,8 @@ int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow, int key_len, const stru int type = nla_type(nla); if (type <= OVS_KEY_ATTR_MAX && ovs_key_lens[type] > 0) { + int err; + if (nla_len(nla) != ovs_key_lens[type]) return -EINVAL; @@ -1246,21 +1337,23 @@ int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow, int key_len, const stru break; - case OVS_KEY_ATTR_IPV4_TUNNEL: + case OVS_KEY_ATTR_TUNNEL: if (tun_key->tun_flags & OVS_TNL_F_KEY) { tun_id = tun_key->tun_id; + err = ipv4_tun_from_nlattr(nla, tun_key); + if (err) + return err; - memcpy(tun_key, nla_data(nla), sizeof(*tun_key)); if (!(tun_key->tun_flags & OVS_TNL_F_KEY)) return -EINVAL; if (tun_key->tun_id != tun_id) return -EINVAL; - } else - memcpy(tun_key, nla_data(nla), sizeof(*tun_key)); - - if (!tun_key->ipv4_dst) - return -EINVAL; + } else { + err = ipv4_tun_from_nlattr(nla, tun_key); + if (err) + return err; + } break; case OVS_KEY_ATTR_IN_PORT: @@ -1297,14 +1390,10 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, swkey->phy.priority)) goto nla_put_failure; - if (swkey->tun_key.ipv4_dst) { - struct ovs_key_ipv4_tunnel *tun_key; - nla = nla_reserve(skb, OVS_KEY_ATTR_IPV4_TUNNEL, sizeof(*tun_key)); - if (!nla) - goto nla_put_failure; - tun_key = nla_data(nla); - memcpy(tun_key, &swkey->tun_key, sizeof(*tun_key)); - } + if (swkey->tun_key.ipv4_dst && + ipv4_tun_to_nlattr(skb, &swkey->tun_key)) + goto nla_put_failure; + if ((swkey->tun_key.tun_flags & OVS_TNL_F_KEY) && nla_put_be64(skb, OVS_KEY_ATTR_TUN_ID, swkey->tun_key.tun_id)) goto nla_put_failure; diff --git a/datapath/flow.h b/datapath/flow.h index 3f3624ffd..cffd450a3 100644 --- a/datapath/flow.h +++ b/datapath/flow.h @@ -40,6 +40,20 @@ struct sw_flow_actions { struct nlattr actions[]; }; +/* Tunnel flow flags. */ +#define OVS_TNL_F_DONT_FRAGMENT (1 << 0) +#define OVS_TNL_F_CSUM (1 << 1) +#define OVS_TNL_F_KEY (1 << 2) + +struct ovs_key_ipv4_tunnel { + __be64 tun_id; + __be32 ipv4_src; + __be32 ipv4_dst; + u16 tun_flags; + u8 ipv4_tos; + u8 ipv4_ttl; +}; + struct sw_flow_key { struct ovs_key_ipv4_tunnel tun_key; /* Encapsulating tunnel key. */ struct { @@ -131,7 +145,7 @@ struct sw_flow *ovs_flow_alloc(void); void ovs_flow_deferred_free(struct sw_flow *); void ovs_flow_free(struct sw_flow *); -struct sw_flow_actions *ovs_flow_actions_alloc(const struct nlattr *); +struct sw_flow_actions *ovs_flow_actions_alloc(int actions_len); void ovs_flow_deferred_free_acts(struct sw_flow_actions *); int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *, @@ -203,5 +217,9 @@ void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow); struct sw_flow *ovs_flow_tbl_next(struct flow_table *table, u32 *bucket, u32 *idx); extern const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1]; +int ipv4_tun_from_nlattr(const struct nlattr *attr, + struct ovs_key_ipv4_tunnel *tun_key); +int ipv4_tun_to_nlattr(struct sk_buff *skb, + const struct ovs_key_ipv4_tunnel *tun_key); #endif /* flow.h */ diff --git a/datapath/tunnel.h b/datapath/tunnel.h index 77054750a..b7de7a986 100644 --- a/datapath/tunnel.h +++ b/datapath/tunnel.h @@ -201,7 +201,6 @@ static inline void tnl_tun_key_init(struct ovs_key_ipv4_tunnel *tun_key, tun_key->ipv4_tos = iph->tos; tun_key->ipv4_ttl = iph->ttl; tun_key->tun_flags = tun_flags; - memset(tun_key->pad, 0, sizeof(tun_key->pad)); } static inline void tnl_get_param(const struct tnl_mutable_config *mutable, diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h index 5e3296594..f471fbc3e 100644 --- a/include/linux/openvswitch.h +++ b/include/linux/openvswitch.h @@ -282,13 +282,30 @@ enum ovs_key_attr { OVS_KEY_ATTR_ARP, /* struct ovs_key_arp */ OVS_KEY_ATTR_ND, /* struct ovs_key_nd */ OVS_KEY_ATTR_SKB_MARK, /* u32 skb mark */ + OVS_KEY_ATTR_TUNNEL, /* Nested set of ovs_tunnel attributes */ + +#ifdef __KERNEL__ OVS_KEY_ATTR_IPV4_TUNNEL, /* struct ovs_key_ipv4_tunnel */ +#endif OVS_KEY_ATTR_TUN_ID = 63, /* be64 tunnel ID */ __OVS_KEY_ATTR_MAX }; #define OVS_KEY_ATTR_MAX (__OVS_KEY_ATTR_MAX - 1) +enum ovs_tunnel_key_attr { + OVS_TUNNEL_KEY_ATTR_ID, /* be64 Tunnel ID */ + OVS_TUNNEL_KEY_ATTR_IPV4_SRC, /* be32 src IP address. */ + OVS_TUNNEL_KEY_ATTR_IPV4_DST, /* be32 dst IP address. */ + OVS_TUNNEL_KEY_ATTR_TOS, /* u8 Tunnel IP ToS. */ + OVS_TUNNEL_KEY_ATTR_TTL, /* u8 Tunnel IP TTL. */ + OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT, /* No argument, set DF. */ + OVS_TUNNEL_KEY_ATTR_CSUM, /* No argument. CSUM packet. */ + __OVS_TUNNEL_KEY_ATTR_MAX +}; + +#define OVS_TUNNEL_KEY_ATTR_MAX (__OVS_TUNNEL_KEY_ATTR_MAX - 1) + /** * enum ovs_frag_type - IPv4 and IPv6 fragment type * @OVS_FRAG_TYPE_NONE: Packet is not a fragment. @@ -365,21 +382,6 @@ struct ovs_key_nd { __u8 nd_tll[6]; }; -/* Values for ovs_key_ipv4_tunnel->tun_flags */ -#define OVS_TNL_F_DONT_FRAGMENT (1 << 0) -#define OVS_TNL_F_CSUM (1 << 1) -#define OVS_TNL_F_KEY (1 << 2) - -struct ovs_key_ipv4_tunnel { - __be64 tun_id; - __u32 tun_flags; - __be32 ipv4_src; - __be32 ipv4_dst; - __u8 ipv4_tos; - __u8 ipv4_ttl; - __u8 pad[2]; -}; - /** * enum ovs_flow_attr - attributes for %OVS_FLOW_* commands. * @OVS_FLOW_ATTR_KEY: Nested %OVS_KEY_ATTR_* attributes specifying the flow diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index bec32c38d..1d924c401 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -1192,7 +1192,7 @@ execute_set_action(struct ofpbuf *packet, const struct nlattr *a) case OVS_KEY_ATTR_TUN_ID: case OVS_KEY_ATTR_PRIORITY: case OVS_KEY_ATTR_SKB_MARK: - case OVS_KEY_ATTR_IPV4_TUNNEL: + case OVS_KEY_ATTR_TUNNEL: /* not implemented */ break; diff --git a/lib/odp-util.c b/lib/odp-util.c index e2f21daa2..96a952301 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -95,7 +95,7 @@ ovs_key_attr_to_string(enum ovs_key_attr attr) 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_IPV4_TUNNEL: return "ipv4_tunnel"; + 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_VLAN: return "vlan"; @@ -617,7 +617,7 @@ odp_flow_key_attr_len(uint16_t type) 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_IPV4_TUNNEL: return sizeof(struct ovs_key_ipv4_tunnel); + 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); @@ -672,19 +672,109 @@ ovs_frag_type_to_string(enum ovs_frag_type type) } } -static const char * -odp_tun_flag_to_string(uint32_t flags) +static int +tunnel_key_attr_len(int type) { - switch (flags) { - case OVS_TNL_F_DONT_FRAGMENT: - return "df"; - case OVS_TNL_F_CSUM: - return "csum"; - case OVS_TNL_F_KEY: - return "key"; - default: - return NULL; + switch (type) { + case OVS_TUNNEL_KEY_ATTR_ID: return 8; + case OVS_TUNNEL_KEY_ATTR_IPV4_SRC: return 4; + case OVS_TUNNEL_KEY_ATTR_IPV4_DST: return 4; + case OVS_TUNNEL_KEY_ATTR_TOS: return 1; + case OVS_TUNNEL_KEY_ATTR_TTL: return 1; + case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT: return 0; + case OVS_TUNNEL_KEY_ATTR_CSUM: return 0; + case __OVS_TUNNEL_KEY_ATTR_MAX: + return -1; } + return -1; +} + +static enum odp_key_fitness +tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun) +{ + unsigned int left; + const struct nlattr *a; + bool ttl = false; + bool unknown = false; + + NL_NESTED_FOR_EACH(a, left, attr) { + uint16_t type = nl_attr_type(a); + size_t len = nl_attr_get_size(a); + int expected_len = tunnel_key_attr_len(type); + + if (len != expected_len && expected_len >= 0) { + return ODP_FIT_ERROR; + } + + switch (type) { + case OVS_TUNNEL_KEY_ATTR_ID: + tun->tun_id = nl_attr_get_be64(a); + tun->flags |= FLOW_TNL_F_KEY; + break; + case OVS_TUNNEL_KEY_ATTR_IPV4_SRC: + tun->ip_src = nl_attr_get_be32(a); + break; + case OVS_TUNNEL_KEY_ATTR_IPV4_DST: + tun->ip_dst = nl_attr_get_be32(a); + break; + case OVS_TUNNEL_KEY_ATTR_TOS: + tun->ip_tos = nl_attr_get_u8(a); + break; + case OVS_TUNNEL_KEY_ATTR_TTL: + tun->ip_ttl = nl_attr_get_u8(a); + ttl = true; + break; + case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT: + tun->flags |= FLOW_TNL_F_DONT_FRAGMENT; + break; + case OVS_TUNNEL_KEY_ATTR_CSUM: + tun->flags |= FLOW_TNL_F_CSUM; + break; + default: + /* Allow this to show up as unexpected, if there are unknown + * tunnel attribute, eventually resulting in ODP_FIT_TOO_MUCH. */ + unknown = true; + break; + } + } + + if (!ttl) { + return ODP_FIT_ERROR; + } + if (unknown) { + return ODP_FIT_TOO_MUCH; + } + return ODP_FIT_PERFECT; +} + +static void +tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key) +{ + size_t tun_key_ofs; + + tun_key_ofs = nl_msg_start_nested(a, OVS_KEY_ATTR_TUNNEL); + + if (tun_key->flags & FLOW_TNL_F_KEY) { + nl_msg_put_be64(a, OVS_TUNNEL_KEY_ATTR_ID, tun_key->tun_id); + } + if (tun_key->ip_src) { + nl_msg_put_be32(a, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, tun_key->ip_src); + } + if (tun_key->ip_dst) { + nl_msg_put_be32(a, OVS_TUNNEL_KEY_ATTR_IPV4_DST, tun_key->ip_dst); + } + if (tun_key->ip_tos) { + nl_msg_put_u8(a, OVS_TUNNEL_KEY_ATTR_TOS, tun_key->ip_tos); + } + nl_msg_put_u8(a, OVS_TUNNEL_KEY_ATTR_TTL, tun_key->ip_ttl); + if (tun_key->flags & FLOW_TNL_F_DONT_FRAGMENT) { + nl_msg_put_flag(a, OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT); + } + if (tun_key->flags & FLOW_TNL_F_CSUM) { + nl_msg_put_flag(a, OVS_TUNNEL_KEY_ATTR_CSUM); + } + + nl_msg_end_nested(a, tun_key_ofs); } static void @@ -699,7 +789,7 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds) const struct ovs_key_icmpv6 *icmpv6_key; const struct ovs_key_arp *arp_key; const struct ovs_key_nd *nd_key; - const struct ovs_key_ipv4_tunnel *ipv4_tun_key; + struct flow_tnl tun_key; enum ovs_key_attr attr = nl_attr_type(a); int expected_len; @@ -734,18 +824,22 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds) ds_put_format(ds, "(%#"PRIx64")", ntohll(nl_attr_get_be64(a))); break; - case OVS_KEY_ATTR_IPV4_TUNNEL: - ipv4_tun_key = nl_attr_get(a); - ds_put_format(ds, "(tun_id=0x%"PRIx64",src="IP_FMT",dst="IP_FMT"," - "tos=0x%"PRIx8",ttl=%"PRIu8",flags(", - ntohll(ipv4_tun_key->tun_id), - IP_ARGS(ipv4_tun_key->ipv4_src), - IP_ARGS(ipv4_tun_key->ipv4_dst), - ipv4_tun_key->ipv4_tos, ipv4_tun_key->ipv4_ttl); - - format_flags(ds, odp_tun_flag_to_string, - ipv4_tun_key->tun_flags, ','); - ds_put_format(ds, "))"); + 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_format(ds, "(error)"); + } else { + ds_put_format(ds, "(tun_id=0x%"PRIx64",src="IP_FMT",dst="IP_FMT"," + "tos=0x%"PRIx8",ttl=%"PRIu8",flags(", + ntohll(tun_key.tun_id), + IP_ARGS(tun_key.ip_src), + IP_ARGS(tun_key.ip_dst), + tun_key.ip_tos, tun_key.ip_ttl); + + format_flags(ds, flow_tun_flag_to_string, + (uint32_t) tun_key.flags, ','); + ds_put_format(ds, "))"); + } break; case OVS_KEY_ATTR_IN_PORT: @@ -974,23 +1068,24 @@ parse_odp_key_attr(const char *s, const struct simap *port_names, { char tun_id_s[32]; int tos, ttl; - struct ovs_key_ipv4_tunnel tun_key; + struct flow_tnl tun_key; int n = -1; - if (sscanf(s, "ipv4_tunnel(tun_id=%31[x0123456789abcdefABCDEF]," + if (sscanf(s, "tunnel(tun_id=%31[x0123456789abcdefABCDEF]," "src="IP_SCAN_FMT",dst="IP_SCAN_FMT ",tos=%i,ttl=%i,flags%n", tun_id_s, - IP_SCAN_ARGS(&tun_key.ipv4_src), - IP_SCAN_ARGS(&tun_key.ipv4_dst), &tos, &ttl, + IP_SCAN_ARGS(&tun_key.ip_src), + IP_SCAN_ARGS(&tun_key.ip_dst), &tos, &ttl, &n) > 0 && n > 0) { int res; + uint32_t flags; tun_key.tun_id = htonll(strtoull(tun_id_s, NULL, 0)); - tun_key.ipv4_tos = tos; - tun_key.ipv4_ttl = ttl; + tun_key.ip_tos = tos; + tun_key.ip_ttl = ttl; + res = parse_flags(&s[n], flow_tun_flag_to_string, &flags); + tun_key.flags = (uint16_t) flags; - res = parse_flags(&s[n], odp_tun_flag_to_string, - &tun_key.tun_flags); if (res < 0) { return res; } @@ -999,10 +1094,7 @@ parse_odp_key_attr(const char *s, const struct simap *port_names, return -EINVAL; } n++; - - memset(&tun_key.pad, 0, sizeof tun_key.pad); - nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV4_TUNNEL, &tun_key, - sizeof tun_key); + tun_key_to_attr(key, &tun_key); return n; } } @@ -1342,27 +1434,6 @@ ovs_to_odp_frag(uint8_t nw_frag) : OVS_FRAG_TYPE_LATER); } -/* The set of kernel flags we understand. Used to detect if ODP_FIT_TOO_MUCH */ -#define OVS_TNL_F_KNOWN_MASK \ - (OVS_TNL_F_DONT_FRAGMENT | OVS_TNL_F_CSUM | OVS_TNL_F_KEY) - -/* These allow the flow/kernel view of the flags to change in future */ -static uint32_t -flow_to_odp_flags(uint16_t flags) -{ - return (flags & FLOW_TNL_F_DONT_FRAGMENT ? OVS_TNL_F_DONT_FRAGMENT : 0) - | (flags & FLOW_TNL_F_CSUM ? OVS_TNL_F_CSUM : 0) - | (flags & FLOW_TNL_F_KEY ? OVS_TNL_F_KEY : 0); -} - -static uint16_t -odp_to_flow_flags(uint32_t tun_flags) -{ - return (tun_flags & OVS_TNL_F_DONT_FRAGMENT ? FLOW_TNL_F_DONT_FRAGMENT : 0) - | (tun_flags & OVS_TNL_F_CSUM ? FLOW_TNL_F_CSUM : 0) - | (tun_flags & OVS_TNL_F_KEY ? FLOW_TNL_F_KEY : 0); -} - /* Appends a representation of 'flow' as OVS_KEY_ATTR_* attributes to 'buf'. * 'flow->in_port' is ignored (since it is likely to be an OpenFlow port * number rather than a datapath port number). Instead, if 'odp_in_port' @@ -1383,18 +1454,7 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow, } if (flow->tunnel.ip_dst) { - struct ovs_key_ipv4_tunnel *ipv4_tun_key; - - ipv4_tun_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_IPV4_TUNNEL, - sizeof *ipv4_tun_key); - /* layouts differ, flags has different size */ - ipv4_tun_key->tun_id = flow->tunnel.tun_id; - ipv4_tun_key->tun_flags = flow_to_odp_flags(flow->tunnel.flags); - ipv4_tun_key->ipv4_src = flow->tunnel.ip_src; - ipv4_tun_key->ipv4_dst = flow->tunnel.ip_dst; - ipv4_tun_key->ipv4_tos = flow->tunnel.ip_tos; - ipv4_tun_key->ipv4_ttl = flow->tunnel.ip_ttl; - memset(ipv4_tun_key->pad, 0, sizeof ipv4_tun_key->pad); + 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); } @@ -1905,23 +1965,14 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TUN_ID; } - if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV4_TUNNEL)) { - const struct ovs_key_ipv4_tunnel *ipv4_tun_key; + if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TUNNEL)) { + enum odp_key_fitness res; - ipv4_tun_key = nl_attr_get(attrs[OVS_KEY_ATTR_IPV4_TUNNEL]); - - flow->tunnel.tun_id = ipv4_tun_key->tun_id; - flow->tunnel.ip_src = ipv4_tun_key->ipv4_src; - flow->tunnel.ip_dst = ipv4_tun_key->ipv4_dst; - flow->tunnel.flags = odp_to_flow_flags(ipv4_tun_key->tun_flags); - flow->tunnel.ip_tos = ipv4_tun_key->ipv4_tos; - flow->tunnel.ip_ttl = ipv4_tun_key->ipv4_ttl; - - /* Allow this to show up as unexpected, if there are unknown flags, - * eventually resulting in ODP_FIT_TOO_MUCH. - * OVS_TNL_F_KNOWN_MASK defined locally above. */ - if (!(ipv4_tun_key->tun_flags & ~OVS_TNL_F_KNOWN_MASK)) { - expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV4_TUNNEL; + res = tun_key_from_attr(attrs[OVS_KEY_ATTR_TUNNEL], &flow->tunnel); + if (res == ODP_FIT_ERROR) { + return ODP_FIT_ERROR; + } else if (res == ODP_FIT_PERFECT) { + expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TUNNEL; } } @@ -2017,18 +2068,11 @@ commit_set_tunnel_action(const struct flow *flow, struct flow *base, /* A valid IPV4_TUNNEL must have non-zero ip_dst. */ if (flow->tunnel.ip_dst) { - struct ovs_key_ipv4_tunnel ipv4_tun_key; - - ipv4_tun_key.tun_id = base->tunnel.tun_id; - ipv4_tun_key.tun_flags = flow_to_odp_flags(base->tunnel.flags); - ipv4_tun_key.ipv4_src = base->tunnel.ip_src; - ipv4_tun_key.ipv4_dst = base->tunnel.ip_dst; - ipv4_tun_key.ipv4_tos = base->tunnel.ip_tos; - ipv4_tun_key.ipv4_ttl = base->tunnel.ip_ttl; - memset(&ipv4_tun_key.pad, 0, sizeof ipv4_tun_key.pad); - - commit_set_action(odp_actions, OVS_KEY_ATTR_IPV4_TUNNEL, - &ipv4_tun_key, sizeof ipv4_tun_key); + size_t offset; + + offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET); + tun_key_to_attr(odp_actions, &base->tunnel); + nl_msg_end_nested(odp_actions, offset); } else { commit_set_action(odp_actions, OVS_KEY_ATTR_TUN_ID, &base->tunnel.tun_id, sizeof base->tunnel.tun_id); diff --git a/tests/odp.at b/tests/odp.at index 009ac36dc..687f9c914 100644 --- a/tests/odp.at +++ b/tests/odp.at @@ -32,7 +32,7 @@ skb_mark(0x1234),in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth echo echo '# Valid forms with tunnel header.' - sed 's/^/ipv4_tunnel(tun_id=0x7f10354,src=10.10.10.10,dst=20.20.20.20,tos=0x0,ttl=64,flags(csum,key)),/' odp-base.txt + sed 's/^/tunnel(tun_id=0x7f10354,src=10.10.10.10,dst=20.20.20.20,tos=0x0,ttl=64,flags(csum,key)),/' odp-base.txt echo echo '# Valid forms with VLAN header.' @@ -45,13 +45,13 @@ s/$/)/' odp-base.txt echo echo '# Valid forms with tunnel and VLAN headers.' - sed 's/^/ipv4_tunnel(tun_id=0xfedcba9876543210,src=10.0.0.1,dst=10.0.0.2,tos=0x8,ttl=128,flags()),/ + sed 's/^/tunnel(tun_id=0xfedcba9876543210,src=10.0.0.1,dst=10.0.0.2,tos=0x8,ttl=128,flags(key)),/ s/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/ s/$/)/' odp-base.txt echo echo '# Valid forms with QOS priority, tunnel, and VLAN headers.' - sed 's/^/skb_priority(0x1234),ipv4_tunnel(tun_id=0xfedcba9876543210,src=10.10.10.10,dst=20.20.20.20,tos=0x8,ttl=64,flags(key)),/ + sed 's/^/skb_priority(0x1234),tunnel(tun_id=0xfedcba9876543210,src=10.10.10.10,dst=20.20.20.20,tos=0x8,ttl=64,flags(key)),/ s/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/ s/$/)/' odp-base.txt @@ -92,9 +92,8 @@ push_vlan(tpid=0x9100,vid=13,pcp=5) push_vlan(tpid=0x9100,vid=13,pcp=5,cfi=0) pop_vlan sample(sample=9.7%,actions(1,2,3,push_vlan(vid=1,pcp=2))) -set(ipv4_tunnel(tun_id=0xabcdef1234567890,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(df,csum,key))) -set(ipv4_tunnel(tun_id=0xabcdef1234567890,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(df,csum,key,0x20))) -set(ipv4_tunnel(tun_id=0xabcdef1234567890,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags())) +set(tunnel(tun_id=0xabcdef1234567890,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(df,csum,key))) +set(tunnel(tun_id=0xabcdef1234567890,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key))) ]) AT_CHECK_UNQUOTED([test-odp parse-actions < actions.txt], [0], [`cat actions.txt` -- 2.43.0