ppp: Raise WARNS to 5.
[dragonfly.git] / usr.sbin / ppp / netgraph.c
1 /*-
2  * Copyright (c) 2000 Brian Somers <brian@Awfulhak.org>
3  * 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  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/usr.sbin/ppp/netgraph.c,v 1.4.2.1 2002/09/01 02:12:29 brian Exp $
27  * $DragonFly: src/usr.sbin/ppp/netgraph.c,v 1.3 2007/06/04 00:40:32 swildner Exp $
28  */
29
30 #include <sys/param.h>
31 #include <sys/socket.h>
32 #include <sys/un.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35 #include <netdb.h>
36 #include <netgraph.h>
37 #include <net/ethernet.h>
38 #include <netinet/in_systm.h>
39 #include <netinet/ip.h>
40 #include <netgraph/ng_ether.h>
41 #include <netgraph/ng_message.h>
42 #include <netgraph/ng_socket.h>
43
44 #include <errno.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <sysexits.h>
49 #include <sys/fcntl.h>
50 #include <sys/uio.h>
51 #include <termios.h>
52 #include <sys/time.h>
53 #include <unistd.h>
54
55 #include "layer.h"
56 #include "defs.h"
57 #include "mbuf.h"
58 #include "log.h"
59 #include "timer.h"
60 #include "lqr.h"
61 #include "hdlc.h"
62 #include "throughput.h"
63 #include "fsm.h"
64 #include "lcp.h"
65 #include "ccp.h"
66 #include "link.h"
67 #include "async.h"
68 #include "descriptor.h"
69 #include "physical.h"
70 #include "main.h"
71 #include "mp.h"
72 #include "chat.h"
73 #include "auth.h"
74 #include "chap.h"
75 #include "cbcp.h"
76 #include "datalink.h"
77 #include "slcompress.h"
78 #include "iplist.h"
79 #include "ncpaddr.h"
80 #include "ipcp.h"
81 #include "ipv6cp.h"
82 #include "ncp.h"
83 #include "filter.h"
84 #ifndef NORADIUS
85 #include "radius.h"
86 #endif
87 #include "bundle.h"
88 #include "id.h"
89 #include "netgraph.h"
90
91
92 struct ngdevice {
93   struct device dev;                    /* What struct physical knows about */
94   int cs;                               /* Control socket */
95   char hook[NG_HOOKSIZ];                /* Our socket node hook */
96 };
97
98 #define device2ng(d)    ((d)->type == NG_DEVICE ? (struct ngdevice *)d : NULL)
99 #define NG_MSGBUFSZ     4096
100 #define NETGRAPH_PREFIX "netgraph:"
101
102 unsigned
103 ng_DeviceSize(void)
104 {
105   return sizeof(struct ngdevice);
106 }
107
108 static int
109 ng_MessageOut(struct ngdevice *dev, const char *data)
110 {
111   char path[NG_PATHSIZ];
112   char *fmt;
113   size_t len;
114   int pos, dpos;
115
116   /*
117    * We expect a node path, one or more spaces, a command, one or more
118    * spaces and an ascii netgraph structure.
119    */
120   data += strspn(data, " \t");
121   len = strcspn(data, " \t");
122   if (len >= sizeof path) {
123     log_Printf(LogWARN, "%s: %.*s: Node path too long\n",
124                  dev->dev.name, len, data);
125     return 0;
126   }
127   memcpy(path, data, len);
128   path[len] = '\0';
129   data += len;
130
131   data += strspn(data, " \t");
132   len = strcspn(data, " \t");
133   for (pos = len; pos >= 0; pos--)
134     if (data[pos] == '%')
135       len++;
136   if ((fmt = alloca(len + 4)) == NULL) {
137     log_Printf(LogWARN, "%s: alloca(%d) failure... %s\n",
138                dev->dev.name, len + 4, strerror(errno));
139     return 0;
140   }
141
142   /*
143    * This is probably a waste of time, but we really don't want to end
144    * up stuffing unexpected % escapes into the kernel....
145    */
146   for (pos = dpos = 0; pos < (int)len;) {
147     if (data[dpos] == '%')
148       fmt[pos++] = '%';
149     fmt[pos++] = data[dpos++];
150   }
151   strcpy(fmt + pos, " %s");
152   data += dpos;
153
154   data += strspn(data, " \t");
155   if (NgSendAsciiMsg(dev->cs, path, fmt, data) < 0) {
156     log_Printf(LogDEBUG, "%s: NgSendAsciiMsg (to %s): \"%s\", \"%s\": %s\n",
157                dev->dev.name, path, fmt, data, strerror(errno));
158     return 0;
159   }
160
161   return 1;
162 }
163
164 /*
165  * Get a netgraph message
166  */
167 static ssize_t
168 ng_MessageIn(struct physical *p, char *buf, size_t sz)
169 {
170   char msgbuf[sizeof(struct ng_mesg) * 2 + NG_MSGBUFSZ];
171   struct ngdevice *dev = device2ng(p->handler);
172   struct ng_mesg *rep = (struct ng_mesg *)msgbuf;
173   char path[NG_PATHSIZ];
174   size_t len;
175
176 #ifdef BROKEN_SELECT
177   struct timeval t;
178   fd_set *r;
179   int ret;
180
181   if (dev->cs < 0)
182     return 0;
183
184   if ((r = mkfdset()) == NULL) {
185     log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n");
186     return -1;
187   }
188   zerofdset(r);
189   FD_SET(dev->cs, r);
190   t.tv_sec = t.tv_usec = 0;
191   ret = select(dev->cs + 1, r, NULL, NULL, &t);
192   free(r);
193
194   if (ret <= 0)
195     return 0;
196 #endif
197
198   if (NgRecvAsciiMsg(dev->cs, rep, sizeof msgbuf, path)) {
199     log_Printf(LogWARN, "%s: NgRecvAsciiMsg: %s\n",
200                dev->dev.name, strerror(errno));
201     return -1;
202   }
203
204   /* XXX: Should we check rep->header.version ? */
205
206   if (sz == 0)
207     log_Printf(LogWARN, "%s: Unexpected message: %s\n", dev->dev.name,
208                rep->header.cmdstr);
209   else {
210     log_Printf(LogDEBUG, "%s: Received message: %s\n", dev->dev.name,
211                rep->header.cmdstr);
212     len = strlen(rep->header.cmdstr);
213     if (sz > len)
214       sz = len;
215     memcpy(buf, rep->header.cmdstr, sz);
216   }
217
218   return sz;
219 }
220
221 static ssize_t
222 ng_Write(struct physical *p, const void *v, size_t n)
223 {
224   struct ngdevice *dev = device2ng(p->handler);
225
226   switch (p->dl->state) {
227     case DATALINK_DIAL:
228     case DATALINK_LOGIN:
229       return ng_MessageOut(dev, v) ? (ssize_t)n : -1;
230   }
231   return NgSendData(p->fd, dev->hook, v, n) == -1 ? -1 : (ssize_t)n;
232 }
233
234 static ssize_t
235 ng_Read(struct physical *p, void *v, size_t n)
236 {
237   char hook[NG_HOOKSIZ];
238
239 log_Printf(LogDEBUG, "ng_Read\n");
240   switch (p->dl->state) {
241     case DATALINK_DIAL:
242     case DATALINK_LOGIN:
243       return ng_MessageIn(p, v, n);
244   }
245
246   return NgRecvData(p->fd, v, n, hook);
247 }
248
249 static int
250 ng_RemoveFromSet(struct physical *p, fd_set *r, fd_set *w, fd_set *e)
251 {
252   struct ngdevice *dev = device2ng(p->handler);
253   int result;
254
255   if (r && dev->cs >= 0 && FD_ISSET(dev->cs, r)) {
256     FD_CLR(dev->cs, r);
257     log_Printf(LogTIMER, "%s: fdunset(ctrl) %d\n", p->link.name, dev->cs);
258     result = 1;
259   } else
260     result = 0;
261
262   /* Careful... physical_RemoveFromSet() called us ! */
263
264   p->handler->removefromset = NULL;
265   result += physical_RemoveFromSet(p, r, w, e);
266   p->handler->removefromset = ng_RemoveFromSet;
267
268   return result;
269 }
270
271 static void
272 ng_Free(struct physical *p)
273 {
274   struct ngdevice *dev = device2ng(p->handler);
275
276   physical_SetDescriptor(p);
277   if (dev->cs != -1)
278     close(dev->cs);
279   free(dev);
280 }
281
282 static void
283 ng_device2iov(struct device *d, struct iovec *iov, int *niov,
284               int maxiov __unused, int *auxfd, int *nauxfd)
285 {
286   struct ngdevice *dev = device2ng(d);
287   int sz = physical_MaxDeviceSize();
288
289   iov[*niov].iov_base = realloc(d, sz);
290   if (iov[*niov].iov_base == NULL) {
291     log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz);
292     AbortProgram(EX_OSERR);
293   }
294   iov[*niov].iov_len = sz;
295   (*niov)++;
296
297   *auxfd = dev->cs;
298   (*nauxfd)++;
299 }
300
301 static const struct device basengdevice = {
302   NG_DEVICE,
303   "netgraph",
304   0,
305   { CD_REQUIRED, DEF_NGCDDELAY },
306   NULL,
307   ng_RemoveFromSet,
308   NULL,
309   NULL,
310   NULL,
311   NULL,
312   NULL,
313   ng_Free,
314   ng_Read,
315   ng_Write,
316   ng_device2iov,
317   NULL,
318   NULL,
319   NULL
320 };
321
322 struct device *
323 ng_iov2device(int type, struct physical *p, struct iovec *iov, int *niov,
324               int maxiov __unused, int *auxfd, int *nauxfd)
325 {
326   if (type == NG_DEVICE) {
327     struct ngdevice *dev = (struct ngdevice *)iov[(*niov)++].iov_base;
328
329     dev = realloc(dev, sizeof *dev);    /* Reduce to the correct size */
330     if (dev == NULL) {
331       log_Printf(LogALERT, "Failed to allocate memory: %d\n",
332                  (int)(sizeof *dev));
333       AbortProgram(EX_OSERR);
334     }
335
336     if (*nauxfd) {
337       dev->cs = *auxfd;
338       (*nauxfd)--;
339     } else
340       dev->cs = -1;
341
342     /* Refresh function pointers etc */
343     memcpy(&dev->dev, &basengdevice, sizeof dev->dev);
344
345     /* XXX: Are netgraph always synchronous ? */
346     physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF);
347     return &dev->dev;
348   }
349
350   return NULL;
351 }
352
353 static int
354 ng_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
355 {
356   struct physical *p = descriptor2physical(d);
357   struct ngdevice *dev = device2ng(p->handler);
358   int result;
359
360   switch (p->dl->state) {
361     case DATALINK_DIAL:
362     case DATALINK_LOGIN:
363       if (r) {
364         FD_SET(dev->cs, r);
365         log_Printf(LogTIMER, "%s(ctrl): fdset(r) %d\n", p->link.name, dev->cs);
366         result = 1;
367       } else
368         result = 0;
369       break;
370
371     default:
372       result = physical_doUpdateSet(d, r, w, e, n, 0);
373       break;
374   }
375
376   return result;
377 }
378
379 static int
380 ng_IsSet(struct fdescriptor *d, const fd_set *fdset)
381 {
382   struct physical *p = descriptor2physical(d);
383   struct ngdevice *dev = device2ng(p->handler);
384   int result;
385
386   result = dev->cs >= 0 && FD_ISSET(dev->cs, fdset);
387   result += physical_IsSet(d, fdset);
388
389   return result;
390 }
391
392 static void
393 ng_DescriptorRead(struct fdescriptor *d, struct bundle *bundle,
394                   const fd_set *fdset)
395 {
396   struct physical *p = descriptor2physical(d);
397   struct ngdevice *dev = device2ng(p->handler);
398
399   if (dev->cs >= 0 && FD_ISSET(dev->cs, fdset))
400     ng_MessageIn(p, NULL, 0);
401
402   if (physical_IsSet(d, fdset))
403     physical_DescriptorRead(d, bundle, fdset);
404 }
405
406 static struct device *
407 ng_Abandon(struct ngdevice *dev, struct physical *p)
408 {
409   /* Abandon our node construction */
410   close(dev->cs);
411   close(p->fd);
412   p->fd = -2;   /* Nobody else need try.. */
413   free(dev);
414
415   return NULL;
416 }
417
418
419 /*
420  * Populate the ``word'' (of size ``sz'') named ``what'' from ``from''
421  * ending with any character from ``sep''.  Point ``endp'' at the next
422  * word.
423  */
424
425 #define GETSEGMENT(what, from, sep, endp) \
426         getsegment(#what, (what), sizeof(what), from, sep, endp)
427
428 static int
429 getsegment(const char *what, char *word, size_t sz, const char *from,
430            const char *sep, const char **endp)
431 {
432   size_t len;
433
434   if ((len = strcspn(from, sep)) == 0) {
435     log_Printf(LogWARN, "%s name should not be empty !\n", what);
436     return 0;
437   }
438
439   if (len >= sz) {
440     log_Printf(LogWARN, "%s name too long, max %d !\n", what, sz - 1);
441     return 0;
442   }
443
444   strncpy(word, from, len);
445   word[len] = '\0';
446
447   *endp = from + len;
448   *endp += strspn(*endp, sep);
449
450   return 1;
451 }
452
453 struct device *
454 ng_Create(struct physical *p)
455 {
456   struct sockaddr_ng ngsock;
457   u_char rbuf[2048];
458   struct sockaddr *sock = (struct sockaddr *)&ngsock;
459   const struct hooklist *hlist;
460   const struct nodeinfo *ninfo;
461   const struct linkinfo *nlink;
462   struct ngdevice *dev;
463   struct ng_mesg *resp;
464   struct ngm_mkpeer mkp;
465   struct ngm_connect ngc;
466   const char *devp, *endp;
467   char lasthook[NG_HOOKSIZ];
468   char hook[NG_HOOKSIZ];
469   char nodetype[NG_TYPESIZ + NG_NODESIZ];
470   char modname[NG_TYPESIZ + 3];
471   char path[NG_PATHSIZ];
472   char *nodename;
473   int len, sz, done;
474   unsigned f;
475
476   dev = NULL;
477   if (p->fd < 0 && !strncasecmp(p->name.full, NETGRAPH_PREFIX,
478                                 sizeof NETGRAPH_PREFIX - 1)) {
479     p->fd--;                            /* We own the device - change fd */
480
481     if ((dev = malloc(sizeof *dev)) == NULL)
482       return NULL;
483
484     loadmodules(LOAD_VERBOSLY, "netgraph", "ng_socket", NULL);
485
486     /* Create a socket node */
487     if (ID0NgMkSockNode(NULL, &dev->cs, &p->fd) == -1) {
488       log_Printf(LogWARN, "Cannot create netgraph socket node: %s\n",
489                  strerror(errno));
490       free(dev);
491       p->fd = -2;
492       return NULL;
493     }
494
495     devp = p->name.full + sizeof NETGRAPH_PREFIX - 1;
496     *lasthook = *path = '\0';
497     log_Printf(LogDEBUG, "%s: Opening netgraph device \"%s\"\n",
498                p->link.name, devp);
499     done = 0;
500
501     while (*devp != '\0' && !done) {
502       if (*devp != '[') {
503         if (*lasthook == '\0') {
504           log_Printf(LogWARN, "%s: Netgraph devices must start with"
505                      " [nodetype:nodename]\n", p->link.name);
506           return ng_Abandon(dev, p);
507         }
508
509         /* Get the hook name of the new node */
510         if (!GETSEGMENT(hook, devp, ".[", &endp))
511           return ng_Abandon(dev, p);
512         log_Printf(LogDEBUG, "%s: Got hook \"%s\"\n", p->link.name, hook);
513         devp = endp;
514         if (*devp == '\0') {
515           log_Printf(LogWARN, "%s: Netgraph device must not end with a second"
516                      " hook\n", p->link.name);
517           return ng_Abandon(dev, p);
518         }
519         if (devp[-1] != '[') {
520           log_Printf(LogWARN, "%s: Expected a [nodetype:nodename] at device"
521                      " pos %d\n", p->link.name, devp - p->link.name - 1);
522           return ng_Abandon(dev, p);
523         }
524       } else {
525         /* Use lasthook as the hook name */
526         strcpy(hook, lasthook);
527         devp++;
528       }
529
530       /* We've got ``lasthook'' and ``hook'', get the node type */
531       if (!GETSEGMENT(nodetype, devp, "]", &endp))
532         return ng_Abandon(dev, p);
533       log_Printf(LogDEBUG, "%s: Got node \"%s\"\n", p->link.name, nodetype);
534
535       if ((nodename = strchr(nodetype, ':')) != NULL) {
536         *nodename++ = '\0';
537         if (*nodename == '\0' && *nodetype == '\0') {
538           log_Printf(LogWARN, "%s: Empty [nodetype:nodename] at device"
539                      " pos %d\n", p->link.name, devp - p->link.name - 1);
540           return ng_Abandon(dev, p);
541         }
542       }
543
544       /* Ignore optional colons after nodes */
545       devp = *endp == ':' ? endp + 1 : endp;
546       if (*devp == '.')
547         devp++;
548
549       if (*lasthook == '\0') {
550         /* This is the first node in the chain */
551         if (nodename == NULL || *nodename == '\0') {
552           log_Printf(LogWARN, "%s: %s: No initial device nodename\n",
553                      p->link.name, devp);
554           return ng_Abandon(dev, p);
555         }
556
557         if (*nodetype != '\0') {
558           /* Attempt to load the module */
559           snprintf(modname, sizeof modname, "ng_%s", nodetype);
560           log_Printf(LogDEBUG, "%s: Attempting to load %s.ko\n",
561                      p->link.name, modname);
562           loadmodules(LOAD_QUIETLY, modname, NULL);
563         }
564
565         snprintf(path, sizeof path, "%s:", nodename);
566         /* XXX: If we have a node type, ensure it's correct */
567       } else {
568         /*
569          * Ask for a list of hooks attached to the previous node.  If we
570          * find the one we're interested in, and if it's connected to a
571          * node of the right type using the correct hook, use that.
572          * If we find the hook connected to something else, fail.
573          * If we find no match, mkpeer the new node.
574          */
575         if (*nodetype == '\0') {
576           log_Printf(LogWARN, "%s: Nodetype missing at device offset %d\n",
577                      p->link.name,
578                      devp - p->name.full + sizeof NETGRAPH_PREFIX - 1);
579           return ng_Abandon(dev, p);
580         }
581
582         /* Get a list of node hooks */
583         if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
584                       NULL, 0) < 0) {
585           log_Printf(LogWARN, "%s: %s Cannot send a LISTHOOOKS message: %s\n",
586                      p->link.name, path, strerror(errno));
587           return ng_Abandon(dev, p);
588         }
589
590         /* Get our list back */
591         resp = (struct ng_mesg *)rbuf;
592         if (NgRecvMsg(dev->cs, resp, sizeof rbuf, NULL) <= 0) {
593           log_Printf(LogWARN, "%s: Cannot get netgraph response: %s\n",
594                      p->link.name, strerror(errno));
595           return ng_Abandon(dev, p);
596         }
597
598         hlist = (const struct hooklist *)resp->data;
599         ninfo = &hlist->nodeinfo;
600
601         log_Printf(LogDEBUG, "List of netgraph node ``%s'' (id %x) hooks:\n",
602                    path, ninfo->id);
603
604         /* look for a hook already attached.  */
605         for (f = 0; f < ninfo->hooks; f++) {
606           nlink = &hlist->link[f];
607
608           log_Printf(LogDEBUG, "  Found %s -> %s (type %s)\n", nlink->ourhook,
609                      nlink->peerhook, nlink->nodeinfo.type);
610
611           if (!strcmp(nlink->ourhook, lasthook)) {
612             if (strcmp(nlink->peerhook, hook) ||
613                 strcmp(nlink->nodeinfo.type, nodetype)) {
614               log_Printf(LogWARN, "%s: hook %s:%s is already in use\n",
615                          p->link.name, nlink->ourhook, path);
616               return ng_Abandon(dev, p);
617             }
618             /* The node is already hooked up nicely.... reuse it */
619             break;
620           }
621         }
622
623         if (f == ninfo->hooks) {
624           /* Attempt to load the module */
625           snprintf(modname, sizeof modname, "ng_%s", nodetype);
626           log_Printf(LogDEBUG, "%s: Attempting to load %s.ko\n",
627                      p->link.name, modname);
628           loadmodules(LOAD_QUIETLY, modname, NULL);
629
630           /* Create (mkpeer) the new node */
631
632           snprintf(mkp.type, sizeof mkp.type, "%s", nodetype);
633           snprintf(mkp.ourhook, sizeof mkp.ourhook, "%s", lasthook);
634           snprintf(mkp.peerhook, sizeof mkp.peerhook, "%s", hook);
635
636           log_Printf(LogDEBUG, "%s: Doing MKPEER %s%s -> %s (type %s)\n",
637                      p->link.name, path, mkp.ourhook, mkp.peerhook, nodetype);
638
639           if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE,
640                         NGM_MKPEER, &mkp, sizeof mkp) < 0) {
641             log_Printf(LogWARN, "%s Cannot create %s netgraph node: %s\n",
642                        path, nodetype, strerror(errno));
643             return ng_Abandon(dev, p);
644           }
645         }
646         len = strlen(path);
647         snprintf(path + len, sizeof path - len, "%s%s",
648                  path[len - 1] == ':' ? "" : ".", lasthook);
649       }
650
651       /* Get a list of node hooks */
652       if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
653                     NULL, 0) < 0) {
654         log_Printf(LogWARN, "%s: %s Cannot send a LISTHOOOKS message: %s\n",
655                    p->link.name, path, strerror(errno));
656         return ng_Abandon(dev, p);
657       }
658
659       /* Get our list back */
660       resp = (struct ng_mesg *)rbuf;
661       if (NgRecvMsg(dev->cs, resp, sizeof rbuf, NULL) <= 0) {
662         log_Printf(LogWARN, "%s: Cannot get netgraph response: %s\n",
663                    p->link.name, strerror(errno));
664         return ng_Abandon(dev, p);
665       }
666
667       hlist = (const struct hooklist *)resp->data;
668       ninfo = &hlist->nodeinfo;
669
670       if (*lasthook != '\0' && nodename != NULL && *nodename != '\0' &&
671           strcmp(ninfo->name, nodename) &&
672           NgNameNode(dev->cs, path, "%s", nodename) < 0) {
673         log_Printf(LogWARN, "%s: %s: Cannot name netgraph node: %s\n",
674                    p->link.name, path, strerror(errno));
675         return ng_Abandon(dev, p);
676       }
677
678       if (!GETSEGMENT(lasthook, devp, " \t.[", &endp))
679         return ng_Abandon(dev, p);
680       log_Printf(LogDEBUG, "%s: Got hook \"%s\"\n", p->link.name, lasthook);
681
682       len = strlen(lasthook);
683       done = strchr(" \t", devp[len]) ? 1 : 0;
684       devp = endp;
685
686       if (*devp != '\0') {
687         if (devp[-1] == '[')
688           devp--;
689       } /* else should moan about devp[-1] being '[' ? */
690     }
691
692     snprintf(dev->hook, sizeof dev->hook, "%s", lasthook);
693
694     /* Connect the node to our socket node */
695     snprintf(ngc.path, sizeof ngc.path, "%s", path);
696     snprintf(ngc.ourhook, sizeof ngc.ourhook, "%s", dev->hook);
697     memcpy(ngc.peerhook, ngc.ourhook, sizeof ngc.peerhook);
698
699     log_Printf(LogDEBUG, "Connecting netgraph socket .:%s -> %s.%s\n",
700                ngc.ourhook, ngc.path, ngc.peerhook);
701     if (NgSendMsg(dev->cs, ".:", NGM_GENERIC_COOKIE,
702                   NGM_CONNECT, &ngc, sizeof ngc) < 0) {
703       log_Printf(LogWARN, "Cannot connect %s and socket netgraph "
704                  "nodes: %s\n", path, strerror(errno));
705       return ng_Abandon(dev, p);
706     }
707
708     /* Hook things up so that we monitor dev->cs */
709     p->desc.UpdateSet = ng_UpdateSet;
710     p->desc.IsSet = ng_IsSet;
711     p->desc.Read = ng_DescriptorRead;
712
713     memcpy(&dev->dev, &basengdevice, sizeof dev->dev);
714
715   } else {
716     /* See if we're a netgraph socket */
717
718     sz = sizeof ngsock;
719     if (getsockname(p->fd, sock, &sz) != -1 && sock->sa_family == AF_NETGRAPH) {
720       /*
721        * It's a netgraph node... We can't determine hook names etc, so we
722        * stay pretty impartial....
723        */
724       log_Printf(LogPHASE, "%s: Link is a netgraph node\n", p->link.name);
725
726       if ((dev = malloc(sizeof *dev)) == NULL) {
727         log_Printf(LogWARN, "%s: Cannot allocate an ether device: %s\n",
728                    p->link.name, strerror(errno));
729         return NULL;
730       }
731
732       memcpy(&dev->dev, &basengdevice, sizeof dev->dev);
733       dev->cs = -1;
734       *dev->hook = '\0';
735     }
736   }
737
738   if (dev) {
739     physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF);
740     return &dev->dev;
741   }
742
743   return NULL;
744 }