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