History

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.

Amir Vadai committed 9 years ago

Commit 71d0ed71 parent ea6da4f3 files · +208 −16Browse files

Unified · Split

3 files
  1. include/net/tc_act/tc_pedit.h +5 −0
  2. include/uapi/linux/tc_act/tc_pedit.h +23 −0
  3. net/sched/act_pedit.c +180 −16
include/net/tc_act/tc_pedit.h+5−0
@@ -3,11 +3,16 @@ · 2 unmodified lines
3 3
4#include <net/act_api.h>4#include <net/act_api.h>
5 5
6struct tcf_pedit_key_ex {
7 enum pedit_header_type htype;
8};
9
6struct tcf_pedit {10struct tcf_pedit {
7 struct tc_action common;11 struct tc_action common;
8 unsigned char tcfp_nkeys;12 unsigned char tcfp_nkeys;
9 unsigned char tcfp_flags;13 unsigned char tcfp_flags;
10 struct tc_pedit_key *tcfp_keys;14 struct tc_pedit_key *tcfp_keys;
15 struct tcf_pedit_key_ex *tcfp_keys_ex;
11};16};
12#define to_pedit(a) ((struct tcf_pedit *)a)17#define to_pedit(a) ((struct tcf_pedit *)a)
13 18
include/uapi/linux/tc_act/tc_pedit.h+23−0
@@ -11,10 +11,33 @@ enum { · 10 unmodified lines
11 TCA_PEDIT_TM,11 TCA_PEDIT_TM,
12 TCA_PEDIT_PARMS,12 TCA_PEDIT_PARMS,
13 TCA_PEDIT_PAD,13 TCA_PEDIT_PAD,
14 TCA_PEDIT_PARMS_EX,
15 TCA_PEDIT_KEYS_EX,
16 TCA_PEDIT_KEY_EX,
14 __TCA_PEDIT_MAX17 __TCA_PEDIT_MAX
15};18};
16#define TCA_PEDIT_MAX (__TCA_PEDIT_MAX - 1)19#define TCA_PEDIT_MAX (__TCA_PEDIT_MAX - 1)
17 20
21enum {
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 */
30enum 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
18struct tc_pedit_key {41struct tc_pedit_key {
19 __u32 mask; /* AND */42 __u32 mask; /* AND */
20 __u32 val; /*XOR */43 __u32 val; /*XOR */
net/sched/act_pedit.c+180−16
@@ -22,6 +22,7 @@ · 21 unmodified lines
22#include <net/pkt_sched.h>22#include <net/pkt_sched.h>
23#include <linux/tc_act/tc_pedit.h>23#include <linux/tc_act/tc_pedit.h>
24#include <net/tc_act/tc_pedit.h>24#include <net/tc_act/tc_pedit.h>
25#include <uapi/linux/tc_act/tc_pedit.h>
25 26
26#define PEDIT_TAB_MASK 1527#define PEDIT_TAB_MASK 15
27 28
@@ -30,18 +31,112 @@ static struct tc_action_ops act_pedit_ops; · 2 unmodified lines
30 31
31static const struct nla_policy pedit_policy[TCA_PEDIT_MAX + 1] = {32static const struct nla_policy pedit_policy[TCA_PEDIT_MAX + 1] = {
32 [TCA_PEDIT_PARMS] = { .len = sizeof(struct tc_pedit) },33 [TCA_PEDIT_PARMS] = { .len = sizeof(struct tc_pedit) },
34 [TCA_PEDIT_KEYS_EX] = { .type = NLA_NESTED },
33};35};
34 36
37static 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
41static 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
98err_out:
99 kfree(keys_ex);
100 return ERR_PTR(err);
101}
102
103static 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
35static int tcf_pedit_init(struct net *net, struct nlattr *nla,128static int tcf_pedit_init(struct net *net, struct nlattr *nla,
36 struct nlattr *est, struct tc_action **a,129 struct nlattr *est, struct tc_action **a,
37 int ovr, int bind)130 int ovr, int bind)
38{131{
39 struct tc_action_net *tn = net_generic(net, pedit_net_id);132 struct tc_action_net *tn = net_generic(net, pedit_net_id);
40 struct nlattr *tb[TCA_PEDIT_MAX + 1];133 struct nlattr *tb[TCA_PEDIT_MAX + 1];
134 struct nlattr *pattr;
41 struct tc_pedit *parm;135 struct tc_pedit *parm;
42 int ret = 0, err;136 int ret = 0, err;
43 struct tcf_pedit *p;137 struct tcf_pedit *p;
44 struct tc_pedit_key *keys = NULL;138 struct tc_pedit_key *keys = NULL;
139 struct tcf_pedit_key_ex *keys_ex;
45 int ksize;140 int ksize;
46 141
47 if (nla == NULL)142 if (nla == NULL)
@@ -51,13 +146,21 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, · 3 unmodified lines
51 if (err < 0)146 if (err < 0)
52 return err;147 return err;
53 148
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)
55 return -EINVAL;153 return -EINVAL;
56 parm = nla_data(tb[TCA_PEDIT_PARMS]);154
155 parm = nla_data(pattr);
57 ksize = parm->nkeys * sizeof(struct tc_pedit_key);156 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)
59 return -EINVAL;158 return -EINVAL;
60 159
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
61 if (!tcf_hash_check(tn, parm->index, a, bind)) {164 if (!tcf_hash_check(tn, parm->index, a, bind)) {
62 if (!parm->nkeys)165 if (!parm->nkeys)
63 return -EINVAL;166 return -EINVAL;
@@ -69,6 +172,7 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, · 5 unmodified lines
69 keys = kmalloc(ksize, GFP_KERNEL);172 keys = kmalloc(ksize, GFP_KERNEL);
70 if (keys == NULL) {173 if (keys == NULL) {
71 tcf_hash_cleanup(*a, est);174 tcf_hash_cleanup(*a, est);
175 kfree(keys_ex);
72 return -ENOMEM;176 return -ENOMEM;
73 }177 }
74 ret = ACT_P_CREATED;178 ret = ACT_P_CREATED;
@@ -81,8 +185,10 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, · 6 unmodified lines
81 p = to_pedit(*a);185 p = to_pedit(*a);
82 if (p->tcfp_nkeys && p->tcfp_nkeys != parm->nkeys) {186 if (p->tcfp_nkeys && p->tcfp_nkeys != parm->nkeys) {
83 keys = kmalloc(ksize, GFP_KERNEL);187 keys = kmalloc(ksize, GFP_KERNEL);
84 if (keys == NULL)188 if (!keys) {
189 kfree(keys_ex);
85 return -ENOMEM;190 return -ENOMEM;
191 }
86 }192 }
87 }193 }
88 194
@@ -95,6 +201,10 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, · 6 unmodified lines
95 p->tcfp_nkeys = parm->nkeys;201 p->tcfp_nkeys = parm->nkeys;
96 }202 }
97 memcpy(p->tcfp_keys, parm->keys, ksize);203 memcpy(p->tcfp_keys, parm->keys, ksize);
204
205 kfree(p->tcfp_keys_ex);
206 p->tcfp_keys_ex = keys_ex;
207
98 spin_unlock_bh(&p->tcf_lock);208 spin_unlock_bh(&p->tcf_lock);
99 if (ret == ACT_P_CREATED)209 if (ret == ACT_P_CREATED)
100 tcf_hash_insert(tn, *a);210 tcf_hash_insert(tn, *a);
@@ -106,6 +216,7 @@ static void tcf_pedit_cleanup(struct tc_action *a, int bind) · 5 unmodified lines
106 struct tcf_pedit *p = to_pedit(a);216 struct tcf_pedit *p = to_pedit(a);
107 struct tc_pedit_key *keys = p->tcfp_keys;217 struct tc_pedit_key *keys = p->tcfp_keys;
108 kfree(keys);218 kfree(keys);
219 kfree(p->tcfp_keys_ex);
109}220}
110 221
111static bool offset_valid(struct sk_buff *skb, int offset)222static bool offset_valid(struct sk_buff *skb, int offset)
@@ -119,38 +230,84 @@ static bool offset_valid(struct sk_buff *skb, int offset) · 7 unmodified lines
119 return true;230 return true;
120}231}
121 232
233static 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
122static int tcf_pedit(struct sk_buff *skb, const struct tc_action *a,266static int tcf_pedit(struct sk_buff *skb, const struct tc_action *a,
123 struct tcf_result *res)267 struct tcf_result *res)
124{268{
125 struct tcf_pedit *p = to_pedit(a);269 struct tcf_pedit *p = to_pedit(a);
126 int i;270 int i;
127 unsigned int off;
128 271
129 if (skb_unclone(skb, GFP_ATOMIC))272 if (skb_unclone(skb, GFP_ATOMIC))
130 return p->tcf_action;273 return p->tcf_action;
131 274
132 off = skb_network_offset(skb);
133
134 spin_lock(&p->tcf_lock);275 spin_lock(&p->tcf_lock);
135 276
136 tcf_lastuse_update(&p->tcf_tm);277 tcf_lastuse_update(&p->tcf_tm);
137 278
138 if (p->tcfp_nkeys > 0) {279 if (p->tcfp_nkeys > 0) {
139 struct tc_pedit_key *tkey = p->tcfp_keys;280 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;
140 283
141 for (i = p->tcfp_nkeys; i > 0; i--, tkey++) {284 for (i = p->tcfp_nkeys; i > 0; i--, tkey++) {
142 u32 *ptr, _data;285 u32 *ptr, _data;
143 int offset = tkey->off;286 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 }
144 301
145 if (tkey->offmask) {302 if (tkey->offmask) {
146 char *d, _d;303 char *d, _d;
147 304
148 if (!offset_valid(skb, off + tkey->at)) {305 if (!offset_valid(skb, hoffset + tkey->at)) {
149 pr_info("tc filter pedit 'at' offset %d out of bounds\n",306 pr_info("tc filter pedit 'at' offset %d out of bounds\n",
150 off + tkey->at);307 hoffset + tkey->at);
151 goto bad;308 goto bad;
152 }309 }
153 d = skb_header_pointer(skb, off + tkey->at, 1,310 d = skb_header_pointer(skb, hoffset + tkey->at, 1,
154 &_d);311 &_d);
155 if (!d)312 if (!d)
156 goto bad;313 goto bad;
@@ -163,19 +320,19 @@ static int tcf_pedit(struct sk_buff *skb, const struct tc_action *a, · 6 unmodified lines
163 goto bad;320 goto bad;
164 }321 }
165 322
166 if (!offset_valid(skb, off + offset)) {323 if (!offset_valid(skb, hoffset + offset)) {
167 pr_info("tc filter pedit offset %d out of bounds\n",324 pr_info("tc filter pedit offset %d out of bounds\n",
168 offset);325 hoffset + offset);
169 goto bad;326 goto bad;
170 }327 }
171 328
172 ptr = skb_header_pointer(skb, off + offset, 4, &_data);329 ptr = skb_header_pointer(skb, hoffset + offset, 4, &_data);
173 if (!ptr)330 if (!ptr)
174 goto bad;331 goto bad;
175 /* just do it, baby */332 /* just do it, baby */
176 *ptr = ((*ptr & tkey->mask) ^ tkey->val);333 *ptr = ((*ptr & tkey->mask) ^ tkey->val);
177 if (ptr == &_data)334 if (ptr == &_data)
178 skb_store_bits(skb, off + offset, ptr, 4);335 skb_store_bits(skb, hoffset + offset, ptr, 4);
179 }336 }
180 337
181 goto done;338 goto done;
@@ -215,8 +372,15 @@ static int tcf_pedit_dump(struct sk_buff *skb, struct tc_action *a, · 33 unmodified lines
215 opt->refcnt = p->tcf_refcnt - ref;372 opt->refcnt = p->tcf_refcnt - ref;
216 opt->bindcnt = p->tcf_bindcnt - bind;373 opt->bindcnt = p->tcf_bindcnt - bind;
217 374
218 if (nla_put(skb, TCA_PEDIT_PARMS, s, opt))375 if (p->tcfp_keys_ex) {
219 goto nla_put_failure;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 }
220 384
221 tcf_tm_dump(&t, &p->tcf_tm);385 tcf_tm_dump(&t, &p->tcf_tm);
222 if (nla_put_64bit(skb, TCA_PEDIT_TM, sizeof(t), &t, TCA_PEDIT_PAD))386 if (nla_put_64bit(skb, TCA_PEDIT_TM, sizeof(t), &t, TCA_PEDIT_PAD))