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