From: Pravin B Shelar Date: Fri, 21 Oct 2011 21:38:54 +0000 (-0700) Subject: datapath: Refactor actions in terms of match fields. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4edb9ae90e4092f5f56b9d914d2b88783c49860d;p=openvswitch datapath: Refactor actions in terms of match fields. Almost all current actions can be expressed in the form of push/pop/set , where field is one of the match fields. We can create three base actions and take a field. This has both a nice symmetry and avoids inconsistencies where we can match on the vlan TPID but not set it. Following patch converts all actions to this new format. Signed-off-by: Pravin B Shelar Acked-by: Jesse Gross Bug #7115 --- diff --git a/datapath/actions.c b/datapath/actions.c index a28e9866..70f48eae 100644 --- a/datapath/actions.c +++ b/datapath/actions.c @@ -97,7 +97,7 @@ static int pop_vlan(struct sk_buff *skb) return 0; } -static int push_vlan(struct sk_buff *skb, __be16 new_tci) +static int push_vlan(struct sk_buff *skb, const struct ovs_key_8021q *q_key) { if (unlikely(vlan_tx_tag_present(skb))) { u16 current_tag; @@ -113,117 +113,127 @@ static int push_vlan(struct sk_buff *skb, __be16 new_tci) + ETH_HLEN, VLAN_HLEN, 0)); } - __vlan_hwaccel_put_tag(skb, ntohs(new_tci)); + __vlan_hwaccel_put_tag(skb, ntohs(q_key->q_tci)); return 0; } -static bool is_ip(struct sk_buff *skb) +static int set_eth_addr(struct sk_buff *skb, + const struct ovs_key_ethernet *eth_key) { - return (OVS_CB(skb)->flow->key.eth.type == htons(ETH_P_IP) && - skb->transport_header > skb->network_header); + int err; + err = make_writable(skb, ETH_HLEN); + if (unlikely(err)) + return err; + + memcpy(eth_hdr(skb)->h_source, eth_key->eth_src, ETH_HLEN); + memcpy(eth_hdr(skb)->h_dest, eth_key->eth_dst, ETH_HLEN); + + return 0; } -static __sum16 *get_l4_checksum(struct sk_buff *skb) +static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh, + __be32 *addr, __be32 new_addr) { - u8 nw_proto = OVS_CB(skb)->flow->key.ip.proto; int transport_len = skb->len - skb_transport_offset(skb); - if (nw_proto == IPPROTO_TCP) { + + if (nh->protocol == IPPROTO_TCP) { if (likely(transport_len >= sizeof(struct tcphdr))) - return &tcp_hdr(skb)->check; - } else if (nw_proto == IPPROTO_UDP) { + inet_proto_csum_replace4(&tcp_hdr(skb)->check, skb, + *addr, new_addr, 1); + } else if (nh->protocol == IPPROTO_UDP) { if (likely(transport_len >= sizeof(struct udphdr))) - return &udp_hdr(skb)->check; + inet_proto_csum_replace4(&udp_hdr(skb)->check, skb, + *addr, new_addr, 1); } - return NULL; + + csum_replace4(&nh->check, *addr, new_addr); + skb_clear_rxhash(skb); + *addr = new_addr; } -static int set_nw_addr(struct sk_buff *skb, const struct nlattr *a) +static void set_ip_tos(struct sk_buff *skb, struct iphdr *nh, u8 new_tos) +{ + u8 old, new; + + /* Set the DSCP bits and preserve the ECN bits. */ + old = nh->tos; + new = new_tos | (nh->tos & INET_ECN_MASK); + csum_replace4(&nh->check, (__force __be32)old, + (__force __be32)new); + nh->tos = new; +} + +static int set_ipv4(struct sk_buff *skb, const struct ovs_key_ipv4 *ipv4_key) { - __be32 new_nwaddr = nla_get_be32(a); struct iphdr *nh; - __sum16 *check; - __be32 *nwaddr; int err; - if (unlikely(!is_ip(skb))) - return 0; - err = make_writable(skb, skb_network_offset(skb) + sizeof(struct iphdr)); if (unlikely(err)) return err; nh = ip_hdr(skb); - nwaddr = nla_type(a) == OVS_ACTION_ATTR_SET_NW_SRC ? &nh->saddr : &nh->daddr; - check = get_l4_checksum(skb); - if (likely(check)) - inet_proto_csum_replace4(check, skb, *nwaddr, new_nwaddr, 1); - csum_replace4(&nh->check, *nwaddr, new_nwaddr); + if (ipv4_key->ipv4_src != nh->saddr) + set_ip_addr(skb, nh, &nh->saddr, ipv4_key->ipv4_src); - skb_clear_rxhash(skb); + if (ipv4_key->ipv4_dst != nh->daddr) + set_ip_addr(skb, nh, &nh->daddr, ipv4_key->ipv4_dst); - *nwaddr = new_nwaddr; + if (ipv4_key->ipv4_tos != nh->tos) + set_ip_tos(skb, nh, ipv4_key->ipv4_tos); return 0; } -static int set_nw_tos(struct sk_buff *skb, u8 nw_tos) +/* Must follow make_writable() since that can move the skb data. */ +static void set_tp_port(struct sk_buff *skb, __be16 *port, + __be16 new_port, __sum16 *check) { - struct iphdr *nh = ip_hdr(skb); - u8 old, new; - int err; + inet_proto_csum_replace2(check, skb, *port, new_port, 0); + *port = new_port; + skb_clear_rxhash(skb); +} - if (unlikely(!is_ip(skb))) - return 0; +static int set_udp_port(struct sk_buff *skb, + const struct ovs_key_udp *udp_port_key) +{ + struct udphdr *uh; + int err; - err = make_writable(skb, skb_network_offset(skb) + - sizeof(struct iphdr)); + err = make_writable(skb, skb_transport_offset(skb) + + sizeof(struct udphdr)); if (unlikely(err)) return err; - /* Set the DSCP bits and preserve the ECN bits. */ - old = nh->tos; - new = nw_tos | (nh->tos & INET_ECN_MASK); - csum_replace4(&nh->check, (__force __be32)old, - (__force __be32)new); - nh->tos = new; + uh = udp_hdr(skb); + if (udp_port_key->udp_src != uh->source) + set_tp_port(skb, &uh->source, udp_port_key->udp_src, &uh->check); + + if (udp_port_key->udp_dst != uh->dest) + set_tp_port(skb, &uh->dest, udp_port_key->udp_dst, &uh->check); return 0; } -static int set_tp_port(struct sk_buff *skb, const struct nlattr *a) +static int set_tcp_port(struct sk_buff *skb, + const struct ovs_key_tcp *tcp_port_key) { - struct udphdr *th; - __sum16 *check; - __be16 *port; + struct tcphdr *th; int err; - if (unlikely(!is_ip(skb))) - return 0; - err = make_writable(skb, skb_transport_offset(skb) + sizeof(struct tcphdr)); if (unlikely(err)) return err; - /* Must follow make_writable() since that can move the skb data. */ - check = get_l4_checksum(skb); - if (unlikely(!check)) - return 0; + th = tcp_hdr(skb); + if (tcp_port_key->tcp_src != th->source) + set_tp_port(skb, &th->source, tcp_port_key->tcp_src, &th->check); - /* - * Update port and checksum. - * - * This is OK because source and destination port numbers are at the - * same offsets in both UDP and TCP headers, and get_l4_checksum() only - * supports those protocols. - */ - th = udp_hdr(skb); - port = nla_type(a) == OVS_ACTION_ATTR_SET_TP_SRC ? &th->source : &th->dest; - inet_proto_csum_replace2(check, skb, *port, nla_get_be16(a), 0); - *port = nla_get_be16(a); - skb_clear_rxhash(skb); + if (tcp_port_key->tcp_dst != th->dest) + set_tp_port(skb, &th->dest, tcp_port_key->tcp_dst, &th->check); return 0; } @@ -298,6 +308,36 @@ static int sample(struct datapath *dp, struct sk_buff *skb, nla_len(acts_list), true); } +static int execute_set_action(struct sk_buff *skb, + const struct nlattr *nested_attr) +{ + int err; + + switch (nla_type(nested_attr)) { + case OVS_KEY_ATTR_TUN_ID: + OVS_CB(skb)->tun_id = nla_get_be64(nested_attr); + err = 0; + break; + + case OVS_KEY_ATTR_ETHERNET: + err = set_eth_addr(skb, nla_data(nested_attr)); + break; + + case OVS_KEY_ATTR_IPV4: + err = set_ipv4(skb, nla_data(nested_attr)); + break; + + case OVS_KEY_ATTR_TCP: + err = set_tcp_port(skb, nla_data(nested_attr)); + break; + + case OVS_KEY_ATTR_UDP: + err = set_udp_port(skb, nla_data(nested_attr)); + break; + } + return err; +} + /* Execute a list of actions against 'skb'. */ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, const struct nlattr *attr, int len, bool keep_skb) @@ -329,44 +369,20 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, output_userspace(dp, skb, a); break; - case OVS_ACTION_ATTR_SET_TUNNEL: - OVS_CB(skb)->tun_id = nla_get_be64(a); - break; - - case OVS_ACTION_ATTR_PUSH_VLAN: - err = push_vlan(skb, nla_get_be16(a)); - if (unlikely(err)) /* skb already freed */ + case OVS_ACTION_ATTR_PUSH: + /* Only supported push action is on vlan tag. */ + err = push_vlan(skb, nla_data(nla_data(a))); + if (unlikely(err)) /* skb already freed. */ return err; break; - case OVS_ACTION_ATTR_POP_VLAN: + case OVS_ACTION_ATTR_POP: + /* Only supported pop action is on vlan tag. */ err = pop_vlan(skb); break; - case OVS_ACTION_ATTR_SET_DL_SRC: - err = make_writable(skb, ETH_HLEN); - if (likely(!err)) - memcpy(eth_hdr(skb)->h_source, nla_data(a), ETH_ALEN); - break; - - case OVS_ACTION_ATTR_SET_DL_DST: - err = make_writable(skb, ETH_HLEN); - if (likely(!err)) - memcpy(eth_hdr(skb)->h_dest, nla_data(a), ETH_ALEN); - break; - - case OVS_ACTION_ATTR_SET_NW_SRC: - case OVS_ACTION_ATTR_SET_NW_DST: - err = set_nw_addr(skb, a); - break; - - case OVS_ACTION_ATTR_SET_NW_TOS: - err = set_nw_tos(skb, nla_get_u8(a)); - break; - - case OVS_ACTION_ATTR_SET_TP_SRC: - case OVS_ACTION_ATTR_SET_TP_DST: - err = set_tp_port(skb, a); + case OVS_ACTION_ATTR_SET: + err = execute_set_action(skb, nla_data(a)); break; case OVS_ACTION_ATTR_SET_PRIORITY: diff --git a/datapath/datapath.c b/datapath/datapath.c index 49821507..a3be3254 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -494,9 +494,11 @@ static int flush_flows(int dp_ifindex) return 0; } -static int validate_actions(const struct nlattr *attr, int depth); +static int validate_actions(const struct nlattr *attr, + const struct sw_flow_key *key, int depth); -static int validate_sample(const struct nlattr *attr, int depth) +static int validate_sample(const struct nlattr *attr, + const struct sw_flow_key *key, int depth) { static const struct nla_policy sample_policy[OVS_SAMPLE_ATTR_MAX + 1] = { @@ -515,7 +517,80 @@ static int validate_sample(const struct nlattr *attr, int depth) if (!a[OVS_SAMPLE_ATTR_ACTIONS]) return -EINVAL; - return validate_actions(a[OVS_SAMPLE_ATTR_ACTIONS], (depth + 1)); + return validate_actions(a[OVS_SAMPLE_ATTR_ACTIONS], key, (depth + 1)); +} + +static int validate_action_key(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); + + /* There can be only one key in a action */ + if (nla_total_size(nla_len(ovs_key)) != nla_len(a)) + return -EINVAL; + + if (key_type > OVS_KEY_ATTR_MAX || + nla_len(ovs_key) != ovs_key_lens[key_type]) + return -EINVAL; + +#define ACTION(act, key) ((act << 8) | key) + + switch(ACTION(act_type, 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_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; + break; + + case ACTION(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_IPV4): + if (flow_key->eth.type != htons(ETH_P_IP)) + return -EINVAL; + + if (!flow_key->ipv4.addr.src || !flow_key->ipv4.addr.dst) + return -EINVAL; + + ipv4_key = nla_data(ovs_key); + if (ipv4_key->ipv4_proto != flow_key->ip.proto) + return -EINVAL; + + if (ipv4_key->ipv4_tos & INET_ECN_MASK) + return -EINVAL; + break; + + case ACTION(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_TCP): + if (flow_key->ip.proto != IPPROTO_TCP) + return -EINVAL; + + if (!flow_key->ipv4.tp.src || !flow_key->ipv4.tp.dst) + return -EINVAL; + + break; + + case ACTION(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_UDP): + if (flow_key->ip.proto != IPPROTO_UDP) + return -EINVAL; + + if (!flow_key->ipv4.tp.src || !flow_key->ipv4.tp.dst) + return -EINVAL; + break; + + default: + return -EINVAL; + } +#undef ACTION + return 0; } static int validate_userspace(const struct nlattr *attr) @@ -538,7 +613,8 @@ static int validate_userspace(const struct nlattr *attr) return 0; } -static int validate_actions(const struct nlattr *attr, int depth) +static int validate_actions(const struct nlattr *attr, + const struct sw_flow_key *key, int depth) { const struct nlattr *a; int rem, err; @@ -551,16 +627,9 @@ static int validate_actions(const struct nlattr *attr, int depth) static const u32 action_lens[OVS_ACTION_ATTR_MAX + 1] = { [OVS_ACTION_ATTR_OUTPUT] = 4, [OVS_ACTION_ATTR_USERSPACE] = (u32)-1, - [OVS_ACTION_ATTR_PUSH_VLAN] = 2, - [OVS_ACTION_ATTR_POP_VLAN] = 0, - [OVS_ACTION_ATTR_SET_DL_SRC] = ETH_ALEN, - [OVS_ACTION_ATTR_SET_DL_DST] = ETH_ALEN, - [OVS_ACTION_ATTR_SET_NW_SRC] = 4, - [OVS_ACTION_ATTR_SET_NW_DST] = 4, - [OVS_ACTION_ATTR_SET_NW_TOS] = 1, - [OVS_ACTION_ATTR_SET_TP_SRC] = 2, - [OVS_ACTION_ATTR_SET_TP_DST] = 2, - [OVS_ACTION_ATTR_SET_TUNNEL] = 8, + [OVS_ACTION_ATTR_PUSH] = (u32)-1, + [OVS_ACTION_ATTR_POP] = 2, + [OVS_ACTION_ATTR_SET] = (u32)-1, [OVS_ACTION_ATTR_SET_PRIORITY] = 4, [OVS_ACTION_ATTR_POP_PRIORITY] = 0, [OVS_ACTION_ATTR_SAMPLE] = (u32)-1 @@ -576,14 +645,6 @@ static int validate_actions(const struct nlattr *attr, int depth) case OVS_ACTION_ATTR_UNSPEC: return -EINVAL; - case OVS_ACTION_ATTR_POP_VLAN: - case OVS_ACTION_ATTR_SET_DL_SRC: - case OVS_ACTION_ATTR_SET_DL_DST: - case OVS_ACTION_ATTR_SET_NW_SRC: - case OVS_ACTION_ATTR_SET_NW_DST: - case OVS_ACTION_ATTR_SET_TP_SRC: - case OVS_ACTION_ATTR_SET_TP_DST: - case OVS_ACTION_ATTR_SET_TUNNEL: case OVS_ACTION_ATTR_SET_PRIORITY: case OVS_ACTION_ATTR_POP_PRIORITY: /* No validation needed. */ @@ -600,24 +661,27 @@ static int validate_actions(const struct nlattr *attr, int depth) return -EINVAL; break; - case OVS_ACTION_ATTR_PUSH_VLAN: - if (nla_get_be16(a) & htons(VLAN_CFI_MASK)) + + case OVS_ACTION_ATTR_POP: + if (nla_get_u16(a) != OVS_KEY_ATTR_8021Q) return -EINVAL; break; - case OVS_ACTION_ATTR_SET_NW_TOS: - if (nla_get_u8(a) & INET_ECN_MASK) - return -EINVAL; + case OVS_ACTION_ATTR_SET: + case OVS_ACTION_ATTR_PUSH: + err = validate_action_key(a, key); + if (err) + return err; break; case OVS_ACTION_ATTR_SAMPLE: - err = validate_sample(a, depth); + err = validate_sample(a, key, depth); if (err) return err; break; default: - return -EOPNOTSUPP; + return -EINVAL; } } @@ -626,6 +690,7 @@ static int validate_actions(const struct nlattr *attr, int depth) return 0; } + static void clear_stats(struct sw_flow *flow) { flow->used = 0; @@ -654,10 +719,6 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) nla_len(a[OVS_PACKET_ATTR_PACKET]) < ETH_HLEN) goto err; - err = validate_actions(a[OVS_PACKET_ATTR_ACTIONS], 0); - if (err) - goto err; - len = nla_len(a[OVS_PACKET_ATTR_PACKET]); packet = __dev_alloc_skb(NET_IP_ALIGN + len, GFP_KERNEL); err = -ENOMEM; @@ -694,6 +755,10 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) if (err) goto err_flow_put; + err = validate_actions(a[OVS_PACKET_ATTR_ACTIONS], &flow->key, 0); + if (err) + goto err_flow_put; + flow->hash = flow_hash(&flow->key, key_len); acts = flow_actions_alloc(a[OVS_PACKET_ATTR_ACTIONS]); @@ -914,7 +979,7 @@ 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], 0); + error = validate_actions(a[OVS_FLOW_ATTR_ACTIONS], &key, 0); if (error) goto error; } else if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW) { diff --git a/datapath/flow.c b/datapath/flow.c index 73222954..7b9cb611 100644 --- a/datapath/flow.c +++ b/datapath/flow.c @@ -769,7 +769,7 @@ void flow_tbl_remove(struct flow_table *table, struct sw_flow *flow) } /* The size of the argument for each %OVS_KEY_ATTR_* Netlink attribute. */ -static const u32 key_lens[OVS_KEY_ATTR_MAX + 1] = { +const u32 ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = { [OVS_KEY_ATTR_TUN_ID] = 8, [OVS_KEY_ATTR_IN_PORT] = 4, [OVS_KEY_ATTR_ETHERNET] = sizeof(struct ovs_key_ethernet), @@ -827,8 +827,8 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp, int type = nla_type(nla); - if (type > OVS_KEY_ATTR_MAX || nla_len(nla) != key_lens[type]) - goto invalid; + if (type > OVS_KEY_ATTR_MAX || nla_len(nla) != ovs_key_lens[type]) + goto invalid; #define TRANSITION(PREV_TYPE, TYPE) (((PREV_TYPE) << 16) | (TYPE)) switch (TRANSITION(prev_type, type)) { @@ -1069,8 +1069,8 @@ int flow_metadata_from_nlattrs(u16 *in_port, __be64 *tun_id, nla_for_each_nested(nla, attr, rem) { int type = nla_type(nla); - if (type > OVS_KEY_ATTR_MAX || nla_len(nla) != key_lens[type]) - return -EINVAL; + if (type > OVS_KEY_ATTR_MAX || nla_len(nla) != ovs_key_lens[type]) + return -EINVAL; switch (TRANSITION(prev_type, type)) { case TRANSITION(OVS_KEY_ATTR_UNSPEC, OVS_KEY_ATTR_TUN_ID): diff --git a/datapath/flow.h b/datapath/flow.h index ade8ac80..96b3b4fe 100644 --- a/datapath/flow.h +++ b/datapath/flow.h @@ -180,5 +180,6 @@ void flow_tbl_remove(struct flow_table *table, struct sw_flow *flow); u32 flow_hash(const struct sw_flow_key *key, int key_len); struct sw_flow *flow_tbl_next(struct flow_table *table, u32 *bucket, u32 *idx); +extern const u32 ovs_key_lens[OVS_KEY_ATTR_MAX + 1]; #endif /* flow.h */ diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h index c077f620..acc27693 100644 --- a/include/linux/openvswitch.h +++ b/include/linux/openvswitch.h @@ -423,21 +423,37 @@ enum ovs_userspace_attr { #define OVS_USERSPACE_ATTR_MAX (__OVS_USERSPACE_ATTR_MAX - 1) -/* Action types. */ +/** + * enum ovs_action_attr - Action types. + * + * @OVS_ACTION_ATTR_OUTPUT: Output packet to port passed as NLA data. + * @OVS_ACTION_ATTR_USERSPACE: Nested %OVS_USERSPACE_ATTR_ attributes specifying + * PID. + * @OVS_ACTION_ATTR_PUSH: Nested %OVS_KEY_ATTR_* attribute specifying header to + * push to given packet. + * E.g. Push vlan tag action would be NLA of %OVS_ACTION_ATTR_PUSH type with + * nested attribute of type %OVS_KEY_ATTR_8021Q with struct ovs_key_8021q + * as NLA data. + * @OVS_ACTION_ATTR_POP: Pop header according to %OVS_KEY_ATTR_ sent as + * attribute data. + * @OVS_ACTION_ATTR_SET: Nested %OVS_KEY_ATTR_* attribute specifying the + * field to set to given packet. + * @OVS_ACTION_ATTR_SET_PRIORITY: A set skb->priority to 32-bit number passed + * as NLA data. + * @OVS_ACTION_ATTR_POP_PRIORITY: Restore skb->priority to original value. + * @OVS_ACTION_ATTR_SAMPLE: Execute set of actions according to probability + * %OVS_SAMPLE_ATTR_PROBABILITY. + * + * Only a single field can be set with a single %OVS_ACTION_ATTR_{SET,PUSH}. + */ + enum ovs_action_attr { OVS_ACTION_ATTR_UNSPEC, OVS_ACTION_ATTR_OUTPUT, /* Output to switch port. */ OVS_ACTION_ATTR_USERSPACE, /* Nested OVS_USERSPACE_ATTR_*. */ - OVS_ACTION_ATTR_PUSH_VLAN, /* Set the 802.1q TCI value. */ - OVS_ACTION_ATTR_POP_VLAN, /* Strip the 802.1q header. */ - OVS_ACTION_ATTR_SET_DL_SRC, /* Ethernet source address. */ - OVS_ACTION_ATTR_SET_DL_DST, /* Ethernet destination address. */ - OVS_ACTION_ATTR_SET_NW_SRC, /* IPv4 source address. */ - OVS_ACTION_ATTR_SET_NW_DST, /* IPv4 destination address. */ - OVS_ACTION_ATTR_SET_NW_TOS, /* IP ToS/DSCP field (6 bits). */ - OVS_ACTION_ATTR_SET_TP_SRC, /* TCP/UDP source port. */ - OVS_ACTION_ATTR_SET_TP_DST, /* TCP/UDP destination port. */ - OVS_ACTION_ATTR_SET_TUNNEL, /* Set the encapsulating tunnel ID. */ + OVS_ACTION_ATTR_PUSH, /* Push packet header. */ + OVS_ACTION_ATTR_POP, /* Pop packet header. */ + OVS_ACTION_ATTR_SET, /* Set packet field(s). */ OVS_ACTION_ATTR_SET_PRIORITY, /* Set skb->priority. */ OVS_ACTION_ATTR_POP_PRIORITY, /* Restore original skb->priority. */ OVS_ACTION_ATTR_SAMPLE, /* Nested OVS_SAMPLE_ATTR_*. */ diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index f78cda26..babbce04 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -149,10 +149,10 @@ static int dpif_netdev_open(const struct dpif_class *, const char *name, static int dp_netdev_output_userspace(struct dp_netdev *, const struct ofpbuf *, int queue_no, const struct flow *, uint64_t arg); -static int dp_netdev_execute_actions(struct dp_netdev *, - struct ofpbuf *, struct flow *, - const struct nlattr *actions, - size_t actions_len); +static void dp_netdev_execute_actions(struct dp_netdev *, + struct ofpbuf *, struct flow *, + const struct nlattr *actions, + size_t actions_len); static struct dpif_class dpif_dummy_class; @@ -904,8 +904,8 @@ dpif_netdev_execute(struct dpif *dpif, flow_extract(©, 0, -1, &key); error = dpif_netdev_flow_from_nlattrs(key_attrs, key_len, &key); if (!error) { - error = dp_netdev_execute_actions(dp, ©, &key, - actions, actions_len); + dp_netdev_execute_actions(dp, ©, &key, + actions, actions_len); } ofpbuf_uninit(©); @@ -1076,94 +1076,94 @@ dp_netdev_pop_vlan(struct ofpbuf *packet) } static void -dp_netdev_set_dl_src(struct ofpbuf *packet, const uint8_t dl_addr[ETH_ADDR_LEN]) +dp_netdev_set_dl(struct ofpbuf *packet, const struct ovs_key_ethernet *eth_key) { struct eth_header *eh = packet->l2; - memcpy(eh->eth_src, dl_addr, sizeof eh->eth_src); + + memcpy(eh->eth_src, eth_key->eth_src, sizeof eh->eth_src); + memcpy(eh->eth_dst, eth_key->eth_dst, sizeof eh->eth_dst); } static void -dp_netdev_set_dl_dst(struct ofpbuf *packet, const uint8_t dl_addr[ETH_ADDR_LEN]) +dp_netdev_set_ip_addr(struct ofpbuf *packet, ovs_be32 *addr, ovs_be32 new_addr) { - struct eth_header *eh = packet->l2; - memcpy(eh->eth_dst, dl_addr, sizeof eh->eth_dst); + struct ip_header *nh = packet->l3; + + if (nh->ip_proto == IPPROTO_TCP && packet->l7) { + struct tcp_header *th = packet->l4; + th->tcp_csum = recalc_csum32(th->tcp_csum, *addr, new_addr); + } else if (nh->ip_proto == IPPROTO_UDP && packet->l7) { + struct udp_header *uh = packet->l4; + if (uh->udp_csum) { + uh->udp_csum = recalc_csum32(uh->udp_csum, *addr, new_addr); + if (!uh->udp_csum) { + uh->udp_csum = htons(0xffff); + } + } + } + nh->ip_csum = recalc_csum32(nh->ip_csum, *addr, new_addr); + *addr = new_addr; } -static bool -is_ip(const struct ofpbuf *packet, const struct flow *key) +static void +dp_netdev_set_ip_tos(struct ip_header *nh, uint8_t new_tos) { - return key->dl_type == htons(ETH_TYPE_IP) && packet->l4; + uint8_t *field = &nh->ip_tos; + + /* Set the DSCP bits and preserve the ECN bits. */ + uint8_t new = new_tos | (nh->ip_tos & IP_ECN_MASK); + + nh->ip_csum = recalc_csum16(nh->ip_csum, htons((uint16_t)*field), + htons((uint16_t) new)); + *field = new; } static void -dp_netdev_set_nw_addr(struct ofpbuf *packet, const struct flow *key, - const struct nlattr *a) -{ - if (is_ip(packet, key)) { - struct ip_header *nh = packet->l3; - ovs_be32 ip = nl_attr_get_be32(a); - uint16_t type = nl_attr_type(a); - ovs_be32 *field; - - field = type == OVS_ACTION_ATTR_SET_NW_SRC ? &nh->ip_src : &nh->ip_dst; - if (key->nw_proto == IPPROTO_TCP && packet->l7) { - struct tcp_header *th = packet->l4; - th->tcp_csum = recalc_csum32(th->tcp_csum, *field, ip); - } else if (key->nw_proto == IPPROTO_UDP && packet->l7) { - struct udp_header *uh = packet->l4; - if (uh->udp_csum) { - uh->udp_csum = recalc_csum32(uh->udp_csum, *field, ip); - if (!uh->udp_csum) { - uh->udp_csum = htons(0xffff); - } - } - } - nh->ip_csum = recalc_csum32(nh->ip_csum, *field, ip); - *field = ip; +dp_netdev_set_ipv4(struct ofpbuf *packet, const struct ovs_key_ipv4 *ipv4_key) +{ + struct ip_header *nh = packet->l3; + + if (nh->ip_src != ipv4_key->ipv4_src) { + dp_netdev_set_ip_addr(packet, &nh->ip_src, ipv4_key->ipv4_src); + } + if (nh->ip_dst != ipv4_key->ipv4_dst) { + dp_netdev_set_ip_addr(packet, &nh->ip_dst, ipv4_key->ipv4_dst); + } + if (nh->ip_tos != ipv4_key->ipv4_tos) { + dp_netdev_set_ip_tos(nh, ipv4_key->ipv4_tos); } } static void -dp_netdev_set_nw_tos(struct ofpbuf *packet, const struct flow *key, - uint8_t nw_tos) +dp_netdev_set_port(ovs_be16 *port, ovs_be16 new_port, ovs_be16 *csum) { - if (is_ip(packet, key)) { - struct ip_header *nh = packet->l3; - uint8_t *field = &nh->ip_tos; + *csum = recalc_csum16(*csum, *port, new_port); + *port = new_port; +} - /* Set the DSCP bits and preserve the ECN bits. */ - uint8_t new = nw_tos | (nh->ip_tos & IP_ECN_MASK); +static void +dp_netdev_set_tcp_port(struct ofpbuf *packet, const struct ovs_key_tcp *tcp_key) +{ + struct tcp_header *th = packet->l4; - nh->ip_csum = recalc_csum16(nh->ip_csum, htons((uint16_t)*field), - htons((uint16_t) new)); - *field = new; + if (th->tcp_src != tcp_key->tcp_src) { + dp_netdev_set_port(&th->tcp_src, tcp_key->tcp_src, &th->tcp_csum); + } + if (th->tcp_dst != tcp_key->tcp_dst) { + dp_netdev_set_port(&th->tcp_dst, tcp_key->tcp_dst, &th->tcp_csum); } } static void -dp_netdev_set_tp_port(struct ofpbuf *packet, const struct flow *key, - const struct nlattr *a) -{ - if (is_ip(packet, key)) { - uint16_t type = nl_attr_type(a); - ovs_be16 port = nl_attr_get_be16(a); - ovs_be16 *field; - - if (key->nw_proto == IPPROTO_TCP && packet->l7) { - struct tcp_header *th = packet->l4; - field = (type == OVS_ACTION_ATTR_SET_TP_SRC - ? &th->tcp_src : &th->tcp_dst); - th->tcp_csum = recalc_csum16(th->tcp_csum, *field, port); - *field = port; - } else if (key->nw_proto == IPPROTO_UDP && packet->l7) { - struct udp_header *uh = packet->l4; - field = (type == OVS_ACTION_ATTR_SET_TP_SRC - ? &uh->udp_src : &uh->udp_dst); - uh->udp_csum = recalc_csum16(uh->udp_csum, *field, port); - *field = port; - } else { - return; - } +dp_netdev_set_udp_port(struct ofpbuf *packet, const struct ovs_key_udp *udp_key) +{ + struct udp_header *uh = packet->l4; + + if (uh->udp_src != udp_key->udp_src) { + dp_netdev_set_port(&uh->udp_src, udp_key->udp_src, &uh->udp_csum); + } + if (uh->udp_dst != udp_key->udp_dst) { + dp_netdev_set_port(&uh->udp_dst, udp_key->udp_dst, &uh->udp_csum); } } @@ -1257,7 +1257,50 @@ dp_netdev_action_userspace(struct dp_netdev *dp, dp_netdev_output_userspace(dp, packet, DPIF_UC_ACTION, key, userdata); } -static int +static void +execute_set_action(struct ofpbuf *packet, const struct nlattr *a) +{ + enum ovs_key_attr type = nl_attr_type(a); + switch (type) { + case OVS_KEY_ATTR_TUN_ID: + break; + + case OVS_KEY_ATTR_ETHERNET: + dp_netdev_set_dl(packet, + nl_attr_get_unspec(a, sizeof(struct ovs_key_ethernet))); + break; + + case OVS_KEY_ATTR_IPV4: + dp_netdev_set_ipv4(packet, + nl_attr_get_unspec(a, sizeof(struct ovs_key_ipv4))); + break; + + case OVS_KEY_ATTR_TCP: + dp_netdev_set_tcp_port(packet, + nl_attr_get_unspec(a, sizeof(struct ovs_key_tcp))); + break; + + case OVS_KEY_ATTR_UDP: + dp_netdev_set_udp_port(packet, + nl_attr_get_unspec(a, sizeof(struct ovs_key_udp))); + break; + + case OVS_KEY_ATTR_UNSPEC: + 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_ICMP: + case OVS_KEY_ATTR_ICMPV6: + case OVS_KEY_ATTR_ARP: + case OVS_KEY_ATTR_ND: + case __OVS_KEY_ATTR_MAX: + default: + NOT_REACHED(); + } +} + +static void dp_netdev_execute_actions(struct dp_netdev *dp, struct ofpbuf *packet, struct flow *key, const struct nlattr *actions, @@ -1267,6 +1310,8 @@ dp_netdev_execute_actions(struct dp_netdev *dp, unsigned int left; NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) { + const struct nlattr *nested; + const struct ovs_key_8021q *q_key; int type = nl_attr_type(a); switch ((enum ovs_action_attr) type) { @@ -1278,41 +1323,26 @@ dp_netdev_execute_actions(struct dp_netdev *dp, dp_netdev_action_userspace(dp, packet, key, a); break; - case OVS_ACTION_ATTR_PUSH_VLAN: - eth_push_vlan(packet, nl_attr_get_be16(a)); + 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); break; - case OVS_ACTION_ATTR_POP_VLAN: + case OVS_ACTION_ATTR_POP: + assert(nl_attr_get_u16(a) == OVS_KEY_ATTR_8021Q); dp_netdev_pop_vlan(packet); break; - case OVS_ACTION_ATTR_SET_DL_SRC: - dp_netdev_set_dl_src(packet, nl_attr_get_unspec(a, ETH_ADDR_LEN)); - break; - - case OVS_ACTION_ATTR_SET_DL_DST: - dp_netdev_set_dl_dst(packet, nl_attr_get_unspec(a, ETH_ADDR_LEN)); - break; - - case OVS_ACTION_ATTR_SET_NW_SRC: - case OVS_ACTION_ATTR_SET_NW_DST: - dp_netdev_set_nw_addr(packet, key, a); - break; - - case OVS_ACTION_ATTR_SET_NW_TOS: - dp_netdev_set_nw_tos(packet, key, nl_attr_get_u8(a)); - break; - - case OVS_ACTION_ATTR_SET_TP_SRC: - case OVS_ACTION_ATTR_SET_TP_DST: - dp_netdev_set_tp_port(packet, key, a); + case OVS_ACTION_ATTR_SET: + execute_set_action(packet, nl_attr_get(a)); break; case OVS_ACTION_ATTR_SAMPLE: dp_netdev_sample(dp, packet, key, a); break; - case OVS_ACTION_ATTR_SET_TUNNEL: case OVS_ACTION_ATTR_SET_PRIORITY: case OVS_ACTION_ATTR_POP_PRIORITY: /* not implemented */ @@ -1323,7 +1353,6 @@ dp_netdev_execute_actions(struct dp_netdev *dp, NOT_REACHED(); } } - return 0; } const struct dpif_class dpif_netdev_class = { diff --git a/lib/odp-util.c b/lib/odp-util.c index a4710991..db3535db 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -33,6 +33,9 @@ #include "timeval.h" #include "util.h" +static void +format_odp_key_attr(const struct nlattr *a, struct ds *ds); + /* The interface between userspace and kernel uses an "OVS_*" prefix. * Since this is fairly non-specific for the OVS userspace components, * "ODP_*" (Open vSwitch Datapath) is used as the prefix for @@ -48,7 +51,7 @@ * - For an action with a variable-length argument, returns -2. * * - For an invalid 'type', returns -1. */ -int +static int odp_action_len(uint16_t type) { if (type > OVS_ACTION_ATTR_MAX) { @@ -58,16 +61,9 @@ odp_action_len(uint16_t type) switch ((enum ovs_action_attr) type) { case OVS_ACTION_ATTR_OUTPUT: return 4; case OVS_ACTION_ATTR_USERSPACE: return -2; - case OVS_ACTION_ATTR_PUSH_VLAN: return 2; - case OVS_ACTION_ATTR_POP_VLAN: return 0; - case OVS_ACTION_ATTR_SET_DL_SRC: return ETH_ADDR_LEN; - case OVS_ACTION_ATTR_SET_DL_DST: return ETH_ADDR_LEN; - case OVS_ACTION_ATTR_SET_NW_SRC: return 4; - case OVS_ACTION_ATTR_SET_NW_DST: return 4; - case OVS_ACTION_ATTR_SET_NW_TOS: return 1; - case OVS_ACTION_ATTR_SET_TP_SRC: return 2; - case OVS_ACTION_ATTR_SET_TP_DST: return 2; - case OVS_ACTION_ATTR_SET_TUNNEL: return 8; + case OVS_ACTION_ATTR_PUSH: return -2; + case OVS_ACTION_ATTR_POP: return 2; + case OVS_ACTION_ATTR_SET: return -2; case OVS_ACTION_ATTR_SET_PRIORITY: return 4; case OVS_ACTION_ATTR_POP_PRIORITY: return 0; case OVS_ACTION_ATTR_SAMPLE: return -2; @@ -170,12 +166,11 @@ format_odp_userspace_action(struct ds *ds, const struct nlattr *attr) } -void +static void format_odp_action(struct ds *ds, const struct nlattr *a) { - const uint8_t *eth; int expected_len; - ovs_be32 ip; + enum ovs_action_attr type = nl_attr_type(a); expected_len = odp_action_len(nl_attr_type(a)); if (expected_len != -2 && nl_attr_get_size(a) != expected_len) { @@ -185,49 +180,30 @@ format_odp_action(struct ds *ds, const struct nlattr *a) return; } - switch (nl_attr_type(a)) { + switch (type) { + case OVS_ACTION_ATTR_OUTPUT: ds_put_format(ds, "%"PRIu16, nl_attr_get_u32(a)); break; case OVS_ACTION_ATTR_USERSPACE: format_odp_userspace_action(ds, a); break; - case OVS_ACTION_ATTR_SET_TUNNEL: - ds_put_format(ds, "set_tunnel(%#"PRIx64")", - ntohll(nl_attr_get_be64(a))); - break; - case OVS_ACTION_ATTR_PUSH_VLAN: - ds_put_format(ds, "push_vlan(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_ACTION_ATTR_POP_VLAN: - ds_put_format(ds, "pop_vlan"); + case OVS_ACTION_ATTR_SET: + ds_put_cstr(ds, "set("); + format_odp_key_attr(nl_attr_get(a), ds); + ds_put_cstr(ds, ")"); break; - case OVS_ACTION_ATTR_SET_DL_SRC: - eth = nl_attr_get_unspec(a, ETH_ADDR_LEN); - ds_put_format(ds, "set_dl_src("ETH_ADDR_FMT")", ETH_ADDR_ARGS(eth)); + case OVS_ACTION_ATTR_PUSH: + ds_put_cstr(ds, "push("); + format_odp_key_attr(nl_attr_get(a), ds); + ds_put_cstr(ds, ")"); break; - case OVS_ACTION_ATTR_SET_DL_DST: - eth = nl_attr_get_unspec(a, ETH_ADDR_LEN); - ds_put_format(ds, "set_dl_dst("ETH_ADDR_FMT")", ETH_ADDR_ARGS(eth)); - break; - case OVS_ACTION_ATTR_SET_NW_SRC: - ip = nl_attr_get_be32(a); - ds_put_format(ds, "set_nw_src("IP_FMT")", IP_ARGS(&ip)); - break; - case OVS_ACTION_ATTR_SET_NW_DST: - ip = nl_attr_get_be32(a); - ds_put_format(ds, "set_nw_dst("IP_FMT")", IP_ARGS(&ip)); - break; - case OVS_ACTION_ATTR_SET_NW_TOS: - ds_put_format(ds, "set_nw_tos(%"PRIu8")", nl_attr_get_u8(a)); - break; - case OVS_ACTION_ATTR_SET_TP_SRC: - ds_put_format(ds, "set_tp_src(%"PRIu16")", ntohs(nl_attr_get_be16(a))); - break; - case OVS_ACTION_ATTR_SET_TP_DST: - ds_put_format(ds, "set_tp_dst(%"PRIu16")", ntohs(nl_attr_get_be16(a))); + case OVS_ACTION_ATTR_POP: + if (nl_attr_get_u16(a) == OVS_KEY_ATTR_8021Q) { + ds_put_cstr(ds, "pop(vlan)"); + } else { + ds_put_format(ds, "pop(key%"PRIu16")", nl_attr_get_u16(a)); + } break; case OVS_ACTION_ATTR_SET_PRIORITY: ds_put_format(ds, "set_priority(%#"PRIx32")", nl_attr_get_u32(a)); @@ -238,6 +214,8 @@ format_odp_action(struct ds *ds, const struct nlattr *a) case OVS_ACTION_ATTR_SAMPLE: format_odp_sample_action(ds, a); break; + case OVS_ACTION_ATTR_UNSPEC: + case __OVS_ACTION_ATTR_MAX: default: format_generic_odp_action(ds, a); break; diff --git a/lib/odp-util.h b/lib/odp-util.h index 04b8885b..8c7d4d14 100644 --- a/lib/odp-util.h +++ b/lib/odp-util.h @@ -58,9 +58,6 @@ odp_port_to_ofp_port(uint16_t odp_port) return odp_port; } } - -int odp_action_len(uint16_t type); -void format_odp_action(struct ds *, const struct nlattr *); void format_odp_actions(struct ds *, const struct nlattr *odp_actions, size_t actions_len); diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 0eeda7e5..ff607f78 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -2603,6 +2603,7 @@ facet_account(struct ofproto_dpif *ofproto, struct facet *facet) 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) { @@ -2611,12 +2612,20 @@ facet_account(struct ofproto_dpif *ofproto, struct facet *facet) } break; - case OVS_ACTION_ATTR_POP_VLAN: - vlan_tci = htons(0); + case OVS_ACTION_ATTR_POP: + if (nl_attr_get_u16(a) == OVS_KEY_ATTR_8021Q) { + vlan_tci = htons(0); + } break; - case OVS_ACTION_ATTR_PUSH_VLAN: - vlan_tci = nl_attr_get_be16(a); + 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; + } break; } } @@ -3279,85 +3288,167 @@ fix_sflow_action(struct action_xlate_ctx *ctx) } static void -commit_vlan_tci(struct action_xlate_ctx *ctx, ovs_be16 vlan_tci) +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) { - struct flow *base = &ctx->base_flow; - struct ofpbuf *odp_actions = ctx->odp_actions; + size_t offset = nl_msg_start_nested(odp_actions, act_type); - if (base->vlan_tci != vlan_tci) { - if (!(vlan_tci & htons(VLAN_CFI))) { - nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN); - } else { - if (base->vlan_tci != htons(0)) { - nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN); - } - nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_PUSH_VLAN, - vlan_tci & ~htons(VLAN_CFI)); - } - base->vlan_tci = vlan_tci; + nl_msg_put_unspec(odp_actions, key_type, key, key_size); + nl_msg_end_nested(odp_actions, offset); +} + +static void +commit_set_tun_id_action(const struct flow *flow, struct flow *base, + struct ofpbuf *odp_actions) +{ + if (base->tun_id == flow->tun_id) { + return; } + 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)); } static void -commit_odp_actions(struct action_xlate_ctx *ctx) +commit_set_ether_addr_action(const struct flow *flow, struct flow *base, + struct ofpbuf *odp_actions) +{ + struct ovs_key_ethernet eth_key; + + if (eth_addr_equals(base->dl_src, flow->dl_src) && + eth_addr_equals(base->dl_dst, flow->dl_dst)) { + return; + } + + memcpy(base->dl_src, flow->dl_src, ETH_ADDR_LEN); + memcpy(base->dl_dst, flow->dl_dst, ETH_ADDR_LEN); + + 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)); +} + +static void +commit_vlan_action(struct action_xlate_ctx *ctx, ovs_be16 new_tci) { - const struct flow *flow = &ctx->flow; struct flow *base = &ctx->base_flow; - struct ofpbuf *odp_actions = ctx->odp_actions; - if (base->tun_id != flow->tun_id) { - nl_msg_put_be64(odp_actions, OVS_ACTION_ATTR_SET_TUNNEL, flow->tun_id); - base->tun_id = flow->tun_id; + if (base->vlan_tci == new_tci) { + return; } - if (base->nw_src != flow->nw_src) { - nl_msg_put_be32(odp_actions, OVS_ACTION_ATTR_SET_NW_SRC, flow->nw_src); - base->nw_src = flow->nw_src; + if (base->vlan_tci & htons(VLAN_CFI)) { + nl_msg_put_u16(ctx->odp_actions, OVS_ACTION_ATTR_POP, + OVS_KEY_ATTR_8021Q); } - if (base->nw_dst != flow->nw_dst) { - nl_msg_put_be32(odp_actions, OVS_ACTION_ATTR_SET_NW_DST, flow->nw_dst); - base->nw_dst = flow->nw_dst; + 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); + + commit_action__(ctx->odp_actions, OVS_ACTION_ATTR_PUSH, + OVS_KEY_ATTR_8021Q, &q_key, sizeof(q_key)); } + base->vlan_tci = new_tci; +} - if (base->nw_tos != flow->nw_tos) { - nl_msg_put_u8(odp_actions, OVS_ACTION_ATTR_SET_NW_TOS, flow->nw_tos); - base->nw_tos = flow->nw_tos; +static void +commit_set_nw_action(const struct flow *flow, struct flow *base, + struct ofpbuf *odp_actions) +{ + struct ovs_key_ipv4 ipv4_key; + + if (base->dl_type != htons(ETH_TYPE_IP) || + !base->nw_src || !base->nw_dst) { + return; + } + + if (base->nw_src == flow->nw_src && + base->nw_dst == flow->nw_dst && + base->nw_tos == flow->nw_tos) { + return; } - commit_vlan_tci(ctx, flow->vlan_tci); + memset(&ipv4_key, 0, sizeof(ipv4_key)); + ipv4_key.ipv4_src = base->nw_src = flow->nw_src; + ipv4_key.ipv4_dst = base->nw_dst = flow->nw_dst; + ipv4_key.ipv4_tos = base->nw_tos = flow->nw_tos; - if (base->tp_src != flow->tp_src) { - nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_SET_TP_SRC, flow->tp_src); - base->tp_src = flow->tp_src; + ipv4_key.ipv4_proto = base->nw_proto; + + commit_action__(odp_actions, OVS_ACTION_ATTR_SET, + OVS_KEY_ATTR_IPV4, &ipv4_key, sizeof(ipv4_key)); +} + +static void +commit_set_port_action(const struct flow *flow, struct flow *base, + struct ofpbuf *odp_actions) +{ + if (!base->tp_src || !base->tp_dst) { + return; } - if (base->tp_dst != flow->tp_dst) { - nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_SET_TP_DST, flow->tp_dst); - base->tp_dst = flow->tp_dst; + if (base->tp_src == flow->tp_src && + base->tp_dst == flow->tp_dst) { + return; } - if (!eth_addr_equals(base->dl_src, flow->dl_src)) { - nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_SET_DL_SRC, - flow->dl_src, ETH_ADDR_LEN); - memcpy(base->dl_src, flow->dl_src, ETH_ADDR_LEN); + if (flow->nw_proto == IPPROTO_TCP) { + struct ovs_key_tcp port_key; + + 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)); + + } 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)); } +} - if (!eth_addr_equals(base->dl_dst, flow->dl_dst)) { - nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_SET_DL_DST, - flow->dl_dst, ETH_ADDR_LEN); - memcpy(base->dl_dst, flow->dl_dst, ETH_ADDR_LEN); +static void +commit_priority_action(struct action_xlate_ctx *ctx) +{ + if (ctx->base_priority == ctx->priority) { + return; } - if (ctx->base_priority != ctx->priority) { - if (ctx->priority) { - nl_msg_put_u32(odp_actions, OVS_ACTION_ATTR_SET_PRIORITY, - ctx->priority); - } else { - nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_PRIORITY); - } - ctx->base_priority = ctx->priority; + if (ctx->priority) { + nl_msg_put_u32(ctx->odp_actions, + OVS_ACTION_ATTR_SET_PRIORITY, ctx->priority); + } else { + nl_msg_put_flag(ctx->odp_actions, OVS_ACTION_ATTR_POP_PRIORITY); } + ctx->base_priority = ctx->priority; +} + +static void +commit_odp_actions(struct action_xlate_ctx *ctx) +{ + const struct flow *flow = &ctx->flow; + struct flow *base = &ctx->base_flow; + struct ofpbuf *odp_actions = ctx->odp_actions; + + commit_set_tun_id_action(flow, base, odp_actions); + commit_set_ether_addr_action(flow, base, odp_actions); + commit_vlan_action(ctx, flow->vlan_tci); + commit_set_nw_action(flow, base, odp_actions); + commit_set_port_action(flow, base, odp_actions); + commit_priority_action(ctx); } static void @@ -4240,7 +4331,7 @@ compose_actions(struct action_xlate_ctx *ctx, uint16_t vlan, if (tci) { tci |= htons(VLAN_CFI); } - commit_vlan_tci(ctx, tci); + commit_vlan_action(ctx, tci); cur_vid = dst->vid; } diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index b5ca08cf..59b14e49 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -75,7 +75,7 @@ in_port=5 actions=set_tunnel:5 AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl -t test-openflowd ofproto/trace br0 'tun_id(0x1),in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], - [Datapath actions: set_tunnel(0x1),1,2,set_tunnel(0x3),3,4 + [Datapath actions: set(tun_id(0x1)),1,2,set(tun_id(0x3)),3,4 ]) OFPROTO_STOP AT_CLEANUP @@ -124,84 +124,84 @@ for tuple in \ "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