datapath: Refactor actions in terms of match fields.
authorPravin B Shelar <pshelar@nicira.com>
Fri, 21 Oct 2011 21:38:54 +0000 (14:38 -0700)
committerPravin B Shelar <pshelar@nicira.com>
Fri, 21 Oct 2011 21:38:54 +0000 (14:38 -0700)
Almost all current actions can be expressed in the form of
push/pop/set <field>, 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 <pshelar@nicira.com>
Acked-by: Jesse Gross <jesse@nicira.com>
Bug #7115

datapath/actions.c
datapath/datapath.c
datapath/flow.c
datapath/flow.h
include/linux/openvswitch.h
lib/dpif-netdev.c
lib/odp-util.c
lib/odp-util.h
ofproto/ofproto-dpif.c
tests/ofproto-dpif.at

index a28e98662db314b23486b48d1f321c1b1a7f1aed..70f48eaed3c412f215690addec95533d668be9dd 100644 (file)
@@ -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:
index 49821507c92827c30239db8087edd92ffb347973..a3be3254776c2b3e4b2f3cdc091fa87ecc606b6b 100644 (file)
@@ -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) {
index 73222954fd3a8c3779308b62a9215178b771924d..7b9cb6117fcf9087079676c3463e0a2ef7443e41 100644 (file)
@@ -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):
index ade8ac80828f6df5546bd62190031f755787526e..96b3b4fe1d6662e1860bcb2af382518e6ed15c7e 100644 (file)
@@ -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 */
index c077f620687e44572290a54ff01a57f14827bc11..acc27693bf82e6b1557a431291a5c9678da6b742 100644 (file)
@@ -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_*. */
index f78cda2616bafe614fa7ed526cde817897517f85..babbce047a97398d34840146c138bb49e1a97b2a 100644 (file)
@@ -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(&copy, 0, -1, &key);
     error = dpif_netdev_flow_from_nlattrs(key_attrs, key_len, &key);
     if (!error) {
-        error = dp_netdev_execute_actions(dp, &copy, &key,
-                                          actions, actions_len);
+        dp_netdev_execute_actions(dp, &copy, &key,
+                                  actions, actions_len);
     }
 
     ofpbuf_uninit(&copy);
@@ -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 = {
index a4710991d3e724619ccce5be06a6bf42cb20a48f..db3535dbd5996e4ab32757a983b8581eb8a2b243 100644 (file)
@@ -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;
index 04b8885bc97e4f2ce6a886ea4cb52387d6b9039d..8c7d4d147f48bcb73d8aef3add064217f00aa9b1 100644 (file)
@@ -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);
 
index 0eeda7e551f230f526a0fbbc0abc0865f912d84d..ff607f786effc6a8877e405a9414b6b1f7b806ae 100644 (file)
@@ -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, &eth_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;
         }
index b5ca08cf9b5d9e9830179873e2e3673b65c1d62a..59b14e49bff74c1a1ca3fb9329f926826e96b56e 100644 (file)
@@ -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