8
8
* Development of this code funded by Astaro AG (http://www.astaro.com/)
9
9
*/
10
10
11
+ #include <asm/unaligned.h>
11
12
#include <linux/kernel.h>
12
13
#include <linux/init.h>
13
14
#include <linux/module.h>
@@ -23,6 +24,7 @@ struct nft_exthdr {
23
24
u8 len ;
24
25
u8 op ;
25
26
enum nft_registers dreg :8 ;
27
+ enum nft_registers sreg :8 ;
26
28
u8 flags ;
27
29
};
28
30
@@ -124,6 +126,88 @@ static void nft_exthdr_tcp_eval(const struct nft_expr *expr,
124
126
regs -> verdict .code = NFT_BREAK ;
125
127
}
126
128
129
+ static void nft_exthdr_tcp_set_eval (const struct nft_expr * expr ,
130
+ struct nft_regs * regs ,
131
+ const struct nft_pktinfo * pkt )
132
+ {
133
+ u8 buff [sizeof (struct tcphdr ) + MAX_TCP_OPTION_SPACE ];
134
+ struct nft_exthdr * priv = nft_expr_priv (expr );
135
+ unsigned int i , optl , tcphdr_len , offset ;
136
+ struct tcphdr * tcph ;
137
+ u8 * opt ;
138
+ u32 src ;
139
+
140
+ tcph = nft_tcp_header_pointer (pkt , sizeof (buff ), buff , & tcphdr_len );
141
+ if (!tcph )
142
+ return ;
143
+
144
+ opt = (u8 * )tcph ;
145
+ for (i = sizeof (* tcph ); i < tcphdr_len - 1 ; i += optl ) {
146
+ union {
147
+ u8 octet ;
148
+ __be16 v16 ;
149
+ __be32 v32 ;
150
+ } old , new ;
151
+
152
+ optl = optlen (opt , i );
153
+
154
+ if (priv -> type != opt [i ])
155
+ continue ;
156
+
157
+ if (i + optl > tcphdr_len || priv -> len + priv -> offset > optl )
158
+ return ;
159
+
160
+ if (!skb_make_writable (pkt -> skb , pkt -> xt .thoff + i + priv -> len ))
161
+ return ;
162
+
163
+ tcph = nft_tcp_header_pointer (pkt , sizeof (buff ), buff ,
164
+ & tcphdr_len );
165
+ if (!tcph )
166
+ return ;
167
+
168
+ src = regs -> data [priv -> sreg ];
169
+ offset = i + priv -> offset ;
170
+
171
+ switch (priv -> len ) {
172
+ case 2 :
173
+ old .v16 = get_unaligned ((u16 * )(opt + offset ));
174
+ new .v16 = src ;
175
+
176
+ switch (priv -> type ) {
177
+ case TCPOPT_MSS :
178
+ /* increase can cause connection to stall */
179
+ if (ntohs (old .v16 ) <= ntohs (new .v16 ))
180
+ return ;
181
+ break ;
182
+ }
183
+
184
+ if (old .v16 == new .v16 )
185
+ return ;
186
+
187
+ put_unaligned (new .v16 , (u16 * )(opt + offset ));
188
+ inet_proto_csum_replace2 (& tcph -> check , pkt -> skb ,
189
+ old .v16 , new .v16 , false);
190
+ break ;
191
+ case 4 :
192
+ new .v32 = src ;
193
+ old .v32 = get_unaligned ((u32 * )(opt + offset ));
194
+
195
+ if (old .v32 == new .v32 )
196
+ return ;
197
+
198
+ put_unaligned (new .v32 , (u32 * )(opt + offset ));
199
+ inet_proto_csum_replace4 (& tcph -> check , pkt -> skb ,
200
+ old .v32 , new .v32 , false);
201
+ break ;
202
+ default :
203
+ WARN_ON_ONCE (1 );
204
+ break ;
205
+ }
206
+
207
+ return ;
208
+ }
209
+ }
210
+
127
211
static const struct nla_policy nft_exthdr_policy [NFTA_EXTHDR_MAX + 1 ] = {
128
212
[NFTA_EXTHDR_DREG ] = { .type = NLA_U32 },
129
213
[NFTA_EXTHDR_TYPE ] = { .type = NLA_U8 },
@@ -180,6 +264,55 @@ static int nft_exthdr_init(const struct nft_ctx *ctx,
180
264
NFT_DATA_VALUE , priv -> len );
181
265
}
182
266
267
+ static int nft_exthdr_tcp_set_init (const struct nft_ctx * ctx ,
268
+ const struct nft_expr * expr ,
269
+ const struct nlattr * const tb [])
270
+ {
271
+ struct nft_exthdr * priv = nft_expr_priv (expr );
272
+ u32 offset , len , flags = 0 , op = NFT_EXTHDR_OP_IPV6 ;
273
+ int err ;
274
+
275
+ if (!tb [NFTA_EXTHDR_SREG ] ||
276
+ !tb [NFTA_EXTHDR_TYPE ] ||
277
+ !tb [NFTA_EXTHDR_OFFSET ] ||
278
+ !tb [NFTA_EXTHDR_LEN ])
279
+ return - EINVAL ;
280
+
281
+ if (tb [NFTA_EXTHDR_DREG ] || tb [NFTA_EXTHDR_FLAGS ])
282
+ return - EINVAL ;
283
+
284
+ err = nft_parse_u32_check (tb [NFTA_EXTHDR_OFFSET ], U8_MAX , & offset );
285
+ if (err < 0 )
286
+ return err ;
287
+
288
+ err = nft_parse_u32_check (tb [NFTA_EXTHDR_LEN ], U8_MAX , & len );
289
+ if (err < 0 )
290
+ return err ;
291
+
292
+ if (offset < 2 )
293
+ return - EOPNOTSUPP ;
294
+
295
+ switch (len ) {
296
+ case 2 : break ;
297
+ case 4 : break ;
298
+ default :
299
+ return - EOPNOTSUPP ;
300
+ }
301
+
302
+ err = nft_parse_u32_check (tb [NFTA_EXTHDR_OP ], U8_MAX , & op );
303
+ if (err < 0 )
304
+ return err ;
305
+
306
+ priv -> type = nla_get_u8 (tb [NFTA_EXTHDR_TYPE ]);
307
+ priv -> offset = offset ;
308
+ priv -> len = len ;
309
+ priv -> sreg = nft_parse_register (tb [NFTA_EXTHDR_SREG ]);
310
+ priv -> flags = flags ;
311
+ priv -> op = op ;
312
+
313
+ return nft_validate_register_load (priv -> sreg , priv -> len );
314
+ }
315
+
183
316
static int nft_exthdr_dump_common (struct sk_buff * skb , const struct nft_exthdr * priv )
184
317
{
185
318
if (nla_put_u8 (skb , NFTA_EXTHDR_TYPE , priv -> type ))
@@ -208,6 +341,16 @@ static int nft_exthdr_dump(struct sk_buff *skb, const struct nft_expr *expr)
208
341
return nft_exthdr_dump_common (skb , priv );
209
342
}
210
343
344
+ static int nft_exthdr_dump_set (struct sk_buff * skb , const struct nft_expr * expr )
345
+ {
346
+ const struct nft_exthdr * priv = nft_expr_priv (expr );
347
+
348
+ if (nft_dump_register (skb , NFTA_EXTHDR_SREG , priv -> sreg ))
349
+ return -1 ;
350
+
351
+ return nft_exthdr_dump_common (skb , priv );
352
+ }
353
+
211
354
static struct nft_expr_type nft_exthdr_type ;
212
355
static const struct nft_expr_ops nft_exthdr_ipv6_ops = {
213
356
.type = & nft_exthdr_type ,
@@ -225,6 +368,14 @@ static const struct nft_expr_ops nft_exthdr_tcp_ops = {
225
368
.dump = nft_exthdr_dump ,
226
369
};
227
370
371
+ static const struct nft_expr_ops nft_exthdr_tcp_set_ops = {
372
+ .type = & nft_exthdr_type ,
373
+ .size = NFT_EXPR_SIZE (sizeof (struct nft_exthdr )),
374
+ .eval = nft_exthdr_tcp_set_eval ,
375
+ .init = nft_exthdr_tcp_set_init ,
376
+ .dump = nft_exthdr_dump_set ,
377
+ };
378
+
228
379
static const struct nft_expr_ops *
229
380
nft_exthdr_select_ops (const struct nft_ctx * ctx ,
230
381
const struct nlattr * const tb [])
@@ -234,12 +385,21 @@ nft_exthdr_select_ops(const struct nft_ctx *ctx,
234
385
if (!tb [NFTA_EXTHDR_OP ])
235
386
return & nft_exthdr_ipv6_ops ;
236
387
388
+ if (tb [NFTA_EXTHDR_SREG ] && tb [NFTA_EXTHDR_DREG ])
389
+ return ERR_PTR (- EOPNOTSUPP );
390
+
237
391
op = ntohl (nla_get_u32 (tb [NFTA_EXTHDR_OP ]));
238
392
switch (op ) {
239
393
case NFT_EXTHDR_OP_TCPOPT :
240
- return & nft_exthdr_tcp_ops ;
394
+ if (tb [NFTA_EXTHDR_SREG ])
395
+ return & nft_exthdr_tcp_set_ops ;
396
+ if (tb [NFTA_EXTHDR_DREG ])
397
+ return & nft_exthdr_tcp_ops ;
398
+ break ;
241
399
case NFT_EXTHDR_OP_IPV6 :
242
- return & nft_exthdr_ipv6_ops ;
400
+ if (tb [NFTA_EXTHDR_DREG ])
401
+ return & nft_exthdr_ipv6_ops ;
402
+ break ;
243
403
}
244
404
245
405
return ERR_PTR (- EOPNOTSUPP );
0 commit comments