]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netgraph/ng_eiface.c
This commit was generated by cvs2svn to compensate for changes in r138296,
[FreeBSD/FreeBSD.git] / sys / netgraph / ng_eiface.c
1 /*-
2  *
3  * Copyright (c) 1999-2001, Vitaly V Belekhov
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice unmodified, this list of conditions, and the following
11  *    disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $FreeBSD$
29  */
30
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/errno.h>
35 #include <sys/kernel.h>
36 #include <sys/lock.h>
37 #include <sys/malloc.h>
38 #include <sys/mbuf.h>
39 #include <sys/mutex.h>
40 #include <sys/errno.h>
41 #include <sys/sockio.h>
42 #include <sys/socket.h>
43 #include <sys/syslog.h>
44
45 #include <net/if.h>
46 #include <net/if_dl.h>
47 #include <net/if_types.h>
48 #include <net/netisr.h>
49
50 #include <netinet/in.h>
51 #include <netinet/if_ether.h>
52
53 #include <netgraph/ng_message.h>
54 #include <netgraph/netgraph.h>
55 #include <netgraph/ng_parse.h>
56 #include <netgraph/ng_eiface.h>
57
58 #include <net/bpf.h>
59 #include <net/ethernet.h>
60 #include <net/if_arp.h>
61
62 static const struct ng_cmdlist ng_eiface_cmdlist[] = {
63         {
64           NGM_EIFACE_COOKIE,
65           NGM_EIFACE_SET,
66           "set",
67           &ng_parse_enaddr_type,
68           NULL
69         },
70         { 0 }
71 };
72
73
74 /* Node private data */
75 struct ng_eiface_private {
76         struct arpcom   arpcom; /* per-interface network data */
77         struct ifnet   *ifp;    /* This interface */
78         int     unit;           /* Interface unit number */
79         node_p          node;   /* Our netgraph node */
80         hook_p          ether;  /* Hook for ethernet stream */
81 };
82 typedef struct ng_eiface_private *priv_p;
83
84 /* Interface methods */
85 static void     ng_eiface_init(void *xsc);
86 static void     ng_eiface_start(struct ifnet *ifp);
87 static int      ng_eiface_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data);
88 #ifdef DEBUG
89 static void     ng_eiface_print_ioctl(struct ifnet *ifp, int cmd, caddr_t data);
90 #endif
91
92 /* Netgraph methods */
93 static ng_constructor_t ng_eiface_constructor;
94 static ng_rcvmsg_t ng_eiface_rcvmsg;
95 static ng_shutdown_t ng_eiface_rmnode;
96 static ng_newhook_t ng_eiface_newhook;
97 static ng_rcvdata_t ng_eiface_rcvdata;
98 static ng_connect_t ng_eiface_connect;
99 static ng_disconnect_t ng_eiface_disconnect;
100
101 /* Node type descriptor */
102 static struct ng_type typestruct = {
103         .version =      NG_ABI_VERSION,
104         .name =         NG_EIFACE_NODE_TYPE,
105         .constructor =  ng_eiface_constructor,
106         .rcvmsg =       ng_eiface_rcvmsg,
107         .shutdown =     ng_eiface_rmnode,
108         .newhook =      ng_eiface_newhook,
109         .connect =      ng_eiface_connect,
110         .rcvdata =      ng_eiface_rcvdata,
111         .disconnect =   ng_eiface_disconnect,
112         .cmdlist =      ng_eiface_cmdlist
113 };
114 NETGRAPH_INIT(eiface, &typestruct);
115
116 /* We keep a bitmap indicating which unit numbers are free.
117    One means the unit number is free, zero means it's taken. */
118 static int      *ng_eiface_units = NULL;
119 static int      ng_eiface_units_len = 0;
120 static int      ng_units_in_use = 0;
121
122 #define UNITS_BITSPERWORD       (sizeof(*ng_eiface_units) * NBBY)
123
124 static struct mtx       ng_eiface_mtx;
125 MTX_SYSINIT(ng_eiface, &ng_eiface_mtx, "ng_eiface", MTX_DEF);
126
127 /************************************************************************
128                         HELPER STUFF
129  ************************************************************************/
130 /*
131  * Find the first free unit number for a new interface.
132  * Increase the size of the unit bitmap as necessary.
133  */
134 static __inline int
135 ng_eiface_get_unit(int *unit)
136 {
137         int index, bit;
138
139         mtx_lock(&ng_eiface_mtx);
140         for (index = 0; index < ng_eiface_units_len
141             && ng_eiface_units[index] == 0; index++);
142         if (index == ng_eiface_units_len) {             /* extend array */
143                 int i, *newarray, newlen;
144
145                 newlen = (2 * ng_eiface_units_len) + 4;
146                 MALLOC(newarray, int *, newlen * sizeof(*ng_eiface_units),
147                     M_NETGRAPH, M_NOWAIT);
148                 if (newarray == NULL) {
149                         mtx_unlock(&ng_eiface_mtx);
150                         return (ENOMEM);
151                 }
152                 bcopy(ng_eiface_units, newarray,
153                     ng_eiface_units_len * sizeof(*ng_eiface_units));
154                 for (i = ng_eiface_units_len; i < newlen; i++)
155                         newarray[i] = ~0;
156                 if (ng_eiface_units != NULL)
157                         FREE(ng_eiface_units, M_NETGRAPH);
158                 ng_eiface_units = newarray;
159                 ng_eiface_units_len = newlen;
160         }
161         bit = ffs(ng_eiface_units[index]) - 1;
162         KASSERT(bit >= 0 && bit <= UNITS_BITSPERWORD - 1,
163             ("%s: word=%d bit=%d", __func__, ng_eiface_units[index], bit));
164         ng_eiface_units[index] &= ~(1 << bit);
165         *unit = (index * UNITS_BITSPERWORD) + bit;
166         ng_units_in_use++;
167         mtx_unlock(&ng_eiface_mtx);
168         return (0);
169 }
170
171 /*
172  * Free a no longer needed unit number.
173  */
174 static __inline void
175 ng_eiface_free_unit(int unit)
176 {
177         int index, bit;
178
179         index = unit / UNITS_BITSPERWORD;
180         bit = unit % UNITS_BITSPERWORD;
181         mtx_lock(&ng_eiface_mtx);
182         KASSERT(index < ng_eiface_units_len,
183             ("%s: unit=%d len=%d", __func__, unit, ng_eiface_units_len));
184         KASSERT((ng_eiface_units[index] & (1 << bit)) == 0,
185             ("%s: unit=%d is free", __func__, unit));
186         ng_eiface_units[index] |= (1 << bit);
187         /*
188          * XXX We could think about reducing the size of ng_eiface_units[]
189          * XXX here if the last portion is all ones
190          * XXX At least free it if no more units.
191          * Needed if we are to eventually be able to unload.
192          */
193         ng_units_in_use--;
194         if (ng_units_in_use == 0) { /* XXX make SMP safe */
195                 FREE(ng_eiface_units, M_NETGRAPH);
196                 ng_eiface_units_len = 0;
197                 ng_eiface_units = NULL;
198         }
199         mtx_unlock(&ng_eiface_mtx);
200 }
201
202 /************************************************************************
203                         INTERFACE STUFF
204  ************************************************************************/
205
206 /*
207  * Process an ioctl for the virtual interface
208  */
209 static int
210 ng_eiface_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
211 {
212         struct ifreq   *const ifr = (struct ifreq *)data;
213         int             s, error = 0;
214
215 #ifdef DEBUG
216         ng_eiface_print_ioctl(ifp, command, data);
217 #endif
218         s = splimp();
219         switch (command)
220         {
221         /* These two are mostly handled at a higher layer */
222         case SIOCSIFADDR:
223                 error = ether_ioctl(ifp, command, data);
224                 break;
225         case SIOCGIFADDR:
226                 break;
227
228         /* Set flags */
229         case SIOCSIFFLAGS:
230                 /*
231                  * If the interface is marked up and stopped, then
232                  * start it. If it is marked down and running,
233                  * then stop it.
234                  */
235                 if (ifr->ifr_flags & IFF_UP) {
236                         if (!(ifp->if_flags & IFF_RUNNING)) {
237                                 ifp->if_flags &= ~(IFF_OACTIVE);
238                                 ifp->if_flags |= IFF_RUNNING;
239                         }
240                 } else {
241                         if (ifp->if_flags & IFF_RUNNING)
242                                 ifp->if_flags
243                                         &= ~(IFF_RUNNING | IFF_OACTIVE);
244                 }
245                 break;
246
247         /* Set the interface MTU */
248         case SIOCSIFMTU:
249                 if (ifr->ifr_mtu > NG_EIFACE_MTU_MAX
250                 || ifr->ifr_mtu < NG_EIFACE_MTU_MIN)
251                         error = EINVAL;
252                 else
253                         ifp->if_mtu = ifr->ifr_mtu;
254                 break;
255
256         /* Stuff that's not supported */
257         case SIOCADDMULTI:
258         case SIOCDELMULTI:
259                 error = 0;
260                 break;
261         case SIOCSIFPHYS:
262                 error = EOPNOTSUPP;
263                 break;
264
265         default:
266                 error = EINVAL;
267                 break;
268         }
269         (void)splx(s);
270         return (error);
271 }
272
273 static void
274 ng_eiface_init(void *xsc)
275 {
276         priv_p          sc = xsc;
277         struct ifnet   *ifp = sc->ifp;
278         int             s;
279
280         s = splimp();
281
282         ifp->if_flags |= IFF_RUNNING;
283         ifp->if_flags &= ~IFF_OACTIVE;
284
285         splx(s);
286 }
287
288 /*
289  * We simply relay the packet to the ether hook, if it is connected.
290  * We have been throughthe netgraph locking an are guaranteed to 
291  * be the only code running in this node at this time.
292  */
293 static void
294 ng_eiface_start2(node_p node, hook_p hook, void *arg1, int arg2)
295 {
296         struct ifnet *ifp = arg1;
297         const priv_p priv = (priv_p) ifp->if_softc;
298         int             len, error = 0;
299         struct mbuf    *m;
300
301         /* Check interface flags */
302         if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING))
303                 return;
304
305         /* Don't do anything if output is active */
306         if (ifp->if_flags & IFF_OACTIVE)
307                 return;
308
309         ifp->if_flags |= IFF_OACTIVE;
310
311         /*
312          * Grab a packet to transmit.
313          */
314         IF_DEQUEUE(&ifp->if_snd, m);
315
316         /* If there's nothing to send, return. */
317         if (m == NULL) {
318                 ifp->if_flags &= ~IFF_OACTIVE;
319                 return;
320         }
321
322         /* Berkeley packet filter
323          * Pass packet to bpf if there is a listener.
324          * XXX is this safe? locking?
325          */
326         BPF_MTAP(ifp, m);
327
328         /* Copy length before the mbuf gets invalidated */
329         len = m->m_pkthdr.len;
330
331         /*
332          * Send packet; if hook is not connected, mbuf will get
333          * freed.
334          */
335         NG_SEND_DATA_ONLY(error, priv->ether, m);
336
337         /* Update stats */
338         if (error == 0) {
339                 ifp->if_obytes += len;
340                 ifp->if_opackets++;
341         }
342         ifp->if_flags &= ~IFF_OACTIVE;
343         return;
344 }
345
346 /*
347  * This routine is called to deliver a packet out the interface.
348  * We simply queue the netgraph version to be called when netgraph locking
349  * allows it to happen.
350  * Until we know what the rest of the networking code is doing for
351  * locking, we don't know how we will interact with it.
352  * Take comfort from the fact that the ifnet struct is part of our
353  * private info and can't go away while we are queued.
354  * [Though we don't know it is still there now....]
355  * it is possible we don't gain anything from this because
356  * we would like to get the mbuf and queue it as data
357  * somehow, but we can't and if we did would we solve anything?
358  */
359 static void
360 ng_eiface_start(struct ifnet *ifp)
361 {
362         
363         const priv_p priv = (priv_p) ifp->if_softc;
364
365         ng_send_fn(priv->node, NULL, &ng_eiface_start2, ifp, 0);
366 }
367
368 #ifdef DEBUG
369 /*
370  * Display an ioctl to the virtual interface
371  */
372
373 static void
374 ng_eiface_print_ioctl(struct ifnet *ifp, int command, caddr_t data){
375         char            *str;
376
377         switch (command & IOC_DIRMASK)
378         {
379         case IOC_VOID:
380                 str = "IO";
381                 break;
382         case IOC_OUT:
383                 str = "IOR";
384                 break;
385         case IOC_IN:
386                 str = "IOW";
387                 break;
388         case IOC_INOUT:
389                 str = "IORW";
390                 break;
391         default:
392                 str = "IO??";
393         }
394         log(LOG_DEBUG, "%s: %s('%c', %d, char[%d])\n",
395                         ifp->if_xname,
396                         str,
397                         IOCGROUP(command),
398                         command & 0xff,
399                         IOCPARM_LEN(command));
400 }
401 #endif  /* DEBUG */
402
403 /************************************************************************
404                         NETGRAPH NODE STUFF
405  ************************************************************************/
406
407 /*
408  * Constructor for a node
409  */
410 static int
411 ng_eiface_constructor(node_p node)
412 {
413         struct ifnet   *ifp;
414         priv_p          priv;
415         int             error = 0;
416
417         /* Allocate node and interface private structures */
418         MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK);
419         if (priv == NULL) {
420                 return (ENOMEM);
421         }
422         bzero(priv, sizeof(*priv));
423
424         ifp = &(priv->arpcom.ac_if);
425
426         /* Link them together */
427         ifp->if_softc = priv;
428         priv->ifp = ifp;
429
430         /* Get an interface unit number */
431         if ((error = ng_eiface_get_unit(&priv->unit)) != 0) {
432                 FREE(priv, M_NETGRAPH);
433                 return (error);
434         }
435
436         /* Link together node and private info */
437         NG_NODE_SET_PRIVATE(node, priv);
438         priv->node = node;
439
440         /* Initialize interface structure */
441         if_initname(ifp, NG_EIFACE_EIFACE_NAME, priv->unit);
442         ifp->if_init = ng_eiface_init;
443         ifp->if_output = ether_output;
444         ifp->if_start = ng_eiface_start;
445         ifp->if_ioctl = ng_eiface_ioctl;
446         ifp->if_watchdog = NULL;
447         ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
448         ifp->if_flags = (IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST);
449
450         /*
451          * Give this node name * bzero(ifname, sizeof(ifname));
452          * sprintf(ifname, "if%s", ifp->if_xname); (void)
453          * ng_name_node(node, ifname);
454          */
455
456         /* Attach the interface */
457         ether_ifattach(ifp, priv->arpcom.ac_enaddr);
458
459         /* Done */
460         return (0);
461 }
462
463 /*
464  * Give our ok for a hook to be added
465  */
466 static int
467 ng_eiface_newhook(node_p node, hook_p hook, const char *name)
468 {
469         priv_p          priv = NG_NODE_PRIVATE(node);
470
471         if (strcmp(name, NG_EIFACE_HOOK_ETHER))
472                 return (EPFNOSUPPORT);
473         if (priv->ether != NULL)
474                 return (EISCONN);
475         priv->ether = hook;
476         NG_HOOK_SET_PRIVATE(hook, &priv->ether);
477
478         return (0);
479 }
480
481 /*
482  * Receive a control message
483  */
484 static int
485 ng_eiface_rcvmsg(node_p node, item_p item, hook_p lasthook)
486 {
487         priv_p          priv = NG_NODE_PRIVATE(node);
488         struct ifnet   *const ifp = priv->ifp;
489         struct ng_mesg *resp = NULL;
490         int             error = 0;
491         struct ng_mesg *msg;
492
493         NGI_GET_MSG(item, msg);
494         switch          (msg->header.typecookie) {
495         case NGM_EIFACE_COOKIE:
496                 switch (msg->header.cmd) {
497                 case NGM_EIFACE_SET:
498                 {
499                         struct ether_addr *eaddr;
500                         struct ifaddr *ifa;
501                         struct sockaddr_dl *sdl;
502
503                         if (msg->header.arglen != sizeof(struct ether_addr)){
504                                 error = EINVAL;
505                                 break;
506                         }
507                         eaddr = (struct ether_addr *)(msg->data);
508                         bcopy(eaddr, priv->arpcom.ac_enaddr, ETHER_ADDR_LEN);
509
510                         /* And put it in the ifaddr list */
511                         TAILQ_FOREACH(ifa, &(ifp->if_addrhead), ifa_link) {
512                                 sdl = (struct sockaddr_dl *)ifa->ifa_addr;
513                                 if (sdl->sdl_type == IFT_ETHER) {
514                                         bcopy((IFP2AC(ifp))->ac_enaddr,
515                                                 LLADDR(sdl), ifp->if_addrlen);
516                                         break;
517                                 }
518                         }
519                         break;
520                 }
521
522                 case NGM_EIFACE_GET_IFNAME:
523                 {
524                         struct ng_eiface_ifname *arg;
525
526                         NG_MKRESPONSE(resp, msg, sizeof(*arg), M_NOWAIT);
527                         if (resp == NULL) {
528                                 error = ENOMEM;
529                                 break;
530                         }
531                         arg = (struct ng_eiface_ifname *)resp->data;
532                         strlcpy(arg->ngif_name, ifp->if_xname,
533                             sizeof(arg->ngif_name));
534                         break;
535                 }
536
537                 case NGM_EIFACE_GET_IFADDRS:
538                 {
539                         struct ifaddr  *ifa;
540                         caddr_t         ptr;
541                         int             buflen;
542
543 #define SA_SIZE(s)      ((s)->sa_len<sizeof(*(s))? sizeof(*(s)):(s)->sa_len)
544
545                         /* Determine size of response and allocate it */
546                         buflen = 0;
547                         TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link)
548                         buflen += SA_SIZE(ifa->ifa_addr);
549                         NG_MKRESPONSE(resp, msg, buflen, M_NOWAIT);
550                         if (resp == NULL) {
551                                 error = ENOMEM;
552                                 break;
553                         }
554                         /* Add addresses */
555                         ptr = resp->data;
556                         TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
557                                 const int       len = SA_SIZE(ifa->ifa_addr);
558
559                                 if (buflen < len) {
560                                         log(LOG_ERR, "%s: len changed?\n",
561                                         ifp->if_xname);
562                                         break;
563                                 }
564                                 bcopy(ifa->ifa_addr, ptr, len);
565                                 ptr += len;
566                                 buflen -= len;
567                         }
568                         break;
569 #undef SA_SIZE
570                 }
571
572                 default:
573                         error = EINVAL;
574                         break;
575                 } /* end of inner switch() */
576                 break;
577         default:
578                 error = EINVAL;
579                 break;
580         }
581         NG_RESPOND_MSG(error, node, item, resp);
582         NG_FREE_MSG(msg);
583         return (error);
584 }
585
586 /*
587  * Recive data from a hook. Pass the packet to the ether_input routine.
588  */
589 static int
590 ng_eiface_rcvdata(hook_p hook, item_p item)
591 {
592         priv_p          priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
593         struct ifnet   *const ifp = priv->ifp;
594         struct mbuf *m;
595
596         NGI_GET_M(item, m);
597         NG_FREE_ITEM(item);
598
599         if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
600                 NG_FREE_M(m);
601                 return (ENETDOWN);
602         }
603
604         /* Note receiving interface */
605         m->m_pkthdr.rcvif = ifp;
606
607         /* Update interface stats */
608         ifp->if_ipackets++;
609
610         (*ifp->if_input)(ifp, m);
611
612         /* Done */
613         return (0);
614 }
615
616 /*
617  * the node.
618  */
619 static int
620 ng_eiface_rmnode(node_p node)
621 {
622         priv_p          priv = NG_NODE_PRIVATE(node);
623         struct ifnet   *const ifp = priv->ifp;
624
625         ether_ifdetach(ifp);
626         ng_eiface_free_unit(priv->unit);
627         FREE(priv, M_NETGRAPH);
628         NG_NODE_SET_PRIVATE(node, NULL);
629         NG_NODE_UNREF(node);
630         return (0);
631 }
632
633
634 /*
635  * This is called once we've already connected a new hook to the other node.
636  * It gives us a chance to balk at the last minute.
637  */
638 static int
639 ng_eiface_connect(hook_p hook)
640 {
641         /* be really amiable and just say "YUP that's OK by me! " */
642         return (0);
643 }
644
645 /*
646  * Hook disconnection
647  */
648 static int
649 ng_eiface_disconnect(hook_p hook)
650 {
651         priv_p          priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
652
653         priv->ether = NULL;
654         return (0);
655 }