xref: /OK3568_Linux_fs/kernel/net/sched/cls_rsvp.h (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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