netgraph.c revision 134789
1187767Sluigi/*- 2204591Sluigi * Copyright (c) 2000 Brian Somers <brian@Awfulhak.org> 3187767Sluigi * All rights reserved. 4187767Sluigi * 5187767Sluigi * Redistribution and use in source and binary forms, with or without 6187767Sluigi * modification, are permitted provided that the following conditions 7187767Sluigi * are met: 8187767Sluigi * 1. Redistributions of source code must retain the above copyright 9187767Sluigi * notice, this list of conditions and the following disclaimer. 10187767Sluigi * 2. Redistributions in binary form must reproduce the above copyright 11187767Sluigi * notice, this list of conditions and the following disclaimer in the 12187767Sluigi * documentation and/or other materials provided with the distribution. 13187767Sluigi * 14187767Sluigi * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15187767Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16187767Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17187767Sluigi * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18187767Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19187767Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20187767Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21187767Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22187767Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23187767Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24187767Sluigi * SUCH DAMAGE. 25187767Sluigi * 26187767Sluigi * $FreeBSD: head/usr.sbin/ppp/netgraph.c 134789 2004-09-05 01:46:52Z brian $ 27187767Sluigi */ 28187767Sluigi 29187767Sluigi#include <sys/param.h> 30187767Sluigi#include <sys/socket.h> 31187767Sluigi#include <sys/un.h> 32187767Sluigi#include <netinet/in.h> 33187767Sluigi#include <arpa/inet.h> 34187767Sluigi#include <netdb.h> 35187767Sluigi#include <netgraph.h> 36187767Sluigi#include <net/ethernet.h> 37187767Sluigi#include <netinet/in_systm.h> 38187767Sluigi#include <netinet/ip.h> 39187767Sluigi#include <netgraph/ng_ether.h> 40187767Sluigi#include <netgraph/ng_message.h> 41187767Sluigi#include <netgraph/ng_socket.h> 42187767Sluigi 43187767Sluigi#include <errno.h> 44187767Sluigi#include <stdio.h> 45187767Sluigi#include <stdlib.h> 46187767Sluigi#include <string.h> 47187767Sluigi#include <sysexits.h> 48187767Sluigi#include <sys/fcntl.h> 49187767Sluigi#include <sys/uio.h> 50187767Sluigi#include <termios.h> 51187767Sluigi#include <sys/time.h> 52187767Sluigi#include <unistd.h> 53187767Sluigi 54187767Sluigi#include "layer.h" 55187767Sluigi#include "defs.h" 56187767Sluigi#include "mbuf.h" 57190633Spiso#include "log.h" 58187767Sluigi#include "timer.h" 59187767Sluigi#include "lqr.h" 60187767Sluigi#include "hdlc.h" 61187767Sluigi#include "throughput.h" 62187767Sluigi#include "fsm.h" 63187767Sluigi#include "lcp.h" 64187767Sluigi#include "ccp.h" 65187767Sluigi#include "link.h" 66187767Sluigi#include "async.h" 67187767Sluigi#include "descriptor.h" 68187767Sluigi#include "physical.h" 69187767Sluigi#include "main.h" 70187767Sluigi#include "mp.h" 71187767Sluigi#include "chat.h" 72187767Sluigi#include "auth.h" 73187767Sluigi#include "chap.h" 74187767Sluigi#include "cbcp.h" 75187767Sluigi#include "datalink.h" 76187767Sluigi#include "slcompress.h" 77187767Sluigi#include "iplist.h" 78187767Sluigi#include "ncpaddr.h" 79187767Sluigi#include "ipcp.h" 80187767Sluigi#include "ipv6cp.h" 81187767Sluigi#include "ncp.h" 82187767Sluigi#include "filter.h" 83187767Sluigi#ifndef NORADIUS 84187767Sluigi#include "radius.h" 85187767Sluigi#endif 86204591Sluigi#include "bundle.h" 87204591Sluigi#include "id.h" 88204591Sluigi#include "netgraph.h" 89204591Sluigi 90227489Seadler 91204591Sluigistruct ngdevice { 92204591Sluigi struct device dev; /* What struct physical knows about */ 93187767Sluigi int cs; /* Control socket */ 94187767Sluigi char hook[NG_HOOKSIZ]; /* Our socket node hook */ 95187767Sluigi}; 96187767Sluigi 97204591Sluigi#define device2ng(d) ((d)->type == NG_DEVICE ? (struct ngdevice *)d : NULL) 98187767Sluigi#define NG_MSGBUFSZ 4096 99187767Sluigi#define NETGRAPH_PREFIX "netgraph:" 100187767Sluigi 101189396Sluigiunsigned 102204591Sluiging_DeviceSize(void) 103204591Sluigi{ 104187767Sluigi return sizeof(struct ngdevice); 105187767Sluigi} 106187767Sluigi 107187767Sluigistatic int 108187767Sluiging_MessageOut(struct ngdevice *dev, const char *data) 109187767Sluigi{ 110187767Sluigi char path[NG_PATHSIZ]; 111204591Sluigi char *fmt; 112204591Sluigi size_t len; 113204591Sluigi int pos, dpos; 114187767Sluigi 115187767Sluigi /* 116187767Sluigi * We expect a node path, one or more spaces, a command, one or more 117187767Sluigi * spaces and an ascii netgraph structure. 118187767Sluigi */ 119187767Sluigi data += strspn(data, " \t"); 120187767Sluigi len = strcspn(data, " \t"); 121187767Sluigi if (len >= sizeof path) { 122187767Sluigi log_Printf(LogWARN, "%s: %.*s: Node path too long\n", 123187767Sluigi dev->dev.name, len, data); 124187767Sluigi return 0; 125229403Sed } 126187767Sluigi memcpy(path, data, len); 127229403Sed path[len] = '\0'; 128187767Sluigi data += len; 129187767Sluigi 130187767Sluigi data += strspn(data, " \t"); 131187767Sluigi len = strcspn(data, " \t"); 132187767Sluigi for (pos = len; pos >= 0; pos--) 133187767Sluigi if (data[pos] == '%') 134187767Sluigi len++; 135187767Sluigi if ((fmt = alloca(len + 4)) == NULL) { 136187767Sluigi log_Printf(LogWARN, "%s: alloca(%d) failure... %s\n", 137187767Sluigi dev->dev.name, len + 4, strerror(errno)); 138187767Sluigi return 0; 139187767Sluigi } 140187767Sluigi 141187767Sluigi /* 142187767Sluigi * This is probably a waste of time, but we really don't want to end 143187767Sluigi * up stuffing unexpected % escapes into the kernel.... 144229403Sed */ 145187767Sluigi for (pos = dpos = 0; pos < (int)len;) { 146187767Sluigi if (data[dpos] == '%') 147187767Sluigi fmt[pos++] = '%'; 148204591Sluigi fmt[pos++] = data[dpos++]; 149204591Sluigi } 150204591Sluigi strcpy(fmt + pos, " %s"); 151204591Sluigi data += dpos; 152204591Sluigi 153187767Sluigi data += strspn(data, " \t"); 154204591Sluigi if (NgSendAsciiMsg(dev->cs, path, fmt, data) < 0) { 155204591Sluigi log_Printf(LogDEBUG, "%s: NgSendAsciiMsg (to %s): \"%s\", \"%s\": %s\n", 156204591Sluigi dev->dev.name, path, fmt, data, strerror(errno)); 157187767Sluigi return 0; 158187767Sluigi } 159204591Sluigi 160204591Sluigi return 1; 161187767Sluigi} 162187767Sluigi 163204591Sluigi/* 164204591Sluigi * Get a netgraph message 165229403Sed */ 166187767Sluigistatic ssize_t 167187767Sluiging_MessageIn(struct physical *p, char *buf, size_t sz) 168204591Sluigi{ 169204591Sluigi char msgbuf[sizeof(struct ng_mesg) * 2 + NG_MSGBUFSZ]; 170204591Sluigi struct ngdevice *dev = device2ng(p->handler); 171204591Sluigi struct ng_mesg *rep = (struct ng_mesg *)msgbuf; 172187767Sluigi char path[NG_PATHSIZ]; 173187767Sluigi size_t len; 174187767Sluigi 175204591Sluigi#ifdef BROKEN_SELECT 176187767Sluigi struct timeval t; 177187767Sluigi fd_set *r; 178187767Sluigi int ret; 179187767Sluigi 180204591Sluigi if (dev->cs < 0) 181187767Sluigi return 0; 182204591Sluigi 183204591Sluigi if ((r = mkfdset()) == NULL) { 184204591Sluigi log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n"); 185204591Sluigi return -1; 186204591Sluigi } 187204591Sluigi zerofdset(r); 188204591Sluigi FD_SET(dev->cs, r); 189204591Sluigi t.tv_sec = t.tv_usec = 0; 190204591Sluigi ret = select(dev->cs + 1, r, NULL, NULL, &t); 191204591Sluigi free(r); 192204591Sluigi 193204591Sluigi if (ret <= 0) 194204591Sluigi return 0; 195204591Sluigi#endif 196204591Sluigi 197204591Sluigi if (NgRecvAsciiMsg(dev->cs, rep, sizeof msgbuf, path)) { 198204591Sluigi log_Printf(LogWARN, "%s: NgRecvAsciiMsg: %s\n", 199204591Sluigi dev->dev.name, strerror(errno)); 200204591Sluigi return -1; 201187767Sluigi } 202187767Sluigi 203187767Sluigi /* XXX: Should we check rep->header.version ? */ 204187767Sluigi 205187767Sluigi if (sz == 0) 206187767Sluigi log_Printf(LogWARN, "%s: Unexpected message: %s\n", dev->dev.name, 207187767Sluigi rep->header.cmdstr); 208204591Sluigi else { 209187767Sluigi log_Printf(LogDEBUG, "%s: Received message: %s\n", dev->dev.name, 210204591Sluigi rep->header.cmdstr); 211204591Sluigi len = strlen(rep->header.cmdstr); 212187767Sluigi if (sz > len) 213204591Sluigi sz = len; 214187767Sluigi memcpy(buf, rep->header.cmdstr, sz); 215187767Sluigi } 216187767Sluigi 217187767Sluigi return sz; 218187767Sluigi} 219187767Sluigi 220187767Sluigistatic ssize_t 221204591Sluiging_Write(struct physical *p, const void *v, size_t n) 222204591Sluigi{ 223204591Sluigi struct ngdevice *dev = device2ng(p->handler); 224204591Sluigi 225204591Sluigi switch (p->dl->state) { 226204591Sluigi case DATALINK_DIAL: 227204591Sluigi case DATALINK_LOGIN: 228187767Sluigi return ng_MessageOut(dev, v) ? (ssize_t)n : -1; 229187767Sluigi } 230187767Sluigi return NgSendData(p->fd, dev->hook, v, n) == -1 ? -1 : (ssize_t)n; 231187767Sluigi} 232204591Sluigi 233204591Sluigistatic ssize_t 234204591Sluiging_Read(struct physical *p, void *v, size_t n) 235204591Sluigi{ 236204591Sluigi char hook[NG_HOOKSIZ]; 237204591Sluigi 238204591Sluigilog_Printf(LogDEBUG, "ng_Read\n"); 239204591Sluigi switch (p->dl->state) { 240204591Sluigi case DATALINK_DIAL: 241204591Sluigi case DATALINK_LOGIN: 242204591Sluigi return ng_MessageIn(p, v, n); 243229403Sed } 244204591Sluigi 245204591Sluigi return NgRecvData(p->fd, v, n, hook); 246204591Sluigi} 247204591Sluigi 248204591Sluigistatic int 249204591Sluiging_RemoveFromSet(struct physical *p, fd_set *r, fd_set *w, fd_set *e) 250204591Sluigi{ 251204591Sluigi struct ngdevice *dev = device2ng(p->handler); 252204591Sluigi int result; 253204591Sluigi 254204591Sluigi if (r && dev->cs >= 0 && FD_ISSET(dev->cs, r)) { 255204591Sluigi FD_CLR(dev->cs, r); 256204591Sluigi log_Printf(LogTIMER, "%s: fdunset(ctrl) %d\n", p->link.name, dev->cs); 257204591Sluigi result = 1; 258204591Sluigi } else 259204591Sluigi result = 0; 260204591Sluigi 261187767Sluigi /* Careful... physical_RemoveFromSet() called us ! */ 262187767Sluigi 263187767Sluigi p->handler->removefromset = NULL; 264187767Sluigi result += physical_RemoveFromSet(p, r, w, e); 265223661Sae p->handler->removefromset = ng_RemoveFromSet; 266187767Sluigi 267187767Sluigi return result; 268187767Sluigi} 269187767Sluigi 270187767Sluigistatic void 271187767Sluiging_Free(struct physical *p) 272187767Sluigi{ 273187767Sluigi struct ngdevice *dev = device2ng(p->handler); 274187767Sluigi 275187767Sluigi physical_SetDescriptor(p); 276187767Sluigi if (dev->cs != -1) 277187767Sluigi close(dev->cs); 278187767Sluigi free(dev); 279187767Sluigi} 280187767Sluigi 281187767Sluigistatic void 282187767Sluiging_device2iov(struct device *d, struct iovec *iov, int *niov, 283187767Sluigi int maxiov __unused, int *auxfd, int *nauxfd) 284187767Sluigi{ 285187767Sluigi struct ngdevice *dev = device2ng(d); 286187767Sluigi int sz = physical_MaxDeviceSize(); 287187767Sluigi 288187767Sluigi iov[*niov].iov_base = realloc(d, sz); 289187767Sluigi if (iov[*niov].iov_base == NULL) { 290187767Sluigi log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz); 291187767Sluigi AbortProgram(EX_OSERR); 292187767Sluigi } 293204591Sluigi iov[*niov].iov_len = sz; 294187767Sluigi (*niov)++; 295187767Sluigi 296187767Sluigi *auxfd = dev->cs; 297187767Sluigi (*nauxfd)++; 298187767Sluigi} 299187767Sluigi 300187767Sluigistatic const struct device basengdevice = { 301187767Sluigi NG_DEVICE, 302187767Sluigi "netgraph", 303187767Sluigi 0, 304187767Sluigi { CD_REQUIRED, DEF_NGCDDELAY }, 305187767Sluigi NULL, 306187767Sluigi ng_RemoveFromSet, 307187767Sluigi NULL, 308187767Sluigi NULL, 309223661Sae NULL, 310223661Sae NULL, 311223661Sae NULL, 312228871Seadler ng_Free, 313223661Sae ng_Read, 314187767Sluigi ng_Write, 315187767Sluigi ng_device2iov, 316187767Sluigi NULL, 317187767Sluigi NULL, 318187767Sluigi NULL 319187767Sluigi}; 320187767Sluigi 321187767Sluigistruct device * 322187767Sluiging_iov2device(int type, struct physical *p, struct iovec *iov, int *niov, 323187767Sluigi int maxiov __unused, int *auxfd, int *nauxfd) 324187767Sluigi{ 325187767Sluigi if (type == NG_DEVICE) { 326187767Sluigi struct ngdevice *dev = (struct ngdevice *)iov[(*niov)++].iov_base; 327187767Sluigi 328187767Sluigi dev = realloc(dev, sizeof *dev); /* Reduce to the correct size */ 329187767Sluigi if (dev == NULL) { 330187767Sluigi log_Printf(LogALERT, "Failed to allocate memory: %d\n", 331187767Sluigi (int)(sizeof *dev)); 332187767Sluigi AbortProgram(EX_OSERR); 333187767Sluigi } 334187767Sluigi 335187767Sluigi if (*nauxfd) { 336187767Sluigi dev->cs = *auxfd; 337187767Sluigi (*nauxfd)--; 338187767Sluigi } else 339204591Sluigi dev->cs = -1; 340187767Sluigi 341187767Sluigi /* Refresh function pointers etc */ 342187767Sluigi memcpy(&dev->dev, &basengdevice, sizeof dev->dev); 343187767Sluigi 344187767Sluigi /* XXX: Are netgraph always synchronous ? */ 345187767Sluigi physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF); 346187767Sluigi return &dev->dev; 347187767Sluigi } 348187767Sluigi 349187767Sluigi return NULL; 350187767Sluigi} 351187767Sluigi 352187767Sluigistatic int 353187767Sluiging_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n) 354187767Sluigi{ 355187767Sluigi struct physical *p = descriptor2physical(d); 356187767Sluigi struct ngdevice *dev = device2ng(p->handler); 357187767Sluigi int result; 358187767Sluigi 359187767Sluigi switch (p->dl->state) { 360187767Sluigi case DATALINK_DIAL: 361187767Sluigi case DATALINK_LOGIN: 362187767Sluigi if (r) { 363187767Sluigi FD_SET(dev->cs, r); 364222745Sae log_Printf(LogTIMER, "%s(ctrl): fdset(r) %d\n", p->link.name, dev->cs); 365187767Sluigi result = 1; 366220802Sglebius } else 367187767Sluigi result = 0; 368187767Sluigi break; 369187767Sluigi 370187767Sluigi default: 371204591Sluigi result = physical_doUpdateSet(d, r, w, e, n, 0); 372204591Sluigi break; 373204591Sluigi } 374204591Sluigi 375187767Sluigi return result; 376187767Sluigi} 377187767Sluigi 378187767Sluigistatic int 379187767Sluiging_IsSet(struct fdescriptor *d, const fd_set *fdset) 380187767Sluigi{ 381187767Sluigi struct physical *p = descriptor2physical(d); 382187767Sluigi struct ngdevice *dev = device2ng(p->handler); 383187767Sluigi int result; 384187767Sluigi 385187767Sluigi result = dev->cs >= 0 && FD_ISSET(dev->cs, fdset); 386187767Sluigi result += physical_IsSet(d, fdset); 387187767Sluigi 388187767Sluigi return result; 389187767Sluigi} 390187767Sluigi 391187767Sluigistatic void 392187767Sluiging_DescriptorRead(struct fdescriptor *d, struct bundle *bundle, 393187767Sluigi const fd_set *fdset) 394187767Sluigi{ 395187767Sluigi struct physical *p = descriptor2physical(d); 396187767Sluigi struct ngdevice *dev = device2ng(p->handler); 397187767Sluigi 398187767Sluigi if (dev->cs >= 0 && FD_ISSET(dev->cs, fdset)) 399187767Sluigi ng_MessageIn(p, NULL, 0); 400187767Sluigi 401187767Sluigi if (physical_IsSet(d, fdset)) 402187767Sluigi physical_DescriptorRead(d, bundle, fdset); 403187767Sluigi} 404187767Sluigi 405187767Sluigistatic struct device * 406204591Sluiging_Abandon(struct ngdevice *dev, struct physical *p) 407187767Sluigi{ 408187767Sluigi /* Abandon our node construction */ 409187767Sluigi close(dev->cs); 410187767Sluigi close(p->fd); 411187767Sluigi p->fd = -2; /* Nobody else need try.. */ 412187767Sluigi free(dev); 413187767Sluigi 414204591Sluigi return NULL; 415187767Sluigi} 416187767Sluigi 417187767Sluigi 418204591Sluigi/* 419187767Sluigi * Populate the ``word'' (of size ``sz'') named ``what'' from ``from'' 420204591Sluigi * ending with any character from ``sep''. Point ``endp'' at the next 421187767Sluigi * word. 422187767Sluigi */ 423187767Sluigi 424187767Sluigi#define GETSEGMENT(what, from, sep, endp) \ 425187767Sluigi getsegment(#what, (what), sizeof(what), from, sep, endp) 426187767Sluigi 427204591Sluigistatic int 428187767Sluigigetsegment(const char *what, char *word, size_t sz, const char *from, 429187767Sluigi const char *sep, const char **endp) 430187767Sluigi{ 431187767Sluigi size_t len; 432187767Sluigi 433187767Sluigi if ((len = strcspn(from, sep)) == 0) { 434187767Sluigi log_Printf(LogWARN, "%s name should not be empty !\n", what); 435220802Sglebius return 0; 436187767Sluigi } 437187767Sluigi 438187767Sluigi if (len >= sz) { 439187767Sluigi log_Printf(LogWARN, "%s name too long, max %d !\n", what, sz - 1); 440187767Sluigi return 0; 441187767Sluigi } 442187767Sluigi 443187767Sluigi strncpy(word, from, len); 444204591Sluigi word[len] = '\0'; 445187767Sluigi 446187767Sluigi *endp = from + len; 447187767Sluigi *endp += strspn(*endp, sep); 448187767Sluigi 449187767Sluigi return 1; 450187767Sluigi} 451187767Sluigi 452187767Sluigistruct device * 453222744Saeng_Create(struct physical *p) 454187767Sluigi{ 455187767Sluigi struct sockaddr_ng ngsock; 456187767Sluigi u_char rbuf[2048]; 457187767Sluigi struct sockaddr *sock = (struct sockaddr *)&ngsock; 458187767Sluigi const struct hooklist *hlist; 459187767Sluigi const struct nodeinfo *ninfo; 460187767Sluigi const struct linkinfo *nlink; 461187767Sluigi struct ngdevice *dev; 462187767Sluigi struct ng_mesg *resp; 463187767Sluigi struct ngm_mkpeer mkp; 464187767Sluigi struct ngm_connect ngc; 465187767Sluigi const char *devp, *endp; 466187767Sluigi char lasthook[NG_HOOKSIZ]; 467187767Sluigi char hook[NG_HOOKSIZ]; 468187767Sluigi char nodetype[NG_TYPESIZ + NG_NODESIZ]; 469187767Sluigi char modname[NG_TYPESIZ + 3]; 470187767Sluigi char path[NG_PATHSIZ]; 471187767Sluigi char *nodename; 472187767Sluigi int len, sz, done; 473187767Sluigi unsigned f; 474187767Sluigi 475187767Sluigi dev = NULL; 476187767Sluigi if (p->fd < 0 && !strncasecmp(p->name.full, NETGRAPH_PREFIX, 477187767Sluigi sizeof NETGRAPH_PREFIX - 1)) { 478187767Sluigi p->fd--; /* We own the device - change fd */ 479187767Sluigi 480187767Sluigi if ((dev = malloc(sizeof *dev)) == NULL) 481187767Sluigi return NULL; 482187767Sluigi 483187767Sluigi loadmodules(LOAD_VERBOSLY, "netgraph", "ng_socket", NULL); 484187767Sluigi 485187767Sluigi /* Create a socket node */ 486187767Sluigi if (ID0NgMkSockNode(NULL, &dev->cs, &p->fd) == -1) { 487187767Sluigi log_Printf(LogWARN, "Cannot create netgraph socket node: %s\n", 488187767Sluigi strerror(errno)); 489187767Sluigi free(dev); 490187767Sluigi p->fd = -2; 491187767Sluigi return NULL; 492187767Sluigi } 493187767Sluigi 494187767Sluigi devp = p->name.full + sizeof NETGRAPH_PREFIX - 1; 495187767Sluigi *lasthook = *path = '\0'; 496187767Sluigi log_Printf(LogDEBUG, "%s: Opening netgraph device \"%s\"\n", 497187767Sluigi p->link.name, devp); 498187767Sluigi done = 0; 499187767Sluigi 500187767Sluigi while (*devp != '\0' && !done) { 501187767Sluigi if (*devp != '[') { 502187767Sluigi if (*lasthook == '\0') { 503187767Sluigi log_Printf(LogWARN, "%s: Netgraph devices must start with" 504187767Sluigi " [nodetype:nodename]\n", p->link.name); 505187767Sluigi return ng_Abandon(dev, p); 506187767Sluigi } 507187767Sluigi 508187767Sluigi /* Get the hook name of the new node */ 509187767Sluigi if (!GETSEGMENT(hook, devp, ".[", &endp)) 510187767Sluigi return ng_Abandon(dev, p); 511187767Sluigi log_Printf(LogDEBUG, "%s: Got hook \"%s\"\n", p->link.name, hook); 512187767Sluigi devp = endp; 513187767Sluigi if (*devp == '\0') { 514187767Sluigi log_Printf(LogWARN, "%s: Netgraph device must not end with a second" 515187767Sluigi " hook\n", p->link.name); 516187767Sluigi return ng_Abandon(dev, p); 517187767Sluigi } 518187767Sluigi if (devp[-1] != '[') { 519187767Sluigi log_Printf(LogWARN, "%s: Expected a [nodetype:nodename] at device" 520187767Sluigi " pos %d\n", p->link.name, devp - p->link.name - 1); 521187767Sluigi return ng_Abandon(dev, p); 522187767Sluigi } 523187767Sluigi } else { 524187767Sluigi /* Use lasthook as the hook name */ 525187767Sluigi strcpy(hook, lasthook); 526187767Sluigi devp++; 527187767Sluigi } 528187767Sluigi 529187767Sluigi /* We've got ``lasthook'' and ``hook'', get the node type */ 530187767Sluigi if (!GETSEGMENT(nodetype, devp, "]", &endp)) 531187767Sluigi return ng_Abandon(dev, p); 532187767Sluigi log_Printf(LogDEBUG, "%s: Got node \"%s\"\n", p->link.name, nodetype); 533187767Sluigi 534187767Sluigi if ((nodename = strchr(nodetype, ':')) != NULL) { 535187767Sluigi *nodename++ = '\0'; 536187767Sluigi if (*nodename == '\0' && *nodetype == '\0') { 537187767Sluigi log_Printf(LogWARN, "%s: Empty [nodetype:nodename] at device" 538187767Sluigi " pos %d\n", p->link.name, devp - p->link.name - 1); 539187767Sluigi return ng_Abandon(dev, p); 540187767Sluigi } 541187767Sluigi } 542187767Sluigi 543187767Sluigi /* Ignore optional colons after nodes */ 544187767Sluigi devp = *endp == ':' ? endp + 1 : endp; 545187767Sluigi if (*devp == '.') 546187767Sluigi devp++; 547187767Sluigi 548187767Sluigi if (*lasthook == '\0') { 549187767Sluigi /* This is the first node in the chain */ 550187767Sluigi if (nodename == NULL || *nodename == '\0') { 551187767Sluigi log_Printf(LogWARN, "%s: %s: No initial device nodename\n", 552187767Sluigi p->link.name, devp); 553187767Sluigi return ng_Abandon(dev, p); 554187767Sluigi } 555187767Sluigi 556187767Sluigi if (*nodetype != '\0') { 557187767Sluigi /* Attempt to load the module */ 558187767Sluigi snprintf(modname, sizeof modname, "ng_%s", nodetype); 559187767Sluigi log_Printf(LogDEBUG, "%s: Attempting to load %s.ko\n", 560187767Sluigi p->link.name, modname); 561222744Sae loadmodules(LOAD_QUIETLY, modname, NULL); 562206494Sluigi } 563187767Sluigi 564187767Sluigi snprintf(path, sizeof path, "%s:", nodename); 565187767Sluigi /* XXX: If we have a node type, ensure it's correct */ 566206494Sluigi } else { 567187767Sluigi /* 568187767Sluigi * Ask for a list of hooks attached to the previous node. If we 569187767Sluigi * find the one we're interested in, and if it's connected to a 570187767Sluigi * node of the right type using the correct hook, use that. 571187767Sluigi * If we find the hook connected to something else, fail. 572187767Sluigi * If we find no match, mkpeer the new node. 573187767Sluigi */ 574187767Sluigi if (*nodetype == '\0') { 575187767Sluigi log_Printf(LogWARN, "%s: Nodetype missing at device offset %d\n", 576187767Sluigi p->link.name, 577187767Sluigi devp - p->name.full + sizeof NETGRAPH_PREFIX - 1); 578187767Sluigi return ng_Abandon(dev, p); 579187767Sluigi } 580187767Sluigi 581187767Sluigi /* Get a list of node hooks */ 582187767Sluigi if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, 583187767Sluigi NULL, 0) < 0) { 584187767Sluigi log_Printf(LogWARN, "%s: %s Cannot send a LISTHOOOKS message: %s\n", 585187767Sluigi p->link.name, path, strerror(errno)); 586187767Sluigi return ng_Abandon(dev, p); 587187767Sluigi } 588187767Sluigi 589187767Sluigi /* Get our list back */ 590187767Sluigi resp = (struct ng_mesg *)rbuf; 591187767Sluigi if (NgRecvMsg(dev->cs, resp, sizeof rbuf, NULL) <= 0) { 592204716Sluigi log_Printf(LogWARN, "%s: Cannot get netgraph response: %s\n", 593204716Sluigi p->link.name, strerror(errno)); 594204716Sluigi return ng_Abandon(dev, p); 595204716Sluigi } 596204716Sluigi 597204716Sluigi hlist = (const struct hooklist *)resp->data; 598204716Sluigi ninfo = &hlist->nodeinfo; 599204716Sluigi 600220802Sglebius log_Printf(LogDEBUG, "List of netgraph node ``%s'' (id %x) hooks:\n", 601204716Sluigi path, ninfo->id); 602204716Sluigi 603204716Sluigi /* look for a hook already attached. */ 604204716Sluigi for (f = 0; f < ninfo->hooks; f++) { 605204716Sluigi nlink = &hlist->link[f]; 606187767Sluigi 607187767Sluigi log_Printf(LogDEBUG, " Found %s -> %s (type %s)\n", nlink->ourhook, 608187767Sluigi nlink->peerhook, nlink->nodeinfo.type); 609187767Sluigi 610187767Sluigi if (!strcmp(nlink->ourhook, lasthook)) { 611223661Sae if (strcmp(nlink->peerhook, hook) || 612223661Sae strcmp(nlink->nodeinfo.type, nodetype)) { 613223661Sae log_Printf(LogWARN, "%s: hook %s:%s is already in use\n", 614223661Sae p->link.name, nlink->ourhook, path); 615223661Sae return ng_Abandon(dev, p); 616223661Sae } 617187767Sluigi /* The node is already hooked up nicely.... reuse it */ 618187767Sluigi break; 619187767Sluigi } 620187767Sluigi } 621187767Sluigi 622187767Sluigi if (f == ninfo->hooks) { 623187767Sluigi /* Attempt to load the module */ 624187767Sluigi snprintf(modname, sizeof modname, "ng_%s", nodetype); 625 log_Printf(LogDEBUG, "%s: Attempting to load %s.ko\n", 626 p->link.name, modname); 627 loadmodules(LOAD_QUIETLY, modname, NULL); 628 629 /* Create (mkpeer) the new node */ 630 631 snprintf(mkp.type, sizeof mkp.type, "%s", nodetype); 632 snprintf(mkp.ourhook, sizeof mkp.ourhook, "%s", lasthook); 633 snprintf(mkp.peerhook, sizeof mkp.peerhook, "%s", hook); 634 635 log_Printf(LogDEBUG, "%s: Doing MKPEER %s%s -> %s (type %s)\n", 636 p->link.name, path, mkp.ourhook, mkp.peerhook, nodetype); 637 638 if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, 639 NGM_MKPEER, &mkp, sizeof mkp) < 0) { 640 log_Printf(LogWARN, "%s Cannot create %s netgraph node: %s\n", 641 path, nodetype, strerror(errno)); 642 return ng_Abandon(dev, p); 643 } 644 } 645 len = strlen(path); 646 snprintf(path + len, sizeof path - len, "%s%s", 647 path[len - 1] == ':' ? "" : ".", lasthook); 648 } 649 650 /* Get a list of node hooks */ 651 if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, 652 NULL, 0) < 0) { 653 log_Printf(LogWARN, "%s: %s Cannot send a LISTHOOOKS message: %s\n", 654 p->link.name, path, strerror(errno)); 655 return ng_Abandon(dev, p); 656 } 657 658 /* Get our list back */ 659 resp = (struct ng_mesg *)rbuf; 660 if (NgRecvMsg(dev->cs, resp, sizeof rbuf, NULL) <= 0) { 661 log_Printf(LogWARN, "%s: Cannot get netgraph response: %s\n", 662 p->link.name, strerror(errno)); 663 return ng_Abandon(dev, p); 664 } 665 666 hlist = (const struct hooklist *)resp->data; 667 ninfo = &hlist->nodeinfo; 668 669 if (*lasthook != '\0' && nodename != NULL && *nodename != '\0' && 670 strcmp(ninfo->name, nodename) && 671 NgNameNode(dev->cs, path, "%s", nodename) < 0) { 672 log_Printf(LogWARN, "%s: %s: Cannot name netgraph node: %s\n", 673 p->link.name, path, strerror(errno)); 674 return ng_Abandon(dev, p); 675 } 676 677 if (!GETSEGMENT(lasthook, devp, " \t.[", &endp)) 678 return ng_Abandon(dev, p); 679 log_Printf(LogDEBUG, "%s: Got hook \"%s\"\n", p->link.name, lasthook); 680 681 len = strlen(lasthook); 682 done = strchr(" \t", devp[len]) ? 1 : 0; 683 devp = endp; 684 685 if (*devp != '\0') { 686 if (devp[-1] == '[') 687 devp--; 688 } /* else should moan about devp[-1] being '[' ? */ 689 } 690 691 snprintf(dev->hook, sizeof dev->hook, "%s", lasthook); 692 693 /* Connect the node to our socket node */ 694 snprintf(ngc.path, sizeof ngc.path, "%s", path); 695 snprintf(ngc.ourhook, sizeof ngc.ourhook, "%s", dev->hook); 696 memcpy(ngc.peerhook, ngc.ourhook, sizeof ngc.peerhook); 697 698 log_Printf(LogDEBUG, "Connecting netgraph socket .:%s -> %s.%s\n", 699 ngc.ourhook, ngc.path, ngc.peerhook); 700 if (NgSendMsg(dev->cs, ".:", NGM_GENERIC_COOKIE, 701 NGM_CONNECT, &ngc, sizeof ngc) < 0) { 702 log_Printf(LogWARN, "Cannot connect %s and socket netgraph " 703 "nodes: %s\n", path, strerror(errno)); 704 return ng_Abandon(dev, p); 705 } 706 707 /* Hook things up so that we monitor dev->cs */ 708 p->desc.UpdateSet = ng_UpdateSet; 709 p->desc.IsSet = ng_IsSet; 710 p->desc.Read = ng_DescriptorRead; 711 712 memcpy(&dev->dev, &basengdevice, sizeof dev->dev); 713 714 } else { 715 /* See if we're a netgraph socket */ 716 717 sz = sizeof ngsock; 718 if (getsockname(p->fd, sock, &sz) != -1 && sock->sa_family == AF_NETGRAPH) { 719 /* 720 * It's a netgraph node... We can't determine hook names etc, so we 721 * stay pretty impartial.... 722 */ 723 log_Printf(LogPHASE, "%s: Link is a netgraph node\n", p->link.name); 724 725 if ((dev = malloc(sizeof *dev)) == NULL) { 726 log_Printf(LogWARN, "%s: Cannot allocate an ether device: %s\n", 727 p->link.name, strerror(errno)); 728 return NULL; 729 } 730 731 memcpy(&dev->dev, &basengdevice, sizeof dev->dev); 732 dev->cs = -1; 733 *dev->hook = '\0'; 734 } 735 } 736 737 if (dev) { 738 physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF); 739 return &dev->dev; 740 } 741 742 return NULL; 743} 744