nrelease - fix/improve livecd
[dragonfly.git] / sys / net / if_clone.c
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  * 3. 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.3 (Berkeley) 1/4/94
30  * $FreeBSD: src/sys/net/if.c,v 1.185 2004/03/13 02:35:03 brooks Exp $
31  */
32
33 #include <sys/param.h>
34 #include <sys/kernel.h>
35 #include <sys/malloc.h>
36 #include <sys/eventhandler.h>
37 #include <sys/limits.h>
38
39 #include <net/if.h>
40 #include <net/if_var.h>
41 #include <net/if_clone.h>
42
43 static LIST_HEAD(, if_clone) if_cloners = LIST_HEAD_INITIALIZER(if_cloners);
44 static int if_cloners_count;
45
46 MALLOC_DEFINE(M_CLONE, "clone", "interface cloning framework");
47
48 static int      if_name2unit(const char *, int *);
49 static bool     if_clone_match(struct if_clone *, const char *);
50 static struct if_clone *if_clone_lookup(const char *);
51 static int      if_clone_alloc_unit(struct if_clone *, int *);
52 static void     if_clone_free_unit(struct if_clone *, int);
53 static int      if_clone_createif(struct if_clone *, int, caddr_t, caddr_t);
54
55 /*
56  * Lookup the cloner and create a clone network interface.
57  */
58 int
59 if_clone_create(char *name, int len, caddr_t params, caddr_t data)
60 {
61         struct if_clone *ifc;
62         char ifname[IFNAMSIZ];
63         bool wildcard;
64         int unit;
65         int err;
66
67         if ((ifc = if_clone_lookup(name)) == NULL)
68                 return (EINVAL);
69         if ((err = if_name2unit(name, &unit)) != 0)
70                 return (err);
71
72         wildcard = (unit < 0);
73
74         ifnet_lock();
75         if ((err = if_clone_alloc_unit(ifc, &unit)) != 0) {
76                 ifnet_unlock();
77                 return (err);
78         }
79
80         ksnprintf(ifname, IFNAMSIZ, "%s%d", ifc->ifc_name, unit);
81
82         /*
83          * Update the name with the allocated unit for the caller,
84          * who must preserve enough space.
85          */
86         if (wildcard && strlcpy(name, ifname, len) >= len) {
87                 if_clone_free_unit(ifc, unit);
88                 ifnet_unlock();
89                 return (ENOSPC);
90         }
91
92         err = if_clone_createif(ifc, unit, params, data);
93         if (err)
94                 if_clone_free_unit(ifc, unit);
95         ifnet_unlock();
96
97         return (err);
98 }
99
100 /*
101  * Lookup the cloner and destroy a clone network interface.
102  */
103 int
104 if_clone_destroy(const char *name)
105 {
106         struct if_clone *ifc;
107         struct ifnet *ifp;
108         int unit, error;
109
110         ifnet_lock();
111         ifp = ifunit(name);
112         ifnet_unlock();
113         if (ifp == NULL)
114                 return (ENXIO);
115
116         if ((ifc = if_clone_lookup(ifp->if_dname)) == NULL)
117                 return (EINVAL);
118
119         unit = ifp->if_dunit;
120         if (unit < ifc->ifc_minifs)
121                 return (EINVAL);
122
123         if (ifc->ifc_destroy == NULL)
124                 return (EOPNOTSUPP);
125
126         ifnet_lock();
127         if_clone_free_unit(ifc, unit);
128         error = ifc->ifc_destroy(ifp);
129         if (error)
130                 if_clone_alloc_unit(ifc, &unit);
131         /* else ifc structure is dead */
132         ifnet_unlock();
133
134         return (error);
135 }
136
137 /*
138  * Register a network interface cloner.
139  */
140 int
141 if_clone_attach(struct if_clone *ifc)
142 {
143         struct if_clone *ifct;
144         int len, maxclone;
145         int unit;
146
147         LIST_FOREACH(ifct, &if_cloners, ifc_list) {
148                 if (strcmp(ifct->ifc_name, ifc->ifc_name) == 0)
149                         return (EEXIST);
150         }
151
152         KASSERT(ifc->ifc_minifs - 1 <= ifc->ifc_maxunit,
153             ("%s: %s requested more units then allowed (%d > %d)",
154             __func__, ifc->ifc_name, ifc->ifc_minifs,
155             ifc->ifc_maxunit + 1));
156         /*
157          * Compute bitmap size and allocate it.
158          */
159         maxclone = ifc->ifc_maxunit + 1;
160         len = maxclone >> 3;
161         if ((len << 3) < maxclone)
162                 len++;
163         ifc->ifc_units = kmalloc(len, M_CLONE, M_WAITOK | M_ZERO);
164         ifc->ifc_bmlen = len;
165
166         LIST_INSERT_HEAD(&if_cloners, ifc, ifc_list);
167         if_cloners_count++;
168
169         ifnet_lock();
170         for (unit = 0; unit < ifc->ifc_minifs; unit++) {
171                 if_clone_alloc_unit(ifc, &unit);
172                 if (if_clone_createif(ifc, unit, NULL, NULL) != 0) {
173                         ifnet_unlock();
174                         panic("%s: failed to create required interface %s%d",
175                               __func__, ifc->ifc_name, unit);
176                 }
177         }
178         ifnet_unlock();
179
180         EVENTHANDLER_INVOKE(if_clone_event, ifc);
181
182         return (0);
183 }
184
185 /*
186  * Unregister a network interface cloner.
187  */
188 void
189 if_clone_detach(struct if_clone *ifc)
190 {
191
192         LIST_REMOVE(ifc, ifc_list);
193         kfree(ifc->ifc_units, M_CLONE);
194         if_cloners_count--;
195 }
196
197 /*
198  * Provide list of interface cloners to userspace.
199  */
200 int
201 if_clone_list(struct if_clonereq *ifcr)
202 {
203         char outbuf[IFNAMSIZ], *dst;
204         struct if_clone *ifc;
205         int count, error = 0;
206
207         ifcr->ifcr_total = if_cloners_count;
208         if ((dst = ifcr->ifcr_buffer) == NULL) {
209                 /* Just asking how many there are. */
210                 return (0);
211         }
212
213         if (ifcr->ifcr_count < 0)
214                 return (EINVAL);
215
216         count = (if_cloners_count < ifcr->ifcr_count) ?
217             if_cloners_count : ifcr->ifcr_count;
218
219         for (ifc = LIST_FIRST(&if_cloners);
220              ifc != NULL && count != 0;
221              ifc = LIST_NEXT(ifc, ifc_list), count--, dst += IFNAMSIZ) {
222                 bzero(outbuf, IFNAMSIZ);        /* sanitize */
223                 strlcpy(outbuf, ifc->ifc_name, IFNAMSIZ);
224                 error = copyout(outbuf, dst, IFNAMSIZ);
225                 if (error)
226                         break;
227         }
228
229         return (error);
230 }
231
232 /*
233  * Extract the unit number from interface name of the form "name###".
234  * A unit of -1 is stored if the given name doesn't have a unit.
235  *
236  * Returns 0 on success and an error on failure.
237  */
238 static int
239 if_name2unit(const char *name, int *unit)
240 {
241         const char *cp;
242         int cutoff = INT_MAX / 10;
243         int cutlim = INT_MAX % 10;
244
245         for (cp = name; *cp != '\0' && (*cp < '0' || *cp > '9'); cp++)
246                 ;
247         if (*cp == '\0') {
248                 *unit = -1;
249         } else if (cp[0] == '0' && cp[1] != '\0') {
250                 /* Disallow leading zeroes. */
251                 return (EINVAL);
252         } else {
253                 for (*unit = 0; *cp != '\0'; cp++) {
254                         if (*cp < '0' || *cp > '9') {
255                                 /* Bogus unit number. */
256                                 return (EINVAL);
257                         }
258                         if (*unit > cutoff ||
259                             (*unit == cutoff && *cp - '0' > cutlim))
260                                 return (EINVAL);
261                         *unit = (*unit * 10) + (*cp - '0');
262                 }
263         }
264
265         return (0);
266 }
267
268 /*
269  * Check whether the interface cloner matches the name.
270  */
271 static bool
272 if_clone_match(struct if_clone *ifc, const char *name)
273 {
274         const char *cp;
275         int i;
276
277         /* Match the name */
278         for (cp = name, i = 0; i < strlen(ifc->ifc_name); i++, cp++) {
279                 if (ifc->ifc_name[i] != *cp)
280                         return (false);
281         }
282
283         /* Make sure there's a unit number or nothing after the name */
284         for ( ; *cp != '\0'; cp++) {
285                 if (*cp < '0' || *cp > '9')
286                         return (false);
287         }
288
289         return (true);
290 }
291
292 /*
293  * Look up a network interface cloner.
294  */
295 static struct if_clone *
296 if_clone_lookup(const char *name)
297 {
298         struct if_clone *ifc;
299
300         LIST_FOREACH(ifc, &if_cloners, ifc_list) {
301                 if (if_clone_match(ifc, name))
302                         return ifc;
303         }
304
305         return (NULL);
306 }
307
308 /*
309  * Allocate a unit number.
310  *
311  * ifnet must be locked.
312  *
313  * Returns 0 on success and an error on failure.
314  */
315 static int
316 if_clone_alloc_unit(struct if_clone *ifc, int *unit)
317 {
318         int bytoff, bitoff;
319
320         if (*unit < 0) {
321                 /*
322                  * Wildcard mode: find a free unit.
323                  */
324                 bytoff = bitoff = 0;
325                 while (bytoff < ifc->ifc_bmlen &&
326                        ifc->ifc_units[bytoff] == 0xff)
327                         bytoff++;
328                 if (bytoff >= ifc->ifc_bmlen)
329                         return (ENOSPC);
330                 while ((ifc->ifc_units[bytoff] & (1 << bitoff)) != 0)
331                         bitoff++;
332                 *unit = (bytoff << 3) + bitoff;
333         } else {
334                 bytoff = *unit >> 3;
335                 bitoff = *unit - (bytoff << 3);
336         }
337
338         if (*unit > ifc->ifc_maxunit)
339                 return (ENXIO);
340
341         /*
342          * Allocate the unit in the bitmap.
343          */
344 #if 0
345         KASSERT((ifc->ifc_units[bytoff] & (1 << bitoff)) == 0,
346                 ("%s: bit is already set", __func__));
347 #endif
348         if (ifc->ifc_units[bytoff] & (1 << bitoff))
349                 return (EEXIST);
350         ifc->ifc_units[bytoff] |= (1 << bitoff);
351
352         return (0);
353 }
354
355 /*
356  * Free an allocated unit number.
357  *
358  * ifnet must be locked.
359  */
360 static void
361 if_clone_free_unit(struct if_clone *ifc, int unit)
362 {
363         int bytoff, bitoff;
364
365         bytoff = unit >> 3;
366         bitoff = unit - (bytoff << 3);
367         KASSERT((ifc->ifc_units[bytoff] & (1 << bitoff)) != 0,
368                 ("%s: bit is already cleared", __func__));
369         ifc->ifc_units[bytoff] &= ~(1 << bitoff);
370 }
371
372 /*
373  * Create a clone network interface.
374  *
375  * ifnet must be locked
376  */
377 static int
378 if_clone_createif(struct if_clone *ifc, int unit, caddr_t params, caddr_t data)
379 {
380         struct ifnet *ifp;
381         char ifname[IFNAMSIZ];
382         int err;
383
384         ksnprintf(ifname, IFNAMSIZ, "%s%d", ifc->ifc_name, unit);
385
386         ifp = ifunit(ifname);
387         if (ifp != NULL)
388                 return (EEXIST);
389
390         /*ifnet_unlock();*/
391         err = (*ifc->ifc_create)(ifc, unit, params, data);
392         /*ifnet_lock();*/
393         if (err != 0)
394                 return (err);
395
396         ifp = ifunit(ifname);
397         if (ifp == NULL)
398                 return (ENXIO);
399
400         err = if_addgroup(ifp, ifc->ifc_name);
401         if (err != 0)
402                 return (err);
403
404         return (0);
405 }