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


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

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

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

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

Cache object: 79a6d5f033be41aa827c9a58a9c2cdeb


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