[ovs-dev] [ovs-dev 1/2] tc: Make the actions order consistent

Chris Mi chrism at mellanox.com
Tue Apr 10 05:18:08 UTC 2018


When OVS DP passes the actions to TC library, we save all the
actions in data structure tc_flower and each action type has its
own field in tc_flower. So when TC library passes the actions to
kernel, actually the actions order is lost.

We add an actions array in tc_flower to keep the actions order
in this patch.

Issue: 1321102
Change-Id: If1d96eb3d1dc3b482667f456d2222dcddb17ac4c
Signed-off-by: Chris Mi <chrism at mellanox.com>
Reviewed-by: Paul Blakey <paulb at mellanox.com>
Reviewed-by: Roi Dayan <roid at mellanox.com>
---
 lib/netdev-tc-offloads.c | 182 +++++++++++++++++++++++++++--------------------
 lib/tc.c                 | 158 ++++++++++++++++++++++++----------------
 lib/tc.h                 |  57 +++++++++------
 3 files changed, 238 insertions(+), 159 deletions(-)

diff --git a/lib/netdev-tc-offloads.c b/lib/netdev-tc-offloads.c
index 6db76801f..101b2ffb1 100644
--- a/lib/netdev-tc-offloads.c
+++ b/lib/netdev-tc-offloads.c
@@ -393,18 +393,14 @@ parse_tc_flower_to_match(struct tc_flower *flower,
                          struct match *match,
                          struct nlattr **actions,
                          struct dpif_flow_stats *stats,
-                         struct ofpbuf *buf) {
+                         struct ofpbuf *buf)
+{
     size_t act_off;
     struct tc_flower_key *key = &flower->key;
     struct tc_flower_key *mask = &flower->mask;
     odp_port_t outport = 0;
-
-    if (flower->ifindex_out) {
-        outport = netdev_ifindex_to_odp_port(flower->ifindex_out);
-        if (!outport) {
-            return ENOENT;
-        }
-    }
+    struct tc_action *action;
+    int i;
 
     ofpbuf_clear(buf);
 
@@ -487,60 +483,71 @@ parse_tc_flower_to_match(struct tc_flower *flower,
 
     act_off = nl_msg_start_nested(buf, OVS_FLOW_ATTR_ACTIONS);
     {
-        if (flower->vlan_pop) {
-            nl_msg_put_flag(buf, OVS_ACTION_ATTR_POP_VLAN);
-        }
-
-        if (flower->vlan_push_id || flower->vlan_push_prio) {
-            struct ovs_action_push_vlan *push;
-            push = nl_msg_put_unspec_zero(buf, OVS_ACTION_ATTR_PUSH_VLAN,
-                                          sizeof *push);
-
-            push->vlan_tpid = htons(ETH_TYPE_VLAN);
-            push->vlan_tci = htons(flower->vlan_push_id
-                                   | (flower->vlan_push_prio << 13)
-                                   | VLAN_CFI);
-        }
-
-        if (flower->rewrite.rewrite) {
-            parse_flower_rewrite_to_netlink_action(buf, flower);
-        }
-
-        if (flower->set.set) {
-            size_t set_offset = nl_msg_start_nested(buf, OVS_ACTION_ATTR_SET);
-            size_t tunnel_offset =
-                nl_msg_start_nested(buf, OVS_KEY_ATTR_TUNNEL);
-
-            nl_msg_put_be64(buf, OVS_TUNNEL_KEY_ATTR_ID, flower->set.id);
-            if (flower->set.ipv4.ipv4_src) {
-                nl_msg_put_be32(buf, OVS_TUNNEL_KEY_ATTR_IPV4_SRC,
-                                flower->set.ipv4.ipv4_src);
-            }
-            if (flower->set.ipv4.ipv4_dst) {
-                nl_msg_put_be32(buf, OVS_TUNNEL_KEY_ATTR_IPV4_DST,
-                                flower->set.ipv4.ipv4_dst);
+        action = flower->actions;
+        for (i = 0; i < flower->action_count; i++, action++) {
+            switch (action->type) {
+            case TC_ACT_VLAN_POP: {
+                nl_msg_put_flag(buf, OVS_ACTION_ATTR_POP_VLAN);
             }
-            if (!is_all_zeros(&flower->set.ipv6.ipv6_src,
-                              sizeof flower->set.ipv6.ipv6_src)) {
-                nl_msg_put_in6_addr(buf, OVS_TUNNEL_KEY_ATTR_IPV6_SRC,
-                                    &flower->set.ipv6.ipv6_src);
+            break;
+            case TC_ACT_VLAN_PUSH: {
+                struct ovs_action_push_vlan *push;
+
+                push = nl_msg_put_unspec_zero(buf, OVS_ACTION_ATTR_PUSH_VLAN,
+                                              sizeof *push);
+                push->vlan_tpid = htons(ETH_TYPE_VLAN);
+                push->vlan_tci = htons(action->vlan.vlan_push_id
+                                       | (action->vlan.vlan_push_prio << 13)
+                                       | VLAN_CFI);
             }
-            if (!is_all_zeros(&flower->set.ipv6.ipv6_dst,
-                              sizeof flower->set.ipv6.ipv6_dst)) {
-                nl_msg_put_in6_addr(buf, OVS_TUNNEL_KEY_ATTR_IPV6_DST,
-                                    &flower->set.ipv6.ipv6_dst);
+            break;
+            case TC_ACT_PEDIT: {
+                parse_flower_rewrite_to_netlink_action(buf, flower);
             }
-            nl_msg_put_be16(buf, OVS_TUNNEL_KEY_ATTR_TP_DST,
-                            flower->set.tp_dst);
-
-            nl_msg_end_nested(buf, tunnel_offset);
-            nl_msg_end_nested(buf, set_offset);
-        }
+            break;
+            case TC_ACT_ENCAP: {
+                size_t set_offset = nl_msg_start_nested(buf, OVS_ACTION_ATTR_SET);
+                size_t tunnel_offset =
+                    nl_msg_start_nested(buf, OVS_KEY_ATTR_TUNNEL);
+
+                nl_msg_put_be64(buf, OVS_TUNNEL_KEY_ATTR_ID, action->encap.id);
+                if (action->encap.ipv4.ipv4_src) {
+                    nl_msg_put_be32(buf, OVS_TUNNEL_KEY_ATTR_IPV4_SRC,
+                                    action->encap.ipv4.ipv4_src);
+                }
+                if (action->encap.ipv4.ipv4_dst) {
+                    nl_msg_put_be32(buf, OVS_TUNNEL_KEY_ATTR_IPV4_DST,
+                                    action->encap.ipv4.ipv4_dst);
+                }
+                if (!is_all_zeros(&action->encap.ipv6.ipv6_src,
+                                  sizeof action->encap.ipv6.ipv6_src)) {
+                    nl_msg_put_in6_addr(buf, OVS_TUNNEL_KEY_ATTR_IPV6_SRC,
+                                        &action->encap.ipv6.ipv6_src);
+                }
+                if (!is_all_zeros(&action->encap.ipv6.ipv6_dst,
+                                  sizeof action->encap.ipv6.ipv6_dst)) {
+                    nl_msg_put_in6_addr(buf, OVS_TUNNEL_KEY_ATTR_IPV6_DST,
+                                        &action->encap.ipv6.ipv6_dst);
+                }
+                nl_msg_put_be16(buf, OVS_TUNNEL_KEY_ATTR_TP_DST,
+                                action->encap.tp_dst);
 
-        if (flower->ifindex_out > 0) {
-            nl_msg_put_u32(buf, OVS_ACTION_ATTR_OUTPUT, odp_to_u32(outport));
+                nl_msg_end_nested(buf, tunnel_offset);
+                nl_msg_end_nested(buf, set_offset);
+            }
+            break;
+            case TC_ACT_OUTPUT: {
+                if (action->ifindex_out) {
+                    outport = netdev_ifindex_to_odp_port(action->ifindex_out);
+                    if (!outport) {
+                        return ENOENT;
+                    }
+                }
+                nl_msg_put_u32(buf, OVS_ACTION_ATTR_OUTPUT, odp_to_u32(outport));
+            }
+            break;
+            }
         }
-
     }
     nl_msg_end_nested(buf, act_off);
 
@@ -597,6 +604,7 @@ netdev_tc_flow_dump_next(struct netdev_flow_dump *dump,
 
 static int
 parse_put_flow_set_masked_action(struct tc_flower *flower,
+                                 struct tc_action *action,
                                  const struct nlattr *set,
                                  size_t set_len,
                                  bool hasmask)
@@ -649,7 +657,11 @@ parse_put_flow_set_masked_action(struct tc_flower *flower,
     }
 
     if (!is_all_zeros(&flower->rewrite, sizeof flower->rewrite)) {
-        flower->rewrite.rewrite = true;
+        if (flower->rewrite.rewrite == false) {
+            flower->rewrite.rewrite = true;
+            action->type = TC_ACT_PEDIT;
+            flower->action_count++;
+        }
     }
 
     if (hasmask && !is_all_zeros(set_mask, size)) {
@@ -664,52 +676,53 @@ parse_put_flow_set_masked_action(struct tc_flower *flower,
 }
 
 static int
-parse_put_flow_set_action(struct tc_flower *flower, const struct nlattr *set,
-                          size_t set_len)
+parse_put_flow_set_action(struct tc_flower *flower, struct tc_action *action,
+                          const struct nlattr *set, size_t set_len)
 {
     const struct nlattr *tunnel;
     const struct nlattr *tun_attr;
     size_t tun_left, tunnel_len;
 
     if (nl_attr_type(set) != OVS_KEY_ATTR_TUNNEL) {
-            return parse_put_flow_set_masked_action(flower, set, set_len,
-                                                    false);
+            return parse_put_flow_set_masked_action(flower, action, set,
+                                                    set_len, false);
     }
 
     tunnel = nl_attr_get(set);
     tunnel_len = nl_attr_get_size(set);
 
-    flower->set.set = true;
+    action->type = TC_ACT_ENCAP;
+    flower->action_count++;
     NL_ATTR_FOR_EACH_UNSAFE(tun_attr, tun_left, tunnel, tunnel_len) {
         switch (nl_attr_type(tun_attr)) {
         case OVS_TUNNEL_KEY_ATTR_ID: {
-            flower->set.id = nl_attr_get_be64(tun_attr);
+            action->encap.id = nl_attr_get_be64(tun_attr);
         }
         break;
         case OVS_TUNNEL_KEY_ATTR_IPV4_SRC: {
-            flower->set.ipv4.ipv4_src = nl_attr_get_be32(tun_attr);
+            action->encap.ipv4.ipv4_src = nl_attr_get_be32(tun_attr);
         }
         break;
         case OVS_TUNNEL_KEY_ATTR_IPV4_DST: {
-            flower->set.ipv4.ipv4_dst = nl_attr_get_be32(tun_attr);
+            action->encap.ipv4.ipv4_dst = nl_attr_get_be32(tun_attr);
         }
         break;
         case OVS_TUNNEL_KEY_ATTR_IPV6_SRC: {
-            flower->set.ipv6.ipv6_src =
+            action->encap.ipv6.ipv6_src =
                 nl_attr_get_in6_addr(tun_attr);
         }
         break;
         case OVS_TUNNEL_KEY_ATTR_IPV6_DST: {
-            flower->set.ipv6.ipv6_dst =
+            action->encap.ipv6.ipv6_dst =
                 nl_attr_get_in6_addr(tun_attr);
         }
         break;
         case OVS_TUNNEL_KEY_ATTR_TP_SRC: {
-            flower->set.tp_src = nl_attr_get_be16(tun_attr);
+            action->encap.tp_src = nl_attr_get_be16(tun_attr);
         }
         break;
         case OVS_TUNNEL_KEY_ATTR_TP_DST: {
-            flower->set.tp_dst = nl_attr_get_be16(tun_attr);
+            action->encap.tp_dst = nl_attr_get_be16(tun_attr);
         }
         break;
         }
@@ -865,6 +878,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
     const struct flow *key = &match->flow;
     struct flow *mask = &match->wc.masks;
     const struct flow_tnl *tnl = &match->flow.tunnel;
+    struct tc_action *action;
     struct nlattr *nla;
     size_t left;
     int prio = 0;
@@ -1019,34 +1033,46 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
     }
 
     NL_ATTR_FOR_EACH(nla, left, actions, actions_len) {
+        if (flower.action_count >= TCA_ACT_MAX_PRIO) {
+            VLOG_DBG_RL(&rl, "Can only support %d actions", flower.action_count);
+            return EOPNOTSUPP;
+        }
+        action = &flower.actions[flower.action_count];
         if (nl_attr_type(nla) == OVS_ACTION_ATTR_OUTPUT) {
             odp_port_t port = nl_attr_get_odp_port(nla);
             struct netdev *outdev = netdev_ports_get(port, info->dpif_class);
 
-            flower.ifindex_out = netdev_get_ifindex(outdev);
-            flower.set.tp_dst = info->tp_dst_port;
+            action->ifindex_out = netdev_get_ifindex(outdev);
+            action->type = TC_ACT_OUTPUT;
+            flower.action_count++;
             netdev_close(outdev);
         } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_PUSH_VLAN) {
             const struct ovs_action_push_vlan *vlan_push = nl_attr_get(nla);
 
-            flower.vlan_push_id = vlan_tci_to_vid(vlan_push->vlan_tci);
-            flower.vlan_push_prio = vlan_tci_to_pcp(vlan_push->vlan_tci);
+            action->vlan.vlan_push_id = vlan_tci_to_vid(vlan_push->vlan_tci);
+            action->vlan.vlan_push_prio = vlan_tci_to_pcp(vlan_push->vlan_tci);
+            action->type = TC_ACT_VLAN_PUSH;
+            flower.action_count++;
         } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_POP_VLAN) {
-            flower.vlan_pop = 1;
+            action->type = TC_ACT_VLAN_POP;
+            flower.action_count++;
         } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_SET) {
             const struct nlattr *set = nl_attr_get(nla);
             const size_t set_len = nl_attr_get_size(nla);
 
-            err = parse_put_flow_set_action(&flower, set, set_len);
+            err = parse_put_flow_set_action(&flower, action, set, set_len);
             if (err) {
                 return err;
             }
+            if (action->type == TC_ACT_ENCAP) {
+                action->encap.tp_dst = info->tp_dst_port;
+            }
         } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_SET_MASKED) {
             const struct nlattr *set = nl_attr_get(nla);
             const size_t set_len = nl_attr_get_size(nla);
 
-            err = parse_put_flow_set_masked_action(&flower, set, set_len,
-                                                   true);
+            err = parse_put_flow_set_masked_action(&flower, action, set,
+                                                   set_len, true);
             if (err) {
                 return err;
             }
diff --git a/lib/tc.c b/lib/tc.c
index 6daa44710..aeec94ac7 100644
--- a/lib/tc.c
+++ b/lib/tc.c
@@ -469,6 +469,7 @@ static const struct nl_policy pedit_policy[] = {
 static int
 nl_parse_act_pedit(struct nlattr *options, struct tc_flower *flower)
 {
+    struct tc_action *action;
     struct nlattr *pe_attrs[ARRAY_SIZE(pedit_policy)];
     const struct tc_pedit *pe;
     const struct tc_pedit_key *keys;
@@ -548,7 +549,8 @@ nl_parse_act_pedit(struct nlattr *options, struct tc_flower *flower)
         i++;
     }
 
-    flower->rewrite.rewrite = true;
+    action = &flower->actions[flower->action_count++];
+    action->type = TC_ACT_PEDIT;
 
     return 0;
 }
@@ -575,6 +577,7 @@ nl_parse_act_tunnel_key(struct nlattr *options, struct tc_flower *flower)
     struct nlattr *tun_attrs[ARRAY_SIZE(tunnel_key_policy)];
     const struct nlattr *tun_parms;
     const struct tc_tunnel_key *tun;
+    struct tc_action *action;
 
     if (!nl_parse_nested(options, tunnel_key_policy, tun_attrs,
                 ARRAY_SIZE(tunnel_key_policy))) {
@@ -592,17 +595,18 @@ nl_parse_act_tunnel_key(struct nlattr *options, struct tc_flower *flower)
         struct nlattr *ipv6_src = tun_attrs[TCA_TUNNEL_KEY_ENC_IPV6_SRC];
         struct nlattr *ipv6_dst = tun_attrs[TCA_TUNNEL_KEY_ENC_IPV6_DST];
 
-        flower->set.set = true;
-        flower->set.ipv4.ipv4_src = ipv4_src ? nl_attr_get_be32(ipv4_src) : 0;
-        flower->set.ipv4.ipv4_dst = ipv4_dst ? nl_attr_get_be32(ipv4_dst) : 0;
+        action = &flower->actions[flower->action_count++];
+        action->type = TC_ACT_ENCAP;
+        action->encap.ipv4.ipv4_src = ipv4_src ? nl_attr_get_be32(ipv4_src) : 0;
+        action->encap.ipv4.ipv4_dst = ipv4_dst ? nl_attr_get_be32(ipv4_dst) : 0;
         if (ipv6_src) {
-            flower->set.ipv6.ipv6_src = nl_attr_get_in6_addr(ipv6_src);
+            action->encap.ipv6.ipv6_src = nl_attr_get_in6_addr(ipv6_src);
         }
         if (ipv6_dst) {
-            flower->set.ipv6.ipv6_dst = nl_attr_get_in6_addr(ipv6_dst);
+            action->encap.ipv6.ipv6_dst = nl_attr_get_in6_addr(ipv6_dst);
         }
-        flower->set.id = id ? be32_to_be64(nl_attr_get_be32(id)) : 0;
-        flower->set.tp_dst = dst_port ? nl_attr_get_be16(dst_port) : 0;
+        action->encap.id = id ? be32_to_be64(nl_attr_get_be32(id)) : 0;
+        action->encap.tp_dst = dst_port ? nl_attr_get_be16(dst_port) : 0;
     } else if (tun->t_action == TCA_TUNNEL_KEY_ACT_RELEASE) {
         flower->tunnel.tunnel = true;
     } else {
@@ -688,6 +692,7 @@ nl_parse_act_mirred(struct nlattr *options, struct tc_flower *flower)
     const struct nlattr *mirred_parms;
     const struct tcf_t *tm;
     struct nlattr *mirred_tm;
+    struct tc_action *action;
 
     if (!nl_parse_nested(options, mirred_policy, mirred_attrs,
                          ARRAY_SIZE(mirred_policy))) {
@@ -698,13 +703,15 @@ nl_parse_act_mirred(struct nlattr *options, struct tc_flower *flower)
     mirred_parms = mirred_attrs[TCA_MIRRED_PARMS];
     m = nl_attr_get_unspec(mirred_parms, sizeof *m);
 
-    if (m->action != TC_ACT_STOLEN ||  m->eaction != TCA_EGRESS_REDIR) {
+    if (m->eaction != TCA_EGRESS_REDIR && m->eaction != TCA_EGRESS_MIRROR) {
         VLOG_ERR_RL(&error_rl, "unknown mirred action: %d, %d, %d",
-                 m->action, m->eaction, m->ifindex);
+                    m->action, m->eaction, m->ifindex);
         return EINVAL;
     }
 
-    flower->ifindex_out = m->ifindex;
+    action = &flower->actions[flower->action_count++];
+    action->ifindex_out = m->ifindex;
+    action->type = TC_ACT_OUTPUT;
 
     mirred_tm = mirred_attrs[TCA_MIRRED_TM];
     tm = nl_attr_get_unspec(mirred_tm, sizeof *tm);
@@ -728,6 +735,7 @@ nl_parse_act_vlan(struct nlattr *options, struct tc_flower *flower)
     struct nlattr *vlan_attrs[ARRAY_SIZE(vlan_policy)];
     const struct tc_vlan *v;
     const struct nlattr *vlan_parms;
+    struct tc_action *action;
 
     if (!nl_parse_nested(options, vlan_policy, vlan_attrs,
                          ARRAY_SIZE(vlan_policy))) {
@@ -735,16 +743,18 @@ nl_parse_act_vlan(struct nlattr *options, struct tc_flower *flower)
         return EPROTO;
     }
 
+    action = &flower->actions[flower->action_count++];
     vlan_parms = vlan_attrs[TCA_VLAN_PARMS];
     v = nl_attr_get_unspec(vlan_parms, sizeof *v);
     if (v->v_action == TCA_VLAN_ACT_PUSH) {
         struct nlattr *vlan_id = vlan_attrs[TCA_VLAN_PUSH_VLAN_ID];
         struct nlattr *vlan_prio = vlan_attrs[TCA_VLAN_PUSH_VLAN_PRIORITY];
 
-        flower->vlan_push_id = nl_attr_get_u16(vlan_id);
-        flower->vlan_push_prio = vlan_prio ? nl_attr_get_u8(vlan_prio) : 0;
+        action->vlan.vlan_push_id = nl_attr_get_u16(vlan_id);
+        action->vlan.vlan_push_prio = vlan_prio ? nl_attr_get_u8(vlan_prio) : 0;
+        action->type = TC_ACT_VLAN_PUSH;
     } else if (v->v_action == TCA_VLAN_ACT_POP) {
-        flower->vlan_pop = 1;
+        action->type = TC_ACT_VLAN_POP;
     } else {
         VLOG_ERR_RL(&error_rl, "unknown vlan action: %d, %d",
                     v->action, v->v_action);
@@ -893,7 +903,13 @@ nl_parse_flower_actions(struct nlattr **attrs, struct tc_flower *flower)
 
     for (int i = TCA_ACT_MIN_PRIO; i < max_size; i++) {
         if (actions_orders[i]) {
-            int err = nl_parse_single_action(actions_orders[i], flower);
+            int err;
+
+            if (flower->action_count >= TCA_ACT_MAX_PRIO) {
+                VLOG_DBG_RL(&error_rl, "Can only support %d actions", flower->action_count);
+                return EOPNOTSUPP;
+            }
+            err = nl_parse_single_action(actions_orders[i], flower);
 
             if (err) {
                 return err;
@@ -1372,64 +1388,84 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
 {
     size_t offset;
     size_t act_offset;
+    uint16_t act_index = 1;
+    struct tc_action *action;
+    int i, ifindex = 0;
 
     offset = nl_msg_start_nested(request, TCA_FLOWER_ACT);
     {
-        uint16_t act_index = 1;
         int error;
 
-        if (flower->rewrite.rewrite) {
+        if (flower->tunnel.tunnel) {
             act_offset = nl_msg_start_nested(request, act_index++);
-            error = nl_msg_put_flower_rewrite_pedits(request, flower);
-            if (error) {
-                return error;
-            }
+            nl_msg_put_act_tunnel_key_release(request);
             nl_msg_end_nested(request, act_offset);
+        }
 
-            if (flower->csum_update_flags) {
+        action = flower->actions;
+        for (i = 0; i < flower->action_count; i++, action++) {
+            switch (action->type) {
+            case TC_ACT_PEDIT: {
                 act_offset = nl_msg_start_nested(request, act_index++);
-                nl_msg_put_act_csum(request, flower->csum_update_flags);
+                error = nl_msg_put_flower_rewrite_pedits(request, flower);
+                if (error) {
+                    return error;
+                }
                 nl_msg_end_nested(request, act_offset);
+
+                if (flower->csum_update_flags) {
+                    act_offset = nl_msg_start_nested(request, act_index++);
+                    nl_msg_put_act_csum(request, flower->csum_update_flags);
+                    nl_msg_end_nested(request, act_offset);
+                }
+            }
+            break;
+            case TC_ACT_ENCAP: {
+                act_offset = nl_msg_start_nested(request, act_index++);
+                nl_msg_put_act_tunnel_key_set(request, action->encap.id,
+                                              action->encap.ipv4.ipv4_src,
+                                              action->encap.ipv4.ipv4_dst,
+                                              &action->encap.ipv6.ipv6_src,
+                                              &action->encap.ipv6.ipv6_dst,
+                                              action->encap.tp_dst);
+                nl_msg_end_nested(request, act_offset);
+            }
+            break;
+            case TC_ACT_VLAN_POP: {
+                act_offset = nl_msg_start_nested(request, act_index++);
+                nl_msg_put_act_pop_vlan(request);
+                nl_msg_end_nested(request, act_offset);
+            }
+            break;
+            case TC_ACT_VLAN_PUSH: {
+                act_offset = nl_msg_start_nested(request, act_index++);
+                nl_msg_put_act_push_vlan(request,
+                                         action->vlan.vlan_push_id,
+                                         action->vlan.vlan_push_prio);
+                nl_msg_end_nested(request, act_offset);
+            }
+            break;
+            case TC_ACT_OUTPUT: {
+                ifindex = action->ifindex_out;
+                if (ifindex < 1) {
+                    VLOG_ERR_RL(&error_rl, "%s: invalid ifindex: %d, type: %d",
+                                __func__, ifindex, action->type);
+                    return EINVAL;
+                }
+                act_offset = nl_msg_start_nested(request, act_index++);
+                nl_msg_put_act_redirect(request, ifindex);
+                nl_msg_put_act_cookie(request, &flower->act_cookie);
+                nl_msg_end_nested(request, act_offset);
+            }
+            break;
             }
         }
-        if (flower->tunnel.tunnel) {
-            act_offset = nl_msg_start_nested(request, act_index++);
-            nl_msg_put_act_tunnel_key_release(request);
-            nl_msg_end_nested(request, act_offset);
-        }
-        if (flower->set.set) {
-            act_offset = nl_msg_start_nested(request, act_index++);
-            nl_msg_put_act_tunnel_key_set(request, flower->set.id,
-                                          flower->set.ipv4.ipv4_src,
-                                          flower->set.ipv4.ipv4_dst,
-                                          &flower->set.ipv6.ipv6_src,
-                                          &flower->set.ipv6.ipv6_dst,
-                                          flower->set.tp_dst);
-            nl_msg_end_nested(request, act_offset);
-        }
-        if (flower->vlan_pop) {
-            act_offset = nl_msg_start_nested(request, act_index++);
-            nl_msg_put_act_pop_vlan(request);
-            nl_msg_end_nested(request, act_offset);
-        }
-        if (flower->vlan_push_id) {
-            act_offset = nl_msg_start_nested(request, act_index++);
-            nl_msg_put_act_push_vlan(request,
-                                     flower->vlan_push_id,
-                                     flower->vlan_push_prio);
-            nl_msg_end_nested(request, act_offset);
-        }
-        if (flower->ifindex_out) {
-            act_offset = nl_msg_start_nested(request, act_index++);
-            nl_msg_put_act_redirect(request, flower->ifindex_out);
-            nl_msg_put_act_cookie(request, &flower->act_cookie);
-            nl_msg_end_nested(request, act_offset);
-        } else {
-            act_offset = nl_msg_start_nested(request, act_index++);
-            nl_msg_put_act_drop(request);
-            nl_msg_put_act_cookie(request, &flower->act_cookie);
-            nl_msg_end_nested(request, act_offset);
-        }
+    }
+    if (!ifindex) {
+        act_offset = nl_msg_start_nested(request, act_index++);
+        nl_msg_put_act_drop(request);
+        nl_msg_put_act_cookie(request, &flower->act_cookie);
+        nl_msg_end_nested(request, act_offset);
     }
     nl_msg_end_nested(request, offset);
 
diff --git a/lib/tc.h b/lib/tc.h
index 4400a829e..963db54dc 100644
--- a/lib/tc.h
+++ b/lib/tc.h
@@ -106,6 +106,41 @@ struct tc_flower_key {
     } ipv6;
 };
 
+enum tc_action_type {
+    TC_ACT_OUTPUT,
+    TC_ACT_ENCAP,
+    TC_ACT_PEDIT,
+    TC_ACT_VLAN_POP,
+    TC_ACT_VLAN_PUSH,
+};
+
+struct tc_action {
+    union {
+        int ifindex_out;
+
+        struct {
+            uint16_t vlan_push_id;
+            uint8_t vlan_push_prio;
+        } vlan;
+
+        struct {
+            ovs_be64 id;
+            ovs_be16 tp_src;
+            ovs_be16 tp_dst;
+            struct {
+                ovs_be32 ipv4_src;
+                ovs_be32 ipv4_dst;
+            } ipv4;
+            struct {
+                struct in6_addr ipv6_src;
+                struct in6_addr ipv6_dst;
+            } ipv6;
+        } encap;
+     };
+
+     enum tc_action_type type;
+};
+
 struct tc_flower {
     uint32_t handle;
     uint32_t prio;
@@ -113,11 +148,8 @@ struct tc_flower {
     struct tc_flower_key key;
     struct tc_flower_key mask;
 
-    uint8_t vlan_pop;
-    uint16_t vlan_push_id;
-    uint8_t vlan_push_prio;
-
-    int ifindex_out;
+    int action_count;
+    struct tc_action actions[TCA_ACT_MAX_PRIO];
 
     struct ovs_flow_stats stats;
     uint64_t lastused;
@@ -130,21 +162,6 @@ struct tc_flower {
 
     uint32_t csum_update_flags;
 
-    struct {
-        bool set;
-        ovs_be64 id;
-        ovs_be16 tp_src;
-        ovs_be16 tp_dst;
-        struct {
-            ovs_be32 ipv4_src;
-            ovs_be32 ipv4_dst;
-        } ipv4;
-        struct {
-            struct in6_addr ipv6_src;
-            struct in6_addr ipv6_dst;
-        } ipv6;
-    } set;
-
     struct {
         bool tunnel;
         struct {
-- 
2.14.3



More information about the dev mailing list