Changes for kernel and Busybox
[tomato.git] / release / src / router / pppd / pppd / plugins / rp-pppoe / discovery.c
blobd910f2b9b5fdc9cd959ba104f2171b98fb96c17a
1 /***********************************************************************
3 * discovery.c
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.6 2008/06/15 04:35:50 paulus Exp $";
14 #define _GNU_SOURCE 1
15 #include "pppoe.h"
16 #include "pppd/pppd.h"
17 #include "pppd/fsm.h"
18 #include "pppd/lcp.h"
20 #include <string.h>
21 #include <stdlib.h>
22 #include <errno.h>
24 #ifdef HAVE_SYS_TIME_H
25 #include <sys/time.h>
26 #endif
27 #include <time.h>
29 #ifdef HAVE_SYS_UIO_H
30 #include <sys/uio.h>
31 #endif
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
37 #ifdef USE_LINUX_PACKET
38 #include <sys/ioctl.h>
39 #include <fcntl.h>
40 #endif
42 #include <signal.h>
44 /**********************************************************************
45 *%FUNCTION: parseForHostUniq
46 *%ARGUMENTS:
47 * type -- tag type
48 * len -- tag length
49 * data -- tag data.
50 * extra -- user-supplied pointer. This is assumed to be a pointer to int.
51 *%RETURNS:
52 * Nothing
53 *%DESCRIPTION:
54 * If a HostUnique tag is found which matches our PID, sets *extra to 1.
55 ***********************************************************************/
56 static void
57 parseForHostUniq(UINT16_t type, UINT16_t len, unsigned char *data,
58 void *extra)
60 int *val = (int *) extra;
61 if (type == TAG_HOST_UNIQ && len == sizeof(pid_t)) {
62 pid_t tmp;
63 memcpy(&tmp, data, len);
64 if (tmp == getpid()) {
65 *val = 1;
70 /**********************************************************************
71 *%FUNCTION: packetIsForMe
72 *%ARGUMENTS:
73 * conn -- PPPoE connection info
74 * packet -- a received PPPoE packet
75 *%RETURNS:
76 * 1 if packet is for this PPPoE daemon; 0 otherwise.
77 *%DESCRIPTION:
78 * If we are using the Host-Unique tag, verifies that packet contains
79 * our unique identifier.
80 ***********************************************************************/
81 static int
82 packetIsForMe(PPPoEConnection *conn, PPPoEPacket *packet)
84 int forMe = 0;
86 /* If packet is not directed to our MAC address, forget it */
87 if (memcmp(packet->ethHdr.h_dest, conn->myEth, ETH_ALEN)) return 0;
89 /* If we're not using the Host-Unique tag, then accept the packet */
90 if (!conn->useHostUniq) return 1;
92 parsePacket(packet, parseForHostUniq, &forMe);
93 return forMe;
96 /**********************************************************************
97 *%FUNCTION: parsePADOTags
98 *%ARGUMENTS:
99 * type -- tag type
100 * len -- tag length
101 * data -- tag data
102 * extra -- extra user data. Should point to a PacketCriteria structure
103 * which gets filled in according to selected AC name and service
104 * name.
105 *%RETURNS:
106 * Nothing
107 *%DESCRIPTION:
108 * Picks interesting tags out of a PADO packet
109 ***********************************************************************/
110 static void
111 parsePADOTags(UINT16_t type, UINT16_t len, unsigned char *data,
112 void *extra)
114 struct PacketCriteria *pc = (struct PacketCriteria *) extra;
115 PPPoEConnection *conn = pc->conn;
116 UINT16_t mru;
117 int i;
119 switch(type) {
120 case TAG_AC_NAME:
121 pc->seenACName = 1;
122 if (conn->printACNames) {
123 info("Access-Concentrator: %.*s", (int) len, data);
125 if (conn->acName && len == strlen(conn->acName) &&
126 !strncmp((char *) data, conn->acName, len)) {
127 pc->acNameOK = 1;
129 break;
130 case TAG_SERVICE_NAME:
131 pc->seenServiceName = 1;
132 if (conn->serviceName && len == strlen(conn->serviceName) &&
133 !strncmp((char *) data, conn->serviceName, len)) {
134 pc->serviceNameOK = 1;
136 break;
137 case TAG_AC_COOKIE:
138 conn->cookie.type = htons(type);
139 conn->cookie.length = htons(len);
140 memcpy(conn->cookie.payload, data, len);
141 break;
142 case TAG_RELAY_SESSION_ID:
143 conn->relayId.type = htons(type);
144 conn->relayId.length = htons(len);
145 memcpy(conn->relayId.payload, data, len);
146 break;
147 case TAG_PPP_MAX_PAYLOAD:
148 if (len == sizeof(mru)) {
149 memcpy(&mru, data, sizeof(mru));
150 mru = ntohs(mru);
151 if (mru >= ETH_PPPOE_MTU) {
152 if (lcp_allowoptions[0].mru > mru)
153 lcp_allowoptions[0].mru = mru;
154 if (lcp_wantoptions[0].mru > mru)
155 lcp_wantoptions[0].mru = mru;
156 conn->seenMaxPayload = 1;
159 break;
160 case TAG_SERVICE_NAME_ERROR:
161 error("PADO: Service-Name-Error: %.*s", (int) len, data);
162 conn->error = 1;
163 break;
164 case TAG_AC_SYSTEM_ERROR:
165 error("PADO: System-Error: %.*s", (int) len, data);
166 conn->error = 1;
167 break;
168 case TAG_GENERIC_ERROR:
169 error("PADO: Generic-Error: %.*s", (int) len, data);
170 conn->error = 1;
171 break;
175 /**********************************************************************
176 *%FUNCTION: parsePADSTags
177 *%ARGUMENTS:
178 * type -- tag type
179 * len -- tag length
180 * data -- tag data
181 * extra -- extra user data (pointer to PPPoEConnection structure)
182 *%RETURNS:
183 * Nothing
184 *%DESCRIPTION:
185 * Picks interesting tags out of a PADS packet
186 ***********************************************************************/
187 static void
188 parsePADSTags(UINT16_t type, UINT16_t len, unsigned char *data,
189 void *extra)
191 PPPoEConnection *conn = (PPPoEConnection *) extra;
192 UINT16_t mru;
193 switch(type) {
194 case TAG_SERVICE_NAME:
195 dbglog("PADS: Service-Name: '%.*s'", (int) len, data);
196 break;
197 case TAG_PPP_MAX_PAYLOAD:
198 if (len == sizeof(mru)) {
199 memcpy(&mru, data, sizeof(mru));
200 mru = ntohs(mru);
201 if (mru >= ETH_PPPOE_MTU) {
202 if (lcp_allowoptions[0].mru > mru)
203 lcp_allowoptions[0].mru = mru;
204 if (lcp_wantoptions[0].mru > mru)
205 lcp_wantoptions[0].mru = mru;
206 conn->seenMaxPayload = 1;
209 break;
210 case TAG_SERVICE_NAME_ERROR:
211 error("PADS: Service-Name-Error: %.*s", (int) len, data);
212 conn->error = 1;
213 break;
214 case TAG_AC_SYSTEM_ERROR:
215 error("PADS: System-Error: %.*s", (int) len, data);
216 conn->error = 1;
217 break;
218 case TAG_GENERIC_ERROR:
219 error("PADS: Generic-Error: %.*s", (int) len, data);
220 conn->error = 1;
221 break;
222 case TAG_RELAY_SESSION_ID:
223 conn->relayId.type = htons(type);
224 conn->relayId.length = htons(len);
225 memcpy(conn->relayId.payload, data, len);
226 break;
230 /***********************************************************************
231 *%FUNCTION: sendPADI
232 *%ARGUMENTS:
233 * conn -- PPPoEConnection structure
234 *%RETURNS:
235 * Nothing
236 *%DESCRIPTION:
237 * Sends a PADI packet
238 ***********************************************************************/
239 static void
240 sendPADI(PPPoEConnection *conn)
242 PPPoEPacket packet;
243 unsigned char *cursor = packet.payload;
244 PPPoETag *svc = (PPPoETag *) (&packet.payload);
245 UINT16_t namelen = 0;
246 UINT16_t plen;
247 int omit_service_name = 0;
249 if (conn->serviceName) {
250 namelen = (UINT16_t) strlen(conn->serviceName);
251 if (!strcmp(conn->serviceName, "NO-SERVICE-NAME-NON-RFC-COMPLIANT")) {
252 omit_service_name = 1;
256 /* Set destination to Ethernet broadcast address */
257 memset(packet.ethHdr.h_dest, 0xFF, ETH_ALEN);
258 memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN);
260 packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
261 packet.vertype = PPPOE_VER_TYPE(1, 1);
262 packet.code = CODE_PADI;
263 packet.session = 0;
265 if (!omit_service_name) {
266 plen = TAG_HDR_SIZE + namelen;
267 CHECK_ROOM(cursor, packet.payload, plen);
269 svc->type = TAG_SERVICE_NAME;
270 svc->length = htons(namelen);
272 if (conn->serviceName) {
273 memcpy(svc->payload, conn->serviceName, strlen(conn->serviceName));
275 cursor += namelen + TAG_HDR_SIZE;
276 } else {
277 plen = 0;
280 /* If we're using Host-Uniq, copy it over */
281 if (conn->useHostUniq) {
282 PPPoETag hostUniq;
283 pid_t pid = getpid();
284 hostUniq.type = htons(TAG_HOST_UNIQ);
285 hostUniq.length = htons(sizeof(pid));
286 memcpy(hostUniq.payload, &pid, sizeof(pid));
287 CHECK_ROOM(cursor, packet.payload, sizeof(pid) + TAG_HDR_SIZE);
288 memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE);
289 cursor += sizeof(pid) + TAG_HDR_SIZE;
290 plen += sizeof(pid) + TAG_HDR_SIZE;
293 /* Add our maximum MTU/MRU */
294 if (MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru) > ETH_PPPOE_MTU) {
295 PPPoETag maxPayload;
296 UINT16_t mru = htons(MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru));
297 maxPayload.type = htons(TAG_PPP_MAX_PAYLOAD);
298 maxPayload.length = htons(sizeof(mru));
299 memcpy(maxPayload.payload, &mru, sizeof(mru));
300 CHECK_ROOM(cursor, packet.payload, sizeof(mru) + TAG_HDR_SIZE);
301 memcpy(cursor, &maxPayload, sizeof(mru) + TAG_HDR_SIZE);
302 cursor += sizeof(mru) + TAG_HDR_SIZE;
303 plen += sizeof(mru) + TAG_HDR_SIZE;
306 packet.length = htons(plen);
308 sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE));
311 /**********************************************************************
312 *%FUNCTION: waitForPADO
313 *%ARGUMENTS:
314 * conn -- PPPoEConnection structure
315 * timeout -- how long to wait (in seconds)
316 *%RETURNS:
317 * Nothing
318 *%DESCRIPTION:
319 * Waits for a PADO packet and copies useful information
320 ***********************************************************************/
321 void
322 waitForPADO(PPPoEConnection *conn, int timeout)
324 fd_set readable;
325 int r;
326 struct timeval tv;
327 struct timeval expire_at;
328 struct timeval now;
330 PPPoEPacket packet;
331 int len;
333 struct PacketCriteria pc;
334 pc.conn = conn;
335 pc.acNameOK = (conn->acName) ? 0 : 1;
336 pc.serviceNameOK = (conn->serviceName) ? 0 : 1;
337 pc.seenACName = 0;
338 pc.seenServiceName = 0;
339 conn->seenMaxPayload = 0;
340 conn->error = 0;
342 if (gettimeofday(&expire_at, NULL) < 0) {
343 fatalSys("gettimeofday (waitForPADO)");
345 expire_at.tv_sec += timeout;
347 do {
348 if (BPF_BUFFER_IS_EMPTY) {
349 if (gettimeofday(&now, NULL) < 0) {
350 fatalSys("gettimeofday (waitForPADO)");
352 tv.tv_sec = expire_at.tv_sec - now.tv_sec;
353 tv.tv_usec = expire_at.tv_usec - now.tv_usec;
354 if (tv.tv_usec < 0) {
355 tv.tv_usec += 1000000;
356 if (tv.tv_sec) {
357 tv.tv_sec--;
358 } else {
359 /* Timed out */
360 return;
363 if (tv.tv_sec <= 0 && tv.tv_usec <= 0) {
364 /* Timed out */
365 return;
368 FD_ZERO(&readable);
369 FD_SET(conn->discoverySocket, &readable);
371 while(1) {
372 r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv);
373 if (r >= 0 || errno != EINTR) break;
375 if (r < 0) {
376 error("select (waitForPADO): %m");
377 return;
379 if (r == 0) {
380 /* Timed out */
381 return;
385 /* Get the packet */
386 receivePacket(conn->discoverySocket, &packet, &len);
388 /* Check length */
389 if (ntohs(packet.length) + HDR_SIZE > len) {
390 error("Bogus PPPoE length field (%u)",
391 (unsigned int) ntohs(packet.length));
392 continue;
395 #ifdef USE_BPF
396 /* If it's not a Discovery packet, loop again */
397 if (etherType(&packet) != Eth_PPPOE_Discovery) continue;
398 #endif
400 /* If it's not for us, loop again */
401 if (!packetIsForMe(conn, &packet)) continue;
403 if (packet.code == CODE_PADO) {
404 if (BROADCAST(packet.ethHdr.h_source)) {
405 error("Ignoring PADO packet from broadcast MAC address");
406 continue;
408 if (conn->req_peer
409 && memcmp(packet.ethHdr.h_source, conn->req_peer_mac, ETH_ALEN) != 0) {
410 warn("Ignoring PADO packet from wrong MAC address");
411 continue;
413 if (parsePacket(&packet, parsePADOTags, &pc) < 0)
414 return;
415 if (conn->error)
416 return;
417 if (!pc.seenACName) {
418 error("Ignoring PADO packet with no AC-Name tag");
419 continue;
421 if (!pc.seenServiceName) {
422 error("Ignoring PADO packet with no Service-Name tag");
423 continue;
425 conn->numPADOs++;
426 if (pc.acNameOK && pc.serviceNameOK) {
427 memcpy(conn->peerEth, packet.ethHdr.h_source, ETH_ALEN);
428 conn->discoveryState = STATE_RECEIVED_PADO;
429 break;
432 } while (conn->discoveryState != STATE_RECEIVED_PADO);
435 /***********************************************************************
436 *%FUNCTION: sendPADR
437 *%ARGUMENTS:
438 * conn -- PPPoE connection structur
439 *%RETURNS:
440 * Nothing
441 *%DESCRIPTION:
442 * Sends a PADR packet
443 ***********************************************************************/
444 static void
445 sendPADR(PPPoEConnection *conn)
447 PPPoEPacket packet;
448 PPPoETag *svc = (PPPoETag *) packet.payload;
449 unsigned char *cursor = packet.payload;
451 UINT16_t namelen = 0;
452 UINT16_t plen;
454 if (conn->serviceName) {
455 namelen = (UINT16_t) strlen(conn->serviceName);
457 plen = TAG_HDR_SIZE + namelen;
458 CHECK_ROOM(cursor, packet.payload, plen);
460 memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN);
461 memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN);
463 packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
464 packet.vertype = PPPOE_VER_TYPE(1, 1);
465 packet.code = CODE_PADR;
466 packet.session = 0;
468 svc->type = TAG_SERVICE_NAME;
469 svc->length = htons(namelen);
470 if (conn->serviceName) {
471 memcpy(svc->payload, conn->serviceName, namelen);
473 cursor += namelen + TAG_HDR_SIZE;
475 /* If we're using Host-Uniq, copy it over */
476 if (conn->useHostUniq) {
477 PPPoETag hostUniq;
478 pid_t pid = getpid();
479 hostUniq.type = htons(TAG_HOST_UNIQ);
480 hostUniq.length = htons(sizeof(pid));
481 memcpy(hostUniq.payload, &pid, sizeof(pid));
482 CHECK_ROOM(cursor, packet.payload, sizeof(pid)+TAG_HDR_SIZE);
483 memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE);
484 cursor += sizeof(pid) + TAG_HDR_SIZE;
485 plen += sizeof(pid) + TAG_HDR_SIZE;
488 /* Add our maximum MTU/MRU */
489 if (MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru) > ETH_PPPOE_MTU) {
490 PPPoETag maxPayload;
491 UINT16_t mru = htons(MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru));
492 maxPayload.type = htons(TAG_PPP_MAX_PAYLOAD);
493 maxPayload.length = htons(sizeof(mru));
494 memcpy(maxPayload.payload, &mru, sizeof(mru));
495 CHECK_ROOM(cursor, packet.payload, sizeof(mru) + TAG_HDR_SIZE);
496 memcpy(cursor, &maxPayload, sizeof(mru) + TAG_HDR_SIZE);
497 cursor += sizeof(mru) + TAG_HDR_SIZE;
498 plen += sizeof(mru) + TAG_HDR_SIZE;
501 /* Copy cookie and relay-ID if needed */
502 if (conn->cookie.type) {
503 CHECK_ROOM(cursor, packet.payload,
504 ntohs(conn->cookie.length) + TAG_HDR_SIZE);
505 memcpy(cursor, &conn->cookie, ntohs(conn->cookie.length) + TAG_HDR_SIZE);
506 cursor += ntohs(conn->cookie.length) + TAG_HDR_SIZE;
507 plen += ntohs(conn->cookie.length) + TAG_HDR_SIZE;
510 if (conn->relayId.type) {
511 CHECK_ROOM(cursor, packet.payload,
512 ntohs(conn->relayId.length) + TAG_HDR_SIZE);
513 memcpy(cursor, &conn->relayId, ntohs(conn->relayId.length) + TAG_HDR_SIZE);
514 cursor += ntohs(conn->relayId.length) + TAG_HDR_SIZE;
515 plen += ntohs(conn->relayId.length) + TAG_HDR_SIZE;
518 packet.length = htons(plen);
519 sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE));
522 /**********************************************************************
523 *%FUNCTION: waitForPADS
524 *%ARGUMENTS:
525 * conn -- PPPoE connection info
526 * timeout -- how long to wait (in seconds)
527 *%RETURNS:
528 * Nothing
529 *%DESCRIPTION:
530 * Waits for a PADS packet and copies useful information
531 ***********************************************************************/
532 static void
533 waitForPADS(PPPoEConnection *conn, int timeout)
535 fd_set readable;
536 int r;
537 struct timeval tv;
538 struct timeval expire_at;
539 struct timeval now;
541 PPPoEPacket packet;
542 int len;
544 if (gettimeofday(&expire_at, NULL) < 0) {
545 fatalSys("gettimeofday (waitForPADS)");
547 expire_at.tv_sec += timeout;
549 conn->error = 0;
550 do {
551 if (BPF_BUFFER_IS_EMPTY) {
552 if (gettimeofday(&now, NULL) < 0) {
553 fatalSys("gettimeofday (waitForPADS)");
555 tv.tv_sec = expire_at.tv_sec - now.tv_sec;
556 tv.tv_usec = expire_at.tv_usec - now.tv_usec;
557 if (tv.tv_usec < 0) {
558 tv.tv_usec += 1000000;
559 if (tv.tv_sec) {
560 tv.tv_sec--;
561 } else {
562 /* Timed out */
563 return;
566 if (tv.tv_sec <= 0 && tv.tv_usec <= 0) {
567 /* Timed out */
568 return;
571 FD_ZERO(&readable);
572 FD_SET(conn->discoverySocket, &readable);
574 while(1) {
575 r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv);
576 if (r >= 0 || errno != EINTR) break;
578 if (r < 0) {
579 error("select (waitForPADS): %m");
580 return;
582 if (r == 0) {
583 /* Timed out */
584 return;
588 /* Get the packet */
589 receivePacket(conn->discoverySocket, &packet, &len);
591 /* Check length */
592 if (ntohs(packet.length) + HDR_SIZE > len) {
593 error("Bogus PPPoE length field (%u)",
594 (unsigned int) ntohs(packet.length));
595 continue;
598 #ifdef USE_BPF
599 /* If it's not a Discovery packet, loop again */
600 if (etherType(&packet) != Eth_PPPOE_Discovery) continue;
601 #endif
603 /* If it's not from the AC, it's not for me */
604 if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) continue;
606 /* If it's not for us, loop again */
607 if (!packetIsForMe(conn, &packet)) continue;
609 /* Is it PADS? */
610 if (packet.code == CODE_PADS) {
611 /* Parse for goodies */
612 if (parsePacket(&packet, parsePADSTags, conn) < 0)
613 return;
614 if (conn->error)
615 return;
616 conn->discoveryState = STATE_SESSION;
617 break;
619 } while (conn->discoveryState != STATE_SESSION);
621 /* Don't bother with ntohs; we'll just end up converting it back... */
622 conn->session = packet.session;
624 info("PPP session is %d", (int) ntohs(conn->session));
626 /* RFC 2516 says session id MUST NOT be zero or 0xFFFF */
627 if (ntohs(conn->session) == 0 || ntohs(conn->session) == 0xFFFF) {
628 error("Access concentrator used a session value of %x -- the AC is violating RFC 2516", (unsigned int) ntohs(conn->session));
632 /**********************************************************************
633 *%FUNCTION: discovery
634 *%ARGUMENTS:
635 * conn -- PPPoE connection info structure
636 *%RETURNS:
637 * Nothing
638 *%DESCRIPTION:
639 * Performs the PPPoE discovery phase
640 ***********************************************************************/
641 void
642 discovery(PPPoEConnection *conn)
644 int padiAttempts = 0;
645 int padrAttempts = 0;
646 int timeout = conn->discoveryTimeout;
648 conn->discoverySocket =
649 openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth);
651 do {
652 padiAttempts++;
653 if (padiAttempts > MAX_PADI_ATTEMPTS) {
654 warn("Timeout waiting for PADO packets");
655 system("ppp_event -t PADO_TIMEOUT &");
656 close(conn->discoverySocket);
657 conn->discoverySocket = -1;
658 return;
660 sendPADI(conn);
661 conn->discoveryState = STATE_SENT_PADI;
662 waitForPADO(conn, timeout);
664 timeout *= 2;
665 } while (conn->discoveryState == STATE_SENT_PADI);
667 timeout = conn->discoveryTimeout;
668 do {
669 padrAttempts++;
670 if (padrAttempts > MAX_PADI_ATTEMPTS) {
671 warn("Timeout waiting for PADS packets");
672 system("ppp_event -t PADS_TIMEOUT &");
673 close(conn->discoverySocket);
674 conn->discoverySocket = -1;
675 return;
677 sendPADR(conn);
678 conn->discoveryState = STATE_SENT_PADR;
679 waitForPADS(conn, timeout);
680 timeout *= 2;
681 } while (conn->discoveryState == STATE_SENT_PADR);
683 if (!conn->seenMaxPayload) {
684 /* RFC 4638: MUST limit MTU/MRU to 1492 */
685 if (lcp_allowoptions[0].mru > ETH_PPPOE_MTU)
686 lcp_allowoptions[0].mru = ETH_PPPOE_MTU;
687 if (lcp_wantoptions[0].mru > ETH_PPPOE_MTU)
688 lcp_wantoptions[0].mru = ETH_PPPOE_MTU;
691 /* We're done. */
692 conn->discoveryState = STATE_SESSION;
693 return;