blob: 6fc848cd0dae299014993ca0b234a9daba5f874e
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 | |
16 | static 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 */ |
65 | int optInactivityTimeout = 0; /* Inactivity timeout */ |
66 | int optClampMSS = 0; /* Clamp MSS to this value */ |
67 | int optSkipSession = 0; /* Perform discovery, print session info |
68 | and exit */ |
69 | int 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 | |
73 | PPPoEConnection *Connection = NULL; /* Must be global -- used |
74 | in signal handler */ |
75 | |
76 | int 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 | ***********************************************************************/ |
88 | void |
89 | sendSessionPacket(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 | ***********************************************************************/ |
129 | static void |
130 | sessionDiscoveryPacket(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 | ***********************************************************************/ |
168 | static void |
169 | sessionDiscoveryPacket(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 | ***********************************************************************/ |
228 | void |
229 | session(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 | ***********************************************************************/ |
337 | static void |
338 | sigPADT(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 | ***********************************************************************/ |
355 | void |
356 | usage(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 | ***********************************************************************/ |
403 | int |
404 | main(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, "discovery\n"); |
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(SIGKILL, sigPADT); |
668 | session(&conn); |
669 | return 0; |
670 | } |
671 | |
672 | /********************************************************************** |
673 | *%FUNCTION: fatalSys |
674 | *%ARGUMENTS: |
675 | * str -- error message |
676 | *%RETURNS: |
677 | * Nothing |
678 | *%DESCRIPTION: |
679 | * Prints a message plus the errno value to stderr and syslog and exits. |
680 | ***********************************************************************/ |
681 | void |
682 | fatalSys(char const *str) |
683 | { |
684 | char buf[1024]; |
685 | sprintf(buf, "%.256s: Session %d: %.256s", |
686 | str, (int) ntohs(Connection->session), strerror(errno)); |
687 | printErr(buf); |
688 | sendPADTf(Connection, "RP-PPPoE: System call error: %s", |
689 | strerror(errno)); |
690 | exit(EXIT_FAILURE); |
691 | } |
692 | |
693 | /********************************************************************** |
694 | *%FUNCTION: sysErr |
695 | *%ARGUMENTS: |
696 | * str -- error message |
697 | *%RETURNS: |
698 | * Nothing |
699 | *%DESCRIPTION: |
700 | * Prints a message plus the errno value to syslog. |
701 | ***********************************************************************/ |
702 | void |
703 | sysErr(char const *str) |
704 | { |
705 | char buf[1024]; |
706 | sprintf(buf, "%.256s: %.256s", str, strerror(errno)); |
707 | printErr(buf); |
708 | } |
709 | |
710 | /********************************************************************** |
711 | *%FUNCTION: rp_fatal |
712 | *%ARGUMENTS: |
713 | * str -- error message |
714 | *%RETURNS: |
715 | * Nothing |
716 | *%DESCRIPTION: |
717 | * Prints a message to stderr and syslog and exits. |
718 | ***********************************************************************/ |
719 | void |
720 | rp_fatal(char const *str) |
721 | { |
722 | printErr(str); |
723 | sendPADTf(Connection, "RP-PPPoE: Session %d: %.256s", |
724 | (int) ntohs(Connection->session), str); |
725 | exit(EXIT_FAILURE); |
726 | } |
727 | |
728 | /********************************************************************** |
729 | *%FUNCTION: asyncReadFromEth |
730 | *%ARGUMENTS: |
731 | * conn -- PPPoE connection info |
732 | * sock -- Ethernet socket |
733 | * clampMss -- if non-zero, do MSS-clamping |
734 | *%RETURNS: |
735 | * Nothing |
736 | *%DESCRIPTION: |
737 | * Reads a packet from the Ethernet interface and sends it to async PPP |
738 | * device. |
739 | ***********************************************************************/ |
740 | void |
741 | asyncReadFromEth(PPPoEConnection *conn, int sock, int clampMss) |
742 | { |
743 | PPPoEPacket packet; |
744 | int len; |
745 | int plen; |
746 | int i; |
747 | unsigned char pppBuf[4096]; |
748 | unsigned char *ptr = pppBuf; |
749 | unsigned char c; |
750 | UINT16_t fcs; |
751 | unsigned char header[2] = {FRAME_ADDR, FRAME_CTRL}; |
752 | unsigned char tail[2]; |
753 | #ifdef USE_BPF |
754 | int type; |
755 | #endif |
756 | |
757 | if (receivePacket(sock, &packet, &len) < 0) { |
758 | return; |
759 | } |
760 | |
761 | /* Check length */ |
762 | if (ntohs(packet.length) + HDR_SIZE > (unsigned int)len) { |
763 | syslog(LOG_ERR, "Bogus PPPoE length field (%u)", |
764 | (unsigned int) ntohs(packet.length)); |
765 | return; |
766 | } |
767 | #ifdef DEBUGGING_ENABLED |
768 | if (conn->debugFile) { |
769 | dumpPacket(conn->debugFile, &packet, "RCVD"); |
770 | fprintf(conn->debugFile, "\n"); |
771 | fflush(conn->debugFile); |
772 | } |
773 | #endif |
774 | |
775 | #ifdef USE_BPF |
776 | /* Make sure this is a session packet before processing further */ |
777 | type = etherType(&packet); |
778 | if (type == Eth_PPPOE_Discovery) { |
779 | sessionDiscoveryPacket(&packet); |
780 | } else if (type != Eth_PPPOE_Session) { |
781 | return; |
782 | } |
783 | #endif |
784 | |
785 | /* Sanity check */ |
786 | if (packet.code != CODE_SESS) { |
787 | syslog(LOG_ERR, "Unexpected packet code %d", (int) packet.code); |
788 | return; |
789 | } |
790 | if (packet.ver != 1) { |
791 | syslog(LOG_ERR, "Unexpected packet version %d", (int) packet.ver); |
792 | return; |
793 | } |
794 | if (packet.type != 1) { |
795 | syslog(LOG_ERR, "Unexpected packet type %d", (int) packet.type); |
796 | return; |
797 | } |
798 | if (memcmp(packet.ethHdr.h_dest, conn->myEth, ETH_ALEN)) { |
799 | return; |
800 | } |
801 | if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) { |
802 | /* Not for us -- must be another session. This is not an error, |
803 | so don't log anything. */ |
804 | return; |
805 | } |
806 | |
807 | if (packet.session != conn->session) { |
808 | /* Not for us -- must be another session. This is not an error, |
809 | so don't log anything. */ |
810 | return; |
811 | } |
812 | plen = ntohs(packet.length); |
813 | if (plen + HDR_SIZE > (unsigned int)len) { |
814 | syslog(LOG_ERR, "Bogus length field in session packet %d (%d)", |
815 | (int) plen, (int) len); |
816 | return; |
817 | } |
818 | |
819 | /* Clamp MSS */ |
820 | if (clampMss) { |
821 | clampMSS(&packet, "incoming", clampMss); |
822 | } |
823 | |
824 | /* Compute FCS */ |
825 | fcs = pppFCS16(PPPINITFCS16, header, 2); |
826 | fcs = pppFCS16(fcs, packet.payload, plen) ^ 0xffff; |
827 | tail[0] = fcs & 0x00ff; |
828 | tail[1] = (fcs >> 8) & 0x00ff; |
829 | |
830 | /* Build a buffer to send to PPP */ |
831 | *ptr++ = FRAME_FLAG; |
832 | *ptr++ = FRAME_ADDR; |
833 | *ptr++ = FRAME_ESC; |
834 | *ptr++ = FRAME_CTRL ^ FRAME_ENC; |
835 | |
836 | for (i=0; i<plen; i++) { |
837 | c = packet.payload[i]; |
838 | if (c == FRAME_FLAG || c == FRAME_ADDR || c == FRAME_ESC || c < 0x20) { |
839 | *ptr++ = FRAME_ESC; |
840 | *ptr++ = c ^ FRAME_ENC; |
841 | } else { |
842 | *ptr++ = c; |
843 | } |
844 | } |
845 | for (i=0; i<2; i++) { |
846 | c = tail[i]; |
847 | if (c == FRAME_FLAG || c == FRAME_ADDR || c == FRAME_ESC || c < 0x20) { |
848 | *ptr++ = FRAME_ESC; |
849 | *ptr++ = c ^ FRAME_ENC; |
850 | } else { |
851 | *ptr++ = c; |
852 | } |
853 | } |
854 | *ptr++ = FRAME_FLAG; |
855 | |
856 | /* Ship it out */ |
857 | if (write(1, pppBuf, (ptr-pppBuf)) < 0) { |
858 | fatalSys("asyncReadFromEth: write"); |
859 | } |
860 | } |
861 | |
862 | /********************************************************************** |
863 | *%FUNCTION: syncReadFromEth |
864 | *%ARGUMENTS: |
865 | * conn -- PPPoE connection info |
866 | * sock -- Ethernet socket |
867 | * clampMss -- if true, clamp MSS. |
868 | *%RETURNS: |
869 | * Nothing |
870 | *%DESCRIPTION: |
871 | * Reads a packet from the Ethernet interface and sends it to sync PPP |
872 | * device. |
873 | ***********************************************************************/ |
874 | void |
875 | syncReadFromEth(PPPoEConnection *conn, int sock, int clampMss) |
876 | { |
877 | PPPoEPacket packet; |
878 | int len; |
879 | int plen; |
880 | struct iovec vec[2]; |
881 | unsigned char dummy[2]; |
882 | #ifdef USE_BPF |
883 | int type; |
884 | #endif |
885 | |
886 | if (receivePacket(sock, &packet, &len) < 0) { |
887 | return; |
888 | } |
889 | |
890 | /* Check length */ |
891 | if (ntohs(packet.length) + HDR_SIZE > (unsigned int)len) { |
892 | syslog(LOG_ERR, "Bogus PPPoE length field (%u)", |
893 | (unsigned int) ntohs(packet.length)); |
894 | return; |
895 | } |
896 | #ifdef DEBUGGING_ENABLED |
897 | if (conn->debugFile) { |
898 | dumpPacket(conn->debugFile, &packet, "RCVD"); |
899 | fprintf(conn->debugFile, "\n"); |
900 | fflush(conn->debugFile); |
901 | } |
902 | #endif |
903 | |
904 | #ifdef USE_BPF |
905 | /* Make sure this is a session packet before processing further */ |
906 | type = etherType(&packet); |
907 | if (type == Eth_PPPOE_Discovery) { |
908 | sessionDiscoveryPacket(&packet); |
909 | } else if (type != Eth_PPPOE_Session) { |
910 | return; |
911 | } |
912 | #endif |
913 | |
914 | /* Sanity check */ |
915 | if (packet.code != CODE_SESS) { |
916 | syslog(LOG_ERR, "Unexpected packet code %d", (int) packet.code); |
917 | return; |
918 | } |
919 | if (packet.ver != 1) { |
920 | syslog(LOG_ERR, "Unexpected packet version %d", (int) packet.ver); |
921 | return; |
922 | } |
923 | if (packet.type != 1) { |
924 | syslog(LOG_ERR, "Unexpected packet type %d", (int) packet.type); |
925 | return; |
926 | } |
927 | if (memcmp(packet.ethHdr.h_dest, conn->myEth, ETH_ALEN)) { |
928 | /* Not for us -- must be another session. This is not an error, |
929 | so don't log anything. */ |
930 | return; |
931 | } |
932 | if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) { |
933 | /* Not for us -- must be another session. This is not an error, |
934 | so don't log anything. */ |
935 | return; |
936 | } |
937 | if (packet.session != conn->session) { |
938 | /* Not for us -- must be another session. This is not an error, |
939 | so don't log anything. */ |
940 | return; |
941 | } |
942 | plen = ntohs(packet.length); |
943 | if (plen + HDR_SIZE > (unsigned int)len) { |
944 | syslog(LOG_ERR, "Bogus length field in session packet %d (%d)", |
945 | (int) plen, (int) len); |
946 | return; |
947 | } |
948 | |
949 | /* Clamp MSS */ |
950 | if (clampMss) { |
951 | clampMSS(&packet, "incoming", clampMss); |
952 | } |
953 | |
954 | /* Ship it out */ |
955 | vec[0].iov_base = (void *) dummy; |
956 | dummy[0] = FRAME_ADDR; |
957 | dummy[1] = FRAME_CTRL; |
958 | vec[0].iov_len = 2; |
959 | vec[1].iov_base = (void *) packet.payload; |
960 | vec[1].iov_len = plen; |
961 | |
962 | if (writev(1, vec, 2) < 0) { |
963 | fatalSys("syncReadFromEth: write"); |
964 | } |
965 | } |
966 |