1 /***********************************************************************
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.
25 ***********************************************************************/
27 static char const RCSID
[] =
33 #include "pppd/pppd.h"
36 #include "pppd/ipcp.h"
38 /* #include "pppd/pathnames.h" */
40 #include <linux/types.h>
42 #include <sys/ioctl.h>
43 #include <sys/types.h>
44 #include <sys/socket.h>
52 #include <net/ethernet.h>
53 #include <net/if_arp.h>
54 #include <linux/ppp_defs.h>
55 #include <linux/if_pppox.h>
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
,
80 OPT_DEVNAM
| OPT_PRIVFIX
| OPT_NOARG
| OPT_A2STRVAL
| OPT_STATIC
,
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" },
94 int (*OldDevnameHook
)(char *cmd
, char **argv
, int doit
) = NULL
;
95 static PPPoEConnection
*conn
= NULL
;
97 /**********************************************************************
98 * %FUNCTION: PPPOEInitDevice
104 * Initializes PPPoE device.
105 ***********************************************************************/
107 PPPOEInitDevice(void)
109 conn
= malloc(sizeof(PPPoEConnection
));
111 fatal("Could not allocate memory for PPPoE session");
113 memset(conn
, 0, sizeof(PPPoEConnection
));
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
;
129 /**********************************************************************
130 * %FUNCTION: PPPOEConnectDevice
134 * Non-negative if all goes well; -1 otherwise
136 * Connects PPPoE device.
137 ***********************************************************************/
139 PPPOEConnectDevice(void)
141 struct sockaddr_pppox sp
;
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);
152 error("Can't get MTU for %s: %m", conn
->ifName
);
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
);
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");
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
];
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
];
202 conn
->discoverySocket
=
203 openInterface(conn
->ifName
, Eth_PPPOE_Discovery
, conn
->myEth
, NULL
);
205 if (conn
->discoveryState
!= STATE_SESSION
) {
206 error("Unable to complete PPPoE Discovery");
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],
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
);
246 return conn
->sessionSocket
;
250 PPPOESendConfig(int mtu
,
258 if (mtu
> MAX_PPPOE_MTU
) {
259 warn("Couldn't increase MTU to %d", mtu
);
262 sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
264 warn("Couldn't create IP socket: %m");
267 strlcpy(ifr
.ifr_name
, ifname
, sizeof(ifr
.ifr_name
));
269 if (ioctl(sock
, SIOCSIFMTU
, &ifr
) < 0) {
270 warn("ioctl(SIOCSIFMTU): %m");
278 PPPOERecvConfig(int mru
,
283 if (mru
> MAX_PPPOE_MTU
) {
284 warn("Couldn't increase MRU to %d", mru
);
288 /**********************************************************************
289 * %FUNCTION: PPPOEDisconnectDevice
295 * Disconnects PPPoE device
296 ***********************************************************************/
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
);
312 close(conn
->sessionSocket
);
313 close(conn
->discoverySocket
);
318 PPPOEDeviceOptions(void)
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
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
336 * 1 if we will handle this device; 0 otherwise.
338 * Checks if name is a valid interface name; if so, returns 1. Also
339 * sets up devnam (string representation of device).
340 ***********************************************************************/
342 PPPoEDevnameHook(char *cmd
, char **argv
, int doit
)
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
356 Patch based on suggestion from Mike Ireton.
358 if (seen_devnam
[seen_idx
]) {
359 if (OldDevnameHook
) return OldDevnameHook(cmd
, argv
, doit
);
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)) {
369 if (strncmp(cmd
, "eth", 3) &&
370 strncmp(cmd
, "br", 2)) {
371 if (OldDevnameHook
) return OldDevnameHook(cmd
, argv
, doit
);
377 if ((fd
= socket(PF_PACKET
, SOCK_RAW
, 0)) < 0) {
381 /* Try getting interface index */
383 strncpy(ifr
.ifr_name
, cmd
, IFNAMSIZ
);
384 if (ioctl(fd
, SIOCGIFINDEX
, &ifr
) < 0) {
387 if (ioctl(fd
, SIOCGIFHWADDR
, &ifr
) < 0) {
390 if (ifr
.ifr_hwaddr
.sa_family
!= ARPHRD_ETHER
) {
391 error("Interface %s not Ethernet", cmd
);
401 seen_devnam
[seen_idx
] = 1;
403 strncpy(devnam
, cmd
, sizeof(devnam
));
404 if (the_channel
!= &pppoe_channel
) {
406 the_channel
= &pppoe_channel
;
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;
434 if (OldDevnameHook
) r
= OldDevnameHook(cmd
, argv
, doit
);
438 /**********************************************************************
439 * %FUNCTION: plugin_init
445 * Initializes hooks for pppd plugin
446 ***********************************************************************/
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 /**********************************************************************
463 * str -- error message
467 * Prints a message plus the errno value to stderr and syslog and exits.
469 ***********************************************************************/
471 fatalSys(char const *str
)
475 sprintf(buf
, "%.256s: %.256s", str
, strerror(i
));
477 sprintf(buf
, "RP-PPPoE: %.256s: %.256s", str
, strerror(i
));
482 /**********************************************************************
485 * str -- error message
489 * Prints a message to stderr and syslog and exits.
490 ***********************************************************************/
492 rp_fatal(char const *str
)
495 sendPADTf(conn
, "RP-PPPoE: %.256s", str
);
499 /**********************************************************************
502 * str -- error message
506 * Prints a message plus the errno value to syslog.
507 ***********************************************************************/
509 sysErr(char const *str
)
514 void pppoe_check_options(void)
516 unsigned int mac
[ETH_ALEN
];
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
];
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
= {
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
,