ether.c revision 68032
16059Samurai/*- 26059Samurai * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org> 36059Samurai * All rights reserved. 46059Samurai * 56059Samurai * Redistribution and use in source and binary forms, with or without 66059Samurai * modification, are permitted provided that the following conditions 76059Samurai * are met: 86059Samurai * 1. Redistributions of source code must retain the above copyright 96059Samurai * notice, this list of conditions and the following disclaimer. 106059Samurai * 2. Redistributions in binary form must reproduce the above copyright 116059Samurai * notice, this list of conditions and the following disclaimer in the 126059Samurai * documentation and/or other materials provided with the distribution. 136059Samurai * 146059Samurai * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 156059Samurai * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 166059Samurai * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 176059Samurai * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 186059Samurai * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 198857Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2050479Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 218857Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 226059Samurai * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 236059Samurai * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2443313Sbrian * SUCH DAMAGE. 2530715Sbrian * 2636285Sbrian * $FreeBSD: head/usr.sbin/ppp/ether.c 68032 2000-10-31 02:46:12Z brian $ 2736285Sbrian */ 2836285Sbrian 2930715Sbrian#include <sys/param.h> 3043888Sbrian#include <sys/socket.h> 3143888Sbrian#include <sys/un.h> 3237192Sbrian#include <netinet/in.h> 3336287Sbrian#include <arpa/inet.h> 3437192Sbrian#include <netdb.h> 3530715Sbrian#include <netgraph.h> 3643888Sbrian#include <net/ethernet.h> 3743888Sbrian#include <netinet/in_systm.h> 3849976Sbrian#include <netinet/ip.h> 3930715Sbrian#include <netgraph/ng_ether.h> 4044106Sbrian#include <netgraph/ng_message.h> 4143888Sbrian#include <netgraph/ng_pppoe.h> 4236285Sbrian#include <netgraph/ng_socket.h> 4343888Sbrian 4429840Sbrian#include <errno.h> 4546686Sbrian#include <stdio.h> 4630715Sbrian#include <stdlib.h> 4730715Sbrian#include <string.h> 4830715Sbrian#include <sysexits.h> 4930715Sbrian#include <sys/fcntl.h> 506059Samurai#if defined(__FreeBSD__) && !defined(NOKLDLOAD) 5146686Sbrian#include <sys/linker.h> 526059Samurai#include <sys/module.h> 5336285Sbrian#endif 546059Samurai#include <sys/uio.h> 556735Samurai#include <termios.h> 5636285Sbrian#include <sys/time.h> 5736285Sbrian#include <unistd.h> 5836285Sbrian 5943888Sbrian#include "layer.h" 6036285Sbrian#include "defs.h" 6136285Sbrian#include "mbuf.h" 6236285Sbrian#include "log.h" 6336285Sbrian#include "timer.h" 6436285Sbrian#include "lqr.h" 6536285Sbrian#include "hdlc.h" 6636285Sbrian#include "throughput.h" 6736285Sbrian#include "fsm.h" 6843313Sbrian#include "lcp.h" 6943313Sbrian#include "ccp.h" 7043313Sbrian#include "link.h" 7136285Sbrian#include "async.h" 7236285Sbrian#include "descriptor.h" 7338174Sbrian#include "physical.h" 7443888Sbrian#include "main.h" 7536285Sbrian#include "mp.h" 7637192Sbrian#include "chat.h" 7736287Sbrian#include "auth.h" 7837192Sbrian#include "chap.h" 796059Samurai#include "cbcp.h" 8031343Sbrian#include "datalink.h" 8119866Sphk#include "slcompress.h" 826059Samurai#include "iplist.h" 8343693Sbrian#include "ipcp.h" 846059Samurai#include "filter.h" 8530715Sbrian#ifndef NORADIUS 8636285Sbrian#include "radius.h" 8743693Sbrian#endif 886059Samurai#include "bundle.h" 896059Samurai#include "id.h" 906059Samurai#include "ether.h" 916059Samurai 926059Samurai 9328679Sbrian#define PPPOE_NODE_TYPE_LEN (sizeof NG_PPPOE_NODE_TYPE - 1) /* "PPPoE" */ 946059Samurai 956059Samuraistruct etherdevice { 966059Samurai struct device dev; /* What struct physical knows about */ 9754912Sbrian int cs; /* Control socket */ 9830715Sbrian int connected; /* Are we connected yet ? */ 996059Samurai int timeout; /* Seconds attempting to connect */ 10030715Sbrian char hook[sizeof TUN_NAME + 11]; /* Our socket node hook */ 10136285Sbrian}; 10237926Sbrian 10337926Sbrian#define device2ether(d) \ 10437926Sbrian ((d)->type == ETHER_DEVICE ? (struct etherdevice *)d : NULL) 10537926Sbrian 10646686Sbrianint 10750867Sbrianether_DeviceSize(void) 1086059Samurai{ 1096059Samurai return sizeof(struct etherdevice); 11043693Sbrian} 11144123Sbrian 11244123Sbrianstatic ssize_t 11344123Sbrianether_Write(struct physical *p, const void *v, size_t n) 11444123Sbrian{ 11544123Sbrian struct etherdevice *dev = device2ether(p->handler); 1166059Samurai 11743693Sbrian return NgSendData(p->fd, dev->hook, v, n) == -1 ? -1 : n; 11843693Sbrian} 11943693Sbrian 12043693Sbrianstatic ssize_t 12143693Sbrianether_Read(struct physical *p, void *v, size_t n) 12243693Sbrian{ 12343693Sbrian char hook[sizeof TUN_NAME + 11]; 12444106Sbrian 12543693Sbrian return NgRecvData(p->fd, v, n, hook); 12643693Sbrian} 12743693Sbrian 12843693Sbrianstatic int 12943693Sbrianether_RemoveFromSet(struct physical *p, fd_set *r, fd_set *w, fd_set *e) 13043693Sbrian{ 13143693Sbrian struct etherdevice *dev = device2ether(p->handler); 13244106Sbrian int result; 13344106Sbrian 13444106Sbrian if (r && dev->cs >= 0 && FD_ISSET(dev->cs, r)) { 13544106Sbrian FD_CLR(dev->cs, r); 13644106Sbrian log_Printf(LogTIMER, "%s: fdunset(ctrl) %d\n", p->link.name, dev->cs); 13744106Sbrian result = 1; 13844106Sbrian } else 13944106Sbrian result = 0; 14044106Sbrian 14143693Sbrian /* Careful... physical_RemoveFromSet() called us ! */ 14244106Sbrian 14344106Sbrian p->handler->removefromset = NULL; 14444106Sbrian result += physical_RemoveFromSet(p, r, w, e); 14544106Sbrian p->handler->removefromset = ether_RemoveFromSet; 14644106Sbrian 14744106Sbrian return result; 14844106Sbrian} 14944106Sbrian 15044106Sbrianstatic void 15144106Sbrianether_Free(struct physical *p) 15244106Sbrian{ 15344106Sbrian struct etherdevice *dev = device2ether(p->handler); 15444106Sbrian 15544106Sbrian physical_SetDescriptor(p); 15644106Sbrian if (dev->cs != -1) 15744106Sbrian close(dev->cs); 15844106Sbrian free(dev); 15944106Sbrian} 16044106Sbrian 16143693Sbrianstatic const char * 16243693Sbrianether_OpenInfo(struct physical *p) 16344106Sbrian{ 16444106Sbrian struct etherdevice *dev = device2ether(p->handler); 16544106Sbrian 16644106Sbrian switch (dev->connected) { 16744106Sbrian case CARRIER_PENDING: 16843693Sbrian return "negotiating"; 16943693Sbrian case CARRIER_OK: 17043693Sbrian return "established"; 17143693Sbrian } 17243693Sbrian 17343693Sbrian return "disconnected"; 17443693Sbrian} 17543693Sbrian 17643693Sbrianstatic void 17743693Sbrianether_device2iov(struct device *d, struct iovec *iov, int *niov, 17843693Sbrian int maxiov, int *auxfd, int *nauxfd) 17943693Sbrian{ 18043693Sbrian struct etherdevice *dev = device2ether(d); 18143693Sbrian int sz = physical_MaxDeviceSize(); 18243693Sbrian 18343693Sbrian iov[*niov].iov_base = realloc(d, sz); 18443693Sbrian if (iov[*niov].iov_base == NULL) { 18543693Sbrian log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz); 18643693Sbrian AbortProgram(EX_OSERR); 18743693Sbrian } 18843693Sbrian iov[*niov].iov_len = sz; 18943693Sbrian (*niov)++; 19043693Sbrian 19143693Sbrian if (dev->cs >= 0) { 19243693Sbrian *auxfd = dev->cs; 19343693Sbrian (*nauxfd)++; 19443693Sbrian } 19543693Sbrian} 19643888Sbrian 19743888Sbrianstatic void 19843888Sbrianether_MessageIn(struct etherdevice *dev) 19943888Sbrian{ 20043888Sbrian char msgbuf[sizeof(struct ng_mesg) + sizeof(struct ngpppoe_sts)]; 20147849Sbrian struct ng_mesg *rep = (struct ng_mesg *)msgbuf; 20243888Sbrian struct ngpppoe_sts *sts = (struct ngpppoe_sts *)(msgbuf + sizeof *rep); 20343888Sbrian char unknown[14]; 20443888Sbrian const char *msg; 20543888Sbrian struct timeval t; 20643888Sbrian fd_set *r; 20743888Sbrian int ret; 20843888Sbrian 20943888Sbrian if (dev->cs < 0) 21043888Sbrian return; 21143888Sbrian 21243888Sbrian if ((r = mkfdset()) == NULL) { 21343888Sbrian log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n"); 21443888Sbrian return; 21543888Sbrian } 21643888Sbrian zerofdset(r); 21743888Sbrian FD_SET(dev->cs, r); 21843888Sbrian t.tv_sec = t.tv_usec = 0; 21943888Sbrian ret = select(dev->cs + 1, r, NULL, NULL, &t); 22047849Sbrian free(r); 22143888Sbrian 22243888Sbrian if (ret <= 0) 22343888Sbrian return; 22443888Sbrian 22543888Sbrian if (NgRecvMsg(dev->cs, rep, sizeof msgbuf, NULL) < 0) 22643888Sbrian return; 22743888Sbrian 22843888Sbrian if (rep->header.version != NG_VERSION) { 22943888Sbrian log_Printf(LogWARN, "%ld: Unexpected netgraph version, expected %ld\n", 23043888Sbrian (long)rep->header.version, (long)NG_VERSION); 23143888Sbrian return; 23243888Sbrian } 23343888Sbrian 23443888Sbrian if (rep->header.typecookie != NGM_PPPOE_COOKIE) { 23549976Sbrian log_Printf(LogWARN, "%ld: Unexpected netgraph cookie, expected %ld\n", 23649976Sbrian (long)rep->header.typecookie, (long)NGM_PPPOE_COOKIE); 23743888Sbrian return; 23843888Sbrian } 23949976Sbrian 24049976Sbrian switch (rep->header.cmd) { 24143888Sbrian case NGM_PPPOE_SET_FLAG: msg = "SET_FLAG"; break; 24243888Sbrian case NGM_PPPOE_CONNECT: msg = "CONNECT"; break; 24343888Sbrian case NGM_PPPOE_LISTEN: msg = "LISTEN"; break; 24443888Sbrian case NGM_PPPOE_OFFER: msg = "OFFER"; break; 24549976Sbrian case NGM_PPPOE_SUCCESS: msg = "SUCCESS"; break; 24649976Sbrian case NGM_PPPOE_FAIL: msg = "FAIL"; break; 24743888Sbrian case NGM_PPPOE_CLOSE: msg = "CLOSE"; break; 24843888Sbrian case NGM_PPPOE_GET_STATUS: msg = "GET_STATUS"; break; 24943888Sbrian default: 25047849Sbrian snprintf(unknown, sizeof unknown, "<%d>", (int)rep->header.cmd); 25143888Sbrian msg = unknown; 25249976Sbrian break; 25349976Sbrian } 25443888Sbrian 25543888Sbrian log_Printf(LogPHASE, "Received NGM_PPPOE_%s (hook \"%s\")\n", msg, sts->hook); 25643888Sbrian 25743888Sbrian switch (rep->header.cmd) { 25843888Sbrian case NGM_PPPOE_SUCCESS: 25943888Sbrian dev->connected = CARRIER_OK; 26043888Sbrian break; 26143888Sbrian case NGM_PPPOE_FAIL: 26245907Sbrian case NGM_PPPOE_CLOSE: 26343888Sbrian dev->connected = CARRIER_LOST; 26443888Sbrian break; 26543888Sbrian } 26643888Sbrian} 26743888Sbrian 26843888Sbrianstatic int 26943888Sbrianether_AwaitCarrier(struct physical *p) 27043888Sbrian{ 27143888Sbrian struct etherdevice *dev = device2ether(p->handler); 27243888Sbrian 27343888Sbrian if (dev->connected != CARRIER_OK && !dev->timeout--) 27443888Sbrian dev->connected = CARRIER_LOST; 27543888Sbrian else if (dev->connected == CARRIER_PENDING) 27643888Sbrian ether_MessageIn(dev); 27743888Sbrian 27843888Sbrian return dev->connected; 27943888Sbrian} 28043888Sbrian 28143888Sbrianstatic const struct device baseetherdevice = { 28243888Sbrian ETHER_DEVICE, 28343888Sbrian "ether", 28443888Sbrian { CD_REQUIRED, DEF_ETHERCDDELAY }, 28543888Sbrian ether_AwaitCarrier, 28643888Sbrian ether_RemoveFromSet, 28743888Sbrian NULL, 28843888Sbrian NULL, 28943888Sbrian NULL, 29043888Sbrian NULL, 29145907Sbrian ether_Free, 29244123Sbrian ether_Read, 29344106Sbrian ether_Write, 29444123Sbrian ether_device2iov, 29543888Sbrian NULL, 29643888Sbrian ether_OpenInfo 29743888Sbrian}; 29844123Sbrian 29944123Sbrianstruct device * 30044123Sbrianether_iov2device(int type, struct physical *p, struct iovec *iov, int *niov, 30144123Sbrian int maxiov, int *auxfd, int *nauxfd) 30244123Sbrian{ 30343888Sbrian if (type == ETHER_DEVICE) { 30444106Sbrian struct etherdevice *dev = (struct etherdevice *)iov[(*niov)++].iov_base; 30543888Sbrian 30645907Sbrian dev = realloc(dev, sizeof *dev); /* Reduce to the correct size */ 30744123Sbrian if (dev == NULL) { 30844123Sbrian log_Printf(LogALERT, "Failed to allocate memory: %d\n", 30944123Sbrian (int)(sizeof *dev)); 31044123Sbrian AbortProgram(EX_OSERR); 31143888Sbrian } 31243888Sbrian 31343888Sbrian if (*nauxfd) { 31443888Sbrian dev->cs = *auxfd; 31544123Sbrian (*nauxfd)--; 31644106Sbrian } else 31744123Sbrian dev->cs = -1; 31843888Sbrian 31943888Sbrian /* Refresh function pointers etc */ 32043888Sbrian memcpy(&dev->dev, &baseetherdevice, sizeof dev->dev); 32143888Sbrian 32243888Sbrian physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF); 32343888Sbrian return &dev->dev; 32443888Sbrian } 32543888Sbrian 32643888Sbrian return NULL; 32743888Sbrian} 32843888Sbrian 32943888Sbrianstatic int 33043888Sbrianether_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n) 33143888Sbrian{ 33243888Sbrian struct physical *p = descriptor2physical(d); 33343888Sbrian struct etherdevice *dev = device2ether(p->handler); 33443888Sbrian int result; 33543888Sbrian 33643888Sbrian if (r && dev->cs >= 0) { 33743888Sbrian FD_SET(dev->cs, r); 33843888Sbrian log_Printf(LogTIMER, "%s(ctrl): fdset(r) %d\n", p->link.name, dev->cs); 33943888Sbrian result = 1; 34043888Sbrian } else 34143888Sbrian result = 0; 34243888Sbrian 34343888Sbrian result += physical_doUpdateSet(d, r, w, e, n, 0); 34443888Sbrian 34543888Sbrian return result; 34643888Sbrian} 34743888Sbrian 34843888Sbrianstatic int 34943888Sbrianether_IsSet(struct fdescriptor *d, const fd_set *fdset) 35043888Sbrian{ 35143888Sbrian struct physical *p = descriptor2physical(d); 35243888Sbrian struct etherdevice *dev = device2ether(p->handler); 35343888Sbrian int result; 35443888Sbrian 35543888Sbrian result = dev->cs >= 0 && FD_ISSET(dev->cs, fdset); 35643888Sbrian result += physical_IsSet(d, fdset); 35743888Sbrian 35843888Sbrian return result; 35943888Sbrian} 36043888Sbrian 36143888Sbrianstatic void 36243888Sbrianether_DescriptorRead(struct fdescriptor *d, struct bundle *bundle, 36343888Sbrian const fd_set *fdset) 36443888Sbrian{ 36543888Sbrian struct physical *p = descriptor2physical(d); 36643888Sbrian struct etherdevice *dev = device2ether(p->handler); 36743888Sbrian 36843888Sbrian if (dev->cs >= 0 && FD_ISSET(dev->cs, fdset)) { 36943888Sbrian ether_MessageIn(dev); 37043888Sbrian if (dev->connected == CARRIER_LOST) { 37143888Sbrian log_Printf(LogPHASE, "%s: Device disconnected\n", p->link.name); 37243888Sbrian datalink_Down(p->dl, CLOSE_NORMAL); 37343888Sbrian return; 37443888Sbrian } 37543888Sbrian } 37643888Sbrian 37743888Sbrian if (physical_IsSet(d, fdset)) 37843888Sbrian physical_DescriptorRead(d, bundle, fdset); 37943888Sbrian} 38044123Sbrian 38144106Sbrianstatic struct device * 38244106Sbrianether_Abandon(struct etherdevice *dev, struct physical *p) 38344106Sbrian{ 38444106Sbrian /* Abandon our node construction */ 38544123Sbrian close(dev->cs); 38644106Sbrian close(p->fd); 38743888Sbrian p->fd = -2; /* Nobody else need try.. */ 38843888Sbrian free(dev); 38943888Sbrian 39043888Sbrian return NULL; 39143888Sbrian} 39243888Sbrian 39343888Sbrianstruct device * 39444123Sbrianether_Create(struct physical *p) 39544123Sbrian{ 39644123Sbrian u_char rbuf[2048]; 39744123Sbrian struct etherdevice *dev; 39844123Sbrian struct ng_mesg *resp; 39943888Sbrian const struct hooklist *hlist; 40043888Sbrian const struct nodeinfo *ninfo; 40143888Sbrian int f; 40243888Sbrian 40343888Sbrian dev = NULL; 40443888Sbrian if (p->fd < 0 && !strncasecmp(p->name.full, NG_PPPOE_NODE_TYPE, 40543888Sbrian PPPOE_NODE_TYPE_LEN) && 40643888Sbrian p->name.full[PPPOE_NODE_TYPE_LEN] == ':') { 40743888Sbrian const struct linkinfo *nlink; 40843888Sbrian struct ngpppoe_init_data *data; 40943888Sbrian struct ngm_mkpeer mkp; 41043888Sbrian struct ngm_connect ngc; 41143888Sbrian const char *iface, *provider; 41243888Sbrian char *path, etherid[12]; 41343693Sbrian int ifacelen, providerlen; 41443693Sbrian char connectpath[sizeof dev->hook + 2]; /* .:<hook> */ 41543693Sbrian 41613379Sphk p->fd--; /* We own the device - change fd */ 4176059Samurai 4186059Samurai#if defined(__FreeBSD__) && !defined(NOKLDLOAD) 41943888Sbrian if (modfind("netgraph") == -1) { 42043401Sbrian log_Printf(LogWARN, "Netgraph is not built into the kernel\n"); 42145907Sbrian return NULL; 42243888Sbrian } 42345907Sbrian 42443888Sbrian if (modfind("ng_ether") == -1 && ID0kldload("ng_ether") == -1) 42543313Sbrian /* 42643888Sbrian * Don't treat this as an error as older kernels have this stuff 42743888Sbrian * built in as part of the netgraph node itself. 42843888Sbrian */ 42943888Sbrian log_Printf(LogWARN, "kldload: ng_ether: %s\n", strerror(errno)); 43043888Sbrian 43143888Sbrian if (modfind("ng_socket") == -1 && ID0kldload("ng_socket") == -1) { 43243313Sbrian log_Printf(LogWARN, "kldload: ng_socket: %s\n", strerror(errno)); 43343888Sbrian return NULL; 43444106Sbrian } 43544106Sbrian#endif 43644106Sbrian 43744106Sbrian if ((dev = malloc(sizeof *dev)) == NULL) 43844106Sbrian return NULL; 43944106Sbrian 44045907Sbrian iface = p->name.full + PPPOE_NODE_TYPE_LEN + 1; 44143888Sbrian 44243888Sbrian provider = strchr(iface, ':'); 44343888Sbrian if (provider) { 44443313Sbrian ifacelen = provider - iface; 44545907Sbrian provider++; 44645907Sbrian providerlen = strlen(provider); 4476059Samurai } else { 4486059Samurai ifacelen = strlen(iface); 44930715Sbrian provider = ""; 45043693Sbrian providerlen = 0; 4516059Samurai } 45243693Sbrian 45343693Sbrian /* 45443693Sbrian * We're going to do this (where tunN is our tunnel device): 45543693Sbrian * 45643693Sbrian * .---------. 4576059Samurai * | ether | 45843693Sbrian * | <iface> | dev->cs 45943693Sbrian * `---------' | 46043693Sbrian * (orphan) p->fd | 46143693Sbrian * | | | 46243693Sbrian * | | | 46343693Sbrian * (ethernet) | | 46443693Sbrian * .---------. .-----------. 4656059Samurai * | pppoe | | socket | 46643693Sbrian * | <iface> |(tunN)<---->(tunN)| <unnamed> | 46743693Sbrian * `--------- `-----------' 46843693Sbrian * (tunX) 46943693Sbrian * ^ 47043693Sbrian * | 47143693Sbrian * `--->(tunX) 47237926Sbrian */ 47344106Sbrian 47444123Sbrian /* Create a socket node */ 47544123Sbrian if (ID0NgMkSockNode(NULL, &dev->cs, &p->fd) == -1) { 47644123Sbrian log_Printf(LogWARN, "Cannot create netgraph socket node: %s\n", 47744123Sbrian strerror(errno)); 47844123Sbrian free(dev); 47944106Sbrian return NULL; 48044106Sbrian } 48144106Sbrian 48244123Sbrian /* 48344106Sbrian * Ask for a list of hooks attached to the "ether" node. This node should 48444106Sbrian * magically exist as a way of hooking stuff onto an ethernet device 48544106Sbrian */ 48644106Sbrian path = (char *)alloca(ifacelen + 2); 48744106Sbrian sprintf(path, "%.*s:", ifacelen, iface); 48844123Sbrian if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, 48944123Sbrian NULL, 0) < 0) { 49044123Sbrian log_Printf(LogWARN, "%s Cannot send a netgraph message: %s\n", 49144106Sbrian path, strerror(errno)); 49244106Sbrian return ether_Abandon(dev, p); 49344106Sbrian } 49444106Sbrian 49544106Sbrian /* Get our list back */ 49644123Sbrian resp = (struct ng_mesg *)rbuf; 49744106Sbrian if (NgRecvMsg(dev->cs, resp, sizeof rbuf, NULL) < 0) { 49844106Sbrian log_Printf(LogWARN, "Cannot get netgraph response: %s\n", 49944106Sbrian strerror(errno)); 50044106Sbrian return ether_Abandon(dev, p); 50144106Sbrian } 50245907Sbrian 50344106Sbrian hlist = (const struct hooklist *)resp->data; 50444106Sbrian ninfo = &hlist->nodeinfo; 50544106Sbrian 50644106Sbrian /* Make sure we've got the right type of node */ 50744106Sbrian if (strncmp(ninfo->type, NG_ETHER_NODE_TYPE, 50844106Sbrian sizeof NG_ETHER_NODE_TYPE - 1)) { 50944123Sbrian log_Printf(LogWARN, "%s Unexpected node type ``%s'' (wanted ``" 51044106Sbrian NG_ETHER_NODE_TYPE "'')\n", path, ninfo->type); 51143693Sbrian return ether_Abandon(dev, p); 51243693Sbrian } 51343693Sbrian 51443888Sbrian log_Printf(LogDEBUG, "List of netgraph node ``%s'' (id %x) hooks:\n", 51543888Sbrian path, ninfo->id); 51643888Sbrian 51743888Sbrian /* look for a hook already attached. */ 51843888Sbrian for (f = 0; f < ninfo->hooks; f++) { 51943888Sbrian nlink = &hlist->link[f]; 52043888Sbrian 52143693Sbrian log_Printf(LogDEBUG, " Found %s -> %s\n", nlink->ourhook, 52245907Sbrian nlink->peerhook); 52344123Sbrian 52444106Sbrian if (!strcmp(nlink->ourhook, NG_ETHER_HOOK_ORPHAN) || 52544106Sbrian !strcmp(nlink->ourhook, NG_ETHER_HOOK_DIVERT)) { 52644123Sbrian /* 52743693Sbrian * Something is using the data coming out of this ``ether'' node. 52829840Sbrian * If it's a PPPoE node, we use that node, otherwise we complain that 52943693Sbrian * someone else is using the node. 53043888Sbrian */ 53143888Sbrian if (!strcmp(nlink->nodeinfo.type, NG_PPPOE_NODE_TYPE)) 53243888Sbrian /* Use this PPPoE node ! */ 53343888Sbrian snprintf(ngc.path, sizeof ngc.path, "[%x]:", nlink->nodeinfo.id); 53443888Sbrian else { 53546686Sbrian log_Printf(LogWARN, "%s Node type ``%s'' is currently active\n", 53646686Sbrian path, nlink->nodeinfo.type); 53743693Sbrian return ether_Abandon(dev, p); 53846686Sbrian } 53943693Sbrian break; 54044106Sbrian } 54144123Sbrian } 54248817Sbrian 54344123Sbrian if (f == ninfo->hooks) { 54444123Sbrian /* 54544123Sbrian * Create a new ``PPPoE'' node connected to the ``ether'' node using 54629840Sbrian * the magic ``orphan'' and ``ethernet'' hooks 54746686Sbrian */ 54846686Sbrian snprintf(mkp.type, sizeof mkp.type, "%s", NG_PPPOE_NODE_TYPE); 54954912Sbrian snprintf(mkp.ourhook, sizeof mkp.ourhook, "%s", NG_ETHER_HOOK_ORPHAN); 55046686Sbrian snprintf(mkp.peerhook, sizeof mkp.peerhook, "%s", NG_PPPOE_HOOK_ETHERNET); 55146686Sbrian snprintf(etherid, sizeof etherid, "[%x]:", ninfo->id); 55246686Sbrian 55346686Sbrian log_Printf(LogDEBUG, "Creating PPPoE netgraph node %s%s -> %s\n", 55446686Sbrian etherid, mkp.ourhook, mkp.peerhook); 55545220Sbrian 55654912Sbrian if (NgSendMsg(dev->cs, etherid, NGM_GENERIC_COOKIE, 55746686Sbrian NGM_MKPEER, &mkp, sizeof mkp) < 0) { 55845220Sbrian log_Printf(LogWARN, "%s Cannot create PPPoE netgraph node: %s\n", 55945220Sbrian etherid, strerror(errno)); 56054912Sbrian return ether_Abandon(dev, p); 56144159Sbrian } 56244159Sbrian 56344159Sbrian snprintf(ngc.path, sizeof ngc.path, "%s%s", path, NG_ETHER_HOOK_ORPHAN); 56443693Sbrian } 56543693Sbrian 56643693Sbrian snprintf(dev->hook, sizeof dev->hook, "%s%d", 56743693Sbrian TUN_NAME, p->dl->bundle->unit); 56854912Sbrian 56943693Sbrian /* 57043693Sbrian * Connect the PPPoE node to our socket node. 57143693Sbrian * ngc.path has already been set up 57243693Sbrian */ 57346686Sbrian snprintf(ngc.ourhook, sizeof ngc.ourhook, "%s", dev->hook); 57443693Sbrian memcpy(ngc.peerhook, ngc.ourhook, sizeof ngc.peerhook); 57543693Sbrian 57643693Sbrian log_Printf(LogDEBUG, "Connecting netgraph socket .:%s -> %s:%s\n", 57743693Sbrian ngc.ourhook, ngc.path, ngc.peerhook); 57854912Sbrian if (NgSendMsg(dev->cs, ".:", NGM_GENERIC_COOKIE, 57946686Sbrian NGM_CONNECT, &ngc, sizeof ngc) < 0) { 58025630Sbrian log_Printf(LogWARN, "Cannot connect PPPoE and socket netgraph " 58143693Sbrian "nodes: %s\n", strerror(errno)); 58229840Sbrian return ether_Abandon(dev, p); 58344123Sbrian } 58444106Sbrian 58544123Sbrian /* And finally, request a connection to the given provider */ 58643693Sbrian 58743693Sbrian data = (struct ngpppoe_init_data *)alloca(sizeof *data + providerlen); 58844106Sbrian 58944106Sbrian snprintf(data->hook, sizeof data->hook, "%s", dev->hook); 59043693Sbrian strcpy(data->data, provider); 59143693Sbrian 59254912Sbrian snprintf(connectpath, sizeof connectpath, ".:%s", dev->hook); 59346686Sbrian log_Printf(LogDEBUG, "Sending PPPOE_CONNECT to %s\n", connectpath); 59443693Sbrian if (NgSendMsg(dev->cs, connectpath, NGM_PPPOE_COOKIE, 59545907Sbrian NGM_PPPOE_CONNECT, data, sizeof *data + providerlen) == -1) { 59645907Sbrian log_Printf(LogWARN, "``%s'': Cannot start netgraph node: %s\n", 59743693Sbrian connectpath, strerror(errno)); 59844123Sbrian return ether_Abandon(dev, p); 59944106Sbrian } 60044106Sbrian 60144106Sbrian /* Hook things up so that we monitor dev->cs */ 60244123Sbrian p->desc.UpdateSet = ether_UpdateSet; 60343693Sbrian p->desc.IsSet = ether_IsSet; 60443313Sbrian p->desc.Read = ether_DescriptorRead; 60543693Sbrian 60643693Sbrian memcpy(&dev->dev, &baseetherdevice, sizeof dev->dev); 60743693Sbrian switch (p->cfg.cd.necessity) { 60843693Sbrian case CD_VARIABLE: 60943693Sbrian dev->dev.cd.delay = p->cfg.cd.delay; 61043693Sbrian break; 61154912Sbrian case CD_REQUIRED: 61246686Sbrian dev->dev.cd = p->cfg.cd; 61343693Sbrian break; 61443693Sbrian case CD_NOTREQUIRED: 61543693Sbrian log_Printf(LogWARN, "%s: Carrier must be set, using ``set cd %d!''\n", 61654912Sbrian p->link.name, dev->dev.cd.delay); 61746686Sbrian case CD_DEFAULT: 61843693Sbrian break; 61943693Sbrian } 62043693Sbrian 62143693Sbrian dev->timeout = dev->dev.cd.delay; 62243693Sbrian dev->connected = CARRIER_PENDING; 62344123Sbrian 62444106Sbrian } else { 62544123Sbrian /* See if we're a netgraph socket */ 62643693Sbrian struct sockaddr_ng ngsock; 62743313Sbrian struct sockaddr *sock = (struct sockaddr *)&ngsock; 62843693Sbrian int sz; 62943693Sbrian 63043693Sbrian sz = sizeof ngsock; 63143693Sbrian if (getsockname(p->fd, sock, &sz) != -1 && sock->sa_family == AF_NETGRAPH) { 63243693Sbrian /* 63354912Sbrian * It's a netgraph node... We can't determine hook names etc, so we 63446686Sbrian * stay pretty impartial.... 63543693Sbrian */ 63643693Sbrian log_Printf(LogPHASE, "%s: Link is a netgraph node\n", p->link.name); 63743693Sbrian 63843693Sbrian if ((dev = malloc(sizeof *dev)) == NULL) { 63943401Sbrian log_Printf(LogWARN, "%s: Cannot allocate an ether device: %s\n", 64036285Sbrian p->link.name, strerror(errno)); 64143693Sbrian return NULL; 64243693Sbrian } 64343693Sbrian 64443693Sbrian memcpy(&dev->dev, &baseetherdevice, sizeof dev->dev); 64544106Sbrian dev->cs = -1; 64644106Sbrian dev->timeout = 0; 64744106Sbrian dev->connected = CARRIER_OK; 64844123Sbrian *dev->hook = '\0'; 64944106Sbrian } 65044123Sbrian } 65144123Sbrian 65244123Sbrian if (dev) { 65343693Sbrian physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF); 65444106Sbrian 65544106Sbrian /* Moan about (and fix) invalid LCP configurations */ 65644123Sbrian if (p->link.lcp.cfg.mru > 1492) { 65744106Sbrian log_Printf(LogWARN, "%s: Reducing MRU to 1492\n", p->link.name); 65844123Sbrian p->link.lcp.cfg.mru = 1492; 65944123Sbrian } 66044123Sbrian if (p->dl->bundle->cfg.mtu > 1492) { 66143693Sbrian log_Printf(LogWARN, "%s: Reducing MTU to 1492\n", p->link.name); 66236285Sbrian p->dl->bundle->cfg.mtu = 1492; 66343693Sbrian } 66443693Sbrian 66543693Sbrian return &dev->dev; 66643693Sbrian } 66743693Sbrian 66843693Sbrian return NULL; 66943693Sbrian} 67043693Sbrian