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