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
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+23−0
@@ -11,10 +11,33 @@ enum { · 10 unmodified lines
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+180−16
@@ -22,6 +22,7 @@ · 21 unmodified lines
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; · 2 unmodified lines
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, · 3 unmodified lines
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, · 5 unmodified lines
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, · 6 unmodified lines
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, · 6 unmodified lines
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) · 5 unmodified lines
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) · 7 unmodified lines
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, · 6 unmodified lines
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, · 33 unmodified lines
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))