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