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

Cache object: 74dbac2f75aa29092388320783cef4d6


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