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.5 2008/06/09 08:34:23 paulus Exp $";
16 #include "pppd/pppd.h"
22 #ifdef HAVE_SYS_TIME_H
34 #ifdef USE_LINUX_PACKET
35 #include <sys/ioctl.h>
41 /**********************************************************************
42 *%FUNCTION: parseForHostUniq
47 * extra -- user-supplied pointer. This is assumed to be a pointer to int.
51 * If a HostUnique tag is found which matches our PID, sets *extra to 1.
52 ***********************************************************************/
54 parseForHostUniq(UINT16_t type, UINT16_t len, unsigned char *data,
57 int *val = (int *) extra;
58 if (type == TAG_HOST_UNIQ && len == sizeof(pid_t)) {
60 memcpy(&tmp, data, len);
61 if (tmp == getpid()) {
67 /**********************************************************************
68 *%FUNCTION: packetIsForMe
70 * conn -- PPPoE connection info
71 * packet -- a received PPPoE packet
73 * 1 if packet is for this PPPoE daemon; 0 otherwise.
75 * If we are using the Host-Unique tag, verifies that packet contains
76 * our unique identifier.
77 ***********************************************************************/
79 packetIsForMe(PPPoEConnection *conn, PPPoEPacket *packet)
83 /* If packet is not directed to our MAC address, forget it */
84 if (memcmp(packet->ethHdr.h_dest, conn->myEth, ETH_ALEN)) return 0;
86 /* If we're not using the Host-Unique tag, then accept the packet */
87 if (!conn->useHostUniq) return 1;
89 parsePacket(packet, parseForHostUniq, &forMe);
93 /**********************************************************************
94 *%FUNCTION: parsePADOTags
99 * extra -- extra user data. Should point to a PacketCriteria structure
100 * which gets filled in according to selected AC name and service
105 * Picks interesting tags out of a PADO packet
106 ***********************************************************************/
108 parsePADOTags(UINT16_t type, UINT16_t len, unsigned char *data,
111 struct PacketCriteria *pc = (struct PacketCriteria *) extra;
112 PPPoEConnection *conn = pc->conn;
118 if (conn->printACNames) {
119 info("Access-Concentrator: %.*s", (int) len, data);
121 if (conn->acName && len == strlen(conn->acName) &&
122 !strncmp((char *) data, conn->acName, len)) {
126 case TAG_SERVICE_NAME:
127 pc->seenServiceName = 1;
128 if (conn->serviceName && len == strlen(conn->serviceName) &&
129 !strncmp((char *) data, conn->serviceName, len)) {
130 pc->serviceNameOK = 1;
134 conn->cookie.type = htons(type);
135 conn->cookie.length = htons(len);
136 memcpy(conn->cookie.payload, data, len);
138 case TAG_RELAY_SESSION_ID:
139 conn->relayId.type = htons(type);
140 conn->relayId.length = htons(len);
141 memcpy(conn->relayId.payload, data, len);
143 case TAG_SERVICE_NAME_ERROR:
144 error("PADO: Service-Name-Error: %.*s", (int) len, data);
147 case TAG_AC_SYSTEM_ERROR:
148 error("PADO: System-Error: %.*s", (int) len, data);
151 case TAG_GENERIC_ERROR:
152 error("PADO: Generic-Error: %.*s", (int) len, data);
158 /**********************************************************************
159 *%FUNCTION: parsePADSTags
164 * extra -- extra user data (pointer to PPPoEConnection structure)
168 * Picks interesting tags out of a PADS packet
169 ***********************************************************************/
171 parsePADSTags(UINT16_t type, UINT16_t len, unsigned char *data,
174 PPPoEConnection *conn = (PPPoEConnection *) extra;
176 case TAG_SERVICE_NAME:
177 dbglog("PADS: Service-Name: '%.*s'", (int) len, data);
179 case TAG_SERVICE_NAME_ERROR:
180 error("PADS: Service-Name-Error: %.*s", (int) len, data);
183 case TAG_AC_SYSTEM_ERROR:
184 error("PADS: System-Error: %.*s", (int) len, data);
187 case TAG_GENERIC_ERROR:
188 error("PADS: Generic-Error: %.*s", (int) len, data);
191 case TAG_RELAY_SESSION_ID:
192 conn->relayId.type = htons(type);
193 conn->relayId.length = htons(len);
194 memcpy(conn->relayId.payload, data, len);
199 /***********************************************************************
202 * conn -- PPPoEConnection structure
206 * Sends a PADI packet
207 ***********************************************************************/
209 sendPADI(PPPoEConnection *conn)
212 unsigned char *cursor = packet.payload;
213 PPPoETag *svc = (PPPoETag *) (&packet.payload);
214 UINT16_t namelen = 0;
216 int omit_service_name = 0;
218 if (conn->serviceName) {
219 namelen = (UINT16_t) strlen(conn->serviceName);
220 if (!strcmp(conn->serviceName, "NO-SERVICE-NAME-NON-RFC-COMPLIANT")) {
221 omit_service_name = 1;
225 /* Set destination to Ethernet broadcast address */
226 memset(packet.ethHdr.h_dest, 0xFF, ETH_ALEN);
227 memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN);
229 packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
230 packet.vertype = PPPOE_VER_TYPE(1, 1);
231 packet.code = CODE_PADI;
234 if (!omit_service_name) {
235 plen = TAG_HDR_SIZE + namelen;
236 CHECK_ROOM(cursor, packet.payload, plen);
238 svc->type = TAG_SERVICE_NAME;
239 svc->length = htons(namelen);
241 if (conn->serviceName) {
242 memcpy(svc->payload, conn->serviceName, strlen(conn->serviceName));
244 cursor += namelen + TAG_HDR_SIZE;
249 /* If we're using Host-Uniq, copy it over */
250 if (conn->useHostUniq) {
252 pid_t pid = getpid();
253 hostUniq.type = htons(TAG_HOST_UNIQ);
254 hostUniq.length = htons(sizeof(pid));
255 memcpy(hostUniq.payload, &pid, sizeof(pid));
256 CHECK_ROOM(cursor, packet.payload, sizeof(pid) + TAG_HDR_SIZE);
257 memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE);
258 cursor += sizeof(pid) + TAG_HDR_SIZE;
259 plen += sizeof(pid) + TAG_HDR_SIZE;
262 packet.length = htons(plen);
264 sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE));
267 /**********************************************************************
268 *%FUNCTION: waitForPADO
270 * conn -- PPPoEConnection structure
271 * timeout -- how long to wait (in seconds)
275 * Waits for a PADO packet and copies useful information
276 ***********************************************************************/
278 waitForPADO(PPPoEConnection *conn, int timeout)
286 struct PacketCriteria pc;
288 pc.acNameOK = (conn->acName) ? 0 : 1;
289 pc.serviceNameOK = (conn->serviceName) ? 0 : 1;
291 pc.seenServiceName = 0;
295 if (BPF_BUFFER_IS_EMPTY) {
300 FD_SET(conn->discoverySocket, &readable);
303 r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv);
304 if (r >= 0 || errno != EINTR) break;
307 error("select (waitForPADO): %m");
310 if (r == 0) return; /* Timed out */
314 receivePacket(conn->discoverySocket, &packet, &len);
317 if (ntohs(packet.length) + HDR_SIZE > len) {
318 error("Bogus PPPoE length field (%u)",
319 (unsigned int) ntohs(packet.length));
324 /* If it's not a Discovery packet, loop again */
325 if (etherType(&packet) != Eth_PPPOE_Discovery) continue;
328 /* If it's not for us, loop again */
329 if (!packetIsForMe(conn, &packet)) continue;
331 if (packet.code == CODE_PADO) {
332 if (NOT_UNICAST(packet.ethHdr.h_source)) {
333 error("Ignoring PADO packet from non-unicast MAC address");
336 if (parsePacket(&packet, parsePADOTags, &pc) < 0)
340 if (!pc.seenACName) {
341 error("Ignoring PADO packet with no AC-Name tag");
344 if (!pc.seenServiceName) {
345 error("Ignoring PADO packet with no Service-Name tag");
349 if (pc.acNameOK && pc.serviceNameOK) {
350 memcpy(conn->peerEth, packet.ethHdr.h_source, ETH_ALEN);
351 conn->discoveryState = STATE_RECEIVED_PADO;
355 } while (conn->discoveryState != STATE_RECEIVED_PADO);
358 /***********************************************************************
361 * conn -- PPPoE connection structur
365 * Sends a PADR packet
366 ***********************************************************************/
368 sendPADR(PPPoEConnection *conn)
371 PPPoETag *svc = (PPPoETag *) packet.payload;
372 unsigned char *cursor = packet.payload;
374 UINT16_t namelen = 0;
377 if (conn->serviceName) {
378 namelen = (UINT16_t) strlen(conn->serviceName);
380 plen = TAG_HDR_SIZE + namelen;
381 CHECK_ROOM(cursor, packet.payload, plen);
383 memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN);
384 memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN);
386 packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
387 packet.vertype = PPPOE_VER_TYPE(1, 1);
388 packet.code = CODE_PADR;
391 svc->type = TAG_SERVICE_NAME;
392 svc->length = htons(namelen);
393 if (conn->serviceName) {
394 memcpy(svc->payload, conn->serviceName, namelen);
396 cursor += namelen + TAG_HDR_SIZE;
398 /* If we're using Host-Uniq, copy it over */
399 if (conn->useHostUniq) {
401 pid_t pid = getpid();
402 hostUniq.type = htons(TAG_HOST_UNIQ);
403 hostUniq.length = htons(sizeof(pid));
404 memcpy(hostUniq.payload, &pid, sizeof(pid));
405 CHECK_ROOM(cursor, packet.payload, sizeof(pid)+TAG_HDR_SIZE);
406 memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE);
407 cursor += sizeof(pid) + TAG_HDR_SIZE;
408 plen += sizeof(pid) + TAG_HDR_SIZE;
411 /* Copy cookie and relay-ID if needed */
412 if (conn->cookie.type) {
413 CHECK_ROOM(cursor, packet.payload,
414 ntohs(conn->cookie.length) + TAG_HDR_SIZE);
415 memcpy(cursor, &conn->cookie, ntohs(conn->cookie.length) + TAG_HDR_SIZE);
416 cursor += ntohs(conn->cookie.length) + TAG_HDR_SIZE;
417 plen += ntohs(conn->cookie.length) + TAG_HDR_SIZE;
420 if (conn->relayId.type) {
421 CHECK_ROOM(cursor, packet.payload,
422 ntohs(conn->relayId.length) + TAG_HDR_SIZE);
423 memcpy(cursor, &conn->relayId, ntohs(conn->relayId.length) + TAG_HDR_SIZE);
424 cursor += ntohs(conn->relayId.length) + TAG_HDR_SIZE;
425 plen += ntohs(conn->relayId.length) + TAG_HDR_SIZE;
428 packet.length = htons(plen);
429 sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE));
432 /**********************************************************************
433 *%FUNCTION: waitForPADS
435 * conn -- PPPoE connection info
436 * timeout -- how long to wait (in seconds)
440 * Waits for a PADS packet and copies useful information
441 ***********************************************************************/
443 waitForPADS(PPPoEConnection *conn, int timeout)
453 if (BPF_BUFFER_IS_EMPTY) {
458 FD_SET(conn->discoverySocket, &readable);
461 r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv);
462 if (r >= 0 || errno != EINTR) break;
465 error("select (waitForPADS): %m");
472 receivePacket(conn->discoverySocket, &packet, &len);
475 if (ntohs(packet.length) + HDR_SIZE > len) {
476 error("Bogus PPPoE length field (%u)",
477 (unsigned int) ntohs(packet.length));
482 /* If it's not a Discovery packet, loop again */
483 if (etherType(&packet) != Eth_PPPOE_Discovery) continue;
486 /* If it's not from the AC, it's not for me */
487 if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) continue;
489 /* If it's not for us, loop again */
490 if (!packetIsForMe(conn, &packet)) continue;
493 if (packet.code == CODE_PADS) {
494 /* Parse for goodies */
495 if (parsePacket(&packet, parsePADSTags, conn) < 0)
499 conn->discoveryState = STATE_SESSION;
502 } while (conn->discoveryState != STATE_SESSION);
504 /* Don't bother with ntohs; we'll just end up converting it back... */
505 conn->session = packet.session;
507 info("PPP session is %d", (int) ntohs(conn->session));
509 /* RFC 2516 says session id MUST NOT be zero or 0xFFFF */
510 if (ntohs(conn->session) == 0 || ntohs(conn->session) == 0xFFFF) {
511 error("Access concentrator used a session value of %x -- the AC is violating RFC 2516", (unsigned int) ntohs(conn->session));
515 /**********************************************************************
516 *%FUNCTION: discovery
518 * conn -- PPPoE connection info structure
522 * Performs the PPPoE discovery phase
523 ***********************************************************************/
525 discovery(PPPoEConnection *conn)
527 int padiAttempts = 0;
528 int padrAttempts = 0;
529 int timeout = conn->discoveryTimeout;
531 conn->discoverySocket =
532 openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth);
536 if (padiAttempts > MAX_PADI_ATTEMPTS) {
537 warn("Timeout waiting for PADO packets");
538 close(conn->discoverySocket);
539 conn->discoverySocket = -1;
543 conn->discoveryState = STATE_SENT_PADI;
544 waitForPADO(conn, timeout);
547 } while (conn->discoveryState == STATE_SENT_PADI);
549 timeout = conn->discoveryTimeout;
552 if (padrAttempts > MAX_PADI_ATTEMPTS) {
553 warn("Timeout waiting for PADS packets");
554 close(conn->discoverySocket);
555 conn->discoverySocket = -1;
559 conn->discoveryState = STATE_SENT_PADR;
560 waitForPADS(conn, timeout);
562 } while (conn->discoveryState == STATE_SENT_PADR);
565 conn->discoveryState = STATE_SESSION;