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