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

Cache object: 38fe58463ef4694b20ac6a6e3414af56


[ 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.