Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/net/sched/em_meta.c
15109 views
1
/*
2
* net/sched/em_meta.c Metadata ematch
3
*
4
* This program is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU General Public License
6
* as published by the Free Software Foundation; either version
7
* 2 of the License, or (at your option) any later version.
8
*
9
* Authors: Thomas Graf <[email protected]>
10
*
11
* ==========================================================================
12
*
13
* The metadata ematch compares two meta objects where each object
14
* represents either a meta value stored in the kernel or a static
15
* value provided by userspace. The objects are not provided by
16
* userspace itself but rather a definition providing the information
17
* to build them. Every object is of a certain type which must be
18
* equal to the object it is being compared to.
19
*
20
* The definition of a objects conists of the type (meta type), a
21
* identifier (meta id) and additional type specific information.
22
* The meta id is either TCF_META_TYPE_VALUE for values provided by
23
* userspace or a index to the meta operations table consisting of
24
* function pointers to type specific meta data collectors returning
25
* the value of the requested meta value.
26
*
27
* lvalue rvalue
28
* +-----------+ +-----------+
29
* | type: INT | | type: INT |
30
* def | id: DEV | | id: VALUE |
31
* | data: | | data: 3 |
32
* +-----------+ +-----------+
33
* | |
34
* ---> meta_ops[INT][DEV](...) |
35
* | |
36
* ----------- |
37
* V V
38
* +-----------+ +-----------+
39
* | type: INT | | type: INT |
40
* obj | id: DEV | | id: VALUE |
41
* | data: 2 |<--data got filled out | data: 3 |
42
* +-----------+ +-----------+
43
* | |
44
* --------------> 2 equals 3 <--------------
45
*
46
* This is a simplified schema, the complexity varies depending
47
* on the meta type. Obviously, the length of the data must also
48
* be provided for non-numeric types.
49
*
50
* Additionally, type dependent modifiers such as shift operators
51
* or mask may be applied to extend the functionaliy. As of now,
52
* the variable length type supports shifting the byte string to
53
* the right, eating up any number of octets and thus supporting
54
* wildcard interface name comparisons such as "ppp%" matching
55
* ppp0..9.
56
*
57
* NOTE: Certain meta values depend on other subsystems and are
58
* only available if that subsystem is enabled in the kernel.
59
*/
60
61
#include <linux/slab.h>
62
#include <linux/module.h>
63
#include <linux/types.h>
64
#include <linux/kernel.h>
65
#include <linux/sched.h>
66
#include <linux/string.h>
67
#include <linux/skbuff.h>
68
#include <linux/random.h>
69
#include <linux/if_vlan.h>
70
#include <linux/tc_ematch/tc_em_meta.h>
71
#include <net/dst.h>
72
#include <net/route.h>
73
#include <net/pkt_cls.h>
74
#include <net/sock.h>
75
76
struct meta_obj {
77
unsigned long value;
78
unsigned int len;
79
};
80
81
struct meta_value {
82
struct tcf_meta_val hdr;
83
unsigned long val;
84
unsigned int len;
85
};
86
87
struct meta_match {
88
struct meta_value lvalue;
89
struct meta_value rvalue;
90
};
91
92
static inline int meta_id(struct meta_value *v)
93
{
94
return TCF_META_ID(v->hdr.kind);
95
}
96
97
static inline int meta_type(struct meta_value *v)
98
{
99
return TCF_META_TYPE(v->hdr.kind);
100
}
101
102
#define META_COLLECTOR(FUNC) static void meta_##FUNC(struct sk_buff *skb, \
103
struct tcf_pkt_info *info, struct meta_value *v, \
104
struct meta_obj *dst, int *err)
105
106
/**************************************************************************
107
* System status & misc
108
**************************************************************************/
109
110
META_COLLECTOR(int_random)
111
{
112
get_random_bytes(&dst->value, sizeof(dst->value));
113
}
114
115
static inline unsigned long fixed_loadavg(int load)
116
{
117
int rnd_load = load + (FIXED_1/200);
118
int rnd_frac = ((rnd_load & (FIXED_1-1)) * 100) >> FSHIFT;
119
120
return ((rnd_load >> FSHIFT) * 100) + rnd_frac;
121
}
122
123
META_COLLECTOR(int_loadavg_0)
124
{
125
dst->value = fixed_loadavg(avenrun[0]);
126
}
127
128
META_COLLECTOR(int_loadavg_1)
129
{
130
dst->value = fixed_loadavg(avenrun[1]);
131
}
132
133
META_COLLECTOR(int_loadavg_2)
134
{
135
dst->value = fixed_loadavg(avenrun[2]);
136
}
137
138
/**************************************************************************
139
* Device names & indices
140
**************************************************************************/
141
142
static inline int int_dev(struct net_device *dev, struct meta_obj *dst)
143
{
144
if (unlikely(dev == NULL))
145
return -1;
146
147
dst->value = dev->ifindex;
148
return 0;
149
}
150
151
static inline int var_dev(struct net_device *dev, struct meta_obj *dst)
152
{
153
if (unlikely(dev == NULL))
154
return -1;
155
156
dst->value = (unsigned long) dev->name;
157
dst->len = strlen(dev->name);
158
return 0;
159
}
160
161
META_COLLECTOR(int_dev)
162
{
163
*err = int_dev(skb->dev, dst);
164
}
165
166
META_COLLECTOR(var_dev)
167
{
168
*err = var_dev(skb->dev, dst);
169
}
170
171
/**************************************************************************
172
* vlan tag
173
**************************************************************************/
174
175
META_COLLECTOR(int_vlan_tag)
176
{
177
unsigned short tag;
178
179
tag = vlan_tx_tag_get(skb);
180
if (!tag && __vlan_get_tag(skb, &tag))
181
*err = -1;
182
else
183
dst->value = tag;
184
}
185
186
187
188
/**************************************************************************
189
* skb attributes
190
**************************************************************************/
191
192
META_COLLECTOR(int_priority)
193
{
194
dst->value = skb->priority;
195
}
196
197
META_COLLECTOR(int_protocol)
198
{
199
/* Let userspace take care of the byte ordering */
200
dst->value = skb->protocol;
201
}
202
203
META_COLLECTOR(int_pkttype)
204
{
205
dst->value = skb->pkt_type;
206
}
207
208
META_COLLECTOR(int_pktlen)
209
{
210
dst->value = skb->len;
211
}
212
213
META_COLLECTOR(int_datalen)
214
{
215
dst->value = skb->data_len;
216
}
217
218
META_COLLECTOR(int_maclen)
219
{
220
dst->value = skb->mac_len;
221
}
222
223
META_COLLECTOR(int_rxhash)
224
{
225
dst->value = skb_get_rxhash(skb);
226
}
227
228
/**************************************************************************
229
* Netfilter
230
**************************************************************************/
231
232
META_COLLECTOR(int_mark)
233
{
234
dst->value = skb->mark;
235
}
236
237
/**************************************************************************
238
* Traffic Control
239
**************************************************************************/
240
241
META_COLLECTOR(int_tcindex)
242
{
243
dst->value = skb->tc_index;
244
}
245
246
/**************************************************************************
247
* Routing
248
**************************************************************************/
249
250
META_COLLECTOR(int_rtclassid)
251
{
252
if (unlikely(skb_dst(skb) == NULL))
253
*err = -1;
254
else
255
#ifdef CONFIG_IP_ROUTE_CLASSID
256
dst->value = skb_dst(skb)->tclassid;
257
#else
258
dst->value = 0;
259
#endif
260
}
261
262
META_COLLECTOR(int_rtiif)
263
{
264
if (unlikely(skb_rtable(skb) == NULL))
265
*err = -1;
266
else
267
dst->value = skb_rtable(skb)->rt_iif;
268
}
269
270
/**************************************************************************
271
* Socket Attributes
272
**************************************************************************/
273
274
#define SKIP_NONLOCAL(skb) \
275
if (unlikely(skb->sk == NULL)) { \
276
*err = -1; \
277
return; \
278
}
279
280
META_COLLECTOR(int_sk_family)
281
{
282
SKIP_NONLOCAL(skb);
283
dst->value = skb->sk->sk_family;
284
}
285
286
META_COLLECTOR(int_sk_state)
287
{
288
SKIP_NONLOCAL(skb);
289
dst->value = skb->sk->sk_state;
290
}
291
292
META_COLLECTOR(int_sk_reuse)
293
{
294
SKIP_NONLOCAL(skb);
295
dst->value = skb->sk->sk_reuse;
296
}
297
298
META_COLLECTOR(int_sk_bound_if)
299
{
300
SKIP_NONLOCAL(skb);
301
/* No error if bound_dev_if is 0, legal userspace check */
302
dst->value = skb->sk->sk_bound_dev_if;
303
}
304
305
META_COLLECTOR(var_sk_bound_if)
306
{
307
SKIP_NONLOCAL(skb);
308
309
if (skb->sk->sk_bound_dev_if == 0) {
310
dst->value = (unsigned long) "any";
311
dst->len = 3;
312
} else {
313
struct net_device *dev;
314
315
rcu_read_lock();
316
dev = dev_get_by_index_rcu(sock_net(skb->sk),
317
skb->sk->sk_bound_dev_if);
318
*err = var_dev(dev, dst);
319
rcu_read_unlock();
320
}
321
}
322
323
META_COLLECTOR(int_sk_refcnt)
324
{
325
SKIP_NONLOCAL(skb);
326
dst->value = atomic_read(&skb->sk->sk_refcnt);
327
}
328
329
META_COLLECTOR(int_sk_rcvbuf)
330
{
331
SKIP_NONLOCAL(skb);
332
dst->value = skb->sk->sk_rcvbuf;
333
}
334
335
META_COLLECTOR(int_sk_shutdown)
336
{
337
SKIP_NONLOCAL(skb);
338
dst->value = skb->sk->sk_shutdown;
339
}
340
341
META_COLLECTOR(int_sk_proto)
342
{
343
SKIP_NONLOCAL(skb);
344
dst->value = skb->sk->sk_protocol;
345
}
346
347
META_COLLECTOR(int_sk_type)
348
{
349
SKIP_NONLOCAL(skb);
350
dst->value = skb->sk->sk_type;
351
}
352
353
META_COLLECTOR(int_sk_rmem_alloc)
354
{
355
SKIP_NONLOCAL(skb);
356
dst->value = sk_rmem_alloc_get(skb->sk);
357
}
358
359
META_COLLECTOR(int_sk_wmem_alloc)
360
{
361
SKIP_NONLOCAL(skb);
362
dst->value = sk_wmem_alloc_get(skb->sk);
363
}
364
365
META_COLLECTOR(int_sk_omem_alloc)
366
{
367
SKIP_NONLOCAL(skb);
368
dst->value = atomic_read(&skb->sk->sk_omem_alloc);
369
}
370
371
META_COLLECTOR(int_sk_rcv_qlen)
372
{
373
SKIP_NONLOCAL(skb);
374
dst->value = skb->sk->sk_receive_queue.qlen;
375
}
376
377
META_COLLECTOR(int_sk_snd_qlen)
378
{
379
SKIP_NONLOCAL(skb);
380
dst->value = skb->sk->sk_write_queue.qlen;
381
}
382
383
META_COLLECTOR(int_sk_wmem_queued)
384
{
385
SKIP_NONLOCAL(skb);
386
dst->value = skb->sk->sk_wmem_queued;
387
}
388
389
META_COLLECTOR(int_sk_fwd_alloc)
390
{
391
SKIP_NONLOCAL(skb);
392
dst->value = skb->sk->sk_forward_alloc;
393
}
394
395
META_COLLECTOR(int_sk_sndbuf)
396
{
397
SKIP_NONLOCAL(skb);
398
dst->value = skb->sk->sk_sndbuf;
399
}
400
401
META_COLLECTOR(int_sk_alloc)
402
{
403
SKIP_NONLOCAL(skb);
404
dst->value = (__force int) skb->sk->sk_allocation;
405
}
406
407
META_COLLECTOR(int_sk_route_caps)
408
{
409
SKIP_NONLOCAL(skb);
410
dst->value = skb->sk->sk_route_caps;
411
}
412
413
META_COLLECTOR(int_sk_hash)
414
{
415
SKIP_NONLOCAL(skb);
416
dst->value = skb->sk->sk_hash;
417
}
418
419
META_COLLECTOR(int_sk_lingertime)
420
{
421
SKIP_NONLOCAL(skb);
422
dst->value = skb->sk->sk_lingertime / HZ;
423
}
424
425
META_COLLECTOR(int_sk_err_qlen)
426
{
427
SKIP_NONLOCAL(skb);
428
dst->value = skb->sk->sk_error_queue.qlen;
429
}
430
431
META_COLLECTOR(int_sk_ack_bl)
432
{
433
SKIP_NONLOCAL(skb);
434
dst->value = skb->sk->sk_ack_backlog;
435
}
436
437
META_COLLECTOR(int_sk_max_ack_bl)
438
{
439
SKIP_NONLOCAL(skb);
440
dst->value = skb->sk->sk_max_ack_backlog;
441
}
442
443
META_COLLECTOR(int_sk_prio)
444
{
445
SKIP_NONLOCAL(skb);
446
dst->value = skb->sk->sk_priority;
447
}
448
449
META_COLLECTOR(int_sk_rcvlowat)
450
{
451
SKIP_NONLOCAL(skb);
452
dst->value = skb->sk->sk_rcvlowat;
453
}
454
455
META_COLLECTOR(int_sk_rcvtimeo)
456
{
457
SKIP_NONLOCAL(skb);
458
dst->value = skb->sk->sk_rcvtimeo / HZ;
459
}
460
461
META_COLLECTOR(int_sk_sndtimeo)
462
{
463
SKIP_NONLOCAL(skb);
464
dst->value = skb->sk->sk_sndtimeo / HZ;
465
}
466
467
META_COLLECTOR(int_sk_sendmsg_off)
468
{
469
SKIP_NONLOCAL(skb);
470
dst->value = skb->sk->sk_sndmsg_off;
471
}
472
473
META_COLLECTOR(int_sk_write_pend)
474
{
475
SKIP_NONLOCAL(skb);
476
dst->value = skb->sk->sk_write_pending;
477
}
478
479
/**************************************************************************
480
* Meta value collectors assignment table
481
**************************************************************************/
482
483
struct meta_ops {
484
void (*get)(struct sk_buff *, struct tcf_pkt_info *,
485
struct meta_value *, struct meta_obj *, int *);
486
};
487
488
#define META_ID(name) TCF_META_ID_##name
489
#define META_FUNC(name) { .get = meta_##name }
490
491
/* Meta value operations table listing all meta value collectors and
492
* assigns them to a type and meta id. */
493
static struct meta_ops __meta_ops[TCF_META_TYPE_MAX + 1][TCF_META_ID_MAX + 1] = {
494
[TCF_META_TYPE_VAR] = {
495
[META_ID(DEV)] = META_FUNC(var_dev),
496
[META_ID(SK_BOUND_IF)] = META_FUNC(var_sk_bound_if),
497
},
498
[TCF_META_TYPE_INT] = {
499
[META_ID(RANDOM)] = META_FUNC(int_random),
500
[META_ID(LOADAVG_0)] = META_FUNC(int_loadavg_0),
501
[META_ID(LOADAVG_1)] = META_FUNC(int_loadavg_1),
502
[META_ID(LOADAVG_2)] = META_FUNC(int_loadavg_2),
503
[META_ID(DEV)] = META_FUNC(int_dev),
504
[META_ID(PRIORITY)] = META_FUNC(int_priority),
505
[META_ID(PROTOCOL)] = META_FUNC(int_protocol),
506
[META_ID(PKTTYPE)] = META_FUNC(int_pkttype),
507
[META_ID(PKTLEN)] = META_FUNC(int_pktlen),
508
[META_ID(DATALEN)] = META_FUNC(int_datalen),
509
[META_ID(MACLEN)] = META_FUNC(int_maclen),
510
[META_ID(NFMARK)] = META_FUNC(int_mark),
511
[META_ID(TCINDEX)] = META_FUNC(int_tcindex),
512
[META_ID(RTCLASSID)] = META_FUNC(int_rtclassid),
513
[META_ID(RTIIF)] = META_FUNC(int_rtiif),
514
[META_ID(SK_FAMILY)] = META_FUNC(int_sk_family),
515
[META_ID(SK_STATE)] = META_FUNC(int_sk_state),
516
[META_ID(SK_REUSE)] = META_FUNC(int_sk_reuse),
517
[META_ID(SK_BOUND_IF)] = META_FUNC(int_sk_bound_if),
518
[META_ID(SK_REFCNT)] = META_FUNC(int_sk_refcnt),
519
[META_ID(SK_RCVBUF)] = META_FUNC(int_sk_rcvbuf),
520
[META_ID(SK_SNDBUF)] = META_FUNC(int_sk_sndbuf),
521
[META_ID(SK_SHUTDOWN)] = META_FUNC(int_sk_shutdown),
522
[META_ID(SK_PROTO)] = META_FUNC(int_sk_proto),
523
[META_ID(SK_TYPE)] = META_FUNC(int_sk_type),
524
[META_ID(SK_RMEM_ALLOC)] = META_FUNC(int_sk_rmem_alloc),
525
[META_ID(SK_WMEM_ALLOC)] = META_FUNC(int_sk_wmem_alloc),
526
[META_ID(SK_OMEM_ALLOC)] = META_FUNC(int_sk_omem_alloc),
527
[META_ID(SK_WMEM_QUEUED)] = META_FUNC(int_sk_wmem_queued),
528
[META_ID(SK_RCV_QLEN)] = META_FUNC(int_sk_rcv_qlen),
529
[META_ID(SK_SND_QLEN)] = META_FUNC(int_sk_snd_qlen),
530
[META_ID(SK_ERR_QLEN)] = META_FUNC(int_sk_err_qlen),
531
[META_ID(SK_FORWARD_ALLOCS)] = META_FUNC(int_sk_fwd_alloc),
532
[META_ID(SK_ALLOCS)] = META_FUNC(int_sk_alloc),
533
[META_ID(SK_ROUTE_CAPS)] = META_FUNC(int_sk_route_caps),
534
[META_ID(SK_HASH)] = META_FUNC(int_sk_hash),
535
[META_ID(SK_LINGERTIME)] = META_FUNC(int_sk_lingertime),
536
[META_ID(SK_ACK_BACKLOG)] = META_FUNC(int_sk_ack_bl),
537
[META_ID(SK_MAX_ACK_BACKLOG)] = META_FUNC(int_sk_max_ack_bl),
538
[META_ID(SK_PRIO)] = META_FUNC(int_sk_prio),
539
[META_ID(SK_RCVLOWAT)] = META_FUNC(int_sk_rcvlowat),
540
[META_ID(SK_RCVTIMEO)] = META_FUNC(int_sk_rcvtimeo),
541
[META_ID(SK_SNDTIMEO)] = META_FUNC(int_sk_sndtimeo),
542
[META_ID(SK_SENDMSG_OFF)] = META_FUNC(int_sk_sendmsg_off),
543
[META_ID(SK_WRITE_PENDING)] = META_FUNC(int_sk_write_pend),
544
[META_ID(VLAN_TAG)] = META_FUNC(int_vlan_tag),
545
[META_ID(RXHASH)] = META_FUNC(int_rxhash),
546
}
547
};
548
549
static inline struct meta_ops *meta_ops(struct meta_value *val)
550
{
551
return &__meta_ops[meta_type(val)][meta_id(val)];
552
}
553
554
/**************************************************************************
555
* Type specific operations for TCF_META_TYPE_VAR
556
**************************************************************************/
557
558
static int meta_var_compare(struct meta_obj *a, struct meta_obj *b)
559
{
560
int r = a->len - b->len;
561
562
if (r == 0)
563
r = memcmp((void *) a->value, (void *) b->value, a->len);
564
565
return r;
566
}
567
568
static int meta_var_change(struct meta_value *dst, struct nlattr *nla)
569
{
570
int len = nla_len(nla);
571
572
dst->val = (unsigned long)kmemdup(nla_data(nla), len, GFP_KERNEL);
573
if (dst->val == 0UL)
574
return -ENOMEM;
575
dst->len = len;
576
return 0;
577
}
578
579
static void meta_var_destroy(struct meta_value *v)
580
{
581
kfree((void *) v->val);
582
}
583
584
static void meta_var_apply_extras(struct meta_value *v,
585
struct meta_obj *dst)
586
{
587
int shift = v->hdr.shift;
588
589
if (shift && shift < dst->len)
590
dst->len -= shift;
591
}
592
593
static int meta_var_dump(struct sk_buff *skb, struct meta_value *v, int tlv)
594
{
595
if (v->val && v->len)
596
NLA_PUT(skb, tlv, v->len, (void *) v->val);
597
return 0;
598
599
nla_put_failure:
600
return -1;
601
}
602
603
/**************************************************************************
604
* Type specific operations for TCF_META_TYPE_INT
605
**************************************************************************/
606
607
static int meta_int_compare(struct meta_obj *a, struct meta_obj *b)
608
{
609
/* Let gcc optimize it, the unlikely is not really based on
610
* some numbers but jump free code for mismatches seems
611
* more logical. */
612
if (unlikely(a->value == b->value))
613
return 0;
614
else if (a->value < b->value)
615
return -1;
616
else
617
return 1;
618
}
619
620
static int meta_int_change(struct meta_value *dst, struct nlattr *nla)
621
{
622
if (nla_len(nla) >= sizeof(unsigned long)) {
623
dst->val = *(unsigned long *) nla_data(nla);
624
dst->len = sizeof(unsigned long);
625
} else if (nla_len(nla) == sizeof(u32)) {
626
dst->val = nla_get_u32(nla);
627
dst->len = sizeof(u32);
628
} else
629
return -EINVAL;
630
631
return 0;
632
}
633
634
static void meta_int_apply_extras(struct meta_value *v,
635
struct meta_obj *dst)
636
{
637
if (v->hdr.shift)
638
dst->value >>= v->hdr.shift;
639
640
if (v->val)
641
dst->value &= v->val;
642
}
643
644
static int meta_int_dump(struct sk_buff *skb, struct meta_value *v, int tlv)
645
{
646
if (v->len == sizeof(unsigned long))
647
NLA_PUT(skb, tlv, sizeof(unsigned long), &v->val);
648
else if (v->len == sizeof(u32))
649
NLA_PUT_U32(skb, tlv, v->val);
650
651
return 0;
652
653
nla_put_failure:
654
return -1;
655
}
656
657
/**************************************************************************
658
* Type specific operations table
659
**************************************************************************/
660
661
struct meta_type_ops {
662
void (*destroy)(struct meta_value *);
663
int (*compare)(struct meta_obj *, struct meta_obj *);
664
int (*change)(struct meta_value *, struct nlattr *);
665
void (*apply_extras)(struct meta_value *, struct meta_obj *);
666
int (*dump)(struct sk_buff *, struct meta_value *, int);
667
};
668
669
static struct meta_type_ops __meta_type_ops[TCF_META_TYPE_MAX + 1] = {
670
[TCF_META_TYPE_VAR] = {
671
.destroy = meta_var_destroy,
672
.compare = meta_var_compare,
673
.change = meta_var_change,
674
.apply_extras = meta_var_apply_extras,
675
.dump = meta_var_dump
676
},
677
[TCF_META_TYPE_INT] = {
678
.compare = meta_int_compare,
679
.change = meta_int_change,
680
.apply_extras = meta_int_apply_extras,
681
.dump = meta_int_dump
682
}
683
};
684
685
static inline struct meta_type_ops *meta_type_ops(struct meta_value *v)
686
{
687
return &__meta_type_ops[meta_type(v)];
688
}
689
690
/**************************************************************************
691
* Core
692
**************************************************************************/
693
694
static int meta_get(struct sk_buff *skb, struct tcf_pkt_info *info,
695
struct meta_value *v, struct meta_obj *dst)
696
{
697
int err = 0;
698
699
if (meta_id(v) == TCF_META_ID_VALUE) {
700
dst->value = v->val;
701
dst->len = v->len;
702
return 0;
703
}
704
705
meta_ops(v)->get(skb, info, v, dst, &err);
706
if (err < 0)
707
return err;
708
709
if (meta_type_ops(v)->apply_extras)
710
meta_type_ops(v)->apply_extras(v, dst);
711
712
return 0;
713
}
714
715
static int em_meta_match(struct sk_buff *skb, struct tcf_ematch *m,
716
struct tcf_pkt_info *info)
717
{
718
int r;
719
struct meta_match *meta = (struct meta_match *) m->data;
720
struct meta_obj l_value, r_value;
721
722
if (meta_get(skb, info, &meta->lvalue, &l_value) < 0 ||
723
meta_get(skb, info, &meta->rvalue, &r_value) < 0)
724
return 0;
725
726
r = meta_type_ops(&meta->lvalue)->compare(&l_value, &r_value);
727
728
switch (meta->lvalue.hdr.op) {
729
case TCF_EM_OPND_EQ:
730
return !r;
731
case TCF_EM_OPND_LT:
732
return r < 0;
733
case TCF_EM_OPND_GT:
734
return r > 0;
735
}
736
737
return 0;
738
}
739
740
static void meta_delete(struct meta_match *meta)
741
{
742
if (meta) {
743
struct meta_type_ops *ops = meta_type_ops(&meta->lvalue);
744
745
if (ops && ops->destroy) {
746
ops->destroy(&meta->lvalue);
747
ops->destroy(&meta->rvalue);
748
}
749
}
750
751
kfree(meta);
752
}
753
754
static inline int meta_change_data(struct meta_value *dst, struct nlattr *nla)
755
{
756
if (nla) {
757
if (nla_len(nla) == 0)
758
return -EINVAL;
759
760
return meta_type_ops(dst)->change(dst, nla);
761
}
762
763
return 0;
764
}
765
766
static inline int meta_is_supported(struct meta_value *val)
767
{
768
return !meta_id(val) || meta_ops(val)->get;
769
}
770
771
static const struct nla_policy meta_policy[TCA_EM_META_MAX + 1] = {
772
[TCA_EM_META_HDR] = { .len = sizeof(struct tcf_meta_hdr) },
773
};
774
775
static int em_meta_change(struct tcf_proto *tp, void *data, int len,
776
struct tcf_ematch *m)
777
{
778
int err;
779
struct nlattr *tb[TCA_EM_META_MAX + 1];
780
struct tcf_meta_hdr *hdr;
781
struct meta_match *meta = NULL;
782
783
err = nla_parse(tb, TCA_EM_META_MAX, data, len, meta_policy);
784
if (err < 0)
785
goto errout;
786
787
err = -EINVAL;
788
if (tb[TCA_EM_META_HDR] == NULL)
789
goto errout;
790
hdr = nla_data(tb[TCA_EM_META_HDR]);
791
792
if (TCF_META_TYPE(hdr->left.kind) != TCF_META_TYPE(hdr->right.kind) ||
793
TCF_META_TYPE(hdr->left.kind) > TCF_META_TYPE_MAX ||
794
TCF_META_ID(hdr->left.kind) > TCF_META_ID_MAX ||
795
TCF_META_ID(hdr->right.kind) > TCF_META_ID_MAX)
796
goto errout;
797
798
meta = kzalloc(sizeof(*meta), GFP_KERNEL);
799
if (meta == NULL)
800
goto errout;
801
802
memcpy(&meta->lvalue.hdr, &hdr->left, sizeof(hdr->left));
803
memcpy(&meta->rvalue.hdr, &hdr->right, sizeof(hdr->right));
804
805
if (!meta_is_supported(&meta->lvalue) ||
806
!meta_is_supported(&meta->rvalue)) {
807
err = -EOPNOTSUPP;
808
goto errout;
809
}
810
811
if (meta_change_data(&meta->lvalue, tb[TCA_EM_META_LVALUE]) < 0 ||
812
meta_change_data(&meta->rvalue, tb[TCA_EM_META_RVALUE]) < 0)
813
goto errout;
814
815
m->datalen = sizeof(*meta);
816
m->data = (unsigned long) meta;
817
818
err = 0;
819
errout:
820
if (err && meta)
821
meta_delete(meta);
822
return err;
823
}
824
825
static void em_meta_destroy(struct tcf_proto *tp, struct tcf_ematch *m)
826
{
827
if (m)
828
meta_delete((struct meta_match *) m->data);
829
}
830
831
static int em_meta_dump(struct sk_buff *skb, struct tcf_ematch *em)
832
{
833
struct meta_match *meta = (struct meta_match *) em->data;
834
struct tcf_meta_hdr hdr;
835
struct meta_type_ops *ops;
836
837
memset(&hdr, 0, sizeof(hdr));
838
memcpy(&hdr.left, &meta->lvalue.hdr, sizeof(hdr.left));
839
memcpy(&hdr.right, &meta->rvalue.hdr, sizeof(hdr.right));
840
841
NLA_PUT(skb, TCA_EM_META_HDR, sizeof(hdr), &hdr);
842
843
ops = meta_type_ops(&meta->lvalue);
844
if (ops->dump(skb, &meta->lvalue, TCA_EM_META_LVALUE) < 0 ||
845
ops->dump(skb, &meta->rvalue, TCA_EM_META_RVALUE) < 0)
846
goto nla_put_failure;
847
848
return 0;
849
850
nla_put_failure:
851
return -1;
852
}
853
854
static struct tcf_ematch_ops em_meta_ops = {
855
.kind = TCF_EM_META,
856
.change = em_meta_change,
857
.match = em_meta_match,
858
.destroy = em_meta_destroy,
859
.dump = em_meta_dump,
860
.owner = THIS_MODULE,
861
.link = LIST_HEAD_INIT(em_meta_ops.link)
862
};
863
864
static int __init init_em_meta(void)
865
{
866
return tcf_em_register(&em_meta_ops);
867
}
868
869
static void __exit exit_em_meta(void)
870
{
871
tcf_em_unregister(&em_meta_ops);
872
}
873
874
MODULE_LICENSE("GPL");
875
876
module_init(init_em_meta);
877
module_exit(exit_em_meta);
878
879
MODULE_ALIAS_TCF_EMATCH(TCF_EM_META);
880
881