summaryrefslogtreecommitdiff
path: root/src/relay.c (plain)
blob: 2dad044d7c0237e2eda8a5ac6952419a5776b69b
1/***********************************************************************
2*
3* relay.c
4*
5* Implementation of PPPoE relay
6*
7* Copyright (C) 2001-2006 Roaring Penguin Software Inc.
8*
9* This program may be distributed according to the terms of the GNU
10* General Public License, version 2 or (at your option) any later version.
11*
12* LIC: GPL
13*
14* $Id$
15*
16***********************************************************************/
17static char const RCSID[] =
18"$Id$";
19
20#define _GNU_SOURCE 1 /* For SA_RESTART */
21
22#include "relay.h"
23
24#include <signal.h>
25
26#ifdef HAVE_SYSLOG_H
27#include <syslog.h>
28#endif
29
30#ifdef HAVE_GETOPT_H
31#include <getopt.h>
32#endif
33
34#include <stdlib.h>
35#include <string.h>
36#include <errno.h>
37
38#ifdef HAVE_SYS_TIME_H
39#include <sys/time.h>
40#endif
41
42#ifdef HAVE_SYS_UIO_H
43#include <sys/uio.h>
44#endif
45
46#ifdef HAVE_UNISTD_H
47#include <unistd.h>
48#endif
49
50
51/* Interfaces (max MAX_INTERFACES) */
52PPPoEInterface Interfaces[MAX_INTERFACES];
53int NumInterfaces;
54
55/* Relay info */
56int NumSessions;
57int MaxSessions;
58PPPoESession *AllSessions;
59PPPoESession *FreeSessions;
60PPPoESession *ActiveSessions;
61
62SessionHash *AllHashes;
63SessionHash *FreeHashes;
64SessionHash *Buckets[HASHTAB_SIZE];
65
66volatile unsigned int Epoch = 0;
67volatile unsigned int CleanCounter = 0;
68
69/* How often to clean up stale sessions? */
70#define MIN_CLEAN_PERIOD 30 /* Minimum period to run cleaner */
71#define TIMEOUT_DIVISOR 20 /* How often to run cleaner per timeout period */
72unsigned int CleanPeriod = MIN_CLEAN_PERIOD;
73
74/* How long a session can be idle before it is cleaned up? */
75unsigned int IdleTimeout = MIN_CLEAN_PERIOD * TIMEOUT_DIVISOR;
76
77/* Pipe for breaking select() to initiate periodic cleaning */
78int CleanPipe[2];
79
80/* Our relay: if_index followed by peer_mac */
81#define MY_RELAY_TAG_LEN (sizeof(int) + ETH_ALEN)
82
83/* Hack for daemonizing */
84#define CLOSEFD 64
85
86/**********************************************************************
87*%FUNCTION: keepDescriptor
88*%ARGUMENTS:
89* fd -- a file descriptor
90*%RETURNS:
91* 1 if descriptor should NOT be closed during daemonizing; 0 otherwise.
92***********************************************************************/
93static int
94keepDescriptor(int fd)
95{
96 int i;
97 if (fd == CleanPipe[0] || fd == CleanPipe[1]) return 1;
98 for (i=0; i<NumInterfaces; i++) {
99 if (fd == Interfaces[i].discoverySock ||
100 fd == Interfaces[i].sessionSock) return 1;
101 }
102 return 0;
103}
104
105/**********************************************************************
106*%FUNCTION: addTag
107*%ARGUMENTS:
108* packet -- a PPPoE packet
109* tag -- tag to add
110*%RETURNS:
111* -1 if no room in packet; number of bytes added otherwise.
112*%DESCRIPTION:
113* Inserts a tag as the first tag in a PPPoE packet.
114***********************************************************************/
115int
116addTag(PPPoEPacket *packet, PPPoETag const *tag)
117{
118 return insertBytes(packet, packet->payload, tag,
119 ntohs(tag->length) + TAG_HDR_SIZE);
120}
121
122/**********************************************************************
123*%FUNCTION: insertBytes
124*%ARGUMENTS:
125* packet -- a PPPoE packet
126* loc -- location at which to insert bytes of data
127* bytes -- the data to insert
128* len -- length of data to insert
129*%RETURNS:
130* -1 if no room in packet; len otherwise.
131*%DESCRIPTION:
132* Inserts "len" bytes of data at location "loc" in "packet", moving all
133* other data up to make room.
134***********************************************************************/
135int
136insertBytes(PPPoEPacket *packet,
137 unsigned char *loc,
138 void const *bytes,
139 int len)
140{
141 int toMove;
142 int plen = ntohs(packet->length);
143 /* Sanity checks */
144 if (loc < packet->payload ||
145 loc > packet->payload + plen ||
146 len + plen > MAX_PPPOE_PAYLOAD) {
147 return -1;
148 }
149
150 toMove = (packet->payload + plen) - loc;
151 memmove(loc+len, loc, toMove);
152 memcpy(loc, bytes, len);
153 packet->length = htons(plen + len);
154 return len;
155}
156
157/**********************************************************************
158*%FUNCTION: removeBytes
159*%ARGUMENTS:
160* packet -- a PPPoE packet
161* loc -- location at which to remove bytes of data
162* len -- length of data to remove
163*%RETURNS:
164* -1 if there was a problem, len otherwise
165*%DESCRIPTION:
166* Removes "len" bytes of data from location "loc" in "packet", moving all
167* other data down to close the gap
168***********************************************************************/
169int
170removeBytes(PPPoEPacket *packet,
171 unsigned char *loc,
172 int len)
173{
174 int toMove;
175 int plen = ntohs(packet->length);
176 /* Sanity checks */
177 if (len < 0 || len > plen ||
178 loc < packet->payload ||
179 loc + len > packet->payload + plen) {
180 return -1;
181 }
182
183 toMove = ((packet->payload + plen) - loc) - len;
184 memmove(loc, loc+len, toMove);
185 packet->length = htons(plen - len);
186 return len;
187}
188
189/**********************************************************************
190*%FUNCTION: usage
191*%ARGUMENTS:
192* argv0 -- program name
193*%RETURNS:
194* Nothing
195*%DESCRIPTION:
196* Prints usage information and exits.
197***********************************************************************/
198void
199usage(char const *argv0)
200{
201 fprintf(stderr, "Usage: %s [options]\n", argv0);
202 fprintf(stderr, "Options:\n");
203 fprintf(stderr, " -S if_name -- Specify interface for PPPoE Server\n");
204 fprintf(stderr, " -C if_name -- Specify interface for PPPoE Client\n");
205 fprintf(stderr, " -B if_name -- Specify interface for both clients and server\n");
206 fprintf(stderr, " -n nsess -- Maxmimum number of sessions to relay\n");
207 fprintf(stderr, " -i timeout -- Idle timeout in seconds (0 = no timeout)\n");
208 fprintf(stderr, " -F -- Do not fork into background\n");
209 fprintf(stderr, " -h -- Print this help message\n");
210
211 fprintf(stderr, "\nPPPoE Version %s, Copyright (C) 2001-2006 Roaring Penguin Software Inc.\n", VERSION);
212 fprintf(stderr, "PPPoE comes with ABSOLUTELY NO WARRANTY.\n");
213 fprintf(stderr, "This is free software, and you are welcome to redistribute it under the terms\n");
214 fprintf(stderr, "of the GNU General Public License, version 2 or any later version.\n");
215 fprintf(stderr, "http://www.roaringpenguin.com\n");
216 exit(EXIT_SUCCESS);
217}
218
219/**********************************************************************
220*%FUNCTION: main
221*%ARGUMENTS:
222* argc, argv -- usual suspects
223*%RETURNS:
224* EXIT_SUCCESS or EXIT_FAILURE
225*%DESCRIPTION:
226* Main program. Options:
227* -C ifname -- Use interface for PPPoE clients
228* -S ifname -- Use interface for PPPoE servers
229* -B ifname -- Use interface for both clients and servers
230* -n sessions -- Maximum of "n" sessions
231***********************************************************************/
232int
233main(int argc, char *argv[])
234{
235 int opt;
236 int nsess = DEFAULT_SESSIONS;
237 struct sigaction sa;
238 int beDaemon = 1;
239
240 if (getuid() != geteuid() ||
241 getgid() != getegid()) {
242 fprintf(stderr, "SECURITY WARNING: pppoe-relay will NOT run suid or sgid. Fix your installation.\n");
243 exit(1);
244 }
245
246
247 openlog("pppoe-relay", LOG_PID, LOG_DAEMON);
248
249 while((opt = getopt(argc, argv, "hC:S:B:n:i:F")) != -1) {
250 switch(opt) {
251 case 'h':
252 usage(argv[0]);
253 break;
254 case 'F':
255 beDaemon = 0;
256 break;
257 case 'C':
258 addInterface(optarg, 1, 0);
259 break;
260 case 'S':
261 addInterface(optarg, 0, 1);
262 break;
263 case 'B':
264 addInterface(optarg, 1, 1);
265 break;
266 case 'i':
267 if (sscanf(optarg, "%u", &IdleTimeout) != 1) {
268 fprintf(stderr, "Illegal argument to -i: should be -i timeout\n");
269 exit(EXIT_FAILURE);
270 }
271 CleanPeriod = IdleTimeout / TIMEOUT_DIVISOR;
272 if (CleanPeriod < MIN_CLEAN_PERIOD) CleanPeriod = MIN_CLEAN_PERIOD;
273 break;
274 case 'n':
275 if (sscanf(optarg, "%d", &nsess) != 1) {
276 fprintf(stderr, "Illegal argument to -n: should be -n #sessions\n");
277 exit(EXIT_FAILURE);
278 }
279 if (nsess < 1 || nsess > 65534) {
280 fprintf(stderr, "Illegal argument to -n: must range from 1 to 65534\n");
281 exit(EXIT_FAILURE);
282 }
283 break;
284 default:
285 usage(argv[0]);
286 }
287 }
288
289#ifdef USE_LINUX_PACKET
290#ifndef HAVE_STRUCT_SOCKADDR_LL
291 fprintf(stderr, "The PPPoE relay does not work on Linux 2.0 kernels.\n");
292 exit(EXIT_FAILURE);
293#endif
294#endif
295
296 /* Check that at least two interfaces were defined */
297 if (NumInterfaces < 2) {
298 fprintf(stderr, "%s: Must define at least two interfaces\n",
299 argv[0]);
300 exit(EXIT_FAILURE);
301 }
302
303 /* Make a pipe for the cleaner */
304 if (pipe(CleanPipe) < 0) {
305 fatalSys("pipe");
306 }
307
308 /* Set up alarm handler */
309 sa.sa_handler = alarmHandler;
310 sigemptyset(&sa.sa_mask);
311 sa.sa_flags = SA_RESTART;
312 if (sigaction(SIGALRM, &sa, NULL) < 0) {
313 fatalSys("sigaction");
314 }
315
316 /* Allocate memory for sessions, etc. */
317 initRelay(nsess);
318
319 /* Daemonize -- UNIX Network Programming, Vol. 1, Stevens */
320 if (beDaemon) {
321 int i;
322 i = fork();
323 if (i < 0) {
324 fatalSys("fork");
325 } else if (i != 0) {
326 /* parent */
327 exit(0);
328 }
329 setsid();
330 signal(SIGHUP, SIG_IGN);
331 i = fork();
332 if (i < 0) {
333 fatalSys("fork");
334 } else if (i != 0) {
335 exit(0);
336 }
337
338 chdir("/");
339 closelog();
340 for (i=0; i<CLOSEFD; i++) {
341 if (!keepDescriptor(i)) {
342 close(i);
343 }
344 }
345 /* We nuked our syslog descriptor... */
346 openlog("pppoe-relay", LOG_PID, LOG_DAEMON);
347 }
348
349 /* Kick off SIGALRM if there is an idle timeout */
350 if (IdleTimeout) alarm(1);
351
352 /* Enter the relay loop */
353 relayLoop();
354
355 /* Shouldn't ever get here... */
356 return EXIT_FAILURE;
357}
358
359/**********************************************************************
360*%FUNCTION: addInterface
361*%ARGUMENTS:
362* ifname -- interface name
363* clientOK -- true if this interface should relay PADI, PADR packets.
364* acOK -- true if this interface should relay PADO, PADS packets.
365*%RETURNS:
366* Nothing
367*%DESCRIPTION:
368* Opens an interface; sets up discovery and session sockets.
369***********************************************************************/
370void
371addInterface(char const *ifname,
372 int clientOK,
373 int acOK)
374{
375 PPPoEInterface *i;
376 int j;
377 for (j=0; j<NumInterfaces; j++) {
378 if (!strncmp(Interfaces[j].name, ifname, IFNAMSIZ)) {
379 fprintf(stderr, "Interface %s specified more than once.\n", ifname);
380 exit(EXIT_FAILURE);
381 }
382 }
383
384 if (NumInterfaces >= MAX_INTERFACES) {
385 fprintf(stderr, "Too many interfaces (%d max)\n",
386 MAX_INTERFACES);
387 exit(EXIT_FAILURE);
388 }
389 i = &Interfaces[NumInterfaces++];
390 strncpy(i->name, ifname, IFNAMSIZ);
391 i->name[IFNAMSIZ] = 0;
392
393 i->discoverySock = openInterface(ifname, Eth_PPPOE_Discovery, i->mac);
394 i->sessionSock = openInterface(ifname, Eth_PPPOE_Session, NULL);
395 i->clientOK = clientOK;
396 i->acOK = acOK;
397}
398
399/**********************************************************************
400*%FUNCTION: initRelay
401*%ARGUMENTS:
402* nsess -- maximum allowable number of sessions
403*%RETURNS:
404* Nothing
405*%DESCRIPTION:
406* Initializes relay hash table and session tables.
407***********************************************************************/
408void
409initRelay(int nsess)
410{
411 int i;
412 NumSessions = 0;
413 MaxSessions = nsess;
414
415 AllSessions = calloc(MaxSessions, sizeof(PPPoESession));
416 if (!AllSessions) {
417 rp_fatal("Unable to allocate memory for PPPoE session table");
418 }
419 AllHashes = calloc(MaxSessions*2, sizeof(SessionHash));
420 if (!AllHashes) {
421 rp_fatal("Unable to allocate memory for PPPoE hash table");
422 }
423
424 /* Initialize sessions in a linked list */
425 AllSessions[0].prev = NULL;
426 if (MaxSessions > 1) {
427 AllSessions[0].next = &AllSessions[1];
428 } else {
429 AllSessions[0].next = NULL;
430 }
431 for (i=1; i<MaxSessions-1; i++) {
432 AllSessions[i].prev = &AllSessions[i-1];
433 AllSessions[i].next = &AllSessions[i+1];
434 }
435 if (MaxSessions > 1) {
436 AllSessions[MaxSessions-1].prev = &AllSessions[MaxSessions-2];
437 AllSessions[MaxSessions-1].next = NULL;
438 }
439
440 FreeSessions = AllSessions;
441 ActiveSessions = NULL;
442
443 /* Initialize session numbers which we hand out */
444 for (i=0; i<MaxSessions; i++) {
445 AllSessions[i].sesNum = htons((UINT16_t) i+1);
446 }
447
448 /* Initialize hashes in a linked list */
449 AllHashes[0].prev = NULL;
450 AllHashes[0].next = &AllHashes[1];
451 for (i=1; i<2*MaxSessions-1; i++) {
452 AllHashes[i].prev = &AllHashes[i-1];
453 AllHashes[i].next = &AllHashes[i+1];
454 }
455 AllHashes[2*MaxSessions-1].prev = &AllHashes[2*MaxSessions-2];
456 AllHashes[2*MaxSessions-1].next = NULL;
457
458 FreeHashes = AllHashes;
459}
460
461/**********************************************************************
462*%FUNCTION: createSession
463*%ARGUMENTS:
464* ac -- Ethernet interface on access-concentrator side
465* cli -- Ethernet interface on client side
466* acMac -- Access concentrator's MAC address
467* cliMac -- Client's MAC address
468* acSess -- Access concentrator's session ID.
469*%RETURNS:
470* PPPoESession structure; NULL if one could not be allocated
471*%DESCRIPTION:
472* Initializes relay hash table and session tables.
473***********************************************************************/
474PPPoESession *
475createSession(PPPoEInterface const *ac,
476 PPPoEInterface const *cli,
477 unsigned char const *acMac,
478 unsigned char const *cliMac,
479 UINT16_t acSes)
480{
481 PPPoESession *sess;
482 SessionHash *acHash, *cliHash;
483
484 if (NumSessions >= MaxSessions) {
485 printErr("Maximum number of sessions reached -- cannot create new session");
486 return NULL;
487 }
488
489 /* Grab a free session */
490 sess = FreeSessions;
491 FreeSessions = sess->next;
492 NumSessions++;
493
494 /* Link it to the active list */
495 sess->next = ActiveSessions;
496 if (sess->next) {
497 sess->next->prev = sess;
498 }
499 ActiveSessions = sess;
500 sess->prev = NULL;
501
502 sess->epoch = Epoch;
503
504 /* Get two hash entries */
505 acHash = FreeHashes;
506 cliHash = acHash->next;
507 FreeHashes = cliHash->next;
508
509 acHash->peer = cliHash;
510 cliHash->peer = acHash;
511
512 sess->acHash = acHash;
513 sess->clientHash = cliHash;
514
515 acHash->interface = ac;
516 cliHash->interface = cli;
517
518 memcpy(acHash->peerMac, acMac, ETH_ALEN);
519 acHash->sesNum = acSes;
520 acHash->ses = sess;
521
522 memcpy(cliHash->peerMac, cliMac, ETH_ALEN);
523 cliHash->sesNum = sess->sesNum;
524 cliHash->ses = sess;
525
526 addHash(acHash);
527 addHash(cliHash);
528
529 /* Log */
530 syslog(LOG_INFO,
531 "Opened session: server=%02x:%02x:%02x:%02x:%02x:%02x(%s:%d), client=%02x:%02x:%02x:%02x:%02x:%02x(%s:%d)",
532 acHash->peerMac[0], acHash->peerMac[1],
533 acHash->peerMac[2], acHash->peerMac[3],
534 acHash->peerMac[4], acHash->peerMac[5],
535 acHash->interface->name,
536 ntohs(acHash->sesNum),
537 cliHash->peerMac[0], cliHash->peerMac[1],
538 cliHash->peerMac[2], cliHash->peerMac[3],
539 cliHash->peerMac[4], cliHash->peerMac[5],
540 cliHash->interface->name,
541 ntohs(cliHash->sesNum));
542
543 return sess;
544}
545
546/**********************************************************************
547*%FUNCTION: freeSession
548*%ARGUMENTS:
549* ses -- session to free
550* msg -- extra message to log on syslog.
551*%RETURNS:
552* Nothing
553*%DESCRIPTION:
554* Frees data used by a PPPoE session -- adds hashes and session back
555* to the free list
556***********************************************************************/
557void
558freeSession(PPPoESession *ses, char const *msg)
559{
560 syslog(LOG_INFO,
561 "Closed session: server=%02x:%02x:%02x:%02x:%02x:%02x(%s:%d), client=%02x:%02x:%02x:%02x:%02x:%02x(%s:%d): %s",
562 ses->acHash->peerMac[0], ses->acHash->peerMac[1],
563 ses->acHash->peerMac[2], ses->acHash->peerMac[3],
564 ses->acHash->peerMac[4], ses->acHash->peerMac[5],
565 ses->acHash->interface->name,
566 ntohs(ses->acHash->sesNum),
567 ses->clientHash->peerMac[0], ses->clientHash->peerMac[1],
568 ses->clientHash->peerMac[2], ses->clientHash->peerMac[3],
569 ses->clientHash->peerMac[4], ses->clientHash->peerMac[5],
570 ses->clientHash->interface->name,
571 ntohs(ses->clientHash->sesNum), msg);
572
573 /* Unlink from active sessions */
574 if (ses->prev) {
575 ses->prev->next = ses->next;
576 } else {
577 ActiveSessions = ses->next;
578 }
579 if (ses->next) {
580 ses->next->prev = ses->prev;
581 }
582
583 /* Link onto free list -- this is a singly-linked list, so
584 we do not care about prev */
585 ses->next = FreeSessions;
586 FreeSessions = ses;
587
588 unhash(ses->acHash);
589 unhash(ses->clientHash);
590 NumSessions--;
591}
592
593/**********************************************************************
594*%FUNCTION: unhash
595*%ARGUMENTS:
596* sh -- session hash to free
597*%RETURNS:
598* Nothing
599*%DESCRIPTION:
600* Frees a session hash -- takes it out of hash table and puts it on
601* free list.
602***********************************************************************/
603void
604unhash(SessionHash *sh)
605{
606 unsigned int b = hash(sh->peerMac, sh->sesNum) % HASHTAB_SIZE;
607 if (sh->prev) {
608 sh->prev->next = sh->next;
609 } else {
610 Buckets[b] = sh->next;
611 }
612
613 if (sh->next) {
614 sh->next->prev = sh->prev;
615 }
616
617 /* Add to free list (singly-linked) */
618 sh->next = FreeHashes;
619 FreeHashes = sh;
620}
621
622/**********************************************************************
623*%FUNCTION: addHash
624*%ARGUMENTS:
625* sh -- a session hash
626*%RETURNS:
627* Nothing
628*%DESCRIPTION:
629* Adds a SessionHash to the hash table
630***********************************************************************/
631void
632addHash(SessionHash *sh)
633{
634 unsigned int b = hash(sh->peerMac, sh->sesNum) % HASHTAB_SIZE;
635 sh->next = Buckets[b];
636 sh->prev = NULL;
637 if (sh->next) {
638 sh->next->prev = sh;
639 }
640 Buckets[b] = sh;
641}
642
643/**********************************************************************
644*%FUNCTION: hash
645*%ARGUMENTS:
646* mac -- an Ethernet address
647* sesNum -- a session number
648*%RETURNS:
649* A hash value combining Ethernet address with session number.
650* Currently very simplistic; we may need to experiment with different
651* hash values.
652***********************************************************************/
653unsigned int
654hash(unsigned char const *mac, UINT16_t sesNum)
655{
656 unsigned int ans1 =
657 ((unsigned int) mac[0]) |
658 (((unsigned int) mac[1]) << 8) |
659 (((unsigned int) mac[2]) << 16) |
660 (((unsigned int) mac[3]) << 24);
661 unsigned int ans2 =
662 ((unsigned int) sesNum) |
663 (((unsigned int) mac[4]) << 16) |
664 (((unsigned int) mac[5]) << 24);
665 return ans1 ^ ans2;
666}
667
668/**********************************************************************
669*%FUNCTION: findSession
670*%ARGUMENTS:
671* mac -- an Ethernet address
672* sesNum -- a session number
673*%RETURNS:
674* The session hash for peer address "mac", session number sesNum
675***********************************************************************/
676SessionHash *
677findSession(unsigned char const *mac, UINT16_t sesNum)
678{
679 unsigned int b = hash(mac, sesNum) % HASHTAB_SIZE;
680 SessionHash *sh = Buckets[b];
681 while(sh) {
682 if (!memcmp(mac, sh->peerMac, ETH_ALEN) && sesNum == sh->sesNum) {
683 return sh;
684 }
685 sh = sh->next;
686 }
687 return NULL;
688}
689
690/**********************************************************************
691*%FUNCTION: fatalSys
692*%ARGUMENTS:
693* str -- error message
694*%RETURNS:
695* Nothing
696*%DESCRIPTION:
697* Prints a message plus the errno value to stderr and syslog and exits.
698***********************************************************************/
699void
700fatalSys(char const *str)
701{
702 char buf[1024];
703 sprintf(buf, "%.256s: %.256s", str, strerror(errno));
704 printErr(buf);
705 exit(EXIT_FAILURE);
706}
707
708/**********************************************************************
709*%FUNCTION: sysErr
710*%ARGUMENTS:
711* str -- error message
712*%RETURNS:
713* Nothing
714*%DESCRIPTION:
715* Prints a message plus the errno value to syslog.
716***********************************************************************/
717void
718sysErr(char const *str)
719{
720 char buf[1024];
721 sprintf(buf, "%.256s: %.256s", str, strerror(errno));
722 printErr(buf);
723}
724
725/**********************************************************************
726*%FUNCTION: rp_fatal
727*%ARGUMENTS:
728* str -- error message
729*%RETURNS:
730* Nothing
731*%DESCRIPTION:
732* Prints a message to stderr and syslog and exits.
733***********************************************************************/
734void
735rp_fatal(char const *str)
736{
737 printErr(str);
738 exit(EXIT_FAILURE);
739}
740
741/**********************************************************************
742*%FUNCTION: relayLoop
743*%ARGUMENTS:
744* None
745*%RETURNS:
746* Nothing
747*%DESCRIPTION:
748* Runs the relay loop. This function never returns
749***********************************************************************/
750void
751relayLoop()
752{
753 fd_set readable, readableCopy;
754 int maxFD;
755 int i, r;
756 int sock;
757
758 /* Build the select set */
759 FD_ZERO(&readable);
760 maxFD = 0;
761 for (i=0; i<NumInterfaces; i++) {
762 sock = Interfaces[i].discoverySock;
763 if (sock > maxFD) maxFD = sock;
764 FD_SET(sock, &readable);
765 sock = Interfaces[i].sessionSock;
766 if (sock > maxFD) maxFD = sock;
767 FD_SET(sock, &readable);
768 if (CleanPipe[0] > maxFD) maxFD = CleanPipe[0];
769 FD_SET(CleanPipe[0], &readable);
770 }
771 maxFD++;
772 for(;;) {
773 readableCopy = readable;
774 for(;;) {
775 r = select(maxFD, &readableCopy, NULL, NULL, NULL);
776 if (r >= 0 || errno != EINTR) break;
777 }
778 if (r < 0) {
779 sysErr("select (relayLoop)");
780 continue;
781 }
782
783 /* Handle session packets first */
784 for (i=0; i<NumInterfaces; i++) {
785 if (FD_ISSET(Interfaces[i].sessionSock, &readableCopy)) {
786 relayGotSessionPacket(&Interfaces[i]);
787 }
788 }
789
790 /* Now handle discovery packets */
791 for (i=0; i<NumInterfaces; i++) {
792 if (FD_ISSET(Interfaces[i].discoverySock, &readableCopy)) {
793 relayGotDiscoveryPacket(&Interfaces[i]);
794 }
795 }
796
797 /* Handle the session-cleaning process */
798 if (FD_ISSET(CleanPipe[0], &readableCopy)) {
799 char dummy;
800 CleanCounter = 0;
801 read(CleanPipe[0], &dummy, 1);
802 if (IdleTimeout) cleanSessions();
803 }
804 }
805}
806
807/**********************************************************************
808*%FUNCTION: relayGotDiscoveryPacket
809*%ARGUMENTS:
810* iface -- interface on which packet is waiting
811*%RETURNS:
812* Nothing
813*%DESCRIPTION:
814* Receives and processes a discovery packet.
815***********************************************************************/
816void
817relayGotDiscoveryPacket(PPPoEInterface const *iface)
818{
819 PPPoEPacket packet;
820 int size;
821
822 if (receivePacket(iface->discoverySock, &packet, &size) < 0) {
823 return;
824 }
825 /* Ignore unknown code/version */
826 if (packet.ver != 1 || packet.type != 1) {
827 return;
828 }
829
830 /* Validate length */
831 if (ntohs(packet.length) + HDR_SIZE > (unsigned int)size) {
832 syslog(LOG_ERR, "Bogus PPPoE length field (%u)",
833 (unsigned int) ntohs(packet.length));
834 return;
835 }
836
837 /* Drop Ethernet frame padding */
838 if ((unsigned int)size > ntohs(packet.length) + HDR_SIZE) {
839 size = ntohs(packet.length) + HDR_SIZE;
840 }
841
842 switch(packet.code) {
843 case CODE_PADT:
844 relayHandlePADT(iface, &packet, size);
845 break;
846 case CODE_PADI:
847 relayHandlePADI(iface, &packet, size);
848 break;
849 case CODE_PADO:
850 relayHandlePADO(iface, &packet, size);
851 break;
852 case CODE_PADR:
853 relayHandlePADR(iface, &packet, size);
854 break;
855 case CODE_PADS:
856 relayHandlePADS(iface, &packet, size);
857 break;
858 default:
859 syslog(LOG_ERR, "Discovery packet on %s with unknown code %d",
860 iface->name, (int) packet.code);
861 }
862}
863
864/**********************************************************************
865*%FUNCTION: relayGotSessionPacket
866*%ARGUMENTS:
867* iface -- interface on which packet is waiting
868*%RETURNS:
869* Nothing
870*%DESCRIPTION:
871* Receives and processes a session packet.
872***********************************************************************/
873void
874relayGotSessionPacket(PPPoEInterface const *iface)
875{
876 PPPoEPacket packet;
877 int size;
878 SessionHash *sh;
879 PPPoESession *ses;
880
881 if (receivePacket(iface->sessionSock, &packet, &size) < 0) {
882 return;
883 }
884
885 /* Ignore unknown code/version */
886 if (packet.ver != 1 || packet.type != 1) {
887 return;
888 }
889
890 /* Must be a session packet */
891 if (packet.code != CODE_SESS) {
892 syslog(LOG_ERR, "Session packet with code %d", (int) packet.code);
893 return;
894 }
895
896 /* Ignore session packets whose destination address isn't ours */
897 if (memcmp(packet.ethHdr.h_dest, iface->mac, ETH_ALEN)) {
898 return;
899 }
900
901 /* Validate length */
902 if (ntohs(packet.length) + HDR_SIZE > (unsigned int)size) {
903 syslog(LOG_ERR, "Bogus PPPoE length field (%u)",
904 (unsigned int) ntohs(packet.length));
905 return;
906 }
907
908 /* Drop Ethernet frame padding */
909 if ((unsigned int)size > ntohs(packet.length) + HDR_SIZE) {
910 size = ntohs(packet.length) + HDR_SIZE;
911 }
912
913 /* We're in business! Find the hash */
914 sh = findSession(packet.ethHdr.h_source, packet.session);
915 if (!sh) {
916 /* Don't log this. Someone could be running the client and the
917 relay on the same box. */
918 return;
919 }
920
921 /* Relay it */
922 ses = sh->ses;
923 ses->epoch = Epoch;
924 sh = sh->peer;
925 packet.session = sh->sesNum;
926 memcpy(packet.ethHdr.h_source, sh->interface->mac, ETH_ALEN);
927 memcpy(packet.ethHdr.h_dest, sh->peerMac, ETH_ALEN);
928#if 0
929 fprintf(stderr, "Relaying %02x:%02x:%02x:%02x:%02x:%02x(%s:%d) to %02x:%02x:%02x:%02x:%02x:%02x(%s:%d)\n",
930 sh->peer->peerMac[0], sh->peer->peerMac[1], sh->peer->peerMac[2],
931 sh->peer->peerMac[3], sh->peer->peerMac[4], sh->peer->peerMac[5],
932 sh->peer->interface->name, ntohs(sh->peer->sesNum),
933 sh->peerMac[0], sh->peerMac[1], sh->peerMac[2],
934 sh->peerMac[3], sh->peerMac[4], sh->peerMac[5],
935 sh->interface->name, ntohs(sh->sesNum));
936#endif
937 sendPacket(NULL, sh->interface->sessionSock, &packet, size);
938}
939
940/**********************************************************************
941*%FUNCTION: relayHandlePADT
942*%ARGUMENTS:
943* iface -- interface on which packet was received
944* packet -- the PADT packet
945*%RETURNS:
946* Nothing
947*%DESCRIPTION:
948* Receives and processes a PADT packet.
949***********************************************************************/
950void
951relayHandlePADT(PPPoEInterface const *iface,
952 PPPoEPacket *packet,
953 int size)
954{
955 SessionHash *sh;
956 PPPoESession *ses;
957
958 sh = findSession(packet->ethHdr.h_source, packet->session);
959 if (!sh) {
960 return;
961 }
962 /* Relay the PADT to the peer */
963 sh = sh->peer;
964 ses = sh->ses;
965 packet->session = sh->sesNum;
966 memcpy(packet->ethHdr.h_source, sh->interface->mac, ETH_ALEN);
967 memcpy(packet->ethHdr.h_dest, sh->peerMac, ETH_ALEN);
968 sendPacket(NULL, sh->interface->sessionSock, packet, size);
969
970 /* Destroy the session */
971 freeSession(ses, "Received PADT");
972}
973
974/**********************************************************************
975*%FUNCTION: relayHandlePADI
976*%ARGUMENTS:
977* iface -- interface on which packet was received
978* packet -- the PADI packet
979*%RETURNS:
980* Nothing
981*%DESCRIPTION:
982* Receives and processes a PADI packet.
983***********************************************************************/
984void
985relayHandlePADI(PPPoEInterface const *iface,
986 PPPoEPacket *packet,
987 int size)
988{
989 PPPoETag tag;
990 unsigned char *loc;
991 int i, r;
992
993 int ifIndex;
994
995 /* Can a client legally be behind this interface? */
996 if (!iface->clientOK) {
997 syslog(LOG_ERR,
998 "PADI packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not permitted",
999 packet->ethHdr.h_source[0],
1000 packet->ethHdr.h_source[1],
1001 packet->ethHdr.h_source[2],
1002 packet->ethHdr.h_source[3],
1003 packet->ethHdr.h_source[4],
1004 packet->ethHdr.h_source[5],
1005 iface->name);
1006 return;
1007 }
1008
1009 /* Source address must be unicast */
1010 if (NOT_UNICAST(packet->ethHdr.h_source)) {
1011 syslog(LOG_ERR,
1012 "PADI packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not from a unicast address",
1013 packet->ethHdr.h_source[0],
1014 packet->ethHdr.h_source[1],
1015 packet->ethHdr.h_source[2],
1016 packet->ethHdr.h_source[3],
1017 packet->ethHdr.h_source[4],
1018 packet->ethHdr.h_source[5],
1019 iface->name);
1020 return;
1021 }
1022
1023 /* Destination address must be broadcast */
1024 if (NOT_BROADCAST(packet->ethHdr.h_dest)) {
1025 syslog(LOG_ERR,
1026 "PADI packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not to a broadcast address",
1027 packet->ethHdr.h_source[0],
1028 packet->ethHdr.h_source[1],
1029 packet->ethHdr.h_source[2],
1030 packet->ethHdr.h_source[3],
1031 packet->ethHdr.h_source[4],
1032 packet->ethHdr.h_source[5],
1033 iface->name);
1034 return;
1035 }
1036
1037 /* Get array index of interface */
1038 ifIndex = iface - Interfaces;
1039
1040 loc = findTag(packet, TAG_RELAY_SESSION_ID, &tag);
1041 if (!loc) {
1042 tag.type = htons(TAG_RELAY_SESSION_ID);
1043 tag.length = htons(MY_RELAY_TAG_LEN);
1044 memcpy(tag.payload, &ifIndex, sizeof(ifIndex));
1045 memcpy(tag.payload+sizeof(ifIndex), packet->ethHdr.h_source, ETH_ALEN);
1046 /* Add a relay tag if there's room */
1047 r = addTag(packet, &tag);
1048 if (r < 0) return;
1049 size += r;
1050 } else {
1051 /* We do not re-use relay-id tags. Drop the frame. The RFC says the
1052 relay agent SHOULD return a Generic-Error tag, but this does not
1053 make sense for PADI packets. */
1054 return;
1055 }
1056
1057 /* Broadcast the PADI on all AC-capable interfaces except the interface
1058 on which it came */
1059 for (i=0; i < NumInterfaces; i++) {
1060 if (iface == &Interfaces[i]) continue;
1061 if (!Interfaces[i].acOK) continue;
1062 memcpy(packet->ethHdr.h_source, Interfaces[i].mac, ETH_ALEN);
1063 sendPacket(NULL, Interfaces[i].discoverySock, packet, size);
1064 }
1065
1066}
1067
1068/**********************************************************************
1069*%FUNCTION: relayHandlePADO
1070*%ARGUMENTS:
1071* iface -- interface on which packet was received
1072* packet -- the PADO packet
1073*%RETURNS:
1074* Nothing
1075*%DESCRIPTION:
1076* Receives and processes a PADO packet.
1077***********************************************************************/
1078void
1079relayHandlePADO(PPPoEInterface const *iface,
1080 PPPoEPacket *packet,
1081 int size)
1082{
1083 PPPoETag tag;
1084 unsigned char *loc;
1085 int ifIndex;
1086 int acIndex;
1087
1088 /* Can a server legally be behind this interface? */
1089 if (!iface->acOK) {
1090 syslog(LOG_ERR,
1091 "PADO packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not permitted",
1092 packet->ethHdr.h_source[0],
1093 packet->ethHdr.h_source[1],
1094 packet->ethHdr.h_source[2],
1095 packet->ethHdr.h_source[3],
1096 packet->ethHdr.h_source[4],
1097 packet->ethHdr.h_source[5],
1098 iface->name);
1099 return;
1100 }
1101
1102 acIndex = iface - Interfaces;
1103
1104 /* Source address must be unicast */
1105 if (NOT_UNICAST(packet->ethHdr.h_source)) {
1106 syslog(LOG_ERR,
1107 "PADO packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not from a unicast address",
1108 packet->ethHdr.h_source[0],
1109 packet->ethHdr.h_source[1],
1110 packet->ethHdr.h_source[2],
1111 packet->ethHdr.h_source[3],
1112 packet->ethHdr.h_source[4],
1113 packet->ethHdr.h_source[5],
1114 iface->name);
1115 return;
1116 }
1117
1118 /* Destination address must be interface's MAC address */
1119 if (memcmp(packet->ethHdr.h_dest, iface->mac, ETH_ALEN)) {
1120 return;
1121 }
1122
1123 /* Find relay tag */
1124 loc = findTag(packet, TAG_RELAY_SESSION_ID, &tag);
1125 if (!loc) {
1126 syslog(LOG_ERR,
1127 "PADO packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s does not have Relay-Session-Id tag",
1128 packet->ethHdr.h_source[0],
1129 packet->ethHdr.h_source[1],
1130 packet->ethHdr.h_source[2],
1131 packet->ethHdr.h_source[3],
1132 packet->ethHdr.h_source[4],
1133 packet->ethHdr.h_source[5],
1134 iface->name);
1135 return;
1136 }
1137
1138 /* If it's the wrong length, ignore it */
1139 if (ntohs(tag.length) != MY_RELAY_TAG_LEN) {
1140 syslog(LOG_ERR,
1141 "PADO packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s does not have correct length Relay-Session-Id tag",
1142 packet->ethHdr.h_source[0],
1143 packet->ethHdr.h_source[1],
1144 packet->ethHdr.h_source[2],
1145 packet->ethHdr.h_source[3],
1146 packet->ethHdr.h_source[4],
1147 packet->ethHdr.h_source[5],
1148 iface->name);
1149 return;
1150 }
1151
1152 /* Extract interface index */
1153 memcpy(&ifIndex, tag.payload, sizeof(ifIndex));
1154
1155 if (ifIndex < 0 || ifIndex >= NumInterfaces ||
1156 !Interfaces[ifIndex].clientOK ||
1157 iface == &Interfaces[ifIndex]) {
1158 syslog(LOG_ERR,
1159 "PADO packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s has invalid interface in Relay-Session-Id tag",
1160 packet->ethHdr.h_source[0],
1161 packet->ethHdr.h_source[1],
1162 packet->ethHdr.h_source[2],
1163 packet->ethHdr.h_source[3],
1164 packet->ethHdr.h_source[4],
1165 packet->ethHdr.h_source[5],
1166 iface->name);
1167 return;
1168 }
1169
1170 /* Replace Relay-ID tag with opposite-direction tag */
1171 memcpy(loc+TAG_HDR_SIZE, &acIndex, sizeof(acIndex));
1172 memcpy(loc+TAG_HDR_SIZE+sizeof(ifIndex), packet->ethHdr.h_source, ETH_ALEN);
1173
1174 /* Set destination address to MAC address in relay ID */
1175 memcpy(packet->ethHdr.h_dest, tag.payload + sizeof(ifIndex), ETH_ALEN);
1176
1177 /* Set source address to MAC address of interface */
1178 memcpy(packet->ethHdr.h_source, Interfaces[ifIndex].mac, ETH_ALEN);
1179
1180 /* Send the PADO to the proper client */
1181 sendPacket(NULL, Interfaces[ifIndex].discoverySock, packet, size);
1182}
1183
1184/**********************************************************************
1185*%FUNCTION: relayHandlePADR
1186*%ARGUMENTS:
1187* iface -- interface on which packet was received
1188* packet -- the PADR packet
1189*%RETURNS:
1190* Nothing
1191*%DESCRIPTION:
1192* Receives and processes a PADR packet.
1193***********************************************************************/
1194void
1195relayHandlePADR(PPPoEInterface const *iface,
1196 PPPoEPacket *packet,
1197 int size)
1198{
1199 PPPoETag tag;
1200 unsigned char *loc;
1201 int ifIndex;
1202 int cliIndex;
1203
1204 /* Can a client legally be behind this interface? */
1205 if (!iface->clientOK) {
1206 syslog(LOG_ERR,
1207 "PADR packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not permitted",
1208 packet->ethHdr.h_source[0],
1209 packet->ethHdr.h_source[1],
1210 packet->ethHdr.h_source[2],
1211 packet->ethHdr.h_source[3],
1212 packet->ethHdr.h_source[4],
1213 packet->ethHdr.h_source[5],
1214 iface->name);
1215 return;
1216 }
1217
1218 cliIndex = iface - Interfaces;
1219
1220 /* Source address must be unicast */
1221 if (NOT_UNICAST(packet->ethHdr.h_source)) {
1222 syslog(LOG_ERR,
1223 "PADR packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not from a unicast address",
1224 packet->ethHdr.h_source[0],
1225 packet->ethHdr.h_source[1],
1226 packet->ethHdr.h_source[2],
1227 packet->ethHdr.h_source[3],
1228 packet->ethHdr.h_source[4],
1229 packet->ethHdr.h_source[5],
1230 iface->name);
1231 return;
1232 }
1233
1234 /* Destination address must be interface's MAC address */
1235 if (memcmp(packet->ethHdr.h_dest, iface->mac, ETH_ALEN)) {
1236 return;
1237 }
1238
1239 /* Find relay tag */
1240 loc = findTag(packet, TAG_RELAY_SESSION_ID, &tag);
1241 if (!loc) {
1242 syslog(LOG_ERR,
1243 "PADR packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s does not have Relay-Session-Id tag",
1244 packet->ethHdr.h_source[0],
1245 packet->ethHdr.h_source[1],
1246 packet->ethHdr.h_source[2],
1247 packet->ethHdr.h_source[3],
1248 packet->ethHdr.h_source[4],
1249 packet->ethHdr.h_source[5],
1250 iface->name);
1251 return;
1252 }
1253
1254 /* If it's the wrong length, ignore it */
1255 if (ntohs(tag.length) != MY_RELAY_TAG_LEN) {
1256 syslog(LOG_ERR,
1257 "PADR packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s does not have correct length Relay-Session-Id tag",
1258 packet->ethHdr.h_source[0],
1259 packet->ethHdr.h_source[1],
1260 packet->ethHdr.h_source[2],
1261 packet->ethHdr.h_source[3],
1262 packet->ethHdr.h_source[4],
1263 packet->ethHdr.h_source[5],
1264 iface->name);
1265 return;
1266 }
1267
1268 /* Extract interface index */
1269 memcpy(&ifIndex, tag.payload, sizeof(ifIndex));
1270
1271 if (ifIndex < 0 || ifIndex >= NumInterfaces ||
1272 !Interfaces[ifIndex].acOK ||
1273 iface == &Interfaces[ifIndex]) {
1274 syslog(LOG_ERR,
1275 "PADR packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s has invalid interface in Relay-Session-Id tag",
1276 packet->ethHdr.h_source[0],
1277 packet->ethHdr.h_source[1],
1278 packet->ethHdr.h_source[2],
1279 packet->ethHdr.h_source[3],
1280 packet->ethHdr.h_source[4],
1281 packet->ethHdr.h_source[5],
1282 iface->name);
1283 return;
1284 }
1285
1286 /* Replace Relay-ID tag with opposite-direction tag */
1287 memcpy(loc+TAG_HDR_SIZE, &cliIndex, sizeof(cliIndex));
1288 memcpy(loc+TAG_HDR_SIZE+sizeof(ifIndex), packet->ethHdr.h_source, ETH_ALEN);
1289
1290 /* Set destination address to MAC address in relay ID */
1291 memcpy(packet->ethHdr.h_dest, tag.payload + sizeof(ifIndex), ETH_ALEN);
1292
1293 /* Set source address to MAC address of interface */
1294 memcpy(packet->ethHdr.h_source, Interfaces[ifIndex].mac, ETH_ALEN);
1295
1296 /* Send the PADR to the proper access concentrator */
1297 sendPacket(NULL, Interfaces[ifIndex].discoverySock, packet, size);
1298}
1299
1300/**********************************************************************
1301*%FUNCTION: relayHandlePADS
1302*%ARGUMENTS:
1303* iface -- interface on which packet was received
1304* packet -- the PADS packet
1305*%RETURNS:
1306* Nothing
1307*%DESCRIPTION:
1308* Receives and processes a PADS packet.
1309***********************************************************************/
1310void
1311relayHandlePADS(PPPoEInterface const *iface,
1312 PPPoEPacket *packet,
1313 int size)
1314{
1315 PPPoETag tag;
1316 unsigned char *loc;
1317 int ifIndex;
1318 int acIndex;
1319 PPPoESession *ses = NULL;
1320 SessionHash *sh;
1321
1322 /* Can a server legally be behind this interface? */
1323 if (!iface->acOK) {
1324 syslog(LOG_ERR,
1325 "PADS packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not permitted",
1326 packet->ethHdr.h_source[0],
1327 packet->ethHdr.h_source[1],
1328 packet->ethHdr.h_source[2],
1329 packet->ethHdr.h_source[3],
1330 packet->ethHdr.h_source[4],
1331 packet->ethHdr.h_source[5],
1332 iface->name);
1333 return;
1334 }
1335
1336 acIndex = iface - Interfaces;
1337
1338 /* Source address must be unicast */
1339 if (NOT_UNICAST(packet->ethHdr.h_source)) {
1340 syslog(LOG_ERR,
1341 "PADS packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s not from a unicast address",
1342 packet->ethHdr.h_source[0],
1343 packet->ethHdr.h_source[1],
1344 packet->ethHdr.h_source[2],
1345 packet->ethHdr.h_source[3],
1346 packet->ethHdr.h_source[4],
1347 packet->ethHdr.h_source[5],
1348 iface->name);
1349 return;
1350 }
1351
1352 /* Destination address must be interface's MAC address */
1353 if (memcmp(packet->ethHdr.h_dest, iface->mac, ETH_ALEN)) {
1354 return;
1355 }
1356
1357 /* Find relay tag */
1358 loc = findTag(packet, TAG_RELAY_SESSION_ID, &tag);
1359 if (!loc) {
1360 syslog(LOG_ERR,
1361 "PADS packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s does not have Relay-Session-Id tag",
1362 packet->ethHdr.h_source[0],
1363 packet->ethHdr.h_source[1],
1364 packet->ethHdr.h_source[2],
1365 packet->ethHdr.h_source[3],
1366 packet->ethHdr.h_source[4],
1367 packet->ethHdr.h_source[5],
1368 iface->name);
1369 return;
1370 }
1371
1372 /* If it's the wrong length, ignore it */
1373 if (ntohs(tag.length) != MY_RELAY_TAG_LEN) {
1374 syslog(LOG_ERR,
1375 "PADS packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s does not have correct length Relay-Session-Id tag",
1376 packet->ethHdr.h_source[0],
1377 packet->ethHdr.h_source[1],
1378 packet->ethHdr.h_source[2],
1379 packet->ethHdr.h_source[3],
1380 packet->ethHdr.h_source[4],
1381 packet->ethHdr.h_source[5],
1382 iface->name);
1383 return;
1384 }
1385
1386 /* Extract interface index */
1387 memcpy(&ifIndex, tag.payload, sizeof(ifIndex));
1388
1389 if (ifIndex < 0 || ifIndex >= NumInterfaces ||
1390 !Interfaces[ifIndex].clientOK ||
1391 iface == &Interfaces[ifIndex]) {
1392 syslog(LOG_ERR,
1393 "PADS packet from %02x:%02x:%02x:%02x:%02x:%02x on interface %s has invalid interface in Relay-Session-Id tag",
1394 packet->ethHdr.h_source[0],
1395 packet->ethHdr.h_source[1],
1396 packet->ethHdr.h_source[2],
1397 packet->ethHdr.h_source[3],
1398 packet->ethHdr.h_source[4],
1399 packet->ethHdr.h_source[5],
1400 iface->name);
1401 return;
1402 }
1403
1404 /* If session ID is zero, it's the AC respoding with an error.
1405 Just relay it; do not create a session */
1406 if (packet->session != htons(0)) {
1407 /* Check for existing session */
1408 sh = findSession(packet->ethHdr.h_source, packet->session);
1409 if (sh) ses = sh->ses;
1410
1411 /* If already an existing session, assume it's a duplicate PADS. Send
1412 the frame, but do not create a new session. Is this the right
1413 thing to do? Arguably, should send an error to the client and
1414 a PADT to the server, because this could happen due to a
1415 server crash and reboot. */
1416
1417 if (!ses) {
1418 /* Create a new session */
1419 ses = createSession(iface, &Interfaces[ifIndex],
1420 packet->ethHdr.h_source,
1421 loc + TAG_HDR_SIZE + sizeof(ifIndex), packet->session);
1422 if (!ses) {
1423 /* Can't allocate session -- send error PADS to client and
1424 PADT to server */
1425 PPPoETag hostUniq, *hu;
1426 if (findTag(packet, TAG_HOST_UNIQ, &hostUniq)) {
1427 hu = &hostUniq;
1428 } else {
1429 hu = NULL;
1430 }
1431 relaySendError(CODE_PADS, htons(0), &Interfaces[ifIndex],
1432 loc + TAG_HDR_SIZE + sizeof(ifIndex),
1433 hu, "RP-PPPoE: Relay: Unable to allocate session");
1434 relaySendError(CODE_PADT, packet->session, iface,
1435 packet->ethHdr.h_source, NULL,
1436 "RP-PPPoE: Relay: Unable to allocate session");
1437 return;
1438 }
1439 }
1440 /* Replace session number */
1441 packet->session = ses->sesNum;
1442 }
1443
1444 /* Remove relay-ID tag */
1445 removeBytes(packet, loc, MY_RELAY_TAG_LEN + TAG_HDR_SIZE);
1446 size -= (MY_RELAY_TAG_LEN + TAG_HDR_SIZE);
1447
1448 /* Set destination address to MAC address in relay ID */
1449 memcpy(packet->ethHdr.h_dest, tag.payload + sizeof(ifIndex), ETH_ALEN);
1450
1451 /* Set source address to MAC address of interface */
1452 memcpy(packet->ethHdr.h_source, Interfaces[ifIndex].mac, ETH_ALEN);
1453
1454 /* Send the PADS to the proper client */
1455 sendPacket(NULL, Interfaces[ifIndex].discoverySock, packet, size);
1456}
1457
1458/**********************************************************************
1459*%FUNCTION: relaySendError
1460*%ARGUMENTS:
1461* code -- PPPoE packet code (PADS or PADT, typically)
1462* session -- PPPoE session number
1463* iface -- interface on which to send frame
1464* mac -- Ethernet address to which frame should be sent
1465* hostUniq -- if non-NULL, a hostUniq tag to add to error frame
1466* errMsg -- error message to insert into Generic-Error tag.
1467*%RETURNS:
1468* Nothing
1469*%DESCRIPTION:
1470* Sends either a PADS or PADT packet with a Generic-Error tag and an
1471* error message.
1472***********************************************************************/
1473void
1474relaySendError(unsigned char code,
1475 UINT16_t session,
1476 PPPoEInterface const *iface,
1477 unsigned char const *mac,
1478 PPPoETag const *hostUniq,
1479 char const *errMsg)
1480{
1481 PPPoEPacket packet;
1482 PPPoETag errTag;
1483 int size;
1484
1485 memcpy(packet.ethHdr.h_source, iface->mac, ETH_ALEN);
1486 memcpy(packet.ethHdr.h_dest, mac, ETH_ALEN);
1487 packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
1488 packet.type = 1;
1489 packet.ver = 1;
1490 packet.code = code;
1491 packet.session = session;
1492 packet.length = htons(0);
1493 if (hostUniq) {
1494 if (addTag(&packet, hostUniq) < 0) return;
1495 }
1496 errTag.type = htons(TAG_GENERIC_ERROR);
1497 errTag.length = htons(strlen(errMsg));
1498 strcpy((char *) errTag.payload, errMsg);
1499 if (addTag(&packet, &errTag) < 0) return;
1500 size = ntohs(packet.length) + HDR_SIZE;
1501 if (code == CODE_PADT) {
1502 sendPacket(NULL, iface->discoverySock, &packet, size);
1503 } else {
1504 sendPacket(NULL, iface->sessionSock, &packet, size);
1505 }
1506}
1507
1508/**********************************************************************
1509*%FUNCTION: alarmHandler
1510*%ARGUMENTS:
1511* sig -- signal number
1512*%RETURNS:
1513* Nothing
1514*%DESCRIPTION:
1515* SIGALRM handler. Increments Epoch; if necessary, writes a byte of
1516* data to the alarm pipe to trigger the stale-session cleaner.
1517***********************************************************************/
1518void
1519alarmHandler(int sig)
1520{
1521 alarm(1);
1522 Epoch++;
1523 CleanCounter++;
1524 if (CleanCounter == CleanPeriod) {
1525 write(CleanPipe[1], "", 1);
1526 }
1527}
1528
1529/**********************************************************************
1530*%FUNCTION: cleanSessions
1531*%ARGUMENTS:
1532* None
1533*%RETURNS:
1534* Nothing
1535*%DESCRIPTION:
1536* Goes through active sessions and cleans sessions idle for longer
1537* than IdleTimeout seconds.
1538***********************************************************************/
1539void cleanSessions(void)
1540{
1541 PPPoESession *cur, *next;
1542 cur = ActiveSessions;
1543 while(cur) {
1544 next = cur->next;
1545 if (Epoch - cur->epoch > IdleTimeout) {
1546 /* Send PADT to each peer */
1547 relaySendError(CODE_PADT, cur->acHash->sesNum,
1548 cur->acHash->interface,
1549 cur->acHash->peerMac, NULL,
1550 "RP-PPPoE: Relay: Session exceeded idle timeout");
1551 relaySendError(CODE_PADT, cur->clientHash->sesNum,
1552 cur->clientHash->interface,
1553 cur->clientHash->peerMac, NULL,
1554 "RP-PPPoE: Relay: Session exceeded idle timeout");
1555 freeSession(cur, "Idle Timeout");
1556 }
1557 cur = next;
1558 }
1559}
1560