xref: /netbsd-src/sys/net/npf/npf_rproc.c (revision 96fc3e30a7c3f7bba53384bf41dad5f78306fac4)
1 /*	$NetBSD: npf_rproc.c,v 1.5 2013/01/20 18:45:56 rmind Exp $	*/
2 
3 /*-
4  * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This material is based upon work partially supported by The
8  * NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * NPF extension and rule procedure interface.
34  */
35 
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD");
38 
39 #include <sys/param.h>
40 #include <sys/types.h>
41 
42 #include <sys/atomic.h>
43 #include <sys/kmem.h>
44 #include <sys/mutex.h>
45 
46 #include "npf_impl.h"
47 
48 #define	EXT_NAME_LEN		32
49 
50 typedef struct npf_ext {
51 	char			ext_callname[EXT_NAME_LEN];
52 	LIST_ENTRY(npf_ext)	ext_entry;
53 	const npf_ext_ops_t *	ext_ops;
54 	unsigned		ext_refcnt;
55 } npf_ext_t;
56 
57 #define	RPROC_NAME_LEN		32
58 #define	RPROC_EXT_COUNT		16
59 
60 struct npf_rproc {
61 	/* Name, reference count and flags. */
62 	char			rp_name[RPROC_NAME_LEN];
63 	u_int			rp_refcnt;
64 	uint32_t		rp_flags;
65 	/* Associated extensions and their metadata . */
66 	unsigned		rp_ext_count;
67 	npf_ext_t *		rp_ext[RPROC_EXT_COUNT];
68 	void *			rp_ext_meta[RPROC_EXT_COUNT];
69 };
70 
71 static LIST_HEAD(, npf_ext)	ext_list	__cacheline_aligned;
72 static kmutex_t			ext_lock	__cacheline_aligned;
73 
74 void
75 npf_ext_sysinit(void)
76 {
77 	mutex_init(&ext_lock, MUTEX_DEFAULT, IPL_NONE);
78 	LIST_INIT(&ext_list);
79 }
80 
81 void
82 npf_ext_sysfini(void)
83 {
84 	KASSERT(LIST_EMPTY(&ext_list));
85 	mutex_destroy(&ext_lock);
86 }
87 
88 /*
89  * NPF extension management for the rule procedures.
90  */
91 
92 static npf_ext_t *
93 npf_ext_lookup(const char *name)
94 {
95 	npf_ext_t *ext = NULL;
96 
97 	KASSERT(mutex_owned(&ext_lock));
98 
99 	LIST_FOREACH(ext, &ext_list, ext_entry)
100 		if (strcmp(ext->ext_callname, name) == 0)
101 			break;
102 	return ext;
103 }
104 
105 void *
106 npf_ext_register(const char *name, const npf_ext_ops_t *ops)
107 {
108 	npf_ext_t *ext;
109 
110 	ext = kmem_zalloc(sizeof(npf_ext_t), KM_SLEEP);
111 	strlcpy(ext->ext_callname, name, EXT_NAME_LEN);
112 	ext->ext_ops = ops;
113 
114 	mutex_enter(&ext_lock);
115 	if (npf_ext_lookup(name)) {
116 		mutex_exit(&ext_lock);
117 		kmem_free(ext, sizeof(npf_ext_t));
118 		return NULL;
119 	}
120 	LIST_INSERT_HEAD(&ext_list, ext, ext_entry);
121 	mutex_exit(&ext_lock);
122 
123 	return (void *)ext;
124 }
125 
126 int
127 npf_ext_unregister(void *extid)
128 {
129 	npf_ext_t *ext = extid;
130 
131 	/*
132 	 * Check if in-use first (re-check with the lock held).
133 	 */
134 	if (ext->ext_refcnt) {
135 		return EBUSY;
136 	}
137 
138 	mutex_enter(&ext_lock);
139 	if (ext->ext_refcnt) {
140 		mutex_exit(&ext_lock);
141 		return EBUSY;
142 	}
143 	KASSERT(npf_ext_lookup(ext->ext_callname));
144 	LIST_REMOVE(ext, ext_entry);
145 	mutex_exit(&ext_lock);
146 
147 	kmem_free(ext, sizeof(npf_ext_t));
148 	return 0;
149 }
150 
151 int
152 npf_ext_construct(const char *name, npf_rproc_t *rp, prop_dictionary_t params)
153 {
154 	const npf_ext_ops_t *extops;
155 	npf_ext_t *ext;
156 	unsigned i;
157 	int error;
158 
159 	if (rp->rp_ext_count >= RPROC_EXT_COUNT) {
160 		return ENOSPC;
161 	}
162 
163 	mutex_enter(&ext_lock);
164 	ext = npf_ext_lookup(name);
165 	if (ext) {
166 		atomic_inc_uint(&ext->ext_refcnt);
167 	}
168 	mutex_exit(&ext_lock);
169 
170 	if (!ext) {
171 		return ENOENT;
172 	}
173 
174 	extops = ext->ext_ops;
175 	KASSERT(extops != NULL);
176 
177 	error = extops->ctor(rp, params);
178 	if (error) {
179 		atomic_dec_uint(&ext->ext_refcnt);
180 		return error;
181 	}
182 	i = rp->rp_ext_count++;
183 	rp->rp_ext[i] = ext;
184 	return 0;
185 }
186 
187 /*
188  * Rule procedure management.
189  */
190 
191 /*
192  * npf_rproc_create: construct a new rule procedure, lookup and associate
193  * the extension calls with it.
194  */
195 npf_rproc_t *
196 npf_rproc_create(prop_dictionary_t rpdict)
197 {
198 	const char *name;
199 	npf_rproc_t *rp;
200 
201 	if (!prop_dictionary_get_cstring_nocopy(rpdict, "name", &name)) {
202 		return NULL;
203 	}
204 
205 	rp = kmem_intr_zalloc(sizeof(npf_rproc_t), KM_SLEEP);
206 	rp->rp_refcnt = 1;
207 
208 	strlcpy(rp->rp_name, name, RPROC_NAME_LEN);
209 	prop_dictionary_get_uint32(rpdict, "flags", &rp->rp_flags);
210 	return rp;
211 }
212 
213 /*
214  * npf_rproc_acquire: acquire the reference on the rule procedure.
215  */
216 void
217 npf_rproc_acquire(npf_rproc_t *rp)
218 {
219 
220 	atomic_inc_uint(&rp->rp_refcnt);
221 }
222 
223 /*
224  * npf_rproc_release: drop the reference count and destroy the rule
225  * procedure on the last reference.
226  */
227 void
228 npf_rproc_release(npf_rproc_t *rp)
229 {
230 
231 	KASSERT(rp->rp_refcnt > 0);
232 	if (atomic_dec_uint_nv(&rp->rp_refcnt) != 0) {
233 		return;
234 	}
235 	/* XXXintr */
236 	for (unsigned i = 0; i < rp->rp_ext_count; i++) {
237 		npf_ext_t *ext = rp->rp_ext[i];
238 		const npf_ext_ops_t *extops = ext->ext_ops;
239 
240 		extops->dtor(rp, rp->rp_ext_meta[i]);
241 		atomic_dec_uint(&ext->ext_refcnt);
242 	}
243 	kmem_intr_free(rp, sizeof(npf_rproc_t));
244 }
245 
246 void
247 npf_rproc_assign(npf_rproc_t *rp, void *params)
248 {
249 	unsigned i = rp->rp_ext_count;
250 
251 	/* Note: params may be NULL. */
252 	KASSERT(i < RPROC_EXT_COUNT);
253 	rp->rp_ext_meta[i] = params;
254 }
255 
256 /*
257  * npf_rproc_run: run the rule procedure by executing each extension call.
258  *
259  * => Reference on the rule procedure must be held.
260  */
261 void
262 npf_rproc_run(npf_cache_t *npc, nbuf_t *nbuf, npf_rproc_t *rp, int *decision)
263 {
264 	const unsigned extcount = rp->rp_ext_count;
265 
266 	KASSERT(!nbuf_flag_p(nbuf, NBUF_DATAREF_RESET));
267 	KASSERT(rp->rp_refcnt > 0);
268 
269 	for (unsigned i = 0; i < extcount; i++) {
270 		const npf_ext_t *ext = rp->rp_ext[i];
271 		const npf_ext_ops_t *extops = ext->ext_ops;
272 
273 		KASSERT(ext->ext_refcnt > 0);
274 		extops->proc(npc, nbuf, rp->rp_ext_meta[i], decision);
275 
276 		if (nbuf_flag_p(nbuf, NBUF_DATAREF_RESET)) {
277 			npf_recache(npc, nbuf);
278 		}
279 	}
280 }
281