struct device * udp_iov2device(int type, struct physical *p, struct iovec *iov, int *niov, int maxiov, int *auxfd, int *nauxfd) { if (type == UDP_DEVICE) { struct udpdevice *dev = (struct udpdevice *)iov[(*niov)++].iov_base; struct udpdevice *newdev; newdev = realloc(dev, sizeof *dev); /* Reduce to the correct size */ if (newdev == NULL) { log_Printf(LogALERT, "Failed to allocate memory: %d\n", (int)(sizeof *dev)); AbortProgram(EX_OSERR); } dev = newdev; /* Refresh function pointers etc */ memcpy(&dev->dev, &baseudpdevice, sizeof dev->dev); physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNC); return &dev->dev; } return NULL; }
struct device * i4b_iov2device(int type, struct physical *p, struct iovec *iov, int *niov, int maxiov, int *auxfd, int *nauxfd) { if (type == I4B_DEVICE) { struct i4bdevice *dev = (struct i4bdevice *)iov[(*niov)++].iov_base; dev = realloc(dev, sizeof *dev); /* Reduce to the correct size */ if (dev == NULL) { log_Printf(LogALERT, "Failed to allocate memory: %d\n", (int)(sizeof *dev)); AbortProgram(EX_OSERR); } /* Refresh function pointers etc */ memcpy(&dev->dev, &basei4bdevice, sizeof dev->dev); physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE); if (dev->Timer.state != TIMER_STOPPED) { dev->Timer.state = TIMER_STOPPED; p->handler = &dev->dev; /* For the benefit of StartTimer */ i4b_StartTimer(p); } return &dev->dev; } return NULL; }
struct device * ether_iov2device(int type, struct physical *p, struct iovec *iov, int *niov, int maxiov __unused, int *auxfd, int *nauxfd) { if (type == ETHER_DEVICE) { struct etherdevice *dev = (struct etherdevice *)iov[(*niov)++].iov_base; dev = realloc(dev, sizeof *dev); /* Reduce to the correct size */ if (dev == NULL) { log_Printf(LogALERT, "Failed to allocate memory: %d\n", (int)(sizeof *dev)); AbortProgram(EX_OSERR); } if (*nauxfd) { dev->cs = *auxfd; (*nauxfd)--; } else dev->cs = -1; /* Refresh function pointers etc */ memcpy(&dev->dev, &baseetherdevice, sizeof dev->dev); physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF); return &dev->dev; } return NULL; }
struct device * atm_Create(struct physical *p) { struct atmdevice *dev; dev = NULL; if (p->fd < 0 && !strncasecmp(p->name.full, PPPOA, PPPOA_LEN) && p->name.full[PPPOA_LEN] == ':') { char iface[25]; unsigned vci, vpi; if (sscanf(p->name.full + PPPOA_LEN + 1, "%25[A-Za-z0-9]:%u.%u", iface, &vpi, &vci) != 3) { log_Printf(LogWARN, "Malformed ATM device name \'%s\', " "PPPoA:if:vpi.vci expected\n", p->name.full); return NULL; } dev = atm_CreateDevice(p, iface, vpi, vci); } if (dev) { memcpy(&dev->dev, &baseatmdevice, sizeof dev->dev); physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF); if (p->cfg.cd.necessity != CD_DEFAULT) log_Printf(LogWARN, "Carrier settings ignored\n"); return &dev->dev; } return NULL; }
int physical_Open(struct physical *p, struct bundle *bundle) { int devno, h, wasfd, err; char *dev; if (p->fd >= 0) log_Printf(LogDEBUG, "%s: Open: Modem is already open!\n", p->link.name); /* We're going back into "term" mode */ else if (p->type == PHYS_DIRECT) { physical_SetDevice(p, ""); p->fd = STDIN_FILENO; for (h = 0; h < NDEVICES && p->handler == NULL && p->fd >= 0; h++) p->handler = (*devices[h].create)(p); if (p->fd >= 0) { if (p->handler == NULL) { physical_SetupStack(p, "unknown", PHYSICAL_NOFORCE); log_Printf(LogDEBUG, "%s: stdin is unidentified\n", p->link.name); } physical_Found(p); } } else { dev = p->cfg.devlist; devno = 0; while (devno < p->cfg.ndev && p->fd < 0) { physical_SetDevice(p, dev); if (physical_Lock(p)) { err = 0; if (*p->name.full == '/') { p->fd = ID0open(p->name.full, O_RDWR | O_NONBLOCK); if (p->fd < 0) err = errno; } wasfd = p->fd; for (h = 0; h < NDEVICES && p->handler == NULL; h++) if ((p->handler = (*devices[h].create)(p)) == NULL && wasfd != p->fd) break; if (p->fd < 0) { if (h == NDEVICES) { if (err) log_Printf(LogWARN, "%s: %s: %s\n", p->link.name, p->name.full, strerror(errno)); else log_Printf(LogWARN, "%s: Device (%s) must begin with a '/'," " a '!' or contain at least one ':'\n", p->link.name, p->name.full); } physical_Unlock(p); } else physical_Found(p); } dev += strlen(dev) + 1; devno++; } } return p->fd; }
struct physical * iov2physical(struct datalink *dl, struct iovec *iov, int *niov, int maxiov, int fd, int *auxfd, int *nauxfd) { struct physical *p; int len, h, type; p = (struct physical *)iov[(*niov)++].iov_base; p->link.name = dl->name; memset(p->link.Queue, '\0', sizeof p->link.Queue); p->desc.UpdateSet = physical_UpdateSet; p->desc.IsSet = physical_IsSet; p->desc.Read = physical_DescriptorRead; p->desc.Write = physical_DescriptorWrite; p->type = PHYS_DIRECT; p->dl = dl; len = strlen(_PATH_DEV); p->out = NULL; p->connect_count = 1; physical_SetDevice(p, p->name.full); p->link.lcp.fsm.bundle = dl->bundle; p->link.lcp.fsm.link = &p->link; memset(&p->link.lcp.fsm.FsmTimer, '\0', sizeof p->link.lcp.fsm.FsmTimer); memset(&p->link.lcp.fsm.OpenTimer, '\0', sizeof p->link.lcp.fsm.OpenTimer); memset(&p->link.lcp.fsm.StoppedTimer, '\0', sizeof p->link.lcp.fsm.StoppedTimer); p->link.lcp.fsm.parent = &dl->fsmp; lcp_SetupCallbacks(&p->link.lcp); p->link.ccp.fsm.bundle = dl->bundle; p->link.ccp.fsm.link = &p->link; /* Our in.state & out.state are NULL (no link-level ccp yet) */ memset(&p->link.ccp.fsm.FsmTimer, '\0', sizeof p->link.ccp.fsm.FsmTimer); memset(&p->link.ccp.fsm.OpenTimer, '\0', sizeof p->link.ccp.fsm.OpenTimer); memset(&p->link.ccp.fsm.StoppedTimer, '\0', sizeof p->link.ccp.fsm.StoppedTimer); p->link.ccp.fsm.parent = &dl->fsmp; ccp_SetupCallbacks(&p->link.ccp); p->hdlc.lqm.owner = &p->link.lcp; p->hdlc.ReportTimer.state = TIMER_STOPPED; p->hdlc.lqm.timer.state = TIMER_STOPPED; p->fd = fd; p->link.stats.total.in.SampleOctets = (long long *)iov[(*niov)++].iov_base; p->link.stats.total.out.SampleOctets = (long long *)iov[(*niov)++].iov_base; p->link.stats.parent = dl->bundle->ncp.mp.active ? &dl->bundle->ncp.mp.link.stats.total : NULL; p->link.stats.gather = 1; type = (long)p->handler; p->handler = NULL; for (h = 0; h < NDEVICES && p->handler == NULL; h++) p->handler = (*devices[h].iov2device)(type, p, iov, niov, maxiov, auxfd, nauxfd); if (p->handler == NULL) { log_Printf(LogPHASE, "%s: Unknown link type\n", p->link.name); free(iov[(*niov)++].iov_base); physical_SetupStack(p, "unknown", PHYSICAL_NOFORCE); } else log_Printf(LogPHASE, "%s: Device %s, link type is %s\n", p->link.name, p->name.full, p->handler->name); if (p->hdlc.lqm.method && p->hdlc.lqm.timer.load) lqr_reStart(&p->link.lcp); hdlc_StartTimer(&p->hdlc); throughput_restart(&p->link.stats.total, "physical throughput", Enabled(dl->bundle, OPT_THROUGHPUT)); return p; }
struct device * udp_Create(struct physical *p) { char *cp, *host, *port, *svc; struct udpdevice *dev; dev = NULL; if (p->fd < 0) { if ((cp = strchr(p->name.full, ':')) != NULL && !strchr(cp + 1, ':')) { *cp = '\0'; host = p->name.full; port = cp + 1; svc = strchr(port, '/'); if (svc && strcasecmp(svc, "/udp")) { *cp = ':'; return NULL; } if (svc) { p->fd--; /* We own the device but maybe can't use it - change fd */ *svc = '\0'; } if (*host && *port) dev = udp_CreateDevice(p, host, port); *cp = ':'; if (svc) *svc = '/'; } } else { /* See if we're a connected udp socket */ struct stat st; if (fstat(p->fd, &st) != -1 && S_ISSOCK(st.st_mode)) { int type, sz; sz = sizeof type; if (getsockopt(p->fd, SOL_SOCKET, SO_TYPE, &type, &sz) == -1) { log_Printf(LogPHASE, "%s: Link is a closed socket !\n", p->link.name); close(p->fd); p->fd = -1; return NULL; } if (sz == sizeof type && type == SOCK_DGRAM) { struct sockaddr_in sock; struct sockaddr *sockp = (struct sockaddr *)&sock; if ((dev = malloc(sizeof *dev)) == NULL) { log_Printf(LogWARN, "%s: Cannot allocate a udp device: %s\n", p->link.name, strerror(errno)); return NULL; } if (getpeername(p->fd, sockp, &sz) == 0) { log_Printf(LogPHASE, "%s: Link is a connected udp socket\n", p->link.name); dev->connected = UDP_CONNECTED; } else { log_Printf(LogPHASE, "%s: Link is a disconnected udp socket\n", p->link.name); dev->connected = UDP_MAYBEUNCONNECTED; if (p->link.lcp.cfg.openmode != OPEN_PASSIVE) { log_Printf(LogPHASE, "%s: Changing openmode to PASSIVE\n", p->link.name); p->link.lcp.cfg.openmode = OPEN_PASSIVE; } } } } } if (dev) { memcpy(&dev->dev, &baseudpdevice, sizeof dev->dev); physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNC); if (p->cfg.cd.necessity != CD_DEFAULT) log_Printf(LogWARN, "Carrier settings ignored\n"); return &dev->dev; } return NULL; }
struct device * i4b_Create(struct physical *p) { struct i4bdevice *dev; int oldflag, dial; msg_vr_req_t req; telno_t number; if (p->fd < 0 || ioctl(p->fd, I4B_RBCH_VR_REQ, &req)) /* Don't want this */ return NULL; /* * We don't bother validating the version.... all versions of i4b that * support I4B_RBCH_VR_REQ are fair game :-) */ if (*p->name.full == '\0') { physical_SetDevice(p, ttyname(p->fd)); log_Printf(LogDEBUG, "%s: Input is an i4b version %d.%d.%d isdn " "device (%s)\n", p->link.name, req.version, req.release, req.step, p->name.full); dial = 0; } else { log_Printf(LogDEBUG, "%s: Opened %s (i4b version %d.%d.%d)\n", p->link.name, p->name.full, req.version, req.release, req.step); dial = 1; } /* We're gonna return an i4bdevice (unless something goes horribly wrong) */ if ((dev = malloc(sizeof *dev)) == NULL) { /* Complete failure - parent doesn't continue trying to ``create'' */ close(p->fd); p->fd = -1; return NULL; } memcpy(&dev->dev, &basei4bdevice, sizeof dev->dev); memset(&dev->Timer, '\0', sizeof dev->Timer); dev->mbits = -1; switch (p->cfg.cd.necessity) { case CD_VARIABLE: dev->dev.cd.delay = p->cfg.cd.delay; break; case CD_REQUIRED: dev->dev.cd = p->cfg.cd; break; case CD_NOTREQUIRED: log_Printf(LogWARN, "%s: Carrier must be set, using ``set cd %d!''\n", p->link.name, dev->dev.cd.delay); case CD_DEFAULT: break; } oldflag = fcntl(p->fd, F_GETFL, 0); if (oldflag < 0) { /* Complete failure - parent doesn't continue trying to ``create'' */ log_Printf(LogWARN, "%s: Open: Cannot get physical flags: %s\n", p->link.name, strerror(errno)); i4b_Cooked(p); close(p->fd); p->fd = -1; free(dev); return NULL; } else fcntl(p->fd, F_SETFL, oldflag & ~O_NONBLOCK); if (dial) { strncpy(number, datalink_ChoosePhoneNumber(p->dl), sizeof number - 1); number[sizeof number - 1] = '\0'; if (number[0] == '\0') dial = 0; } if (dial && ioctl(p->fd, I4B_RBCH_DIALOUT, number) == -1) { /* Complete failure - parent doesn't continue trying to ``create'' */ log_Printf(LogWARN, "%s: ioctl(I4B_RBCH_DIALOUT): %s\n", p->link.name, strerror(errno)); i4b_Cooked(p); close(p->fd); p->fd = -1; free(dev); return NULL; } physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNC); return &dev->dev; }
struct device * ether_Create(struct physical *p) { u_char rbuf[2048]; struct etherdevice *dev; struct ng_mesg *resp; const struct hooklist *hlist; const struct nodeinfo *ninfo; char *path, *sessionid; const char *mode; size_t ifacelen; unsigned f; dev = NULL; path = NULL; ifacelen = 0; if (p->fd < 0 && !strncasecmp(p->name.full, NG_PPPOE_NODE_TYPE, PPPOE_NODE_TYPE_LEN) && p->name.full[PPPOE_NODE_TYPE_LEN] == ':') { const struct linkinfo *nlink; struct ngpppoe_init_data *data; struct ngm_mkpeer mkp; struct ngm_connect ngc; const char *iface, *provider; char etherid[12]; int providerlen; char connectpath[sizeof dev->hook + 2]; /* .:<hook> */ p->fd--; /* We own the device - change fd */ loadmodules(LOAD_VERBOSLY, "netgraph", "ng_ether", "ng_pppoe", "ng_socket", NULL); if ((dev = malloc(sizeof *dev)) == NULL) return NULL; iface = p->name.full + PPPOE_NODE_TYPE_LEN + 1; provider = strchr(iface, ':'); if (provider) { ifacelen = provider - iface; provider++; providerlen = strlen(provider); } else { ifacelen = strlen(iface); provider = ""; providerlen = 0; } /* * We're going to do this (where tunN is our tunnel device): * * .---------. * | ether | * | <iface> | dev->cs * `---------' | * (orphan) p->fd | * | | | * | | | * (ethernet) | | * .---------. .-----------. * | pppoe | | socket | * | <iface> |(tunN)<---->(tunN)| <unnamed> | * `--------- `-----------' * (tunX) * ^ * | * `--->(tunX) */ /* Create a socket node */ if (ID0NgMkSockNode(NULL, &dev->cs, &p->fd) == -1) { log_Printf(LogWARN, "Cannot create netgraph socket node: %s\n", strerror(errno)); free(dev); p->fd = -2; return NULL; } /* * Ask for a list of hooks attached to the "ether" node. This node should * magically exist as a way of hooking stuff onto an ethernet device */ path = (char *)alloca(ifacelen + 2); sprintf(path, "%.*s:", (int)ifacelen, iface); if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0) < 0) { log_Printf(LogWARN, "%s Cannot send a netgraph message: %s\n", path, strerror(errno)); return ether_Abandon(dev, p); } /* Get our list back */ resp = (struct ng_mesg *)rbuf; if (NgRecvMsg(dev->cs, resp, sizeof rbuf, NULL) <= 0) { log_Printf(LogWARN, "Cannot get netgraph response: %s\n", strerror(errno)); return ether_Abandon(dev, p); } hlist = (const struct hooklist *)resp->data; ninfo = &hlist->nodeinfo; /* Make sure we've got the right type of node */ if (strncmp(ninfo->type, NG_ETHER_NODE_TYPE, sizeof NG_ETHER_NODE_TYPE - 1)) { log_Printf(LogWARN, "%s Unexpected node type ``%s'' (wanted ``" NG_ETHER_NODE_TYPE "'')\n", path, ninfo->type); return ether_Abandon(dev, p); } log_Printf(LogDEBUG, "List of netgraph node ``%s'' (id %x) hooks:\n", path, ninfo->id); /* look for a hook already attached. */ for (f = 0; f < ninfo->hooks; f++) { nlink = &hlist->link[f]; log_Printf(LogDEBUG, " Found %s -> %s\n", nlink->ourhook, nlink->peerhook); if (!strcmp(nlink->ourhook, NG_ETHER_HOOK_ORPHAN) || !strcmp(nlink->ourhook, NG_ETHER_HOOK_DIVERT)) { /* * Something is using the data coming out of this ``ether'' node. * If it's a PPPoE node, we use that node, otherwise we complain that * someone else is using the node. */ if (!strcmp(nlink->nodeinfo.type, NG_PPPOE_NODE_TYPE)) /* Use this PPPoE node ! */ snprintf(ngc.path, sizeof ngc.path, "[%x]:", nlink->nodeinfo.id); else { log_Printf(LogWARN, "%s Node type ``%s'' is currently active\n", path, nlink->nodeinfo.type); return ether_Abandon(dev, p); } break; } } if (f == ninfo->hooks) { /* * Create a new ``PPPoE'' node connected to the ``ether'' node using * the ``orphan'' and ``ethernet'' hooks */ snprintf(mkp.type, sizeof mkp.type, "%s", NG_PPPOE_NODE_TYPE); snprintf(mkp.ourhook, sizeof mkp.ourhook, "%s", NG_ETHER_HOOK_ORPHAN); snprintf(mkp.peerhook, sizeof mkp.peerhook, "%s", NG_PPPOE_HOOK_ETHERNET); snprintf(etherid, sizeof etherid, "[%x]:", ninfo->id); log_Printf(LogDEBUG, "Creating PPPoE netgraph node %s%s -> %s\n", etherid, mkp.ourhook, mkp.peerhook); if (NgSendMsg(dev->cs, etherid, NGM_GENERIC_COOKIE, NGM_MKPEER, &mkp, sizeof mkp) < 0) { log_Printf(LogWARN, "%s Cannot create PPPoE netgraph node: %s\n", etherid, strerror(errno)); return ether_Abandon(dev, p); } snprintf(ngc.path, sizeof ngc.path, "%s%s", path, NG_ETHER_HOOK_ORPHAN); } snprintf(dev->hook, sizeof dev->hook, "%s%d", TUN_NAME, p->dl->bundle->unit); /* * Connect the PPPoE node to our socket node. * ngc.path has already been set up */ snprintf(ngc.ourhook, sizeof ngc.ourhook, "%s", dev->hook); memcpy(ngc.peerhook, ngc.ourhook, sizeof ngc.peerhook); log_Printf(LogDEBUG, "Connecting netgraph socket .:%s -> %s:%s\n", ngc.ourhook, ngc.path, ngc.peerhook); if (NgSendMsg(dev->cs, ".:", NGM_GENERIC_COOKIE, NGM_CONNECT, &ngc, sizeof ngc) < 0) { log_Printf(LogWARN, "Cannot connect PPPoE and socket netgraph " "nodes: %s\n", strerror(errno)); return ether_Abandon(dev, p); } /* Bring the Ethernet interface up */ path[ifacelen] = '\0'; /* Remove the trailing ':' */ if (!iface_SetFlags(path, IFF_UP)) log_Printf(LogWARN, "%s: Failed to set the IFF_UP flag on %s\n", p->link.name, path); snprintf(connectpath, sizeof connectpath, ".:%s", dev->hook); /* Configure node to 3Com mode if needed */ if (p->cfg.pppoe_configured) { mode = p->cfg.nonstandard_pppoe ? NG_PPPOE_NONSTANDARD : NG_PPPOE_STANDARD; if (NgSendMsg(dev->cs, connectpath, NGM_PPPOE_COOKIE, NGM_PPPOE_SETMODE, mode, strlen(mode) + 1) == -1) { log_Printf(LogWARN, "``%s'': Cannot configure netgraph node: %s\n", connectpath, strerror(errno)); return ether_Abandon(dev, p); } } /* And finally, request a connection to the given provider */ data = (struct ngpppoe_init_data *)alloca(sizeof *data + providerlen); snprintf(data->hook, sizeof data->hook, "%s", dev->hook); memcpy(data->data, provider, providerlen); data->data_len = providerlen; log_Printf(LogDEBUG, "Sending PPPOE_CONNECT to %s\n", connectpath); if (NgSendMsg(dev->cs, connectpath, NGM_PPPOE_COOKIE, NGM_PPPOE_CONNECT, data, sizeof *data + providerlen) == -1) { log_Printf(LogWARN, "``%s'': Cannot start netgraph node: %s\n", connectpath, strerror(errno)); return ether_Abandon(dev, p); } /* Hook things up so that we monitor dev->cs */ p->desc.UpdateSet = ether_UpdateSet; p->desc.IsSet = ether_IsSet; p->desc.Read = ether_DescriptorRead; memcpy(&dev->dev, &baseetherdevice, sizeof dev->dev); switch (p->cfg.cd.necessity) { case CD_VARIABLE: dev->dev.cd.delay = p->cfg.cd.delay; break; case CD_REQUIRED: dev->dev.cd = p->cfg.cd; break; case CD_NOTREQUIRED: log_Printf(LogWARN, "%s: Carrier must be set, using ``set cd %d!''\n", p->link.name, dev->dev.cd.delay); case CD_DEFAULT: break; } dev->timeout = dev->dev.cd.delay; dev->connected = CARRIER_PENDING; /* This will be overridden by our session id - if provided by netgraph */ dev->slot = GetIfIndex(path); } else { /* See if we're a netgraph socket */ struct stat st; if (fstat(p->fd, &st) != -1 && (st.st_mode & S_IFSOCK)) { struct sockaddr_storage ssock; struct sockaddr *sock = (struct sockaddr *)&ssock; int sz; sz = sizeof ssock; if (getsockname(p->fd, sock, &sz) == -1) { log_Printf(LogPHASE, "%s: Link is a closed socket !\n", p->link.name); close(p->fd); p->fd = -1; return NULL; } if (sock->sa_family == AF_NETGRAPH) { /* * It's a netgraph node... We can't determine hook names etc, so we * stay pretty impartial.... */ log_Printf(LogPHASE, "%s: Link is a netgraph node\n", p->link.name); if ((dev = malloc(sizeof *dev)) == NULL) { log_Printf(LogWARN, "%s: Cannot allocate an ether device: %s\n", p->link.name, strerror(errno)); return NULL; } memcpy(&dev->dev, &baseetherdevice, sizeof dev->dev); dev->cs = -1; dev->timeout = 0; dev->connected = CARRIER_OK; *dev->hook = '\0'; /* * If we're being envoked from pppoed(8), we may have a SESSIONID * set in the environment. If so, use it as the slot */ if ((sessionid = getenv("SESSIONID")) != NULL) { char *end; u_long slot; slot = strtoul(sessionid, &end, 16); dev->slot = end != sessionid && *end == '\0' ? slot : 0; } else dev->slot = 0; } } } if (dev) { physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF); return &dev->dev; } return NULL; }
struct device * exec_Create(struct physical *p) { struct execdevice *dev; dev = NULL; if (p->fd < 0) { if (*p->name.full == '!') { int fids[2], type; if ((dev = malloc(sizeof *dev)) == NULL) { log_Printf(LogWARN, "%s: Cannot allocate an exec device: %s\n", p->link.name, strerror(errno)); return NULL; } dev->fd_out = -1; p->fd--; /* We own the device but maybe can't use it - change fd */ type = physical_IsSync(p) ? SOCK_DGRAM : SOCK_STREAM; if (socketpair(AF_UNIX, type, PF_UNSPEC, fids) < 0) { log_Printf(LogPHASE, "Unable to create pipe for line exec: %s\n", strerror(errno)); free(dev); dev = NULL; } else { static int child_status; /* This variable is abused ! */ int stat, argc, i, ret, wret, pidpipe[2]; pid_t pid, realpid; char *argv[MAXARGS]; stat = fcntl(fids[0], F_GETFL, 0); if (stat > 0) { stat |= O_NONBLOCK; fcntl(fids[0], F_SETFL, stat); } realpid = getpid(); if (pipe(pidpipe) == -1) { log_Printf(LogPHASE, "Unable to pipe for line exec: %s\n", strerror(errno)); close(fids[1]); close(fids[0]); free(dev); dev = NULL; } else switch ((pid = fork())) { case -1: log_Printf(LogPHASE, "Unable to fork for line exec: %s\n", strerror(errno)); close(pidpipe[0]); close(pidpipe[1]); close(fids[1]); close(fids[0]); break; case 0: close(pidpipe[0]); close(fids[0]); timer_TermService(); #ifndef NOSUID setuid(ID0realuid()); #endif child_status = 0; switch ((pid = vfork())) { case 0: close(pidpipe[1]); break; case -1: ret = errno; log_Printf(LogPHASE, "Unable to vfork to drop parent: %s\n", strerror(errno)); close(pidpipe[1]); _exit(ret); default: write(pidpipe[1], &pid, sizeof pid); close(pidpipe[1]); _exit(child_status); /* The error from exec() ! */ } log_Printf(LogDEBUG, "Exec'ing ``%s''\n", p->name.base); if ((argc = MakeArgs(p->name.base, argv, VECSIZE(argv), PARSE_REDUCE|PARSE_NOHASH)) < 0) { log_Printf(LogWARN, "Syntax error in exec command\n"); _exit(ESRCH); } command_Expand(argv, argc, (char const *const *)argv, p->dl->bundle, 0, realpid); dup2(fids[1], STDIN_FILENO); dup2(fids[1], STDOUT_FILENO); dup2(fids[1], STDERR_FILENO); for (i = getdtablesize(); i > STDERR_FILENO; i--) fcntl(i, F_SETFD, 1); execvp(*argv, argv); child_status = errno; /* Only works for vfork() */ printf("execvp failed: %s: %s\r\n", *argv, strerror(child_status)); _exit(child_status); break; default: close(pidpipe[1]); close(fids[1]); if (read(pidpipe[0], &p->session_owner, sizeof p->session_owner) != sizeof p->session_owner) p->session_owner = (pid_t)-1; close(pidpipe[0]); while ((wret = waitpid(pid, &stat, 0)) == -1 && errno == EINTR) ; if (wret == -1) { log_Printf(LogWARN, "Waiting for child process: %s\n", strerror(errno)); close(fids[0]); p->session_owner = (pid_t)-1; break; } else if (WIFSIGNALED(stat)) { log_Printf(LogWARN, "Child process received sig %d !\n", WTERMSIG(stat)); close(fids[0]); p->session_owner = (pid_t)-1; break; } else if (WIFSTOPPED(stat)) { log_Printf(LogWARN, "Child process received stop sig %d !\n", WSTOPSIG(stat)); /* I guess that's ok.... */ } else if ((ret = WEXITSTATUS(stat))) { log_Printf(LogWARN, "Cannot exec \"%s\": %s\n", p->name.base, strerror(ret)); close(fids[0]); p->session_owner = (pid_t)-1; break; } p->fd = fids[0]; log_Printf(LogDEBUG, "Using descriptor %d for child\n", p->fd); } } } } else { struct stat st; if (fstat(p->fd, &st) != -1 && (st.st_mode & S_IFIFO)) { if ((dev = malloc(sizeof *dev)) == NULL) log_Printf(LogWARN, "%s: Cannot allocate an exec device: %s\n", p->link.name, strerror(errno)); else if (p->fd == STDIN_FILENO) { log_Printf(LogPHASE, "%s: Using stdin/stdout to communicate with " "parent (pipe mode)\n", p->link.name); dev->fd_out = dup(STDOUT_FILENO); /* Hook things up so that we monitor dev->fd_out */ p->desc.UpdateSet = exec_UpdateSet; p->desc.IsSet = exec_IsSet; } else dev->fd_out = -1; } } if (dev) { memcpy(&dev->dev, &baseexecdevice, sizeof dev->dev); physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE); if (p->cfg.cd.necessity != CD_DEFAULT) log_Printf(LogWARN, "Carrier settings ignored\n"); return &dev->dev; } return NULL; }
struct device * ng_Create(struct physical *p) { struct sockaddr_ng ngsock; u_char rbuf[2048]; struct sockaddr *sock = (struct sockaddr *)&ngsock; const struct hooklist *hlist; const struct nodeinfo *ninfo; const struct linkinfo *nlink; struct ngdevice *dev; struct ng_mesg *resp; struct ngm_mkpeer mkp; struct ngm_connect ngc; const char *devp, *endp; char lasthook[NG_HOOKSIZ]; char hook[NG_HOOKSIZ]; char nodetype[NG_TYPESIZ + NG_NODESIZ]; char modname[NG_TYPESIZ + 3]; char path[NG_PATHSIZ]; char *nodename; int len, sz, done; unsigned f; dev = NULL; if (p->fd < 0 && !strncasecmp(p->name.full, NETGRAPH_PREFIX, sizeof NETGRAPH_PREFIX - 1)) { p->fd--; /* We own the device - change fd */ if ((dev = malloc(sizeof *dev)) == NULL) return NULL; loadmodules(LOAD_VERBOSLY, "netgraph", "ng_socket", NULL); /* Create a socket node */ if (ID0NgMkSockNode(NULL, &dev->cs, &p->fd) == -1) { log_Printf(LogWARN, "Cannot create netgraph socket node: %s\n", strerror(errno)); free(dev); p->fd = -2; return NULL; } devp = p->name.full + sizeof NETGRAPH_PREFIX - 1; *lasthook = *path = '\0'; log_Printf(LogDEBUG, "%s: Opening netgraph device \"%s\"\n", p->link.name, devp); done = 0; while (*devp != '\0' && !done) { if (*devp != '[') { if (*lasthook == '\0') { log_Printf(LogWARN, "%s: Netgraph devices must start with" " [nodetype:nodename]\n", p->link.name); return ng_Abandon(dev, p); } /* Get the hook name of the new node */ if (!GETSEGMENT(hook, devp, ".[", &endp)) return ng_Abandon(dev, p); log_Printf(LogDEBUG, "%s: Got hook \"%s\"\n", p->link.name, hook); devp = endp; if (*devp == '\0') { log_Printf(LogWARN, "%s: Netgraph device must not end with a second" " hook\n", p->link.name); return ng_Abandon(dev, p); } if (devp[-1] != '[') { log_Printf(LogWARN, "%s: Expected a [nodetype:nodename] at device" " pos %d\n", p->link.name, devp - p->link.name - 1); return ng_Abandon(dev, p); } } else { /* Use lasthook as the hook name */ strcpy(hook, lasthook); devp++; } /* We've got ``lasthook'' and ``hook'', get the node type */ if (!GETSEGMENT(nodetype, devp, "]", &endp)) return ng_Abandon(dev, p); log_Printf(LogDEBUG, "%s: Got node \"%s\"\n", p->link.name, nodetype); if ((nodename = strchr(nodetype, ':')) != NULL) { *nodename++ = '\0'; if (*nodename == '\0' && *nodetype == '\0') { log_Printf(LogWARN, "%s: Empty [nodetype:nodename] at device" " pos %d\n", p->link.name, devp - p->link.name - 1); return ng_Abandon(dev, p); } } /* Ignore optional colons after nodes */ devp = *endp == ':' ? endp + 1 : endp; if (*devp == '.') devp++; if (*lasthook == '\0') { /* This is the first node in the chain */ if (nodename == NULL || *nodename == '\0') { log_Printf(LogWARN, "%s: %s: No initial device nodename\n", p->link.name, devp); return ng_Abandon(dev, p); } if (*nodetype != '\0') { /* Attempt to load the module */ snprintf(modname, sizeof modname, "ng_%s", nodetype); log_Printf(LogDEBUG, "%s: Attempting to load %s.ko\n", p->link.name, modname); loadmodules(LOAD_QUIETLY, modname, NULL); } snprintf(path, sizeof path, "%s:", nodename); /* XXX: If we have a node type, ensure it's correct */ } else { /* * Ask for a list of hooks attached to the previous node. If we * find the one we're interested in, and if it's connected to a * node of the right type using the correct hook, use that. * If we find the hook connected to something else, fail. * If we find no match, mkpeer the new node. */ if (*nodetype == '\0') { log_Printf(LogWARN, "%s: Nodetype missing at device offset %d\n", p->link.name, devp - p->name.full + sizeof NETGRAPH_PREFIX - 1); return ng_Abandon(dev, p); } /* Get a list of node hooks */ if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0) < 0) { log_Printf(LogWARN, "%s: %s Cannot send a LISTHOOOKS message: %s\n", p->link.name, path, strerror(errno)); return ng_Abandon(dev, p); } /* Get our list back */ resp = (struct ng_mesg *)rbuf; if (NgRecvMsg(dev->cs, resp, sizeof rbuf, NULL) <= 0) { log_Printf(LogWARN, "%s: Cannot get netgraph response: %s\n", p->link.name, strerror(errno)); return ng_Abandon(dev, p); } hlist = (const struct hooklist *)resp->data; ninfo = &hlist->nodeinfo; log_Printf(LogDEBUG, "List of netgraph node ``%s'' (id %x) hooks:\n", path, ninfo->id); /* look for a hook already attached. */ for (f = 0; f < ninfo->hooks; f++) { nlink = &hlist->link[f]; log_Printf(LogDEBUG, " Found %s -> %s (type %s)\n", nlink->ourhook, nlink->peerhook, nlink->nodeinfo.type); if (!strcmp(nlink->ourhook, lasthook)) { if (strcmp(nlink->peerhook, hook) || strcmp(nlink->nodeinfo.type, nodetype)) { log_Printf(LogWARN, "%s: hook %s:%s is already in use\n", p->link.name, nlink->ourhook, path); return ng_Abandon(dev, p); } /* The node is already hooked up nicely.... reuse it */ break; } } if (f == ninfo->hooks) { /* Attempt to load the module */ snprintf(modname, sizeof modname, "ng_%s", nodetype); log_Printf(LogDEBUG, "%s: Attempting to load %s.ko\n", p->link.name, modname); loadmodules(LOAD_QUIETLY, modname, NULL); /* Create (mkpeer) the new node */ snprintf(mkp.type, sizeof mkp.type, "%s", nodetype); snprintf(mkp.ourhook, sizeof mkp.ourhook, "%s", lasthook); snprintf(mkp.peerhook, sizeof mkp.peerhook, "%s", hook); log_Printf(LogDEBUG, "%s: Doing MKPEER %s%s -> %s (type %s)\n", p->link.name, path, mkp.ourhook, mkp.peerhook, nodetype); if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, NGM_MKPEER, &mkp, sizeof mkp) < 0) { log_Printf(LogWARN, "%s Cannot create %s netgraph node: %s\n", path, nodetype, strerror(errno)); return ng_Abandon(dev, p); } } len = strlen(path); snprintf(path + len, sizeof path - len, "%s%s", path[len - 1] == ':' ? "" : ".", lasthook); } /* Get a list of node hooks */ if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0) < 0) { log_Printf(LogWARN, "%s: %s Cannot send a LISTHOOOKS message: %s\n", p->link.name, path, strerror(errno)); return ng_Abandon(dev, p); } /* Get our list back */ resp = (struct ng_mesg *)rbuf; if (NgRecvMsg(dev->cs, resp, sizeof rbuf, NULL) <= 0) { log_Printf(LogWARN, "%s: Cannot get netgraph response: %s\n", p->link.name, strerror(errno)); return ng_Abandon(dev, p); } hlist = (const struct hooklist *)resp->data; ninfo = &hlist->nodeinfo; if (*lasthook != '\0' && nodename != NULL && *nodename != '\0' && strcmp(ninfo->name, nodename) && NgNameNode(dev->cs, path, "%s", nodename) < 0) { log_Printf(LogWARN, "%s: %s: Cannot name netgraph node: %s\n", p->link.name, path, strerror(errno)); return ng_Abandon(dev, p); } if (!GETSEGMENT(lasthook, devp, " \t.[", &endp)) return ng_Abandon(dev, p); log_Printf(LogDEBUG, "%s: Got hook \"%s\"\n", p->link.name, lasthook); len = strlen(lasthook); done = strchr(" \t", devp[len]) ? 1 : 0; devp = endp; if (*devp != '\0') { if (devp[-1] == '[') devp--; } /* else should moan about devp[-1] being '[' ? */ } snprintf(dev->hook, sizeof dev->hook, "%s", lasthook); /* Connect the node to our socket node */ snprintf(ngc.path, sizeof ngc.path, "%s", path); snprintf(ngc.ourhook, sizeof ngc.ourhook, "%s", dev->hook); memcpy(ngc.peerhook, ngc.ourhook, sizeof ngc.peerhook); log_Printf(LogDEBUG, "Connecting netgraph socket .:%s -> %s.%s\n", ngc.ourhook, ngc.path, ngc.peerhook); if (NgSendMsg(dev->cs, ".:", NGM_GENERIC_COOKIE, NGM_CONNECT, &ngc, sizeof ngc) < 0) { log_Printf(LogWARN, "Cannot connect %s and socket netgraph " "nodes: %s\n", path, strerror(errno)); return ng_Abandon(dev, p); } /* Hook things up so that we monitor dev->cs */ p->desc.UpdateSet = ng_UpdateSet; p->desc.IsSet = ng_IsSet; p->desc.Read = ng_DescriptorRead; memcpy(&dev->dev, &basengdevice, sizeof dev->dev); } else { /* See if we're a netgraph socket */ sz = sizeof ngsock; if (getsockname(p->fd, sock, &sz) != -1 && sock->sa_family == AF_NETGRAPH) { /* * It's a netgraph node... We can't determine hook names etc, so we * stay pretty impartial.... */ log_Printf(LogPHASE, "%s: Link is a netgraph node\n", p->link.name); if ((dev = malloc(sizeof *dev)) == NULL) { log_Printf(LogWARN, "%s: Cannot allocate an ether device: %s\n", p->link.name, strerror(errno)); return NULL; } memcpy(&dev->dev, &basengdevice, sizeof dev->dev); dev->cs = -1; *dev->hook = '\0'; } } if (dev) { physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF); return &dev->dev; } return NULL; }