1 | #include <machine/rtems-bsd-kernel-space.h> |
---|
2 | |
---|
3 | /*- |
---|
4 | * Copyright (c) 2012 Gleb Smirnoff <glebius@FreeBSD.org> |
---|
5 | * Copyright (c) 1980, 1986, 1993 |
---|
6 | * The Regents of the University of California. All rights reserved. |
---|
7 | * |
---|
8 | * Redistribution and use in source and binary forms, with or without |
---|
9 | * modification, are permitted provided that the following conditions |
---|
10 | * are met: |
---|
11 | * 1. Redistributions of source code must retain the above copyright |
---|
12 | * notice, this list of conditions and the following disclaimer. |
---|
13 | * 2. Redistributions in binary form must reproduce the above copyright |
---|
14 | * notice, this list of conditions and the following disclaimer in the |
---|
15 | * documentation and/or other materials provided with the distribution. |
---|
16 | * 3. Neither the name of the University nor the names of its contributors |
---|
17 | * may be used to endorse or promote products derived from this software |
---|
18 | * without specific prior written permission. |
---|
19 | * |
---|
20 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
---|
21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
---|
22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
---|
23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
---|
24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
---|
25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
---|
26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
---|
27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
---|
28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
---|
29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
---|
30 | * SUCH DAMAGE. |
---|
31 | * |
---|
32 | * @(#)if.c 8.5 (Berkeley) 1/9/95 |
---|
33 | * $FreeBSD$ |
---|
34 | */ |
---|
35 | |
---|
36 | #include <sys/param.h> |
---|
37 | #include <sys/eventhandler.h> |
---|
38 | #include <sys/malloc.h> |
---|
39 | #include <sys/limits.h> |
---|
40 | #include <sys/lock.h> |
---|
41 | #include <sys/mutex.h> |
---|
42 | #include <sys/kernel.h> |
---|
43 | #include <sys/systm.h> |
---|
44 | #include <sys/types.h> |
---|
45 | #include <sys/socket.h> |
---|
46 | |
---|
47 | #include <net/if.h> |
---|
48 | #include <net/if_var.h> |
---|
49 | #include <net/if_clone.h> |
---|
50 | #include <net/radix.h> |
---|
51 | #include <net/route.h> |
---|
52 | #include <net/vnet.h> |
---|
53 | |
---|
54 | /* Current IF_MAXUNIT expands maximum to 5 characters. */ |
---|
55 | #define IFCLOSIZ (IFNAMSIZ - 5) |
---|
56 | |
---|
57 | /* |
---|
58 | * Structure describing a `cloning' interface. |
---|
59 | * |
---|
60 | * List of locks |
---|
61 | * (c) const until freeing |
---|
62 | * (d) driver specific data, may need external protection. |
---|
63 | * (e) locked by if_cloners_mtx |
---|
64 | * (i) locked by ifc_mtx mtx |
---|
65 | */ |
---|
66 | struct if_clone { |
---|
67 | char ifc_name[IFCLOSIZ]; /* (c) Name of device, e.g. `gif' */ |
---|
68 | struct unrhdr *ifc_unrhdr; /* (c) alloc_unr(9) header */ |
---|
69 | int ifc_maxunit; /* (c) maximum unit number */ |
---|
70 | long ifc_refcnt; /* (i) Reference count. */ |
---|
71 | LIST_HEAD(, ifnet) ifc_iflist; /* (i) List of cloned interfaces */ |
---|
72 | struct mtx ifc_mtx; /* Mutex to protect members. */ |
---|
73 | |
---|
74 | enum { SIMPLE, ADVANCED } ifc_type; /* (c) */ |
---|
75 | |
---|
76 | /* (c) Driver specific cloning functions. Called with no locks held. */ |
---|
77 | union { |
---|
78 | struct { /* advanced cloner */ |
---|
79 | ifc_match_t *_ifc_match; |
---|
80 | ifc_create_t *_ifc_create; |
---|
81 | ifc_destroy_t *_ifc_destroy; |
---|
82 | } A; |
---|
83 | struct { /* simple cloner */ |
---|
84 | ifcs_create_t *_ifcs_create; |
---|
85 | ifcs_destroy_t *_ifcs_destroy; |
---|
86 | int _ifcs_minifs; /* minimum ifs */ |
---|
87 | |
---|
88 | } S; |
---|
89 | } U; |
---|
90 | #define ifc_match U.A._ifc_match |
---|
91 | #define ifc_create U.A._ifc_create |
---|
92 | #define ifc_destroy U.A._ifc_destroy |
---|
93 | #define ifcs_create U.S._ifcs_create |
---|
94 | #define ifcs_destroy U.S._ifcs_destroy |
---|
95 | #define ifcs_minifs U.S._ifcs_minifs |
---|
96 | |
---|
97 | LIST_ENTRY(if_clone) ifc_list; /* (e) On list of cloners */ |
---|
98 | }; |
---|
99 | |
---|
100 | static void if_clone_free(struct if_clone *ifc); |
---|
101 | static int if_clone_createif(struct if_clone *ifc, char *name, size_t len, |
---|
102 | caddr_t params); |
---|
103 | |
---|
104 | static int ifc_simple_match(struct if_clone *, const char *); |
---|
105 | static int ifc_simple_create(struct if_clone *, char *, size_t, caddr_t); |
---|
106 | static int ifc_simple_destroy(struct if_clone *, struct ifnet *); |
---|
107 | |
---|
108 | static struct mtx if_cloners_mtx; |
---|
109 | MTX_SYSINIT(if_cloners_lock, &if_cloners_mtx, "if_cloners lock", MTX_DEF); |
---|
110 | static VNET_DEFINE(int, if_cloners_count); |
---|
111 | VNET_DEFINE(LIST_HEAD(, if_clone), if_cloners); |
---|
112 | |
---|
113 | #define V_if_cloners_count VNET(if_cloners_count) |
---|
114 | #define V_if_cloners VNET(if_cloners) |
---|
115 | |
---|
116 | #define IF_CLONERS_LOCK_ASSERT() mtx_assert(&if_cloners_mtx, MA_OWNED) |
---|
117 | #define IF_CLONERS_LOCK() mtx_lock(&if_cloners_mtx) |
---|
118 | #define IF_CLONERS_UNLOCK() mtx_unlock(&if_cloners_mtx) |
---|
119 | |
---|
120 | #define IF_CLONE_LOCK_INIT(ifc) \ |
---|
121 | mtx_init(&(ifc)->ifc_mtx, "if_clone lock", NULL, MTX_DEF) |
---|
122 | #define IF_CLONE_LOCK_DESTROY(ifc) mtx_destroy(&(ifc)->ifc_mtx) |
---|
123 | #define IF_CLONE_LOCK_ASSERT(ifc) mtx_assert(&(ifc)->ifc_mtx, MA_OWNED) |
---|
124 | #define IF_CLONE_LOCK(ifc) mtx_lock(&(ifc)->ifc_mtx) |
---|
125 | #define IF_CLONE_UNLOCK(ifc) mtx_unlock(&(ifc)->ifc_mtx) |
---|
126 | |
---|
127 | #define IF_CLONE_ADDREF(ifc) \ |
---|
128 | do { \ |
---|
129 | IF_CLONE_LOCK(ifc); \ |
---|
130 | IF_CLONE_ADDREF_LOCKED(ifc); \ |
---|
131 | IF_CLONE_UNLOCK(ifc); \ |
---|
132 | } while (0) |
---|
133 | #define IF_CLONE_ADDREF_LOCKED(ifc) \ |
---|
134 | do { \ |
---|
135 | IF_CLONE_LOCK_ASSERT(ifc); \ |
---|
136 | KASSERT((ifc)->ifc_refcnt >= 0, \ |
---|
137 | ("negative refcnt %ld", (ifc)->ifc_refcnt)); \ |
---|
138 | (ifc)->ifc_refcnt++; \ |
---|
139 | } while (0) |
---|
140 | #define IF_CLONE_REMREF(ifc) \ |
---|
141 | do { \ |
---|
142 | IF_CLONE_LOCK(ifc); \ |
---|
143 | IF_CLONE_REMREF_LOCKED(ifc); \ |
---|
144 | } while (0) |
---|
145 | #define IF_CLONE_REMREF_LOCKED(ifc) \ |
---|
146 | do { \ |
---|
147 | IF_CLONE_LOCK_ASSERT(ifc); \ |
---|
148 | KASSERT((ifc)->ifc_refcnt > 0, \ |
---|
149 | ("bogus refcnt %ld", (ifc)->ifc_refcnt)); \ |
---|
150 | if (--(ifc)->ifc_refcnt == 0) { \ |
---|
151 | IF_CLONE_UNLOCK(ifc); \ |
---|
152 | if_clone_free(ifc); \ |
---|
153 | } else { \ |
---|
154 | /* silently free the lock */ \ |
---|
155 | IF_CLONE_UNLOCK(ifc); \ |
---|
156 | } \ |
---|
157 | } while (0) |
---|
158 | |
---|
159 | #define IFC_IFLIST_INSERT(_ifc, _ifp) \ |
---|
160 | LIST_INSERT_HEAD(&_ifc->ifc_iflist, _ifp, if_clones) |
---|
161 | #define IFC_IFLIST_REMOVE(_ifc, _ifp) \ |
---|
162 | LIST_REMOVE(_ifp, if_clones) |
---|
163 | |
---|
164 | static MALLOC_DEFINE(M_CLONE, "clone", "interface cloning framework"); |
---|
165 | |
---|
166 | void |
---|
167 | vnet_if_clone_init(void) |
---|
168 | { |
---|
169 | |
---|
170 | LIST_INIT(&V_if_cloners); |
---|
171 | } |
---|
172 | |
---|
173 | /* |
---|
174 | * Lookup and create a clone network interface. |
---|
175 | */ |
---|
176 | int |
---|
177 | if_clone_create(char *name, size_t len, caddr_t params) |
---|
178 | { |
---|
179 | struct if_clone *ifc; |
---|
180 | |
---|
181 | /* Try to find an applicable cloner for this request */ |
---|
182 | IF_CLONERS_LOCK(); |
---|
183 | LIST_FOREACH(ifc, &V_if_cloners, ifc_list) |
---|
184 | if (ifc->ifc_type == SIMPLE) { |
---|
185 | if (ifc_simple_match(ifc, name)) |
---|
186 | break; |
---|
187 | } else { |
---|
188 | if (ifc->ifc_match(ifc, name)) |
---|
189 | break; |
---|
190 | } |
---|
191 | #ifdef VIMAGE |
---|
192 | if (ifc == NULL && !IS_DEFAULT_VNET(curvnet)) { |
---|
193 | CURVNET_SET_QUIET(vnet0); |
---|
194 | LIST_FOREACH(ifc, &V_if_cloners, ifc_list) |
---|
195 | if (ifc->ifc_type == SIMPLE) { |
---|
196 | if (ifc_simple_match(ifc, name)) |
---|
197 | break; |
---|
198 | } else { |
---|
199 | if (ifc->ifc_match(ifc, name)) |
---|
200 | break; |
---|
201 | } |
---|
202 | CURVNET_RESTORE(); |
---|
203 | } |
---|
204 | #endif |
---|
205 | IF_CLONERS_UNLOCK(); |
---|
206 | |
---|
207 | if (ifc == NULL) |
---|
208 | return (EINVAL); |
---|
209 | |
---|
210 | return (if_clone_createif(ifc, name, len, params)); |
---|
211 | } |
---|
212 | |
---|
213 | /* |
---|
214 | * Create a clone network interface. |
---|
215 | */ |
---|
216 | static int |
---|
217 | if_clone_createif(struct if_clone *ifc, char *name, size_t len, caddr_t params) |
---|
218 | { |
---|
219 | int err; |
---|
220 | struct ifnet *ifp; |
---|
221 | |
---|
222 | if (ifunit(name) != NULL) |
---|
223 | return (EEXIST); |
---|
224 | |
---|
225 | if (ifc->ifc_type == SIMPLE) |
---|
226 | err = ifc_simple_create(ifc, name, len, params); |
---|
227 | else |
---|
228 | err = (*ifc->ifc_create)(ifc, name, len, params); |
---|
229 | |
---|
230 | if (!err) { |
---|
231 | ifp = ifunit(name); |
---|
232 | if (ifp == NULL) |
---|
233 | panic("%s: lookup failed for %s", __func__, name); |
---|
234 | |
---|
235 | if_addgroup(ifp, ifc->ifc_name); |
---|
236 | |
---|
237 | IF_CLONE_LOCK(ifc); |
---|
238 | IFC_IFLIST_INSERT(ifc, ifp); |
---|
239 | IF_CLONE_UNLOCK(ifc); |
---|
240 | } |
---|
241 | |
---|
242 | return (err); |
---|
243 | } |
---|
244 | |
---|
245 | /* |
---|
246 | * Lookup and destroy a clone network interface. |
---|
247 | */ |
---|
248 | int |
---|
249 | if_clone_destroy(const char *name) |
---|
250 | { |
---|
251 | int err; |
---|
252 | struct if_clone *ifc; |
---|
253 | struct ifnet *ifp; |
---|
254 | |
---|
255 | ifp = ifunit_ref(name); |
---|
256 | if (ifp == NULL) |
---|
257 | return (ENXIO); |
---|
258 | |
---|
259 | /* Find the cloner for this interface */ |
---|
260 | IF_CLONERS_LOCK(); |
---|
261 | LIST_FOREACH(ifc, &V_if_cloners, ifc_list) { |
---|
262 | if (strcmp(ifc->ifc_name, ifp->if_dname) == 0) { |
---|
263 | break; |
---|
264 | } |
---|
265 | } |
---|
266 | #ifdef VIMAGE |
---|
267 | if (ifc == NULL && !IS_DEFAULT_VNET(curvnet)) { |
---|
268 | CURVNET_SET_QUIET(vnet0); |
---|
269 | LIST_FOREACH(ifc, &V_if_cloners, ifc_list) |
---|
270 | if (ifc->ifc_type == SIMPLE) { |
---|
271 | if (ifc_simple_match(ifc, name)) |
---|
272 | break; |
---|
273 | } else { |
---|
274 | if (ifc->ifc_match(ifc, name)) |
---|
275 | break; |
---|
276 | } |
---|
277 | CURVNET_RESTORE(); |
---|
278 | } |
---|
279 | #endif |
---|
280 | IF_CLONERS_UNLOCK(); |
---|
281 | if (ifc == NULL) { |
---|
282 | if_rele(ifp); |
---|
283 | return (EINVAL); |
---|
284 | } |
---|
285 | |
---|
286 | err = if_clone_destroyif(ifc, ifp); |
---|
287 | if_rele(ifp); |
---|
288 | return err; |
---|
289 | } |
---|
290 | |
---|
291 | /* |
---|
292 | * Destroy a clone network interface. |
---|
293 | */ |
---|
294 | int |
---|
295 | if_clone_destroyif(struct if_clone *ifc, struct ifnet *ifp) |
---|
296 | { |
---|
297 | int err; |
---|
298 | struct ifnet *ifcifp; |
---|
299 | |
---|
300 | if (ifc->ifc_type == ADVANCED && ifc->ifc_destroy == NULL) |
---|
301 | return(EOPNOTSUPP); |
---|
302 | |
---|
303 | /* |
---|
304 | * Given that the cloned ifnet might be attached to a different |
---|
305 | * vnet from where its cloner was registered, we have to |
---|
306 | * switch to the vnet context of the target vnet. |
---|
307 | */ |
---|
308 | CURVNET_SET_QUIET(ifp->if_vnet); |
---|
309 | |
---|
310 | IF_CLONE_LOCK(ifc); |
---|
311 | LIST_FOREACH(ifcifp, &ifc->ifc_iflist, if_clones) { |
---|
312 | if (ifcifp == ifp) { |
---|
313 | IFC_IFLIST_REMOVE(ifc, ifp); |
---|
314 | break; |
---|
315 | } |
---|
316 | } |
---|
317 | IF_CLONE_UNLOCK(ifc); |
---|
318 | if (ifcifp == NULL) { |
---|
319 | CURVNET_RESTORE(); |
---|
320 | return (ENXIO); /* ifp is not on the list. */ |
---|
321 | } |
---|
322 | |
---|
323 | if_delgroup(ifp, ifc->ifc_name); |
---|
324 | |
---|
325 | if (ifc->ifc_type == SIMPLE) |
---|
326 | err = ifc_simple_destroy(ifc, ifp); |
---|
327 | else |
---|
328 | err = (*ifc->ifc_destroy)(ifc, ifp); |
---|
329 | |
---|
330 | if (err != 0) { |
---|
331 | if_addgroup(ifp, ifc->ifc_name); |
---|
332 | |
---|
333 | IF_CLONE_LOCK(ifc); |
---|
334 | IFC_IFLIST_INSERT(ifc, ifp); |
---|
335 | IF_CLONE_UNLOCK(ifc); |
---|
336 | } |
---|
337 | CURVNET_RESTORE(); |
---|
338 | return (err); |
---|
339 | } |
---|
340 | |
---|
341 | static struct if_clone * |
---|
342 | if_clone_alloc(const char *name, int maxunit) |
---|
343 | { |
---|
344 | struct if_clone *ifc; |
---|
345 | |
---|
346 | KASSERT(name != NULL, ("%s: no name\n", __func__)); |
---|
347 | |
---|
348 | ifc = malloc(sizeof(struct if_clone), M_CLONE, M_WAITOK | M_ZERO); |
---|
349 | strncpy(ifc->ifc_name, name, IFCLOSIZ-1); |
---|
350 | IF_CLONE_LOCK_INIT(ifc); |
---|
351 | IF_CLONE_ADDREF(ifc); |
---|
352 | ifc->ifc_maxunit = maxunit ? maxunit : IF_MAXUNIT; |
---|
353 | ifc->ifc_unrhdr = new_unrhdr(0, ifc->ifc_maxunit, &ifc->ifc_mtx); |
---|
354 | LIST_INIT(&ifc->ifc_iflist); |
---|
355 | |
---|
356 | return (ifc); |
---|
357 | } |
---|
358 | |
---|
359 | static int |
---|
360 | if_clone_attach(struct if_clone *ifc) |
---|
361 | { |
---|
362 | struct if_clone *ifc1; |
---|
363 | |
---|
364 | IF_CLONERS_LOCK(); |
---|
365 | LIST_FOREACH(ifc1, &V_if_cloners, ifc_list) |
---|
366 | if (strcmp(ifc->ifc_name, ifc1->ifc_name) == 0) { |
---|
367 | IF_CLONERS_UNLOCK(); |
---|
368 | IF_CLONE_REMREF(ifc); |
---|
369 | return (EEXIST); |
---|
370 | } |
---|
371 | LIST_INSERT_HEAD(&V_if_cloners, ifc, ifc_list); |
---|
372 | V_if_cloners_count++; |
---|
373 | IF_CLONERS_UNLOCK(); |
---|
374 | |
---|
375 | return (0); |
---|
376 | } |
---|
377 | |
---|
378 | struct if_clone * |
---|
379 | if_clone_advanced(const char *name, u_int maxunit, ifc_match_t match, |
---|
380 | ifc_create_t create, ifc_destroy_t destroy) |
---|
381 | { |
---|
382 | struct if_clone *ifc; |
---|
383 | |
---|
384 | ifc = if_clone_alloc(name, maxunit); |
---|
385 | ifc->ifc_type = ADVANCED; |
---|
386 | ifc->ifc_match = match; |
---|
387 | ifc->ifc_create = create; |
---|
388 | ifc->ifc_destroy = destroy; |
---|
389 | |
---|
390 | if (if_clone_attach(ifc) != 0) { |
---|
391 | if_clone_free(ifc); |
---|
392 | return (NULL); |
---|
393 | } |
---|
394 | |
---|
395 | EVENTHANDLER_INVOKE(if_clone_event, ifc); |
---|
396 | |
---|
397 | return (ifc); |
---|
398 | } |
---|
399 | |
---|
400 | struct if_clone * |
---|
401 | if_clone_simple(const char *name, ifcs_create_t create, ifcs_destroy_t destroy, |
---|
402 | u_int minifs) |
---|
403 | { |
---|
404 | struct if_clone *ifc; |
---|
405 | u_int unit; |
---|
406 | |
---|
407 | ifc = if_clone_alloc(name, 0); |
---|
408 | ifc->ifc_type = SIMPLE; |
---|
409 | ifc->ifcs_create = create; |
---|
410 | ifc->ifcs_destroy = destroy; |
---|
411 | ifc->ifcs_minifs = minifs; |
---|
412 | |
---|
413 | if (if_clone_attach(ifc) != 0) { |
---|
414 | if_clone_free(ifc); |
---|
415 | return (NULL); |
---|
416 | } |
---|
417 | |
---|
418 | for (unit = 0; unit < minifs; unit++) { |
---|
419 | char name[IFNAMSIZ]; |
---|
420 | int error; |
---|
421 | |
---|
422 | snprintf(name, IFNAMSIZ, "%s%d", ifc->ifc_name, unit); |
---|
423 | error = if_clone_createif(ifc, name, IFNAMSIZ, NULL); |
---|
424 | KASSERT(error == 0, |
---|
425 | ("%s: failed to create required interface %s", |
---|
426 | __func__, name)); |
---|
427 | } |
---|
428 | |
---|
429 | EVENTHANDLER_INVOKE(if_clone_event, ifc); |
---|
430 | |
---|
431 | return (ifc); |
---|
432 | } |
---|
433 | |
---|
434 | /* |
---|
435 | * Unregister a network interface cloner. |
---|
436 | */ |
---|
437 | void |
---|
438 | if_clone_detach(struct if_clone *ifc) |
---|
439 | { |
---|
440 | |
---|
441 | IF_CLONERS_LOCK(); |
---|
442 | LIST_REMOVE(ifc, ifc_list); |
---|
443 | V_if_cloners_count--; |
---|
444 | IF_CLONERS_UNLOCK(); |
---|
445 | |
---|
446 | /* Allow all simples to be destroyed */ |
---|
447 | if (ifc->ifc_type == SIMPLE) |
---|
448 | ifc->ifcs_minifs = 0; |
---|
449 | |
---|
450 | /* destroy all interfaces for this cloner */ |
---|
451 | while (!LIST_EMPTY(&ifc->ifc_iflist)) |
---|
452 | if_clone_destroyif(ifc, LIST_FIRST(&ifc->ifc_iflist)); |
---|
453 | |
---|
454 | IF_CLONE_REMREF(ifc); |
---|
455 | } |
---|
456 | |
---|
457 | static void |
---|
458 | if_clone_free(struct if_clone *ifc) |
---|
459 | { |
---|
460 | |
---|
461 | KASSERT(LIST_EMPTY(&ifc->ifc_iflist), |
---|
462 | ("%s: ifc_iflist not empty", __func__)); |
---|
463 | |
---|
464 | IF_CLONE_LOCK_DESTROY(ifc); |
---|
465 | delete_unrhdr(ifc->ifc_unrhdr); |
---|
466 | free(ifc, M_CLONE); |
---|
467 | } |
---|
468 | |
---|
469 | /* |
---|
470 | * Provide list of interface cloners to userspace. |
---|
471 | */ |
---|
472 | int |
---|
473 | if_clone_list(struct if_clonereq *ifcr) |
---|
474 | { |
---|
475 | char *buf, *dst, *outbuf = NULL; |
---|
476 | struct if_clone *ifc; |
---|
477 | int buf_count, count, err = 0; |
---|
478 | |
---|
479 | if (ifcr->ifcr_count < 0) |
---|
480 | return (EINVAL); |
---|
481 | |
---|
482 | IF_CLONERS_LOCK(); |
---|
483 | /* |
---|
484 | * Set our internal output buffer size. We could end up not |
---|
485 | * reporting a cloner that is added between the unlock and lock |
---|
486 | * below, but that's not a major problem. Not caping our |
---|
487 | * allocation to the number of cloners actually in the system |
---|
488 | * could be because that would let arbitrary users cause us to |
---|
489 | * allocate arbitrary amounts of kernel memory. |
---|
490 | */ |
---|
491 | buf_count = (V_if_cloners_count < ifcr->ifcr_count) ? |
---|
492 | V_if_cloners_count : ifcr->ifcr_count; |
---|
493 | IF_CLONERS_UNLOCK(); |
---|
494 | |
---|
495 | outbuf = malloc(IFNAMSIZ*buf_count, M_CLONE, M_WAITOK | M_ZERO); |
---|
496 | |
---|
497 | IF_CLONERS_LOCK(); |
---|
498 | |
---|
499 | ifcr->ifcr_total = V_if_cloners_count; |
---|
500 | if ((dst = ifcr->ifcr_buffer) == NULL) { |
---|
501 | /* Just asking how many there are. */ |
---|
502 | goto done; |
---|
503 | } |
---|
504 | count = (V_if_cloners_count < buf_count) ? |
---|
505 | V_if_cloners_count : buf_count; |
---|
506 | |
---|
507 | for (ifc = LIST_FIRST(&V_if_cloners), buf = outbuf; |
---|
508 | ifc != NULL && count != 0; |
---|
509 | ifc = LIST_NEXT(ifc, ifc_list), count--, buf += IFNAMSIZ) { |
---|
510 | strlcpy(buf, ifc->ifc_name, IFNAMSIZ); |
---|
511 | } |
---|
512 | |
---|
513 | done: |
---|
514 | IF_CLONERS_UNLOCK(); |
---|
515 | if (err == 0) |
---|
516 | err = copyout(outbuf, dst, buf_count*IFNAMSIZ); |
---|
517 | if (outbuf != NULL) |
---|
518 | free(outbuf, M_CLONE); |
---|
519 | return (err); |
---|
520 | } |
---|
521 | |
---|
522 | /* |
---|
523 | * if_clone_findifc() looks up ifnet from the current |
---|
524 | * cloner list, and returns ifc if found. Note that ifc_refcnt |
---|
525 | * is incremented. |
---|
526 | */ |
---|
527 | struct if_clone * |
---|
528 | if_clone_findifc(struct ifnet *ifp) |
---|
529 | { |
---|
530 | struct if_clone *ifc, *ifc0; |
---|
531 | struct ifnet *ifcifp; |
---|
532 | |
---|
533 | ifc0 = NULL; |
---|
534 | IF_CLONERS_LOCK(); |
---|
535 | LIST_FOREACH(ifc, &V_if_cloners, ifc_list) { |
---|
536 | IF_CLONE_LOCK(ifc); |
---|
537 | LIST_FOREACH(ifcifp, &ifc->ifc_iflist, if_clones) { |
---|
538 | if (ifp == ifcifp) { |
---|
539 | ifc0 = ifc; |
---|
540 | IF_CLONE_ADDREF_LOCKED(ifc); |
---|
541 | break; |
---|
542 | } |
---|
543 | } |
---|
544 | IF_CLONE_UNLOCK(ifc); |
---|
545 | if (ifc0 != NULL) |
---|
546 | break; |
---|
547 | } |
---|
548 | IF_CLONERS_UNLOCK(); |
---|
549 | |
---|
550 | return (ifc0); |
---|
551 | } |
---|
552 | |
---|
553 | /* |
---|
554 | * if_clone_addgroup() decrements ifc_refcnt because it is called after |
---|
555 | * if_clone_findifc(). |
---|
556 | */ |
---|
557 | void |
---|
558 | if_clone_addgroup(struct ifnet *ifp, struct if_clone *ifc) |
---|
559 | { |
---|
560 | |
---|
561 | if_addgroup(ifp, ifc->ifc_name); |
---|
562 | IF_CLONE_REMREF(ifc); |
---|
563 | } |
---|
564 | |
---|
565 | /* |
---|
566 | * A utility function to extract unit numbers from interface names of |
---|
567 | * the form name###. |
---|
568 | * |
---|
569 | * Returns 0 on success and an error on failure. |
---|
570 | */ |
---|
571 | int |
---|
572 | ifc_name2unit(const char *name, int *unit) |
---|
573 | { |
---|
574 | const char *cp; |
---|
575 | int cutoff = INT_MAX / 10; |
---|
576 | int cutlim = INT_MAX % 10; |
---|
577 | |
---|
578 | for (cp = name; *cp != '\0' && (*cp < '0' || *cp > '9'); cp++); |
---|
579 | if (*cp == '\0') { |
---|
580 | *unit = -1; |
---|
581 | } else if (cp[0] == '0' && cp[1] != '\0') { |
---|
582 | /* Disallow leading zeroes. */ |
---|
583 | return (EINVAL); |
---|
584 | } else { |
---|
585 | for (*unit = 0; *cp != '\0'; cp++) { |
---|
586 | if (*cp < '0' || *cp > '9') { |
---|
587 | /* Bogus unit number. */ |
---|
588 | return (EINVAL); |
---|
589 | } |
---|
590 | if (*unit > cutoff || |
---|
591 | (*unit == cutoff && *cp - '0' > cutlim)) |
---|
592 | return (EINVAL); |
---|
593 | *unit = (*unit * 10) + (*cp - '0'); |
---|
594 | } |
---|
595 | } |
---|
596 | |
---|
597 | return (0); |
---|
598 | } |
---|
599 | |
---|
600 | int |
---|
601 | ifc_alloc_unit(struct if_clone *ifc, int *unit) |
---|
602 | { |
---|
603 | char name[IFNAMSIZ]; |
---|
604 | int wildcard; |
---|
605 | |
---|
606 | wildcard = (*unit < 0); |
---|
607 | retry: |
---|
608 | if (*unit > ifc->ifc_maxunit) |
---|
609 | return (ENOSPC); |
---|
610 | if (*unit < 0) { |
---|
611 | *unit = alloc_unr(ifc->ifc_unrhdr); |
---|
612 | if (*unit == -1) |
---|
613 | return (ENOSPC); |
---|
614 | } else { |
---|
615 | *unit = alloc_unr_specific(ifc->ifc_unrhdr, *unit); |
---|
616 | if (*unit == -1) { |
---|
617 | if (wildcard) { |
---|
618 | (*unit)++; |
---|
619 | goto retry; |
---|
620 | } else |
---|
621 | return (EEXIST); |
---|
622 | } |
---|
623 | } |
---|
624 | |
---|
625 | snprintf(name, IFNAMSIZ, "%s%d", ifc->ifc_name, *unit); |
---|
626 | if (ifunit(name) != NULL) { |
---|
627 | free_unr(ifc->ifc_unrhdr, *unit); |
---|
628 | if (wildcard) { |
---|
629 | (*unit)++; |
---|
630 | goto retry; |
---|
631 | } else |
---|
632 | return (EEXIST); |
---|
633 | } |
---|
634 | |
---|
635 | IF_CLONE_ADDREF(ifc); |
---|
636 | |
---|
637 | return (0); |
---|
638 | } |
---|
639 | |
---|
640 | void |
---|
641 | ifc_free_unit(struct if_clone *ifc, int unit) |
---|
642 | { |
---|
643 | |
---|
644 | free_unr(ifc->ifc_unrhdr, unit); |
---|
645 | IF_CLONE_REMREF(ifc); |
---|
646 | } |
---|
647 | |
---|
648 | static int |
---|
649 | ifc_simple_match(struct if_clone *ifc, const char *name) |
---|
650 | { |
---|
651 | const char *cp; |
---|
652 | int i; |
---|
653 | |
---|
654 | /* Match the name */ |
---|
655 | for (cp = name, i = 0; i < strlen(ifc->ifc_name); i++, cp++) { |
---|
656 | if (ifc->ifc_name[i] != *cp) |
---|
657 | return (0); |
---|
658 | } |
---|
659 | |
---|
660 | /* Make sure there's a unit number or nothing after the name */ |
---|
661 | for (; *cp != '\0'; cp++) { |
---|
662 | if (*cp < '0' || *cp > '9') |
---|
663 | return (0); |
---|
664 | } |
---|
665 | |
---|
666 | return (1); |
---|
667 | } |
---|
668 | |
---|
669 | static int |
---|
670 | ifc_simple_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) |
---|
671 | { |
---|
672 | char *dp; |
---|
673 | int wildcard; |
---|
674 | int unit; |
---|
675 | int err; |
---|
676 | |
---|
677 | err = ifc_name2unit(name, &unit); |
---|
678 | if (err != 0) |
---|
679 | return (err); |
---|
680 | |
---|
681 | wildcard = (unit < 0); |
---|
682 | |
---|
683 | err = ifc_alloc_unit(ifc, &unit); |
---|
684 | if (err != 0) |
---|
685 | return (err); |
---|
686 | |
---|
687 | err = ifc->ifcs_create(ifc, unit, params); |
---|
688 | if (err != 0) { |
---|
689 | ifc_free_unit(ifc, unit); |
---|
690 | return (err); |
---|
691 | } |
---|
692 | |
---|
693 | /* In the wildcard case, we need to update the name. */ |
---|
694 | if (wildcard) { |
---|
695 | for (dp = name; *dp != '\0'; dp++); |
---|
696 | if (snprintf(dp, len - (dp-name), "%d", unit) > |
---|
697 | len - (dp-name) - 1) { |
---|
698 | /* |
---|
699 | * This can only be a programmer error and |
---|
700 | * there's no straightforward way to recover if |
---|
701 | * it happens. |
---|
702 | */ |
---|
703 | panic("if_clone_create(): interface name too long"); |
---|
704 | } |
---|
705 | |
---|
706 | } |
---|
707 | |
---|
708 | return (0); |
---|
709 | } |
---|
710 | |
---|
711 | static int |
---|
712 | ifc_simple_destroy(struct if_clone *ifc, struct ifnet *ifp) |
---|
713 | { |
---|
714 | int unit; |
---|
715 | |
---|
716 | unit = ifp->if_dunit; |
---|
717 | |
---|
718 | if (unit < ifc->ifcs_minifs) |
---|
719 | return (EINVAL); |
---|
720 | |
---|
721 | ifc->ifcs_destroy(ifp); |
---|
722 | |
---|
723 | ifc_free_unit(ifc, unit); |
---|
724 | |
---|
725 | return (0); |
---|
726 | } |
---|