The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netgraph/ng_pppoe.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 
    2 /*
    3  * ng_pppoe.c
    4  *
    5  * Copyright (c) 1996-1999 Whistle Communications, Inc.
    6  * All rights reserved.
    7  * 
    8  * Subject to the following obligations and disclaimer of warranty, use and
    9  * redistribution of this software, in source or object code forms, with or
   10  * without modifications are expressly permitted by Whistle Communications;
   11  * provided, however, that:
   12  * 1. Any and all reproductions of the source or object code must include the
   13  *    copyright notice above and the following disclaimer of warranties; and
   14  * 2. No rights are granted, in any manner or form, to use Whistle
   15  *    Communications, Inc. trademarks, including the mark "WHISTLE
   16  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
   17  *    such appears in the above copyright notice or in the software.
   18  * 
   19  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
   20  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
   21  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
   22  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
   23  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
   24  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
   25  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
   26  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
   27  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
   28  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
   29  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
   30  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
   31  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
   32  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   33  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   34  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
   35  * OF SUCH DAMAGE.
   36  *
   37  * Author: Julian Elischer <julian@freebsd.org>
   38  *
   39  * $FreeBSD$
   40  * $Whistle: ng_pppoe.c,v 1.10 1999/11/01 09:24:52 julian Exp $
   41  */
   42 #if 0
   43 #define AAA printf("pppoe: %s\n", __FUNCTION__ );
   44 #define BBB printf("-%d-", __LINE__ );
   45 #else
   46 #define AAA
   47 #define BBB
   48 #endif
   49 
   50 #include <sys/param.h>
   51 #include <sys/systm.h>
   52 #include <sys/kernel.h>
   53 #include <sys/mbuf.h>
   54 #include <sys/malloc.h>
   55 #include <sys/errno.h>
   56 #include <sys/sysctl.h>
   57 #include <sys/syslog.h>
   58 #include <net/ethernet.h>
   59 
   60 #include <netgraph/ng_message.h>
   61 #include <netgraph/netgraph.h>
   62 #include <netgraph/ng_pppoe.h>
   63 
   64 #define SIGNOFF "session closed"
   65 
   66 /*
   67  * This section contains the netgraph method declarations for the
   68  * pppoe node. These methods define the netgraph pppoe 'type'.
   69  */
   70 
   71 static ng_constructor_t ng_pppoe_constructor;
   72 static ng_rcvmsg_t      ng_pppoe_rcvmsg;
   73 static ng_shutdown_t    ng_pppoe_rmnode;
   74 static ng_newhook_t     ng_pppoe_newhook;
   75 static ng_connect_t     ng_pppoe_connect;
   76 static ng_rcvdata_t     ng_pppoe_rcvdata;
   77 static ng_disconnect_t  ng_pppoe_disconnect;
   78 
   79 /* Netgraph node type descriptor */
   80 static struct ng_type typestruct = {
   81         NG_VERSION,
   82         NG_PPPOE_NODE_TYPE,
   83         NULL,
   84         ng_pppoe_constructor,
   85         ng_pppoe_rcvmsg,
   86         ng_pppoe_rmnode,
   87         ng_pppoe_newhook,
   88         NULL,
   89         ng_pppoe_connect,
   90         ng_pppoe_rcvdata,
   91         ng_pppoe_rcvdata,
   92         ng_pppoe_disconnect,
   93         NULL
   94 };
   95 NETGRAPH_INIT(pppoe, &typestruct);
   96 
   97 /*
   98  * States for the session state machine.
   99  * These have no meaning if there is no hook attached yet.
  100  */
  101 enum state {
  102     PPPOE_SNONE=0,      /* [both] Initial state */
  103     PPPOE_LISTENING,    /* [Daemon] Listening for discover initiation pkt */
  104     PPPOE_SINIT,        /* [Client] Sent discovery initiation */
  105     PPPOE_PRIMED,       /* [Server] Awaiting PADI from daemon */
  106     PPPOE_SOFFER,       /* [Server] Sent offer message  (got PADI)*/
  107     PPPOE_SREQ,         /* [Client] Sent a Request */
  108     PPPOE_NEWCONNECTED, /* [Server] Connection established, No data received */
  109     PPPOE_CONNECTED,    /* [Both] Connection established, Data received */
  110     PPPOE_DEAD          /* [Both] */
  111 };
  112 
  113 #define NUMTAGS 20 /* number of tags we are set up to work with */
  114 
  115 /*
  116  * Information we store for each hook on each node for negotiating the 
  117  * session. The mbuf and cluster are freed once negotiation has completed.
  118  * The whole negotiation block is then discarded.
  119  */
  120 
  121 struct sess_neg {
  122         struct mbuf             *m; /* holds cluster with last sent packet */
  123         union   packet          *pkt; /* points within the above cluster */
  124         struct callout_handle   timeout_handle;   /* see timeout(9) */
  125         u_int                   timeout; /* 0,1,2,4,8,16 etc. seconds */
  126         u_int                   numtags;
  127         const struct pppoe_tag  *tags[NUMTAGS];
  128         u_int                   service_len;
  129         u_int                   ac_name_len;
  130 
  131         struct datatag          service;
  132         struct datatag          ac_name;
  133 };
  134 typedef struct sess_neg *negp;
  135 
  136 /*
  137  * Session information that is needed after connection.
  138  */
  139 struct sess_con {
  140         hook_p                  hook;
  141         u_int16_t               Session_ID;
  142         enum state              state;
  143         char                    creator[NG_NODELEN + 1]; /* who to notify */
  144         struct pppoe_full_hdr   pkt_hdr;        /* used when connected */
  145         negp                    neg;            /* used when negotiating */
  146         /*struct sess_con       *hash_next;*/   /* not yet used */
  147 };
  148 typedef struct sess_con *sessp;
  149 
  150 /*
  151  * Information we store for each node
  152  */
  153 struct PPPOE {
  154         node_p          node;           /* back pointer to node */
  155         hook_p          ethernet_hook;
  156         hook_p          debug_hook;
  157         u_int           packets_in;     /* packets in from ethernet */
  158         u_int           packets_out;    /* packets out towards ethernet */
  159         u_int32_t       flags;
  160         /*struct sess_con *buckets[HASH_SIZE];*/        /* not yet used */
  161 };
  162 typedef struct PPPOE *priv_p;
  163 
  164 struct ether_header eh_prototype =
  165         {{0xff,0xff,0xff,0xff,0xff,0xff},
  166          {0x00,0x00,0x00,0x00,0x00,0x00},
  167          ETHERTYPE_PPPOE_DISC};
  168 
  169 #define PPPOE_KEEPSTANDARD      -1      /* never switch to nonstandard mode */
  170 #define PPPOE_STANDARD          0       /* try standard mode (dangerous!) */
  171 #define PPPOE_NONSTANDARD       1       /* just be in nonstandard mode */
  172 static int pppoe_mode = PPPOE_KEEPSTANDARD;
  173 
  174 static int
  175 ngpppoe_set_ethertype(SYSCTL_HANDLER_ARGS)
  176 {
  177         int error;
  178         int val;
  179 
  180         val = pppoe_mode;
  181         error = sysctl_handle_int(oidp, &val, sizeof(int), req);
  182         if (error != 0 || req->newptr == NULL)
  183                 return (error);
  184         switch (val) {
  185         case PPPOE_NONSTANDARD:
  186                 pppoe_mode = PPPOE_NONSTANDARD;
  187                 eh_prototype.ether_type = ETHERTYPE_PPPOE_STUPID_DISC;
  188                 break;
  189         case PPPOE_STANDARD:
  190                 pppoe_mode = PPPOE_STANDARD;
  191                 eh_prototype.ether_type = ETHERTYPE_PPPOE_DISC;
  192                 break;
  193         case PPPOE_KEEPSTANDARD:
  194                 pppoe_mode = PPPOE_KEEPSTANDARD;
  195                 eh_prototype.ether_type = ETHERTYPE_PPPOE_DISC;
  196                 break;
  197         default:
  198                 return (EINVAL);
  199         }
  200         return (0);
  201 }
  202 
  203 SYSCTL_PROC(_net_graph, OID_AUTO, nonstandard_pppoe, CTLTYPE_INT | CTLFLAG_RW,
  204     0, sizeof(int), ngpppoe_set_ethertype, "I", "nonstandard ethertype");
  205 
  206 union uniq {
  207         char bytes[sizeof(void *)];
  208         void * pointer;
  209         };
  210 
  211 #define LEAVE(x) do { error = x; goto quit; } while(0)
  212 static void     pppoe_start(sessp sp);
  213 static void     sendpacket(sessp sp);
  214 static void     pppoe_ticker(void *arg);
  215 static const    struct pppoe_tag *scan_tags(sessp sp,
  216                         const struct pppoe_hdr* ph);
  217 static  int     pppoe_send_event(sessp sp, enum cmd cmdid);
  218 
  219 /*************************************************************************
  220  * Some basic utilities  from the Linux version with author's permission.*
  221  * Author:      Michal Ostrowski <mostrows@styx.uwaterloo.ca>            *
  222  ************************************************************************/
  223 
  224 /*
  225  * Generate a new session id
  226  * XXX find out the FreeBSD locking scheme.
  227  */
  228 static u_int16_t
  229 get_new_sid(node_p node)
  230 {
  231         static int pppoe_sid = 10;
  232         sessp sp;
  233         hook_p  hook;
  234         u_int16_t val; 
  235         priv_p privp = node->private;
  236 
  237 AAA
  238 restart:
  239         val = pppoe_sid++;
  240         /*
  241          * Spec says 0xFFFF is reserved.
  242          * Also don't use 0x0000
  243          */
  244         if (val == 0xffff) {
  245                 pppoe_sid = 20;
  246                 goto restart;
  247         }
  248 
  249         /* Check it isn't already in use */
  250         LIST_FOREACH(hook, &node->hooks, hooks) {
  251                 /* don't check special hooks */
  252                 if ((hook->private == &privp->debug_hook)
  253                 ||  (hook->private == &privp->ethernet_hook)) 
  254                         continue;
  255                 sp = hook->private;
  256                 if (sp->Session_ID == val)
  257                         goto restart;
  258         }
  259 
  260         return val;
  261 }
  262 
  263 
  264 /*
  265  * Return the location where the next tag can be put 
  266  */
  267 static __inline const struct pppoe_tag*
  268 next_tag(const struct pppoe_hdr* ph)
  269 {
  270         return (const struct pppoe_tag*)(((const char*)&ph->tag[0])
  271             + ntohs(ph->length));
  272 }
  273 
  274 /*
  275  * Look for a tag of a specific type
  276  * Don't trust any length the other end says.
  277  * but assume we already sanity checked ph->length.
  278  */
  279 static const struct pppoe_tag*
  280 get_tag(const struct pppoe_hdr* ph, u_int16_t idx)
  281 {
  282         const char *const end = (const char *)next_tag(ph);
  283         const char *ptn;
  284         const struct pppoe_tag *pt = &ph->tag[0];
  285         /*
  286          * Keep processing tags while a tag header will still fit.
  287          */
  288 AAA
  289         while((const char*)(pt + 1) <= end) {
  290             /*
  291              * If the tag data would go past the end of the packet, abort.
  292              */
  293             ptn = (((const char *)(pt + 1)) + ntohs(pt->tag_len));
  294             if(ptn > end)
  295                 return NULL;
  296 
  297             if(pt->tag_type == idx)
  298                 return pt;
  299 
  300             pt = (const struct pppoe_tag*)ptn;
  301         }
  302         return NULL;
  303 }
  304 
  305 /**************************************************************************
  306  * inlines to initialise or add tags to a session's tag list,
  307  **************************************************************************/
  308 /*
  309  * Initialise the session's tag list
  310  */
  311 static void
  312 init_tags(sessp sp)
  313 {
  314 AAA
  315         if(sp->neg == NULL) {
  316                 printf("pppoe: asked to init NULL neg pointer\n");
  317                 return;
  318         }
  319         sp->neg->numtags = 0;
  320 }
  321 
  322 static void
  323 insert_tag(sessp sp, const struct pppoe_tag *tp)
  324 {
  325         int     i;
  326         negp neg;
  327 
  328 AAA
  329         if((neg = sp->neg) == NULL) {
  330                 printf("pppoe: asked to use NULL neg pointer\n");
  331                 return;
  332         }
  333         if ((i = neg->numtags++) < NUMTAGS) {
  334                 neg->tags[i] = tp;
  335         } else {
  336                 printf("pppoe: asked to add too many tags to packet\n");
  337                 neg->numtags--;
  338         }
  339 }
  340 
  341 /*
  342  * Make up a packet, using the tags filled out for the session.
  343  *
  344  * Assume that the actual pppoe header and ethernet header 
  345  * are filled out externally to this routine.
  346  * Also assume that neg->wh points to the correct 
  347  * location at the front of the buffer space.
  348  */
  349 static void
  350 make_packet(sessp sp) {
  351         struct pppoe_full_hdr *wh = &sp->neg->pkt->pkt_header;
  352         const struct pppoe_tag **tag;
  353         char *dp;
  354         int count;
  355         int tlen;
  356         u_int16_t length = 0;
  357 
  358 AAA
  359         if ((sp->neg == NULL) || (sp->neg->m == NULL)) {
  360                 printf("pppoe: make_packet called from wrong state\n");
  361         }
  362         dp = (char *)wh->ph.tag;
  363         for (count = 0, tag = sp->neg->tags;
  364             ((count < sp->neg->numtags) && (count < NUMTAGS)); 
  365             tag++, count++) {
  366                 tlen = ntohs((*tag)->tag_len) + sizeof(**tag);
  367                 if ((length + tlen) > (ETHER_MAX_LEN - 4 - sizeof(*wh))) {
  368                         printf("pppoe: tags too long\n");
  369                         sp->neg->numtags = count;
  370                         break;  /* XXX chop off what's too long */
  371                 }
  372                 bcopy(*tag, (char *)dp, tlen);
  373                 length += tlen;
  374                 dp += tlen;
  375         }
  376         wh->ph.length = htons(length);
  377         sp->neg->m->m_len = length + sizeof(*wh);
  378         sp->neg->m->m_pkthdr.len = length + sizeof(*wh);
  379 }
  380 
  381 /**************************************************************************
  382  * Routine to match a service offered                                     *
  383  **************************************************************************/
  384 /* 
  385  * Find a hook that has a service string that matches that
  386  * we are seeking. for now use a simple string.
  387  * In the future we may need something like regexp().
  388  * for testing allow a null string to match 1st found and a null service
  389  * to match all requests. Also make '*' do the same.
  390  */
  391 
  392 #define NG_MATCH_EXACT  1
  393 #define NG_MATCH_ANY    2
  394 
  395 static hook_p
  396 pppoe_match_svc(node_p node, const char *svc_name, int svc_len, int match)
  397 {
  398         sessp   sp      = NULL;
  399         negp    neg     = NULL;
  400         priv_p  privp   = node->private;
  401         hook_p  allhook = NULL;
  402         hook_p  hook;
  403 
  404 AAA
  405         LIST_FOREACH(hook, &node->hooks, hooks) {
  406 
  407                 /* skip any hook that is debug or ethernet */
  408                 if ((hook->private == &privp->debug_hook)
  409                 ||  (hook->private == &privp->ethernet_hook))
  410                         continue;
  411                 sp = hook->private;
  412 
  413                 /* Skip any sessions which are not in LISTEN mode. */
  414                 if ( sp->state != PPPOE_LISTENING)
  415                         continue;
  416 
  417                 neg = sp->neg;
  418 
  419                 /* Special case for a blank or "*" service name (wildcard) */
  420                 if (match == NG_MATCH_ANY && neg->service_len == 1 &&
  421                     neg->service.data[0] == '*') {
  422                         allhook = hook;
  423                         continue;
  424                 }
  425 
  426                 /* If the lengths don't match, that aint it. */
  427                 if (neg->service_len != svc_len)
  428                         continue;
  429 
  430                 /* An exact match? */
  431                 if (svc_len == 0)
  432                         break;
  433 
  434                 if (strncmp(svc_name, neg->service.data, svc_len) == 0)
  435                         break;
  436         }
  437         return (hook ? hook : allhook);
  438 }
  439 /**************************************************************************
  440  * Routine to find a particular session that matches an incoming packet   *
  441  **************************************************************************/
  442 static hook_p
  443 pppoe_findsession(node_p node, const struct pppoe_full_hdr *wh)
  444 {
  445         sessp   sp = NULL;
  446         hook_p hook = NULL;
  447         priv_p  privp = node->private;
  448         u_int16_t       session = ntohs(wh->ph.sid);
  449 
  450         /*
  451          * find matching peer/session combination.
  452          */
  453 AAA
  454         LIST_FOREACH(hook, &node->hooks, hooks) {
  455                 /* don't check special hooks */
  456                 if ((hook->private == &privp->debug_hook)
  457                 ||  (hook->private == &privp->ethernet_hook)) {
  458                         continue;
  459                 }
  460                 sp = hook->private;
  461                 if ( ( (sp->state == PPPOE_CONNECTED)
  462                     || (sp->state == PPPOE_NEWCONNECTED) )
  463                 && (sp->Session_ID == session)
  464                 && (bcmp(sp->pkt_hdr.eh.ether_dhost,
  465                     wh->eh.ether_shost,
  466                     ETHER_ADDR_LEN)) == 0) {
  467                         break;
  468                 }
  469         }
  470         return (hook);
  471 }
  472 
  473 static hook_p
  474 pppoe_finduniq(node_p node, const struct pppoe_tag *tag)
  475 {
  476         hook_p hook = NULL;
  477         priv_p  privp = node->private;
  478         union uniq              uniq;
  479 
  480 AAA
  481         bcopy(tag->tag_data, uniq.bytes, sizeof(void *));
  482         /* cycle through all known hooks */
  483         LIST_FOREACH(hook, &node->hooks, hooks) {
  484                 /* don't check special hooks */
  485                 if ((hook->private == &privp->debug_hook)
  486                 ||  (hook->private == &privp->ethernet_hook)) 
  487                         continue;
  488                 if (uniq.pointer == hook->private)
  489                         break;
  490         }
  491         return (hook);
  492 }
  493 
  494 /**************************************************************************
  495  * start of Netgraph entrypoints                                          *
  496  **************************************************************************/
  497 
  498 /*
  499  * Allocate the private data structure and the generic node
  500  * and link them together.
  501  *
  502  * ng_make_node_common() returns with a generic node struct
  503  * with a single reference for us.. we transfer it to the
  504  * private structure.. when we free the private struct we must
  505  * unref the node so it gets freed too.
  506  */
  507 static int
  508 ng_pppoe_constructor(node_p *nodep)
  509 {
  510         priv_p privdata;
  511         int error;
  512 
  513 AAA
  514         /* Initialize private descriptor */
  515         MALLOC(privdata, priv_p, sizeof(*privdata), M_NETGRAPH, M_NOWAIT);
  516         if (privdata == NULL)
  517                 return (ENOMEM);
  518         bzero(privdata, sizeof(*privdata));
  519 
  520         /* Call the 'generic' (ie, superclass) node constructor */
  521         if ((error = ng_make_node_common(&typestruct, nodep))) {
  522                 FREE(privdata, M_NETGRAPH);
  523                 return (error);
  524         }
  525 
  526         /* Link structs together; this counts as our one reference to *nodep */
  527         (*nodep)->private = privdata;
  528         privdata->node = *nodep;
  529         return (0);
  530 }
  531 
  532 /*
  533  * Give our ok for a hook to be added...
  534  * point the hook's private info to the hook structure.
  535  *
  536  * The following hook names are special:
  537  *  Ethernet:  the hook that should be connected to a NIC.
  538  *  debug:      copies of data sent out here  (when I write the code).
  539  * All other hook names need only be unique. (the framework checks this).
  540  */
  541 static int
  542 ng_pppoe_newhook(node_p node, hook_p hook, const char *name)
  543 {
  544         const priv_p privp = node->private;
  545         sessp sp;
  546 
  547 AAA
  548         if (strcmp(name, NG_PPPOE_HOOK_ETHERNET) == 0) {
  549                 privp->ethernet_hook = hook;
  550                 hook->private = &privp->ethernet_hook;
  551         } else if (strcmp(name, NG_PPPOE_HOOK_DEBUG) == 0) {
  552                 privp->debug_hook = hook;
  553                 hook->private = &privp->debug_hook;
  554         } else {
  555                 /*
  556                  * Any other unique name is OK.
  557                  * The infrastructure has already checked that it's unique,
  558                  * so just allocate it and hook it in.
  559                  */
  560                 MALLOC(sp, sessp, sizeof(*sp), M_NETGRAPH, M_NOWAIT);
  561                 if (sp == NULL) {
  562                                 return (ENOMEM);
  563                 }
  564                 bzero(sp, sizeof(*sp));
  565 
  566                 hook->private = sp;
  567                 sp->hook = hook;
  568         }
  569         return(0);
  570 }
  571 
  572 /*
  573  * Get a netgraph control message.
  574  * Check it is one we understand. If needed, send a response.
  575  * We sometimes save the address for an async action later.
  576  * Always free the message.
  577  */
  578 static int
  579 ng_pppoe_rcvmsg(node_p node,
  580            struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr)
  581 {
  582         priv_p privp = node->private;
  583         struct ngpppoe_init_data *ourmsg = NULL;
  584         struct ng_mesg *resp = NULL;
  585         int error = 0;
  586         hook_p hook = NULL;
  587         sessp sp = NULL;
  588         negp neg = NULL;
  589 
  590 AAA
  591         /* Deal with message according to cookie and command */
  592         switch (msg->header.typecookie) {
  593         case NGM_PPPOE_COOKIE: 
  594                 switch (msg->header.cmd) {
  595                 case NGM_PPPOE_CONNECT:
  596                 case NGM_PPPOE_LISTEN: 
  597                 case NGM_PPPOE_OFFER: 
  598                 case NGM_PPPOE_SERVICE: 
  599                         ourmsg = (struct ngpppoe_init_data *)msg->data;
  600                         if (( sizeof(*ourmsg) > msg->header.arglen)
  601                         || ((sizeof(*ourmsg) + ourmsg->data_len)
  602                             > msg->header.arglen)) {
  603                                 printf("pppoe_rcvmsg: bad arg size");
  604                                 LEAVE(EMSGSIZE);
  605                         }
  606                         if (ourmsg->data_len > PPPOE_SERVICE_NAME_SIZE) {
  607                                 printf("pppoe: init data too long (%d)\n",
  608                                                         ourmsg->data_len);
  609                                 LEAVE(EMSGSIZE);
  610                         }
  611                         /* make sure strcmp will terminate safely */
  612                         ourmsg->hook[sizeof(ourmsg->hook) - 1] = '\0';
  613 
  614                         /* cycle through all known hooks */
  615                         LIST_FOREACH(hook, &node->hooks, hooks) {
  616                                 if (hook->name
  617                                 && strcmp(hook->name, ourmsg->hook) == 0)
  618                                         break;
  619                         }
  620                         if (hook == NULL) {
  621                                 LEAVE(ENOENT);
  622                         }
  623                         if ((hook->private == &privp->debug_hook)
  624                         ||  (hook->private == &privp->ethernet_hook)) {
  625                                 LEAVE(EINVAL);
  626                         }
  627                         sp = hook->private;
  628 
  629                         if (msg->header.cmd == NGM_PPPOE_LISTEN) {
  630                                 /*
  631                                  * Ensure we aren't already listening for this
  632                                  * service.
  633                                  */
  634                                 if (pppoe_match_svc(node, ourmsg->data,
  635                                     ourmsg->data_len, NG_MATCH_EXACT) != NULL) {
  636                                         LEAVE(EEXIST);
  637                                 }
  638                         }
  639 
  640                         /*
  641                          * PPPOE_SERVICE advertisments are set up
  642                          * on sessions that are in PRIMED state.
  643                          */
  644                         if (msg->header.cmd == NGM_PPPOE_SERVICE) {
  645                                 break;
  646                         }
  647                         if (sp->state != PPPOE_SNONE) {
  648                                 printf("pppoe: Session already active\n");
  649                                 LEAVE(EISCONN);
  650                         }
  651 
  652                         /*
  653                          * set up prototype header
  654                          */
  655                         MALLOC(neg, negp, sizeof(*neg), M_NETGRAPH, M_NOWAIT);
  656 
  657                         if (neg == NULL) {
  658                                 printf("pppoe: Session out of memory\n");
  659                                 LEAVE(ENOMEM);
  660                         }
  661                         bzero(neg, sizeof(*neg));
  662                         MGETHDR(neg->m, M_DONTWAIT, MT_DATA);
  663                         if(neg->m == NULL) {
  664                                 printf("pppoe: Session out of mbufs\n");
  665                                 FREE(neg, M_NETGRAPH);
  666                                 LEAVE(ENOBUFS);
  667                         }
  668                         neg->m->m_pkthdr.rcvif = NULL;
  669                         MCLGET(neg->m, M_DONTWAIT);
  670                         if ((neg->m->m_flags & M_EXT) == 0) {
  671                                 printf("pppoe: Session out of mcls\n");
  672                                 m_freem(neg->m);
  673                                 FREE(neg, M_NETGRAPH);
  674                                 LEAVE(ENOBUFS);
  675                         }
  676                         sp->neg = neg;
  677                         callout_handle_init( &neg->timeout_handle);
  678                         neg->m->m_len = sizeof(struct pppoe_full_hdr);
  679                         neg->pkt = mtod(neg->m, union packet*);
  680                         neg->pkt->pkt_header.eh = eh_prototype;
  681                         neg->pkt->pkt_header.ph.ver = 0x1;
  682                         neg->pkt->pkt_header.ph.type = 0x1;
  683                         neg->pkt->pkt_header.ph.sid = 0x0000;
  684                         neg->timeout = 0;
  685 
  686                         strncpy(sp->creator, retaddr, NG_NODELEN);
  687                         sp->creator[NG_NODELEN] = '\0';
  688                 }
  689                 switch (msg->header.cmd) {
  690                 case NGM_PPPOE_GET_STATUS:
  691                     {
  692                         struct ngpppoestat *stats;
  693 
  694                         NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT);
  695                         if (!resp) {
  696                                 LEAVE(ENOMEM);
  697                         }
  698                         stats = (struct ngpppoestat *) resp->data;
  699                         stats->packets_in = privp->packets_in;
  700                         stats->packets_out = privp->packets_out;
  701                         break;
  702                     }
  703                 case NGM_PPPOE_CONNECT:
  704                         /*
  705                          * Check the hook exists and is Uninitialised.
  706                          * Send a PADI request, and start the timeout logic.
  707                          * Store the originator of this message so we can send
  708                          * a success of fail message to them later.
  709                          * Move the session to SINIT
  710                          * Set up the session to the correct state and
  711                          * start it.
  712                          */
  713                         neg->service.hdr.tag_type = PTT_SRV_NAME;
  714                         neg->service.hdr.tag_len =
  715                                         htons((u_int16_t)ourmsg->data_len);
  716                         if (ourmsg->data_len) {
  717                                 bcopy(ourmsg->data,
  718                                         neg->service.data, ourmsg->data_len);
  719                         }
  720                         neg->service_len = ourmsg->data_len;
  721                         pppoe_start(sp);
  722                         break;
  723                 case NGM_PPPOE_LISTEN:
  724                         /*
  725                          * Check the hook exists and is Uninitialised.
  726                          * Install the service matching string.
  727                          * Store the originator of this message so we can send
  728                          * a success of fail message to them later.
  729                          * Move the hook to 'LISTENING'
  730                          */
  731                         neg->service.hdr.tag_type = PTT_SRV_NAME;
  732                         neg->service.hdr.tag_len =
  733                                         htons((u_int16_t)ourmsg->data_len);
  734 
  735                         if (ourmsg->data_len) {
  736                                 bcopy(ourmsg->data,
  737                                         neg->service.data, ourmsg->data_len);
  738                         }
  739                         neg->service_len = ourmsg->data_len;
  740                         neg->pkt->pkt_header.ph.code = PADT_CODE;
  741                         /*
  742                          * wait for PADI packet coming from ethernet
  743                          */
  744                         sp->state = PPPOE_LISTENING;
  745                         break;
  746                 case NGM_PPPOE_OFFER:
  747                         /*
  748                          * Check the hook exists and is Uninitialised.
  749                          * Store the originator of this message so we can send
  750                          * a success of fail message to them later.
  751                          * Store the AC-Name given and go to PRIMED.
  752                          */
  753                         neg->ac_name.hdr.tag_type = PTT_AC_NAME;
  754                         neg->ac_name.hdr.tag_len =
  755                                         htons((u_int16_t)ourmsg->data_len);
  756                         if (ourmsg->data_len) {
  757                                 bcopy(ourmsg->data,
  758                                         neg->ac_name.data, ourmsg->data_len);
  759                         }
  760                         neg->ac_name_len = ourmsg->data_len;
  761                         neg->pkt->pkt_header.ph.code = PADO_CODE;
  762                         /*
  763                          * Wait for PADI packet coming from hook
  764                          */
  765                         sp->state = PPPOE_PRIMED;
  766                         break;
  767                 case NGM_PPPOE_SERVICE: 
  768                         /* 
  769                          * Check the session is primed.
  770                          * for now just allow ONE service to be advertised.
  771                          * If you do it twice you just overwrite.
  772                          */
  773                         if (sp->state != PPPOE_PRIMED) {
  774                                 printf("pppoe: Session not primed\n");
  775                                 LEAVE(EISCONN);
  776                         }
  777                         neg = sp->neg;
  778                         neg->service.hdr.tag_type = PTT_SRV_NAME;
  779                         neg->service.hdr.tag_len =
  780                             htons((u_int16_t)ourmsg->data_len);
  781 
  782                         if (ourmsg->data_len)
  783                                 bcopy(ourmsg->data, neg->service.data,
  784                                     ourmsg->data_len);
  785                         neg->service_len = ourmsg->data_len;
  786                         break;
  787                 default:
  788                         LEAVE(EINVAL);
  789                 }
  790                 break;
  791         default:
  792                 LEAVE(EINVAL);
  793         }
  794 
  795         /* Take care of synchronous response, if any */
  796         if (rptr)
  797                 *rptr = resp;
  798         else if (resp)
  799                 FREE(resp, M_NETGRAPH);
  800 
  801         /* Free the message and return */
  802 quit:
  803         FREE(msg, M_NETGRAPH);
  804         return(error);
  805 }
  806 
  807 /*
  808  * Start a client into the first state. A separate function because
  809  * it can be needed if the negotiation times out.
  810  */
  811 static void
  812 pppoe_start(sessp sp)
  813 {
  814         struct {
  815                 struct pppoe_tag hdr;
  816                 union   uniq    data;
  817         } __attribute ((packed)) uniqtag;
  818 
  819         /* 
  820          * kick the state machine into starting up
  821          */
  822 AAA
  823         sp->state = PPPOE_SINIT;
  824         /* reset the packet header to broadcast */
  825         sp->neg->pkt->pkt_header.eh = eh_prototype;
  826         sp->neg->pkt->pkt_header.ph.code = PADI_CODE;
  827         uniqtag.hdr.tag_type = PTT_HOST_UNIQ;
  828         uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(uniqtag.data));
  829         uniqtag.data.pointer = sp;
  830         init_tags(sp);
  831         insert_tag(sp, &uniqtag.hdr);
  832         insert_tag(sp, &sp->neg->service.hdr);
  833         make_packet(sp);
  834         sendpacket(sp);
  835 }
  836 
  837 static int
  838 send_acname(sessp sp, const struct pppoe_tag *tag)
  839 {
  840         int error, tlen;
  841         struct ng_mesg *msg;
  842         struct ngpppoe_sts *sts;
  843 
  844         NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, NGM_PPPOE_ACNAME,
  845             sizeof(struct ngpppoe_sts), M_NOWAIT);
  846         if (msg == NULL)
  847                 return (ENOMEM);
  848 
  849         sts = (struct ngpppoe_sts *)msg->data;
  850         tlen = min(NG_HOOKLEN, ntohs(tag->tag_len));
  851         strncpy(sts->hook, tag->tag_data, tlen);
  852         sts->hook[tlen] = '\0';
  853         error = ng_send_msg(sp->hook->node, msg, sp->creator, NULL);
  854 
  855         return (error);
  856 }
  857 
  858 static int
  859 send_sessionid(sessp sp)
  860 {
  861         int error;
  862         struct ng_mesg *msg;
  863 
  864         NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, NGM_PPPOE_SESSIONID,
  865             sizeof(u_int16_t), M_NOWAIT);
  866         if (msg == NULL)
  867                 return (ENOMEM);
  868 
  869         *(u_int16_t *)msg->data = sp->Session_ID;
  870         error = ng_send_msg(sp->hook->node, msg, sp->creator, NULL);
  871 
  872         return (error);
  873 }
  874 
  875 /*
  876  * Receive data, and do something with it.
  877  * The caller will never free m or meta, so
  878  * if we use up this data or abort we must free BOTH of these.
  879  */
  880 static int
  881 ng_pppoe_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
  882 {
  883         node_p                  node = hook->node;
  884         const priv_p            privp = node->private;
  885         sessp                   sp = hook->private;
  886         const struct pppoe_full_hdr *wh;
  887         const struct pppoe_hdr  *ph;
  888         int                     error = 0;
  889         u_int16_t               session;
  890         u_int16_t               length;
  891         u_int8_t                code;
  892         const struct pppoe_tag  *utag = NULL, *tag = NULL;
  893         hook_p                  sendhook;
  894         struct {
  895                 struct pppoe_tag hdr;
  896                 union   uniq    data;
  897         } __attribute ((packed)) uniqtag;
  898         negp                    neg = NULL;
  899 
  900 AAA
  901         if (hook->private == &privp->debug_hook) {
  902                 /*
  903                  * Data from the debug hook gets sent without modification
  904                  * straight to the ethernet. 
  905                  */
  906                 NG_SEND_DATA( error, privp->ethernet_hook, m, meta);
  907                 privp->packets_out++;
  908         } else if (hook->private == &privp->ethernet_hook) {
  909                 /*
  910                  * Incoming data. 
  911                  * Dig out various fields from the packet.
  912                  * use them to decide where to send it.
  913                  */
  914                 
  915                 privp->packets_in++;
  916                 if( m->m_len < sizeof(*wh)) {
  917                         m = m_pullup(m, sizeof(*wh)); /* Checks length */
  918                         if (m == NULL) {
  919                                 printf("couldn't m_pullup\n");
  920                                 LEAVE(ENOBUFS);
  921                         }
  922                 }
  923                 wh = mtod(m, struct pppoe_full_hdr *);
  924                 ph = &wh->ph;
  925                 session = ntohs(wh->ph.sid);
  926                 length = ntohs(wh->ph.length);
  927                 code = wh->ph.code; 
  928                 switch(wh->eh.ether_type) {
  929                 case    ETHERTYPE_PPPOE_STUPID_DISC:
  930                         if (pppoe_mode == PPPOE_STANDARD) {
  931                                 pppoe_mode = PPPOE_NONSTANDARD;
  932                                 eh_prototype.ether_type =
  933                                     ETHERTYPE_PPPOE_STUPID_DISC;
  934                                 log(LOG_NOTICE,
  935                                     "Switched to nonstandard PPPoE mode due to "
  936                                     "packet from %*D\n",
  937                                     ETHER_ADDR_LEN,
  938                                     wh->eh.ether_shost, ":");
  939                         } else if (pppoe_mode == PPPOE_KEEPSTANDARD)
  940                                 log(LOG_NOTICE,
  941                                     "Ignored nonstandard PPPoE packet "
  942                                     "from %*D\n",
  943                                     ETHER_ADDR_LEN,
  944                                     wh->eh.ether_shost, ":");
  945                         /* fall through */
  946                 case    ETHERTYPE_PPPOE_DISC:
  947                         /*
  948                          * We need to try to make sure that the tag area
  949                          * is contiguous, or we could wander off the end
  950                          * of a buffer and make a mess. 
  951                          * (Linux wouldn't have this problem).
  952                          */
  953 /*XXX fix this mess */
  954                         
  955                         if (m->m_pkthdr.len <= MHLEN) {
  956                                 if( m->m_len < m->m_pkthdr.len) {
  957                                         m = m_pullup(m, m->m_pkthdr.len);
  958                                         if (m == NULL) {
  959                                                 printf("couldn't m_pullup\n");
  960                                                 LEAVE(ENOBUFS);
  961                                         }
  962                                 }
  963                         }
  964                         if (m->m_len != m->m_pkthdr.len) {
  965                                 /*
  966                                  * It's not all in one piece.
  967                                  * We need to do extra work.
  968                                  */
  969                                 printf("packet fragmented\n");
  970                                 LEAVE(EMSGSIZE);
  971                         }
  972 
  973                         switch(code) {
  974                         case    PADI_CODE:
  975                                 /*
  976                                  * We are a server:
  977                                  * Look for a hook with the required service
  978                                  * and send the ENTIRE packet up there.
  979                                  * It should come back to a new hook in 
  980                                  * PRIMED state. Look there for further
  981                                  * processing.
  982                                  */
  983                                 tag = get_tag(ph, PTT_SRV_NAME);
  984                                 if (tag == NULL) {
  985                                         printf("no service tag\n");
  986                                         LEAVE(ENETUNREACH);
  987                                 }
  988                                 sendhook = pppoe_match_svc(hook->node,
  989                                         tag->tag_data, ntohs(tag->tag_len),
  990                                         NG_MATCH_ANY);
  991                                 if (sendhook) {
  992                                         NG_SEND_DATA(error, sendhook, m, meta);
  993                                 } else {
  994                                         LEAVE(ENETUNREACH);
  995                                 }
  996                                 break;
  997                         case    PADO_CODE:
  998                                 /*
  999                                  * We are a client:
 1000                                  * Use the host_uniq tag to find the 
 1001                                  * hook this is in response to.
 1002                                  * Received #2, now send #3
 1003                                  * For now simply accept the first we receive.
 1004                                  */
 1005                                 utag = get_tag(ph, PTT_HOST_UNIQ);
 1006                                 if ((utag == NULL)
 1007                                 || (ntohs(utag->tag_len) != sizeof(sp))) {
 1008                                         printf("no host unique field\n");
 1009                                         LEAVE(ENETUNREACH);
 1010                                 }
 1011 
 1012                                 sendhook = pppoe_finduniq(node, utag);
 1013                                 if (sendhook == NULL) {
 1014                                         printf("no matching session\n");
 1015                                         LEAVE(ENETUNREACH);
 1016                                 }
 1017 
 1018                                 /*
 1019                                  * Check the session is in the right state.
 1020                                  * It needs to be in PPPOE_SINIT.
 1021                                  */
 1022                                 sp = sendhook->private;
 1023                                 if (sp->state != PPPOE_SINIT) {
 1024                                         printf("session in wrong state\n");
 1025                                         LEAVE(ENETUNREACH);
 1026                                 }
 1027                                 neg = sp->neg;
 1028                                 untimeout(pppoe_ticker, sendhook,
 1029                                     neg->timeout_handle);
 1030 
 1031                                 /*
 1032                                  * This is the first time we hear
 1033                                  * from the server, so note it's
 1034                                  * unicast address, replacing the
 1035                                  * broadcast address .
 1036                                  */
 1037                                 bcopy(wh->eh.ether_shost,
 1038                                         neg->pkt->pkt_header.eh.ether_dhost,
 1039                                         ETHER_ADDR_LEN);
 1040                                 neg->timeout = 0;
 1041                                 neg->pkt->pkt_header.ph.code = PADR_CODE;
 1042                                 init_tags(sp);
 1043                                 insert_tag(sp, utag);      /* Host Unique */
 1044                                 if ((tag = get_tag(ph, PTT_AC_COOKIE)))
 1045                                         insert_tag(sp, tag); /* return cookie */
 1046                                 if ((tag = get_tag(ph, PTT_AC_NAME))) { 
 1047                                         insert_tag(sp, tag); /* return it */
 1048                                         send_acname(sp, tag);
 1049                                 }
 1050                                 insert_tag(sp, &neg->service.hdr); /* Service */
 1051                                 scan_tags(sp, ph);
 1052                                 make_packet(sp);
 1053                                 sp->state = PPPOE_SREQ;
 1054                                 sendpacket(sp);
 1055                                 break;
 1056                         case    PADR_CODE:
 1057 
 1058                                 /*
 1059                                  * We are a server:
 1060                                  * Use the ac_cookie tag to find the 
 1061                                  * hook this is in response to.
 1062                                  */
 1063                                 utag = get_tag(ph, PTT_AC_COOKIE);
 1064                                 if ((utag == NULL)
 1065                                 || (ntohs(utag->tag_len) != sizeof(sp))) {
 1066                                         LEAVE(ENETUNREACH);
 1067                                 }
 1068 
 1069                                 sendhook = pppoe_finduniq(node, utag);
 1070                                 if (sendhook == NULL) {
 1071                                         LEAVE(ENETUNREACH);
 1072                                 }
 1073 
 1074                                 /*
 1075                                  * Check the session is in the right state.
 1076                                  * It needs to be in PPPOE_SOFFER
 1077                                  * or PPPOE_NEWCONNECTED. If the latter,
 1078                                  * then this is a retry by the client.
 1079                                  * so be nice, and resend.
 1080                                  */
 1081                                 sp = sendhook->private;
 1082                                 if (sp->state == PPPOE_NEWCONNECTED) {
 1083                                         /*
 1084                                          * Whoa! drop back to resend that 
 1085                                          * PADS packet.
 1086                                          * We should still have a copy of it.
 1087                                          */
 1088                                         sp->state = PPPOE_SOFFER;
 1089                                 }
 1090                                 if (sp->state != PPPOE_SOFFER) {
 1091                                         LEAVE (ENETUNREACH);
 1092                                         break;
 1093                                 }
 1094                                 neg = sp->neg;
 1095                                 untimeout(pppoe_ticker, sendhook,
 1096                                     neg->timeout_handle);
 1097                                 neg->pkt->pkt_header.ph.code = PADS_CODE;
 1098                                 if (sp->Session_ID == 0)
 1099                                         neg->pkt->pkt_header.ph.sid =
 1100                                             htons(sp->Session_ID
 1101                                                 = get_new_sid(node));
 1102                                 send_sessionid(sp);
 1103                                 neg->timeout = 0;
 1104                                 /*
 1105                                  * start working out the tags to respond with.
 1106                                  */
 1107                                 init_tags(sp);
 1108                                 insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */
 1109                                 if ((tag = get_tag(ph, PTT_SRV_NAME)))
 1110                                         insert_tag(sp, tag);/* return service */
 1111                                 if ((tag = get_tag(ph, PTT_HOST_UNIQ)))
 1112                                         insert_tag(sp, tag); /* return it */
 1113                                 insert_tag(sp, utag);   /* ac_cookie */
 1114                                 scan_tags(sp, ph);
 1115                                 make_packet(sp);
 1116                                 sp->state = PPPOE_NEWCONNECTED;
 1117                                 sendpacket(sp);
 1118                                 /*
 1119                                  * Having sent the last Negotiation header,
 1120                                  * Set up the stored packet header to 
 1121                                  * be correct for the actual session.
 1122                                  * But keep the negotialtion stuff
 1123                                  * around in case we need to resend this last 
 1124                                  * packet. We'll discard it when we move
 1125                                  * from NEWCONNECTED to CONNECTED
 1126                                  */
 1127                                 sp->pkt_hdr = neg->pkt->pkt_header;
 1128                                 if (pppoe_mode == PPPOE_NONSTANDARD)
 1129                                         sp->pkt_hdr.eh.ether_type
 1130                                                 = ETHERTYPE_PPPOE_STUPID_SESS;
 1131                                 else
 1132                                         sp->pkt_hdr.eh.ether_type
 1133                                                 = ETHERTYPE_PPPOE_SESS;
 1134                                 sp->pkt_hdr.ph.code = 0;
 1135                                 pppoe_send_event(sp, NGM_PPPOE_SUCCESS);
 1136                                 break;
 1137                         case    PADS_CODE:
 1138                                 /*
 1139                                  * We are a client:
 1140                                  * Use the host_uniq tag to find the 
 1141                                  * hook this is in response to.
 1142                                  * take the session ID and store it away.
 1143                                  * Also make sure the pre-made header is
 1144                                  * correct and set us into Session mode.
 1145                                  */
 1146                                 utag = get_tag(ph, PTT_HOST_UNIQ);
 1147                                 if ((utag == NULL)
 1148                                 || (ntohs(utag->tag_len) != sizeof(sp))) {
 1149                                         LEAVE (ENETUNREACH);
 1150                                         break;
 1151                                 }
 1152                                 sendhook = pppoe_finduniq(node, utag);
 1153                                 if (sendhook == NULL) {
 1154                                         LEAVE(ENETUNREACH);
 1155                                 }
 1156 
 1157                                 /*
 1158                                  * Check the session is in the right state.
 1159                                  * It needs to be in PPPOE_SREQ.
 1160                                  */
 1161                                 sp = sendhook->private;
 1162                                 if (sp->state != PPPOE_SREQ) {
 1163                                         LEAVE(ENETUNREACH);
 1164                                 }
 1165                                 neg = sp->neg;
 1166                                 untimeout(pppoe_ticker, sendhook,
 1167                                     neg->timeout_handle);
 1168                                 neg->pkt->pkt_header.ph.sid = wh->ph.sid;
 1169                                 sp->Session_ID = ntohs(wh->ph.sid);
 1170                                 send_sessionid(sp);
 1171                                 neg->timeout = 0;
 1172                                 sp->state = PPPOE_CONNECTED;
 1173                                 /*
 1174                                  * Now we have gone to Connected mode, 
 1175                                  * Free all resources needed for 
 1176                                  * negotiation.
 1177                                  * Keep a copy of the header we will be using.
 1178                                  */
 1179                                 sp->pkt_hdr = neg->pkt->pkt_header;
 1180                                 if (pppoe_mode == PPPOE_NONSTANDARD)
 1181                                         sp->pkt_hdr.eh.ether_type
 1182                                                 = ETHERTYPE_PPPOE_STUPID_SESS;
 1183                                 else
 1184                                         sp->pkt_hdr.eh.ether_type
 1185                                                 = ETHERTYPE_PPPOE_SESS;
 1186                                 sp->pkt_hdr.ph.code = 0;
 1187                                 m_freem(neg->m);
 1188                                 FREE(sp->neg, M_NETGRAPH);
 1189                                 sp->neg = NULL;
 1190                                 pppoe_send_event(sp, NGM_PPPOE_SUCCESS);
 1191                                 break;
 1192                         case    PADT_CODE:
 1193                                 /*
 1194                                  * Send a 'close' message to the controlling
 1195                                  * process (the one that set us up);
 1196                                  * And then tear everything down.
 1197                                  *
 1198                                  * Find matching peer/session combination.
 1199                                  */
 1200                                 sendhook = pppoe_findsession(node, wh);
 1201                                 NG_FREE_DATA(m, meta); /* no longer needed */
 1202                                 if (sendhook == NULL) {
 1203                                         LEAVE(ENETUNREACH);
 1204                                 }
 1205                                 /* send message to creator */
 1206                                 /* close hook */
 1207                                 if (sendhook) {
 1208                                         ng_destroy_hook(sendhook);
 1209                                 }
 1210                                 break;
 1211                         default:
 1212                                 LEAVE(EPFNOSUPPORT);
 1213                         }
 1214                         break;
 1215                 case    ETHERTYPE_PPPOE_STUPID_SESS:
 1216                 case    ETHERTYPE_PPPOE_SESS:
 1217                         /*
 1218                          * find matching peer/session combination.
 1219                          */
 1220                         sendhook = pppoe_findsession(node, wh);
 1221                         if (sendhook == NULL) {
 1222                                 LEAVE (ENETUNREACH);
 1223                                 break;
 1224                         }
 1225                         sp = sendhook->private;
 1226                         m_adj(m, sizeof(*wh));
 1227                         if (m->m_pkthdr.len < length) {
 1228                                 /* Packet too short, dump it */
 1229                                 LEAVE(EMSGSIZE);
 1230                         }
 1231 
 1232                         /* Also need to trim excess at the end */
 1233                         if (m->m_pkthdr.len > length) {
 1234                                 m_adj(m, -((int)(m->m_pkthdr.len - length)));
 1235                         }
 1236                         if ( sp->state != PPPOE_CONNECTED) {
 1237                                 if (sp->state == PPPOE_NEWCONNECTED) {
 1238                                         sp->state = PPPOE_CONNECTED;
 1239                                         /*
 1240                                          * Now we have gone to Connected mode, 
 1241                                          * Free all resources needed for 
 1242                                          * negotiation. Be paranoid about
 1243                                          * whether there may be a timeout.
 1244                                          */
 1245                                         m_freem(sp->neg->m);
 1246                                         untimeout(pppoe_ticker, sendhook,
 1247                                                 sp->neg->timeout_handle);
 1248                                         FREE(sp->neg, M_NETGRAPH);
 1249                                         sp->neg = NULL;
 1250                                 } else {
 1251                                         LEAVE (ENETUNREACH);
 1252                                         break;
 1253                                 }
 1254                         }
 1255                         NG_SEND_DATA( error, sendhook, m, meta);
 1256                         break;
 1257                 default:
 1258                         LEAVE(EPFNOSUPPORT);
 1259                 }
 1260         } else {
 1261                 /*
 1262                  *      Not ethernet or debug hook..
 1263                  *
 1264                  * The packet has come in on a normal hook.
 1265                  * We need to find out what kind of hook,
 1266                  * So we can decide how to handle it.
 1267                  * Check the hook's state.
 1268                  */
 1269                 sp = hook->private;
 1270                 switch (sp->state) {
 1271                 case    PPPOE_NEWCONNECTED:
 1272                 case    PPPOE_CONNECTED: {
 1273                         static const u_char addrctrl[] = { 0xff, 0x03 };
 1274                         struct pppoe_full_hdr *wh;
 1275 
 1276                         /*
 1277                          * Remove PPP address and control fields, if any.
 1278                          * For example, ng_ppp(4) always sends LCP packets
 1279                          * with address and control fields as required by
 1280                          * generic PPP. PPPoE is an exception to the rule.
 1281                          */
 1282                         if (m->m_pkthdr.len >= 2) {
 1283                                 if (m->m_len < 2 && !(m = m_pullup(m, 2)))
 1284                                         LEAVE(ENOBUFS);
 1285                                 if (bcmp(mtod(m, u_char *), addrctrl, 2) == 0)
 1286                                         m_adj(m, 2);
 1287                         }
 1288                         /*
 1289                          * Bang in a pre-made header, and set the length up
 1290                          * to be correct. Then send it to the ethernet driver.
 1291                          * But first correct the length.
 1292                          */
 1293                         sp->pkt_hdr.ph.length = htons((short)(m->m_pkthdr.len));
 1294                         M_PREPEND(m, sizeof(*wh), M_DONTWAIT);
 1295                         if (m == NULL) {
 1296                                 LEAVE(ENOBUFS);
 1297                         }
 1298                         wh = mtod(m, struct pppoe_full_hdr *);
 1299                         bcopy(&sp->pkt_hdr, wh, sizeof(*wh));
 1300                         NG_SEND_DATA( error, privp->ethernet_hook, m, meta);
 1301                         privp->packets_out++;
 1302                         break;
 1303                         }
 1304                 case    PPPOE_PRIMED:
 1305                         /*
 1306                          * A PADI packet is being returned by the application
 1307                          * that has set up this hook. This indicates that it 
 1308                          * wants us to offer service.
 1309                          */
 1310                         neg = sp->neg;
 1311                         if (m->m_len < sizeof(*wh)) {
 1312                                 m = m_pullup(m, sizeof(*wh));
 1313                                 if (m == NULL) {
 1314                                         LEAVE(ENOBUFS);
 1315                                 }
 1316                         }
 1317                         wh = mtod(m, struct pppoe_full_hdr *);
 1318                         ph = &wh->ph;
 1319                         session = ntohs(wh->ph.sid);
 1320                         length = ntohs(wh->ph.length);
 1321                         code = wh->ph.code; 
 1322                         if ( code != PADI_CODE) {
 1323                                 LEAVE(EINVAL);
 1324                         };
 1325                         untimeout(pppoe_ticker, hook,
 1326                                     neg->timeout_handle);
 1327 
 1328                         /*
 1329                          * This is the first time we hear
 1330                          * from the client, so note it's
 1331                          * unicast address, replacing the
 1332                          * broadcast address.
 1333                          */
 1334                         bcopy(wh->eh.ether_shost,
 1335                                 neg->pkt->pkt_header.eh.ether_dhost,
 1336                                 ETHER_ADDR_LEN);
 1337                         sp->state = PPPOE_SOFFER;
 1338                         neg->timeout = 0;
 1339                         neg->pkt->pkt_header.ph.code = PADO_CODE;
 1340 
 1341                         /*
 1342                          * start working out the tags to respond with.
 1343                          */
 1344                         uniqtag.hdr.tag_type = PTT_AC_COOKIE;
 1345                         uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(sp));
 1346                         uniqtag.data.pointer = sp;
 1347                         init_tags(sp);
 1348                         insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */
 1349                         if ((tag = get_tag(ph, PTT_SRV_NAME)))
 1350                                 insert_tag(sp, tag);      /* return service */
 1351                         /*
 1352                          * If we have a NULL service request
 1353                          * and have an extra service defined in this hook,
 1354                          * then also add a tag for the extra service.
 1355                          * XXX this is a hack. eventually we should be able
 1356                          * to support advertising many services, not just one 
 1357                          */
 1358                         if (((tag == NULL) || (tag->tag_len == 0))
 1359                         && (neg->service.hdr.tag_len != 0)) {
 1360                                 insert_tag(sp, &neg->service.hdr); /* SERVICE */
 1361                         }
 1362                         if ((tag = get_tag(ph, PTT_HOST_UNIQ)))
 1363                                 insert_tag(sp, tag); /* returned hostunique */
 1364                         insert_tag(sp, &uniqtag.hdr);
 1365                         scan_tags(sp, ph);
 1366                         make_packet(sp);
 1367                         sendpacket(sp);
 1368                         break;
 1369 
 1370                 /*
 1371                  * Packets coming from the hook make no sense
 1372                  * to sessions in these states. Throw them away.
 1373                  */
 1374                 case    PPPOE_SINIT:
 1375                 case    PPPOE_SREQ:
 1376                 case    PPPOE_SOFFER:
 1377                 case    PPPOE_SNONE:
 1378                 case    PPPOE_LISTENING:
 1379                 case    PPPOE_DEAD:
 1380                 default:
 1381                         LEAVE(ENETUNREACH);
 1382                 }
 1383         }
 1384 quit:
 1385         NG_FREE_DATA(m, meta);
 1386         return error;
 1387 }
 1388 
 1389 /*
 1390  * Do local shutdown processing..
 1391  * If we are a persistant device, we might refuse to go away, and
 1392  * we'd only remove our links and reset ourself.
 1393  */
 1394 static int
 1395 ng_pppoe_rmnode(node_p node)
 1396 {
 1397         const priv_p privdata = node->private;
 1398 
 1399 AAA
 1400         node->flags |= NG_INVALID;
 1401         ng_cutlinks(node);
 1402         ng_unname(node);
 1403         node->private = NULL;
 1404         ng_unref(privdata->node);
 1405         FREE(privdata, M_NETGRAPH);
 1406         return (0);
 1407 }
 1408 
 1409 /*
 1410  * This is called once we've already connected a new hook to the other node.
 1411  * It gives us a chance to balk at the last minute.
 1412  */
 1413 static int
 1414 ng_pppoe_connect(hook_p hook)
 1415 {
 1416         /* be really amiable and just say "YUP that's OK by me! " */
 1417         return (0);
 1418 }
 1419 
 1420 /*
 1421  * Hook disconnection
 1422  *
 1423  * Clean up all dangling links and information about the session/hook.
 1424  * For this type, removal of the last link destroys the node
 1425  */
 1426 static int
 1427 ng_pppoe_disconnect(hook_p hook)
 1428 {
 1429         node_p node = hook->node;
 1430         priv_p privp = node->private;
 1431         sessp   sp;
 1432         int     hooks;
 1433 
 1434 AAA
 1435         hooks = node->numhooks; /* this one already not counted */
 1436         if (hook->private == &privp->debug_hook) {
 1437                 privp->debug_hook = NULL;
 1438         } else if (hook->private == &privp->ethernet_hook) {
 1439                 privp->ethernet_hook = NULL;
 1440                 ng_rmnode(node);
 1441         } else {
 1442                 sp = hook->private;
 1443                 if (sp->state != PPPOE_SNONE ) {
 1444                         pppoe_send_event(sp, NGM_PPPOE_CLOSE);
 1445                 }
 1446                 /*
 1447                  * According to the spec, if we are connected,
 1448                  * we should send a DISC packet if we are shutting down
 1449                  * a session.
 1450                  */
 1451                 if ((privp->ethernet_hook)
 1452                 && ((sp->state == PPPOE_CONNECTED)
 1453                  || (sp->state == PPPOE_NEWCONNECTED))) {
 1454                         struct mbuf *m;
 1455                         struct pppoe_full_hdr *wh;
 1456                         struct pppoe_tag *tag;
 1457                         int     msglen = strlen(SIGNOFF);
 1458                         void *dummy = NULL;
 1459                         int error = 0;
 1460 
 1461                         /* revert the stored header to DISC/PADT mode */
 1462                         wh = &sp->pkt_hdr;
 1463                         wh->ph.code = PADT_CODE;
 1464                         if (pppoe_mode == PPPOE_NONSTANDARD)
 1465                                 wh->eh.ether_type = ETHERTYPE_PPPOE_STUPID_DISC;
 1466                         else
 1467                                 wh->eh.ether_type = ETHERTYPE_PPPOE_DISC;
 1468 
 1469                         /* generate a packet of that type */
 1470                         MGETHDR(m, M_DONTWAIT, MT_DATA);
 1471                         if(m == NULL)
 1472                                 printf("pppoe: Session out of mbufs\n");
 1473                         else {
 1474                                 m->m_pkthdr.rcvif = NULL;
 1475                                 m->m_pkthdr.len = m->m_len = sizeof(*wh);
 1476                                 bcopy((caddr_t)wh, mtod(m, caddr_t),
 1477                                     sizeof(*wh));
 1478                                 /*
 1479                                  * Add a General error message and adjust
 1480                                  * sizes
 1481                                  */
 1482                                 wh = mtod(m, struct pppoe_full_hdr *);
 1483                                 tag = wh->ph.tag;
 1484                                 tag->tag_type = PTT_GEN_ERR;
 1485                                 tag->tag_len = htons((u_int16_t)msglen);
 1486                                 strncpy(tag->tag_data, SIGNOFF, msglen);
 1487                                 m->m_pkthdr.len = (m->m_len += sizeof(*tag) +
 1488                                     msglen);
 1489                                 wh->ph.length = htons(sizeof(*tag) + msglen);
 1490                                 NG_SEND_DATA(error, privp->ethernet_hook, m,
 1491                                     dummy);
 1492                         }
 1493                 }
 1494                 /*
 1495                  * As long as we have somewhere to store the timeout handle,
 1496                  * we may have a timeout pending.. get rid of it.
 1497                  */
 1498                 if (sp->neg) {
 1499                         untimeout(pppoe_ticker, hook, sp->neg->timeout_handle);
 1500                         if (sp->neg->m)
 1501                                 m_freem(sp->neg->m);
 1502                         FREE(sp->neg, M_NETGRAPH);
 1503                 }
 1504                 FREE(sp, M_NETGRAPH);
 1505                 hook->private = NULL;
 1506                 /* work out how many session hooks there are */
 1507                 /* Node goes away on last session hook removal */
 1508                 if (privp->ethernet_hook) hooks -= 1;
 1509                 if (privp->debug_hook) hooks -= 1;
 1510         }
 1511         if (node->numhooks == 0)
 1512                 ng_rmnode(node);
 1513         return (0);
 1514 }
 1515 
 1516 /*
 1517  * timeouts come here.
 1518  */
 1519 static void
 1520 pppoe_ticker(void *arg)
 1521 {
 1522         int s = splnet();
 1523         hook_p hook = arg;
 1524         sessp   sp = hook->private;
 1525         negp    neg = sp->neg;
 1526         int     error = 0;
 1527         struct mbuf *m0 = NULL;
 1528         priv_p privp = hook->node->private;
 1529         meta_p dummy = NULL;
 1530 
 1531 AAA
 1532         switch(sp->state) {
 1533                 /*
 1534                  * resend the last packet, using an exponential backoff.
 1535                  * After a period of time, stop growing the backoff,
 1536                  * and either leave it, or revert to the start.
 1537                  */
 1538         case    PPPOE_SINIT:
 1539         case    PPPOE_SREQ:
 1540                 /* timeouts on these produce resends */
 1541                 m0 = m_copypacket(sp->neg->m, M_DONTWAIT);
 1542                 NG_SEND_DATA( error, privp->ethernet_hook, m0, dummy);
 1543                 neg->timeout_handle = timeout(pppoe_ticker,
 1544                                         hook, neg->timeout * hz);
 1545                 if ((neg->timeout <<= 1) > PPPOE_TIMEOUT_LIMIT) {
 1546                         if (sp->state == PPPOE_SREQ) {
 1547                                 /* revert to SINIT mode */
 1548                                 pppoe_start(sp);
 1549                         } else {
 1550                                 neg->timeout = PPPOE_TIMEOUT_LIMIT;
 1551                         }
 1552                 }
 1553                 break;
 1554         case    PPPOE_PRIMED:
 1555         case    PPPOE_SOFFER:
 1556                 /* a timeout on these says "give up" */
 1557                 ng_destroy_hook(hook);
 1558                 break;
 1559         default:
 1560                 /* timeouts have no meaning in other states */
 1561                 printf("pppoe: unexpected timeout\n");
 1562         }
 1563         splx(s);
 1564 }
 1565 
 1566 
 1567 static void
 1568 sendpacket(sessp sp)
 1569 {
 1570         int     error = 0;
 1571         struct mbuf *m0 = NULL;
 1572         hook_p hook = sp->hook;
 1573         negp    neg = sp->neg;
 1574         priv_p  privp = hook->node->private;
 1575         meta_p dummy = NULL;
 1576 
 1577 AAA
 1578         switch(sp->state) {
 1579         case    PPPOE_LISTENING:
 1580         case    PPPOE_DEAD:
 1581         case    PPPOE_SNONE:
 1582         case    PPPOE_CONNECTED:
 1583                 printf("pppoe: sendpacket: unexpected state\n");
 1584                 break;
 1585 
 1586         case    PPPOE_NEWCONNECTED:
 1587                 /* send the PADS without a timeout - we're now connected */
 1588                 m0 = m_copypacket(sp->neg->m, M_DONTWAIT);
 1589                 NG_SEND_DATA( error, privp->ethernet_hook, m0, dummy);
 1590                 break;
 1591 
 1592         case    PPPOE_PRIMED:
 1593                 /* No packet to send, but set up the timeout */
 1594                 neg->timeout_handle = timeout(pppoe_ticker,
 1595                                         hook, PPPOE_OFFER_TIMEOUT * hz);
 1596                 break;
 1597 
 1598         case    PPPOE_SOFFER:
 1599                 /*
 1600                  * send the offer but if they don't respond
 1601                  * in PPPOE_OFFER_TIMEOUT seconds, forget about it.
 1602                  */
 1603                 m0 = m_copypacket(sp->neg->m, M_DONTWAIT);
 1604                 NG_SEND_DATA( error, privp->ethernet_hook, m0, dummy);
 1605                 neg->timeout_handle = timeout(pppoe_ticker,
 1606                                         hook, PPPOE_OFFER_TIMEOUT * hz);
 1607                 break;
 1608 
 1609         case    PPPOE_SINIT:
 1610         case    PPPOE_SREQ:
 1611                 m0 = m_copypacket(sp->neg->m, M_DONTWAIT);
 1612                 NG_SEND_DATA( error, privp->ethernet_hook, m0, dummy);
 1613                 neg->timeout_handle = timeout(pppoe_ticker, hook,
 1614                                         (hz * PPPOE_INITIAL_TIMEOUT));
 1615                 neg->timeout = PPPOE_INITIAL_TIMEOUT * 2;
 1616                 break;
 1617 
 1618         default:
 1619                 error = EINVAL;
 1620                 printf("pppoe: timeout: bad state\n");
 1621         }
 1622         /* return (error); */
 1623 }
 1624 
 1625 /*
 1626  * Parse an incoming packet to see if any tags should be copied to the
 1627  * output packet. Don't do any tags that have been handled in the main
 1628  * state machine.
 1629  */
 1630 static const struct pppoe_tag* 
 1631 scan_tags(sessp sp, const struct pppoe_hdr* ph)
 1632 {
 1633         const char *const end = (const char *)next_tag(ph);
 1634         const char *ptn;
 1635         const struct pppoe_tag *pt = &ph->tag[0];
 1636         /*
 1637          * Keep processing tags while a tag header will still fit.
 1638          */
 1639 AAA
 1640         while((const char*)(pt + 1) <= end) {
 1641                 /*
 1642                  * If the tag data would go past the end of the packet, abort.
 1643                  */
 1644                 ptn = (((const char *)(pt + 1)) + ntohs(pt->tag_len));
 1645                 if(ptn > end)
 1646                         return NULL;
 1647 
 1648                 switch (pt->tag_type) {
 1649                 case    PTT_RELAY_SID:
 1650                         insert_tag(sp, pt);
 1651                         break;
 1652                 case    PTT_EOL:
 1653                         return NULL;
 1654                 case    PTT_SRV_NAME:
 1655                 case    PTT_AC_NAME:
 1656                 case    PTT_HOST_UNIQ:
 1657                 case    PTT_AC_COOKIE:
 1658                 case    PTT_VENDOR:
 1659                 case    PTT_SRV_ERR:
 1660                 case    PTT_SYS_ERR:
 1661                 case    PTT_GEN_ERR:
 1662                         break;
 1663                 }
 1664                 pt = (const struct pppoe_tag*)ptn;
 1665         }
 1666         return NULL;
 1667 }
 1668         
 1669 static  int
 1670 pppoe_send_event(sessp sp, enum cmd cmdid)
 1671 {
 1672         int error;
 1673         struct ng_mesg *msg;
 1674         struct ngpppoe_sts *sts;
 1675 
 1676 AAA
 1677         NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, cmdid,
 1678                         sizeof(struct ngpppoe_sts), M_NOWAIT);
 1679         if (msg == NULL)
 1680                 return (ENOMEM);
 1681         sts = (struct ngpppoe_sts *)msg->data;
 1682         strncpy(sts->hook, sp->hook->name, NG_HOOKLEN + 1);
 1683         error = ng_send_msg(sp->hook->node, msg, sp->creator, NULL);
 1684         return (error);
 1685 }

Cache object: 07117ef00d91487149c845032510a735


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.