Busybox: Upgrade to 1.21.1 (stable). lsof active.
[tomato.git] / release / src / router / rp-pppoe / src / plugin.c
blob99ab24e60d87b5c4896a1161d3e2a71f20f1e83c
1 /***********************************************************************
3 * plugin.c
5 * pppd plugin for kernel-mode PPPoE on Linux
7 * Copyright (C) 2001-2012 by Roaring Penguin Software Inc.
8 * Portions copyright 2000 Michal Ostrowski and Jamal Hadi Salim.
10 * Much code and many ideas derived from pppoe plugin by Michal
11 * Ostrowski and Jamal Hadi Salim, which carries this copyright:
13 * Copyright 2000 Michal Ostrowski <mostrows@styx.uwaterloo.ca>,
14 * Jamal Hadi Salim <hadi@cyberus.ca>
15 * Borrows heavily from the PPPoATM plugin by Mitchell Blank Jr.,
16 * which is based in part on work from Jens Axboe and Paul Mackerras.
18 * This program is free software; you can redistribute it and/or
19 * modify it under the terms of the GNU General Public License
20 * as published by the Free Software Foundation; either version
21 * 2 of the License, or (at your option) any later version.
23 * LIC: GPL
25 ***********************************************************************/
27 static char const RCSID[] =
28 "$Id$";
30 #define _GNU_SOURCE 1
31 #include "pppoe.h"
33 #include "pppd/pppd.h"
34 #include "pppd/fsm.h"
35 #include "pppd/lcp.h"
36 #include "pppd/ipcp.h"
37 #include "pppd/ccp.h"
38 /* #include "pppd/pathnames.h" */
40 #include <linux/types.h>
41 #include <syslog.h>
42 #include <sys/ioctl.h>
43 #include <sys/types.h>
44 #include <sys/socket.h>
45 #include <sys/stat.h>
46 #include <string.h>
47 #include <stdlib.h>
48 #include <errno.h>
49 #include <unistd.h>
50 #include <fcntl.h>
51 #include <signal.h>
52 #include <net/ethernet.h>
53 #include <net/if_arp.h>
54 #include <linux/ppp_defs.h>
55 #include <linux/if_pppox.h>
57 #ifndef _ROOT_PATH
58 #define _ROOT_PATH ""
59 #endif
61 #define _PATH_ETHOPT _ROOT_PATH "/etc/ppp/options."
63 char pppd_version[] = VERSION;
65 static int seen_devnam[2] = {0, 0};
66 static char *pppoe_reqd_mac = NULL;
68 /* From sys-linux.c in pppd -- MUST FIX THIS! */
69 extern int new_style_driver;
71 char *pppd_pppoe_service = NULL;
72 static char *acName = NULL;
73 static char *existingSession = NULL;
74 static int printACNames = 0;
76 static int PPPoEDevnameHook(char *cmd, char **argv, int doit);
77 static option_t Options[] = {
78 { "device name", o_wild, (void *) &PPPoEDevnameHook,
79 "PPPoE device name",
80 OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG | OPT_A2STRVAL | OPT_STATIC,
81 devnam},
82 { "rp_pppoe_service", o_string, &pppd_pppoe_service,
83 "Desired PPPoE service name" },
84 { "rp_pppoe_ac", o_string, &acName,
85 "Desired PPPoE access concentrator name" },
86 { "rp_pppoe_sess", o_string, &existingSession,
87 "Attach to existing session (sessid:macaddr)" },
88 { "rp_pppoe_verbose", o_int, &printACNames,
89 "Be verbose about discovered access concentrators"},
90 { "rp_pppoe_mac", o_string, &pppoe_reqd_mac,
91 "Only connect to specified MAC address" },
92 { NULL }
94 int (*OldDevnameHook)(char *cmd, char **argv, int doit) = NULL;
95 static PPPoEConnection *conn = NULL;
97 /**********************************************************************
98 * %FUNCTION: PPPOEInitDevice
99 * %ARGUMENTS:
100 * None
101 * %RETURNS:
103 * %DESCRIPTION:
104 * Initializes PPPoE device.
105 ***********************************************************************/
106 static int
107 PPPOEInitDevice(void)
109 conn = malloc(sizeof(PPPoEConnection));
110 if (!conn) {
111 fatal("Could not allocate memory for PPPoE session");
113 memset(conn, 0, sizeof(PPPoEConnection));
114 if (acName) {
115 SET_STRING(conn->acName, acName);
117 if (pppd_pppoe_service) {
118 SET_STRING(conn->serviceName, pppd_pppoe_service);
120 SET_STRING(conn->ifName, devnam);
121 conn->discoverySocket = -1;
122 conn->sessionSocket = -1;
123 conn->useHostUniq = 1;
124 conn->printACNames = printACNames;
125 conn->discoveryTimeout = PADI_TIMEOUT;
126 return 1;
129 /**********************************************************************
130 * %FUNCTION: PPPOEConnectDevice
131 * %ARGUMENTS:
132 * None
133 * %RETURNS:
134 * Non-negative if all goes well; -1 otherwise
135 * %DESCRIPTION:
136 * Connects PPPoE device.
137 ***********************************************************************/
138 static int
139 PPPOEConnectDevice(void)
141 struct sockaddr_pppox sp;
142 struct ifreq ifr;
143 int s;
145 /* Restore configuration */
146 lcp_allowoptions[0].mru = conn->mtu;
147 lcp_wantoptions[0].mru = conn->mru;
149 /* Update maximum MRU */
150 s = socket(AF_INET, SOCK_DGRAM, 0);
151 if (s < 0) {
152 error("Can't get MTU for %s: %m", conn->ifName);
153 return -1;
155 strncpy(ifr.ifr_name, conn->ifName, sizeof(ifr.ifr_name));
156 if (ioctl(s, SIOCGIFMTU, &ifr) < 0) {
157 error("Can't get MTU for %s: %m", conn->ifName);
158 close(s);
159 return -1;
161 close(s);
163 if (lcp_allowoptions[0].mru > ifr.ifr_mtu - TOTAL_OVERHEAD) {
164 lcp_allowoptions[0].mru = ifr.ifr_mtu - TOTAL_OVERHEAD;
166 if (lcp_wantoptions[0].mru > ifr.ifr_mtu - TOTAL_OVERHEAD) {
167 lcp_wantoptions[0].mru = ifr.ifr_mtu - TOTAL_OVERHEAD;
170 /* Open session socket before discovery phase, to avoid losing session */
171 /* packets sent by peer just after PADS packet (noted on some Cisco */
172 /* server equipment). */
173 /* Opening this socket just before waitForPADS in the discovery() */
174 /* function would be more appropriate, but it would mess-up the code */
175 conn->sessionSocket = socket(AF_PPPOX, SOCK_STREAM, PX_PROTO_OE);
176 if (conn->sessionSocket < 0) {
177 error("Failed to create PPPoE socket: %m");
178 return -1;
181 if (acName) {
182 SET_STRING(conn->acName, acName);
184 if (pppd_pppoe_service) {
185 SET_STRING(conn->serviceName, pppd_pppoe_service);
188 strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));
189 if (existingSession) {
190 unsigned int mac[ETH_ALEN];
191 int i, ses;
192 if (sscanf(existingSession, "%d:%x:%x:%x:%x:%x:%x",
193 &ses, &mac[0], &mac[1], &mac[2],
194 &mac[3], &mac[4], &mac[5]) != 7) {
195 fatal("Illegal value for rp_pppoe_sess option");
197 conn->session = htons(ses);
198 for (i=0; i<ETH_ALEN; i++) {
199 conn->peerEth[i] = (unsigned char) mac[i];
201 } else {
202 conn->discoverySocket =
203 openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth, NULL);
204 discovery(conn);
205 if (conn->discoveryState != STATE_SESSION) {
206 error("Unable to complete PPPoE Discovery");
207 return -1;
211 /* Set PPPoE session-number for further consumption */
212 ppp_session_number = ntohs(conn->session);
214 sp.sa_family = AF_PPPOX;
215 sp.sa_protocol = PX_PROTO_OE;
216 sp.sa_addr.pppoe.sid = conn->session;
217 memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ);
218 memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN);
220 /* Set remote_number for ServPoET */
221 sprintf(remote_number, "%02X:%02X:%02X:%02X:%02X:%02X",
222 (unsigned) conn->peerEth[0],
223 (unsigned) conn->peerEth[1],
224 (unsigned) conn->peerEth[2],
225 (unsigned) conn->peerEth[3],
226 (unsigned) conn->peerEth[4],
227 (unsigned) conn->peerEth[5]);
229 warn("Connected to %02X:%02X:%02X:%02X:%02X:%02X via interface %s",
230 (unsigned) conn->peerEth[0],
231 (unsigned) conn->peerEth[1],
232 (unsigned) conn->peerEth[2],
233 (unsigned) conn->peerEth[3],
234 (unsigned) conn->peerEth[4],
235 (unsigned) conn->peerEth[5],
236 conn->ifName);
238 script_setenv("MACREMOTE", remote_number, 0);
240 if (connect(conn->sessionSocket, (struct sockaddr *) &sp,
241 sizeof(struct sockaddr_pppox)) < 0) {
242 error("Failed to connect PPPoE socket: %d %m", errno);
243 return -1;
246 return conn->sessionSocket;
249 static void
250 PPPOESendConfig(int mtu,
251 u_int32_t asyncmap,
252 int pcomp,
253 int accomp)
255 int sock;
256 struct ifreq ifr;
258 if (mtu > MAX_PPPOE_MTU) {
259 warn("Couldn't increase MTU to %d", mtu);
260 mtu = MAX_PPPOE_MTU;
262 sock = socket(AF_INET, SOCK_DGRAM, 0);
263 if (sock < 0) {
264 warn("Couldn't create IP socket: %m");
265 return;
267 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
268 ifr.ifr_mtu = mtu;
269 if (ioctl(sock, SIOCSIFMTU, &ifr) < 0) {
270 warn("ioctl(SIOCSIFMTU): %m");
271 return;
273 (void) close (sock);
277 static void
278 PPPOERecvConfig(int mru,
279 u_int32_t asyncmap,
280 int pcomp,
281 int accomp)
283 if (mru > MAX_PPPOE_MTU) {
284 warn("Couldn't increase MRU to %d", mru);
288 /**********************************************************************
289 * %FUNCTION: PPPOEDisconnectDevice
290 * %ARGUMENTS:
291 * None
292 * %RETURNS:
293 * Nothing
294 * %DESCRIPTION:
295 * Disconnects PPPoE device
296 ***********************************************************************/
297 static void
298 PPPOEDisconnectDevice(void)
300 struct sockaddr_pppox sp;
302 sp.sa_family = AF_PPPOX;
303 sp.sa_protocol = PX_PROTO_OE;
304 sp.sa_addr.pppoe.sid = 0;
305 memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ);
306 memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN);
307 if (connect(conn->sessionSocket, (struct sockaddr *) &sp,
308 sizeof(struct sockaddr_pppox)) < 0) {
309 fatal("Failed to disconnect PPPoE socket: %d %m", errno);
310 return;
312 close(conn->sessionSocket);
313 close(conn->discoverySocket);
317 static void
318 PPPOEDeviceOptions(void)
320 char buf[256];
321 snprintf(buf, 256, _PATH_ETHOPT "%s",devnam);
322 if(!options_from_file(buf, 0, 0, 1))
323 exit(EXIT_OPTION_ERROR);
327 struct channel pppoe_channel;
329 /**********************************************************************
330 * %FUNCTION: PPPoEDevnameHook
331 * %ARGUMENTS:
332 * cmd -- the command (actually, the device name
333 * argv -- argument vector
334 * doit -- if non-zero, set device name. Otherwise, just check if possible
335 * %RETURNS:
336 * 1 if we will handle this device; 0 otherwise.
337 * %DESCRIPTION:
338 * Checks if name is a valid interface name; if so, returns 1. Also
339 * sets up devnam (string representation of device).
340 ***********************************************************************/
341 static int
342 PPPoEDevnameHook(char *cmd, char **argv, int doit)
344 int r = 1;
345 int fd;
346 struct ifreq ifr;
347 int seen_idx = doit ? 1 : 0;
349 /* If "devnam" has already been set, ignore.
350 This prevents kernel from doing modprobes against random
351 pppd arguments that happen to begin with "nic-", "eth" or "br"
353 Ideally, "nix-ethXXX" should be supplied immediately after
354 "plugin rp-pppoe.so"
356 Patch based on suggestion from Mike Ireton.
358 if (seen_devnam[seen_idx]) {
359 if (OldDevnameHook) return OldDevnameHook(cmd, argv, doit);
360 return 0;
363 /* Only do it if name is "ethXXX" or "brXXX" or what was specified
364 by rp_pppoe_dev option (ugh). */
365 /* Can also specify nic-XXXX in which case the nic- is stripped off. */
366 if (!strncmp(cmd, "nic-", 4)) {
367 cmd += 4;
368 } else {
369 if (strncmp(cmd, "eth", 3) &&
370 strncmp(cmd, "br", 2)) {
371 if (OldDevnameHook) return OldDevnameHook(cmd, argv, doit);
372 return 0;
376 /* Open a socket */
377 if ((fd = socket(PF_PACKET, SOCK_RAW, 0)) < 0) {
378 r = 0;
381 /* Try getting interface index */
382 if (r) {
383 strncpy(ifr.ifr_name, cmd, IFNAMSIZ);
384 if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
385 r = 0;
386 } else {
387 if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
388 r = 0;
389 } else {
390 if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
391 error("Interface %s not Ethernet", cmd);
392 r=0;
398 /* Close socket */
399 close(fd);
400 if (r) {
401 seen_devnam[seen_idx] = 1;
402 if (doit) {
403 strncpy(devnam, cmd, sizeof(devnam));
404 if (the_channel != &pppoe_channel) {
406 the_channel = &pppoe_channel;
407 modem = 0;
409 lcp_allowoptions[0].neg_accompression = 0;
410 lcp_wantoptions[0].neg_accompression = 0;
412 lcp_allowoptions[0].neg_asyncmap = 0;
413 lcp_wantoptions[0].neg_asyncmap = 0;
415 lcp_allowoptions[0].neg_pcompression = 0;
416 lcp_wantoptions[0].neg_pcompression = 0;
418 ipcp_allowoptions[0].neg_vj=0;
419 ipcp_wantoptions[0].neg_vj=0;
421 ccp_allowoptions[0].deflate = 0 ;
422 ccp_wantoptions[0].deflate = 0 ;
424 ccp_allowoptions[0].bsd_compress = 0;
425 ccp_wantoptions[0].bsd_compress = 0;
428 PPPOEInitDevice();
431 return 1;
434 if (OldDevnameHook) r = OldDevnameHook(cmd, argv, doit);
435 return r;
438 /**********************************************************************
439 * %FUNCTION: plugin_init
440 * %ARGUMENTS:
441 * None
442 * %RETURNS:
443 * Nothing
444 * %DESCRIPTION:
445 * Initializes hooks for pppd plugin
446 ***********************************************************************/
447 void
448 plugin_init(void)
450 if (!ppp_available() && !new_style_driver) {
451 fatal("Linux kernel does not support PPPoE -- are you running 2.4.x?");
454 add_options(Options);
456 info("RP-PPPoE plugin version %s compiled against pppd %s",
457 RP_VERSION, VERSION);
460 /**********************************************************************
461 *%FUNCTION: fatalSys
462 *%ARGUMENTS:
463 * str -- error message
464 *%RETURNS:
465 * Nothing
466 *%DESCRIPTION:
467 * Prints a message plus the errno value to stderr and syslog and exits.
469 ***********************************************************************/
470 void
471 fatalSys(char const *str)
473 char buf[1024];
474 int i = errno;
475 sprintf(buf, "%.256s: %.256s", str, strerror(i));
476 printErr(buf);
477 sprintf(buf, "RP-PPPoE: %.256s: %.256s", str, strerror(i));
478 sendPADT(conn, buf);
479 exit(1);
482 /**********************************************************************
483 *%FUNCTION: rp_fatal
484 *%ARGUMENTS:
485 * str -- error message
486 *%RETURNS:
487 * Nothing
488 *%DESCRIPTION:
489 * Prints a message to stderr and syslog and exits.
490 ***********************************************************************/
491 void
492 rp_fatal(char const *str)
494 printErr(str);
495 sendPADTf(conn, "RP-PPPoE: %.256s", str);
496 exit(1);
499 /**********************************************************************
500 *%FUNCTION: sysErr
501 *%ARGUMENTS:
502 * str -- error message
503 *%RETURNS:
504 * Nothing
505 *%DESCRIPTION:
506 * Prints a message plus the errno value to syslog.
507 ***********************************************************************/
508 void
509 sysErr(char const *str)
511 rp_fatal(str);
514 void pppoe_check_options(void)
516 unsigned int mac[ETH_ALEN];
517 int i;
519 if (pppoe_reqd_mac != NULL) {
520 if (sscanf(pppoe_reqd_mac, "%x:%x:%x:%x:%x:%x",
521 &mac[0], &mac[1], &mac[2], &mac[3],
522 &mac[4], &mac[5]) != ETH_ALEN) {
523 option_error("cannot parse pppoe-mac option value");
524 exit(EXIT_OPTION_ERROR);
526 for (i = 0; i < 6; ++i)
527 conn->req_peer_mac[i] = mac[i];
528 conn->req_peer = 1;
531 lcp_allowoptions[0].neg_accompression = 0;
532 lcp_wantoptions[0].neg_accompression = 0;
534 lcp_allowoptions[0].neg_asyncmap = 0;
535 lcp_wantoptions[0].neg_asyncmap = 0;
537 lcp_allowoptions[0].neg_pcompression = 0;
538 lcp_wantoptions[0].neg_pcompression = 0;
540 if (lcp_allowoptions[0].mru > MAX_PPPOE_MTU) {
541 lcp_allowoptions[0].mru = MAX_PPPOE_MTU;
543 if (lcp_wantoptions[0].mru > MAX_PPPOE_MTU) {
544 lcp_wantoptions[0].mru = MAX_PPPOE_MTU;
547 /* Save configuration */
548 conn->mtu = lcp_allowoptions[0].mru;
549 conn->mru = lcp_wantoptions[0].mru;
551 ccp_allowoptions[0].deflate = 0;
552 ccp_wantoptions[0].deflate = 0;
554 ipcp_allowoptions[0].neg_vj = 0;
555 ipcp_wantoptions[0].neg_vj = 0;
557 ccp_allowoptions[0].bsd_compress = 0;
558 ccp_wantoptions[0].bsd_compress = 0;
561 struct channel pppoe_channel = {
562 .options = Options,
563 .process_extra_options = &PPPOEDeviceOptions,
564 .check_options = &pppoe_check_options,
565 .connect = &PPPOEConnectDevice,
566 .disconnect = &PPPOEDisconnectDevice,
567 .establish_ppp = &generic_establish_ppp,
568 .disestablish_ppp = &generic_disestablish_ppp,
569 .send_config = &PPPOESendConfig,
570 .recv_config = &PPPOERecvConfig,
571 .close = NULL,
572 .cleanup = NULL