1/***********************************************************************
2*
3* pppoe.h
4*
5* Implementation of a user-space PPPoE server
6*
7* Copyright (C) 2000 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* $Id: pppoe-server.c,v 1.1.1.1 2008/10/15 03:31:00 james26_jang Exp $
13*
14***********************************************************************/
15
16static char const RCSID[] =
17"$Id: pppoe-server.c,v 1.1.1.1 2008/10/15 03:31:00 james26_jang Exp $";
18
19#include "config.h"
20
21#if defined(HAVE_NETPACKET_PACKET_H) || defined(HAVE_LINUX_IF_PACKET_H)
22#define _POSIX_SOURCE 1 /* For sigaction defines */
23#endif
24
25#define _BSD_SOURCE 1 /* for gethostname */
26
27#include "pppoe.h"
28#include "md5.h"
29
30#ifdef HAVE_SYSLOG_H
31#include <syslog.h>
32#endif
33
34#include <errno.h>
35#include <string.h>
36#include <stdlib.h>
37
38#ifdef HAVE_UNISTD_H
39#include <unistd.h>
40#endif
41
42#ifdef HAVE_GETOPT_H
43#include <getopt.h>
44#endif
45
46#ifdef HAVE_SYS_WAIT_H
47#include <sys/wait.h>
48#endif
49
50#include <signal.h>
51
52/* Hack for daemonizing */
53#define CLOSEFD 64
54
55/* Max. 64 sessions by default */
56#define DEFAULT_MAX_SESSIONS 64
57
58/* A list of client sessions */
59struct ClientSession *Sessions = NULL;
60
61/* The number of session slots */
62size_t NumSessionSlots;
63
64/* Socket for client's discovery phases */
65int Socket = -1;
66
67/* Pipe written on reception of SIGCHLD */
68int Pipe[2] = {-1, -1};
69int ReapPending = 0;
70
71/* Synchronous mode */
72int Synchronous = 0;
73
74/* Random seed for cookie generation */
75#define SEED_LEN 16
76unsigned char CookieSeed[SEED_LEN];
77
78/* Default interface if no -I option given */
79#define DEFAULT_IF "eth0"
80char *IfName = NULL;
81
82/* Access concentrator name */
83char *ACName = NULL;
84
85/* Options to pass to pppoe process */
86char PppoeOptions[SMALLBUF] = "";
87
88/* Our local IP address */
89unsigned char LocalIP[IPV4ALEN] = {10, 0, 0, 1};
90unsigned char RemoteIP[IPV4ALEN] = {10, 67, 15, 1}; /* Counter STARTS here */
91
92struct PPPoETag hostUniq;
93struct PPPoETag relayId;
94struct PPPoETag receivedCookie;
95struct PPPoETag requestedService;
96
97#define HOSTNAMELEN 256
98
99static void startPPPD(struct ClientSession *sess);
100static void sendErrorPADS(int sock, unsigned char *source, unsigned char *dest,
101			  int errorTag, char *errorMsg);
102
103#define CHECK_ROOM(cursor, start, len) \
104do {\
105    if (((cursor)-(start))+(len) > MAX_PPPOE_PAYLOAD) { \
106        syslog(LOG_ERR, "Would create too-long packet"); \
107        return; \
108    } \
109} while(0)
110
111/**********************************************************************
112*%FUNCTION: parsePADITags
113*%ARGUMENTS:
114* type -- tag type
115* len -- tag length
116* data -- tag data
117* extra -- extra user data.
118*%RETURNS:
119* Nothing
120*%DESCRIPTION:
121* Picks interesting tags out of a PADI packet
122***********************************************************************/
123void
124parsePADITags(UINT16_t type, UINT16_t len, unsigned char *data,
125	      void *extra)
126{
127    switch(type) {
128    case TAG_SERVICE_NAME:
129	/* Should do something -- currently ignored */
130	break;
131    case TAG_RELAY_SESSION_ID:
132	relayId.type = htons(type);
133	relayId.length = htons(len);
134	memcpy(relayId.payload, data, len);
135	break;
136    case TAG_HOST_UNIQ:
137	hostUniq.type = htons(type);
138	hostUniq.length = htons(len);
139	memcpy(hostUniq.payload, data, len);
140	break;
141    }
142}
143
144/**********************************************************************
145*%FUNCTION: parsePADRTags
146*%ARGUMENTS:
147* type -- tag type
148* len -- tag length
149* data -- tag data
150* extra -- extra user data.
151*%RETURNS:
152* Nothing
153*%DESCRIPTION:
154* Picks interesting tags out of a PADR packet
155***********************************************************************/
156void
157parsePADRTags(UINT16_t type, UINT16_t len, unsigned char *data,
158	      void *extra)
159{
160    switch(type) {
161    case TAG_RELAY_SESSION_ID:
162	relayId.type = htons(type);
163	relayId.length = htons(len);
164	memcpy(relayId.payload, data, len);
165	break;
166    case TAG_HOST_UNIQ:
167	hostUniq.type = htons(type);
168	hostUniq.length = htons(len);
169	memcpy(hostUniq.payload, data, len);
170	break;
171    case TAG_AC_COOKIE:
172	receivedCookie.type = htons(type);
173	receivedCookie.length = htons(len);
174	memcpy(receivedCookie.payload, data, len);
175	break;
176    case TAG_SERVICE_NAME:
177	requestedService.type = htons(type);
178	requestedService.length = htons(len);
179	memcpy(requestedService.payload, data, len);
180	break;
181    }
182}
183
184/**********************************************************************
185*%FUNCTION: findSession
186*%ARGUMENTS:
187* pid -- PID of child which owns session.  If PID is 0, searches for
188* empty session slots.
189*%RETURNS:
190* A pointer to the session, or NULL if no such session found.
191*%DESCRIPTION:
192* Searches for specified session.
193**********************************************************************/
194struct ClientSession *
195findSession(pid_t pid)
196{
197    size_t i;
198    for (i=0; i<NumSessionSlots; i++) {
199	if (Sessions[i].pid == pid) {
200	    return &Sessions[i];
201	}
202    }
203    return NULL;
204}
205
206/**********************************************************************
207*%FUNCTION: reapSessions
208*%ARGUMENTS:
209* None
210*%RETURNS:
211* Nothing
212*%DESCRIPTION:
213* Reaps children which have exited and removes their sessions
214**********************************************************************/
215void
216reapSessions(void)
217{
218    int status;
219    pid_t pid;
220    struct ClientSession *session;
221
222    /* Clear pipe */
223    char buf[SMALLBUF];
224    read(Pipe[0], buf, SMALLBUF);
225
226    while((pid = waitpid(-1, &status, WNOHANG)) > 0) {
227	session = findSession(pid);
228	if (!session) {
229	    syslog(LOG_ERR, "Child %d died but couldn't find session!",
230		   (int) pid);
231	} else {
232	    syslog(LOG_INFO,
233		   "Session %d closed for client %02x:%02x:%02x:%02x:%02x:%02x (%d.%d.%d.%d)",
234		   session->sess,
235		   session->eth[0], session->eth[1], session->eth[2],
236		   session->eth[3], session->eth[4], session->eth[5],
237		   (int) session->ip[0], (int) session->ip[1],
238		   (int) session->ip[2], (int) session->ip[3]);
239	    session->pid = 0;
240	}
241    }
242}
243
244/**********************************************************************
245*%FUNCTION: fatalSys
246*%ARGUMENTS:
247* str -- error message
248*%RETURNS:
249* Nothing
250*%DESCRIPTION:
251* Prints a message plus the errno value to stderr and syslog and exits.
252***********************************************************************/
253void
254fatalSys(char const *str)
255{
256    char buf[SMALLBUF];
257    snprintf(buf, SMALLBUF, "%s: %s", str, strerror(errno));
258    printErr(buf);
259    exit(1);
260}
261
262/**********************************************************************
263*%FUNCTION: fatal
264*%ARGUMENTS:
265* str -- error message
266*%RETURNS:
267* Nothing
268*%DESCRIPTION:
269* Prints a message to stderr and syslog and exits.
270***********************************************************************/
271void
272fatal(char const *str)
273{
274    printErr(str);
275    exit(1);
276}
277
278/**********************************************************************
279*%FUNCTION: genCookie
280*%ARGUMENTS:
281* peerEthAddr -- peer Ethernet address (6 bytes)
282* myEthAddr -- my Ethernet address (6 bytes)
283* seed -- random cookie seed to make things tasty (16 bytes)
284* cookie -- 16-byte buffer which is filled with md5 sum of previous items
285*%RETURNS:
286* Nothing
287*%DESCRIPTION:
288* Forms the md5 sum of peer MAC address, our MAC address and seed, useful
289* in a PPPoE Cookie tag.
290***********************************************************************/
291void
292genCookie(unsigned char const *peerEthAddr,
293	  unsigned char const *myEthAddr,
294	  unsigned char const *seed,
295	  unsigned char *cookie)
296{
297    struct MD5Context ctx;
298
299    MD5Init(&ctx);
300    MD5Update(&ctx, peerEthAddr, ETH_ALEN);
301    MD5Update(&ctx, myEthAddr, ETH_ALEN);
302    MD5Update(&ctx, seed, SEED_LEN);
303    MD5Final(cookie, &ctx);
304}
305
306/**********************************************************************
307*%FUNCTION: processPADI
308*%ARGUMENTS:
309* sock -- Ethernet socket
310* myAddr -- my Ethernet address
311* packet -- PPPoE PADI packet
312* len -- length of received packet
313*%RETURNS:
314* Nothing
315*%DESCRIPTION:
316* Sends a PADO packet back to client
317***********************************************************************/
318void
319processPADI(int sock, unsigned char *myAddr,
320	    struct PPPoEPacket *packet, int len)
321{
322    struct PPPoEPacket pado;
323    struct PPPoETag acname;
324    struct PPPoETag servname;
325    struct PPPoETag cookie;
326    size_t acname_len;
327
328    unsigned char *cursor = pado.payload;
329    UINT16_t plen;
330
331    acname.type = htons(TAG_AC_NAME);
332    acname_len = strlen(ACName);
333    acname.length = htons(acname_len);
334    memcpy(acname.payload, ACName, acname_len);
335
336    servname.type = htons(TAG_SERVICE_NAME);
337    servname.length = 0;
338
339    relayId.type = 0;
340    hostUniq.type = 0;
341    parsePacket(packet, parsePADITags, NULL);
342
343    /* Generate a cookie */
344    cookie.type = htons(TAG_AC_COOKIE);
345    cookie.length = htons(16);		/* MD5 output is 16 bytes */
346    genCookie(packet->ethHdr.h_source, myAddr, CookieSeed, cookie.payload);
347
348    /* Construct a PADO packet */
349    memcpy(pado.ethHdr.h_dest, packet->ethHdr.h_source, ETH_ALEN);
350    memcpy(pado.ethHdr.h_source, myAddr, ETH_ALEN);
351    pado.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
352    pado.ver = 1;
353    pado.type = 1;
354    pado.code = CODE_PADO;
355    pado.session = 0;
356    plen = TAG_HDR_SIZE + acname_len;
357
358    CHECK_ROOM(cursor, pado.payload, acname_len+TAG_HDR_SIZE);
359    memcpy(cursor, &acname, acname_len + TAG_HDR_SIZE);
360    cursor += acname_len + TAG_HDR_SIZE;
361
362    CHECK_ROOM(cursor, pado.payload, TAG_HDR_SIZE);
363    memcpy(cursor, &servname, TAG_HDR_SIZE);
364    cursor += TAG_HDR_SIZE;
365    plen += TAG_HDR_SIZE;
366
367    CHECK_ROOM(cursor, pado.payload, TAG_HDR_SIZE + 16);
368    memcpy(cursor, &cookie, TAG_HDR_SIZE + 16);
369    cursor += TAG_HDR_SIZE + 16;
370    plen += TAG_HDR_SIZE + 16;
371
372    if (relayId.type) {
373	CHECK_ROOM(cursor, pado.payload, ntohs(relayId.length) + TAG_HDR_SIZE);
374	memcpy(cursor, &relayId, ntohs(relayId.length) + TAG_HDR_SIZE);
375	cursor += ntohs(relayId.length) + TAG_HDR_SIZE;
376	plen += ntohs(relayId.length) + TAG_HDR_SIZE;
377    }
378    if (hostUniq.type) {
379	CHECK_ROOM(cursor, pado.payload, ntohs(hostUniq.length)+TAG_HDR_SIZE);
380	memcpy(cursor, &hostUniq, ntohs(hostUniq.length) + TAG_HDR_SIZE);
381	cursor += ntohs(hostUniq.length) + TAG_HDR_SIZE;
382	plen += ntohs(hostUniq.length) + TAG_HDR_SIZE;
383    }
384    pado.length = htons(plen);
385    sendPacket(sock, &pado, (int) (plen + HDR_SIZE));
386}
387
388/**********************************************************************
389*%FUNCTION: processPADR
390*%ARGUMENTS:
391* sock -- Ethernet socket
392* myAddr -- my Ethernet address
393* packet -- PPPoE PADR packet
394* len -- length of received packet
395*%RETURNS:
396* Nothing
397*%DESCRIPTION:
398* Sends a PADS packet back to client and starts a PPP session if PADR
399* packet is OK.
400***********************************************************************/
401void
402processPADR(int sock, unsigned char *myAddr,
403	    struct PPPoEPacket *packet, int len)
404{
405    unsigned char cookieBuffer[16];
406    struct ClientSession *cliSession;
407    pid_t child;
408    struct PPPoEPacket pads;
409    unsigned char *cursor = pads.payload;
410    UINT16_t plen;
411    struct PPPoETag servname;
412
413    /* Initialize some globals */
414    relayId.type = 0;
415    hostUniq.type = 0;
416    receivedCookie.type = 0;
417    requestedService.type = 0;
418
419    parsePacket(packet, parsePADRTags, NULL);
420
421    /* Check that everything's cool */
422    if (!receivedCookie.type) {
423	syslog(LOG_ERR, "Received PADR packet without cookie tag");
424	sendErrorPADS(sock, myAddr, packet->ethHdr.h_source,
425		      TAG_GENERIC_ERROR, "No cookie.  Me hungry!");
426	return;
427    }
428
429    /* Is cookie kosher? */
430    if (receivedCookie.length != htons(16)) {
431	syslog(LOG_ERR, "Received PADR packet with invalid cookie tag length");
432	sendErrorPADS(sock, myAddr, packet->ethHdr.h_source,
433		      TAG_GENERIC_ERROR, "Cookie wrong size.");
434	return;
435    }
436
437    genCookie(packet->ethHdr.h_source, myAddr, CookieSeed, cookieBuffer);
438    if (memcmp(receivedCookie.payload, cookieBuffer, 16)) {
439	syslog(LOG_ERR, "Received PADR packet with invalid cookie tag");
440	sendErrorPADS(sock, myAddr, packet->ethHdr.h_source,
441		      TAG_GENERIC_ERROR, "Bad cookie.  Me have tummy-ache.");
442	return;
443    }
444
445    /* Check service name -- we only offer service "" */
446    if (!requestedService.type) {
447	syslog(LOG_ERR, "Received PADR packet with no SERVICE_NAME tag");
448	sendErrorPADS(sock, myAddr, packet->ethHdr.h_source,
449		      TAG_SERVICE_NAME_ERROR, "No service name tag");
450	return;
451    }
452
453    if (requestedService.length) {
454	syslog(LOG_ERR, "Received PADR packet asking for unsupported service %.*s", (int) ntohs(requestedService.length), requestedService.payload);
455	sendErrorPADS(sock, myAddr, packet->ethHdr.h_source,
456		      TAG_SERVICE_NAME_ERROR, "Invalid service name tag");
457	return;
458    }
459
460    /* Looks cool... find a slot for the session */
461    cliSession = findSession(0);
462    if (!cliSession) {
463	syslog(LOG_ERR, "No client slots available (%02x:%02x:%02x:%02x:%02x:%02x)",
464	       (unsigned int) packet->ethHdr.h_source[0],
465	       (unsigned int) packet->ethHdr.h_source[1],
466	       (unsigned int) packet->ethHdr.h_source[2],
467	       (unsigned int) packet->ethHdr.h_source[3],
468	       (unsigned int) packet->ethHdr.h_source[4],
469	       (unsigned int) packet->ethHdr.h_source[5]);
470	sendErrorPADS(sock, myAddr, packet->ethHdr.h_source,
471		      TAG_AC_SYSTEM_ERROR, "No client slots available");
472	return;
473    }
474
475    /* Set up client session peer Ethernet address */
476    memcpy(cliSession->eth, packet->ethHdr.h_source, ETH_ALEN);
477
478    /* Create child process, send PADS packet back */
479    child = fork();
480    if (child < 0) {
481	sendErrorPADS(sock, myAddr, packet->ethHdr.h_source,
482		      TAG_AC_SYSTEM_ERROR, "Unable to start session process");
483	return;
484    }
485    if (child != 0) {
486	/* In the parent process.  Mark pid in session slot */
487	cliSession->pid = child;
488	return;
489    }
490
491    /* In the child process.  */
492
493    /* pppd has a nasty habit of killing all processes in its process group.
494       Start a new session to stop pppd from killing us! */
495    setsid();
496
497    /* Send PADS and Start pppd */
498    memcpy(pads.ethHdr.h_dest, packet->ethHdr.h_source, ETH_ALEN);
499    memcpy(pads.ethHdr.h_source, myAddr, ETH_ALEN);
500    pads.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
501    pads.ver = 1;
502    pads.type = 1;
503    pads.code = CODE_PADS;
504
505    pads.session = htons(cliSession->sess);
506    plen = 0;
507
508    servname.type = htons(TAG_SERVICE_NAME);
509    servname.length = 0;
510
511    memcpy(cursor, &servname, TAG_HDR_SIZE);
512    cursor += TAG_HDR_SIZE;
513    plen += TAG_HDR_SIZE;
514
515    if (relayId.type) {
516	memcpy(cursor, &relayId, ntohs(relayId.length) + TAG_HDR_SIZE);
517	cursor += ntohs(relayId.length) + TAG_HDR_SIZE;
518	plen += ntohs(relayId.length) + TAG_HDR_SIZE;
519    }
520    if (hostUniq.type) {
521	memcpy(cursor, &hostUniq, ntohs(hostUniq.length) + TAG_HDR_SIZE);
522	cursor += ntohs(hostUniq.length) + TAG_HDR_SIZE;
523	plen += ntohs(hostUniq.length) + TAG_HDR_SIZE;
524    }
525    pads.length = htons(plen);
526    sendPacket(sock, &pads, (int) (plen + HDR_SIZE));
527    startPPPD(cliSession);
528}
529
530/**********************************************************************
531*%FUNCTION: childHandler
532*%ARGUMENTS:
533* sig -- signal number
534*%RETURNS:
535* Nothing
536*%DESCRIPTION:
537* Called by SIGCHLD.  Writes one byte to Pipe to wake up the select
538* loop and cause reaping of dead sessions
539***********************************************************************/
540void
541childHandler(int sig)
542{
543    if (!ReapPending) {
544	ReapPending = 1;
545	write(Pipe[1], &ReapPending, 1);
546    }
547}
548
549/**********************************************************************
550*%FUNCTION: usage
551*%ARGUMENTS:
552* argv0 -- argv[0] from main
553*%RETURNS:
554* Nothing
555*%DESCRIPTION:
556* Prints usage instructions
557***********************************************************************/
558void
559usage(char const *argv0)
560{
561    fprintf(stderr, "Usage: %s [options]\n", argv0);
562    fprintf(stderr, "Options:\n");
563#ifdef USE_BPF
564    fprintf(stderr, "   -I if_name     -- Specify interface (REQUIRED)\n");
565#else
566    fprintf(stderr, "   -I if_name     -- Specify interface (default %s.)\n",
567	    DEFAULT_IF);
568#endif
569    fprintf(stderr, "   -T timeout     -- Specify inactivity timeout in seconds.\n");
570    fprintf(stderr, "   -C name        -- Set access concentrator name.\n");
571    fprintf(stderr, "   -m MSS         -- Clamp incoming and outgoing MSS options.\n");
572    fprintf(stderr, "   -L ip          -- Set local IP address.\n");
573    fprintf(stderr, "   -R ip          -- Set start address of remote IP pool.\n");
574    fprintf(stderr, "   -N num         -- Allow 'num' concurrent sessions.\n");
575    fprintf(stderr, "   -f disc:sess   -- Set Ethernet frame types (hex).\n");
576    fprintf(stderr, "   -s             -- Use synchronous PPP mode.\n");
577    fprintf(stderr, "   -h             -- Print usage information.\n\n");
578    fprintf(stderr, "PPPoE-Server Version %s, Copyright (C) 2000 Roaring Penguin Software Inc.\n", VERSION);
579    fprintf(stderr, "PPPoE-Server comes with ABSOLUTELY NO WARRANTY.\n");
580    fprintf(stderr, "This is free software, and you are welcome to redistribute it\n");
581    fprintf(stderr, "under the terms of the GNU General Public License, version 2\n");
582    fprintf(stderr, "or (at your option) any later version.\n");
583    fprintf(stderr, "http://www.roaringpenguin.com\n");
584}
585
586/**********************************************************************
587*%FUNCTION: main
588*%ARGUMENTS:
589* argc, argv -- usual suspects
590*%RETURNS:
591* Exit status
592*%DESCRIPTION:
593* Main program of PPPoE server
594***********************************************************************/
595int
596main(int argc, char **argv)
597{
598
599    FILE *fp;
600    int i;
601    int opt;
602    unsigned char myAddr[ETH_ALEN];
603    struct PPPoEPacket packet;
604    int len;
605    int sock;
606    int d[IPV4ALEN];
607    int beDaemon = 1;
608    struct sigaction act;
609    int maxFD;
610    unsigned int discoveryType, sessionType;
611
612    /* Initialize syslog */
613    openlog("pppoe-server", LOG_PID, LOG_DAEMON);
614
615    /* Default number of session slots */
616    NumSessionSlots = DEFAULT_MAX_SESSIONS;
617
618    /* Parse command-line options */
619    while((opt = getopt(argc, argv, "hI:C:L:R:T:m:FN:f:s")) != -1) {
620	switch(opt) {
621	case 's':
622	    Synchronous = 1;
623	    /* Pass the Synchronous option on to pppoe */
624	    snprintf(PppoeOptions + strlen(PppoeOptions),
625		     SMALLBUF-strlen(PppoeOptions),
626		     " -s");
627	    break;
628	case 'f':
629	    if (sscanf(optarg, "%x:%x", &discoveryType, &sessionType) != 2) {
630		fprintf(stderr, "Illegal argument to -f: Should be disc:sess in hex\n");
631		exit(1);
632	    }
633	    Eth_PPPOE_Discovery = (UINT16_t) discoveryType;
634	    Eth_PPPOE_Session   = (UINT16_t) sessionType;
635	    /* This option gets passed to pppoe */
636	    snprintf(PppoeOptions + strlen(PppoeOptions),
637		     SMALLBUF-strlen(PppoeOptions),
638		     " -%c %s", opt, optarg);
639	    break;
640	case 'F':
641	    beDaemon = 0;
642	    break;
643	case 'N':
644	    if (sscanf(optarg, "%d", &opt) != 1) {
645		usage(argv[0]);
646		exit(1);
647	    }
648	    if (opt <= 0) {
649		fprintf(stderr, "-N: Value must be positive\n");
650		exit(1);
651	    }
652	    NumSessionSlots = opt;
653	    break;
654
655	case 'I':
656	    SET_STRING(IfName, optarg);
657	    break;
658	case 'C':
659	    SET_STRING(ACName, optarg);
660	    break;
661	case 'L':
662	case 'R':
663	    /* Get local/remote IP address */
664	    if (sscanf(optarg, "%d.%d.%d.%d", &d[0], &d[1], &d[2], &d[3]) != 4) {
665		usage(argv[0]);
666		exit(1);
667	    }
668	    for (i=0; i<IPV4ALEN; i++) {
669		if (d[i] < 0 || d[i] > 255) {
670		    usage(argv[0]);
671		    exit(1);
672		}
673		if (opt == 'L') {
674		    LocalIP[i] = (unsigned char) d[i];
675		} else {
676		    RemoteIP[i] = (unsigned char) d[i];
677		}
678	    }
679	    break;
680	case 'T':
681	case 'm':
682	    /* These just get passed to pppoe */
683	    snprintf(PppoeOptions + strlen(PppoeOptions),
684		     SMALLBUF-strlen(PppoeOptions),
685		     " -%c %s", opt, optarg);
686	    break;
687	case 'h':
688	    usage(argv[0]);
689	    exit(0);
690	}
691    }
692
693    if (!IfName) {
694	IfName = DEFAULT_IF;
695    }
696
697    if (!ACName) {
698	ACName = malloc(HOSTNAMELEN);
699	if (gethostname(ACName, HOSTNAMELEN) < 0) {
700	    fatalSys("gethostname");
701	}
702    }
703
704    /* Allocate memory for sessions */
705    Sessions = calloc(NumSessionSlots, sizeof(struct ClientSession));
706    if (!Sessions) {
707	fatal("Cannot allocate memory for session slots");
708    }
709
710    /* Daemonize -- UNIX Network Programming, Vol. 1, Stevens */
711    if (beDaemon) {
712	i = fork();
713	if (i < 0) {
714	    fatalSys("fork");
715	} else if (i != 0) {
716	    /* parent */
717	    exit(0);
718	}
719	setsid();
720	signal(SIGHUP, SIG_IGN);
721	i = fork();
722	if (i < 0) {
723	    fatalSys("fork");
724	} else if (i != 0) {
725	    exit(0);
726	}
727
728	chdir("/");
729	closelog();
730	for (i=0; i<CLOSEFD; i++) close(i);
731	/* We nuked our syslog descriptor... */
732	openlog("pppoe-server", LOG_PID, LOG_DAEMON);
733    }
734
735    for(i=0; i<NumSessionSlots; i++) {
736	Sessions[i].pid = 0;
737	memcpy(Sessions[i].ip, RemoteIP, sizeof(RemoteIP));
738	Sessions[i].sess = i+1;
739
740	/* Increment IP */
741	RemoteIP[3]++;
742	if (!RemoteIP[3] || RemoteIP[3] == 255) {
743	    RemoteIP[3] = 1;
744	    RemoteIP[2]++;
745	    if (!RemoteIP[2]) {
746		RemoteIP[1]++;
747		if (!RemoteIP[1]) {
748		    RemoteIP[0]++;
749		}
750	    }
751	}
752    }
753
754    /* Initialize our random cookie.  Try /dev/urandom; if that fails,
755       use PID and rand() */
756    fp = fopen("/dev/urandom", "r");
757    if (fp) {
758	fread(&CookieSeed, 1, SEED_LEN, fp);
759	fclose(fp);
760    } else {
761	CookieSeed[0] = getpid() & 0xFF;
762	CookieSeed[1] = (getpid() >> 8) & 0xFF;
763	for (i=2; i<SEED_LEN; i++) {
764	    CookieSeed[i] = (rand() >> (i % 9)) & 0xFF;
765	}
766    }
767
768    sock = openInterface(IfName, Eth_PPPOE_Discovery, myAddr);
769
770    /* Set signal handler for SIGCHLD */
771    act.sa_handler = childHandler;
772    sigemptyset(&act.sa_mask);
773    act.sa_flags = SA_NOCLDSTOP | SA_RESTART;
774    if (sigaction(SIGCHLD, &act, NULL) < 0) {
775	fatalSys("sigaction");
776    }
777
778    /* Set up pipe for signal handler */
779    if (pipe(Pipe) < 0) {
780	fatalSys("pipe");
781    }
782
783    /* Main server loop */
784    maxFD = sock;
785    if (Pipe[0] > maxFD) maxFD = Pipe[0];
786    maxFD++;
787
788    for(;;) {
789	fd_set readable;
790	FD_ZERO(&readable);
791	FD_SET(sock, &readable);
792	FD_SET(Pipe[0], &readable);
793
794	while(1) {
795	    i = select(maxFD, &readable, NULL, NULL, NULL);
796	    if (i >= 0 || errno != EINTR) break;
797	}
798	if (i < 0) {
799	    fatalSys("select");
800	}
801
802	if (ReapPending) {
803	    ReapPending = 0;
804	    reapSessions();
805	}
806	if (!FD_ISSET(sock, &readable)) {
807	    continue;
808	}
809
810	receivePacket(sock, &packet, &len);
811
812	/* Check length */
813	if (ntohs(packet.length) + HDR_SIZE > len) {
814	    syslog(LOG_ERR, "Bogus PPPoE length field");
815	    continue;
816	}
817
818	/* Sanity check on packet */
819	if (packet.ver != 1 || packet.type != 1) {
820	    /* Syslog an error */
821	    continue;
822	}
823	switch(packet.code) {
824	case CODE_PADI:
825	    processPADI(sock, myAddr, &packet, len);
826	    break;
827	case CODE_PADR:
828	    processPADR(sock, myAddr, &packet, len);
829	    break;
830	case CODE_PADT:
831	case CODE_SESS:
832	    /* Ignore PADT and SESS -- children will handle them */
833	    break;
834	case CODE_PADO:
835	case CODE_PADS:
836  	    /* Ignore PADO and PADS totally */
837	    break;
838	default:
839	    /* Syslog an error */
840	    break;
841	}
842    }
843    return 0;
844}
845
846/**********************************************************************
847*%FUNCTION: sendErrorPADS
848*%ARGUMENTS:
849* sock -- socket to write to
850* source -- source Ethernet address
851* dest -- destination Ethernet address
852* errorTag -- error tag
853* errorMsg -- error message
854*%RETURNS:
855* Nothing
856*%DESCRIPTION:
857* Sends a PADS packet with an error message
858***********************************************************************/
859void
860sendErrorPADS(int sock,
861	      unsigned char *source,
862	      unsigned char *dest,
863	      int errorTag,
864	      char *errorMsg)
865{
866    struct PPPoEPacket pads;
867    unsigned char *cursor = pads.payload;
868    UINT16_t plen;
869    struct PPPoETag err;
870    int elen = strlen(errorMsg);
871
872    memcpy(pads.ethHdr.h_dest, dest, ETH_ALEN);
873    memcpy(pads.ethHdr.h_source, source, ETH_ALEN);
874    pads.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
875    pads.ver = 1;
876    pads.type = 1;
877    pads.code = CODE_PADS;
878
879    pads.session = htons(0);
880    plen = 0;
881
882    err.type = htons(errorTag);
883    err.length = htons(elen);
884
885    memcpy(err.payload, errorMsg, elen);
886    memcpy(cursor, &err, TAG_HDR_SIZE+elen);
887    cursor += TAG_HDR_SIZE + elen;
888    plen += TAG_HDR_SIZE + elen;
889
890    if (relayId.type) {
891	memcpy(cursor, &relayId, ntohs(relayId.length) + TAG_HDR_SIZE);
892	cursor += ntohs(relayId.length) + TAG_HDR_SIZE;
893	plen += ntohs(relayId.length) + TAG_HDR_SIZE;
894    }
895    if (hostUniq.type) {
896	memcpy(cursor, &hostUniq, ntohs(hostUniq.length) + TAG_HDR_SIZE);
897	cursor += ntohs(hostUniq.length) + TAG_HDR_SIZE;
898	plen += ntohs(hostUniq.length) + TAG_HDR_SIZE;
899    }
900    pads.length = htons(plen);
901    sendPacket(sock, &pads, (int) (plen + HDR_SIZE));
902}
903
904
905/**********************************************************************
906*%FUNCTION: startPPPD
907*%ARGUMENTS:
908* session -- client session record
909*%RETURNS:
910* Nothing
911*%DESCRIPTION:
912* Starts PPPD
913***********************************************************************/
914void
915startPPPD(struct ClientSession *session)
916{
917    /* Leave some room */
918    char *argv[20];
919
920    char buffer[SMALLBUF];
921
922    argv[0] = "pppd";
923    argv[1] = "pty";
924
925    snprintf(buffer, SMALLBUF, "%s -I %s -e %d:%02x:%02x:%02x:%02x:%02x:%02x%s",
926	     PPPOE_PATH, IfName,
927	     session->sess,
928	     session->eth[0], session->eth[1], session->eth[2],
929	     session->eth[3], session->eth[4], session->eth[5],
930	     PppoeOptions);
931    argv[2] = strdup(buffer);
932    if (!argv[2]) {
933	/* TODO: Send a PADT */
934	exit(1);
935    }
936
937    argv[3] = "file";
938    argv[4] = PPPOE_SERVER_OPTIONS;
939
940    snprintf(buffer, SMALLBUF, "%d.%d.%d.%d:%d.%d.%d.%d",
941	    (int) LocalIP[0], (int) LocalIP[1],
942	    (int) LocalIP[2], (int) LocalIP[3],
943	    (int) session->ip[0], (int) session->ip[1],
944	    (int) session->ip[2], (int) session->ip[3]);
945    syslog(LOG_INFO,
946	   "Session %d created for client %02x:%02x:%02x:%02x:%02x:%02x (%d.%d.%d.%d)",
947	   session->sess,
948	   session->eth[0], session->eth[1], session->eth[2],
949	   session->eth[3], session->eth[4], session->eth[5],
950	   (int) session->ip[0], (int) session->ip[1],
951	   (int) session->ip[2], (int) session->ip[3]);
952    argv[5] = buffer; /* No need for strdup -- about to execv! */
953    argv[6] = "nodetach";
954    argv[7] = "noaccomp";
955    argv[8] = "nobsdcomp";
956    argv[9] = "nodeflate";
957    argv[10] = "nopcomp";
958    argv[11] = "novj";
959    argv[12] = "novjccomp";
960    argv[13] = "default-asyncmap";
961    if (Synchronous) {
962	argv[14] = "sync";
963	argv[15] = NULL;
964    } else {
965	argv[14] = NULL;
966    }
967
968    execv(PPPD_PATH, argv);
969    exit(1);
970}
971