1 /***********************************************************************
5 * Perform PPPoE discovery
7 * Copyright (C) 1999 by Roaring Penguin Software Inc.
9 ***********************************************************************/
11 static char const RCSID
[] =
12 "$Id: discovery.c,v 1.3 2004/11/04 10:07:37 paulus Exp $";
24 #ifdef HAVE_SYS_TIME_H
36 #ifdef USE_LINUX_PACKET
37 #include <sys/ioctl.h>
43 /**********************************************************************
44 *%FUNCTION: parseForHostUniq
49 * extra -- user-supplied pointer. This is assumed to be a pointer to int.
53 * If a HostUnique tag is found which matches our PID, sets *extra to 1.
54 ***********************************************************************/
56 parseForHostUniq(UINT16_t type
, UINT16_t len
, unsigned char *data
,
59 int *val
= (int *) extra
;
60 if (type
== TAG_HOST_UNIQ
&& len
== sizeof(pid_t
)) {
62 memcpy(&tmp
, data
, len
);
63 if (tmp
== getpid()) {
69 /**********************************************************************
70 *%FUNCTION: packetIsForMe
72 * conn -- PPPoE connection info
73 * packet -- a received PPPoE packet
75 * 1 if packet is for this PPPoE daemon; 0 otherwise.
77 * If we are using the Host-Unique tag, verifies that packet contains
78 * our unique identifier.
79 ***********************************************************************/
81 packetIsForMe(PPPoEConnection
*conn
, PPPoEPacket
*packet
)
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;
88 /* If we're not using the Host-Unique tag, then accept the packet */
89 if (!conn
->useHostUniq
) return 1;
91 parsePacket(packet
, parseForHostUniq
, &forMe
);
95 /**********************************************************************
96 *%FUNCTION: parsePADOTags
101 * extra -- extra user data. Should point to a PacketCriteria structure
102 * which gets filled in according to selected AC name and service
107 * Picks interesting tags out of a PADO packet
108 ***********************************************************************/
110 parsePADOTags(UINT16_t type
, UINT16_t len
, unsigned char *data
,
113 struct PacketCriteria
*pc
= (struct PacketCriteria
*) extra
;
114 PPPoEConnection
*conn
= pc
->conn
;
120 if (conn
->printACNames
) {
121 printf("Access-Concentrator: %.*s\n", (int) len
, data
);
123 if (conn
->acName
&& len
== strlen(conn
->acName
) &&
124 !strncmp((char *) data
, conn
->acName
, len
)) {
128 case TAG_SERVICE_NAME
:
129 pc
->seenServiceName
= 1;
130 if (conn
->printACNames
&& len
> 0) {
131 printf(" Service-Name: %.*s\n", (int) len
, data
);
133 if (conn
->serviceName
&& len
== strlen(conn
->serviceName
) &&
134 !strncmp((char *) data
, conn
->serviceName
, len
)) {
135 pc
->serviceNameOK
= 1;
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
]);
145 if (i
< len
) printf("...");
148 conn
->cookie
.type
= htons(type
);
149 conn
->cookie
.length
= htons(len
);
150 memcpy(conn
->cookie
.payload
, data
, len
);
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
]);
159 if (i
< len
) printf("...");
162 conn
->relayId
.type
= htons(type
);
163 conn
->relayId
.length
= htons(len
);
164 memcpy(conn
->relayId
.payload
, data
, len
);
166 case TAG_SERVICE_NAME_ERROR
:
167 if (conn
->printACNames
) {
168 printf("Got a Service-Name-Error tag: %.*s\n", (int) len
, data
);
170 syslog(LOG_ERR
, "PADO: Service-Name-Error: %.*s", (int) len
, data
);
174 case TAG_AC_SYSTEM_ERROR
:
175 if (conn
->printACNames
) {
176 printf("Got a System-Error tag: %.*s\n", (int) len
, data
);
178 syslog(LOG_ERR
, "PADO: System-Error: %.*s", (int) len
, data
);
182 case TAG_GENERIC_ERROR
:
183 if (conn
->printACNames
) {
184 printf("Got a Generic-Error tag: %.*s\n", (int) len
, data
);
186 syslog(LOG_ERR
, "PADO: Generic-Error: %.*s", (int) len
, data
);
193 /**********************************************************************
194 *%FUNCTION: parsePADSTags
199 * extra -- extra user data (pointer to PPPoEConnection structure)
203 * Picks interesting tags out of a PADS packet
204 ***********************************************************************/
206 parsePADSTags(UINT16_t type
, UINT16_t len
, unsigned char *data
,
209 PPPoEConnection
*conn
= (PPPoEConnection
*) extra
;
211 case TAG_SERVICE_NAME
:
212 syslog(LOG_DEBUG
, "PADS: Service-Name: '%.*s'", (int) len
, data
);
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
);
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
);
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
);
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
);
234 /***********************************************************************
237 * conn -- PPPoEConnection structure
241 * Sends a PADI packet
242 ***********************************************************************/
244 sendPADI(PPPoEConnection
*conn
)
247 unsigned char *cursor
= packet
.payload
;
248 PPPoETag
*svc
= (PPPoETag
*) (&packet
.payload
);
249 UINT16_t namelen
= 0;
252 if (conn
->serviceName
) {
253 namelen
= (UINT16_t
) strlen(conn
->serviceName
);
255 plen
= TAG_HDR_SIZE
+ namelen
;
256 CHECK_ROOM(cursor
, packet
.payload
, plen
);
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
);
262 packet
.ethHdr
.h_proto
= htons(Eth_PPPOE_Discovery
);
265 packet
.code
= CODE_PADI
;
268 svc
->type
= TAG_SERVICE_NAME
;
269 svc
->length
= htons(namelen
);
270 CHECK_ROOM(cursor
, packet
.payload
, namelen
+TAG_HDR_SIZE
);
272 if (conn
->serviceName
) {
273 memcpy(svc
->payload
, conn
->serviceName
, strlen(conn
->serviceName
));
275 cursor
+= namelen
+ TAG_HDR_SIZE
;
277 /* If we're using Host-Uniq, copy it over */
278 if (conn
->useHostUniq
) {
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
;
290 packet
.length
= htons(plen
);
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
);
300 /**********************************************************************
301 *%FUNCTION: waitForPADO
303 * conn -- PPPoEConnection structure
304 * timeout -- how long to wait (in seconds)
308 * Waits for a PADO packet and copies useful information
309 ***********************************************************************/
311 waitForPADO(PPPoEConnection
*conn
, int timeout
)
319 struct PacketCriteria pc
;
321 pc
.acNameOK
= (conn
->acName
) ? 0 : 1;
322 pc
.serviceNameOK
= (conn
->serviceName
) ? 0 : 1;
324 pc
.seenServiceName
= 0;
327 if (BPF_BUFFER_IS_EMPTY
) {
332 FD_SET(conn
->discoverySocket
, &readable
);
335 r
= select(conn
->discoverySocket
+1, &readable
, NULL
, NULL
, &tv
);
336 if (r
>= 0 || errno
!= EINTR
) break;
339 fatalSys("select (waitForPADO)");
341 if (r
== 0) return; /* Timed out */
345 receivePacket(conn
->discoverySocket
, &packet
, &len
);
348 if (ntohs(packet
.length
) + HDR_SIZE
> len
) {
349 syslog(LOG_ERR
, "Bogus PPPoE length field (%u)",
350 (unsigned int) ntohs(packet
.length
));
355 /* If it's not a Discovery packet, loop again */
356 if (etherType(&packet
) != Eth_PPPOE_Discovery
) continue;
359 if (conn
->debugFile
) {
360 dumpPacket(conn
->debugFile
, &packet
, "RCVD");
361 fprintf(conn
->debugFile
, "\n");
362 fflush(conn
->debugFile
);
364 /* If it's not for us, loop again */
365 if (!packetIsForMe(conn
, &packet
)) continue;
367 if (packet
.code
== CODE_PADO
) {
368 if (NOT_UNICAST(packet
.ethHdr
.h_source
)) {
369 printErr("Ignoring PADO packet from non-unicast MAC address");
372 parsePacket(&packet
, parsePADOTags
, &pc
);
373 if (!pc
.seenACName
) {
374 printErr("Ignoring PADO packet with no AC-Name tag");
377 if (!pc
.seenServiceName
) {
378 printErr("Ignoring PADO packet with no Service-Name tag");
382 if (conn
->printACNames
) {
383 printf("--------------------------------------------------\n");
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]);
397 conn
->discoveryState
= STATE_RECEIVED_PADO
;
401 } while (conn
->discoveryState
!= STATE_RECEIVED_PADO
);
404 /***********************************************************************
407 * conn -- PPPoE connection structur
411 * Sends a PADR packet
412 ***********************************************************************/
414 sendPADR(PPPoEConnection
*conn
)
417 PPPoETag
*svc
= (PPPoETag
*) packet
.payload
;
418 unsigned char *cursor
= packet
.payload
;
420 UINT16_t namelen
= 0;
423 if (conn
->serviceName
) {
424 namelen
= (UINT16_t
) strlen(conn
->serviceName
);
426 plen
= TAG_HDR_SIZE
+ namelen
;
427 CHECK_ROOM(cursor
, packet
.payload
, plen
);
429 memcpy(packet
.ethHdr
.h_dest
, conn
->peerEth
, ETH_ALEN
);
430 memcpy(packet
.ethHdr
.h_source
, conn
->myEth
, ETH_ALEN
);
432 packet
.ethHdr
.h_proto
= htons(Eth_PPPOE_Discovery
);
435 packet
.code
= CODE_PADR
;
438 svc
->type
= TAG_SERVICE_NAME
;
439 svc
->length
= htons(namelen
);
440 if (conn
->serviceName
) {
441 memcpy(svc
->payload
, conn
->serviceName
, namelen
);
443 cursor
+= namelen
+ TAG_HDR_SIZE
;
445 /* If we're using Host-Uniq, copy it over */
446 if (conn
->useHostUniq
) {
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
;
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
;
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
;
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
);
484 /**********************************************************************
485 *%FUNCTION: waitForPADS
487 * conn -- PPPoE connection info
488 * timeout -- how long to wait (in seconds)
492 * Waits for a PADS packet and copies useful information
493 ***********************************************************************/
495 waitForPADS(PPPoEConnection
*conn
, int timeout
)
504 if (BPF_BUFFER_IS_EMPTY
) {
509 FD_SET(conn
->discoverySocket
, &readable
);
512 r
= select(conn
->discoverySocket
+1, &readable
, NULL
, NULL
, &tv
);
513 if (r
>= 0 || errno
!= EINTR
) break;
516 fatalSys("select (waitForPADS)");
522 receivePacket(conn
->discoverySocket
, &packet
, &len
);
525 if (ntohs(packet
.length
) + HDR_SIZE
> len
) {
526 syslog(LOG_ERR
, "Bogus PPPoE length field (%u)",
527 (unsigned int) ntohs(packet
.length
));
532 /* If it's not a Discovery packet, loop again */
533 if (etherType(&packet
) != Eth_PPPOE_Discovery
) continue;
535 if (conn
->debugFile
) {
536 dumpPacket(conn
->debugFile
, &packet
, "RCVD");
537 fprintf(conn
->debugFile
, "\n");
538 fflush(conn
->debugFile
);
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;
544 /* If it's not for us, loop again */
545 if (!packetIsForMe(conn
, &packet
)) continue;
548 if (packet
.code
== CODE_PADS
) {
549 /* Parse for goodies */
550 parsePacket(&packet
, parsePADSTags
, conn
);
551 conn
->discoveryState
= STATE_SESSION
;
554 } while (conn
->discoveryState
!= STATE_SESSION
);
556 /* Don't bother with ntohs; we'll just end up converting it back... */
557 conn
->session
= packet
.session
;
559 syslog(LOG_INFO
, "PPP session is %d", (int) ntohs(conn
->session
));
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
));
567 /**********************************************************************
568 *%FUNCTION: discovery
570 * conn -- PPPoE connection info structure
574 * Performs the PPPoE discovery phase
575 ***********************************************************************/
577 discovery(PPPoEConnection
*conn
)
579 int padiAttempts
= 0;
580 int padrAttempts
= 0;
581 int timeout
= PADI_TIMEOUT
;
583 /* Skip discovery and don't open discovery socket? */
584 if (conn
->skipDiscovery
&& conn
->noDiscoverySocket
) {
585 conn
->discoveryState
= STATE_SESSION
;
589 conn
->discoverySocket
=
590 openInterface(conn
->ifName
, Eth_PPPOE_Discovery
, conn
->myEth
);
592 /* Skip discovery? */
593 if (conn
->skipDiscovery
) {
594 conn
->discoveryState
= STATE_SESSION
;
600 if (padiAttempts
> MAX_PADI_ATTEMPTS
) {
601 warn("Timeout waiting for PADO packets");
602 close(conn
->discoverySocket
);
603 conn
->discoverySocket
= -1;
607 conn
->discoveryState
= STATE_SENT_PADI
;
608 waitForPADO(conn
, timeout
);
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
) {
616 if (conn
->printACNames
&& conn
->numPADOs
) {
619 } while (conn
->discoveryState
== STATE_SENT_PADI
);
621 /* If we're only printing access concentrator names, we're done */
622 if (conn
->printACNames
) {
626 timeout
= PADI_TIMEOUT
;
629 if (padrAttempts
> MAX_PADI_ATTEMPTS
) {
630 warn("Timeout waiting for PADS packets");
631 close(conn
->discoverySocket
);
632 conn
->discoverySocket
= -1;
636 conn
->discoveryState
= STATE_SENT_PADR
;
637 waitForPADS(conn
, timeout
);
639 } while (conn
->discoveryState
== STATE_SENT_PADR
);
642 conn
->discoveryState
= STATE_SESSION
;