Commit 8ffb055beae58574d3e77b4bf9d4d15eace1ca27

Authored by Yoshiki Komachi
Committed by David S. Miller
1 parent 2f23cd42e1

cls_flower: Fix the behavior using port ranges with hw-offload

The recent commit 5c72299fba9d ("net: sched: cls_flower: Classify
packets using port ranges") had added filtering based on port ranges
to tc flower. However the commit missed necessary changes in hw-offload
code, so the feature gave rise to generating incorrect offloaded flow
keys in NIC.

One more detailed example is below:

$ tc qdisc add dev eth0 ingress
$ tc filter add dev eth0 ingress protocol ip flower ip_proto tcp \
  dst_port 100-200 action drop

With the setup above, an exact match filter with dst_port == 0 will be
installed in NIC by hw-offload. IOW, the NIC will have a rule which is
equivalent to the following one.

$ tc qdisc add dev eth0 ingress
$ tc filter add dev eth0 ingress protocol ip flower ip_proto tcp \
  dst_port 0 action drop

The behavior was caused by the flow dissector which extracts packet
data into the flow key in the tc flower. More specifically, regardless
of exact match or specified port ranges, fl_init_dissector() set the
FLOW_DISSECTOR_KEY_PORTS flag in struct flow_dissector to extract port
numbers from skb in skb_flow_dissect() called by fl_classify(). Note
that device drivers received the same struct flow_dissector object as
used in skb_flow_dissect(). Thus, offloaded drivers could not identify
which of these is used because the FLOW_DISSECTOR_KEY_PORTS flag was
set to struct flow_dissector in either case.

This patch adds the new FLOW_DISSECTOR_KEY_PORTS_RANGE flag and the new
tp_range field in struct fl_flow_key to recognize which filters are applied
to offloaded drivers. At this point, when filters based on port ranges
passed to drivers, drivers return the EOPNOTSUPP error because they do
not support the feature (the newly created FLOW_DISSECTOR_KEY_PORTS_RANGE
flag).

Fixes: 5c72299fba9d ("net: sched: cls_flower: Classify packets using port ranges")
Signed-off-by: Yoshiki Komachi <komachi.yoshiki@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

Showing 3 changed files with 95 additions and 61 deletions Side-by-side Diff

include/net/flow_dissector.h
... ... @@ -235,6 +235,7 @@
235 235 FLOW_DISSECTOR_KEY_IPV4_ADDRS, /* struct flow_dissector_key_ipv4_addrs */
236 236 FLOW_DISSECTOR_KEY_IPV6_ADDRS, /* struct flow_dissector_key_ipv6_addrs */
237 237 FLOW_DISSECTOR_KEY_PORTS, /* struct flow_dissector_key_ports */
  238 + FLOW_DISSECTOR_KEY_PORTS_RANGE, /* struct flow_dissector_key_ports */
238 239 FLOW_DISSECTOR_KEY_ICMP, /* struct flow_dissector_key_icmp */
239 240 FLOW_DISSECTOR_KEY_ETH_ADDRS, /* struct flow_dissector_key_eth_addrs */
240 241 FLOW_DISSECTOR_KEY_TIPC, /* struct flow_dissector_key_tipc */
net/core/flow_dissector.c
... ... @@ -760,6 +760,31 @@
760 760 }
761 761  
762 762 static void
  763 +__skb_flow_dissect_ports(const struct sk_buff *skb,
  764 + struct flow_dissector *flow_dissector,
  765 + void *target_container, void *data, int nhoff,
  766 + u8 ip_proto, int hlen)
  767 +{
  768 + enum flow_dissector_key_id dissector_ports = FLOW_DISSECTOR_KEY_MAX;
  769 + struct flow_dissector_key_ports *key_ports;
  770 +
  771 + if (dissector_uses_key(flow_dissector, FLOW_DISSECTOR_KEY_PORTS))
  772 + dissector_ports = FLOW_DISSECTOR_KEY_PORTS;
  773 + else if (dissector_uses_key(flow_dissector,
  774 + FLOW_DISSECTOR_KEY_PORTS_RANGE))
  775 + dissector_ports = FLOW_DISSECTOR_KEY_PORTS_RANGE;
  776 +
  777 + if (dissector_ports == FLOW_DISSECTOR_KEY_MAX)
  778 + return;
  779 +
  780 + key_ports = skb_flow_dissector_target(flow_dissector,
  781 + dissector_ports,
  782 + target_container);
  783 + key_ports->ports = __skb_flow_get_ports(skb, nhoff, ip_proto,
  784 + data, hlen);
  785 +}
  786 +
  787 +static void
763 788 __skb_flow_dissect_ipv4(const struct sk_buff *skb,
764 789 struct flow_dissector *flow_dissector,
765 790 void *target_container, void *data, const struct iphdr *iph)
... ... @@ -928,7 +953,6 @@
928 953 struct flow_dissector_key_control *key_control;
929 954 struct flow_dissector_key_basic *key_basic;
930 955 struct flow_dissector_key_addrs *key_addrs;
931   - struct flow_dissector_key_ports *key_ports;
932 956 struct flow_dissector_key_tags *key_tags;
933 957 struct flow_dissector_key_vlan *key_vlan;
934 958 struct bpf_prog *attached = NULL;
... ... @@ -1383,14 +1407,9 @@
1383 1407 break;
1384 1408 }
1385 1409  
1386   - if (dissector_uses_key(flow_dissector, FLOW_DISSECTOR_KEY_PORTS) &&
1387   - !(key_control->flags & FLOW_DIS_IS_FRAGMENT)) {
1388   - key_ports = skb_flow_dissector_target(flow_dissector,
1389   - FLOW_DISSECTOR_KEY_PORTS,
1390   - target_container);
1391   - key_ports->ports = __skb_flow_get_ports(skb, nhoff, ip_proto,
1392   - data, hlen);
1393   - }
  1410 + if (!(key_control->flags & FLOW_DIS_IS_FRAGMENT))
  1411 + __skb_flow_dissect_ports(skb, flow_dissector, target_container,
  1412 + data, nhoff, ip_proto, hlen);
1394 1413  
1395 1414 /* Process result of IP proto processing */
1396 1415 switch (fdret) {
net/sched/cls_flower.c
... ... @@ -56,8 +56,13 @@
56 56 struct flow_dissector_key_ip ip;
57 57 struct flow_dissector_key_ip enc_ip;
58 58 struct flow_dissector_key_enc_opts enc_opts;
59   - struct flow_dissector_key_ports tp_min;
60   - struct flow_dissector_key_ports tp_max;
  59 + union {
  60 + struct flow_dissector_key_ports tp;
  61 + struct {
  62 + struct flow_dissector_key_ports tp_min;
  63 + struct flow_dissector_key_ports tp_max;
  64 + };
  65 + } tp_range;
61 66 struct flow_dissector_key_ct ct;
62 67 } __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */
63 68  
64 69  
65 70  
... ... @@ -200,19 +205,19 @@
200 205 {
201 206 __be16 min_mask, max_mask, min_val, max_val;
202 207  
203   - min_mask = htons(filter->mask->key.tp_min.dst);
204   - max_mask = htons(filter->mask->key.tp_max.dst);
205   - min_val = htons(filter->key.tp_min.dst);
206   - max_val = htons(filter->key.tp_max.dst);
  208 + min_mask = htons(filter->mask->key.tp_range.tp_min.dst);
  209 + max_mask = htons(filter->mask->key.tp_range.tp_max.dst);
  210 + min_val = htons(filter->key.tp_range.tp_min.dst);
  211 + max_val = htons(filter->key.tp_range.tp_max.dst);
207 212  
208 213 if (min_mask && max_mask) {
209   - if (htons(key->tp.dst) < min_val ||
210   - htons(key->tp.dst) > max_val)
  214 + if (htons(key->tp_range.tp.dst) < min_val ||
  215 + htons(key->tp_range.tp.dst) > max_val)
211 216 return false;
212 217  
213 218 /* skb does not have min and max values */
214   - mkey->tp_min.dst = filter->mkey.tp_min.dst;
215   - mkey->tp_max.dst = filter->mkey.tp_max.dst;
  219 + mkey->tp_range.tp_min.dst = filter->mkey.tp_range.tp_min.dst;
  220 + mkey->tp_range.tp_max.dst = filter->mkey.tp_range.tp_max.dst;
216 221 }
217 222 return true;
218 223 }
219 224  
220 225  
... ... @@ -223,19 +228,19 @@
223 228 {
224 229 __be16 min_mask, max_mask, min_val, max_val;
225 230  
226   - min_mask = htons(filter->mask->key.tp_min.src);
227   - max_mask = htons(filter->mask->key.tp_max.src);
228   - min_val = htons(filter->key.tp_min.src);
229   - max_val = htons(filter->key.tp_max.src);
  231 + min_mask = htons(filter->mask->key.tp_range.tp_min.src);
  232 + max_mask = htons(filter->mask->key.tp_range.tp_max.src);
  233 + min_val = htons(filter->key.tp_range.tp_min.src);
  234 + max_val = htons(filter->key.tp_range.tp_max.src);
230 235  
231 236 if (min_mask && max_mask) {
232   - if (htons(key->tp.src) < min_val ||
233   - htons(key->tp.src) > max_val)
  237 + if (htons(key->tp_range.tp.src) < min_val ||
  238 + htons(key->tp_range.tp.src) > max_val)
234 239 return false;
235 240  
236 241 /* skb does not have min and max values */
237   - mkey->tp_min.src = filter->mkey.tp_min.src;
238   - mkey->tp_max.src = filter->mkey.tp_max.src;
  242 + mkey->tp_range.tp_min.src = filter->mkey.tp_range.tp_min.src;
  243 + mkey->tp_range.tp_max.src = filter->mkey.tp_range.tp_max.src;
239 244 }
240 245 return true;
241 246 }
242 247  
... ... @@ -734,23 +739,25 @@
734 739 static int fl_set_key_port_range(struct nlattr **tb, struct fl_flow_key *key,
735 740 struct fl_flow_key *mask)
736 741 {
737   - fl_set_key_val(tb, &key->tp_min.dst,
738   - TCA_FLOWER_KEY_PORT_DST_MIN, &mask->tp_min.dst,
739   - TCA_FLOWER_UNSPEC, sizeof(key->tp_min.dst));
740   - fl_set_key_val(tb, &key->tp_max.dst,
741   - TCA_FLOWER_KEY_PORT_DST_MAX, &mask->tp_max.dst,
742   - TCA_FLOWER_UNSPEC, sizeof(key->tp_max.dst));
743   - fl_set_key_val(tb, &key->tp_min.src,
744   - TCA_FLOWER_KEY_PORT_SRC_MIN, &mask->tp_min.src,
745   - TCA_FLOWER_UNSPEC, sizeof(key->tp_min.src));
746   - fl_set_key_val(tb, &key->tp_max.src,
747   - TCA_FLOWER_KEY_PORT_SRC_MAX, &mask->tp_max.src,
748   - TCA_FLOWER_UNSPEC, sizeof(key->tp_max.src));
  742 + fl_set_key_val(tb, &key->tp_range.tp_min.dst,
  743 + TCA_FLOWER_KEY_PORT_DST_MIN, &mask->tp_range.tp_min.dst,
  744 + TCA_FLOWER_UNSPEC, sizeof(key->tp_range.tp_min.dst));
  745 + fl_set_key_val(tb, &key->tp_range.tp_max.dst,
  746 + TCA_FLOWER_KEY_PORT_DST_MAX, &mask->tp_range.tp_max.dst,
  747 + TCA_FLOWER_UNSPEC, sizeof(key->tp_range.tp_max.dst));
  748 + fl_set_key_val(tb, &key->tp_range.tp_min.src,
  749 + TCA_FLOWER_KEY_PORT_SRC_MIN, &mask->tp_range.tp_min.src,
  750 + TCA_FLOWER_UNSPEC, sizeof(key->tp_range.tp_min.src));
  751 + fl_set_key_val(tb, &key->tp_range.tp_max.src,
  752 + TCA_FLOWER_KEY_PORT_SRC_MAX, &mask->tp_range.tp_max.src,
  753 + TCA_FLOWER_UNSPEC, sizeof(key->tp_range.tp_max.src));
749 754  
750   - if ((mask->tp_min.dst && mask->tp_max.dst &&
751   - htons(key->tp_max.dst) <= htons(key->tp_min.dst)) ||
752   - (mask->tp_min.src && mask->tp_max.src &&
753   - htons(key->tp_max.src) <= htons(key->tp_min.src)))
  755 + if ((mask->tp_range.tp_min.dst && mask->tp_range.tp_max.dst &&
  756 + htons(key->tp_range.tp_max.dst) <=
  757 + htons(key->tp_range.tp_min.dst)) ||
  758 + (mask->tp_range.tp_min.src && mask->tp_range.tp_max.src &&
  759 + htons(key->tp_range.tp_max.src) <=
  760 + htons(key->tp_range.tp_min.src)))
754 761 return -EINVAL;
755 762  
756 763 return 0;
757 764  
... ... @@ -1509,10 +1516,11 @@
1509 1516 FLOW_DISSECTOR_KEY_IPV4_ADDRS, ipv4);
1510 1517 FL_KEY_SET_IF_MASKED(mask, keys, cnt,
1511 1518 FLOW_DISSECTOR_KEY_IPV6_ADDRS, ipv6);
1512   - if (FL_KEY_IS_MASKED(mask, tp) ||
1513   - FL_KEY_IS_MASKED(mask, tp_min) || FL_KEY_IS_MASKED(mask, tp_max))
1514   - FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_PORTS, tp);
1515 1519 FL_KEY_SET_IF_MASKED(mask, keys, cnt,
  1520 + FLOW_DISSECTOR_KEY_PORTS, tp);
  1521 + FL_KEY_SET_IF_MASKED(mask, keys, cnt,
  1522 + FLOW_DISSECTOR_KEY_PORTS_RANGE, tp_range);
  1523 + FL_KEY_SET_IF_MASKED(mask, keys, cnt,
1516 1524 FLOW_DISSECTOR_KEY_IP, ip);
1517 1525 FL_KEY_SET_IF_MASKED(mask, keys, cnt,
1518 1526 FLOW_DISSECTOR_KEY_TCP, tcp);
... ... @@ -1560,8 +1568,10 @@
1560 1568  
1561 1569 fl_mask_copy(newmask, mask);
1562 1570  
1563   - if ((newmask->key.tp_min.dst && newmask->key.tp_max.dst) ||
1564   - (newmask->key.tp_min.src && newmask->key.tp_max.src))
  1571 + if ((newmask->key.tp_range.tp_min.dst &&
  1572 + newmask->key.tp_range.tp_max.dst) ||
  1573 + (newmask->key.tp_range.tp_min.src &&
  1574 + newmask->key.tp_range.tp_max.src))
1565 1575 newmask->flags |= TCA_FLOWER_MASK_FLAGS_RANGE;
1566 1576  
1567 1577 err = fl_init_mask_hashtable(newmask);
... ... @@ -2159,18 +2169,22 @@
2159 2169 static int fl_dump_key_port_range(struct sk_buff *skb, struct fl_flow_key *key,
2160 2170 struct fl_flow_key *mask)
2161 2171 {
2162   - if (fl_dump_key_val(skb, &key->tp_min.dst, TCA_FLOWER_KEY_PORT_DST_MIN,
2163   - &mask->tp_min.dst, TCA_FLOWER_UNSPEC,
2164   - sizeof(key->tp_min.dst)) ||
2165   - fl_dump_key_val(skb, &key->tp_max.dst, TCA_FLOWER_KEY_PORT_DST_MAX,
2166   - &mask->tp_max.dst, TCA_FLOWER_UNSPEC,
2167   - sizeof(key->tp_max.dst)) ||
2168   - fl_dump_key_val(skb, &key->tp_min.src, TCA_FLOWER_KEY_PORT_SRC_MIN,
2169   - &mask->tp_min.src, TCA_FLOWER_UNSPEC,
2170   - sizeof(key->tp_min.src)) ||
2171   - fl_dump_key_val(skb, &key->tp_max.src, TCA_FLOWER_KEY_PORT_SRC_MAX,
2172   - &mask->tp_max.src, TCA_FLOWER_UNSPEC,
2173   - sizeof(key->tp_max.src)))
  2172 + if (fl_dump_key_val(skb, &key->tp_range.tp_min.dst,
  2173 + TCA_FLOWER_KEY_PORT_DST_MIN,
  2174 + &mask->tp_range.tp_min.dst, TCA_FLOWER_UNSPEC,
  2175 + sizeof(key->tp_range.tp_min.dst)) ||
  2176 + fl_dump_key_val(skb, &key->tp_range.tp_max.dst,
  2177 + TCA_FLOWER_KEY_PORT_DST_MAX,
  2178 + &mask->tp_range.tp_max.dst, TCA_FLOWER_UNSPEC,
  2179 + sizeof(key->tp_range.tp_max.dst)) ||
  2180 + fl_dump_key_val(skb, &key->tp_range.tp_min.src,
  2181 + TCA_FLOWER_KEY_PORT_SRC_MIN,
  2182 + &mask->tp_range.tp_min.src, TCA_FLOWER_UNSPEC,
  2183 + sizeof(key->tp_range.tp_min.src)) ||
  2184 + fl_dump_key_val(skb, &key->tp_range.tp_max.src,
  2185 + TCA_FLOWER_KEY_PORT_SRC_MAX,
  2186 + &mask->tp_range.tp_max.src, TCA_FLOWER_UNSPEC,
  2187 + sizeof(key->tp_range.tp_max.src)))
2174 2188 return -1;
2175 2189  
2176 2190 return 0;