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