Initial import from FreeBSD RELENG_4:
[games.git] / sys / netgraph / eiface / ng_eiface.c
1 /*
2  * ng_eiface.c
3  *
4  * Copyright (c) 1999-2000, Vitaly V Belekhov
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice unmodified, this list of conditions, and the following
12  *    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  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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  *      $Id: ng_eiface.c,v 1.14 2000/03/15 12:28:44 vitaly Exp $
30  * $FreeBSD: src/sys/netgraph/ng_eiface.c,v 1.4.2.5 2002/12/17 21:47:48 julian Exp $
31  */
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/errno.h>
36 #include <sys/kernel.h>
37 #include <sys/malloc.h>
38 #include <sys/mbuf.h>
39 #include <sys/errno.h>
40 #include <sys/sockio.h>
41 #include <sys/socket.h>
42 #include <sys/syslog.h>
43
44 #include <net/if.h>
45 #include <net/if_types.h>
46 #include <net/netisr.h>
47
48
49 #include <netgraph/ng_message.h>
50 #include <netgraph/netgraph.h>
51 #include <netgraph/ng_parse.h>
52 #include <netgraph/ng_eiface.h>
53
54 #include <net/bpf.h>
55 #include <net/ethernet.h>
56 #include <net/if_arp.h>
57
58 static const struct ng_parse_struct_field ng_eiface_par_fields[]
59         = NG_EIFACE_PAR_FIELDS;
60
61 static const struct ng_parse_type ng_eiface_par_type = {
62         &ng_parse_struct_type,
63         &ng_eiface_par_fields
64 };
65
66 static const struct ng_cmdlist ng_eiface_cmdlist[] = {
67         {
68           NGM_EIFACE_COOKIE,
69           NGM_EIFACE_SET,
70           "set",
71           &ng_eiface_par_type,
72           NULL
73         },
74         { 0 }
75 };
76
77 /* Node private data */
78 struct ng_eiface_private {
79         struct arpcom   arpcom;         /* per-interface network data */
80         struct  ifnet *ifp;             /* This interface */
81         node_p  node;                   /* Our netgraph node */
82         hook_p  ether;                  /* Hook for ethernet stream */
83         struct  private *next;          /* When hung on the free list */
84 };
85 typedef struct ng_eiface_private *priv_p;
86
87 /* Interface methods */
88 static void     ng_eiface_init(void *xsc);
89 static void     ng_eiface_start(struct ifnet *ifp);
90 static int      ng_eiface_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data);
91 #ifdef DEBUG
92 static void     ng_eiface_print_ioctl(struct ifnet *ifp, int cmd, caddr_t data);
93 #endif
94
95 /* Netgraph methods */
96 static ng_constructor_t ng_eiface_constructor;
97 static ng_rcvmsg_t      ng_eiface_rcvmsg;
98 static ng_shutdown_t    ng_eiface_rmnode;
99 static ng_newhook_t     ng_eiface_newhook;
100 static ng_rcvdata_t     ng_eiface_rcvdata;
101 static ng_connect_t     ng_eiface_connect;
102 static ng_disconnect_t  ng_eiface_disconnect;
103
104 /* Node type descriptor */
105 static struct ng_type typestruct = {
106         NG_VERSION,
107         NG_EIFACE_NODE_TYPE,
108         NULL,
109         ng_eiface_constructor,
110         ng_eiface_rcvmsg,
111         ng_eiface_rmnode,
112         ng_eiface_newhook,
113         NULL,
114         ng_eiface_connect,
115         ng_eiface_rcvdata,
116         ng_eiface_rcvdata,
117         ng_eiface_disconnect,
118         ng_eiface_cmdlist
119 };
120 NETGRAPH_INIT(eiface, &typestruct);
121
122 static char ng_eiface_ifname[] = NG_EIFACE_EIFACE_NAME;
123 static int ng_eiface_next_unit;
124
125 /************************************************************************
126                         INTERFACE STUFF
127  ************************************************************************/
128
129 /*
130  * Process an ioctl for the virtual interface
131  */
132 static int
133 ng_eiface_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
134 {
135         struct ifreq *const ifr = (struct ifreq *) data;
136         int s, error = 0;
137
138 #ifdef DEBUG
139         ng_eiface_print_ioctl(ifp, command, data);
140 #endif
141         s = splimp();
142         switch (command) {
143
144         /* These two are mostly handled at a higher layer */
145         case SIOCSIFADDR:
146                 error = ether_ioctl(ifp, command, data);
147                 break;
148         case SIOCGIFADDR:
149                 break;
150
151         /* Set flags */
152         case SIOCSIFFLAGS:
153                 /*
154                  * If the interface is marked up and stopped, then start it.
155                  * If it is marked down and running, then stop it.
156                  */
157                 if (ifr->ifr_flags & IFF_UP) {
158                         if (!(ifp->if_flags & IFF_RUNNING)) {
159                                 ifp->if_flags &= ~(IFF_OACTIVE);
160                                 ifp->if_flags |= IFF_RUNNING;
161                         }
162                 } else {
163                         if (ifp->if_flags & IFF_RUNNING)
164                                 ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
165                 }
166                 break;
167
168         /* Set the interface MTU */
169         case SIOCSIFMTU:
170                 if (ifr->ifr_mtu > NG_EIFACE_MTU_MAX
171                     || ifr->ifr_mtu < NG_EIFACE_MTU_MIN)
172                         error = EINVAL;
173                 else
174                         ifp->if_mtu = ifr->ifr_mtu;
175                 break;
176
177         /* Stuff that's not supported */
178         case SIOCADDMULTI:
179         case SIOCDELMULTI:
180                 error = 0;
181                 break;
182         case SIOCSIFPHYS:
183                 error = EOPNOTSUPP;
184                 break;
185
186         default:
187                 error = EINVAL;
188                 break;
189         }
190         (void) splx(s);
191         return (error);
192 }
193
194 static void
195 ng_eiface_init(void *xsc)
196 {
197         priv_p sc = xsc;
198         struct ifnet *ifp = sc->ifp;
199         int s;
200
201         s = splimp();
202
203         ifp->if_flags |= IFF_RUNNING;
204         ifp->if_flags &= ~IFF_OACTIVE;
205
206         splx(s);
207
208 }
209
210 /*
211  * This routine is called to deliver a packet out the interface.
212  * We simply relay the packet to
213  * the ether hook, if it is connected.
214  */
215
216 static void
217 ng_eiface_start(struct ifnet *ifp)
218 {
219         const priv_p priv = (priv_p) ifp->if_softc;
220         meta_p meta = NULL;
221         int len, error = 0;
222         struct mbuf *m;
223
224         /* Check interface flags */
225         if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
226                 return;
227
228         /* Don't do anything if output is active */
229         if( ifp->if_flags & IFF_OACTIVE )
230                 return;
231
232         ifp->if_flags |= IFF_OACTIVE;
233
234         /*
235          * Grab a packet to transmit.
236          */
237         IF_DEQUEUE(&ifp->if_snd, m);
238
239         /* If there's nothing to send, return. */
240         if(m == NULL)
241         {
242                 ifp->if_flags &= ~IFF_OACTIVE;
243                 return;
244         }
245
246         /* Berkeley packet filter */
247         /*
248          * Pass packet to bpf if there is a listener.
249          */
250         if (ifp->if_bpf)
251           bpf_mtap(ifp, m);
252
253         /* Copy length before the mbuf gets invalidated */
254         len = m->m_pkthdr.len;
255
256         /* Send packet; if hook is not connected, mbuf will get freed. */
257         NG_SEND_DATA(error, priv->ether, m, meta);
258
259         /* Update stats */
260         if (error == 0) {
261                 ifp->if_obytes += len;
262                 ifp->if_opackets++;
263         }
264
265         ifp->if_flags &= ~IFF_OACTIVE;
266
267         return;
268 }
269
270 #ifdef DEBUG
271 /*
272  * Display an ioctl to the virtual interface
273  */
274
275 static void
276 ng_eiface_print_ioctl(struct ifnet *ifp, int command, caddr_t data)
277 {
278         char   *str;
279
280         switch (command & IOC_DIRMASK) {
281         case IOC_VOID:
282                 str = "IO";
283                 break;
284         case IOC_OUT:
285                 str = "IOR";
286                 break;
287         case IOC_IN:
288                 str = "IOW";
289                 break;
290         case IOC_INOUT:
291                 str = "IORW";
292                 break;
293         default:
294                 str = "IO??";
295         }
296         log(LOG_DEBUG, "%s%d: %s('%c', %d, char[%d])\n",
297                ifp->if_name, ifp->if_unit,
298                str,
299                IOCGROUP(command),
300                command & 0xff,
301                IOCPARM_LEN(command));
302 }
303 #endif /* DEBUG */
304
305 /************************************************************************
306                         NETGRAPH NODE STUFF
307  ************************************************************************/
308
309 /*
310  * Constructor for a node
311  */
312 static int
313 ng_eiface_constructor(node_p *nodep)
314 {
315         struct ifnet *ifp;
316         node_p node;
317         priv_p priv;
318         int error = 0;
319
320         /* Allocate node and interface private structures */
321         MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK);
322         if (priv == NULL)
323                 return (ENOMEM);
324         bzero(priv, sizeof(*priv));
325
326         ifp = &(priv->arpcom.ac_if);
327
328         /* Link them together */
329         ifp->if_softc = priv;
330         priv->ifp = ifp;
331
332         /* Call generic node constructor */
333         if ((error = ng_make_node_common(&typestruct, nodep))) {
334                 FREE(priv, M_NETGRAPH);
335                 return (error);
336         }
337         node = *nodep;
338
339         /* Link together node and private info */
340         node->private = priv;
341         priv->node = node;
342
343         /* Initialize interface structure */
344         ifp->if_name = ng_eiface_ifname;
345         ifp->if_unit = ng_eiface_next_unit++;
346         ifp->if_init = ng_eiface_init;
347         ifp->if_output = ether_output;
348         ifp->if_start = ng_eiface_start;
349         ifp->if_ioctl = ng_eiface_ioctl;
350         ifp->if_watchdog = NULL;
351         ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
352         ifp->if_flags = (IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST);
353
354         TAILQ_INIT(&ifp->if_addrhead);
355
356         /* Give this node name *
357         bzero(ifname, sizeof(ifname));
358         sprintf(ifname, "if%s%d", ifp->if_name, ifp->if_unit);
359         (void) ng_name_node(node, ifname);
360         */
361
362         /* Attach the interface */
363         ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
364
365         /* Done */
366         return (0);
367 }
368
369 /*
370  * Give our ok for a hook to be added
371  */
372 static int
373 ng_eiface_newhook(node_p node, hook_p hook, const char *name)
374 {
375         priv_p priv = node->private;
376
377         if (strcmp(name, NG_EIFACE_HOOK_ETHER))
378                 return (EPFNOSUPPORT);
379         if (priv->ether != NULL)
380                 return (EISCONN);
381         priv->ether = hook;
382         hook->private = &priv->ether;
383
384         return (0);
385 }
386
387 /*
388  * Receive a control message
389  */
390 static int
391 ng_eiface_rcvmsg(node_p node, struct ng_mesg *msg,
392                 const char *retaddr, struct ng_mesg **rptr)
393 {
394         const priv_p priv = node->private;
395         struct ifnet *const ifp = priv->ifp;
396         struct ng_mesg *resp = NULL;
397         int error = 0;
398
399         switch (msg->header.typecookie) {
400         case NGM_EIFACE_COOKIE:
401                 switch (msg->header.cmd) {
402
403                 case NGM_EIFACE_SET:
404                     {
405                       struct ng_eiface_par *eaddr;
406
407                       if (msg->header.arglen != sizeof(struct ng_eiface_par)) 
408                         {
409                           error = EINVAL;
410                           break;
411                         }
412                       eaddr = (struct ng_eiface_par *)(msg->data);
413
414                       priv->arpcom.ac_enaddr[0] = eaddr->oct0;
415                       priv->arpcom.ac_enaddr[1] = eaddr->oct1;
416                       priv->arpcom.ac_enaddr[2] = eaddr->oct2;
417                       priv->arpcom.ac_enaddr[3] = eaddr->oct3;
418                       priv->arpcom.ac_enaddr[4] = eaddr->oct4;
419                       priv->arpcom.ac_enaddr[5] = eaddr->oct5;
420
421                       break;
422                     }
423
424                 case NGM_EIFACE_GET_IFNAME:
425                     {
426                         struct ng_eiface_ifname *arg;
427
428                         NG_MKRESPONSE(resp, msg, sizeof(*arg), M_NOWAIT);
429                         if (resp == NULL) {
430                                 error = ENOMEM;
431                                 break;
432                         }
433                         arg = (struct ng_eiface_ifname *) resp->data;
434                         sprintf(arg->ngif_name,
435                             "%s%d", ifp->if_name, ifp->if_unit);
436                         break;
437                     }
438
439                 case NGM_EIFACE_GET_IFADDRS:
440                     {
441                         struct ifaddr *ifa;
442                         caddr_t ptr;
443                         int buflen;
444
445 #define SA_SIZE(s)      ((s)->sa_len<sizeof(*(s))? sizeof(*(s)):(s)->sa_len)
446
447                         /* Determine size of response and allocate it */
448                         buflen = 0;
449                         TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link)
450                                 buflen += SA_SIZE(ifa->ifa_addr);
451                         NG_MKRESPONSE(resp, msg, buflen, M_NOWAIT);
452                         if (resp == NULL) {
453                                 error = ENOMEM;
454                                 break;
455                         }
456
457                         /* Add addresses */
458                         ptr = resp->data;
459                         TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
460                                 const int len = SA_SIZE(ifa->ifa_addr);
461
462                                 if (buflen < len) {
463                                         log(LOG_ERR, "%s%d: len changed?\n",
464                                             ifp->if_name, ifp->if_unit);
465                                         break;
466                                 }
467                                 bcopy(ifa->ifa_addr, ptr, len);
468                                 ptr += len;
469                                 buflen -= len;
470                         }
471                         break;
472 #undef SA_SIZE
473                     }
474
475                 default:
476                         error = EINVAL;
477                         break;
478                 }
479                 break;
480         default:
481                 error = EINVAL;
482                 break;
483         }
484         if (rptr)
485                 *rptr = resp;
486         else if (resp)
487                 FREE(resp, M_NETGRAPH);
488         FREE(msg, M_NETGRAPH);
489         return (error);
490 }
491
492 /*
493  * Recive data from a hook. Pass the packet to the ether_input routine.
494  */
495 static int
496 ng_eiface_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
497 {
498         const priv_p priv = hook->node->private;
499         struct ifnet *const ifp = priv->ifp;
500         int s, error = 0;
501         struct ether_header *eh;
502         u_short ether_type;
503
504         /* Meta-data is end its life here... */
505         NG_FREE_META(meta);
506
507         if (m == NULL)
508           {
509             printf("ng_eiface: mbuf is null.\n");
510             return (EINVAL);
511           }
512
513         if ( !(ifp->if_flags & IFF_UP) ) {
514                 return (ENETDOWN);
515         }
516
517         /* Note receiving interface */
518         m->m_pkthdr.rcvif = ifp;
519
520         /* Update interface stats */
521         ifp->if_ipackets++;
522
523         /* Berkeley packet filter */
524         if (ifp->if_bpf)
525           bpf_mtap(ifp, m);
526
527         eh = mtod( m, struct ether_header * );
528         ether_type = ntohs(eh->ether_type);
529
530         s = splimp();
531             m->m_pkthdr.len -= sizeof(*eh);
532             m->m_len -= sizeof(*eh);
533             if ( m->m_len )
534               {
535                 m->m_data += sizeof(*eh);
536               }
537             else
538               {
539                 if ( ether_type == ETHERTYPE_ARP )
540                 {
541                 m->m_len = m->m_next->m_len;
542                 m->m_data = m->m_next->m_data;
543                 }
544               }
545         splx(s);
546
547         ether_input(ifp, eh, m);
548
549         /* Done */
550         return (error);
551 }
552
553 /*
554  * Because the BSD networking code doesn't support the removal of
555  * networking interfaces, iface nodes (once created) are persistent.
556  * So this method breaks all connections and marks the interface
557  * down, but does not remove the node.
558  */
559 static int
560 ng_eiface_rmnode(node_p node)
561 {
562         const priv_p priv = node->private;
563         struct ifnet *const ifp = priv->ifp;
564
565         ng_cutlinks(node);
566         node->flags &= ~NG_INVALID;
567         ifp->if_flags &= ~(IFF_UP | IFF_RUNNING | IFF_OACTIVE);
568         return (0);
569 }
570
571
572 /*
573  * This is called once we've already connected a new hook to the other node.
574  * It gives us a chance to balk at the last minute.
575  */
576 static int
577 ng_eiface_connect(hook_p hook)
578 {
579         /* be really amiable and just say "YUP that's OK by me! " */
580         return (0);
581 }
582
583 /*
584  * Hook disconnection
585  */
586 static int
587 ng_eiface_disconnect(hook_p hook)
588 {
589         const priv_p priv = hook->node->private;
590
591         priv->ether = NULL;
592         return (0);
593 }