Skip to content

Commit 5c72299

Browse files
anambiarindavem330
authored andcommitted
net: sched: cls_flower: Classify packets using port ranges
Added support in tc flower for filtering based on port ranges. Example: 1. Match on a port range: ------------------------- $ tc filter add dev enp4s0 protocol ip parent ffff:\ prio 1 flower ip_proto tcp dst_port range 20-30 skip_hw\ action drop $ tc -s filter show dev enp4s0 parent ffff: filter protocol ip pref 1 flower chain 0 filter protocol ip pref 1 flower chain 0 handle 0x1 eth_type ipv4 ip_proto tcp dst_port range 20-30 skip_hw not_in_hw action order 1: gact action drop random type none pass val 0 index 1 ref 1 bind 1 installed 85 sec used 3 sec Action statistics: Sent 460 bytes 10 pkt (dropped 10, overlimits 0 requeues 0) backlog 0b 0p requeues 0 2. Match on IP address and port range: -------------------------------------- $ tc filter add dev enp4s0 protocol ip parent ffff:\ prio 1 flower dst_ip 192.168.1.1 ip_proto tcp dst_port range 100-200\ skip_hw action drop $ tc -s filter show dev enp4s0 parent ffff: filter protocol ip pref 1 flower chain 0 handle 0x2 eth_type ipv4 ip_proto tcp dst_ip 192.168.1.1 dst_port range 100-200 skip_hw not_in_hw action order 1: gact action drop random type none pass val 0 index 2 ref 1 bind 1 installed 58 sec used 2 sec Action statistics: Sent 920 bytes 20 pkt (dropped 20, overlimits 0 requeues 0) backlog 0b 0p requeues 0 v4: 1. Added condition before setting port key. 2. Organized setting and dumping port range keys into functions and added validation of input range. v3: 1. Moved new fields in UAPI enum to the end of enum. 2. Removed couple of empty lines. v2: Addressed Jiri's comments: 1. Added separate functions for dst and src comparisons. 2. Removed endpoint enum. 3. Added new bit TCA_FLOWER_FLAGS_RANGE to decide normal/range lookup. 4. Cleaned up fl_lookup function. Signed-off-by: Amritha Nambiar <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 7fe50ac commit 5c72299

File tree

2 files changed

+156
-6
lines changed

2 files changed

+156
-6
lines changed

include/uapi/linux/pkt_cls.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,11 @@ enum {
485485

486486
TCA_FLOWER_IN_HW_COUNT,
487487

488+
TCA_FLOWER_KEY_PORT_SRC_MIN, /* be16 */
489+
TCA_FLOWER_KEY_PORT_SRC_MAX, /* be16 */
490+
TCA_FLOWER_KEY_PORT_DST_MIN, /* be16 */
491+
TCA_FLOWER_KEY_PORT_DST_MAX, /* be16 */
492+
488493
__TCA_FLOWER_MAX,
489494
};
490495

@@ -518,6 +523,8 @@ enum {
518523
TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST = (1 << 1),
519524
};
520525

526+
#define TCA_FLOWER_MASK_FLAGS_RANGE (1 << 0) /* Range-based match */
527+
521528
/* Match-all classifier */
522529

523530
enum {

net/sched/cls_flower.c

Lines changed: 149 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ struct fl_flow_key {
5555
struct flow_dissector_key_ip ip;
5656
struct flow_dissector_key_ip enc_ip;
5757
struct flow_dissector_key_enc_opts enc_opts;
58+
struct flow_dissector_key_ports tp_min;
59+
struct flow_dissector_key_ports tp_max;
5860
} __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */
5961

6062
struct fl_flow_mask_range {
@@ -65,6 +67,7 @@ struct fl_flow_mask_range {
6567
struct fl_flow_mask {
6668
struct fl_flow_key key;
6769
struct fl_flow_mask_range range;
70+
u32 flags;
6871
struct rhash_head ht_node;
6972
struct rhashtable ht;
7073
struct rhashtable_params filter_ht_params;
@@ -179,13 +182,89 @@ static void fl_clear_masked_range(struct fl_flow_key *key,
179182
memset(fl_key_get_start(key, mask), 0, fl_mask_range(mask));
180183
}
181184

182-
static struct cls_fl_filter *fl_lookup(struct fl_flow_mask *mask,
183-
struct fl_flow_key *mkey)
185+
static bool fl_range_port_dst_cmp(struct cls_fl_filter *filter,
186+
struct fl_flow_key *key,
187+
struct fl_flow_key *mkey)
188+
{
189+
__be16 min_mask, max_mask, min_val, max_val;
190+
191+
min_mask = htons(filter->mask->key.tp_min.dst);
192+
max_mask = htons(filter->mask->key.tp_max.dst);
193+
min_val = htons(filter->key.tp_min.dst);
194+
max_val = htons(filter->key.tp_max.dst);
195+
196+
if (min_mask && max_mask) {
197+
if (htons(key->tp.dst) < min_val ||
198+
htons(key->tp.dst) > max_val)
199+
return false;
200+
201+
/* skb does not have min and max values */
202+
mkey->tp_min.dst = filter->mkey.tp_min.dst;
203+
mkey->tp_max.dst = filter->mkey.tp_max.dst;
204+
}
205+
return true;
206+
}
207+
208+
static bool fl_range_port_src_cmp(struct cls_fl_filter *filter,
209+
struct fl_flow_key *key,
210+
struct fl_flow_key *mkey)
211+
{
212+
__be16 min_mask, max_mask, min_val, max_val;
213+
214+
min_mask = htons(filter->mask->key.tp_min.src);
215+
max_mask = htons(filter->mask->key.tp_max.src);
216+
min_val = htons(filter->key.tp_min.src);
217+
max_val = htons(filter->key.tp_max.src);
218+
219+
if (min_mask && max_mask) {
220+
if (htons(key->tp.src) < min_val ||
221+
htons(key->tp.src) > max_val)
222+
return false;
223+
224+
/* skb does not have min and max values */
225+
mkey->tp_min.src = filter->mkey.tp_min.src;
226+
mkey->tp_max.src = filter->mkey.tp_max.src;
227+
}
228+
return true;
229+
}
230+
231+
static struct cls_fl_filter *__fl_lookup(struct fl_flow_mask *mask,
232+
struct fl_flow_key *mkey)
184233
{
185234
return rhashtable_lookup_fast(&mask->ht, fl_key_get_start(mkey, mask),
186235
mask->filter_ht_params);
187236
}
188237

238+
static struct cls_fl_filter *fl_lookup_range(struct fl_flow_mask *mask,
239+
struct fl_flow_key *mkey,
240+
struct fl_flow_key *key)
241+
{
242+
struct cls_fl_filter *filter, *f;
243+
244+
list_for_each_entry_rcu(filter, &mask->filters, list) {
245+
if (!fl_range_port_dst_cmp(filter, key, mkey))
246+
continue;
247+
248+
if (!fl_range_port_src_cmp(filter, key, mkey))
249+
continue;
250+
251+
f = __fl_lookup(mask, mkey);
252+
if (f)
253+
return f;
254+
}
255+
return NULL;
256+
}
257+
258+
static struct cls_fl_filter *fl_lookup(struct fl_flow_mask *mask,
259+
struct fl_flow_key *mkey,
260+
struct fl_flow_key *key)
261+
{
262+
if ((mask->flags & TCA_FLOWER_MASK_FLAGS_RANGE))
263+
return fl_lookup_range(mask, mkey, key);
264+
265+
return __fl_lookup(mask, mkey);
266+
}
267+
189268
static int fl_classify(struct sk_buff *skb, const struct tcf_proto *tp,
190269
struct tcf_result *res)
191270
{
@@ -208,7 +287,7 @@ static int fl_classify(struct sk_buff *skb, const struct tcf_proto *tp,
208287

209288
fl_set_masked_key(&skb_mkey, &skb_key, mask);
210289

211-
f = fl_lookup(mask, &skb_mkey);
290+
f = fl_lookup(mask, &skb_mkey, &skb_key);
212291
if (f && !tc_skip_sw(f->flags)) {
213292
*res = f->res;
214293
return tcf_exts_exec(skb, &f->exts, res);
@@ -514,6 +593,31 @@ static void fl_set_key_val(struct nlattr **tb,
514593
memcpy(mask, nla_data(tb[mask_type]), len);
515594
}
516595

596+
static int fl_set_key_port_range(struct nlattr **tb, struct fl_flow_key *key,
597+
struct fl_flow_key *mask)
598+
{
599+
fl_set_key_val(tb, &key->tp_min.dst,
600+
TCA_FLOWER_KEY_PORT_DST_MIN, &mask->tp_min.dst,
601+
TCA_FLOWER_UNSPEC, sizeof(key->tp_min.dst));
602+
fl_set_key_val(tb, &key->tp_max.dst,
603+
TCA_FLOWER_KEY_PORT_DST_MAX, &mask->tp_max.dst,
604+
TCA_FLOWER_UNSPEC, sizeof(key->tp_max.dst));
605+
fl_set_key_val(tb, &key->tp_min.src,
606+
TCA_FLOWER_KEY_PORT_SRC_MIN, &mask->tp_min.src,
607+
TCA_FLOWER_UNSPEC, sizeof(key->tp_min.src));
608+
fl_set_key_val(tb, &key->tp_max.src,
609+
TCA_FLOWER_KEY_PORT_SRC_MAX, &mask->tp_max.src,
610+
TCA_FLOWER_UNSPEC, sizeof(key->tp_max.src));
611+
612+
if ((mask->tp_min.dst && mask->tp_max.dst &&
613+
htons(key->tp_max.dst) <= htons(key->tp_min.dst)) ||
614+
(mask->tp_min.src && mask->tp_max.src &&
615+
htons(key->tp_max.src) <= htons(key->tp_min.src)))
616+
return -EINVAL;
617+
618+
return 0;
619+
}
620+
517621
static int fl_set_key_mpls(struct nlattr **tb,
518622
struct flow_dissector_key_mpls *key_val,
519623
struct flow_dissector_key_mpls *key_mask)
@@ -921,6 +1025,14 @@ static int fl_set_key(struct net *net, struct nlattr **tb,
9211025
sizeof(key->arp.tha));
9221026
}
9231027

1028+
if (key->basic.ip_proto == IPPROTO_TCP ||
1029+
key->basic.ip_proto == IPPROTO_UDP ||
1030+
key->basic.ip_proto == IPPROTO_SCTP) {
1031+
ret = fl_set_key_port_range(tb, key, mask);
1032+
if (ret)
1033+
return ret;
1034+
}
1035+
9241036
if (tb[TCA_FLOWER_KEY_ENC_IPV4_SRC] ||
9251037
tb[TCA_FLOWER_KEY_ENC_IPV4_DST]) {
9261038
key->enc_control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
@@ -1038,8 +1150,9 @@ static void fl_init_dissector(struct flow_dissector *dissector,
10381150
FLOW_DISSECTOR_KEY_IPV4_ADDRS, ipv4);
10391151
FL_KEY_SET_IF_MASKED(mask, keys, cnt,
10401152
FLOW_DISSECTOR_KEY_IPV6_ADDRS, ipv6);
1041-
FL_KEY_SET_IF_MASKED(mask, keys, cnt,
1042-
FLOW_DISSECTOR_KEY_PORTS, tp);
1153+
if (FL_KEY_IS_MASKED(mask, tp) ||
1154+
FL_KEY_IS_MASKED(mask, tp_min) || FL_KEY_IS_MASKED(mask, tp_max))
1155+
FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_PORTS, tp);
10431156
FL_KEY_SET_IF_MASKED(mask, keys, cnt,
10441157
FLOW_DISSECTOR_KEY_IP, ip);
10451158
FL_KEY_SET_IF_MASKED(mask, keys, cnt,
@@ -1086,6 +1199,10 @@ static struct fl_flow_mask *fl_create_new_mask(struct cls_fl_head *head,
10861199

10871200
fl_mask_copy(newmask, mask);
10881201

1202+
if ((newmask->key.tp_min.dst && newmask->key.tp_max.dst) ||
1203+
(newmask->key.tp_min.src && newmask->key.tp_max.src))
1204+
newmask->flags |= TCA_FLOWER_MASK_FLAGS_RANGE;
1205+
10891206
err = fl_init_mask_hashtable(newmask);
10901207
if (err)
10911208
goto errout_free;
@@ -1239,7 +1356,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
12391356
goto errout_idr;
12401357

12411358
if (!tc_skip_sw(fnew->flags)) {
1242-
if (!fold && fl_lookup(fnew->mask, &fnew->mkey)) {
1359+
if (!fold && __fl_lookup(fnew->mask, &fnew->mkey)) {
12431360
err = -EEXIST;
12441361
goto errout_mask;
12451362
}
@@ -1476,6 +1593,26 @@ static int fl_dump_key_val(struct sk_buff *skb,
14761593
return 0;
14771594
}
14781595

1596+
static int fl_dump_key_port_range(struct sk_buff *skb, struct fl_flow_key *key,
1597+
struct fl_flow_key *mask)
1598+
{
1599+
if (fl_dump_key_val(skb, &key->tp_min.dst, TCA_FLOWER_KEY_PORT_DST_MIN,
1600+
&mask->tp_min.dst, TCA_FLOWER_UNSPEC,
1601+
sizeof(key->tp_min.dst)) ||
1602+
fl_dump_key_val(skb, &key->tp_max.dst, TCA_FLOWER_KEY_PORT_DST_MAX,
1603+
&mask->tp_max.dst, TCA_FLOWER_UNSPEC,
1604+
sizeof(key->tp_max.dst)) ||
1605+
fl_dump_key_val(skb, &key->tp_min.src, TCA_FLOWER_KEY_PORT_SRC_MIN,
1606+
&mask->tp_min.src, TCA_FLOWER_UNSPEC,
1607+
sizeof(key->tp_min.src)) ||
1608+
fl_dump_key_val(skb, &key->tp_max.src, TCA_FLOWER_KEY_PORT_SRC_MAX,
1609+
&mask->tp_max.src, TCA_FLOWER_UNSPEC,
1610+
sizeof(key->tp_max.src)))
1611+
return -1;
1612+
1613+
return 0;
1614+
}
1615+
14791616
static int fl_dump_key_mpls(struct sk_buff *skb,
14801617
struct flow_dissector_key_mpls *mpls_key,
14811618
struct flow_dissector_key_mpls *mpls_mask)
@@ -1812,6 +1949,12 @@ static int fl_dump_key(struct sk_buff *skb, struct net *net,
18121949
sizeof(key->arp.tha))))
18131950
goto nla_put_failure;
18141951

1952+
if ((key->basic.ip_proto == IPPROTO_TCP ||
1953+
key->basic.ip_proto == IPPROTO_UDP ||
1954+
key->basic.ip_proto == IPPROTO_SCTP) &&
1955+
fl_dump_key_port_range(skb, key, mask))
1956+
goto nla_put_failure;
1957+
18151958
if (key->enc_control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS &&
18161959
(fl_dump_key_val(skb, &key->enc_ipv4.src,
18171960
TCA_FLOWER_KEY_ENC_IPV4_SRC, &mask->enc_ipv4.src,

0 commit comments

Comments
 (0)