xref: /freebsd/sys/net/if_clone.c (revision d37ea99837e6ad50837fd9fe1771ddf1c3ba6002)
1 /*
2  * Copyright (c) 1980, 1986, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 4. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  *	@(#)if.c	8.5 (Berkeley) 1/9/95
30  * $FreeBSD$
31  */
32 
33 #include <sys/param.h>
34 #include <sys/malloc.h>
35 #include <sys/lock.h>
36 #include <sys/mutex.h>
37 #include <sys/kernel.h>
38 #include <sys/systm.h>
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 
42 #include <net/if.h>
43 #include <net/if_clone.h>
44 #if 0
45 #include <net/if_dl.h>
46 #endif
47 #include <net/if_types.h>
48 #include <net/if_var.h>
49 #include <net/radix.h>
50 #include <net/route.h>
51 
52 static void		if_clone_free(struct if_clone *ifc);
53 
54 static struct mtx	if_cloners_mtx;
55 static int		if_cloners_count;
56 LIST_HEAD(, if_clone)	if_cloners = LIST_HEAD_INITIALIZER(if_cloners);
57 
58 #define IF_CLONERS_LOCK_INIT()		\
59     mtx_init(&if_cloners_mtx, "if_cloners lock", NULL, MTX_DEF)
60 #define IF_CLONERS_LOCK_ASSERT()	mtx_assert(&if_cloners_mtx, MA_OWNED)
61 #define IF_CLONERS_LOCK()		mtx_lock(&if_cloners_mtx)
62 #define IF_CLONERS_UNLOCK()		mtx_unlock(&if_cloners_mtx)
63 
64 #define IF_CLONE_LOCK_INIT(ifc)		\
65     mtx_init(&(ifc)->ifc_mtx, "if_clone lock", NULL, MTX_DEF)
66 #define IF_CLONE_LOCK_DESTROY(ifc)	mtx_destroy(&(ifc)->ifc_mtx)
67 #define IF_CLONE_LOCK_ASSERT(ifc)	mtx_assert(&(ifc)->ifc_mtx, MA_OWNED)
68 #define IF_CLONE_LOCK(ifc)		mtx_lock(&(ifc)->ifc_mtx)
69 #define IF_CLONE_UNLOCK(ifc)		mtx_unlock(&(ifc)->ifc_mtx)
70 
71 #define IF_CLONE_ADDREF(ifc)						\
72 	do {								\
73 		IF_CLONE_LOCK(ifc);					\
74 		IF_CLONE_ADDREF_LOCKED(ifc);				\
75 		IF_CLONE_UNLOCK(ifc);					\
76 	} while (0)
77 #define IF_CLONE_ADDREF_LOCKED(ifc)					\
78 	do {								\
79 		IF_CLONE_LOCK_ASSERT(ifc);				\
80 		KASSERT((ifc)->ifc_refcnt >= 0,				\
81 		    ("negative refcnt %ld", (ifc)->ifc_refcnt));	\
82 		(ifc)->ifc_refcnt++;					\
83 	} while (0)
84 #define IF_CLONE_REMREF(ifc)						\
85 	do {								\
86 		IF_CLONE_LOCK(ifc);					\
87 		IF_CLONE_REMREF_LOCKED(ifc);				\
88 	} while (0)
89 #define IF_CLONE_REMREF_LOCKED(ifc)					\
90 	do {								\
91 		IF_CLONE_LOCK_ASSERT(ifc);				\
92 		KASSERT((ifc)->ifc_refcnt > 0,				\
93 		    ("bogus refcnt %ld", (ifc)->ifc_refcnt));		\
94 		if (--(ifc)->ifc_refcnt == 0) {				\
95 			IF_CLONE_UNLOCK(ifc);				\
96 			if_clone_free(ifc);				\
97 		}							\
98 		/* silently free the lock */				\
99 		IF_CLONE_UNLOCK(ifc);					\
100 	} while (0)
101 
102 MALLOC_DEFINE(M_CLONE, "clone", "interface cloning framework");
103 
104 void
105 if_clone_init(void)
106 {
107 	IF_CLONERS_LOCK_INIT();
108 }
109 
110 /*
111  * Create a clone network interface.
112  */
113 int
114 if_clone_create(char *name, size_t len)
115 {
116 	int err;
117 	struct if_clone *ifc;
118 
119 	if (ifunit(name) != NULL)
120 		return (EEXIST);
121 
122 	/* Try to find an applicable cloner for this request */
123 	IF_CLONERS_LOCK();
124 	LIST_FOREACH(ifc, &if_cloners, ifc_list) {
125 		if (ifc->ifc_match(ifc, name)) {
126 			IF_CLONE_ADDREF(ifc);
127 			break;
128 		}
129 	}
130 	IF_CLONERS_UNLOCK();
131 
132 	if (ifc == NULL)
133 		return (EINVAL);
134 
135 	err = (*ifc->ifc_create)(ifc, name, len);
136 	IF_CLONE_REMREF(ifc);
137 	return (err);
138 }
139 
140 /*
141  * Destroy a clone network interface.
142  */
143 int
144 if_clone_destroy(const char *name)
145 {
146 	int err;
147 	struct if_clone *ifc;
148 	struct ifnet *ifp;
149 
150 	ifp = ifunit(name);
151 	if (ifp == NULL)
152 		return (ENXIO);
153 
154 	/* Find the cloner for this interface */
155 	IF_CLONERS_LOCK();
156 	LIST_FOREACH(ifc, &if_cloners, ifc_list) {
157 		if (strcmp(ifc->ifc_name, ifp->if_dname) == 0) {
158 			IF_CLONE_ADDREF(ifc);
159 			break;
160 		}
161 	}
162 	IF_CLONERS_UNLOCK();
163 	if (ifc == NULL)
164 		return (EINVAL);
165 
166 	if (ifc->ifc_destroy == NULL) {
167 		err = EOPNOTSUPP;
168 		goto done;
169 	}
170 
171 	err =  (*ifc->ifc_destroy)(ifc, ifp);
172 
173 done:
174 	IF_CLONE_REMREF(ifc);
175 	return (err);
176 }
177 
178 /*
179  * Register a network interface cloner.
180  */
181 void
182 if_clone_attach(struct if_clone *ifc)
183 {
184 	int len, maxclone;
185 
186 	/*
187 	 * Compute bitmap size and allocate it.
188 	 */
189 	maxclone = ifc->ifc_maxunit + 1;
190 	len = maxclone >> 3;
191 	if ((len << 3) < maxclone)
192 		len++;
193 	ifc->ifc_units = malloc(len, M_CLONE, M_WAITOK | M_ZERO);
194 	ifc->ifc_bmlen = len;
195 	IF_CLONE_LOCK_INIT(ifc);
196 	IF_CLONE_ADDREF(ifc);
197 
198 	IF_CLONERS_LOCK();
199 	LIST_INSERT_HEAD(&if_cloners, ifc, ifc_list);
200 	if_cloners_count++;
201 	IF_CLONERS_UNLOCK();
202 
203 	if (ifc->ifc_attach != NULL)
204 		(*ifc->ifc_attach)(ifc);
205 	EVENTHANDLER_INVOKE(if_clone_event, ifc);
206 }
207 
208 /*
209  * Unregister a network interface cloner.
210  */
211 void
212 if_clone_detach(struct if_clone *ifc)
213 {
214 
215 	IF_CLONERS_LOCK();
216 	LIST_REMOVE(ifc, ifc_list);
217 	if_cloners_count--;
218 	IF_CLONERS_UNLOCK();
219 
220 	IF_CLONE_REMREF(ifc);
221 }
222 
223 static void
224 if_clone_free(struct if_clone *ifc)
225 {
226 
227 	IF_CLONE_LOCK_DESTROY(ifc);
228 	free(ifc->ifc_units, M_CLONE);
229 }
230 
231 /*
232  * Provide list of interface cloners to userspace.
233  */
234 int
235 if_clone_list(struct if_clonereq *ifcr)
236 {
237 	char outbuf[IFNAMSIZ], *dst;
238 	struct if_clone *ifc;
239 	int count, err = 0;
240 
241 	IF_CLONERS_LOCK();
242 
243 	ifcr->ifcr_total = if_cloners_count;
244 	if ((dst = ifcr->ifcr_buffer) == NULL) {
245 		/* Just asking how many there are. */
246 		goto done;
247 	}
248 
249 	if (ifcr->ifcr_count < 0) {
250 		err = EINVAL;
251 		goto done;
252 	}
253 
254 	count = (if_cloners_count < ifcr->ifcr_count) ?
255 	    if_cloners_count : ifcr->ifcr_count;
256 
257 	for (ifc = LIST_FIRST(&if_cloners); ifc != NULL && count != 0;
258 	     ifc = LIST_NEXT(ifc, ifc_list), count--, dst += IFNAMSIZ) {
259 		strlcpy(outbuf, ifc->ifc_name, IFNAMSIZ);
260 		err = copyout(outbuf, dst, IFNAMSIZ);
261 		if (err)
262 			break;
263 	}
264 
265 done:
266 	IF_CLONERS_UNLOCK();
267 	return (err);
268 }
269 
270 /*
271  * A utility function to extract unit numbers from interface names of
272  * the form name###.
273  *
274  * Returns 0 on success and an error on failure.
275  */
276 int
277 ifc_name2unit(const char *name, int *unit)
278 {
279 	const char	*cp;
280 
281 	for (cp = name; *cp != '\0' && (*cp < '0' || *cp > '9'); cp++);
282 	if (*cp == '\0') {
283 		*unit = -1;
284 	} else {
285 		for (*unit = 0; *cp != '\0'; cp++) {
286 			if (*cp < '0' || *cp > '9') {
287 				/* Bogus unit number. */
288 				return (EINVAL);
289 			}
290 			*unit = (*unit * 10) + (*cp - '0');
291 		}
292 	}
293 
294 	return (0);
295 }
296 
297 int
298 ifc_alloc_unit(struct if_clone *ifc, int *unit)
299 {
300 	int wildcard, bytoff, bitoff;
301 	int err = 0;
302 
303 	IF_CLONE_LOCK(ifc);
304 
305 	bytoff = bitoff = 0;
306 	wildcard = (*unit < 0);
307 	/*
308 	 * Find a free unit if none was given.
309 	 */
310 	if (wildcard) {
311 		while ((bytoff < ifc->ifc_bmlen)
312 		    && (ifc->ifc_units[bytoff] == 0xff))
313 			bytoff++;
314 		if (bytoff >= ifc->ifc_bmlen) {
315 			err = ENOSPC;
316 			goto done;
317 		}
318 		while ((ifc->ifc_units[bytoff] & (1 << bitoff)) != 0)
319 			bitoff++;
320 		*unit = (bytoff << 3) + bitoff;
321 	}
322 
323 	if (*unit > ifc->ifc_maxunit) {
324 		err = ENOSPC;
325 		goto done;
326 	}
327 
328 	if (!wildcard) {
329 		bytoff = *unit >> 3;
330 		bitoff = *unit - (bytoff << 3);
331 	}
332 
333 	if((ifc->ifc_units[bytoff] & (1 << bitoff)) != 0) {
334 		err = EEXIST;
335 		goto done;
336 	}
337 	/*
338 	 * Allocate the unit in the bitmap.
339 	 */
340 	ifc->ifc_units[bytoff] |= (1 << bitoff);
341 
342 done:
343 	IF_CLONE_UNLOCK(ifc);
344 	return (err);
345 }
346 
347 void
348 ifc_free_unit(struct if_clone *ifc, int unit)
349 {
350 	int bytoff, bitoff;
351 
352 
353 	/*
354 	 * Compute offset in the bitmap and deallocate the unit.
355 	 */
356 	bytoff = unit >> 3;
357 	bitoff = unit - (bytoff << 3);
358 
359 	IF_CLONE_LOCK(ifc);
360 	KASSERT((ifc->ifc_units[bytoff] & (1 << bitoff)) != 0,
361 	    ("%s: bit is already cleared", __func__));
362 	ifc->ifc_units[bytoff] &= ~(1 << bitoff);
363 	IF_CLONE_UNLOCK(ifc);
364 }
365 
366 void
367 ifc_simple_attach(struct if_clone *ifc)
368 {
369 	int err;
370 	int unit;
371 	char name[IFNAMSIZ];
372 	struct ifc_simple_data *ifcs = ifc->ifc_data;
373 
374 	KASSERT(ifcs->ifcs_minifs - 1 <= ifc->ifc_maxunit,
375 	    ("%s: %s requested more units then allowed (%d > %d)",
376 	    __func__, ifc->ifc_name, ifcs->ifcs_minifs,
377 	    ifc->ifc_maxunit + 1));
378 
379 	for (unit = 0; unit < ifcs->ifcs_minifs; unit++) {
380 		snprintf(name, IFNAMSIZ, "%s%d", ifc->ifc_name, unit);
381 		err = (*ifc->ifc_create)(ifc, name, IFNAMSIZ);
382 		KASSERT(err == 0,
383 		    ("%s: failed to create required interface %s",
384 		    __func__, name));
385 	}
386 }
387 
388 int
389 ifc_simple_match(struct if_clone *ifc, const char *name)
390 {
391 	const char *cp;
392 	int i;
393 
394 	/* Match the name */
395 	for (cp = name, i = 0; i < strlen(ifc->ifc_name); i++, cp++) {
396 		if (ifc->ifc_name[i] != *cp)
397 			return (0);
398 	}
399 
400 	/* Make sure there's a unit number or nothing after the name */
401 	for (; *cp != '\0'; cp++) {
402 		if (*cp < '0' || *cp > '9')
403 			return (0);
404 	}
405 
406 	return (1);
407 }
408 
409 int
410 ifc_simple_create(struct if_clone *ifc, char *name, size_t len)
411 {
412 	char *dp;
413 	int wildcard;
414 	int unit;
415 	int err;
416 	struct ifc_simple_data *ifcs = ifc->ifc_data;
417 
418 	err = ifc_name2unit(name, &unit);
419 	if (err != 0)
420 		return (err);
421 
422 	wildcard = (unit < 0);
423 
424 	err = ifc_alloc_unit(ifc, &unit);
425 	if (err != 0)
426 		return (err);
427 
428 	err = ifcs->ifcs_create(ifc, unit);
429 	if (err != 0) {
430 		ifc_free_unit(ifc, unit);
431 		return (err);
432 	}
433 
434 	/* In the wildcard case, we need to update the name. */
435 	if (wildcard) {
436 		for (dp = name; *dp != '\0'; dp++);
437 		if (snprintf(dp, len - (dp-name), "%d", unit) >
438 		    len - (dp-name) - 1) {
439 			/*
440 			 * This can only be a programmer error and
441 			 * there's no straightforward way to recover if
442 			 * it happens.
443 			 */
444 			panic("if_clone_create(): interface name too long");
445 		}
446 
447 	}
448 
449 	return (0);
450 }
451 
452 int
453 ifc_simple_destroy(struct if_clone *ifc, struct ifnet *ifp)
454 {
455 	int unit;
456 	struct ifc_simple_data *ifcs = ifc->ifc_data;
457 
458 	unit = ifp->if_dunit;
459 
460 	if (unit < ifcs->ifcs_minifs)
461 		return (EINVAL);
462 
463 	ifcs->ifcs_destroy(ifp);
464 
465 	ifc_free_unit(ifc, unit);
466 
467 	return (0);
468 }
469