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

Cache object: ca1cbfdc79e207a8d39954e86021f6e2


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