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

Cache object: 6d0251bc4fd1580d7f0ea1df50d1ffd7


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