1 /***********************************************************************
5 * pppd plugin for kernel-mode PPPoE on Linux
7 * Copyright (C) 2001 by Roaring Penguin Software Inc., Michal Ostrowski
8 * 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.
22 ***********************************************************************/
24 static char const RCSID
[] =
25 "$Id: plugin.c,v 1.13 2005/07/09 09:12:48 paulus Exp $";
30 #include "pppd/pppd.h"
33 #include "pppd/ipcp.h"
35 #include "pppd/pathnames.h"
37 #include <linux/types.h>
39 #include <sys/ioctl.h>
40 #include <sys/types.h>
41 #include <sys/socket.h>
49 #include <net/ethernet.h>
50 #include <net/if_arp.h>
55 #define _PATH_ETHOPT _ROOT_PATH "/etc/ppp/options."
57 char pppd_version
[] = VERSION
;
59 /* From sys-linux.c in pppd -- MUST FIX THIS! */
60 extern int new_style_driver
;
62 char *pppd_pppoe_service
= NULL
;
63 static char *acName
= NULL
;
64 static char *existingSession
= NULL
;
65 static int printACNames
= 0;
67 static int PPPoEDevnameHook(char *cmd
, char **argv
, int doit
);
68 static option_t Options
[] = {
69 { "device name", o_wild
, (void *) &PPPoEDevnameHook
,
71 OPT_DEVNAM
| OPT_PRIVFIX
| OPT_NOARG
| OPT_A2STRVAL
| OPT_STATIC
,
73 { "rp_pppoe_service", o_string
, &pppd_pppoe_service
,
74 "Desired PPPoE service name" },
75 { "rp_pppoe_ac", o_string
, &acName
,
76 "Desired PPPoE access concentrator name" },
77 { "rp_pppoe_sess", o_string
, &existingSession
,
78 "Attach to existing session (sessid:macaddr)" },
79 { "rp_pppoe_verbose", o_int
, &printACNames
,
80 "Be verbose about discovered access concentrators"},
84 static PPPoEConnection
*conn
= NULL
;
86 /**********************************************************************
87 * %FUNCTION: PPPOEInitDevice
93 * Initializes PPPoE device.
94 ***********************************************************************/
98 conn
= malloc(sizeof(PPPoEConnection
));
100 fatal("Could not allocate memory for PPPoE session");
102 memset(conn
, 0, sizeof(PPPoEConnection
));
104 SET_STRING(conn
->acName
, acName
);
106 if (pppd_pppoe_service
) {
107 SET_STRING(conn
->serviceName
, pppd_pppoe_service
);
109 SET_STRING(conn
->ifName
, devnam
);
110 conn
->discoverySocket
= -1;
111 conn
->sessionSocket
= -1;
112 conn
->useHostUniq
= 1;
113 conn
->printACNames
= printACNames
;
117 /**********************************************************************
118 * %FUNCTION: PPPOEConnectDevice
122 * Non-negative if all goes well; -1 otherwise
124 * Connects PPPoE device.
125 ***********************************************************************/
127 PPPOEConnectDevice(void)
129 struct sockaddr_pppox sp
;
131 strlcpy(ppp_devnam
, devnam
, sizeof(ppp_devnam
));
132 if (existingSession
) {
133 unsigned int mac
[ETH_ALEN
];
135 if (sscanf(existingSession
, "%d:%x:%x:%x:%x:%x:%x",
136 &ses
, &mac
[0], &mac
[1], &mac
[2],
137 &mac
[3], &mac
[4], &mac
[5]) != 7) {
138 fatal("Illegal value for rp_pppoe_sess option");
140 conn
->session
= htons(ses
);
141 for (i
=0; i
<ETH_ALEN
; i
++) {
142 conn
->peerEth
[i
] = (unsigned char) mac
[i
];
146 if (conn
->discoveryState
!= STATE_SESSION
) {
147 error("Unable to complete PPPoE Discovery");
152 /* Set PPPoE session-number for further consumption */
153 ppp_session_number
= ntohs(conn
->session
);
155 /* Make the session socket */
156 conn
->sessionSocket
= socket(AF_PPPOX
, SOCK_STREAM
, PX_PROTO_OE
);
157 if (conn
->sessionSocket
< 0) {
158 fatal("Failed to create PPPoE socket: %m");
160 sp
.sa_family
= AF_PPPOX
;
161 sp
.sa_protocol
= PX_PROTO_OE
;
162 sp
.sa_addr
.pppoe
.sid
= conn
->session
;
163 memcpy(sp
.sa_addr
.pppoe
.dev
, conn
->ifName
, IFNAMSIZ
);
164 memcpy(sp
.sa_addr
.pppoe
.remote
, conn
->peerEth
, ETH_ALEN
);
166 /* Set remote_number for ServPoET */
167 sprintf(remote_number
, "%02X:%02X:%02X:%02X:%02X:%02X",
168 (unsigned) conn
->peerEth
[0],
169 (unsigned) conn
->peerEth
[1],
170 (unsigned) conn
->peerEth
[2],
171 (unsigned) conn
->peerEth
[3],
172 (unsigned) conn
->peerEth
[4],
173 (unsigned) conn
->peerEth
[5]);
175 if (connect(conn
->sessionSocket
, (struct sockaddr
*) &sp
,
176 sizeof(struct sockaddr_pppox
)) < 0) {
177 fatal("Failed to connect PPPoE socket: %d %m", errno
);
181 return conn
->sessionSocket
;
185 PPPOERecvConfig(int mru
,
190 if (mru
> MAX_PPPOE_MTU
)
191 warn("Couldn't increase MRU to %d", mru
);
194 /**********************************************************************
195 * %FUNCTION: PPPOEDisconnectDevice
201 * Disconnects PPPoE device
202 ***********************************************************************/
204 PPPOEDisconnectDevice(void)
206 struct sockaddr_pppox sp
;
208 sp
.sa_family
= AF_PPPOX
;
209 sp
.sa_protocol
= PX_PROTO_OE
;
210 sp
.sa_addr
.pppoe
.sid
= 0;
211 memcpy(sp
.sa_addr
.pppoe
.dev
, conn
->ifName
, IFNAMSIZ
);
212 memcpy(sp
.sa_addr
.pppoe
.remote
, conn
->peerEth
, ETH_ALEN
);
213 if (connect(conn
->sessionSocket
, (struct sockaddr
*) &sp
,
214 sizeof(struct sockaddr_pppox
)) < 0) {
215 fatal("Failed to disconnect PPPoE socket: %d %m", errno
);
218 close(conn
->sessionSocket
);
219 /* don't send PADT?? */
220 close(conn
->discoverySocket
);
224 PPPOEDeviceOptions(void)
227 snprintf(buf
, 256, _PATH_ETHOPT
"%s",devnam
);
228 if(!options_from_file(buf
, 0, 0, 1))
229 exit(EXIT_OPTION_ERROR
);
233 struct channel pppoe_channel
;
235 /**********************************************************************
236 * %FUNCTION: PPPoEDevnameHook
238 * cmd -- the command (actually, the device name
239 * argv -- argument vector
240 * doit -- if non-zero, set device name. Otherwise, just check if possible
242 * 1 if we will handle this device; 0 otherwise.
244 * Checks if name is a valid interface name; if so, returns 1. Also
245 * sets up devnam (string representation of device).
246 ***********************************************************************/
248 PPPoEDevnameHook(char *cmd
, char **argv
, int doit
)
254 /* Only do it if name is "ethXXX", "nasXXX", "tapXXX" or "nic-XXXX.
255 In latter case strip off the "nic-" */
256 /* Thanks to Russ Couturier for this fix */
257 if (strlen(cmd
) > 4 && !strncmp(cmd
, "nic-", 4)) {
258 /* Strip off "nic-" */
260 } else if (strlen(cmd
) < 4
261 || (strncmp(cmd
, "eth", 3) && strncmp(cmd
, "nas", 3)
262 && strncmp(cmd
, "tap", 3) && strncmp(cmd
, "br", 2))) {
267 if ((fd
= socket(PF_PACKET
, SOCK_RAW
, 0)) < 0) {
271 /* Try getting interface index */
273 strncpy(ifr
.ifr_name
, cmd
, sizeof(ifr
.ifr_name
));
274 if (ioctl(fd
, SIOCGIFINDEX
, &ifr
) < 0) {
277 if (ioctl(fd
, SIOCGIFHWADDR
, &ifr
) < 0) {
280 if (ifr
.ifr_hwaddr
.sa_family
!= ARPHRD_ETHER
) {
281 error("Interface %s not Ethernet", cmd
);
291 strncpy(devnam
, cmd
, sizeof(devnam
));
292 if (the_channel
!= &pppoe_channel
) {
294 the_channel
= &pppoe_channel
;
297 lcp_allowoptions
[0].neg_accompression
= 0;
298 lcp_wantoptions
[0].neg_accompression
= 0;
300 lcp_allowoptions
[0].neg_asyncmap
= 0;
301 lcp_wantoptions
[0].neg_asyncmap
= 0;
303 lcp_allowoptions
[0].neg_pcompression
= 0;
304 lcp_wantoptions
[0].neg_pcompression
= 0;
306 lcp_allowoptions
[0].mru
= MAX_PPPOE_MTU
;
307 lcp_wantoptions
[0].mru
= MAX_PPPOE_MTU
;
309 ccp_allowoptions
[0].deflate
= 0 ;
310 ccp_wantoptions
[0].deflate
= 0 ;
312 ipcp_allowoptions
[0].neg_vj
=0;
313 ipcp_wantoptions
[0].neg_vj
=0;
315 ccp_allowoptions
[0].bsd_compress
= 0;
316 ccp_wantoptions
[0].bsd_compress
= 0;
326 /**********************************************************************
327 * %FUNCTION: plugin_init
333 * Initializes hooks for pppd plugin
334 ***********************************************************************/
338 if (!ppp_available() && !new_style_driver
) {
339 fatal("Linux kernel does not support PPPoE -- are you running 2.4.x?");
342 add_options(Options
);
344 info("RP-PPPoE plugin version %s compiled against pppd %s",
345 RP_VERSION
, VERSION
);
348 /**********************************************************************
351 * str -- error message
355 * Prints a message plus the errno value to stderr and syslog and exits.
356 ***********************************************************************/
358 fatalSys(char const *str
)
362 sprintf(buf
, "%.256s: %.256s", str
, strerror(i
));
364 sprintf(buf
, "RP-PPPoE: %.256s: %.256s", str
, strerror(i
));
369 /**********************************************************************
372 * str -- error message
376 * Prints a message to stderr and syslog and exits.
377 ***********************************************************************/
379 rp_fatal(char const *str
)
383 sprintf(buf
, "RP-PPPoE: %.256s", str
);
387 /**********************************************************************
390 * str -- error message
394 * Prints a message plus the errno value to syslog.
395 ***********************************************************************/
397 sysErr(char const *str
)
403 struct channel pppoe_channel
= {
405 process_extra_options
: &PPPOEDeviceOptions
,
407 connect
: &PPPOEConnectDevice
,
408 disconnect
: &PPPOEDisconnectDevice
,
409 establish_ppp
: &generic_establish_ppp
,
410 disestablish_ppp
: &generic_disestablish_ppp
,
412 recv_config
: &PPPOERecvConfig
,