summaryrefslogtreecommitdiff
path: root/src/pppoe.c (plain)
blob: f64df82d1c0333184ca8eca9f8b27f54a1cde8e5
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_DEBUG,"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 fprintf(pidfile, "%lu\n", (unsigned long) getpid());
515 fclose(pidfile);
516 }
517 switchToEffectiveID();
518 break;
519 case 'S':
520 SET_STRING(conn.serviceName, optarg);
521 break;
522 case 'C':
523 SET_STRING(conn.acName, optarg);
524 break;
525 case 's':
526 conn.synchronous = 1;
527 break;
528 case 'U':
529 conn.useHostUniq = 1;
530 break;
531#ifdef DEBUGGING_ENABLED
532 case 'D':
533 switchToRealID();
534 conn.debugFile = fopen(optarg, "w");
535 switchToEffectiveID();
536 if (!conn.debugFile) {
537 fprintf(stderr, "Could not open %s: %s\n",
538 optarg, strerror(errno));
539 exit(EXIT_FAILURE);
540 }
541 fprintf(conn.debugFile, "rp-pppoe-%s\n", VERSION);
542 fflush(conn.debugFile);
543 break;
544#endif
545 case 'T':
546 optInactivityTimeout = (int) strtol(optarg, NULL, 10);
547 if (optInactivityTimeout < 0) {
548 optInactivityTimeout = 0;
549 }
550 break;
551 case 'm':
552 optClampMSS = (int) strtol(optarg, NULL, 10);
553 if (optClampMSS < 536) {
554 fprintf(stderr, "-m: %d is too low (min 536)\n", optClampMSS);
555 exit(EXIT_FAILURE);
556 }
557 if (optClampMSS > 1452) {
558 fprintf(stderr, "-m: %d is too high (max 1452)\n", optClampMSS);
559 exit(EXIT_FAILURE);
560 }
561 break;
562 case 'I':
563 SET_STRING(conn.ifName, optarg);
564 break;
565 case 'V':
566 printf("Roaring Penguin PPPoE Version %s\n", VERSION);
567 exit(EXIT_SUCCESS);
568 case 'A':
569 conn.printACNames = 1;
570 break;
571 case 'h':
572 usage(argv[0]);
573 break;
574 default:
575 usage(argv[0]);
576 }
577 }
578
579 /* Pick a default interface name */
580 if (!conn.ifName) {
581#ifdef USE_BPF
582 fprintf(stderr, "No interface specified (-I option)\n");
583 exit(EXIT_FAILURE);
584#else
585 SET_STRING(conn.ifName, DEFAULT_IF);
586#endif
587 }
588
589 if (!conn.printACNames) {
590
591#ifdef HAVE_N_HDLC
592 if (conn.synchronous) {
593 if (ioctl(0, TIOCSETD, &disc) < 0) {
594 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.");
595 exit(EXIT_FAILURE);
596 } else {
597 syslog(LOG_INFO,
598 "Changed pty line discipline to N_HDLC for synchronous mode");
599 }
600 /* There is a bug in Linux's select which returns a descriptor
601 * as readable if N_HDLC line discipline is on, even if
602 * it isn't really readable. This return happens only when
603 * select() times out. To avoid blocking forever in read(),
604 * make descriptor 0 non-blocking */
605 flags = fcntl(0, F_GETFL);
606 if (flags < 0) fatalSys("fcntl(F_GETFL)");
607 if (fcntl(0, F_SETFL, (long) flags | O_NONBLOCK) < 0) {
608 fatalSys("fcntl(F_SETFL)");
609 }
610 }
611#endif
612
613 }
614
615 if (optFloodDiscovery) {
616 for (n=0; n < optFloodDiscovery; n++) {
617 if (conn.printACNames) {
618 fprintf(stderr, "Sending discovery flood %d\n", n+1);
619 }
620 conn.discoverySocket =
621 openInterface(conn.ifName, Eth_PPPOE_Discovery, conn.myEth);
622 discovery(&conn);
623 conn.discoveryState = STATE_SENT_PADI;
624 close(conn.discoverySocket);
625 }
626 exit(EXIT_SUCCESS);
627 }
628
629 /* Open session socket before discovery phase, to avoid losing session */
630 /* packets sent by peer just after PADS packet (noted on some Cisco */
631 /* server equipment). */
632 /* Opening this socket just before waitForPADS in the discovery() */
633 /* function would be more appropriate, but it would mess-up the code */
634 if (!optSkipSession)
635 conn.sessionSocket = openInterface(conn.ifName, Eth_PPPOE_Session, conn.myEth);
636
637 /* Skip discovery and don't open discovery socket? */
638 if (conn.skipDiscovery && conn.noDiscoverySocket) {
639 conn.discoveryState = STATE_SESSION;
640 } else {
641 conn.discoverySocket =
642 openInterface(conn.ifName, Eth_PPPOE_Discovery, conn.myEth);
643 syslog( LOG_INFO, "discovery\n");
644 discovery(&conn);
645 }
646 if (optSkipSession) {
647 printf("%u:%02x:%02x:%02x:%02x:%02x:%02x\n",
648 ntohs(conn.session),
649 conn.peerEth[0],
650 conn.peerEth[1],
651 conn.peerEth[2],
652 conn.peerEth[3],
653 conn.peerEth[4],
654 conn.peerEth[5]);
655 exit(EXIT_SUCCESS);
656 }
657
658 /* Set signal handlers: send PADT on HUP; ignore TERM and INT */
659 signal(SIGTERM, SIG_IGN);
660 signal(SIGINT, SIG_IGN);
661 signal(SIGHUP, sigPADT);
662 session(&conn);
663 return 0;
664}
665
666/**********************************************************************
667*%FUNCTION: fatalSys
668*%ARGUMENTS:
669* str -- error message
670*%RETURNS:
671* Nothing
672*%DESCRIPTION:
673* Prints a message plus the errno value to stderr and syslog and exits.
674***********************************************************************/
675void
676fatalSys(char const *str)
677{
678 char buf[1024];
679 sprintf(buf, "%.256s: Session %d: %.256s",
680 str, (int) ntohs(Connection->session), strerror(errno));
681 printErr(buf);
682 sendPADTf(Connection, "RP-PPPoE: System call error: %s",
683 strerror(errno));
684 exit(EXIT_FAILURE);
685}
686
687/**********************************************************************
688*%FUNCTION: sysErr
689*%ARGUMENTS:
690* str -- error message
691*%RETURNS:
692* Nothing
693*%DESCRIPTION:
694* Prints a message plus the errno value to syslog.
695***********************************************************************/
696void
697sysErr(char const *str)
698{
699 char buf[1024];
700 sprintf(buf, "%.256s: %.256s", str, strerror(errno));
701 printErr(buf);
702}
703
704/**********************************************************************
705*%FUNCTION: rp_fatal
706*%ARGUMENTS:
707* str -- error message
708*%RETURNS:
709* Nothing
710*%DESCRIPTION:
711* Prints a message to stderr and syslog and exits.
712***********************************************************************/
713void
714rp_fatal(char const *str)
715{
716 printErr(str);
717 sendPADTf(Connection, "RP-PPPoE: Session %d: %.256s",
718 (int) ntohs(Connection->session), str);
719 exit(EXIT_FAILURE);
720}
721
722/**********************************************************************
723*%FUNCTION: asyncReadFromEth
724*%ARGUMENTS:
725* conn -- PPPoE connection info
726* sock -- Ethernet socket
727* clampMss -- if non-zero, do MSS-clamping
728*%RETURNS:
729* Nothing
730*%DESCRIPTION:
731* Reads a packet from the Ethernet interface and sends it to async PPP
732* device.
733***********************************************************************/
734void
735asyncReadFromEth(PPPoEConnection *conn, int sock, int clampMss)
736{
737 PPPoEPacket packet;
738 int len;
739 int plen;
740 int i;
741 unsigned char pppBuf[4096];
742 unsigned char *ptr = pppBuf;
743 unsigned char c;
744 UINT16_t fcs;
745 unsigned char header[2] = {FRAME_ADDR, FRAME_CTRL};
746 unsigned char tail[2];
747#ifdef USE_BPF
748 int type;
749#endif
750
751 if (receivePacket(sock, &packet, &len) < 0) {
752 return;
753 }
754
755 /* Check length */
756 if (ntohs(packet.length) + HDR_SIZE > (unsigned int)len) {
757 syslog(LOG_ERR, "Bogus PPPoE length field (%u)",
758 (unsigned int) ntohs(packet.length));
759 return;
760 }
761#ifdef DEBUGGING_ENABLED
762 if (conn->debugFile) {
763 dumpPacket(conn->debugFile, &packet, "RCVD");
764 fprintf(conn->debugFile, "\n");
765 fflush(conn->debugFile);
766 }
767#endif
768
769#ifdef USE_BPF
770 /* Make sure this is a session packet before processing further */
771 type = etherType(&packet);
772 if (type == Eth_PPPOE_Discovery) {
773 sessionDiscoveryPacket(&packet);
774 } else if (type != Eth_PPPOE_Session) {
775 return;
776 }
777#endif
778
779 /* Sanity check */
780 if (packet.code != CODE_SESS) {
781 syslog(LOG_ERR, "Unexpected packet code %d", (int) packet.code);
782 return;
783 }
784 if (packet.ver != 1) {
785 syslog(LOG_ERR, "Unexpected packet version %d", (int) packet.ver);
786 return;
787 }
788 if (packet.type != 1) {
789 syslog(LOG_ERR, "Unexpected packet type %d", (int) packet.type);
790 return;
791 }
792 if (memcmp(packet.ethHdr.h_dest, conn->myEth, ETH_ALEN)) {
793 return;
794 }
795 if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) {
796 /* Not for us -- must be another session. This is not an error,
797 so don't log anything. */
798 return;
799 }
800
801 if (packet.session != conn->session) {
802 /* Not for us -- must be another session. This is not an error,
803 so don't log anything. */
804 return;
805 }
806 plen = ntohs(packet.length);
807 if (plen + HDR_SIZE > (unsigned int)len) {
808 syslog(LOG_ERR, "Bogus length field in session packet %d (%d)",
809 (int) plen, (int) len);
810 return;
811 }
812
813 /* Clamp MSS */
814 if (clampMss) {
815 clampMSS(&packet, "incoming", clampMss);
816 }
817
818 /* Compute FCS */
819 fcs = pppFCS16(PPPINITFCS16, header, 2);
820 fcs = pppFCS16(fcs, packet.payload, plen) ^ 0xffff;
821 tail[0] = fcs & 0x00ff;
822 tail[1] = (fcs >> 8) & 0x00ff;
823
824 /* Build a buffer to send to PPP */
825 *ptr++ = FRAME_FLAG;
826 *ptr++ = FRAME_ADDR;
827 *ptr++ = FRAME_ESC;
828 *ptr++ = FRAME_CTRL ^ FRAME_ENC;
829
830 for (i=0; i<plen; i++) {
831 c = packet.payload[i];
832 if (c == FRAME_FLAG || c == FRAME_ADDR || c == FRAME_ESC || c < 0x20) {
833 *ptr++ = FRAME_ESC;
834 *ptr++ = c ^ FRAME_ENC;
835 } else {
836 *ptr++ = c;
837 }
838 }
839 for (i=0; i<2; i++) {
840 c = tail[i];
841 if (c == FRAME_FLAG || c == FRAME_ADDR || c == FRAME_ESC || c < 0x20) {
842 *ptr++ = FRAME_ESC;
843 *ptr++ = c ^ FRAME_ENC;
844 } else {
845 *ptr++ = c;
846 }
847 }
848 *ptr++ = FRAME_FLAG;
849
850 /* Ship it out */
851 if (write(1, pppBuf, (ptr-pppBuf)) < 0) {
852 fatalSys("asyncReadFromEth: write");
853 }
854}
855
856/**********************************************************************
857*%FUNCTION: syncReadFromEth
858*%ARGUMENTS:
859* conn -- PPPoE connection info
860* sock -- Ethernet socket
861* clampMss -- if true, clamp MSS.
862*%RETURNS:
863* Nothing
864*%DESCRIPTION:
865* Reads a packet from the Ethernet interface and sends it to sync PPP
866* device.
867***********************************************************************/
868void
869syncReadFromEth(PPPoEConnection *conn, int sock, int clampMss)
870{
871 PPPoEPacket packet;
872 int len;
873 int plen;
874 struct iovec vec[2];
875 unsigned char dummy[2];
876#ifdef USE_BPF
877 int type;
878#endif
879
880 if (receivePacket(sock, &packet, &len) < 0) {
881 return;
882 }
883
884 /* Check length */
885 if (ntohs(packet.length) + HDR_SIZE > (unsigned int)len) {
886 syslog(LOG_ERR, "Bogus PPPoE length field (%u)",
887 (unsigned int) ntohs(packet.length));
888 return;
889 }
890#ifdef DEBUGGING_ENABLED
891 if (conn->debugFile) {
892 dumpPacket(conn->debugFile, &packet, "RCVD");
893 fprintf(conn->debugFile, "\n");
894 fflush(conn->debugFile);
895 }
896#endif
897
898#ifdef USE_BPF
899 /* Make sure this is a session packet before processing further */
900 type = etherType(&packet);
901 if (type == Eth_PPPOE_Discovery) {
902 sessionDiscoveryPacket(&packet);
903 } else if (type != Eth_PPPOE_Session) {
904 return;
905 }
906#endif
907
908 /* Sanity check */
909 if (packet.code != CODE_SESS) {
910 syslog(LOG_ERR, "Unexpected packet code %d", (int) packet.code);
911 return;
912 }
913 if (packet.ver != 1) {
914 syslog(LOG_ERR, "Unexpected packet version %d", (int) packet.ver);
915 return;
916 }
917 if (packet.type != 1) {
918 syslog(LOG_ERR, "Unexpected packet type %d", (int) packet.type);
919 return;
920 }
921 if (memcmp(packet.ethHdr.h_dest, conn->myEth, 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 (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) {
927 /* Not for us -- must be another session. This is not an error,
928 so don't log anything. */
929 return;
930 }
931 if (packet.session != conn->session) {
932 /* Not for us -- must be another session. This is not an error,
933 so don't log anything. */
934 return;
935 }
936 plen = ntohs(packet.length);
937 if (plen + HDR_SIZE > (unsigned int)len) {
938 syslog(LOG_ERR, "Bogus length field in session packet %d (%d)",
939 (int) plen, (int) len);
940 return;
941 }
942
943 /* Clamp MSS */
944 if (clampMss) {
945 clampMSS(&packet, "incoming", clampMss);
946 }
947
948 /* Ship it out */
949 vec[0].iov_base = (void *) dummy;
950 dummy[0] = FRAME_ADDR;
951 dummy[1] = FRAME_CTRL;
952 vec[0].iov_len = 2;
953 vec[1].iov_base = (void *) packet.payload;
954 vec[1].iov_len = plen;
955
956 if (writev(1, vec, 2) < 0) {
957 fatalSys("syncReadFromEth: write");
958 }
959}
960