1/***********************************************************************
2*
3* pppoe.c
4*
5* Implementation of user-space PPPoE redirector for Linux.
6*
7* Copyright (C) 2000-2012 by 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***********************************************************************/
15
16static char const RCSID[] =
17"$Id$";
18
19#include "pppoe.h"
20
21#ifdef HAVE_SYSLOG_H
22#include <syslog.h>
23#endif
24
25#ifdef HAVE_GETOPT_H
26#include <getopt.h>
27#endif
28
29#include <string.h>
30#include <stdlib.h>
31#include <errno.h>
32
33#ifdef HAVE_SYS_TIME_H
34#include <sys/time.h>
35#endif
36
37#ifdef HAVE_SYS_UIO_H
38#include <sys/uio.h>
39#endif
40
41#ifdef HAVE_UNISTD_H
42#include <unistd.h>
43#endif
44
45#ifdef USE_LINUX_PACKET
46#include <sys/ioctl.h>
47#include <fcntl.h>
48#endif
49
50#include <signal.h>
51
52#ifdef HAVE_N_HDLC
53#ifndef N_HDLC
54#include <linux/termios.h>
55#endif
56#endif
57
58/* Default interface if no -I option given */
59#define DEFAULT_IF "eth0"
60
61/* Global variables -- options */
62int optInactivityTimeout = 0;	/* Inactivity timeout */
63int optClampMSS          = 0;	/* Clamp MSS to this value */
64int optSkipSession       = 0;	/* Perform discovery, print session info
65				   and exit */
66int optFloodDiscovery    = 0;   /* Flood server with discovery requests.
67				   USED FOR STRESS-TESTING ONLY.  DO NOT
68				   USE THE -F OPTION AGAINST A REAL ISP */
69
70PPPoEConnection *Connection = NULL; /* Must be global -- used
71				       in signal handler */
72
73/***********************************************************************
74*%FUNCTION: sendSessionPacket
75*%ARGUMENTS:
76* conn -- PPPoE connection
77* packet -- the packet to send
78* len -- length of data to send
79*%RETURNS:
80* Nothing
81*%DESCRIPTION:
82* Transmits a session packet to the peer.
83***********************************************************************/
84void
85sendSessionPacket(PPPoEConnection *conn, PPPoEPacket *packet, int len)
86{
87    packet->length = htons(len);
88    if (optClampMSS) {
89	clampMSS(packet, "outgoing", optClampMSS);
90    }
91    if (sendPacket(conn, conn->sessionSocket, packet, len + HDR_SIZE) < 0) {
92	if (errno == ENOBUFS) {
93	    /* No buffer space is a transient error */
94	    return;
95	}
96	exit(EXIT_FAILURE);
97    }
98#ifdef DEBUGGING_ENABLED
99    if (conn->debugFile) {
100	dumpPacket(conn->debugFile, packet, "SENT");
101	fprintf(conn->debugFile, "\n");
102	fflush(conn->debugFile);
103    }
104#endif
105
106}
107
108#ifdef USE_BPF
109/**********************************************************************
110*%FUNCTION: sessionDiscoveryPacket
111*%ARGUMENTS:
112* packet -- the discovery packet that was received
113*%RETURNS:
114* Nothing
115*%DESCRIPTION:
116* We got a discovery packet during the session stage.  This most likely
117* means a PADT.
118*
119* The BSD version uses a single socket for both discovery and session
120* packets.  When a packet comes in over the wire once we are in
121* session mode, either syncReadFromEth() or asyncReadFromEth() will
122* have already read the packet and determined it to be a discovery
123* packet before passing it here.
124***********************************************************************/
125static void
126sessionDiscoveryPacket(PPPoEPacket *packet)
127{
128    /* Sanity check */
129    if (packet->code != CODE_PADT) {
130	return;
131    }
132
133    /* It's a PADT, all right.  Is it for us? */
134    if (packet->session != Connection->session) {
135	/* Nope, ignore it */
136	return;
137    }
138    if (memcmp(packet->ethHdr.h_dest, Connection->myEth, ETH_ALEN)) {
139	return;
140    }
141
142    if (memcmp(packet->ethHdr.h_source, Connection->peerEth, ETH_ALEN)) {
143	return;
144    }
145
146    syslog(LOG_INFO,
147	   "Session %d terminated -- received PADT from peer",
148	   (int) ntohs(packet->session));
149    parsePacket(packet, parseLogErrs, NULL);
150    sendPADT(Connection, "Received PADT from peer");
151    exit(EXIT_SUCCESS);
152}
153#else
154/**********************************************************************
155*%FUNCTION: sessionDiscoveryPacket
156*%ARGUMENTS:
157* conn -- PPPoE connection
158*%RETURNS:
159* Nothing
160*%DESCRIPTION:
161* We got a discovery packet during the session stage.  This most likely
162* means a PADT.
163***********************************************************************/
164static void
165sessionDiscoveryPacket(PPPoEConnection *conn)
166{
167    PPPoEPacket packet;
168    int len;
169
170    if (receivePacket(conn->discoverySocket, &packet, &len) < 0) {
171	return;
172    }
173
174    /* Check length */
175    if (ntohs(packet.length) + HDR_SIZE > len) {
176	syslog(LOG_ERR, "Bogus PPPoE length field (%u)",
177	       (unsigned int) ntohs(packet.length));
178	return;
179    }
180
181    if (packet.code != CODE_PADT) {
182	/* Not PADT; ignore it */
183	return;
184    }
185
186    /* It's a PADT, all right.  Is it for us? */
187    if (packet.session != conn->session) {
188	/* Nope, ignore it */
189	return;
190    }
191
192    if (memcmp(packet.ethHdr.h_dest, conn->myEth, ETH_ALEN)) {
193	return;
194    }
195
196    if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) {
197	return;
198    }
199#ifdef DEBUGGING_ENABLED
200    if (conn->debugFile) {
201	dumpPacket(conn->debugFile, &packet, "RCVD");
202	fprintf(conn->debugFile, "\n");
203	fflush(conn->debugFile);
204    }
205#endif
206    syslog(LOG_INFO,
207	   "Session %d terminated -- received PADT from peer",
208	   (int) ntohs(packet.session));
209    parsePacket(&packet, parseLogErrs, NULL);
210    sendPADT(conn, "Received PADT from peer");
211    exit(EXIT_SUCCESS);
212}
213#endif /* USE_BPF */
214
215/**********************************************************************
216*%FUNCTION: session
217*%ARGUMENTS:
218* conn -- PPPoE connection info
219*%RETURNS:
220* Nothing
221*%DESCRIPTION:
222* Handles the "session" phase of PPPoE
223***********************************************************************/
224void
225session(PPPoEConnection *conn)
226{
227    fd_set readable;
228    PPPoEPacket packet;
229    struct timeval tv;
230    struct timeval *tvp = NULL;
231    int maxFD = 0;
232    int r;
233
234    /* Drop privileges */
235    dropPrivs();
236
237    /* Prepare for select() */
238    if (conn->sessionSocket > maxFD)   maxFD = conn->sessionSocket;
239    if (conn->discoverySocket > maxFD) maxFD = conn->discoverySocket;
240    maxFD++;
241
242    /* Fill in the constant fields of the packet to save time */
243    memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN);
244    memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN);
245    packet.ethHdr.h_proto = htons(Eth_PPPOE_Session);
246    packet.ver = 1;
247    packet.type = 1;
248    packet.code = CODE_SESS;
249    packet.session = conn->session;
250
251    initPPP();
252
253#ifdef USE_BPF
254    /* check for buffered session data */
255    while (BPF_BUFFER_HAS_DATA) {
256	if (conn->synchronous) {
257	    syncReadFromEth(conn, conn->sessionSocket, optClampMSS);
258	} else {
259	    asyncReadFromEth(conn, conn->sessionSocket, optClampMSS);
260	}
261    }
262#endif
263
264    for (;;) {
265	if (optInactivityTimeout > 0) {
266	    tv.tv_sec = optInactivityTimeout;
267	    tv.tv_usec = 0;
268	    tvp = &tv;
269	}
270	FD_ZERO(&readable);
271	FD_SET(0, &readable);     /* ppp packets come from stdin */
272	if (conn->discoverySocket >= 0) {
273	    FD_SET(conn->discoverySocket, &readable);
274	}
275	FD_SET(conn->sessionSocket, &readable);
276	while(1) {
277	    r = select(maxFD, &readable, NULL, NULL, tvp);
278	    if (r >= 0 || errno != EINTR) break;
279	}
280	if (r < 0) {
281	    fatalSys("select (session)");
282	}
283	if (r == 0) { /* Inactivity timeout */
284	    syslog(LOG_ERR, "Inactivity timeout... something wicked happened on session %d",
285		   (int) ntohs(conn->session));
286	    sendPADT(conn, "RP-PPPoE: Inactivity timeout");
287	    exit(EXIT_FAILURE);
288	}
289
290	/* Handle ready sockets */
291	if (FD_ISSET(0, &readable)) {
292	    if (conn->synchronous) {
293		syncReadFromPPP(conn, &packet);
294	    } else {
295		asyncReadFromPPP(conn, &packet);
296	    }
297	}
298
299	if (FD_ISSET(conn->sessionSocket, &readable)) {
300	    do {
301		if (conn->synchronous) {
302		    syncReadFromEth(conn, conn->sessionSocket, optClampMSS);
303		} else {
304		    asyncReadFromEth(conn, conn->sessionSocket, optClampMSS);
305		}
306	    } while (BPF_BUFFER_HAS_DATA);
307	}
308
309#ifndef USE_BPF
310	/* BSD uses a single socket, see *syncReadFromEth() */
311	/* for calls to sessionDiscoveryPacket() */
312	if (conn->discoverySocket >= 0) {
313	    if (FD_ISSET(conn->discoverySocket, &readable)) {
314		sessionDiscoveryPacket(conn);
315	    }
316	}
317#endif
318
319    }
320}
321
322
323/***********************************************************************
324*%FUNCTION: sigPADT
325*%ARGUMENTS:
326* src -- signal received
327*%RETURNS:
328* Nothing
329*%DESCRIPTION:
330* If an established session exists send PADT to terminate from session
331*  from our end
332***********************************************************************/
333static void
334sigPADT(int src)
335{
336  syslog(LOG_DEBUG,"Received signal %d on session %d.",
337	 (int)src, (int) ntohs(Connection->session));
338  sendPADTf(Connection, "RP-PPPoE: Received signal %d", src);
339  exit(EXIT_SUCCESS);
340}
341
342/**********************************************************************
343*%FUNCTION: usage
344*%ARGUMENTS:
345* argv0 -- program name
346*%RETURNS:
347* Nothing
348*%DESCRIPTION:
349* Prints usage information and exits.
350***********************************************************************/
351void
352usage(char const *argv0)
353{
354    fprintf(stderr, "Usage: %s [options]\n", argv0);
355    fprintf(stderr, "Options:\n");
356#ifdef USE_BPF
357    fprintf(stderr, "   -I if_name     -- Specify interface (REQUIRED)\n");
358#else
359    fprintf(stderr, "   -I if_name     -- Specify interface (default %s.)\n",
360	    DEFAULT_IF);
361#endif
362#ifdef DEBUGGING_ENABLED
363    fprintf(stderr, "   -D filename    -- Log debugging information in filename.\n");
364#endif
365    fprintf(stderr,
366	    "   -T timeout     -- Specify inactivity timeout in seconds.\n"
367	    "   -t timeout     -- Initial timeout for discovery packets in seconds\n"
368	    "   -V             -- Print version and exit.\n"
369	    "   -A             -- Print access concentrator names and exit.\n"
370	    "   -S name        -- Set desired service name.\n"
371	    "   -C name        -- Set desired access concentrator name.\n"
372	    "   -U             -- Use Host-Unique to allow multiple PPPoE sessions.\n"
373	    "   -s             -- Use synchronous PPP encapsulation.\n"
374	    "   -m MSS         -- Clamp incoming and outgoing MSS options.\n"
375	    "   -p pidfile     -- Write process-ID to pidfile.\n"
376	    "   -e sess:mac    -- Skip discovery phase; use existing session.\n"
377	    "   -n             -- Do not open discovery socket.\n"
378	    "   -k             -- Kill a session with PADT (requires -e)\n"
379	    "   -d             -- Perform discovery, print session info and exit.\n"
380	    "   -f disc:sess   -- Set Ethernet frame types (hex).\n"
381	    "   -h             -- Print usage information.\n\n"
382	    "PPPoE Version %s, Copyright (C) 2001-2006 Roaring Penguin Software Inc.\n"
383	    "PPPoE comes with ABSOLUTELY NO WARRANTY.\n"
384	    "This is free software, and you are welcome to redistribute it under the terms\n"
385	    "of the GNU General Public License, version 2 or any later version.\n"
386	    "http://www.roaringpenguin.com\n", VERSION);
387    exit(EXIT_SUCCESS);
388}
389
390/**********************************************************************
391*%FUNCTION: main
392*%ARGUMENTS:
393* argc, argv -- count and values of command-line arguments
394*%RETURNS:
395* Nothing
396*%DESCRIPTION:
397* Main program
398***********************************************************************/
399int
400main(int argc, char *argv[])
401{
402    int opt;
403    int n;
404    unsigned int m[6];		/* MAC address in -e option */
405    unsigned int s;		/* Temporary to hold session */
406    FILE *pidfile;
407    unsigned int discoveryType, sessionType;
408    char const *options;
409
410    PPPoEConnection conn;
411
412#ifdef HAVE_N_HDLC
413    int disc = N_HDLC;
414    long flags;
415#endif
416
417    if (getuid() != geteuid() ||
418	getgid() != getegid()) {
419	IsSetID = 1;
420    }
421
422    /* Initialize connection info */
423    memset(&conn, 0, sizeof(conn));
424    conn.discoverySocket = -1;
425    conn.sessionSocket = -1;
426    conn.discoveryTimeout = PADI_TIMEOUT;
427
428    /* For signal handler */
429    Connection = &conn;
430
431    /* Initialize syslog */
432    openlog("pppoe", LOG_PID, LOG_DAEMON);
433
434#ifdef DEBUGGING_ENABLED
435    options = "I:VAT:D:hS:C:Usm:np:e:kdf:F:t:";
436#else
437    options = "I:VAT:hS:C:Usm:np:e:kdf:F:t:";
438#endif
439    while((opt = getopt(argc, argv, options)) != -1) {
440	switch(opt) {
441	case 't':
442	    if (sscanf(optarg, "%d", &conn.discoveryTimeout) != 1) {
443		fprintf(stderr, "Illegal argument to -t: Should be -t timeout\n");
444		exit(EXIT_FAILURE);
445	    }
446	    if (conn.discoveryTimeout < 1) {
447		conn.discoveryTimeout = 1;
448	    }
449	    break;
450	case 'F':
451	    if (sscanf(optarg, "%d", &optFloodDiscovery) != 1) {
452		fprintf(stderr, "Illegal argument to -F: Should be -F numFloods\n");
453		exit(EXIT_FAILURE);
454	    }
455	    if (optFloodDiscovery < 1) optFloodDiscovery = 1;
456	    fprintf(stderr,
457		    "WARNING: DISCOVERY FLOOD IS MEANT FOR STRESS-TESTING\n"
458		    "A PPPOE SERVER WHICH YOU OWN.  DO NOT USE IT AGAINST\n"
459		    "A REAL ISP.  YOU HAVE 5 SECONDS TO ABORT.\n");
460	    sleep(5);
461	    break;
462	case 'f':
463	    if (sscanf(optarg, "%x:%x", &discoveryType, &sessionType) != 2) {
464		fprintf(stderr, "Illegal argument to -f: Should be disc:sess in hex\n");
465		exit(EXIT_FAILURE);
466	    }
467	    Eth_PPPOE_Discovery = (UINT16_t) discoveryType;
468	    Eth_PPPOE_Session   = (UINT16_t) sessionType;
469	    break;
470	case 'd':
471	    optSkipSession = 1;
472	    break;
473
474	case 'k':
475	    conn.killSession = 1;
476	    break;
477
478	case 'n':
479	    /* Do not even open a discovery socket -- used when invoked
480	       by pppoe-server */
481	    conn.noDiscoverySocket = 1;
482	    break;
483
484	case 'e':
485	    /* Existing session: "sess:xx:yy:zz:aa:bb:cc" where "sess" is
486	       session-ID, and xx:yy:zz:aa:bb:cc is MAC-address of peer */
487	    n = sscanf(optarg, "%u:%2x:%2x:%2x:%2x:%2x:%2x",
488		       &s, &m[0], &m[1], &m[2], &m[3], &m[4], &m[5]);
489	    if (n != 7) {
490		fprintf(stderr, "Illegal argument to -e: Should be sess:xx:yy:zz:aa:bb:cc\n");
491		exit(EXIT_FAILURE);
492	    }
493
494	    /* Copy MAC address of peer */
495	    for (n=0; n<6; n++) {
496		conn.peerEth[n] = (unsigned char) m[n];
497	    }
498
499	    /* Convert session */
500	    conn.session = htons(s);
501
502	    /* Skip discovery phase! */
503	    conn.skipDiscovery = 1;
504	    break;
505
506	case 'p':
507	    switchToRealID();
508	    pidfile = fopen(optarg, "w");
509	    if (pidfile) {
510		fprintf(pidfile, "%lu\n", (unsigned long) getpid());
511		fclose(pidfile);
512	    }
513	    switchToEffectiveID();
514	    break;
515	case 'S':
516	    SET_STRING(conn.serviceName, optarg);
517	    break;
518	case 'C':
519	    SET_STRING(conn.acName, optarg);
520	    break;
521	case 's':
522	    conn.synchronous = 1;
523	    break;
524	case 'U':
525	    conn.useHostUniq = 1;
526	    break;
527#ifdef DEBUGGING_ENABLED
528	case 'D':
529	    switchToRealID();
530	    conn.debugFile = fopen(optarg, "w");
531	    switchToEffectiveID();
532	    if (!conn.debugFile) {
533		fprintf(stderr, "Could not open %s: %s\n",
534			optarg, strerror(errno));
535		exit(EXIT_FAILURE);
536	    }
537	    fprintf(conn.debugFile, "rp-pppoe-%s\n", VERSION);
538	    fflush(conn.debugFile);
539	    break;
540#endif
541	case 'T':
542	    optInactivityTimeout = (int) strtol(optarg, NULL, 10);
543	    if (optInactivityTimeout < 0) {
544		optInactivityTimeout = 0;
545	    }
546	    break;
547	case 'm':
548	    optClampMSS = (int) strtol(optarg, NULL, 10);
549	    if (optClampMSS < 536) {
550		fprintf(stderr, "-m: %d is too low (min 536)\n", optClampMSS);
551		exit(EXIT_FAILURE);
552	    }
553	    if (optClampMSS > 1452) {
554		fprintf(stderr, "-m: %d is too high (max 1452)\n", optClampMSS);
555		exit(EXIT_FAILURE);
556	    }
557	    break;
558	case 'I':
559	    SET_STRING(conn.ifName, optarg);
560	    break;
561	case 'V':
562	    printf("Roaring Penguin PPPoE Version %s\n", VERSION);
563	    exit(EXIT_SUCCESS);
564	case 'A':
565	    conn.printACNames = 1;
566	    break;
567	case 'h':
568	    usage(argv[0]);
569	    break;
570	default:
571	    usage(argv[0]);
572	}
573    }
574
575    /* Pick a default interface name */
576    if (!conn.ifName) {
577#ifdef USE_BPF
578	fprintf(stderr, "No interface specified (-I option)\n");
579	exit(EXIT_FAILURE);
580#else
581	SET_STRING(conn.ifName, DEFAULT_IF);
582#endif
583    }
584
585    if (!conn.printACNames) {
586
587#ifdef HAVE_N_HDLC
588	if (conn.synchronous) {
589	    if (ioctl(0, TIOCSETD, &disc) < 0) {
590		printErr("Unable to set line discipline to N_HDLC.  Make sure your kernel supports the N_HDLC line discipline, or do not use the SYNCHRONOUS option.  Quitting.");
591		exit(EXIT_FAILURE);
592	    } else {
593		syslog(LOG_INFO,
594		       "Changed pty line discipline to N_HDLC for synchronous mode");
595	    }
596	    /* There is a bug in Linux's select which returns a descriptor
597	     * as readable if N_HDLC line discipline is on, even if
598	     * it isn't really readable.  This return happens only when
599	     * select() times out.  To avoid blocking forever in read(),
600	     * make descriptor 0 non-blocking */
601	    flags = fcntl(0, F_GETFL);
602	    if (flags < 0) fatalSys("fcntl(F_GETFL)");
603	    if (fcntl(0, F_SETFL, (long) flags | O_NONBLOCK) < 0) {
604		fatalSys("fcntl(F_SETFL)");
605	    }
606	}
607#endif
608
609    }
610
611    if (optFloodDiscovery) {
612	for (n=0; n < optFloodDiscovery; n++) {
613	    if (conn.printACNames) {
614		fprintf(stderr, "Sending discovery flood %d\n", n+1);
615	    }
616            conn.discoverySocket =
617	        openInterface(conn.ifName, Eth_PPPOE_Discovery, conn.myEth, NULL);
618	    discovery(&conn);
619	    conn.discoveryState = STATE_SENT_PADI;
620	    close(conn.discoverySocket);
621	}
622	exit(EXIT_SUCCESS);
623    }
624
625    /* Open session socket before discovery phase, to avoid losing session */
626    /* packets sent by peer just after PADS packet (noted on some Cisco    */
627    /* server equipment).                                                  */
628    /* Opening this socket just before waitForPADS in the discovery()      */
629    /* function would be more appropriate, but it would mess-up the code   */
630    if (!optSkipSession)
631        conn.sessionSocket = openInterface(conn.ifName, Eth_PPPOE_Session, conn.myEth, NULL);
632
633    /* Skip discovery and don't open discovery socket? */
634    if (conn.skipDiscovery && conn.noDiscoverySocket) {
635	conn.discoveryState = STATE_SESSION;
636    } else {
637        conn.discoverySocket =
638	    openInterface(conn.ifName, Eth_PPPOE_Discovery, conn.myEth, NULL);
639        discovery(&conn);
640    }
641    if (optSkipSession) {
642	printf("%u:%02x:%02x:%02x:%02x:%02x:%02x\n",
643	       ntohs(conn.session),
644	       conn.peerEth[0],
645	       conn.peerEth[1],
646	       conn.peerEth[2],
647	       conn.peerEth[3],
648	       conn.peerEth[4],
649	       conn.peerEth[5]);
650	exit(EXIT_SUCCESS);
651    }
652
653    /* Set signal handlers: send PADT on HUP; ignore TERM and INT */
654    signal(SIGTERM, SIG_IGN);
655    signal(SIGINT, SIG_IGN);
656    signal(SIGHUP, sigPADT);
657    session(&conn);
658    return 0;
659}
660
661/**********************************************************************
662*%FUNCTION: fatalSys
663*%ARGUMENTS:
664* str -- error message
665*%RETURNS:
666* Nothing
667*%DESCRIPTION:
668* Prints a message plus the errno value to stderr and syslog and exits.
669***********************************************************************/
670void
671fatalSys(char const *str)
672{
673    char buf[1024];
674    sprintf(buf, "%.256s: Session %d: %.256s",
675	    str, (int) ntohs(Connection->session), strerror(errno));
676    printErr(buf);
677    sendPADTf(Connection, "RP-PPPoE: System call error: %s",
678	      strerror(errno));
679    exit(EXIT_FAILURE);
680}
681
682/**********************************************************************
683*%FUNCTION: sysErr
684*%ARGUMENTS:
685* str -- error message
686*%RETURNS:
687* Nothing
688*%DESCRIPTION:
689* Prints a message plus the errno value to syslog.
690***********************************************************************/
691void
692sysErr(char const *str)
693{
694    char buf[1024];
695    sprintf(buf, "%.256s: %.256s", str, strerror(errno));
696    printErr(buf);
697}
698
699/**********************************************************************
700*%FUNCTION: rp_fatal
701*%ARGUMENTS:
702* str -- error message
703*%RETURNS:
704* Nothing
705*%DESCRIPTION:
706* Prints a message to stderr and syslog and exits.
707***********************************************************************/
708void
709rp_fatal(char const *str)
710{
711    printErr(str);
712    sendPADTf(Connection, "RP-PPPoE: Session %d: %.256s",
713	      (int) ntohs(Connection->session), str);
714    exit(EXIT_FAILURE);
715}
716
717/**********************************************************************
718*%FUNCTION: asyncReadFromEth
719*%ARGUMENTS:
720* conn -- PPPoE connection info
721* sock -- Ethernet socket
722* clampMss -- if non-zero, do MSS-clamping
723*%RETURNS:
724* Nothing
725*%DESCRIPTION:
726* Reads a packet from the Ethernet interface and sends it to async PPP
727* device.
728***********************************************************************/
729void
730asyncReadFromEth(PPPoEConnection *conn, int sock, int clampMss)
731{
732    PPPoEPacket packet;
733    int len;
734    int plen;
735    int i;
736    unsigned char pppBuf[4096];
737    unsigned char *ptr = pppBuf;
738    unsigned char c;
739    UINT16_t fcs;
740    unsigned char header[2] = {FRAME_ADDR, FRAME_CTRL};
741    unsigned char tail[2];
742#ifdef USE_BPF
743    int type;
744#endif
745
746    if (receivePacket(sock, &packet, &len) < 0) {
747	return;
748    }
749
750    /* Check length */
751    if (ntohs(packet.length) + HDR_SIZE > len) {
752	syslog(LOG_ERR, "Bogus PPPoE length field (%u)",
753	       (unsigned int) ntohs(packet.length));
754	return;
755    }
756#ifdef DEBUGGING_ENABLED
757    if (conn->debugFile) {
758	dumpPacket(conn->debugFile, &packet, "RCVD");
759	fprintf(conn->debugFile, "\n");
760	fflush(conn->debugFile);
761    }
762#endif
763
764#ifdef USE_BPF
765    /* Make sure this is a session packet before processing further */
766    type = etherType(&packet);
767    if (type == Eth_PPPOE_Discovery) {
768	sessionDiscoveryPacket(&packet);
769    } else if (type != Eth_PPPOE_Session) {
770	return;
771    }
772#endif
773
774    /* Sanity check */
775    if (packet.code != CODE_SESS) {
776	syslog(LOG_ERR, "Unexpected packet code %d", (int) packet.code);
777	return;
778    }
779    if (packet.ver != 1) {
780	syslog(LOG_ERR, "Unexpected packet version %d", (int) packet.ver);
781	return;
782    }
783    if (packet.type != 1) {
784	syslog(LOG_ERR, "Unexpected packet type %d", (int) packet.type);
785	return;
786    }
787    if (memcmp(packet.ethHdr.h_dest, conn->myEth, ETH_ALEN)) {
788	return;
789    }
790    if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) {
791	/* Not for us -- must be another session.  This is not an error,
792	   so don't log anything.  */
793	return;
794    }
795
796    if (packet.session != conn->session) {
797	/* Not for us -- must be another session.  This is not an error,
798	   so don't log anything.  */
799	return;
800    }
801    plen = ntohs(packet.length);
802    if (plen + HDR_SIZE > len) {
803	syslog(LOG_ERR, "Bogus length field in session packet %d (%d)",
804	       (int) plen, (int) len);
805	return;
806    }
807
808    /* Clamp MSS */
809    if (clampMss) {
810	clampMSS(&packet, "incoming", clampMss);
811    }
812
813    /* Compute FCS */
814    fcs = pppFCS16(PPPINITFCS16, header, 2);
815    fcs = pppFCS16(fcs, packet.payload, plen) ^ 0xffff;
816    tail[0] = fcs & 0x00ff;
817    tail[1] = (fcs >> 8) & 0x00ff;
818
819    /* Build a buffer to send to PPP */
820    *ptr++ = FRAME_FLAG;
821    *ptr++ = FRAME_ADDR;
822    *ptr++ = FRAME_ESC;
823    *ptr++ = FRAME_CTRL ^ FRAME_ENC;
824
825    for (i=0; i<plen; i++) {
826	c = packet.payload[i];
827	if (c == FRAME_FLAG || c == FRAME_ADDR || c == FRAME_ESC || c < 0x20) {
828	    *ptr++ = FRAME_ESC;
829	    *ptr++ = c ^ FRAME_ENC;
830	} else {
831	    *ptr++ = c;
832	}
833    }
834    for (i=0; i<2; i++) {
835	c = tail[i];
836	if (c == FRAME_FLAG || c == FRAME_ADDR || c == FRAME_ESC || c < 0x20) {
837	    *ptr++ = FRAME_ESC;
838	    *ptr++ = c ^ FRAME_ENC;
839	} else {
840	    *ptr++ = c;
841	}
842    }
843    *ptr++ = FRAME_FLAG;
844
845    /* Ship it out */
846    if (write(1, pppBuf, (ptr-pppBuf)) < 0) {
847	fatalSys("asyncReadFromEth: write");
848    }
849}
850
851/**********************************************************************
852*%FUNCTION: syncReadFromEth
853*%ARGUMENTS:
854* conn -- PPPoE connection info
855* sock -- Ethernet socket
856* clampMss -- if true, clamp MSS.
857*%RETURNS:
858* Nothing
859*%DESCRIPTION:
860* Reads a packet from the Ethernet interface and sends it to sync PPP
861* device.
862***********************************************************************/
863void
864syncReadFromEth(PPPoEConnection *conn, int sock, int clampMss)
865{
866    PPPoEPacket packet;
867    int len;
868    int plen;
869    struct iovec vec[2];
870    unsigned char dummy[2];
871#ifdef USE_BPF
872    int type;
873#endif
874
875    if (receivePacket(sock, &packet, &len) < 0) {
876	return;
877    }
878
879    /* Check length */
880    if (ntohs(packet.length) + HDR_SIZE > len) {
881	syslog(LOG_ERR, "Bogus PPPoE length field (%u)",
882	       (unsigned int) ntohs(packet.length));
883	return;
884    }
885#ifdef DEBUGGING_ENABLED
886    if (conn->debugFile) {
887	dumpPacket(conn->debugFile, &packet, "RCVD");
888	fprintf(conn->debugFile, "\n");
889	fflush(conn->debugFile);
890    }
891#endif
892
893#ifdef USE_BPF
894    /* Make sure this is a session packet before processing further */
895    type = etherType(&packet);
896    if (type == Eth_PPPOE_Discovery) {
897	sessionDiscoveryPacket(&packet);
898    } else if (type != Eth_PPPOE_Session) {
899	return;
900    }
901#endif
902
903    /* Sanity check */
904    if (packet.code != CODE_SESS) {
905	syslog(LOG_ERR, "Unexpected packet code %d", (int) packet.code);
906	return;
907    }
908    if (packet.ver != 1) {
909	syslog(LOG_ERR, "Unexpected packet version %d", (int) packet.ver);
910	return;
911    }
912    if (packet.type != 1) {
913	syslog(LOG_ERR, "Unexpected packet type %d", (int) packet.type);
914	return;
915    }
916    if (memcmp(packet.ethHdr.h_dest, conn->myEth, ETH_ALEN)) {
917	/* Not for us -- must be another session.  This is not an error,
918	   so don't log anything.  */
919	return;
920    }
921    if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) {
922	/* Not for us -- must be another session.  This is not an error,
923	   so don't log anything.  */
924	return;
925    }
926    if (packet.session != conn->session) {
927	/* Not for us -- must be another session.  This is not an error,
928	   so don't log anything.  */
929	return;
930    }
931    plen = ntohs(packet.length);
932    if (plen + HDR_SIZE > len) {
933	syslog(LOG_ERR, "Bogus length field in session packet %d (%d)",
934	       (int) plen, (int) len);
935	return;
936    }
937
938    /* Clamp MSS */
939    if (clampMss) {
940	clampMSS(&packet, "incoming", clampMss);
941    }
942
943    /* Ship it out */
944    vec[0].iov_base = (void *) dummy;
945    dummy[0] = FRAME_ADDR;
946    dummy[1] = FRAME_CTRL;
947    vec[0].iov_len = 2;
948    vec[1].iov_base = (void *) packet.payload;
949    vec[1].iov_len = plen;
950
951    if (writev(1, vec, 2) < 0) {
952	fatalSys("syncReadFromEth: write");
953    }
954}
955