]> git.ozlabs.org Git - ppp.git/blob - pppd/plugins/rp-pppoe/discovery.c
rp-pppoe: allow MTU to be increased up to 1500
[ppp.git] / pppd / plugins / rp-pppoe / discovery.c
1 /***********************************************************************
2 *
3 * discovery.c
4 *
5 * Perform PPPoE discovery
6 *
7 * Copyright (C) 1999 by Roaring Penguin Software Inc.
8 *
9 ***********************************************************************/
10
11 static char const RCSID[] =
12 "$Id: discovery.c,v 1.6 2008/06/15 04:35:50 paulus Exp $";
13
14 #define _GNU_SOURCE 1
15 #include "pppoe.h"
16 #include "pppd/pppd.h"
17 #include "pppd/fsm.h"
18 #include "pppd/lcp.h"
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 ***********************************************************************/
55 static void
56 parseForHostUniq(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 ***********************************************************************/
80 static int
81 packetIsForMe(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 ***********************************************************************/
109 static void
110 parsePADOTags(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     UINT16_t mru;
116     int i;
117
118     switch(type) {
119     case TAG_AC_NAME:
120         pc->seenACName = 1;
121         if (conn->printACNames) {
122             info("Access-Concentrator: %.*s", (int) len, data);
123         }
124         if (conn->acName && len == strlen(conn->acName) &&
125             !strncmp((char *) data, conn->acName, len)) {
126             pc->acNameOK = 1;
127         }
128         break;
129     case TAG_SERVICE_NAME:
130         pc->seenServiceName = 1;
131         if (conn->serviceName && len == strlen(conn->serviceName) &&
132             !strncmp((char *) data, conn->serviceName, len)) {
133             pc->serviceNameOK = 1;
134         }
135         break;
136     case TAG_AC_COOKIE:
137         conn->cookie.type = htons(type);
138         conn->cookie.length = htons(len);
139         memcpy(conn->cookie.payload, data, len);
140         break;
141     case TAG_RELAY_SESSION_ID:
142         conn->relayId.type = htons(type);
143         conn->relayId.length = htons(len);
144         memcpy(conn->relayId.payload, data, len);
145         break;
146     case TAG_PPP_MAX_PAYLOAD:
147         if (len == sizeof(mru)) {
148             memcpy(&mru, data, sizeof(mru));
149             mru = ntohs(mru);
150             if (mru >= ETH_PPPOE_MTU) {
151                 if (lcp_allowoptions[0].mru > mru)
152                     lcp_allowoptions[0].mru = mru;
153                 if (lcp_wantoptions[0].mru > mru)
154                     lcp_wantoptions[0].mru = mru;
155                 conn->seenMaxPayload = 1;
156             }
157         }
158         break;
159     case TAG_SERVICE_NAME_ERROR:
160         error("PADO: Service-Name-Error: %.*s", (int) len, data);
161         conn->error = 1;
162         break;
163     case TAG_AC_SYSTEM_ERROR:
164         error("PADO: System-Error: %.*s", (int) len, data);
165         conn->error = 1;
166         break;
167     case TAG_GENERIC_ERROR:
168         error("PADO: Generic-Error: %.*s", (int) len, data);
169         conn->error = 1;
170         break;
171     }
172 }
173
174 /**********************************************************************
175 *%FUNCTION: parsePADSTags
176 *%ARGUMENTS:
177 * type -- tag type
178 * len -- tag length
179 * data -- tag data
180 * extra -- extra user data (pointer to PPPoEConnection structure)
181 *%RETURNS:
182 * Nothing
183 *%DESCRIPTION:
184 * Picks interesting tags out of a PADS packet
185 ***********************************************************************/
186 static void
187 parsePADSTags(UINT16_t type, UINT16_t len, unsigned char *data,
188               void *extra)
189 {
190     PPPoEConnection *conn = (PPPoEConnection *) extra;
191     UINT16_t mru;
192     switch(type) {
193     case TAG_SERVICE_NAME:
194         dbglog("PADS: Service-Name: '%.*s'", (int) len, data);
195         break;
196     case TAG_PPP_MAX_PAYLOAD:
197         if (len == sizeof(mru)) {
198             memcpy(&mru, data, sizeof(mru));
199             mru = ntohs(mru);
200             if (mru >= ETH_PPPOE_MTU) {
201                 if (lcp_allowoptions[0].mru > mru)
202                     lcp_allowoptions[0].mru = mru;
203                 if (lcp_wantoptions[0].mru > mru)
204                     lcp_wantoptions[0].mru = mru;
205                 conn->seenMaxPayload = 1;
206             }
207         }
208         break;
209     case TAG_SERVICE_NAME_ERROR:
210         error("PADS: Service-Name-Error: %.*s", (int) len, data);
211         conn->error = 1;
212         break;
213     case TAG_AC_SYSTEM_ERROR:
214         error("PADS: System-Error: %.*s", (int) len, data);
215         conn->error = 1;
216         break;
217     case TAG_GENERIC_ERROR:
218         error("PADS: Generic-Error: %.*s", (int) len, data);
219         conn->error = 1;
220         break;
221     case TAG_RELAY_SESSION_ID:
222         conn->relayId.type = htons(type);
223         conn->relayId.length = htons(len);
224         memcpy(conn->relayId.payload, data, len);
225         break;
226     }
227 }
228
229 /***********************************************************************
230 *%FUNCTION: sendPADI
231 *%ARGUMENTS:
232 * conn -- PPPoEConnection structure
233 *%RETURNS:
234 * Nothing
235 *%DESCRIPTION:
236 * Sends a PADI packet
237 ***********************************************************************/
238 static void
239 sendPADI(PPPoEConnection *conn)
240 {
241     PPPoEPacket packet;
242     unsigned char *cursor = packet.payload;
243     PPPoETag *svc = (PPPoETag *) (&packet.payload);
244     UINT16_t namelen = 0;
245     UINT16_t plen;
246     int omit_service_name = 0;
247
248     if (conn->serviceName) {
249         namelen = (UINT16_t) strlen(conn->serviceName);
250         if (!strcmp(conn->serviceName, "NO-SERVICE-NAME-NON-RFC-COMPLIANT")) {
251             omit_service_name = 1;
252         }
253     }
254
255     /* Set destination to Ethernet broadcast address */
256     memset(packet.ethHdr.h_dest, 0xFF, ETH_ALEN);
257     memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN);
258
259     packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
260     packet.vertype = PPPOE_VER_TYPE(1, 1);
261     packet.code = CODE_PADI;
262     packet.session = 0;
263
264     if (!omit_service_name) {
265         plen = TAG_HDR_SIZE + namelen;
266         CHECK_ROOM(cursor, packet.payload, plen);
267
268         svc->type = TAG_SERVICE_NAME;
269         svc->length = htons(namelen);
270
271         if (conn->serviceName) {
272             memcpy(svc->payload, conn->serviceName, strlen(conn->serviceName));
273         }
274         cursor += namelen + TAG_HDR_SIZE;
275     } else {
276         plen = 0;
277     }
278
279     /* If we're using Host-Uniq, copy it over */
280     if (conn->useHostUniq) {
281         PPPoETag hostUniq;
282         pid_t pid = getpid();
283         hostUniq.type = htons(TAG_HOST_UNIQ);
284         hostUniq.length = htons(sizeof(pid));
285         memcpy(hostUniq.payload, &pid, sizeof(pid));
286         CHECK_ROOM(cursor, packet.payload, sizeof(pid) + TAG_HDR_SIZE);
287         memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE);
288         cursor += sizeof(pid) + TAG_HDR_SIZE;
289         plen += sizeof(pid) + TAG_HDR_SIZE;
290     }
291
292     /* Add our maximum MTU/MRU */
293     if (MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru) > ETH_PPPOE_MTU) {
294         PPPoETag maxPayload;
295         UINT16_t mru = htons(MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru));
296         maxPayload.type = htons(TAG_PPP_MAX_PAYLOAD);
297         maxPayload.length = htons(sizeof(mru));
298         memcpy(maxPayload.payload, &mru, sizeof(mru));
299         CHECK_ROOM(cursor, packet.payload, sizeof(mru) + TAG_HDR_SIZE);
300         memcpy(cursor, &maxPayload, sizeof(mru) + TAG_HDR_SIZE);
301         cursor += sizeof(mru) + TAG_HDR_SIZE;
302         plen += sizeof(mru) + TAG_HDR_SIZE;
303     }
304
305     packet.length = htons(plen);
306
307     sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE));
308 }
309
310 /**********************************************************************
311 *%FUNCTION: waitForPADO
312 *%ARGUMENTS:
313 * conn -- PPPoEConnection structure
314 * timeout -- how long to wait (in seconds)
315 *%RETURNS:
316 * Nothing
317 *%DESCRIPTION:
318 * Waits for a PADO packet and copies useful information
319 ***********************************************************************/
320 void
321 waitForPADO(PPPoEConnection *conn, int timeout)
322 {
323     fd_set readable;
324     int r;
325     struct timeval tv;
326     PPPoEPacket packet;
327     int len;
328
329     struct PacketCriteria pc;
330     pc.conn          = conn;
331     pc.acNameOK      = (conn->acName)      ? 0 : 1;
332     pc.serviceNameOK = (conn->serviceName) ? 0 : 1;
333     pc.seenACName    = 0;
334     pc.seenServiceName = 0;
335     conn->seenMaxPayload = 0;
336     conn->error = 0;
337
338     do {
339         if (BPF_BUFFER_IS_EMPTY) {
340             tv.tv_sec = timeout;
341             tv.tv_usec = 0;
342
343             FD_ZERO(&readable);
344             FD_SET(conn->discoverySocket, &readable);
345
346             while(1) {
347                 r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv);
348                 if (r >= 0 || errno != EINTR) break;
349             }
350             if (r < 0) {
351                 error("select (waitForPADO): %m");
352                 return;
353             }
354             if (r == 0) return;        /* Timed out */
355         }
356
357         /* Get the packet */
358         receivePacket(conn->discoverySocket, &packet, &len);
359
360         /* Check length */
361         if (ntohs(packet.length) + HDR_SIZE > len) {
362             error("Bogus PPPoE length field (%u)",
363                    (unsigned int) ntohs(packet.length));
364             continue;
365         }
366
367 #ifdef USE_BPF
368         /* If it's not a Discovery packet, loop again */
369         if (etherType(&packet) != Eth_PPPOE_Discovery) continue;
370 #endif
371
372         /* If it's not for us, loop again */
373         if (!packetIsForMe(conn, &packet)) continue;
374
375         if (packet.code == CODE_PADO) {
376             if (NOT_UNICAST(packet.ethHdr.h_source)) {
377                 error("Ignoring PADO packet from non-unicast MAC address");
378                 continue;
379             }
380             if (conn->req_peer
381                 && memcmp(packet.ethHdr.h_source, conn->req_peer_mac, ETH_ALEN) != 0) {
382                 warn("Ignoring PADO packet from wrong MAC address");
383                 continue;
384             }
385             if (parsePacket(&packet, parsePADOTags, &pc) < 0)
386                 return;
387             if (conn->error)
388                 return;
389             if (!pc.seenACName) {
390                 error("Ignoring PADO packet with no AC-Name tag");
391                 continue;
392             }
393             if (!pc.seenServiceName) {
394                 error("Ignoring PADO packet with no Service-Name tag");
395                 continue;
396             }
397             conn->numPADOs++;
398             if (pc.acNameOK && pc.serviceNameOK) {
399                 memcpy(conn->peerEth, packet.ethHdr.h_source, ETH_ALEN);
400                 conn->discoveryState = STATE_RECEIVED_PADO;
401                 break;
402             }
403         }
404     } while (conn->discoveryState != STATE_RECEIVED_PADO);
405 }
406
407 /***********************************************************************
408 *%FUNCTION: sendPADR
409 *%ARGUMENTS:
410 * conn -- PPPoE connection structur
411 *%RETURNS:
412 * Nothing
413 *%DESCRIPTION:
414 * Sends a PADR packet
415 ***********************************************************************/
416 static void
417 sendPADR(PPPoEConnection *conn)
418 {
419     PPPoEPacket packet;
420     PPPoETag *svc = (PPPoETag *) packet.payload;
421     unsigned char *cursor = packet.payload;
422
423     UINT16_t namelen = 0;
424     UINT16_t plen;
425
426     if (conn->serviceName) {
427         namelen = (UINT16_t) strlen(conn->serviceName);
428     }
429     plen = TAG_HDR_SIZE + namelen;
430     CHECK_ROOM(cursor, packet.payload, plen);
431
432     memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN);
433     memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN);
434
435     packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
436     packet.vertype = PPPOE_VER_TYPE(1, 1);
437     packet.code = CODE_PADR;
438     packet.session = 0;
439
440     svc->type = TAG_SERVICE_NAME;
441     svc->length = htons(namelen);
442     if (conn->serviceName) {
443         memcpy(svc->payload, conn->serviceName, namelen);
444     }
445     cursor += namelen + TAG_HDR_SIZE;
446
447     /* If we're using Host-Uniq, copy it over */
448     if (conn->useHostUniq) {
449         PPPoETag hostUniq;
450         pid_t pid = getpid();
451         hostUniq.type = htons(TAG_HOST_UNIQ);
452         hostUniq.length = htons(sizeof(pid));
453         memcpy(hostUniq.payload, &pid, sizeof(pid));
454         CHECK_ROOM(cursor, packet.payload, sizeof(pid)+TAG_HDR_SIZE);
455         memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE);
456         cursor += sizeof(pid) + TAG_HDR_SIZE;
457         plen += sizeof(pid) + TAG_HDR_SIZE;
458     }
459
460     /* Add our maximum MTU/MRU */
461     if (MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru) > ETH_PPPOE_MTU) {
462         PPPoETag maxPayload;
463         UINT16_t mru = htons(MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru));
464         maxPayload.type = htons(TAG_PPP_MAX_PAYLOAD);
465         maxPayload.length = htons(sizeof(mru));
466         memcpy(maxPayload.payload, &mru, sizeof(mru));
467         CHECK_ROOM(cursor, packet.payload, sizeof(mru) + TAG_HDR_SIZE);
468         memcpy(cursor, &maxPayload, sizeof(mru) + TAG_HDR_SIZE);
469         cursor += sizeof(mru) + TAG_HDR_SIZE;
470         plen += sizeof(mru) + TAG_HDR_SIZE;
471     }
472
473     /* Copy cookie and relay-ID if needed */
474     if (conn->cookie.type) {
475         CHECK_ROOM(cursor, packet.payload,
476                    ntohs(conn->cookie.length) + TAG_HDR_SIZE);
477         memcpy(cursor, &conn->cookie, ntohs(conn->cookie.length) + TAG_HDR_SIZE);
478         cursor += ntohs(conn->cookie.length) + TAG_HDR_SIZE;
479         plen += ntohs(conn->cookie.length) + TAG_HDR_SIZE;
480     }
481
482     if (conn->relayId.type) {
483         CHECK_ROOM(cursor, packet.payload,
484                    ntohs(conn->relayId.length) + TAG_HDR_SIZE);
485         memcpy(cursor, &conn->relayId, ntohs(conn->relayId.length) + TAG_HDR_SIZE);
486         cursor += ntohs(conn->relayId.length) + TAG_HDR_SIZE;
487         plen += ntohs(conn->relayId.length) + TAG_HDR_SIZE;
488     }
489
490     packet.length = htons(plen);
491     sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE));
492 }
493
494 /**********************************************************************
495 *%FUNCTION: waitForPADS
496 *%ARGUMENTS:
497 * conn -- PPPoE connection info
498 * timeout -- how long to wait (in seconds)
499 *%RETURNS:
500 * Nothing
501 *%DESCRIPTION:
502 * Waits for a PADS packet and copies useful information
503 ***********************************************************************/
504 static void
505 waitForPADS(PPPoEConnection *conn, int timeout)
506 {
507     fd_set readable;
508     int r;
509     struct timeval tv;
510     PPPoEPacket packet;
511     int len;
512
513     conn->error = 0;
514     do {
515         if (BPF_BUFFER_IS_EMPTY) {
516             tv.tv_sec = timeout;
517             tv.tv_usec = 0;
518
519             FD_ZERO(&readable);
520             FD_SET(conn->discoverySocket, &readable);
521
522             while(1) {
523                 r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv);
524                 if (r >= 0 || errno != EINTR) break;
525             }
526             if (r < 0) {
527                 error("select (waitForPADS): %m");
528                 return;
529             }
530             if (r == 0) return;
531         }
532
533         /* Get the packet */
534         receivePacket(conn->discoverySocket, &packet, &len);
535
536         /* Check length */
537         if (ntohs(packet.length) + HDR_SIZE > len) {
538             error("Bogus PPPoE length field (%u)",
539                    (unsigned int) ntohs(packet.length));
540             continue;
541         }
542
543 #ifdef USE_BPF
544         /* If it's not a Discovery packet, loop again */
545         if (etherType(&packet) != Eth_PPPOE_Discovery) continue;
546 #endif
547
548         /* If it's not from the AC, it's not for me */
549         if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) continue;
550
551         /* If it's not for us, loop again */
552         if (!packetIsForMe(conn, &packet)) continue;
553
554         /* Is it PADS?  */
555         if (packet.code == CODE_PADS) {
556             /* Parse for goodies */
557             if (parsePacket(&packet, parsePADSTags, conn) < 0)
558                 return;
559             if (conn->error)
560                 return;
561             conn->discoveryState = STATE_SESSION;
562             break;
563         }
564     } while (conn->discoveryState != STATE_SESSION);
565
566     /* Don't bother with ntohs; we'll just end up converting it back... */
567     conn->session = packet.session;
568
569     info("PPP session is %d", (int) ntohs(conn->session));
570
571     /* RFC 2516 says session id MUST NOT be zero or 0xFFFF */
572     if (ntohs(conn->session) == 0 || ntohs(conn->session) == 0xFFFF) {
573         error("Access concentrator used a session value of %x -- the AC is violating RFC 2516", (unsigned int) ntohs(conn->session));
574     }
575 }
576
577 /**********************************************************************
578 *%FUNCTION: discovery
579 *%ARGUMENTS:
580 * conn -- PPPoE connection info structure
581 *%RETURNS:
582 * Nothing
583 *%DESCRIPTION:
584 * Performs the PPPoE discovery phase
585 ***********************************************************************/
586 void
587 discovery(PPPoEConnection *conn)
588 {
589     int padiAttempts = 0;
590     int padrAttempts = 0;
591     int timeout = conn->discoveryTimeout;
592
593     conn->discoverySocket =
594         openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth);
595
596     do {
597         padiAttempts++;
598         if (padiAttempts > MAX_PADI_ATTEMPTS) {
599             warn("Timeout waiting for PADO packets");
600             close(conn->discoverySocket);
601             conn->discoverySocket = -1;
602             return;
603         }
604         sendPADI(conn);
605         conn->discoveryState = STATE_SENT_PADI;
606         waitForPADO(conn, timeout);
607
608         timeout *= 2;
609     } while (conn->discoveryState == STATE_SENT_PADI);
610
611     timeout = conn->discoveryTimeout;
612     do {
613         padrAttempts++;
614         if (padrAttempts > MAX_PADI_ATTEMPTS) {
615             warn("Timeout waiting for PADS packets");
616             close(conn->discoverySocket);
617             conn->discoverySocket = -1;
618             return;
619         }
620         sendPADR(conn);
621         conn->discoveryState = STATE_SENT_PADR;
622         waitForPADS(conn, timeout);
623         timeout *= 2;
624     } while (conn->discoveryState == STATE_SENT_PADR);
625
626     if (!conn->seenMaxPayload) {
627         /* RFC 4638: MUST limit MTU/MRU to 1492 */
628         if (lcp_allowoptions[0].mru > ETH_PPPOE_MTU)
629             lcp_allowoptions[0].mru = ETH_PPPOE_MTU;
630         if (lcp_wantoptions[0].mru > ETH_PPPOE_MTU)
631             lcp_wantoptions[0].mru = ETH_PPPOE_MTU;
632     }
633
634     /* We're done. */
635     conn->discoveryState = STATE_SESSION;
636     return;
637 }