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