1/***********************************************************************
2*
3* discovery.c
4*
5* Perform PPPoE discovery
6*
7* Copyright (C) 1999 by Roaring Penguin Software Inc.
8*
9***********************************************************************/
10
11static char const RCSID[] =
12"$Id: discovery.c,v 1.3 2004/11/04 10:07:37 paulus Exp $";
13
14#include "pppoe.h"
15
16#ifdef HAVE_SYSLOG_H
17#include <syslog.h>
18#endif
19
20#include <string.h>
21#include <stdlib.h>
22#include <errno.h>
23
24#ifdef HAVE_SYS_TIME_H
25#include <sys/time.h>
26#endif
27
28#ifdef HAVE_SYS_UIO_H
29#include <sys/uio.h>
30#endif
31
32#ifdef HAVE_UNISTD_H
33#include <unistd.h>
34#endif
35
36#ifdef USE_LINUX_PACKET
37#include <sys/ioctl.h>
38#include <fcntl.h>
39#endif
40
41#include <signal.h>
42
43/**********************************************************************
44*%FUNCTION: parseForHostUniq
45*%ARGUMENTS:
46* type -- tag type
47* len -- tag length
48* data -- tag data.
49* extra -- user-supplied pointer.  This is assumed to be a pointer to int.
50*%RETURNS:
51* Nothing
52*%DESCRIPTION:
53* If a HostUnique tag is found which matches our PID, sets *extra to 1.
54***********************************************************************/
55void
56parseForHostUniq(UINT16_t type, UINT16_t len, unsigned char *data,
57		 void *extra)
58{
59    int *val = (int *) extra;
60    if (type == TAG_HOST_UNIQ && len == sizeof(pid_t)) {
61	pid_t tmp;
62	memcpy(&tmp, data, len);
63	if (tmp == getpid()) {
64	    *val = 1;
65	}
66    }
67}
68
69/**********************************************************************
70*%FUNCTION: packetIsForMe
71*%ARGUMENTS:
72* conn -- PPPoE connection info
73* packet -- a received PPPoE packet
74*%RETURNS:
75* 1 if packet is for this PPPoE daemon; 0 otherwise.
76*%DESCRIPTION:
77* If we are using the Host-Unique tag, verifies that packet contains
78* our unique identifier.
79***********************************************************************/
80int
81packetIsForMe(PPPoEConnection *conn, PPPoEPacket *packet)
82{
83    int forMe = 0;
84
85    /* If packet is not directed to our MAC address, forget it */
86    if (memcmp(packet->ethHdr.h_dest, conn->myEth, ETH_ALEN)) return 0;
87
88    /* If we're not using the Host-Unique tag, then accept the packet */
89    if (!conn->useHostUniq) return 1;
90
91    parsePacket(packet, parseForHostUniq, &forMe);
92    return forMe;
93}
94
95/**********************************************************************
96*%FUNCTION: parsePADOTags
97*%ARGUMENTS:
98* type -- tag type
99* len -- tag length
100* data -- tag data
101* extra -- extra user data.  Should point to a PacketCriteria structure
102*          which gets filled in according to selected AC name and service
103*          name.
104*%RETURNS:
105* Nothing
106*%DESCRIPTION:
107* Picks interesting tags out of a PADO packet
108***********************************************************************/
109void
110parsePADOTags(UINT16_t type, UINT16_t len, unsigned char *data,
111	      void *extra)
112{
113    struct PacketCriteria *pc = (struct PacketCriteria *) extra;
114    PPPoEConnection *conn = pc->conn;
115    int i;
116
117    switch(type) {
118    case TAG_AC_NAME:
119	pc->seenACName = 1;
120	if (conn->printACNames) {
121	    printf("Access-Concentrator: %.*s\n", (int) len, data);
122	}
123	if (conn->acName && len == strlen(conn->acName) &&
124	    !strncmp((char *) data, conn->acName, len)) {
125	    pc->acNameOK = 1;
126	}
127	break;
128    case TAG_SERVICE_NAME:
129	pc->seenServiceName = 1;
130	if (conn->printACNames && len > 0) {
131	    printf("       Service-Name: %.*s\n", (int) len, data);
132	}
133	if (conn->serviceName && len == strlen(conn->serviceName) &&
134	    !strncmp((char *) data, conn->serviceName, len)) {
135	    pc->serviceNameOK = 1;
136	}
137	break;
138    case TAG_AC_COOKIE:
139	if (conn->printACNames) {
140	    printf("Got a cookie:");
141	    /* Print first 20 bytes of cookie */
142	    for (i=0; i<len && i < 20; i++) {
143		printf(" %02x", (unsigned) data[i]);
144	    }
145	    if (i < len) printf("...");
146	    printf("\n");
147	}
148	conn->cookie.type = htons(type);
149	conn->cookie.length = htons(len);
150	memcpy(conn->cookie.payload, data, len);
151	break;
152    case TAG_RELAY_SESSION_ID:
153	if (conn->printACNames) {
154	    printf("Got a Relay-ID:");
155	    /* Print first 20 bytes of relay ID */
156	    for (i=0; i<len && i < 20; i++) {
157		printf(" %02x", (unsigned) data[i]);
158	    }
159	    if (i < len) printf("...");
160	    printf("\n");
161	}
162	conn->relayId.type = htons(type);
163	conn->relayId.length = htons(len);
164	memcpy(conn->relayId.payload, data, len);
165	break;
166    case TAG_SERVICE_NAME_ERROR:
167	if (conn->printACNames) {
168	    printf("Got a Service-Name-Error tag: %.*s\n", (int) len, data);
169	} else {
170	    syslog(LOG_ERR, "PADO: Service-Name-Error: %.*s", (int) len, data);
171	    exit(1);
172	}
173	break;
174    case TAG_AC_SYSTEM_ERROR:
175	if (conn->printACNames) {
176	    printf("Got a System-Error tag: %.*s\n", (int) len, data);
177	} else {
178	    syslog(LOG_ERR, "PADO: System-Error: %.*s", (int) len, data);
179	    exit(1);
180	}
181	break;
182    case TAG_GENERIC_ERROR:
183	if (conn->printACNames) {
184	    printf("Got a Generic-Error tag: %.*s\n", (int) len, data);
185	} else {
186	    syslog(LOG_ERR, "PADO: Generic-Error: %.*s", (int) len, data);
187	    exit(1);
188	}
189	break;
190    }
191}
192
193/**********************************************************************
194*%FUNCTION: parsePADSTags
195*%ARGUMENTS:
196* type -- tag type
197* len -- tag length
198* data -- tag data
199* extra -- extra user data (pointer to PPPoEConnection structure)
200*%RETURNS:
201* Nothing
202*%DESCRIPTION:
203* Picks interesting tags out of a PADS packet
204***********************************************************************/
205void
206parsePADSTags(UINT16_t type, UINT16_t len, unsigned char *data,
207	      void *extra)
208{
209    PPPoEConnection *conn = (PPPoEConnection *) extra;
210    switch(type) {
211    case TAG_SERVICE_NAME:
212	syslog(LOG_DEBUG, "PADS: Service-Name: '%.*s'", (int) len, data);
213	break;
214    case TAG_SERVICE_NAME_ERROR:
215	syslog(LOG_ERR, "PADS: Service-Name-Error: %.*s", (int) len, data);
216	fprintf(stderr, "PADS: Service-Name-Error: %.*s\n", (int) len, data);
217	exit(1);
218    case TAG_AC_SYSTEM_ERROR:
219	syslog(LOG_ERR, "PADS: System-Error: %.*s", (int) len, data);
220	fprintf(stderr, "PADS: System-Error: %.*s\n", (int) len, data);
221	exit(1);
222    case TAG_GENERIC_ERROR:
223	syslog(LOG_ERR, "PADS: Generic-Error: %.*s", (int) len, data);
224	fprintf(stderr, "PADS: Generic-Error: %.*s\n", (int) len, data);
225	exit(1);
226    case TAG_RELAY_SESSION_ID:
227	conn->relayId.type = htons(type);
228	conn->relayId.length = htons(len);
229	memcpy(conn->relayId.payload, data, len);
230	break;
231    }
232}
233
234/***********************************************************************
235*%FUNCTION: sendPADI
236*%ARGUMENTS:
237* conn -- PPPoEConnection structure
238*%RETURNS:
239* Nothing
240*%DESCRIPTION:
241* Sends a PADI packet
242***********************************************************************/
243void
244sendPADI(PPPoEConnection *conn)
245{
246    PPPoEPacket packet;
247    unsigned char *cursor = packet.payload;
248    PPPoETag *svc = (PPPoETag *) (&packet.payload);
249    UINT16_t namelen = 0;
250    UINT16_t plen;
251
252    if (conn->serviceName) {
253	namelen = (UINT16_t) strlen(conn->serviceName);
254    }
255    plen = TAG_HDR_SIZE + namelen;
256    CHECK_ROOM(cursor, packet.payload, plen);
257
258    /* Set destination to Ethernet broadcast address */
259    memset(packet.ethHdr.h_dest, 0xFF, ETH_ALEN);
260    memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN);
261
262    packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
263    packet.ver = 1;
264    packet.type = 1;
265    packet.code = CODE_PADI;
266    packet.session = 0;
267
268    svc->type = TAG_SERVICE_NAME;
269    svc->length = htons(namelen);
270    CHECK_ROOM(cursor, packet.payload, namelen+TAG_HDR_SIZE);
271
272    if (conn->serviceName) {
273	memcpy(svc->payload, conn->serviceName, strlen(conn->serviceName));
274    }
275    cursor += namelen + TAG_HDR_SIZE;
276
277    /* If we're using Host-Uniq, copy it over */
278    if (conn->useHostUniq) {
279	PPPoETag hostUniq;
280	pid_t pid = getpid();
281	hostUniq.type = htons(TAG_HOST_UNIQ);
282	hostUniq.length = htons(sizeof(pid));
283	memcpy(hostUniq.payload, &pid, sizeof(pid));
284	CHECK_ROOM(cursor, packet.payload, sizeof(pid) + TAG_HDR_SIZE);
285	memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE);
286	cursor += sizeof(pid) + TAG_HDR_SIZE;
287	plen += sizeof(pid) + TAG_HDR_SIZE;
288    }
289
290    packet.length = htons(plen);
291
292    sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE));
293    if (conn->debugFile) {
294	dumpPacket(conn->debugFile, &packet, "SENT");
295	fprintf(conn->debugFile, "\n");
296	fflush(conn->debugFile);
297    }
298}
299
300/**********************************************************************
301*%FUNCTION: waitForPADO
302*%ARGUMENTS:
303* conn -- PPPoEConnection structure
304* timeout -- how long to wait (in seconds)
305*%RETURNS:
306* Nothing
307*%DESCRIPTION:
308* Waits for a PADO packet and copies useful information
309***********************************************************************/
310void
311waitForPADO(PPPoEConnection *conn, int timeout)
312{
313    fd_set readable;
314    int r;
315    struct timeval tv;
316    PPPoEPacket packet;
317    int len;
318
319    struct PacketCriteria pc;
320    pc.conn          = conn;
321    pc.acNameOK      = (conn->acName)      ? 0 : 1;
322    pc.serviceNameOK = (conn->serviceName) ? 0 : 1;
323    pc.seenACName    = 0;
324    pc.seenServiceName = 0;
325
326    do {
327	if (BPF_BUFFER_IS_EMPTY) {
328	    tv.tv_sec = timeout;
329	    tv.tv_usec = 0;
330
331	    FD_ZERO(&readable);
332	    FD_SET(conn->discoverySocket, &readable);
333
334	    while(1) {
335		r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv);
336		if (r >= 0 || errno != EINTR) break;
337	    }
338	    if (r < 0) {
339		fatalSys("select (waitForPADO)");
340	    }
341	    if (r == 0) return;        /* Timed out */
342	}
343
344	/* Get the packet */
345	receivePacket(conn->discoverySocket, &packet, &len);
346
347	/* Check length */
348	if (ntohs(packet.length) + HDR_SIZE > len) {
349	    syslog(LOG_ERR, "Bogus PPPoE length field (%u)",
350		   (unsigned int) ntohs(packet.length));
351	    continue;
352	}
353
354#ifdef USE_BPF
355	/* If it's not a Discovery packet, loop again */
356	if (etherType(&packet) != Eth_PPPOE_Discovery) continue;
357#endif
358
359	if (conn->debugFile) {
360	    dumpPacket(conn->debugFile, &packet, "RCVD");
361	    fprintf(conn->debugFile, "\n");
362	    fflush(conn->debugFile);
363	}
364	/* If it's not for us, loop again */
365	if (!packetIsForMe(conn, &packet)) continue;
366
367	if (packet.code == CODE_PADO) {
368	    if (NOT_UNICAST(packet.ethHdr.h_source)) {
369		printErr("Ignoring PADO packet from non-unicast MAC address");
370		continue;
371	    }
372	    parsePacket(&packet, parsePADOTags, &pc);
373	    if (!pc.seenACName) {
374		printErr("Ignoring PADO packet with no AC-Name tag");
375		continue;
376	    }
377	    if (!pc.seenServiceName) {
378		printErr("Ignoring PADO packet with no Service-Name tag");
379		continue;
380	    }
381	    conn->numPADOs++;
382	    if (conn->printACNames) {
383		printf("--------------------------------------------------\n");
384	    }
385	    if (pc.acNameOK && pc.serviceNameOK) {
386		memcpy(conn->peerEth, packet.ethHdr.h_source, ETH_ALEN);
387		if (conn->printACNames) {
388		    printf("AC-Ethernet-Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
389			   (unsigned) conn->peerEth[0],
390			   (unsigned) conn->peerEth[1],
391			   (unsigned) conn->peerEth[2],
392			   (unsigned) conn->peerEth[3],
393			   (unsigned) conn->peerEth[4],
394			   (unsigned) conn->peerEth[5]);
395		    continue;
396		}
397		conn->discoveryState = STATE_RECEIVED_PADO;
398		break;
399	    }
400	}
401    } while (conn->discoveryState != STATE_RECEIVED_PADO);
402}
403
404/***********************************************************************
405*%FUNCTION: sendPADR
406*%ARGUMENTS:
407* conn -- PPPoE connection structur
408*%RETURNS:
409* Nothing
410*%DESCRIPTION:
411* Sends a PADR packet
412***********************************************************************/
413void
414sendPADR(PPPoEConnection *conn)
415{
416    PPPoEPacket packet;
417    PPPoETag *svc = (PPPoETag *) packet.payload;
418    unsigned char *cursor = packet.payload;
419
420    UINT16_t namelen = 0;
421    UINT16_t plen;
422
423    if (conn->serviceName) {
424	namelen = (UINT16_t) strlen(conn->serviceName);
425    }
426    plen = TAG_HDR_SIZE + namelen;
427    CHECK_ROOM(cursor, packet.payload, plen);
428
429    memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN);
430    memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN);
431
432    packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
433    packet.ver = 1;
434    packet.type = 1;
435    packet.code = CODE_PADR;
436    packet.session = 0;
437
438    svc->type = TAG_SERVICE_NAME;
439    svc->length = htons(namelen);
440    if (conn->serviceName) {
441	memcpy(svc->payload, conn->serviceName, namelen);
442    }
443    cursor += namelen + TAG_HDR_SIZE;
444
445    /* If we're using Host-Uniq, copy it over */
446    if (conn->useHostUniq) {
447	PPPoETag hostUniq;
448	pid_t pid = getpid();
449	hostUniq.type = htons(TAG_HOST_UNIQ);
450	hostUniq.length = htons(sizeof(pid));
451	memcpy(hostUniq.payload, &pid, sizeof(pid));
452	CHECK_ROOM(cursor, packet.payload, sizeof(pid)+TAG_HDR_SIZE);
453	memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE);
454	cursor += sizeof(pid) + TAG_HDR_SIZE;
455	plen += sizeof(pid) + TAG_HDR_SIZE;
456    }
457
458    /* Copy cookie and relay-ID if needed */
459    if (conn->cookie.type) {
460	CHECK_ROOM(cursor, packet.payload,
461		   ntohs(conn->cookie.length) + TAG_HDR_SIZE);
462	memcpy(cursor, &conn->cookie, ntohs(conn->cookie.length) + TAG_HDR_SIZE);
463	cursor += ntohs(conn->cookie.length) + TAG_HDR_SIZE;
464	plen += ntohs(conn->cookie.length) + TAG_HDR_SIZE;
465    }
466
467    if (conn->relayId.type) {
468	CHECK_ROOM(cursor, packet.payload,
469		   ntohs(conn->relayId.length) + TAG_HDR_SIZE);
470	memcpy(cursor, &conn->relayId, ntohs(conn->relayId.length) + TAG_HDR_SIZE);
471	cursor += ntohs(conn->relayId.length) + TAG_HDR_SIZE;
472	plen += ntohs(conn->relayId.length) + TAG_HDR_SIZE;
473    }
474
475    packet.length = htons(plen);
476    sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE));
477    if (conn->debugFile) {
478	dumpPacket(conn->debugFile, &packet, "SENT");
479	fprintf(conn->debugFile, "\n");
480	fflush(conn->debugFile);
481    }
482}
483
484/**********************************************************************
485*%FUNCTION: waitForPADS
486*%ARGUMENTS:
487* conn -- PPPoE connection info
488* timeout -- how long to wait (in seconds)
489*%RETURNS:
490* Nothing
491*%DESCRIPTION:
492* Waits for a PADS packet and copies useful information
493***********************************************************************/
494void
495waitForPADS(PPPoEConnection *conn, int timeout)
496{
497    fd_set readable;
498    int r;
499    struct timeval tv;
500    PPPoEPacket packet;
501    int len;
502
503    do {
504	if (BPF_BUFFER_IS_EMPTY) {
505	    tv.tv_sec = timeout;
506	    tv.tv_usec = 0;
507
508	    FD_ZERO(&readable);
509	    FD_SET(conn->discoverySocket, &readable);
510
511	    while(1) {
512		r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv);
513		if (r >= 0 || errno != EINTR) break;
514	    }
515	    if (r < 0) {
516		fatalSys("select (waitForPADS)");
517	    }
518	    if (r == 0) return;
519	}
520
521	/* Get the packet */
522	receivePacket(conn->discoverySocket, &packet, &len);
523
524	/* Check length */
525	if (ntohs(packet.length) + HDR_SIZE > len) {
526	    syslog(LOG_ERR, "Bogus PPPoE length field (%u)",
527		   (unsigned int) ntohs(packet.length));
528	    continue;
529	}
530
531#ifdef USE_BPF
532	/* If it's not a Discovery packet, loop again */
533	if (etherType(&packet) != Eth_PPPOE_Discovery) continue;
534#endif
535	if (conn->debugFile) {
536	    dumpPacket(conn->debugFile, &packet, "RCVD");
537	    fprintf(conn->debugFile, "\n");
538	    fflush(conn->debugFile);
539	}
540
541	/* If it's not from the AC, it's not for me */
542	if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) continue;
543
544	/* If it's not for us, loop again */
545	if (!packetIsForMe(conn, &packet)) continue;
546
547	/* Is it PADS?  */
548	if (packet.code == CODE_PADS) {
549	    /* Parse for goodies */
550	    parsePacket(&packet, parsePADSTags, conn);
551	    conn->discoveryState = STATE_SESSION;
552	    break;
553	}
554    } while (conn->discoveryState != STATE_SESSION);
555
556    /* Don't bother with ntohs; we'll just end up converting it back... */
557    conn->session = packet.session;
558
559    syslog(LOG_INFO, "PPP session is %d", (int) ntohs(conn->session));
560
561    /* RFC 2516 says session id MUST NOT be zero or 0xFFFF */
562    if (ntohs(conn->session) == 0 || ntohs(conn->session) == 0xFFFF) {
563	syslog(LOG_ERR, "Access concentrator used a session value of %x -- the AC is violating RFC 2516", (unsigned int) ntohs(conn->session));
564    }
565}
566
567/**********************************************************************
568*%FUNCTION: discovery
569*%ARGUMENTS:
570* conn -- PPPoE connection info structure
571*%RETURNS:
572* Nothing
573*%DESCRIPTION:
574* Performs the PPPoE discovery phase
575***********************************************************************/
576void
577discovery(PPPoEConnection *conn)
578{
579    int padiAttempts = 0;
580    int padrAttempts = 0;
581    int timeout = PADI_TIMEOUT;
582
583    /* Skip discovery and don't open discovery socket? */
584    if (conn->skipDiscovery && conn->noDiscoverySocket) {
585	conn->discoveryState = STATE_SESSION;
586	return;
587    }
588
589    conn->discoverySocket =
590	openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth);
591
592    /* Skip discovery? */
593    if (conn->skipDiscovery) {
594	conn->discoveryState = STATE_SESSION;
595	return;
596    }
597
598    do {
599	padiAttempts++;
600	if (padiAttempts > MAX_PADI_ATTEMPTS) {
601	    warn("Timeout waiting for PADO packets");
602	    close(conn->discoverySocket);
603	    conn->discoverySocket = -1;
604	    return;
605	}
606	sendPADI(conn);
607	conn->discoveryState = STATE_SENT_PADI;
608	waitForPADO(conn, timeout);
609
610	/* If we're just probing for access concentrators, don't do
611	   exponential backoff.  This reduces the time for an unsuccessful
612	   probe to 15 seconds. */
613	if (!conn->printACNames) {
614	    timeout *= 2;
615	}
616	if (conn->printACNames && conn->numPADOs) {
617	    break;
618	}
619    } while (conn->discoveryState == STATE_SENT_PADI);
620
621    /* If we're only printing access concentrator names, we're done */
622    if (conn->printACNames) {
623	die(0);
624    }
625
626    timeout = PADI_TIMEOUT;
627    do {
628	padrAttempts++;
629	if (padrAttempts > MAX_PADI_ATTEMPTS) {
630	    warn("Timeout waiting for PADS packets");
631	    close(conn->discoverySocket);
632	    conn->discoverySocket = -1;
633	    return;
634	}
635	sendPADR(conn);
636	conn->discoveryState = STATE_SENT_PADR;
637	waitForPADS(conn, timeout);
638	timeout *= 2;
639    } while (conn->discoveryState == STATE_SENT_PADR);
640
641    /* We're done. */
642    conn->discoveryState = STATE_SESSION;
643    return;
644}
645
646