]> git.proxmox.com Git - ovs.git/blobdiff - lib/tc.c
lib/tc: Support matching on ip tos
[ovs.git] / lib / tc.c
index 71334866d8d355d7daef8fb61d67dc75785f6500..ec7efff69cad71c2cc6a1eea06c397015be54114 100644 (file)
--- a/lib/tc.c
+++ b/lib/tc.c
 
 #define MAX_PEDIT_OFFSETS 32
 
+#ifndef TCM_IFINDEX_MAGIC_BLOCK
+#define TCM_IFINDEX_MAGIC_BLOCK (0xFFFFFFFFU)
+#endif
+
+#if TCA_MAX < 14
+#define TCA_INGRESS_BLOCK 13
+#endif
+
 VLOG_DEFINE_THIS_MODULE(tc);
 
 static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(60, 5);
@@ -173,10 +181,14 @@ tc_transact(struct ofpbuf *request, struct ofpbuf **replyp)
  * The configuration and stats may be seen with the following command:
  *     /sbin/tc -s qdisc show dev <devname>
  *
+ * If block_id is greater than 0, then the ingress qdisc is added to a block.
+ * In this case, it is equivalent to running (when 'add' is true):
+ *     /sbin/tc qdisc add dev <devname> ingress_block <block_id> ingress
+ *
  * Returns 0 if successful, otherwise a positive errno value.
  */
 int
-tc_add_del_ingress_qdisc(int ifindex, bool add)
+tc_add_del_ingress_qdisc(int ifindex, bool add, uint32_t block_id)
 {
     struct ofpbuf request;
     struct tcmsg *tcmsg;
@@ -189,6 +201,9 @@ tc_add_del_ingress_qdisc(int ifindex, bool add)
     tcmsg->tcm_parent = TC_H_INGRESS;
     nl_msg_put_string(&request, TCA_KIND, "ingress");
     nl_msg_put_unspec(&request, TCA_OPTIONS, NULL, 0);
+    if (block_id) {
+        nl_msg_put_u32(&request, TCA_INGRESS_BLOCK, block_id);
+    }
 
     error = tc_transact(&request, NULL);
     if (error) {
@@ -287,10 +302,17 @@ static const struct nl_policy tca_flower_policy[] = {
                                 .optional = true, },
     [TCA_FLOWER_KEY_IP_TTL_MASK] = { .type = NL_A_U8,
                                      .optional = true, },
+    [TCA_FLOWER_KEY_IP_TOS] = { .type = NL_A_U8,
+                                .optional = true, },
+    [TCA_FLOWER_KEY_IP_TOS_MASK] = { .type = NL_A_U8,
+                                     .optional = true, },
     [TCA_FLOWER_KEY_TCP_FLAGS] = { .type = NL_A_U16,
                                    .optional = true, },
     [TCA_FLOWER_KEY_TCP_FLAGS_MASK] = { .type = NL_A_U16,
                                         .optional = true, },
+    [TCA_FLOWER_KEY_CVLAN_ID] = { .type = NL_A_U16, .optional = true, },
+    [TCA_FLOWER_KEY_CVLAN_PRIO] = { .type = NL_A_U8, .optional = true, },
+    [TCA_FLOWER_KEY_CVLAN_ETH_TYPE] = { .type = NL_A_U16, .optional = true, },
 };
 
 static void
@@ -317,21 +339,44 @@ nl_parse_flower_eth(struct nlattr **attrs, struct tc_flower *flower)
 static void
 nl_parse_flower_vlan(struct nlattr **attrs, struct tc_flower *flower)
 {
-    if (flower->key.eth_type != htons(ETH_TYPE_VLAN)) {
+    ovs_be16 encap_ethtype;
+
+    if (!eth_type_vlan(flower->key.eth_type)) {
         return;
     }
 
-    flower->key.encap_eth_type =
+    flower->key.encap_eth_type[0] =
         nl_attr_get_be16(attrs[TCA_FLOWER_KEY_ETH_TYPE]);
 
     if (attrs[TCA_FLOWER_KEY_VLAN_ID]) {
-        flower->key.vlan_id =
+        flower->key.vlan_id[0] =
             nl_attr_get_u16(attrs[TCA_FLOWER_KEY_VLAN_ID]);
     }
     if (attrs[TCA_FLOWER_KEY_VLAN_PRIO]) {
-        flower->key.vlan_prio =
+        flower->key.vlan_prio[0] =
             nl_attr_get_u8(attrs[TCA_FLOWER_KEY_VLAN_PRIO]);
     }
+
+    if (!attrs[TCA_FLOWER_KEY_VLAN_ETH_TYPE]) {
+        return;
+    }
+
+    encap_ethtype = nl_attr_get_be16(attrs[TCA_FLOWER_KEY_VLAN_ETH_TYPE]);
+    if (!eth_type_vlan(encap_ethtype)) {
+        return;
+    }
+
+    flower->key.encap_eth_type[1] = flower->key.encap_eth_type[0];
+    flower->key.encap_eth_type[0] = encap_ethtype;
+
+    if (attrs[TCA_FLOWER_KEY_CVLAN_ID]) {
+        flower->key.vlan_id[1] =
+            nl_attr_get_u16(attrs[TCA_FLOWER_KEY_CVLAN_ID]);
+    }
+    if (attrs[TCA_FLOWER_KEY_CVLAN_PRIO]) {
+        flower->key.vlan_prio[1] =
+            nl_attr_get_u8(attrs[TCA_FLOWER_KEY_CVLAN_PRIO]);
+    }
 }
 
 static void
@@ -456,6 +501,11 @@ nl_parse_flower_ip(struct nlattr **attrs, struct tc_flower *flower) {
         key->ip_ttl = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_TTL]);
         mask->ip_ttl = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_TTL_MASK]);
     }
+
+    if (attrs[TCA_FLOWER_KEY_IP_TOS_MASK]) {
+        key->ip_tos = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_TOS]);
+        mask->ip_tos = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_TOS_MASK]);
+    }
 }
 
 static enum tc_offloaded_state
@@ -769,9 +819,11 @@ nl_parse_act_vlan(struct nlattr *options, struct tc_flower *flower)
     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_tpid = vlan_attrs[TCA_VLAN_PUSH_VLAN_PROTOCOL];
         struct nlattr *vlan_id = vlan_attrs[TCA_VLAN_PUSH_VLAN_ID];
         struct nlattr *vlan_prio = vlan_attrs[TCA_VLAN_PUSH_VLAN_PRIORITY];
 
+        action->vlan.vlan_push_tpid = nl_attr_get_be16(vlan_tpid);
         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;
@@ -1007,13 +1059,15 @@ parse_netlink_to_tc_flower(struct ofpbuf *reply, struct tc_flower *flower)
 }
 
 int
-tc_dump_flower_start(int ifindex, struct nl_dump *dump)
+tc_dump_flower_start(int ifindex, struct nl_dump *dump, uint32_t block_id)
 {
     struct ofpbuf request;
     struct tcmsg *tcmsg;
+    int index;
 
-    tcmsg = tc_make_request(ifindex, RTM_GETTFILTER, NLM_F_DUMP, &request);
-    tcmsg->tcm_parent = TC_INGRESS_PARENT;
+    index = block_id ? TCM_IFINDEX_MAGIC_BLOCK : ifindex;
+    tcmsg = tc_make_request(index, RTM_GETTFILTER, NLM_F_DUMP, &request);
+    tcmsg->tcm_parent = block_id ? : TC_INGRESS_PARENT;
     tcmsg->tcm_info = TC_H_UNSPEC;
     tcmsg->tcm_handle = 0;
 
@@ -1024,28 +1078,32 @@ tc_dump_flower_start(int ifindex, struct nl_dump *dump)
 }
 
 int
-tc_flush(int ifindex)
+tc_flush(int ifindex, uint32_t block_id)
 {
     struct ofpbuf request;
     struct tcmsg *tcmsg;
+    int index;
 
-    tcmsg = tc_make_request(ifindex, RTM_DELTFILTER, NLM_F_ACK, &request);
-    tcmsg->tcm_parent = TC_INGRESS_PARENT;
+    index = block_id ? TCM_IFINDEX_MAGIC_BLOCK : ifindex;
+    tcmsg = tc_make_request(index, RTM_DELTFILTER, NLM_F_ACK, &request);
+    tcmsg->tcm_parent = block_id ? : TC_INGRESS_PARENT;
     tcmsg->tcm_info = TC_H_UNSPEC;
 
     return tc_transact(&request, NULL);
 }
 
 int
-tc_del_filter(int ifindex, int prio, int handle)
+tc_del_filter(int ifindex, int prio, int handle, uint32_t block_id)
 {
     struct ofpbuf request;
     struct tcmsg *tcmsg;
     struct ofpbuf *reply;
     int error;
+    int index;
 
-    tcmsg = tc_make_request(ifindex, RTM_DELTFILTER, NLM_F_ECHO, &request);
-    tcmsg->tcm_parent = TC_INGRESS_PARENT;
+    index = block_id ? TCM_IFINDEX_MAGIC_BLOCK : ifindex;
+    tcmsg = tc_make_request(index, RTM_DELTFILTER, NLM_F_ECHO, &request);
+    tcmsg->tcm_parent = block_id ? : TC_INGRESS_PARENT;
     tcmsg->tcm_info = tc_make_handle(prio, 0);
     tcmsg->tcm_handle = handle;
 
@@ -1057,15 +1115,18 @@ tc_del_filter(int ifindex, int prio, int handle)
 }
 
 int
-tc_get_flower(int ifindex, int prio, int handle, struct tc_flower *flower)
+tc_get_flower(int ifindex, int prio, int handle, struct tc_flower *flower,
+              uint32_t block_id)
 {
     struct ofpbuf request;
     struct tcmsg *tcmsg;
     struct ofpbuf *reply;
     int error;
+    int index;
 
-    tcmsg = tc_make_request(ifindex, RTM_GETTFILTER, NLM_F_ECHO, &request);
-    tcmsg->tcm_parent = TC_INGRESS_PARENT;
+    index = block_id ? TCM_IFINDEX_MAGIC_BLOCK : ifindex;
+    tcmsg = tc_make_request(index, RTM_GETTFILTER, NLM_F_ECHO, &request);
+    tcmsg->tcm_parent = block_id ? : TC_INGRESS_PARENT;
     tcmsg->tcm_info = tc_make_handle(prio, 0);
     tcmsg->tcm_handle = handle;
 
@@ -1134,7 +1195,8 @@ nl_msg_put_act_pedit(struct ofpbuf *request, struct tc_pedit *parm,
 }
 
 static void
-nl_msg_put_act_push_vlan(struct ofpbuf *request, uint16_t vid, uint8_t prio)
+nl_msg_put_act_push_vlan(struct ofpbuf *request, ovs_be16 tpid,
+                         uint16_t vid, uint8_t prio)
 {
     size_t offset;
 
@@ -1145,6 +1207,7 @@ nl_msg_put_act_push_vlan(struct ofpbuf *request, uint16_t vid, uint8_t prio)
                                 .v_action = TCA_VLAN_ACT_PUSH };
 
         nl_msg_put_unspec(request, TCA_VLAN_PARMS, &parm, sizeof parm);
+        nl_msg_put_be16(request, TCA_VLAN_PUSH_VLAN_PROTOCOL, tpid);
         nl_msg_put_u16(request, TCA_VLAN_PUSH_VLAN_ID, vid);
         nl_msg_put_u8(request, TCA_VLAN_PUSH_VLAN_PRIORITY, prio);
     }
@@ -1465,6 +1528,7 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
             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_tpid,
                                          action->vlan.vlan_push_id,
                                          action->vlan.vlan_push_prio);
                 nl_msg_end_nested(request, act_offset);
@@ -1547,7 +1611,8 @@ nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower)
 {
 
     uint16_t host_eth_type = ntohs(flower->key.eth_type);
-    bool is_vlan = (host_eth_type == ETH_TYPE_VLAN);
+    bool is_vlan = eth_type_vlan(flower->key.eth_type);
+    bool is_qinq = is_vlan && eth_type_vlan(flower->key.encap_eth_type[0]);
     int err;
 
     /* need to parse acts first as some acts require changing the matching
@@ -1558,13 +1623,20 @@ nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower)
     }
 
     if (is_vlan) {
-        host_eth_type = ntohs(flower->key.encap_eth_type);
+        if (is_qinq) {
+            host_eth_type = ntohs(flower->key.encap_eth_type[1]);
+        } else {
+            host_eth_type = ntohs(flower->key.encap_eth_type[0]);
+        }
     }
 
     FLOWER_PUT_MASKED_VALUE(dst_mac, TCA_FLOWER_KEY_ETH_DST);
     FLOWER_PUT_MASKED_VALUE(src_mac, TCA_FLOWER_KEY_ETH_SRC);
 
     if (host_eth_type == ETH_P_IP || host_eth_type == ETH_P_IPV6) {
+        FLOWER_PUT_MASKED_VALUE(ip_ttl, TCA_FLOWER_KEY_IP_TTL);
+        FLOWER_PUT_MASKED_VALUE(ip_tos, TCA_FLOWER_KEY_IP_TOS);
+
         if (flower->mask.ip_proto && flower->key.ip_proto) {
             nl_msg_put_u8(request, TCA_FLOWER_KEY_IP_PROTO,
                           flower->key.ip_proto);
@@ -1593,7 +1665,6 @@ nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower)
     if (host_eth_type == ETH_P_IP) {
             FLOWER_PUT_MASKED_VALUE(ipv4.ipv4_src, TCA_FLOWER_KEY_IPV4_SRC);
             FLOWER_PUT_MASKED_VALUE(ipv4.ipv4_dst, TCA_FLOWER_KEY_IPV4_DST);
-            FLOWER_PUT_MASKED_VALUE(ip_ttl, TCA_FLOWER_KEY_IP_TTL);
     } else if (host_eth_type == ETH_P_IPV6) {
             FLOWER_PUT_MASKED_VALUE(ipv6.ipv6_src, TCA_FLOWER_KEY_IPV6_SRC);
             FLOWER_PUT_MASKED_VALUE(ipv6.ipv6_dst, TCA_FLOWER_KEY_IPV6_DST);
@@ -1602,15 +1673,28 @@ nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower)
     nl_msg_put_be16(request, TCA_FLOWER_KEY_ETH_TYPE, flower->key.eth_type);
 
     if (is_vlan) {
-        if (flower->key.vlan_id || flower->key.vlan_prio) {
+        if (flower->key.vlan_id[0] || flower->key.vlan_prio[0]) {
             nl_msg_put_u16(request, TCA_FLOWER_KEY_VLAN_ID,
-                           flower->key.vlan_id);
+                           flower->key.vlan_id[0]);
             nl_msg_put_u8(request, TCA_FLOWER_KEY_VLAN_PRIO,
-                          flower->key.vlan_prio);
+                          flower->key.vlan_prio[0]);
         }
-        if (flower->key.encap_eth_type) {
+        if (flower->key.encap_eth_type[0]) {
             nl_msg_put_be16(request, TCA_FLOWER_KEY_VLAN_ETH_TYPE,
-                            flower->key.encap_eth_type);
+                            flower->key.encap_eth_type[0]);
+        }
+
+        if (is_qinq) {
+            if (flower->key.vlan_id[1] || flower->key.vlan_prio[1]) {
+                nl_msg_put_u16(request, TCA_FLOWER_KEY_CVLAN_ID,
+                               flower->key.vlan_id[1]);
+                nl_msg_put_u8(request, TCA_FLOWER_KEY_CVLAN_PRIO,
+                              flower->key.vlan_prio[1]);
+            }
+            if (flower->key.encap_eth_type[1]) {
+                nl_msg_put_be16(request, TCA_FLOWER_KEY_CVLAN_ETH_TYPE,
+                                flower->key.encap_eth_type[1]);
+            }
         }
     }
 
@@ -1625,7 +1709,7 @@ nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower)
 
 int
 tc_replace_flower(int ifindex, uint16_t prio, uint32_t handle,
-                  struct tc_flower *flower)
+                  struct tc_flower *flower, uint32_t block_id)
 {
     struct ofpbuf request;
     struct tcmsg *tcmsg;
@@ -1633,10 +1717,12 @@ tc_replace_flower(int ifindex, uint16_t prio, uint32_t handle,
     int error = 0;
     size_t basic_offset;
     uint16_t eth_type = (OVS_FORCE uint16_t) flower->key.eth_type;
+    int index;
 
-    tcmsg = tc_make_request(ifindex, RTM_NEWTFILTER,
-                            NLM_F_CREATE | NLM_F_ECHO, &request);
-    tcmsg->tcm_parent = TC_INGRESS_PARENT;
+    index = block_id ? TCM_IFINDEX_MAGIC_BLOCK : ifindex;
+    tcmsg = tc_make_request(index, RTM_NEWTFILTER, NLM_F_CREATE | NLM_F_ECHO,
+                            &request);
+    tcmsg->tcm_parent = block_id ? : TC_INGRESS_PARENT;
     tcmsg->tcm_info = tc_make_handle(prio, eth_type);
     tcmsg->tcm_handle = handle;