summaryrefslogtreecommitdiff
path: root/src/plugin.c (plain)
blob: 83978f03a2bc4929506aec5e2a3b57bed7df44f3
1/***********************************************************************
2*
3* plugin.c
4*
5* pppd plugin for kernel-mode PPPoE on Linux
6*
7* Copyright (C) 2001 by Roaring Penguin Software Inc., Michal Ostrowski
8* and Jamal Hadi Salim.
9*
10* Much code and many ideas derived from pppoe plugin by Michal
11* Ostrowski and Jamal Hadi Salim, which carries this copyright:
12*
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.
17*
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*
23* LIC: GPL
24*
25***********************************************************************/
26
27static char const RCSID[] =
28"$Id$";
29
30#define _GNU_SOURCE 1
31#include "pppoe.h"
32
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" */
39
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>
56
57#ifndef _ROOT_PATH
58#define _ROOT_PATH ""
59#endif
60
61#define _PATH_ETHOPT _ROOT_PATH "/etc/ppp/options."
62
63char pppd_version[] = VERSION;
64
65/* From sys-linux.c in pppd -- MUST FIX THIS! */
66extern int new_style_driver;
67
68char *pppd_pppoe_service = NULL;
69static char *acName = NULL;
70static char *existingSession = NULL;
71static int printACNames = 0;
72
73static int PPPoEDevnameHook(char *cmd, char **argv, int doit);
74static option_t Options[] = {
75 { "device name", o_wild, (void *) &PPPoEDevnameHook,
76 "PPPoE device name",
77 OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG | OPT_A2STRVAL | OPT_STATIC,
78 devnam},
79 { "rp_pppoe_service", o_string, &pppd_pppoe_service,
80 "Desired PPPoE service name" },
81 { "rp_pppoe_ac", o_string, &acName,
82 "Desired PPPoE access concentrator name" },
83 { "rp_pppoe_sess", o_string, &existingSession,
84 "Attach to existing session (sessid:macaddr)" },
85 { "rp_pppoe_verbose", o_int, &printACNames,
86 "Be verbose about discovered access concentrators"},
87 { NULL }
88};
89int (*OldDevnameHook)(char *cmd, char **argv, int doit) = NULL;
90static PPPoEConnection *conn = NULL;
91
92/**********************************************************************
93 * %FUNCTION: PPPOEInitDevice
94 * %ARGUMENTS:
95 * None
96 * %RETURNS:
97 *
98 * %DESCRIPTION:
99 * Initializes PPPoE device.
100 ***********************************************************************/
101static int
102PPPOEInitDevice(void)
103{
104 conn = malloc(sizeof(PPPoEConnection));
105 if (!conn) {
106 fatal("Could not allocate memory for PPPoE session");
107 }
108 memset(conn, 0, sizeof(PPPoEConnection));
109 if (acName) {
110 SET_STRING(conn->acName, acName);
111 }
112 if (pppd_pppoe_service) {
113 SET_STRING(conn->serviceName, pppd_pppoe_service);
114 }
115 SET_STRING(conn->ifName, devnam);
116 conn->discoverySocket = -1;
117 conn->sessionSocket = -1;
118 conn->useHostUniq = 1;
119 conn->printACNames = printACNames;
120 conn->discoveryTimeout = PADI_TIMEOUT;
121 return 1;
122}
123
124/**********************************************************************
125 * %FUNCTION: PPPOEConnectDevice
126 * %ARGUMENTS:
127 * None
128 * %RETURNS:
129 * Non-negative if all goes well; -1 otherwise
130 * %DESCRIPTION:
131 * Connects PPPoE device.
132 ***********************************************************************/
133static int
134PPPOEConnectDevice(void)
135{
136 struct sockaddr_pppox sp;
137
138 /* Open session socket before discovery phase, to avoid losing session */
139 /* packets sent by peer just after PADS packet (noted on some Cisco */
140 /* server equipment). */
141 /* Opening this socket just before waitForPADS in the discovery() */
142 /* function would be more appropriate, but it would mess-up the code */
143 conn->sessionSocket = socket(AF_PPPOX, SOCK_STREAM, PX_PROTO_OE);
144 if (conn->sessionSocket < 0) {
145 error("Failed to create PPPoE socket: %m");
146 return -1;
147 }
148
149 strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));
150 if (existingSession) {
151 unsigned int mac[ETH_ALEN];
152 int i, ses;
153 if (sscanf(existingSession, "%d:%x:%x:%x:%x:%x:%x",
154 &ses, &mac[0], &mac[1], &mac[2],
155 &mac[3], &mac[4], &mac[5]) != 7) {
156 fatal("Illegal value for rp_pppoe_sess option");
157 }
158 conn->session = htons(ses);
159 for (i=0; i<ETH_ALEN; i++) {
160 conn->peerEth[i] = (unsigned char) mac[i];
161 }
162 } else {
163 conn->discoverySocket =
164 openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth);
165 discovery(conn);
166 if (conn->discoveryState != STATE_SESSION) {
167 error("Unable to complete PPPoE Discovery");
168 return -1;
169 }
170 }
171
172 /* Set PPPoE session-number for further consumption */
173 ppp_session_number = ntohs(conn->session);
174
175 sp.sa_family = AF_PPPOX;
176 sp.sa_protocol = PX_PROTO_OE;
177 sp.sa_addr.pppoe.sid = conn->session;
178 memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ);
179 memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN);
180
181 /* Set remote_number for ServPoET */
182 sprintf(remote_number, "%02X:%02X:%02X:%02X:%02X:%02X",
183 (unsigned) conn->peerEth[0],
184 (unsigned) conn->peerEth[1],
185 (unsigned) conn->peerEth[2],
186 (unsigned) conn->peerEth[3],
187 (unsigned) conn->peerEth[4],
188 (unsigned) conn->peerEth[5]);
189
190 warn("Connected to %02X:%02X:%02X:%02X:%02X:%02X via interface %s",
191 (unsigned) conn->peerEth[0],
192 (unsigned) conn->peerEth[1],
193 (unsigned) conn->peerEth[2],
194 (unsigned) conn->peerEth[3],
195 (unsigned) conn->peerEth[4],
196 (unsigned) conn->peerEth[5],
197 conn->ifName);
198
199 script_setenv("MACREMOTE", remote_number, 0);
200
201 if (connect(conn->sessionSocket, (struct sockaddr *) &sp,
202 sizeof(struct sockaddr_pppox)) < 0) {
203 error("Failed to connect PPPoE socket: %d %m", errno);
204 return -1;
205 }
206
207 return conn->sessionSocket;
208}
209
210static void
211PPPOESendConfig(int mtu,
212 u_int32_t asyncmap,
213 int pcomp,
214 int accomp)
215{
216 int sock;
217 struct ifreq ifr;
218
219 if (mtu > MAX_PPPOE_MTU) {
220 warn("Couldn't increase MTU to %d", mtu);
221 mtu = MAX_PPPOE_MTU;
222 }
223 sock = socket(AF_INET, SOCK_DGRAM, 0);
224 if (sock < 0) {
225 warn("Couldn't create IP socket: %m");
226 return;
227 }
228 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
229 ifr.ifr_mtu = mtu;
230 if (ioctl(sock, SIOCSIFMTU, &ifr) < 0) {
231 warn("ioctl(SIOCSIFMTU): %m");
232 return;
233 }
234 (void) close (sock);
235}
236
237
238static void
239PPPOERecvConfig(int mru,
240 u_int32_t asyncmap,
241 int pcomp,
242 int accomp)
243{
244 if (mru > MAX_PPPOE_MTU) {
245 warn("Couldn't increase MRU to %d", mru);
246 }
247}
248
249/**********************************************************************
250 * %FUNCTION: PPPOEDisconnectDevice
251 * %ARGUMENTS:
252 * None
253 * %RETURNS:
254 * Nothing
255 * %DESCRIPTION:
256 * Disconnects PPPoE device
257 ***********************************************************************/
258static void
259PPPOEDisconnectDevice(void)
260{
261 struct sockaddr_pppox sp;
262
263 sp.sa_family = AF_PPPOX;
264 sp.sa_protocol = PX_PROTO_OE;
265 sp.sa_addr.pppoe.sid = 0;
266 memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ);
267 memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN);
268 if (connect(conn->sessionSocket, (struct sockaddr *) &sp,
269 sizeof(struct sockaddr_pppox)) < 0) {
270 fatal("Failed to disconnect PPPoE socket: %d %m", errno);
271 return;
272 }
273 close(conn->sessionSocket);
274 close(conn->discoverySocket);
275
276}
277
278static void
279PPPOEDeviceOptions(void)
280{
281 char buf[256];
282 snprintf(buf, 256, _PATH_ETHOPT "%s",devnam);
283 if(!options_from_file(buf, 0, 0, 1))
284 exit(EXIT_OPTION_ERROR);
285
286}
287
288struct channel pppoe_channel;
289
290/**********************************************************************
291 * %FUNCTION: PPPoEDevnameHook
292 * %ARGUMENTS:
293 * cmd -- the command (actually, the device name
294 * argv -- argument vector
295 * doit -- if non-zero, set device name. Otherwise, just check if possible
296 * %RETURNS:
297 * 1 if we will handle this device; 0 otherwise.
298 * %DESCRIPTION:
299 * Checks if name is a valid interface name; if so, returns 1. Also
300 * sets up devnam (string representation of device).
301 ***********************************************************************/
302static int
303PPPoEDevnameHook(char *cmd, char **argv, int doit)
304{
305 int r = 1;
306 int fd;
307 struct ifreq ifr;
308
309 /* Only do it if name is "ethXXX" or "brXXX" or what was specified
310 by rp_pppoe_dev option (ugh). */
311 /* Can also specify nic-XXXX in which case the nic- is stripped off. */
312 if (!strncmp(cmd, "nic-", 4)) {
313 cmd += 4;
314 } else {
315 if (strncmp(cmd, "eth", 3) &&
316 strncmp(cmd, "br", 2)) {
317 if (OldDevnameHook) return OldDevnameHook(cmd, argv, doit);
318 return 0;
319 }
320 }
321
322 /* Open a socket */
323 if ((fd = socket(PF_PACKET, SOCK_RAW, 0)) < 0) {
324 r = 0;
325 }
326
327 /* Try getting interface index */
328 if (r) {
329 strncpy(ifr.ifr_name, cmd, IFNAMSIZ);
330 if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
331 r = 0;
332 } else {
333 if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
334 r = 0;
335 } else {
336 if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
337 error("Interface %s not Ethernet", cmd);
338 r=0;
339 }
340 }
341 }
342 }
343
344 /* Close socket */
345 close(fd);
346 if (r) {
347 strncpy(devnam, cmd, sizeof(devnam));
348 if (the_channel != &pppoe_channel) {
349
350 the_channel = &pppoe_channel;
351 modem = 0;
352
353 lcp_allowoptions[0].neg_accompression = 0;
354 lcp_wantoptions[0].neg_accompression = 0;
355
356 lcp_allowoptions[0].neg_asyncmap = 0;
357 lcp_wantoptions[0].neg_asyncmap = 0;
358
359 lcp_allowoptions[0].neg_pcompression = 0;
360 lcp_wantoptions[0].neg_pcompression = 0;
361
362 ipcp_allowoptions[0].neg_vj=0;
363 ipcp_wantoptions[0].neg_vj=0;
364
365 ccp_allowoptions[0].deflate = 0 ;
366 ccp_wantoptions[0].deflate = 0 ;
367
368 ccp_allowoptions[0].bsd_compress = 0;
369 ccp_wantoptions[0].bsd_compress = 0;
370
371
372 PPPOEInitDevice();
373 }
374 return 1;
375 }
376
377 if (OldDevnameHook) r = OldDevnameHook(cmd, argv, doit);
378 return r;
379}
380
381/**********************************************************************
382 * %FUNCTION: plugin_init
383 * %ARGUMENTS:
384 * None
385 * %RETURNS:
386 * Nothing
387 * %DESCRIPTION:
388 * Initializes hooks for pppd plugin
389 ***********************************************************************/
390void
391plugin_init(void)
392{
393 if (!ppp_available() && !new_style_driver) {
394 fatal("Linux kernel does not support PPPoE -- are you running 2.4.x?");
395 }
396
397 add_options(Options);
398
399 info("RP-PPPoE plugin version %s compiled against pppd %s",
400 RP_VERSION, VERSION);
401}
402
403/**********************************************************************
404*%FUNCTION: fatalSys
405*%ARGUMENTS:
406* str -- error message
407*%RETURNS:
408* Nothing
409*%DESCRIPTION:
410* Prints a message plus the errno value to stderr and syslog and exits.
411***********************************************************************/
412void
413fatalSys(char const *str)
414{
415 char buf[1024];
416 int i = errno;
417 sprintf(buf, "%.256s: %.256s", str, strerror(i));
418 printErr(buf);
419 sprintf(buf, "RP-PPPoE: %.256s: %.256s", str, strerror(i));
420 sendPADT(conn, buf);
421 exit(1);
422}
423
424/**********************************************************************
425*%FUNCTION: rp_fatal
426*%ARGUMENTS:
427* str -- error message
428*%RETURNS:
429* Nothing
430*%DESCRIPTION:
431* Prints a message to stderr and syslog and exits.
432***********************************************************************/
433void
434rp_fatal(char const *str)
435{
436 printErr(str);
437 sendPADTf(conn, "RP-PPPoE: %.256s", str);
438 exit(1);
439}
440
441/**********************************************************************
442*%FUNCTION: sysErr
443*%ARGUMENTS:
444* str -- error message
445*%RETURNS:
446* Nothing
447*%DESCRIPTION:
448* Prints a message plus the errno value to syslog.
449***********************************************************************/
450void
451sysErr(char const *str)
452{
453 rp_fatal(str);
454}
455
456
457struct channel pppoe_channel = {
458 .options = Options,
459 .process_extra_options = &PPPOEDeviceOptions,
460 .check_options = NULL,
461 .connect = &PPPOEConnectDevice,
462 .disconnect = &PPPOEDisconnectDevice,
463 .establish_ppp = &generic_establish_ppp,
464 .disestablish_ppp = &generic_disestablish_ppp,
465 .send_config = &PPPOESendConfig,
466 .recv_config = &PPPOERecvConfig,
467 .close = NULL,
468 .cleanup = NULL
469};
470