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