Skip to content

Commit 8ffb055

Browse files
yosh1k104davem330
authored andcommitted
cls_flower: Fix the behavior using port ranges with hw-offload
The recent commit 5c72299 ("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: 5c72299 ("net: sched: cls_flower: Classify packets using port ranges") Signed-off-by: Yoshiki Komachi <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 2f23cd4 commit 8ffb055

File tree

3 files changed

+95
-61
lines changed

3 files changed

+95
-61
lines changed

include/net/flow_dissector.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ enum flow_dissector_key_id {
235235
FLOW_DISSECTOR_KEY_IPV4_ADDRS, /* struct flow_dissector_key_ipv4_addrs */
236236
FLOW_DISSECTOR_KEY_IPV6_ADDRS, /* struct flow_dissector_key_ipv6_addrs */
237237
FLOW_DISSECTOR_KEY_PORTS, /* struct flow_dissector_key_ports */
238+
FLOW_DISSECTOR_KEY_PORTS_RANGE, /* struct flow_dissector_key_ports */
238239
FLOW_DISSECTOR_KEY_ICMP, /* struct flow_dissector_key_icmp */
239240
FLOW_DISSECTOR_KEY_ETH_ADDRS, /* struct flow_dissector_key_eth_addrs */
240241
FLOW_DISSECTOR_KEY_TIPC, /* struct flow_dissector_key_tipc */

net/core/flow_dissector.c

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,31 @@ __skb_flow_dissect_tcp(const struct sk_buff *skb,
759759
key_tcp->flags = (*(__be16 *) &tcp_flag_word(th) & htons(0x0FFF));
760760
}
761761

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+
762787
static void
763788
__skb_flow_dissect_ipv4(const struct sk_buff *skb,
764789
struct flow_dissector *flow_dissector,
@@ -928,7 +953,6 @@ bool __skb_flow_dissect(const struct net *net,
928953
struct flow_dissector_key_control *key_control;
929954
struct flow_dissector_key_basic *key_basic;
930955
struct flow_dissector_key_addrs *key_addrs;
931-
struct flow_dissector_key_ports *key_ports;
932956
struct flow_dissector_key_tags *key_tags;
933957
struct flow_dissector_key_vlan *key_vlan;
934958
struct bpf_prog *attached = NULL;
@@ -1383,14 +1407,9 @@ bool __skb_flow_dissect(const struct net *net,
13831407
break;
13841408
}
13851409

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);
13941413

13951414
/* Process result of IP proto processing */
13961415
switch (fdret) {

net/sched/cls_flower.c

Lines changed: 66 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,13 @@ struct fl_flow_key {
5656
struct flow_dissector_key_ip ip;
5757
struct flow_dissector_key_ip enc_ip;
5858
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;
6166
struct flow_dissector_key_ct ct;
6267
} __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */
6368

@@ -200,19 +205,19 @@ static bool fl_range_port_dst_cmp(struct cls_fl_filter *filter,
200205
{
201206
__be16 min_mask, max_mask, min_val, max_val;
202207

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);
207212

208213
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)
211216
return false;
212217

213218
/* 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;
216221
}
217222
return true;
218223
}
@@ -223,19 +228,19 @@ static bool fl_range_port_src_cmp(struct cls_fl_filter *filter,
223228
{
224229
__be16 min_mask, max_mask, min_val, max_val;
225230

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);
230235

231236
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)
234239
return false;
235240

236241
/* 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;
239244
}
240245
return true;
241246
}
@@ -734,23 +739,25 @@ static void fl_set_key_val(struct nlattr **tb,
734739
static int fl_set_key_port_range(struct nlattr **tb, struct fl_flow_key *key,
735740
struct fl_flow_key *mask)
736741
{
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));
749-
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)))
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));
754+
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)))
754761
return -EINVAL;
755762

756763
return 0;
@@ -1509,9 +1516,10 @@ static void fl_init_dissector(struct flow_dissector *dissector,
15091516
FLOW_DISSECTOR_KEY_IPV4_ADDRS, ipv4);
15101517
FL_KEY_SET_IF_MASKED(mask, keys, cnt,
15111518
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);
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);
15151523
FL_KEY_SET_IF_MASKED(mask, keys, cnt,
15161524
FLOW_DISSECTOR_KEY_IP, ip);
15171525
FL_KEY_SET_IF_MASKED(mask, keys, cnt,
@@ -1560,8 +1568,10 @@ static struct fl_flow_mask *fl_create_new_mask(struct cls_fl_head *head,
15601568

15611569
fl_mask_copy(newmask, mask);
15621570

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))
15651575
newmask->flags |= TCA_FLOWER_MASK_FLAGS_RANGE;
15661576

15671577
err = fl_init_mask_hashtable(newmask);
@@ -2159,18 +2169,22 @@ static int fl_dump_key_val(struct sk_buff *skb,
21592169
static int fl_dump_key_port_range(struct sk_buff *skb, struct fl_flow_key *key,
21602170
struct fl_flow_key *mask)
21612171
{
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)))
21742188
return -1;
21752189

21762190
return 0;

0 commit comments

Comments
 (0)