void CcpNgCtrlEvent(int type, void *cookie) { Bund b = NULL; union { u_char buf[2048]; struct ng_mesg msg; } u; char raddr[NG_PATHSIZ]; int i, len; ng_ID_t id; /* Read message */ if ((len = NgRecvMsg(gCcpCsock, &u.msg, sizeof(u), raddr)) < 0) { Perror("CcpNgCtrlEvent: can't read message"); return; } if (sscanf(raddr, "[%x]:", &id) != 1) { Log(LG_ERR, ("CcpNgCtrlEvent: can't decode sender id: '%s'", raddr)); return; } for (i = 0; i < gNumBundles; i++) { if (gBundles[i] && !gBundles[i]->dead && gBundles[i]->ccp.decomp_node_id == id) { b = gBundles[i]; break; } } if (!b) return; /* Examine message */ switch (u.msg.header.typecookie) { #ifdef USE_NG_MPPC case NGM_MPPC_COOKIE: #endif #ifdef USE_NG_DEFLATE case NGM_DEFLATE_COOKIE: #endif #ifdef USE_NG_PRED1 case NGM_PRED1_COOKIE: #endif CcpRecvMsg(b, &u.msg, len); return; default: /* Unknown message */ Log(LG_ERR, ("CcpNgCtrlEvent: rec'd unknown ctrl message, cookie=%d cmd=%d", u.msg.header.typecookie, u.msg.header.cmd)); break; } }
/* * Receive a control message and convert the arguments to ASCII */ int NgRecvAsciiMsg(int cs, struct ng_mesg *reply, size_t replen, char *path) { struct ng_mesg *msg, *ascii; int bufSize, errnosv; u_char *buf; /* Allocate buffer */ bufSize = 2 * sizeof(*reply) + replen; if ((buf = malloc(bufSize)) == NULL) return (-1); msg = (struct ng_mesg *)buf; ascii = (struct ng_mesg *)msg->data; /* Get binary message */ if (NgRecvMsg(cs, msg, bufSize, path) < 0) goto fail; memcpy(reply, msg, sizeof(*msg)); /* Ask originating node to convert the arguments to ASCII */ if (NgSendMsg(cs, path, NGM_GENERIC_COOKIE, NGM_BINARY2ASCII, msg, sizeof(*msg) + msg->header.arglen) < 0) goto fail; if (NgRecvMsg(cs, msg, bufSize, NULL) < 0) goto fail; /* Copy result to client buffer */ if (sizeof(*ascii) + ascii->header.arglen > replen) { errno = ERANGE; fail: errnosv = errno; free(buf); errno = errnosv; return (-1); } strncpy(reply->data, ascii->data, ascii->header.arglen); /* Done */ free(buf); return (0); }
static int ConfigCmd(int ac, char **av) { u_char sbuf[sizeof(struct ng_mesg) + NG_TEXTRESPONSE]; struct ng_mesg *const resp = (struct ng_mesg *) sbuf; char *const status = (char *) resp->data; char *path; char buf[NG_TEXTRESPONSE]; int nostat = 0, i; /* Get arguments */ if (ac < 2) return (CMDRTN_USAGE); path = av[1]; *buf = '\0'; for (i = 2; i < ac; i++) { if (i != 2) strcat(buf, " "); strcat(buf, av[i]); } /* Get node config summary */ if (*buf != '\0') i = NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_TEXT_CONFIG, buf, strlen(buf) + 1); else i = NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_TEXT_CONFIG, NULL, 0); if (i < 0) { switch (errno) { case EINVAL: nostat = 1; break; default: warn("send msg"); return (CMDRTN_ERROR); } } else { if (NgRecvMsg(csock, resp, sizeof(sbuf), NULL) < 0 || (resp->header.flags & NGF_RESP) == 0) nostat = 1; } /* Show it */ if (nostat) printf("No config available for \"%s\"\n", path); else printf("Config for \"%s\":\n%s\n", path, status); return (CMDRTN_OK); }
/* * Identical to NgRecvMsg() except buffer is dynamically allocated. */ int NgAllocRecvMsg(int cs, struct ng_mesg **rep, char *path) { int len; socklen_t optlen; optlen = sizeof(len); if (getsockopt(cs, SOL_SOCKET, SO_RCVBUF, &len, &optlen) == -1 || (*rep = malloc(len)) == NULL) return (-1); if ((len = NgRecvMsg(cs, *rep, len, path)) < 0) free(*rep); return (len); }
static void do_show(int version, void (*func)(struct ngnf_show_header *)) { struct ng_mesg *ng_mesg; struct ngnf_show_header req, *resp; int token, nread; ng_mesg = alloca(SORCVBUF_SIZE); req.version = version; req.hash_id = req.list_id = 0; for (;;) { /* request set of accounting records */ token = NgSendMsg(cs, ng_path, NGM_NETFLOW_COOKIE, NGM_NETFLOW_SHOW, (void *)&req, sizeof(req)); if (token == -1) err(1, "NgSendMsg(NGM_NETFLOW_SHOW)"); /* read reply */ nread = NgRecvMsg(cs, ng_mesg, SORCVBUF_SIZE, NULL); if (nread == -1) err(1, "NgRecvMsg() failed"); if (ng_mesg->header.token != token) err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): token mismatch"); resp = (struct ngnf_show_header *)ng_mesg->data; if ((ng_mesg->header.arglen < (sizeof(*resp))) || (ng_mesg->header.arglen < (sizeof(*resp) + (resp->nentries * sizeof(struct flow_entry_data))))) err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): arglen too small"); (*func)(resp); if (resp->hash_id != 0) req.hash_id = resp->hash_id; else break; req.list_id = resp->list_id; } }
static int StatusCmd(int ac, char **av) { u_char sbuf[sizeof(struct ng_mesg) + NG_TEXTRESPONSE]; struct ng_mesg *const resp = (struct ng_mesg *) sbuf; char *const status = (char *) resp->data; char *path; int nostat = 0; /* Get arguments */ switch (ac) { case 2: path = av[1]; break; default: return (CMDRTN_USAGE); } /* Get node status summary */ if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_TEXT_STATUS, NULL, 0) < 0) { switch (errno) { case EINVAL: nostat = 1; break; default: warn("send msg"); return (CMDRTN_ERROR); } } else { if (NgRecvMsg(csock, resp, sizeof(sbuf), NULL) < 0 || (resp->header.flags & NGF_RESP) == 0) nostat = 1; } /* Show it */ if (nostat) printf("No status available for \"%s\"\n", path); else printf("Status for \"%s\":\n%s\n", path, status); return (CMDRTN_OK); }
/* * Input from the control socket. */ static struct ng_mesg * csock_read(char *path) { struct ng_mesg *mesg; int ret, err; if ((mesg = malloc(resbufsiz + 1)) == NULL) { stats[LEAF_begemotNgNoMems]++; syslog(LOG_CRIT, "out of memory"); errno = ENOMEM; return (NULL); } if ((ret = NgRecvMsg(csock, mesg, resbufsiz + 1, path)) < 0) { err = errno; free(mesg); if (errno == EWOULDBLOCK) { errno = err; return (NULL); } stats[LEAF_begemotNgMsgReadErrs]++; syslog(LOG_WARNING, "read from csock: %m"); errno = err; return (NULL); } if (ret == 0) { syslog(LOG_DEBUG, "node closed -- exiting"); exit(0); } if ((size_t)ret > resbufsiz) { stats[LEAF_begemotNgTooLargeMsgs]++; syslog(LOG_WARNING, "ng message too large"); free(mesg); errno = EFBIG; return (NULL); } return (mesg); }
/* * Retrieve hook or database info */ static int ip_account_get_info(int type, void *buf, int blen, int outgoing) { int token, error; struct ng_mesg *ng_mesg; char path[NG_PATHLEN + 1]; /* send control message */ if ((token = ip_account_ctl(type, outgoing)) == -1) return (-1); /* read reply */ if (ng_ready_for_read() != 1) return (-1); ng_mesg = alloca(sizeof(*ng_mesg) + blen); error = NgRecvMsg(ng_cs, ng_mesg, (sizeof(*ng_mesg) + blen), path); if (error == -1) { warn("NgRecvMsg(NGM_IPACCT_xINFO)"); return (-1); } if (ng_mesg->header.token != token) { warnx("NgRecvMsg(NGM_IPACCT_xINFO): token mismatch"); return (-1); } if (ng_mesg->header.arglen < blen) { warnx("NgRecvMsg(NGM_IPACCT_xINFO): arglen too small, " "arglen = %d, blen = %d", ng_mesg->header.arglen, blen); return (-1); } bcopy(ng_mesg->data, buf, blen); return (0); }
static void ether_MessageIn(struct etherdevice *dev) { char msgbuf[sizeof(struct ng_mesg) + sizeof(struct ngpppoe_sts)]; struct ng_mesg *rep = (struct ng_mesg *)msgbuf; struct ngpppoe_sts *sts = (struct ngpppoe_sts *)(msgbuf + sizeof *rep); char *end, unknown[14], sessionid[5]; const char *msg; struct timeval t; fd_set *r; u_long slot; int asciilen, ret; if (dev->cs < 0) return; if ((r = mkfdset()) == NULL) { log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n"); return; } while (1) { zerofdset(r); FD_SET(dev->cs, r); t.tv_sec = t.tv_usec = 0; ret = select(dev->cs + 1, r, NULL, NULL, &t); if (ret <= 0) break; if (NgRecvMsg(dev->cs, rep, sizeof msgbuf, NULL) <= 0) break; if (rep->header.version != NG_VERSION) { log_Printf(LogWARN, "%ld: Unexpected netgraph version, expected %ld\n", (long)rep->header.version, (long)NG_VERSION); break; } if (rep->header.typecookie != NGM_PPPOE_COOKIE) { log_Printf(LogWARN, "%ld: Unexpected netgraph cookie, expected %ld\n", (long)rep->header.typecookie, (long)NGM_PPPOE_COOKIE); break; } asciilen = 0; switch (rep->header.cmd) { case NGM_PPPOE_SET_FLAG: msg = "SET_FLAG"; break; case NGM_PPPOE_CONNECT: msg = "CONNECT"; break; case NGM_PPPOE_LISTEN: msg = "LISTEN"; break; case NGM_PPPOE_OFFER: msg = "OFFER"; break; case NGM_PPPOE_SUCCESS: msg = "SUCCESS"; break; case NGM_PPPOE_FAIL: msg = "FAIL"; break; case NGM_PPPOE_CLOSE: msg = "CLOSE"; break; case NGM_PPPOE_GET_STATUS: msg = "GET_STATUS"; break; case NGM_PPPOE_ACNAME: msg = "ACNAME"; if (setenv("ACNAME", sts->hook, 1) != 0) log_Printf(LogWARN, "setenv: cannot set ACNAME=%s: %m", sts->hook); asciilen = rep->header.arglen; break; case NGM_PPPOE_SESSIONID: msg = "SESSIONID"; snprintf(sessionid, sizeof sessionid, "%04x", *(u_int16_t *)sts); if (setenv("SESSIONID", sessionid, 1) != 0) syslog(LOG_WARNING, "setenv: cannot set SESSIONID=%s: %m", sessionid); /* Use this in preference to our interface index */ slot = strtoul(sessionid, &end, 16); if (end != sessionid && *end == '\0') dev->slot = slot; break; default: snprintf(unknown, sizeof unknown, "<%d>", (int)rep->header.cmd); msg = unknown; break; } if (asciilen) log_Printf(LogPHASE, "Received NGM_PPPOE_%s (hook \"%.*s\")\n", msg, asciilen, sts->hook); else log_Printf(LogPHASE, "Received NGM_PPPOE_%s\n", msg); switch (rep->header.cmd) { case NGM_PPPOE_SUCCESS: dev->connected = CARRIER_OK; break; case NGM_PPPOE_FAIL: case NGM_PPPOE_CLOSE: dev->connected = CARRIER_LOST; break; } } free(r); }
/* * Create a socket type node and give it the supplied name. * Return data and control sockets corresponding to the node. * Returns -1 if error and sets errno. */ int NgMkSockNode(const char *name, int *csp, int *dsp) { char namebuf[NG_NODESIZ]; int cs = -1; /* control socket */ int ds = -1; /* data socket */ int errnosv; /* Empty name means no name */ if (name && *name == 0) name = NULL; /* Create control socket; this also creates the netgraph node. If we get an EPROTONOSUPPORT then the socket node type is not loaded, so load it and try again. */ if ((cs = socket(AF_NETGRAPH, SOCK_DGRAM, NG_CONTROL)) < 0) { if (errno == EPROTONOSUPPORT) { if (kldload(NG_SOCKET_KLD) < 0) { errnosv = errno; if (_gNgDebugLevel >= 1) NGLOG("can't load %s", NG_SOCKET_KLD); goto errout; } cs = socket(AF_NETGRAPH, SOCK_DGRAM, NG_CONTROL); if (cs >= 0) goto gotNode; } errnosv = errno; if (_gNgDebugLevel >= 1) NGLOG("socket"); goto errout; } gotNode: /* Assign the node the desired name, if any */ if (name != NULL) { u_char sbuf[NG_NODESIZ + NGSA_OVERHEAD]; struct sockaddr_ng *const sg = (struct sockaddr_ng *) sbuf; /* Assign name */ strlcpy(sg->sg_data, name, NG_NODESIZ); sg->sg_family = AF_NETGRAPH; sg->sg_len = strlen(sg->sg_data) + 1 + NGSA_OVERHEAD; if (bind(cs, (struct sockaddr *) sg, sg->sg_len) < 0) { errnosv = errno; if (_gNgDebugLevel >= 1) NGLOG("bind(%s)", sg->sg_data); goto errout; } /* Save node name */ strlcpy(namebuf, name, sizeof(namebuf)); } else if (dsp != NULL) { u_char rbuf[sizeof(struct ng_mesg) + sizeof(struct nodeinfo)]; struct ng_mesg *const resp = (struct ng_mesg *) rbuf; struct nodeinfo *const ni = (struct nodeinfo *) resp->data; /* Find out the node ID */ if (NgSendMsg(cs, ".", NGM_GENERIC_COOKIE, NGM_NODEINFO, NULL, 0) < 0) { errnosv = errno; if (_gNgDebugLevel >= 1) NGLOG("send nodeinfo"); goto errout; } if (NgRecvMsg(cs, resp, sizeof(rbuf), NULL) < 0) { errnosv = errno; if (_gNgDebugLevel >= 1) NGLOG("recv nodeinfo"); goto errout; } /* Save node "name" */ snprintf(namebuf, sizeof(namebuf), "[%lx]", (u_long) ni->id); } /* Create data socket if desired */ if (dsp != NULL) { u_char sbuf[NG_NODESIZ + 1 + NGSA_OVERHEAD]; struct sockaddr_ng *const sg = (struct sockaddr_ng *) sbuf; /* Create data socket, initially just "floating" */ if ((ds = socket(AF_NETGRAPH, SOCK_DGRAM, NG_DATA)) < 0) { errnosv = errno; if (_gNgDebugLevel >= 1) NGLOG("socket"); goto errout; } /* Associate the data socket with the node */ snprintf(sg->sg_data, NG_NODESIZ + 1, "%s:", namebuf); sg->sg_family = AF_NETGRAPH; sg->sg_len = strlen(sg->sg_data) + 1 + NGSA_OVERHEAD; if (connect(ds, (struct sockaddr *) sg, sg->sg_len) < 0) { errnosv = errno; if (_gNgDebugLevel >= 1) NGLOG("connect(%s)", sg->sg_data); goto errout; } } /* Return the socket(s) */ if (csp) *csp = cs; else close(cs); if (dsp) *dsp = ds; return (0); errout: /* Failed */ if (cs >= 0) close(cs); if (ds >= 0) close(ds); errno = errnosv; return (-1); }
void netgraphprotopr(u_long off, const char *name, int af1 __unused, int proto __unused) { struct ngpcb *this, *next; struct ngpcb ngpcb; struct socket sockb; int debug = 1; /* If symbol not found, try looking in the KLD module */ if (off == 0) { if (debug) fprintf(stderr, "Error reading symbols from ng_socket.ko"); return; } /* Get pointer to first socket */ kread(off, (char *)&this, sizeof(this)); /* Get my own socket node */ if (csock == -1) NgMkSockNode(NULL, &csock, NULL); for (; this != NULL; this = next) { u_char rbuf[sizeof(struct ng_mesg) + sizeof(struct nodeinfo)]; struct ng_mesg *resp = (struct ng_mesg *) rbuf; struct nodeinfo *ni = (struct nodeinfo *) resp->data; char path[64]; /* Read in ngpcb structure */ kread((u_long)this, (char *)&ngpcb, sizeof(ngpcb)); next = LIST_NEXT(&ngpcb, socks); /* Read in socket structure */ kread((u_long)ngpcb.ng_socket, (char *)&sockb, sizeof(sockb)); /* Check type of socket */ if (strcmp(name, "ctrl") == 0 && ngpcb.type != NG_CONTROL) continue; if (strcmp(name, "data") == 0 && ngpcb.type != NG_DATA) continue; /* Do headline */ if (first) { printf("Netgraph sockets\n"); if (Aflag) printf("%-8.8s ", "PCB"); printf("%-5.5s %-6.6s %-6.6s %-14.14s %s\n", "Type", "Recv-Q", "Send-Q", "Node Address", "#Hooks"); first = 0; } /* Show socket */ if (Aflag) printf("%8lx ", (u_long) this); printf("%-5.5s %6u %6u ", name, sockb.so_rcv.sb_cc, sockb.so_snd.sb_cc); /* Get info on associated node */ if (ngpcb.node_id == 0 || csock == -1) goto finish; snprintf(path, sizeof(path), "[%x]:", ngpcb.node_id); if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_NODEINFO, NULL, 0) < 0) goto finish; if (NgRecvMsg(csock, resp, sizeof(rbuf), NULL) < 0) goto finish; /* Display associated node info */ if (*ni->name != '\0') snprintf(path, sizeof(path), "%s:", ni->name); printf("%-14.14s %4d", path, ni->hooks); finish: putchar('\n'); } }
static int ConfigureNode(const char *prog, const char *iface, const char *provider, int cs, int ds, int debug, struct ngm_connect *ngc) { /* * We're going to do this with the passed `ds' & `cs' descriptors: * * .---------. * | ether | * | <iface> | * `---------' * (orphan) ds cs * | | | * | | | * (ethernet) | | * .---------. .-----------. * | pppoe | | socket | * | <iface> |(pppoe-<pid>)<---->(pppoe-<pid>)| <unnamed> | * `--------- `-----------' * (exec-<pid>) * ^ .-----------. .-------------. * | | socket | | ppp -direct | * `--->(exec-<pid>)| <unnamed> |--fd--| provider | * `-----------' `-------------' * * where there are potentially many ppp processes running off of the * same PPPoE node. * The exec-<pid> hook isn't made 'till we Spawn(). */ char *epath, *spath; struct ngpppoe_init_data *data; const struct hooklist *hlist; const struct nodeinfo *ninfo; const struct linkinfo *nlink; struct ngm_mkpeer mkp; struct ng_mesg *resp; u_char rbuf[2048]; int f, plen; /* * 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 */ epath = (char *)alloca(strlen(iface) + 2); sprintf(epath, "%s:", iface); if (debug) fprintf(stderr, "Sending NGM_LISTHOOKS to %s\n", epath); if (NgSendMsg(cs, epath, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0) < 0) { if (errno == ENOENT) fprintf(stderr, "%s Cannot send a netgraph message: Invalid interface\n", epath); else fprintf(stderr, "%s Cannot send a netgraph message: %s\n", epath, strerror(errno)); return EX_UNAVAILABLE; } /* Get our list back */ resp = (struct ng_mesg *)rbuf; if (NgRecvMsg(cs, resp, sizeof rbuf, NULL) <= 0) { perror("Cannot get netgraph response"); return EX_UNAVAILABLE; } hlist = (const struct hooklist *)resp->data; ninfo = &hlist->nodeinfo; if (debug) fprintf(stderr, "Got reply from id [%x]: Type %s with %d hooks\n", ninfo->id, ninfo->type, ninfo->hooks); /* Make sure we've got the right type of node */ if (strncmp(ninfo->type, NG_ETHER_NODE_TYPE, sizeof NG_ETHER_NODE_TYPE - 1)) { fprintf(stderr, "%s Unexpected node type ``%s'' (wanted ``" NG_ETHER_NODE_TYPE "'')\n", epath, ninfo->type); return EX_DATAERR; } /* look for a hook already attached. */ for (f = 0; f < ninfo->hooks; f++) { nlink = &hlist->link[f]; if (debug) fprintf(stderr, " Got [%x]:%s -> [%x]:%s\n", ninfo->id, nlink->ourhook, nlink->nodeinfo.id, 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)) { fprintf(stderr, "%s Node type %s is currently active\n", epath, nlink->nodeinfo.type); return EX_UNAVAILABLE; } break; } } if (f == ninfo->hooks) { /* * Create a new PPPoE node connected to the `ether' node using * the magic `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); if (debug) fprintf(stderr, "Send MKPEER: %s%s -> [type %s]:%s\n", epath, mkp.ourhook, mkp.type, mkp.peerhook); if (NgSendMsg(cs, epath, NGM_GENERIC_COOKIE, NGM_MKPEER, &mkp, sizeof mkp) < 0) { fprintf(stderr, "%s Cannot create a peer PPPoE node: %s\n", epath, strerror(errno)); return EX_OSERR; } } /* Connect the PPPoE node to our socket node. */ snprintf(ngc->path, sizeof ngc->path, "%s%s", epath, NG_ETHER_HOOK_ORPHAN); snprintf(ngc->ourhook, sizeof ngc->ourhook, "pppoe-%ld", (long)getpid()); memcpy(ngc->peerhook, ngc->ourhook, sizeof ngc->peerhook); if (NgSendMsg(cs, ".:", NGM_GENERIC_COOKIE, NGM_CONNECT, ngc, sizeof *ngc) < 0) { perror("Cannot CONNECT PPPoE and socket nodes"); return EX_OSERR; } plen = strlen(provider); data = (struct ngpppoe_init_data *)alloca(sizeof *data + plen); snprintf(data->hook, sizeof data->hook, "%s", ngc->peerhook); memcpy(data->data, provider, plen); data->data_len = plen; spath = (char *)alloca(strlen(ngc->peerhook) + 3); strcpy(spath, ".:"); strcpy(spath + 2, ngc->ourhook); if (debug) { if (provider) fprintf(stderr, "Sending PPPOE_LISTEN to %s, provider %s\n", spath, provider); else fprintf(stderr, "Sending PPPOE_LISTEN to %s\n", spath); } if (NgSendMsg(cs, spath, NGM_PPPOE_COOKIE, NGM_PPPOE_LISTEN, data, sizeof *data + plen) == -1) { fprintf(stderr, "%s: Cannot LISTEN on netgraph node: %s\n", spath, strerror(errno)); return EX_OSERR; } return 0; }
static void Spawn(const char *prog, const char *acname, const char *provider, const char *exec, struct ngm_connect ngc, int cs, int ds, void *request, int sz, int debug) { char msgbuf[sizeof(struct ng_mesg) + sizeof(struct ngpppoe_sts)]; struct ng_mesg *rep = (struct ng_mesg *)msgbuf; struct ngpppoe_sts *sts = (struct ngpppoe_sts *)(msgbuf + sizeof *rep); struct ngpppoe_init_data *data; char env[18], unknown[14], sessionid[5], *path; unsigned char *macaddr; const char *msg; int ret, slen; switch ((ret = fork())) { case -1: syslog(LOG_ERR, "fork: %m"); break; case 0: switch (fork()) { case 0: break; case -1: _exit(errno); default: _exit(0); } close(cs); close(ds); /* Create a new socket node */ if (debug) syslog(LOG_INFO, "Creating a new socket node"); if (NgMkSockNode(NULL, &cs, &ds) == -1) { syslog(LOG_ERR, "Cannot create netgraph socket node: %m"); _exit(EX_CANTCREAT); } /* Connect the PPPoE node to our new socket node. */ snprintf(ngc.ourhook, sizeof ngc.ourhook, "exec-%ld", (long)getpid()); memcpy(ngc.peerhook, ngc.ourhook, sizeof ngc.peerhook); if (debug) syslog(LOG_INFO, "Sending CONNECT from .:%s -> %s.%s", ngc.ourhook, ngc.path, ngc.peerhook); if (NgSendMsg(cs, ".:", NGM_GENERIC_COOKIE, NGM_CONNECT, &ngc, sizeof ngc) < 0) { syslog(LOG_ERR, "Cannot CONNECT PPPoE and socket nodes: %m"); _exit(EX_OSERR); } /* * If we tell the socket node not to LINGER, it will go away when * the last hook is removed. */ if (debug) syslog(LOG_INFO, "Sending NGM_SOCK_CMD_NOLINGER to socket"); if (NgSendMsg(cs, ".:", NGM_SOCKET_COOKIE, NGM_SOCK_CMD_NOLINGER, NULL, 0) < 0) { syslog(LOG_ERR, "Cannot send NGM_SOCK_CMD_NOLINGER: %m"); _exit(EX_OSERR); } /* Put the PPPoE node into OFFER mode */ slen = strlen(acname); data = (struct ngpppoe_init_data *)alloca(sizeof *data + slen); snprintf(data->hook, sizeof data->hook, "%s", ngc.ourhook); memcpy(data->data, acname, slen); data->data_len = slen; path = (char *)alloca(strlen(ngc.ourhook) + 3); strcpy(path, ".:"); strcpy(path + 2, ngc.ourhook); syslog(LOG_INFO, "Offering to %s as access concentrator %s", path, acname); if (NgSendMsg(cs, path, NGM_PPPOE_COOKIE, NGM_PPPOE_OFFER, data, sizeof *data + slen) == -1) { syslog(LOG_INFO, "%s: Cannot OFFER on netgraph node: %m", path); _exit(EX_OSERR); } /* If we have a provider code, set it */ if (provider) { slen = strlen(provider); data = (struct ngpppoe_init_data *)alloca(sizeof *data + slen); snprintf(data->hook, sizeof data->hook, "%s", ngc.ourhook); memcpy(data->data, provider, slen); data->data_len = slen; syslog(LOG_INFO, "adding to %s as offered service %s", path, acname); if (NgSendMsg(cs, path, NGM_PPPOE_COOKIE, NGM_PPPOE_SERVICE, data, sizeof *data + slen) == -1) { syslog(LOG_INFO, "%s: Cannot add service on netgraph node: %m", path); _exit(EX_OSERR); } } /* Put the peer's MAC address in the environment */ if (sz >= sizeof(struct ether_header)) { macaddr = ((struct ether_header *)request)->ether_shost; snprintf(env, sizeof(env), "%x:%x:%x:%x:%x:%x", macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]); if (setenv(HISMACADDR, env, 1) != 0) syslog(LOG_INFO, "setenv: cannot set %s: %m", HISMACADDR); } /* And send our request data to the waiting node */ if (debug) syslog(LOG_INFO, "Sending original request to %s (%d bytes)", path, sz); if (NgSendData(ds, ngc.ourhook, request, sz) == -1) { syslog(LOG_ERR, "Cannot send original request to %s: %m", path); _exit(EX_OSERR); } /* Then wait for a success indication */ if (debug) syslog(LOG_INFO, "Waiting for a SUCCESS reply %s", path); do { if ((ret = NgRecvMsg(cs, rep, sizeof msgbuf, NULL)) < 0) { syslog(LOG_ERR, "%s: Cannot receive a message: %m", path); _exit(EX_OSERR); } if (ret == 0) { /* The socket has been closed */ syslog(LOG_INFO, "%s: Client timed out", path); _exit(EX_TEMPFAIL); } if (rep->header.version != NG_VERSION) { syslog(LOG_ERR, "%ld: Unexpected netgraph version, expected %ld", (long)rep->header.version, (long)NG_VERSION); _exit(EX_PROTOCOL); } if (rep->header.typecookie != NGM_PPPOE_COOKIE) { syslog(LOG_INFO, "%ld: Unexpected netgraph cookie, expected %ld", (long)rep->header.typecookie, (long)NGM_PPPOE_COOKIE); continue; } switch (rep->header.cmd) { case NGM_PPPOE_SET_FLAG: msg = "SET_FLAG"; break; case NGM_PPPOE_CONNECT: msg = "CONNECT"; break; case NGM_PPPOE_LISTEN: msg = "LISTEN"; break; case NGM_PPPOE_OFFER: msg = "OFFER"; break; case NGM_PPPOE_SUCCESS: msg = "SUCCESS"; break; case NGM_PPPOE_FAIL: msg = "FAIL"; break; case NGM_PPPOE_CLOSE: msg = "CLOSE"; break; case NGM_PPPOE_GET_STATUS: msg = "GET_STATUS"; break; case NGM_PPPOE_ACNAME: msg = "ACNAME"; if (setenv("ACNAME", sts->hook, 1) != 0) syslog(LOG_WARNING, "setenv: cannot set ACNAME=%s: %m", sts->hook); break; case NGM_PPPOE_SESSIONID: msg = "SESSIONID"; snprintf(sessionid, sizeof sessionid, "%04x", *(u_int16_t *)sts); if (setenv("SESSIONID", sessionid, 1) != 0) syslog(LOG_WARNING, "setenv: cannot set SESSIONID=%s: %m", sessionid); break; default: snprintf(unknown, sizeof unknown, "<%d>", (int)rep->header.cmd); msg = unknown; break; } switch (rep->header.cmd) { case NGM_PPPOE_FAIL: case NGM_PPPOE_CLOSE: syslog(LOG_ERR, "Received NGM_PPPOE_%s (hook \"%s\")", msg, sts->hook); _exit(0); } syslog(LOG_INFO, "Received NGM_PPPOE_%s (hook \"%s\")", msg, sts->hook); } while (rep->header.cmd != NGM_PPPOE_SUCCESS); dup2(ds, STDIN_FILENO); dup2(ds, STDOUT_FILENO); close(ds); close(cs); setsid(); syslog(LOG_INFO, "Executing: %s", exec); execlp(_PATH_BSHELL, _PATH_BSHELL, "-c", exec, (char *)NULL); syslog(LOG_ERR, "execlp failed: %m"); _exit(EX_OSFILE); default: wait(&ret); errno = ret; if (errno) syslog(LOG_ERR, "Second fork failed: %m"); break; } }
/* * Display a negraph message */ void _NgDebugMsg(const struct ng_mesg *msg, const char *path) { u_char buf[2 * sizeof(struct ng_mesg) + ARGS_BUFSIZE]; struct ng_mesg *const req = (struct ng_mesg *)buf; struct ng_mesg *const bin = (struct ng_mesg *)req->data; int arglen, csock = -1; /* Display header stuff */ NGLOGX("NG_MESG :"); NGLOGX(" vers %d", msg->header.version); NGLOGX(" arglen %d", msg->header.arglen); NGLOGX(" flags %u", msg->header.flags); NGLOGX(" token %u", msg->header.token); NGLOGX(" cookie %s (%d)", NgCookie(msg->header.typecookie), msg->header.typecookie); /* At lower debugging levels, skip ASCII translation */ if (_gNgDebugLevel <= 2) goto fail2; /* If path is not absolute, don't bother trying to use relative address on a different socket for the ASCII translation */ if (strchr(path, ':') == NULL) goto fail2; /* Get a temporary socket */ if (NgMkSockNode(NULL, &csock, NULL) < 0) goto fail; /* Copy binary message into request message payload */ arglen = msg->header.arglen; if (arglen > ARGS_BUFSIZE) arglen = ARGS_BUFSIZE; memcpy(bin, msg, sizeof(*msg) + arglen); bin->header.arglen = arglen; /* Lower debugging to avoid infinite recursion */ _gNgDebugLevel -= RECURSIVE_DEBUG_ADJUST; /* Ask the node to translate the binary message to ASCII for us */ if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_BINARY2ASCII, bin, sizeof(*bin) + bin->header.arglen) < 0) { _gNgDebugLevel += RECURSIVE_DEBUG_ADJUST; goto fail; } if (NgRecvMsg(csock, req, sizeof(buf), NULL) < 0) { _gNgDebugLevel += RECURSIVE_DEBUG_ADJUST; goto fail; } /* Restore debugging level */ _gNgDebugLevel += RECURSIVE_DEBUG_ADJUST; /* Display command string and arguments */ NGLOGX(" cmd %s (%d)", bin->header.cmdstr, bin->header.cmd); NGLOGX(" args %s", bin->data); goto done; fail: /* Just display binary version */ NGLOGX(" [error decoding message: %s]", strerror(errno)); fail2: NGLOGX(" cmd %d", msg->header.cmd); NGLOGX(" args (%d bytes)", msg->header.arglen); _NgDebugBytes(msg->data, msg->header.arglen); done: if (csock != -1) (void)close(csock); }
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; }
static int ip_account_show(int v, int outgoing) { struct ng_ipacct_ainfo ci; struct ng_ipacct_hinfo hi; int token, nread; struct ng_mesg *ng_mesg; struct ng_ipacct_mesg *ng_cmesg; struct ip_acct_chunk *data; char path[NG_PATHLEN + 1]; ng_cmesg = alloca(sizeof(*ng_cmesg)); snprintf(ng_cmesg->hname, sizeof(ng_cmesg->hname), "%s_%s", ng_hookprefix, outgoing ? "out" : "in"); ng_mesg = alloca(REPL_SIZE); if (ip_account_get_info(NGM_IPACCT_CINFO, &ci, sizeof(ci), outgoing)) { return (-1); } if (ip_account_get_info(NGM_IPACCT_HINFO, &hi, sizeof(hi), outgoing)) { return (-1); } for (;;) { /* request set of accounting records */ token = NgSendMsg(ng_cs, ng_nodename, NGM_IPACCT_COOKIE, NGM_IPACCT_SHOW, ng_cmesg, sizeof(*ng_cmesg)); if (token == -1) { warn("NgSendMsg(NGM_IPACCT_SHOW)"); return (-1); } /* read reply */ if (ng_ready_for_read() != 1) return (-1); nread = NgRecvMsg(ng_cs, ng_mesg, REPL_SIZE, path); if (nread == -1) { warn("NgRecvMsg(NG_IPACCT_SHOW)"); return (-1); } if (ng_mesg->header.token != token) { warnx("NgRecvMsg(NGM_IPACCT_SHOW): token mismatch"); return (-1); } data = (struct ip_acct_chunk*)ng_mesg->data; if (ng_mesg->header.arglen != sizeof(*data)) { warnx("NgRecvMsg(NGM_IPACCT_SHOW): arglen too small"); return (-1); } if (data->nrecs == 0) break; /* no more data available */ ip_account_print(data, hi.hi_flags); } if (ci.ai_th_packets) printf(" Accounting exceed threshold by %u packets (%" PRIu64 " bytes)\n", ci.ai_th_packets, ci.ai_th_bytes); return (0); }
static int ShowCmd(int ac, const char **av) { const char *path; u_char rbuf[16 * 1024]; struct ng_mesg *const resp = (struct ng_mesg *) rbuf; struct hooklist *const hlist = (struct hooklist *) resp->data; struct nodeinfo *const ninfo = &hlist->nodeinfo; int ch, no_hooks = 0; /* Get options */ optind = 1; while ((ch = getopt(ac, __DECONST(char **, av), "n")) != -1) { switch (ch) { case 'n': no_hooks = 1; break; case '?': default: return(CMDRTN_USAGE); break; } } ac -= optind; av += optind; /* Get arguments */ switch (ac) { case 1: path = av[0]; break; default: return(CMDRTN_USAGE); } /* Get node info and hook list */ if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0) < 0) { warn("send msg"); return(CMDRTN_ERROR); } if (NgRecvMsg(csock, resp, sizeof(rbuf), NULL) < 0) { warn("recv msg"); return(CMDRTN_ERROR); } /* Show node information */ if (!*ninfo->name) snprintf(ninfo->name, sizeof(ninfo->name), "%s", UNNAMED); printf(" Name: %-15s Type: %-15s ID: %08x Num hooks: %d\n", ninfo->name, ninfo->type, ninfo->id, ninfo->hooks); if (!no_hooks && ninfo->hooks > 0) { uint32_t k; printf(FMT, "Local hook", "Peer name", "Peer type", "Peer ID", "Peer hook"); printf(FMT, "----------", "---------", "---------", "-------", "---------"); for (k = 0; k < ninfo->hooks; k++) { struct linkinfo *const sc_link = &hlist->link[k]; struct nodeinfo *const peer = &hlist->link[k].nodeinfo; char idbuf[20]; if (!*peer->name) { snprintf(peer->name, sizeof(peer->name), "%s", UNNAMED); } snprintf(idbuf, sizeof(idbuf), "%08x", peer->id); printf(FMT, sc_link->ourhook, peer->name, peer->type, idbuf, sc_link->peerhook); } } return(CMDRTN_OK); }
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; }