1*4882a593Smuzhiyun /* SPDX-License-Identifier: GPL-2.0-or-later */
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * net/sched/cls_rsvp.h Template file for RSVPv[46] classifiers.
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun /*
9*4882a593Smuzhiyun Comparing to general packet classification problem,
10*4882a593Smuzhiyun RSVP needs only sevaral relatively simple rules:
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun * (dst, protocol) are always specified,
13*4882a593Smuzhiyun so that we are able to hash them.
14*4882a593Smuzhiyun * src may be exact, or may be wildcard, so that
15*4882a593Smuzhiyun we can keep a hash table plus one wildcard entry.
16*4882a593Smuzhiyun * source port (or flow label) is important only if src is given.
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun IMPLEMENTATION.
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun We use a two level hash table: The top level is keyed by
21*4882a593Smuzhiyun destination address and protocol ID, every bucket contains a list
22*4882a593Smuzhiyun of "rsvp sessions", identified by destination address, protocol and
23*4882a593Smuzhiyun DPI(="Destination Port ID"): triple (key, mask, offset).
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun Every bucket has a smaller hash table keyed by source address
26*4882a593Smuzhiyun (cf. RSVP flowspec) and one wildcard entry for wildcard reservations.
27*4882a593Smuzhiyun Every bucket is again a list of "RSVP flows", selected by
28*4882a593Smuzhiyun source address and SPI(="Source Port ID" here rather than
29*4882a593Smuzhiyun "security parameter index"): triple (key, mask, offset).
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun NOTE 1. All the packets with IPv6 extension headers (but AH and ESP)
33*4882a593Smuzhiyun and all fragmented packets go to the best-effort traffic class.
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun NOTE 2. Two "port id"'s seems to be redundant, rfc2207 requires
37*4882a593Smuzhiyun only one "Generalized Port Identifier". So that for classic
38*4882a593Smuzhiyun ah, esp (and udp,tcp) both *pi should coincide or one of them
39*4882a593Smuzhiyun should be wildcard.
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun At first sight, this redundancy is just a waste of CPU
42*4882a593Smuzhiyun resources. But DPI and SPI add the possibility to assign different
43*4882a593Smuzhiyun priorities to GPIs. Look also at note 4 about tunnels below.
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun NOTE 3. One complication is the case of tunneled packets.
47*4882a593Smuzhiyun We implement it as following: if the first lookup
48*4882a593Smuzhiyun matches a special session with "tunnelhdr" value not zero,
49*4882a593Smuzhiyun flowid doesn't contain the true flow ID, but the tunnel ID (1...255).
50*4882a593Smuzhiyun In this case, we pull tunnelhdr bytes and restart lookup
51*4882a593Smuzhiyun with tunnel ID added to the list of keys. Simple and stupid 8)8)
52*4882a593Smuzhiyun It's enough for PIMREG and IPIP.
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun NOTE 4. Two GPIs make it possible to parse even GRE packets.
56*4882a593Smuzhiyun F.e. DPI can select ETH_P_IP (and necessary flags to make
57*4882a593Smuzhiyun tunnelhdr correct) in GRE protocol field and SPI matches
58*4882a593Smuzhiyun GRE key. Is it not nice? 8)8)
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun Well, as result, despite its simplicity, we get a pretty
62*4882a593Smuzhiyun powerful classification engine. */
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun struct rsvp_head {
66*4882a593Smuzhiyun u32 tmap[256/32];
67*4882a593Smuzhiyun u32 hgenerator;
68*4882a593Smuzhiyun u8 tgenerator;
69*4882a593Smuzhiyun struct rsvp_session __rcu *ht[256];
70*4882a593Smuzhiyun struct rcu_head rcu;
71*4882a593Smuzhiyun };
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun struct rsvp_session {
74*4882a593Smuzhiyun struct rsvp_session __rcu *next;
75*4882a593Smuzhiyun __be32 dst[RSVP_DST_LEN];
76*4882a593Smuzhiyun struct tc_rsvp_gpi dpi;
77*4882a593Smuzhiyun u8 protocol;
78*4882a593Smuzhiyun u8 tunnelid;
79*4882a593Smuzhiyun /* 16 (src,sport) hash slots, and one wildcard source slot */
80*4882a593Smuzhiyun struct rsvp_filter __rcu *ht[16 + 1];
81*4882a593Smuzhiyun struct rcu_head rcu;
82*4882a593Smuzhiyun };
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun struct rsvp_filter {
86*4882a593Smuzhiyun struct rsvp_filter __rcu *next;
87*4882a593Smuzhiyun __be32 src[RSVP_DST_LEN];
88*4882a593Smuzhiyun struct tc_rsvp_gpi spi;
89*4882a593Smuzhiyun u8 tunnelhdr;
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun struct tcf_result res;
92*4882a593Smuzhiyun struct tcf_exts exts;
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun u32 handle;
95*4882a593Smuzhiyun struct rsvp_session *sess;
96*4882a593Smuzhiyun struct rcu_work rwork;
97*4882a593Smuzhiyun };
98*4882a593Smuzhiyun
hash_dst(__be32 * dst,u8 protocol,u8 tunnelid)99*4882a593Smuzhiyun static inline unsigned int hash_dst(__be32 *dst, u8 protocol, u8 tunnelid)
100*4882a593Smuzhiyun {
101*4882a593Smuzhiyun unsigned int h = (__force __u32)dst[RSVP_DST_LEN - 1];
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun h ^= h>>16;
104*4882a593Smuzhiyun h ^= h>>8;
105*4882a593Smuzhiyun return (h ^ protocol ^ tunnelid) & 0xFF;
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun
hash_src(__be32 * src)108*4882a593Smuzhiyun static inline unsigned int hash_src(__be32 *src)
109*4882a593Smuzhiyun {
110*4882a593Smuzhiyun unsigned int h = (__force __u32)src[RSVP_DST_LEN-1];
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun h ^= h>>16;
113*4882a593Smuzhiyun h ^= h>>8;
114*4882a593Smuzhiyun h ^= h>>4;
115*4882a593Smuzhiyun return h & 0xF;
116*4882a593Smuzhiyun }
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun #define RSVP_APPLY_RESULT() \
119*4882a593Smuzhiyun { \
120*4882a593Smuzhiyun int r = tcf_exts_exec(skb, &f->exts, res); \
121*4882a593Smuzhiyun if (r < 0) \
122*4882a593Smuzhiyun continue; \
123*4882a593Smuzhiyun else if (r > 0) \
124*4882a593Smuzhiyun return r; \
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun
rsvp_classify(struct sk_buff * skb,const struct tcf_proto * tp,struct tcf_result * res)127*4882a593Smuzhiyun static int rsvp_classify(struct sk_buff *skb, const struct tcf_proto *tp,
128*4882a593Smuzhiyun struct tcf_result *res)
129*4882a593Smuzhiyun {
130*4882a593Smuzhiyun struct rsvp_head *head = rcu_dereference_bh(tp->root);
131*4882a593Smuzhiyun struct rsvp_session *s;
132*4882a593Smuzhiyun struct rsvp_filter *f;
133*4882a593Smuzhiyun unsigned int h1, h2;
134*4882a593Smuzhiyun __be32 *dst, *src;
135*4882a593Smuzhiyun u8 protocol;
136*4882a593Smuzhiyun u8 tunnelid = 0;
137*4882a593Smuzhiyun u8 *xprt;
138*4882a593Smuzhiyun #if RSVP_DST_LEN == 4
139*4882a593Smuzhiyun struct ipv6hdr *nhptr;
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun if (!pskb_network_may_pull(skb, sizeof(*nhptr)))
142*4882a593Smuzhiyun return -1;
143*4882a593Smuzhiyun nhptr = ipv6_hdr(skb);
144*4882a593Smuzhiyun #else
145*4882a593Smuzhiyun struct iphdr *nhptr;
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun if (!pskb_network_may_pull(skb, sizeof(*nhptr)))
148*4882a593Smuzhiyun return -1;
149*4882a593Smuzhiyun nhptr = ip_hdr(skb);
150*4882a593Smuzhiyun #endif
151*4882a593Smuzhiyun restart:
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun #if RSVP_DST_LEN == 4
154*4882a593Smuzhiyun src = &nhptr->saddr.s6_addr32[0];
155*4882a593Smuzhiyun dst = &nhptr->daddr.s6_addr32[0];
156*4882a593Smuzhiyun protocol = nhptr->nexthdr;
157*4882a593Smuzhiyun xprt = ((u8 *)nhptr) + sizeof(struct ipv6hdr);
158*4882a593Smuzhiyun #else
159*4882a593Smuzhiyun src = &nhptr->saddr;
160*4882a593Smuzhiyun dst = &nhptr->daddr;
161*4882a593Smuzhiyun protocol = nhptr->protocol;
162*4882a593Smuzhiyun xprt = ((u8 *)nhptr) + (nhptr->ihl<<2);
163*4882a593Smuzhiyun if (ip_is_fragment(nhptr))
164*4882a593Smuzhiyun return -1;
165*4882a593Smuzhiyun #endif
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun h1 = hash_dst(dst, protocol, tunnelid);
168*4882a593Smuzhiyun h2 = hash_src(src);
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun for (s = rcu_dereference_bh(head->ht[h1]); s;
171*4882a593Smuzhiyun s = rcu_dereference_bh(s->next)) {
172*4882a593Smuzhiyun if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN - 1] &&
173*4882a593Smuzhiyun protocol == s->protocol &&
174*4882a593Smuzhiyun !(s->dpi.mask &
175*4882a593Smuzhiyun (*(u32 *)(xprt + s->dpi.offset) ^ s->dpi.key)) &&
176*4882a593Smuzhiyun #if RSVP_DST_LEN == 4
177*4882a593Smuzhiyun dst[0] == s->dst[0] &&
178*4882a593Smuzhiyun dst[1] == s->dst[1] &&
179*4882a593Smuzhiyun dst[2] == s->dst[2] &&
180*4882a593Smuzhiyun #endif
181*4882a593Smuzhiyun tunnelid == s->tunnelid) {
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun for (f = rcu_dereference_bh(s->ht[h2]); f;
184*4882a593Smuzhiyun f = rcu_dereference_bh(f->next)) {
185*4882a593Smuzhiyun if (src[RSVP_DST_LEN-1] == f->src[RSVP_DST_LEN - 1] &&
186*4882a593Smuzhiyun !(f->spi.mask & (*(u32 *)(xprt + f->spi.offset) ^ f->spi.key))
187*4882a593Smuzhiyun #if RSVP_DST_LEN == 4
188*4882a593Smuzhiyun &&
189*4882a593Smuzhiyun src[0] == f->src[0] &&
190*4882a593Smuzhiyun src[1] == f->src[1] &&
191*4882a593Smuzhiyun src[2] == f->src[2]
192*4882a593Smuzhiyun #endif
193*4882a593Smuzhiyun ) {
194*4882a593Smuzhiyun *res = f->res;
195*4882a593Smuzhiyun RSVP_APPLY_RESULT();
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun matched:
198*4882a593Smuzhiyun if (f->tunnelhdr == 0)
199*4882a593Smuzhiyun return 0;
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun tunnelid = f->res.classid;
202*4882a593Smuzhiyun nhptr = (void *)(xprt + f->tunnelhdr - sizeof(*nhptr));
203*4882a593Smuzhiyun goto restart;
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun /* And wildcard bucket... */
208*4882a593Smuzhiyun for (f = rcu_dereference_bh(s->ht[16]); f;
209*4882a593Smuzhiyun f = rcu_dereference_bh(f->next)) {
210*4882a593Smuzhiyun *res = f->res;
211*4882a593Smuzhiyun RSVP_APPLY_RESULT();
212*4882a593Smuzhiyun goto matched;
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun return -1;
215*4882a593Smuzhiyun }
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun return -1;
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun
rsvp_replace(struct tcf_proto * tp,struct rsvp_filter * n,u32 h)220*4882a593Smuzhiyun static void rsvp_replace(struct tcf_proto *tp, struct rsvp_filter *n, u32 h)
221*4882a593Smuzhiyun {
222*4882a593Smuzhiyun struct rsvp_head *head = rtnl_dereference(tp->root);
223*4882a593Smuzhiyun struct rsvp_session *s;
224*4882a593Smuzhiyun struct rsvp_filter __rcu **ins;
225*4882a593Smuzhiyun struct rsvp_filter *pins;
226*4882a593Smuzhiyun unsigned int h1 = h & 0xFF;
227*4882a593Smuzhiyun unsigned int h2 = (h >> 8) & 0xFF;
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun for (s = rtnl_dereference(head->ht[h1]); s;
230*4882a593Smuzhiyun s = rtnl_dereference(s->next)) {
231*4882a593Smuzhiyun for (ins = &s->ht[h2], pins = rtnl_dereference(*ins); ;
232*4882a593Smuzhiyun ins = &pins->next, pins = rtnl_dereference(*ins)) {
233*4882a593Smuzhiyun if (pins->handle == h) {
234*4882a593Smuzhiyun RCU_INIT_POINTER(n->next, pins->next);
235*4882a593Smuzhiyun rcu_assign_pointer(*ins, n);
236*4882a593Smuzhiyun return;
237*4882a593Smuzhiyun }
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun }
240*4882a593Smuzhiyun
241*4882a593Smuzhiyun /* Something went wrong if we are trying to replace a non-existant
242*4882a593Smuzhiyun * node. Mind as well halt instead of silently failing.
243*4882a593Smuzhiyun */
244*4882a593Smuzhiyun BUG_ON(1);
245*4882a593Smuzhiyun }
246*4882a593Smuzhiyun
rsvp_get(struct tcf_proto * tp,u32 handle)247*4882a593Smuzhiyun static void *rsvp_get(struct tcf_proto *tp, u32 handle)
248*4882a593Smuzhiyun {
249*4882a593Smuzhiyun struct rsvp_head *head = rtnl_dereference(tp->root);
250*4882a593Smuzhiyun struct rsvp_session *s;
251*4882a593Smuzhiyun struct rsvp_filter *f;
252*4882a593Smuzhiyun unsigned int h1 = handle & 0xFF;
253*4882a593Smuzhiyun unsigned int h2 = (handle >> 8) & 0xFF;
254*4882a593Smuzhiyun
255*4882a593Smuzhiyun if (h2 > 16)
256*4882a593Smuzhiyun return NULL;
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun for (s = rtnl_dereference(head->ht[h1]); s;
259*4882a593Smuzhiyun s = rtnl_dereference(s->next)) {
260*4882a593Smuzhiyun for (f = rtnl_dereference(s->ht[h2]); f;
261*4882a593Smuzhiyun f = rtnl_dereference(f->next)) {
262*4882a593Smuzhiyun if (f->handle == handle)
263*4882a593Smuzhiyun return f;
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun }
266*4882a593Smuzhiyun return NULL;
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun
rsvp_init(struct tcf_proto * tp)269*4882a593Smuzhiyun static int rsvp_init(struct tcf_proto *tp)
270*4882a593Smuzhiyun {
271*4882a593Smuzhiyun struct rsvp_head *data;
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun data = kzalloc(sizeof(struct rsvp_head), GFP_KERNEL);
274*4882a593Smuzhiyun if (data) {
275*4882a593Smuzhiyun rcu_assign_pointer(tp->root, data);
276*4882a593Smuzhiyun return 0;
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun return -ENOBUFS;
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun
__rsvp_delete_filter(struct rsvp_filter * f)281*4882a593Smuzhiyun static void __rsvp_delete_filter(struct rsvp_filter *f)
282*4882a593Smuzhiyun {
283*4882a593Smuzhiyun tcf_exts_destroy(&f->exts);
284*4882a593Smuzhiyun tcf_exts_put_net(&f->exts);
285*4882a593Smuzhiyun kfree(f);
286*4882a593Smuzhiyun }
287*4882a593Smuzhiyun
rsvp_delete_filter_work(struct work_struct * work)288*4882a593Smuzhiyun static void rsvp_delete_filter_work(struct work_struct *work)
289*4882a593Smuzhiyun {
290*4882a593Smuzhiyun struct rsvp_filter *f = container_of(to_rcu_work(work),
291*4882a593Smuzhiyun struct rsvp_filter,
292*4882a593Smuzhiyun rwork);
293*4882a593Smuzhiyun rtnl_lock();
294*4882a593Smuzhiyun __rsvp_delete_filter(f);
295*4882a593Smuzhiyun rtnl_unlock();
296*4882a593Smuzhiyun }
297*4882a593Smuzhiyun
rsvp_delete_filter(struct tcf_proto * tp,struct rsvp_filter * f)298*4882a593Smuzhiyun static void rsvp_delete_filter(struct tcf_proto *tp, struct rsvp_filter *f)
299*4882a593Smuzhiyun {
300*4882a593Smuzhiyun tcf_unbind_filter(tp, &f->res);
301*4882a593Smuzhiyun /* all classifiers are required to call tcf_exts_destroy() after rcu
302*4882a593Smuzhiyun * grace period, since converted-to-rcu actions are relying on that
303*4882a593Smuzhiyun * in cleanup() callback
304*4882a593Smuzhiyun */
305*4882a593Smuzhiyun if (tcf_exts_get_net(&f->exts))
306*4882a593Smuzhiyun tcf_queue_work(&f->rwork, rsvp_delete_filter_work);
307*4882a593Smuzhiyun else
308*4882a593Smuzhiyun __rsvp_delete_filter(f);
309*4882a593Smuzhiyun }
310*4882a593Smuzhiyun
rsvp_destroy(struct tcf_proto * tp,bool rtnl_held,struct netlink_ext_ack * extack)311*4882a593Smuzhiyun static void rsvp_destroy(struct tcf_proto *tp, bool rtnl_held,
312*4882a593Smuzhiyun struct netlink_ext_ack *extack)
313*4882a593Smuzhiyun {
314*4882a593Smuzhiyun struct rsvp_head *data = rtnl_dereference(tp->root);
315*4882a593Smuzhiyun int h1, h2;
316*4882a593Smuzhiyun
317*4882a593Smuzhiyun if (data == NULL)
318*4882a593Smuzhiyun return;
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun for (h1 = 0; h1 < 256; h1++) {
321*4882a593Smuzhiyun struct rsvp_session *s;
322*4882a593Smuzhiyun
323*4882a593Smuzhiyun while ((s = rtnl_dereference(data->ht[h1])) != NULL) {
324*4882a593Smuzhiyun RCU_INIT_POINTER(data->ht[h1], s->next);
325*4882a593Smuzhiyun
326*4882a593Smuzhiyun for (h2 = 0; h2 <= 16; h2++) {
327*4882a593Smuzhiyun struct rsvp_filter *f;
328*4882a593Smuzhiyun
329*4882a593Smuzhiyun while ((f = rtnl_dereference(s->ht[h2])) != NULL) {
330*4882a593Smuzhiyun rcu_assign_pointer(s->ht[h2], f->next);
331*4882a593Smuzhiyun rsvp_delete_filter(tp, f);
332*4882a593Smuzhiyun }
333*4882a593Smuzhiyun }
334*4882a593Smuzhiyun kfree_rcu(s, rcu);
335*4882a593Smuzhiyun }
336*4882a593Smuzhiyun }
337*4882a593Smuzhiyun kfree_rcu(data, rcu);
338*4882a593Smuzhiyun }
339*4882a593Smuzhiyun
rsvp_delete(struct tcf_proto * tp,void * arg,bool * last,bool rtnl_held,struct netlink_ext_ack * extack)340*4882a593Smuzhiyun static int rsvp_delete(struct tcf_proto *tp, void *arg, bool *last,
341*4882a593Smuzhiyun bool rtnl_held, struct netlink_ext_ack *extack)
342*4882a593Smuzhiyun {
343*4882a593Smuzhiyun struct rsvp_head *head = rtnl_dereference(tp->root);
344*4882a593Smuzhiyun struct rsvp_filter *nfp, *f = arg;
345*4882a593Smuzhiyun struct rsvp_filter __rcu **fp;
346*4882a593Smuzhiyun unsigned int h = f->handle;
347*4882a593Smuzhiyun struct rsvp_session __rcu **sp;
348*4882a593Smuzhiyun struct rsvp_session *nsp, *s = f->sess;
349*4882a593Smuzhiyun int i, h1;
350*4882a593Smuzhiyun
351*4882a593Smuzhiyun fp = &s->ht[(h >> 8) & 0xFF];
352*4882a593Smuzhiyun for (nfp = rtnl_dereference(*fp); nfp;
353*4882a593Smuzhiyun fp = &nfp->next, nfp = rtnl_dereference(*fp)) {
354*4882a593Smuzhiyun if (nfp == f) {
355*4882a593Smuzhiyun RCU_INIT_POINTER(*fp, f->next);
356*4882a593Smuzhiyun rsvp_delete_filter(tp, f);
357*4882a593Smuzhiyun
358*4882a593Smuzhiyun /* Strip tree */
359*4882a593Smuzhiyun
360*4882a593Smuzhiyun for (i = 0; i <= 16; i++)
361*4882a593Smuzhiyun if (s->ht[i])
362*4882a593Smuzhiyun goto out;
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun /* OK, session has no flows */
365*4882a593Smuzhiyun sp = &head->ht[h & 0xFF];
366*4882a593Smuzhiyun for (nsp = rtnl_dereference(*sp); nsp;
367*4882a593Smuzhiyun sp = &nsp->next, nsp = rtnl_dereference(*sp)) {
368*4882a593Smuzhiyun if (nsp == s) {
369*4882a593Smuzhiyun RCU_INIT_POINTER(*sp, s->next);
370*4882a593Smuzhiyun kfree_rcu(s, rcu);
371*4882a593Smuzhiyun goto out;
372*4882a593Smuzhiyun }
373*4882a593Smuzhiyun }
374*4882a593Smuzhiyun
375*4882a593Smuzhiyun break;
376*4882a593Smuzhiyun }
377*4882a593Smuzhiyun }
378*4882a593Smuzhiyun
379*4882a593Smuzhiyun out:
380*4882a593Smuzhiyun *last = true;
381*4882a593Smuzhiyun for (h1 = 0; h1 < 256; h1++) {
382*4882a593Smuzhiyun if (rcu_access_pointer(head->ht[h1])) {
383*4882a593Smuzhiyun *last = false;
384*4882a593Smuzhiyun break;
385*4882a593Smuzhiyun }
386*4882a593Smuzhiyun }
387*4882a593Smuzhiyun
388*4882a593Smuzhiyun return 0;
389*4882a593Smuzhiyun }
390*4882a593Smuzhiyun
gen_handle(struct tcf_proto * tp,unsigned salt)391*4882a593Smuzhiyun static unsigned int gen_handle(struct tcf_proto *tp, unsigned salt)
392*4882a593Smuzhiyun {
393*4882a593Smuzhiyun struct rsvp_head *data = rtnl_dereference(tp->root);
394*4882a593Smuzhiyun int i = 0xFFFF;
395*4882a593Smuzhiyun
396*4882a593Smuzhiyun while (i-- > 0) {
397*4882a593Smuzhiyun u32 h;
398*4882a593Smuzhiyun
399*4882a593Smuzhiyun if ((data->hgenerator += 0x10000) == 0)
400*4882a593Smuzhiyun data->hgenerator = 0x10000;
401*4882a593Smuzhiyun h = data->hgenerator|salt;
402*4882a593Smuzhiyun if (!rsvp_get(tp, h))
403*4882a593Smuzhiyun return h;
404*4882a593Smuzhiyun }
405*4882a593Smuzhiyun return 0;
406*4882a593Smuzhiyun }
407*4882a593Smuzhiyun
tunnel_bts(struct rsvp_head * data)408*4882a593Smuzhiyun static int tunnel_bts(struct rsvp_head *data)
409*4882a593Smuzhiyun {
410*4882a593Smuzhiyun int n = data->tgenerator >> 5;
411*4882a593Smuzhiyun u32 b = 1 << (data->tgenerator & 0x1F);
412*4882a593Smuzhiyun
413*4882a593Smuzhiyun if (data->tmap[n] & b)
414*4882a593Smuzhiyun return 0;
415*4882a593Smuzhiyun data->tmap[n] |= b;
416*4882a593Smuzhiyun return 1;
417*4882a593Smuzhiyun }
418*4882a593Smuzhiyun
tunnel_recycle(struct rsvp_head * data)419*4882a593Smuzhiyun static void tunnel_recycle(struct rsvp_head *data)
420*4882a593Smuzhiyun {
421*4882a593Smuzhiyun struct rsvp_session __rcu **sht = data->ht;
422*4882a593Smuzhiyun u32 tmap[256/32];
423*4882a593Smuzhiyun int h1, h2;
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun memset(tmap, 0, sizeof(tmap));
426*4882a593Smuzhiyun
427*4882a593Smuzhiyun for (h1 = 0; h1 < 256; h1++) {
428*4882a593Smuzhiyun struct rsvp_session *s;
429*4882a593Smuzhiyun for (s = rtnl_dereference(sht[h1]); s;
430*4882a593Smuzhiyun s = rtnl_dereference(s->next)) {
431*4882a593Smuzhiyun for (h2 = 0; h2 <= 16; h2++) {
432*4882a593Smuzhiyun struct rsvp_filter *f;
433*4882a593Smuzhiyun
434*4882a593Smuzhiyun for (f = rtnl_dereference(s->ht[h2]); f;
435*4882a593Smuzhiyun f = rtnl_dereference(f->next)) {
436*4882a593Smuzhiyun if (f->tunnelhdr == 0)
437*4882a593Smuzhiyun continue;
438*4882a593Smuzhiyun data->tgenerator = f->res.classid;
439*4882a593Smuzhiyun tunnel_bts(data);
440*4882a593Smuzhiyun }
441*4882a593Smuzhiyun }
442*4882a593Smuzhiyun }
443*4882a593Smuzhiyun }
444*4882a593Smuzhiyun
445*4882a593Smuzhiyun memcpy(data->tmap, tmap, sizeof(tmap));
446*4882a593Smuzhiyun }
447*4882a593Smuzhiyun
gen_tunnel(struct rsvp_head * data)448*4882a593Smuzhiyun static u32 gen_tunnel(struct rsvp_head *data)
449*4882a593Smuzhiyun {
450*4882a593Smuzhiyun int i, k;
451*4882a593Smuzhiyun
452*4882a593Smuzhiyun for (k = 0; k < 2; k++) {
453*4882a593Smuzhiyun for (i = 255; i > 0; i--) {
454*4882a593Smuzhiyun if (++data->tgenerator == 0)
455*4882a593Smuzhiyun data->tgenerator = 1;
456*4882a593Smuzhiyun if (tunnel_bts(data))
457*4882a593Smuzhiyun return data->tgenerator;
458*4882a593Smuzhiyun }
459*4882a593Smuzhiyun tunnel_recycle(data);
460*4882a593Smuzhiyun }
461*4882a593Smuzhiyun return 0;
462*4882a593Smuzhiyun }
463*4882a593Smuzhiyun
464*4882a593Smuzhiyun static const struct nla_policy rsvp_policy[TCA_RSVP_MAX + 1] = {
465*4882a593Smuzhiyun [TCA_RSVP_CLASSID] = { .type = NLA_U32 },
466*4882a593Smuzhiyun [TCA_RSVP_DST] = { .len = RSVP_DST_LEN * sizeof(u32) },
467*4882a593Smuzhiyun [TCA_RSVP_SRC] = { .len = RSVP_DST_LEN * sizeof(u32) },
468*4882a593Smuzhiyun [TCA_RSVP_PINFO] = { .len = sizeof(struct tc_rsvp_pinfo) },
469*4882a593Smuzhiyun };
470*4882a593Smuzhiyun
rsvp_change(struct net * net,struct sk_buff * in_skb,struct tcf_proto * tp,unsigned long base,u32 handle,struct nlattr ** tca,void ** arg,bool ovr,bool rtnl_held,struct netlink_ext_ack * extack)471*4882a593Smuzhiyun static int rsvp_change(struct net *net, struct sk_buff *in_skb,
472*4882a593Smuzhiyun struct tcf_proto *tp, unsigned long base,
473*4882a593Smuzhiyun u32 handle,
474*4882a593Smuzhiyun struct nlattr **tca,
475*4882a593Smuzhiyun void **arg, bool ovr, bool rtnl_held,
476*4882a593Smuzhiyun struct netlink_ext_ack *extack)
477*4882a593Smuzhiyun {
478*4882a593Smuzhiyun struct rsvp_head *data = rtnl_dereference(tp->root);
479*4882a593Smuzhiyun struct rsvp_filter *f, *nfp;
480*4882a593Smuzhiyun struct rsvp_filter __rcu **fp;
481*4882a593Smuzhiyun struct rsvp_session *nsp, *s;
482*4882a593Smuzhiyun struct rsvp_session __rcu **sp;
483*4882a593Smuzhiyun struct tc_rsvp_pinfo *pinfo = NULL;
484*4882a593Smuzhiyun struct nlattr *opt = tca[TCA_OPTIONS];
485*4882a593Smuzhiyun struct nlattr *tb[TCA_RSVP_MAX + 1];
486*4882a593Smuzhiyun struct tcf_exts e;
487*4882a593Smuzhiyun unsigned int h1, h2;
488*4882a593Smuzhiyun __be32 *dst;
489*4882a593Smuzhiyun int err;
490*4882a593Smuzhiyun
491*4882a593Smuzhiyun if (opt == NULL)
492*4882a593Smuzhiyun return handle ? -EINVAL : 0;
493*4882a593Smuzhiyun
494*4882a593Smuzhiyun err = nla_parse_nested_deprecated(tb, TCA_RSVP_MAX, opt, rsvp_policy,
495*4882a593Smuzhiyun NULL);
496*4882a593Smuzhiyun if (err < 0)
497*4882a593Smuzhiyun return err;
498*4882a593Smuzhiyun
499*4882a593Smuzhiyun err = tcf_exts_init(&e, net, TCA_RSVP_ACT, TCA_RSVP_POLICE);
500*4882a593Smuzhiyun if (err < 0)
501*4882a593Smuzhiyun return err;
502*4882a593Smuzhiyun err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr, true,
503*4882a593Smuzhiyun extack);
504*4882a593Smuzhiyun if (err < 0)
505*4882a593Smuzhiyun goto errout2;
506*4882a593Smuzhiyun
507*4882a593Smuzhiyun f = *arg;
508*4882a593Smuzhiyun if (f) {
509*4882a593Smuzhiyun /* Node exists: adjust only classid */
510*4882a593Smuzhiyun struct rsvp_filter *n;
511*4882a593Smuzhiyun
512*4882a593Smuzhiyun if (f->handle != handle && handle)
513*4882a593Smuzhiyun goto errout2;
514*4882a593Smuzhiyun
515*4882a593Smuzhiyun n = kmemdup(f, sizeof(*f), GFP_KERNEL);
516*4882a593Smuzhiyun if (!n) {
517*4882a593Smuzhiyun err = -ENOMEM;
518*4882a593Smuzhiyun goto errout2;
519*4882a593Smuzhiyun }
520*4882a593Smuzhiyun
521*4882a593Smuzhiyun err = tcf_exts_init(&n->exts, net, TCA_RSVP_ACT,
522*4882a593Smuzhiyun TCA_RSVP_POLICE);
523*4882a593Smuzhiyun if (err < 0) {
524*4882a593Smuzhiyun kfree(n);
525*4882a593Smuzhiyun goto errout2;
526*4882a593Smuzhiyun }
527*4882a593Smuzhiyun
528*4882a593Smuzhiyun if (tb[TCA_RSVP_CLASSID]) {
529*4882a593Smuzhiyun n->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]);
530*4882a593Smuzhiyun tcf_bind_filter(tp, &n->res, base);
531*4882a593Smuzhiyun }
532*4882a593Smuzhiyun
533*4882a593Smuzhiyun tcf_exts_change(&n->exts, &e);
534*4882a593Smuzhiyun rsvp_replace(tp, n, handle);
535*4882a593Smuzhiyun return 0;
536*4882a593Smuzhiyun }
537*4882a593Smuzhiyun
538*4882a593Smuzhiyun /* Now more serious part... */
539*4882a593Smuzhiyun err = -EINVAL;
540*4882a593Smuzhiyun if (handle)
541*4882a593Smuzhiyun goto errout2;
542*4882a593Smuzhiyun if (tb[TCA_RSVP_DST] == NULL)
543*4882a593Smuzhiyun goto errout2;
544*4882a593Smuzhiyun
545*4882a593Smuzhiyun err = -ENOBUFS;
546*4882a593Smuzhiyun f = kzalloc(sizeof(struct rsvp_filter), GFP_KERNEL);
547*4882a593Smuzhiyun if (f == NULL)
548*4882a593Smuzhiyun goto errout2;
549*4882a593Smuzhiyun
550*4882a593Smuzhiyun err = tcf_exts_init(&f->exts, net, TCA_RSVP_ACT, TCA_RSVP_POLICE);
551*4882a593Smuzhiyun if (err < 0)
552*4882a593Smuzhiyun goto errout;
553*4882a593Smuzhiyun h2 = 16;
554*4882a593Smuzhiyun if (tb[TCA_RSVP_SRC]) {
555*4882a593Smuzhiyun memcpy(f->src, nla_data(tb[TCA_RSVP_SRC]), sizeof(f->src));
556*4882a593Smuzhiyun h2 = hash_src(f->src);
557*4882a593Smuzhiyun }
558*4882a593Smuzhiyun if (tb[TCA_RSVP_PINFO]) {
559*4882a593Smuzhiyun pinfo = nla_data(tb[TCA_RSVP_PINFO]);
560*4882a593Smuzhiyun f->spi = pinfo->spi;
561*4882a593Smuzhiyun f->tunnelhdr = pinfo->tunnelhdr;
562*4882a593Smuzhiyun }
563*4882a593Smuzhiyun if (tb[TCA_RSVP_CLASSID])
564*4882a593Smuzhiyun f->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]);
565*4882a593Smuzhiyun
566*4882a593Smuzhiyun dst = nla_data(tb[TCA_RSVP_DST]);
567*4882a593Smuzhiyun h1 = hash_dst(dst, pinfo ? pinfo->protocol : 0, pinfo ? pinfo->tunnelid : 0);
568*4882a593Smuzhiyun
569*4882a593Smuzhiyun err = -ENOMEM;
570*4882a593Smuzhiyun if ((f->handle = gen_handle(tp, h1 | (h2<<8))) == 0)
571*4882a593Smuzhiyun goto errout;
572*4882a593Smuzhiyun
573*4882a593Smuzhiyun if (f->tunnelhdr) {
574*4882a593Smuzhiyun err = -EINVAL;
575*4882a593Smuzhiyun if (f->res.classid > 255)
576*4882a593Smuzhiyun goto errout;
577*4882a593Smuzhiyun
578*4882a593Smuzhiyun err = -ENOMEM;
579*4882a593Smuzhiyun if (f->res.classid == 0 &&
580*4882a593Smuzhiyun (f->res.classid = gen_tunnel(data)) == 0)
581*4882a593Smuzhiyun goto errout;
582*4882a593Smuzhiyun }
583*4882a593Smuzhiyun
584*4882a593Smuzhiyun for (sp = &data->ht[h1];
585*4882a593Smuzhiyun (s = rtnl_dereference(*sp)) != NULL;
586*4882a593Smuzhiyun sp = &s->next) {
587*4882a593Smuzhiyun if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN-1] &&
588*4882a593Smuzhiyun pinfo && pinfo->protocol == s->protocol &&
589*4882a593Smuzhiyun memcmp(&pinfo->dpi, &s->dpi, sizeof(s->dpi)) == 0 &&
590*4882a593Smuzhiyun #if RSVP_DST_LEN == 4
591*4882a593Smuzhiyun dst[0] == s->dst[0] &&
592*4882a593Smuzhiyun dst[1] == s->dst[1] &&
593*4882a593Smuzhiyun dst[2] == s->dst[2] &&
594*4882a593Smuzhiyun #endif
595*4882a593Smuzhiyun pinfo->tunnelid == s->tunnelid) {
596*4882a593Smuzhiyun
597*4882a593Smuzhiyun insert:
598*4882a593Smuzhiyun /* OK, we found appropriate session */
599*4882a593Smuzhiyun
600*4882a593Smuzhiyun fp = &s->ht[h2];
601*4882a593Smuzhiyun
602*4882a593Smuzhiyun f->sess = s;
603*4882a593Smuzhiyun if (f->tunnelhdr == 0)
604*4882a593Smuzhiyun tcf_bind_filter(tp, &f->res, base);
605*4882a593Smuzhiyun
606*4882a593Smuzhiyun tcf_exts_change(&f->exts, &e);
607*4882a593Smuzhiyun
608*4882a593Smuzhiyun fp = &s->ht[h2];
609*4882a593Smuzhiyun for (nfp = rtnl_dereference(*fp); nfp;
610*4882a593Smuzhiyun fp = &nfp->next, nfp = rtnl_dereference(*fp)) {
611*4882a593Smuzhiyun __u32 mask = nfp->spi.mask & f->spi.mask;
612*4882a593Smuzhiyun
613*4882a593Smuzhiyun if (mask != f->spi.mask)
614*4882a593Smuzhiyun break;
615*4882a593Smuzhiyun }
616*4882a593Smuzhiyun RCU_INIT_POINTER(f->next, nfp);
617*4882a593Smuzhiyun rcu_assign_pointer(*fp, f);
618*4882a593Smuzhiyun
619*4882a593Smuzhiyun *arg = f;
620*4882a593Smuzhiyun return 0;
621*4882a593Smuzhiyun }
622*4882a593Smuzhiyun }
623*4882a593Smuzhiyun
624*4882a593Smuzhiyun /* No session found. Create new one. */
625*4882a593Smuzhiyun
626*4882a593Smuzhiyun err = -ENOBUFS;
627*4882a593Smuzhiyun s = kzalloc(sizeof(struct rsvp_session), GFP_KERNEL);
628*4882a593Smuzhiyun if (s == NULL)
629*4882a593Smuzhiyun goto errout;
630*4882a593Smuzhiyun memcpy(s->dst, dst, sizeof(s->dst));
631*4882a593Smuzhiyun
632*4882a593Smuzhiyun if (pinfo) {
633*4882a593Smuzhiyun s->dpi = pinfo->dpi;
634*4882a593Smuzhiyun s->protocol = pinfo->protocol;
635*4882a593Smuzhiyun s->tunnelid = pinfo->tunnelid;
636*4882a593Smuzhiyun }
637*4882a593Smuzhiyun sp = &data->ht[h1];
638*4882a593Smuzhiyun for (nsp = rtnl_dereference(*sp); nsp;
639*4882a593Smuzhiyun sp = &nsp->next, nsp = rtnl_dereference(*sp)) {
640*4882a593Smuzhiyun if ((nsp->dpi.mask & s->dpi.mask) != s->dpi.mask)
641*4882a593Smuzhiyun break;
642*4882a593Smuzhiyun }
643*4882a593Smuzhiyun RCU_INIT_POINTER(s->next, nsp);
644*4882a593Smuzhiyun rcu_assign_pointer(*sp, s);
645*4882a593Smuzhiyun
646*4882a593Smuzhiyun goto insert;
647*4882a593Smuzhiyun
648*4882a593Smuzhiyun errout:
649*4882a593Smuzhiyun tcf_exts_destroy(&f->exts);
650*4882a593Smuzhiyun kfree(f);
651*4882a593Smuzhiyun errout2:
652*4882a593Smuzhiyun tcf_exts_destroy(&e);
653*4882a593Smuzhiyun return err;
654*4882a593Smuzhiyun }
655*4882a593Smuzhiyun
rsvp_walk(struct tcf_proto * tp,struct tcf_walker * arg,bool rtnl_held)656*4882a593Smuzhiyun static void rsvp_walk(struct tcf_proto *tp, struct tcf_walker *arg,
657*4882a593Smuzhiyun bool rtnl_held)
658*4882a593Smuzhiyun {
659*4882a593Smuzhiyun struct rsvp_head *head = rtnl_dereference(tp->root);
660*4882a593Smuzhiyun unsigned int h, h1;
661*4882a593Smuzhiyun
662*4882a593Smuzhiyun if (arg->stop)
663*4882a593Smuzhiyun return;
664*4882a593Smuzhiyun
665*4882a593Smuzhiyun for (h = 0; h < 256; h++) {
666*4882a593Smuzhiyun struct rsvp_session *s;
667*4882a593Smuzhiyun
668*4882a593Smuzhiyun for (s = rtnl_dereference(head->ht[h]); s;
669*4882a593Smuzhiyun s = rtnl_dereference(s->next)) {
670*4882a593Smuzhiyun for (h1 = 0; h1 <= 16; h1++) {
671*4882a593Smuzhiyun struct rsvp_filter *f;
672*4882a593Smuzhiyun
673*4882a593Smuzhiyun for (f = rtnl_dereference(s->ht[h1]); f;
674*4882a593Smuzhiyun f = rtnl_dereference(f->next)) {
675*4882a593Smuzhiyun if (arg->count < arg->skip) {
676*4882a593Smuzhiyun arg->count++;
677*4882a593Smuzhiyun continue;
678*4882a593Smuzhiyun }
679*4882a593Smuzhiyun if (arg->fn(tp, f, arg) < 0) {
680*4882a593Smuzhiyun arg->stop = 1;
681*4882a593Smuzhiyun return;
682*4882a593Smuzhiyun }
683*4882a593Smuzhiyun arg->count++;
684*4882a593Smuzhiyun }
685*4882a593Smuzhiyun }
686*4882a593Smuzhiyun }
687*4882a593Smuzhiyun }
688*4882a593Smuzhiyun }
689*4882a593Smuzhiyun
rsvp_dump(struct net * net,struct tcf_proto * tp,void * fh,struct sk_buff * skb,struct tcmsg * t,bool rtnl_held)690*4882a593Smuzhiyun static int rsvp_dump(struct net *net, struct tcf_proto *tp, void *fh,
691*4882a593Smuzhiyun struct sk_buff *skb, struct tcmsg *t, bool rtnl_held)
692*4882a593Smuzhiyun {
693*4882a593Smuzhiyun struct rsvp_filter *f = fh;
694*4882a593Smuzhiyun struct rsvp_session *s;
695*4882a593Smuzhiyun struct nlattr *nest;
696*4882a593Smuzhiyun struct tc_rsvp_pinfo pinfo;
697*4882a593Smuzhiyun
698*4882a593Smuzhiyun if (f == NULL)
699*4882a593Smuzhiyun return skb->len;
700*4882a593Smuzhiyun s = f->sess;
701*4882a593Smuzhiyun
702*4882a593Smuzhiyun t->tcm_handle = f->handle;
703*4882a593Smuzhiyun
704*4882a593Smuzhiyun nest = nla_nest_start_noflag(skb, TCA_OPTIONS);
705*4882a593Smuzhiyun if (nest == NULL)
706*4882a593Smuzhiyun goto nla_put_failure;
707*4882a593Smuzhiyun
708*4882a593Smuzhiyun if (nla_put(skb, TCA_RSVP_DST, sizeof(s->dst), &s->dst))
709*4882a593Smuzhiyun goto nla_put_failure;
710*4882a593Smuzhiyun pinfo.dpi = s->dpi;
711*4882a593Smuzhiyun pinfo.spi = f->spi;
712*4882a593Smuzhiyun pinfo.protocol = s->protocol;
713*4882a593Smuzhiyun pinfo.tunnelid = s->tunnelid;
714*4882a593Smuzhiyun pinfo.tunnelhdr = f->tunnelhdr;
715*4882a593Smuzhiyun pinfo.pad = 0;
716*4882a593Smuzhiyun if (nla_put(skb, TCA_RSVP_PINFO, sizeof(pinfo), &pinfo))
717*4882a593Smuzhiyun goto nla_put_failure;
718*4882a593Smuzhiyun if (f->res.classid &&
719*4882a593Smuzhiyun nla_put_u32(skb, TCA_RSVP_CLASSID, f->res.classid))
720*4882a593Smuzhiyun goto nla_put_failure;
721*4882a593Smuzhiyun if (((f->handle >> 8) & 0xFF) != 16 &&
722*4882a593Smuzhiyun nla_put(skb, TCA_RSVP_SRC, sizeof(f->src), f->src))
723*4882a593Smuzhiyun goto nla_put_failure;
724*4882a593Smuzhiyun
725*4882a593Smuzhiyun if (tcf_exts_dump(skb, &f->exts) < 0)
726*4882a593Smuzhiyun goto nla_put_failure;
727*4882a593Smuzhiyun
728*4882a593Smuzhiyun nla_nest_end(skb, nest);
729*4882a593Smuzhiyun
730*4882a593Smuzhiyun if (tcf_exts_dump_stats(skb, &f->exts) < 0)
731*4882a593Smuzhiyun goto nla_put_failure;
732*4882a593Smuzhiyun return skb->len;
733*4882a593Smuzhiyun
734*4882a593Smuzhiyun nla_put_failure:
735*4882a593Smuzhiyun nla_nest_cancel(skb, nest);
736*4882a593Smuzhiyun return -1;
737*4882a593Smuzhiyun }
738*4882a593Smuzhiyun
rsvp_bind_class(void * fh,u32 classid,unsigned long cl,void * q,unsigned long base)739*4882a593Smuzhiyun static void rsvp_bind_class(void *fh, u32 classid, unsigned long cl, void *q,
740*4882a593Smuzhiyun unsigned long base)
741*4882a593Smuzhiyun {
742*4882a593Smuzhiyun struct rsvp_filter *f = fh;
743*4882a593Smuzhiyun
744*4882a593Smuzhiyun if (f && f->res.classid == classid) {
745*4882a593Smuzhiyun if (cl)
746*4882a593Smuzhiyun __tcf_bind_filter(q, &f->res, base);
747*4882a593Smuzhiyun else
748*4882a593Smuzhiyun __tcf_unbind_filter(q, &f->res);
749*4882a593Smuzhiyun }
750*4882a593Smuzhiyun }
751*4882a593Smuzhiyun
752*4882a593Smuzhiyun static struct tcf_proto_ops RSVP_OPS __read_mostly = {
753*4882a593Smuzhiyun .kind = RSVP_ID,
754*4882a593Smuzhiyun .classify = rsvp_classify,
755*4882a593Smuzhiyun .init = rsvp_init,
756*4882a593Smuzhiyun .destroy = rsvp_destroy,
757*4882a593Smuzhiyun .get = rsvp_get,
758*4882a593Smuzhiyun .change = rsvp_change,
759*4882a593Smuzhiyun .delete = rsvp_delete,
760*4882a593Smuzhiyun .walk = rsvp_walk,
761*4882a593Smuzhiyun .dump = rsvp_dump,
762*4882a593Smuzhiyun .bind_class = rsvp_bind_class,
763*4882a593Smuzhiyun .owner = THIS_MODULE,
764*4882a593Smuzhiyun };
765*4882a593Smuzhiyun
init_rsvp(void)766*4882a593Smuzhiyun static int __init init_rsvp(void)
767*4882a593Smuzhiyun {
768*4882a593Smuzhiyun return register_tcf_proto_ops(&RSVP_OPS);
769*4882a593Smuzhiyun }
770*4882a593Smuzhiyun
exit_rsvp(void)771*4882a593Smuzhiyun static void __exit exit_rsvp(void)
772*4882a593Smuzhiyun {
773*4882a593Smuzhiyun unregister_tcf_proto_ops(&RSVP_OPS);
774*4882a593Smuzhiyun }
775*4882a593Smuzhiyun
776*4882a593Smuzhiyun module_init(init_rsvp)
777*4882a593Smuzhiyun module_exit(exit_rsvp)
778