Skip to content

Commit 71d0ed7

Browse files
amirvdavem330
authored andcommitted
net/act_pedit: Support using offset relative to the conventional network headers
Extend pedit to enable the user setting offset relative to network headers. This change would enable to work with more complex header schemes (vs the simple IPv4 case) where setting a fixed offset relative to the network header is not enough. After this patch, the action has information about the exact header type and field inside this header. This information could be used later on for hardware offloading of pedit. Backward compatibility was being kept: 1. Old kernel <-> new userspace 2. New kernel <-> old userspace 3. add rule using new userspace <-> dump using old userspace 4. add rule using old userspace <-> dump using new userspace When using the extended api, new netlink attributes are being used. This way, operation will fail in (1) and (3) - and no malformed rule be added or dumped. Of course, new user space that doesn't need the new functionality can use the old netlink attributes and operation will succeed. Since action can support both api's, (2) should work, and it is easy to write the new user space to have (4) work. The action is having a strict check that only header types and commands it can handle are accepted. This way future additions will be much easier. Usage example: $ tc filter add dev enp0s9 protocol ip parent ffff: \ flower \ ip_proto tcp \ dst_port 80 \ action pedit munge tcp dport set 8080 pipe \ action mirred egress redirect dev veth0 Will forward tcp port whose original dest port is 80, while modifying the destination port to 8080. Signed-off-by: Amir Vadai <[email protected]> Reviewed-by: Or Gerlitz <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent ea6da4f commit 71d0ed7

File tree

3 files changed

+208
-16
lines changed

3 files changed

+208
-16
lines changed

include/net/tc_act/tc_pedit.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,16 @@
33

44
#include <net/act_api.h>
55

6+
struct tcf_pedit_key_ex {
7+
enum pedit_header_type htype;
8+
};
9+
610
struct tcf_pedit {
711
struct tc_action common;
812
unsigned char tcfp_nkeys;
913
unsigned char tcfp_flags;
1014
struct tc_pedit_key *tcfp_keys;
15+
struct tcf_pedit_key_ex *tcfp_keys_ex;
1116
};
1217
#define to_pedit(a) ((struct tcf_pedit *)a)
1318

include/uapi/linux/tc_act/tc_pedit.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,33 @@ enum {
1111
TCA_PEDIT_TM,
1212
TCA_PEDIT_PARMS,
1313
TCA_PEDIT_PAD,
14+
TCA_PEDIT_PARMS_EX,
15+
TCA_PEDIT_KEYS_EX,
16+
TCA_PEDIT_KEY_EX,
1417
__TCA_PEDIT_MAX
1518
};
1619
#define TCA_PEDIT_MAX (__TCA_PEDIT_MAX - 1)
1720

21+
enum {
22+
TCA_PEDIT_KEY_EX_HTYPE = 1,
23+
__TCA_PEDIT_KEY_EX_MAX
24+
};
25+
#define TCA_PEDIT_KEY_EX_MAX (__TCA_PEDIT_KEY_EX_MAX - 1)
26+
27+
/* TCA_PEDIT_KEY_EX_HDR_TYPE_NETWROK is a special case for legacy users. It
28+
* means no specific header type - offset is relative to the network layer
29+
*/
30+
enum pedit_header_type {
31+
TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK = 0,
32+
TCA_PEDIT_KEY_EX_HDR_TYPE_ETH = 1,
33+
TCA_PEDIT_KEY_EX_HDR_TYPE_IP4 = 2,
34+
TCA_PEDIT_KEY_EX_HDR_TYPE_IP6 = 3,
35+
TCA_PEDIT_KEY_EX_HDR_TYPE_TCP = 4,
36+
TCA_PEDIT_KEY_EX_HDR_TYPE_UDP = 5,
37+
__PEDIT_HDR_TYPE_MAX,
38+
};
39+
#define TCA_PEDIT_HDR_TYPE_MAX (__PEDIT_HDR_TYPE_MAX - 1)
40+
1841
struct tc_pedit_key {
1942
__u32 mask; /* AND */
2043
__u32 val; /*XOR */

net/sched/act_pedit.c

Lines changed: 180 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <net/pkt_sched.h>
2323
#include <linux/tc_act/tc_pedit.h>
2424
#include <net/tc_act/tc_pedit.h>
25+
#include <uapi/linux/tc_act/tc_pedit.h>
2526

2627
#define PEDIT_TAB_MASK 15
2728

@@ -30,18 +31,112 @@ static struct tc_action_ops act_pedit_ops;
3031

3132
static const struct nla_policy pedit_policy[TCA_PEDIT_MAX + 1] = {
3233
[TCA_PEDIT_PARMS] = { .len = sizeof(struct tc_pedit) },
34+
[TCA_PEDIT_KEYS_EX] = { .type = NLA_NESTED },
3335
};
3436

37+
static const struct nla_policy pedit_key_ex_policy[TCA_PEDIT_KEY_EX_MAX + 1] = {
38+
[TCA_PEDIT_KEY_EX_HTYPE] = { .type = NLA_U16 },
39+
};
40+
41+
static struct tcf_pedit_key_ex *tcf_pedit_keys_ex_parse(struct nlattr *nla,
42+
u8 n)
43+
{
44+
struct tcf_pedit_key_ex *keys_ex;
45+
struct tcf_pedit_key_ex *k;
46+
const struct nlattr *ka;
47+
int err = -EINVAL;
48+
int rem;
49+
50+
if (!nla || !n)
51+
return NULL;
52+
53+
keys_ex = kcalloc(n, sizeof(*k), GFP_KERNEL);
54+
if (!keys_ex)
55+
return ERR_PTR(-ENOMEM);
56+
57+
k = keys_ex;
58+
59+
nla_for_each_nested(ka, nla, rem) {
60+
struct nlattr *tb[TCA_PEDIT_KEY_EX_MAX + 1];
61+
62+
if (!n) {
63+
err = -EINVAL;
64+
goto err_out;
65+
}
66+
n--;
67+
68+
if (nla_type(ka) != TCA_PEDIT_KEY_EX) {
69+
err = -EINVAL;
70+
goto err_out;
71+
}
72+
73+
err = nla_parse_nested(tb, TCA_PEDIT_KEY_EX_MAX, ka,
74+
pedit_key_ex_policy);
75+
if (err)
76+
goto err_out;
77+
78+
if (!tb[TCA_PEDIT_KEY_EX_HTYPE]) {
79+
err = -EINVAL;
80+
goto err_out;
81+
}
82+
83+
k->htype = nla_get_u16(tb[TCA_PEDIT_KEY_EX_HTYPE]);
84+
85+
if (k->htype > TCA_PEDIT_HDR_TYPE_MAX) {
86+
err = -EINVAL;
87+
goto err_out;
88+
}
89+
90+
k++;
91+
}
92+
93+
if (n)
94+
goto err_out;
95+
96+
return keys_ex;
97+
98+
err_out:
99+
kfree(keys_ex);
100+
return ERR_PTR(err);
101+
}
102+
103+
static int tcf_pedit_key_ex_dump(struct sk_buff *skb,
104+
struct tcf_pedit_key_ex *keys_ex, int n)
105+
{
106+
struct nlattr *keys_start = nla_nest_start(skb, TCA_PEDIT_KEYS_EX);
107+
108+
for (; n > 0; n--) {
109+
struct nlattr *key_start;
110+
111+
key_start = nla_nest_start(skb, TCA_PEDIT_KEY_EX);
112+
113+
if (nla_put_u16(skb, TCA_PEDIT_KEY_EX_HTYPE, keys_ex->htype)) {
114+
nlmsg_trim(skb, keys_start);
115+
return -EINVAL;
116+
}
117+
118+
nla_nest_end(skb, key_start);
119+
120+
keys_ex++;
121+
}
122+
123+
nla_nest_end(skb, keys_start);
124+
125+
return 0;
126+
}
127+
35128
static int tcf_pedit_init(struct net *net, struct nlattr *nla,
36129
struct nlattr *est, struct tc_action **a,
37130
int ovr, int bind)
38131
{
39132
struct tc_action_net *tn = net_generic(net, pedit_net_id);
40133
struct nlattr *tb[TCA_PEDIT_MAX + 1];
134+
struct nlattr *pattr;
41135
struct tc_pedit *parm;
42136
int ret = 0, err;
43137
struct tcf_pedit *p;
44138
struct tc_pedit_key *keys = NULL;
139+
struct tcf_pedit_key_ex *keys_ex;
45140
int ksize;
46141

47142
if (nla == NULL)
@@ -51,13 +146,21 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
51146
if (err < 0)
52147
return err;
53148

54-
if (tb[TCA_PEDIT_PARMS] == NULL)
149+
pattr = tb[TCA_PEDIT_PARMS];
150+
if (!pattr)
151+
pattr = tb[TCA_PEDIT_PARMS_EX];
152+
if (!pattr)
55153
return -EINVAL;
56-
parm = nla_data(tb[TCA_PEDIT_PARMS]);
154+
155+
parm = nla_data(pattr);
57156
ksize = parm->nkeys * sizeof(struct tc_pedit_key);
58-
if (nla_len(tb[TCA_PEDIT_PARMS]) < sizeof(*parm) + ksize)
157+
if (nla_len(pattr) < sizeof(*parm) + ksize)
59158
return -EINVAL;
60159

160+
keys_ex = tcf_pedit_keys_ex_parse(tb[TCA_PEDIT_KEYS_EX], parm->nkeys);
161+
if (IS_ERR(keys_ex))
162+
return PTR_ERR(keys_ex);
163+
61164
if (!tcf_hash_check(tn, parm->index, a, bind)) {
62165
if (!parm->nkeys)
63166
return -EINVAL;
@@ -69,6 +172,7 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
69172
keys = kmalloc(ksize, GFP_KERNEL);
70173
if (keys == NULL) {
71174
tcf_hash_cleanup(*a, est);
175+
kfree(keys_ex);
72176
return -ENOMEM;
73177
}
74178
ret = ACT_P_CREATED;
@@ -81,8 +185,10 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
81185
p = to_pedit(*a);
82186
if (p->tcfp_nkeys && p->tcfp_nkeys != parm->nkeys) {
83187
keys = kmalloc(ksize, GFP_KERNEL);
84-
if (keys == NULL)
188+
if (!keys) {
189+
kfree(keys_ex);
85190
return -ENOMEM;
191+
}
86192
}
87193
}
88194

@@ -95,6 +201,10 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
95201
p->tcfp_nkeys = parm->nkeys;
96202
}
97203
memcpy(p->tcfp_keys, parm->keys, ksize);
204+
205+
kfree(p->tcfp_keys_ex);
206+
p->tcfp_keys_ex = keys_ex;
207+
98208
spin_unlock_bh(&p->tcf_lock);
99209
if (ret == ACT_P_CREATED)
100210
tcf_hash_insert(tn, *a);
@@ -106,6 +216,7 @@ static void tcf_pedit_cleanup(struct tc_action *a, int bind)
106216
struct tcf_pedit *p = to_pedit(a);
107217
struct tc_pedit_key *keys = p->tcfp_keys;
108218
kfree(keys);
219+
kfree(p->tcfp_keys_ex);
109220
}
110221

111222
static bool offset_valid(struct sk_buff *skb, int offset)
@@ -119,38 +230,84 @@ static bool offset_valid(struct sk_buff *skb, int offset)
119230
return true;
120231
}
121232

233+
static int pedit_skb_hdr_offset(struct sk_buff *skb,
234+
enum pedit_header_type htype, int *hoffset)
235+
{
236+
int ret = -EINVAL;
237+
238+
switch (htype) {
239+
case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH:
240+
if (skb_mac_header_was_set(skb)) {
241+
*hoffset = skb_mac_offset(skb);
242+
ret = 0;
243+
}
244+
break;
245+
case TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK:
246+
case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
247+
case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
248+
*hoffset = skb_network_offset(skb);
249+
ret = 0;
250+
break;
251+
case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
252+
case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP:
253+
if (skb_transport_header_was_set(skb)) {
254+
*hoffset = skb_transport_offset(skb);
255+
ret = 0;
256+
}
257+
break;
258+
default:
259+
ret = -EINVAL;
260+
break;
261+
};
262+
263+
return ret;
264+
}
265+
122266
static int tcf_pedit(struct sk_buff *skb, const struct tc_action *a,
123267
struct tcf_result *res)
124268
{
125269
struct tcf_pedit *p = to_pedit(a);
126270
int i;
127-
unsigned int off;
128271

129272
if (skb_unclone(skb, GFP_ATOMIC))
130273
return p->tcf_action;
131274

132-
off = skb_network_offset(skb);
133-
134275
spin_lock(&p->tcf_lock);
135276

136277
tcf_lastuse_update(&p->tcf_tm);
137278

138279
if (p->tcfp_nkeys > 0) {
139280
struct tc_pedit_key *tkey = p->tcfp_keys;
281+
struct tcf_pedit_key_ex *tkey_ex = p->tcfp_keys_ex;
282+
enum pedit_header_type htype = TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK;
140283

141284
for (i = p->tcfp_nkeys; i > 0; i--, tkey++) {
142285
u32 *ptr, _data;
143286
int offset = tkey->off;
287+
int hoffset;
288+
int rc;
289+
290+
if (tkey_ex) {
291+
htype = tkey_ex->htype;
292+
tkey_ex++;
293+
}
294+
295+
rc = pedit_skb_hdr_offset(skb, htype, &hoffset);
296+
if (rc) {
297+
pr_info("tc filter pedit bad header type specified (0x%x)\n",
298+
htype);
299+
goto bad;
300+
}
144301

145302
if (tkey->offmask) {
146303
char *d, _d;
147304

148-
if (!offset_valid(skb, off + tkey->at)) {
305+
if (!offset_valid(skb, hoffset + tkey->at)) {
149306
pr_info("tc filter pedit 'at' offset %d out of bounds\n",
150-
off + tkey->at);
307+
hoffset + tkey->at);
151308
goto bad;
152309
}
153-
d = skb_header_pointer(skb, off + tkey->at, 1,
310+
d = skb_header_pointer(skb, hoffset + tkey->at, 1,
154311
&_d);
155312
if (!d)
156313
goto bad;
@@ -163,19 +320,19 @@ static int tcf_pedit(struct sk_buff *skb, const struct tc_action *a,
163320
goto bad;
164321
}
165322

166-
if (!offset_valid(skb, off + offset)) {
323+
if (!offset_valid(skb, hoffset + offset)) {
167324
pr_info("tc filter pedit offset %d out of bounds\n",
168-
offset);
325+
hoffset + offset);
169326
goto bad;
170327
}
171328

172-
ptr = skb_header_pointer(skb, off + offset, 4, &_data);
329+
ptr = skb_header_pointer(skb, hoffset + offset, 4, &_data);
173330
if (!ptr)
174331
goto bad;
175332
/* just do it, baby */
176333
*ptr = ((*ptr & tkey->mask) ^ tkey->val);
177334
if (ptr == &_data)
178-
skb_store_bits(skb, off + offset, ptr, 4);
335+
skb_store_bits(skb, hoffset + offset, ptr, 4);
179336
}
180337

181338
goto done;
@@ -215,8 +372,15 @@ static int tcf_pedit_dump(struct sk_buff *skb, struct tc_action *a,
215372
opt->refcnt = p->tcf_refcnt - ref;
216373
opt->bindcnt = p->tcf_bindcnt - bind;
217374

218-
if (nla_put(skb, TCA_PEDIT_PARMS, s, opt))
219-
goto nla_put_failure;
375+
if (p->tcfp_keys_ex) {
376+
tcf_pedit_key_ex_dump(skb, p->tcfp_keys_ex, p->tcfp_nkeys);
377+
378+
if (nla_put(skb, TCA_PEDIT_PARMS_EX, s, opt))
379+
goto nla_put_failure;
380+
} else {
381+
if (nla_put(skb, TCA_PEDIT_PARMS, s, opt))
382+
goto nla_put_failure;
383+
}
220384

221385
tcf_tm_dump(&t, &p->tcf_tm);
222386
if (nla_put_64bit(skb, TCA_PEDIT_TM, sizeof(t), &t, TCA_PEDIT_PAD))

0 commit comments

Comments
 (0)