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