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