Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
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;
}