2968e72e93c2fb8936520888f0697b49f74852ae
[ppp.git] / pppd / plugins / rp-pppoe / plugin.c
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 ***********************************************************************/
24
25 static char const RCSID[] =
26 "$Id: plugin.c,v 1.16 2008/06/09 08:34:23 paulus Exp $";
27
28 #define _GNU_SOURCE 1
29 #include "pppoe.h"
30
31 #include "pppd/pppd.h"
32 #include "pppd/fsm.h"
33 #include "pppd/lcp.h"
34 #include "pppd/ipcp.h"
35 #include "pppd/ccp.h"
36 /* #include "pppd/pathnames.h" */
37
38 #include <linux/types.h>
39 #include <sys/ioctl.h>
40 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <sys/stat.h>
43 #include <string.h>
44 #include <stdlib.h>
45 #include <errno.h>
46 #include <unistd.h>
47 #include <fcntl.h>
48 #include <signal.h>
49 #include <net/ethernet.h>
50 #include <net/if_arp.h>
51 #include <linux/ppp_defs.h>
52 #include <linux/if_ppp.h>
53 #include <linux/if_pppox.h>
54
55 #ifndef _ROOT_PATH
56 #define _ROOT_PATH ""
57 #endif
58
59 #define _PATH_ETHOPT         _ROOT_PATH "/etc/ppp/options."
60
61 char pppd_version[] = VERSION;
62
63 /* From sys-linux.c in pppd -- MUST FIX THIS! */
64 extern int new_style_driver;
65
66 char *pppd_pppoe_service = NULL;
67 static char *acName = NULL;
68 static char *existingSession = NULL;
69 static int printACNames = 0;
70
71 static int PPPoEDevnameHook(char *cmd, char **argv, int doit);
72 static option_t Options[] = {
73     { "device name", o_wild, (void *) &PPPoEDevnameHook,
74       "PPPoE device name",
75       OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG  | OPT_A2STRVAL | OPT_STATIC,
76       devnam},
77     { "rp_pppoe_service", o_string, &pppd_pppoe_service,
78       "Desired PPPoE service name" },
79     { "rp_pppoe_ac",      o_string, &acName,
80       "Desired PPPoE access concentrator name" },
81     { "rp_pppoe_sess",    o_string, &existingSession,
82       "Attach to existing session (sessid:macaddr)" },
83     { "rp_pppoe_verbose", o_int, &printACNames,
84       "Be verbose about discovered access concentrators"},
85     { NULL }
86 };
87 int (*OldDevnameHook)(char *cmd, char **argv, int doit) = NULL;
88 static PPPoEConnection *conn = NULL;
89
90 /**********************************************************************
91  * %FUNCTION: PPPOEInitDevice
92  * %ARGUMENTS:
93  * None
94  * %RETURNS:
95  *
96  * %DESCRIPTION:
97  * Initializes PPPoE device.
98  ***********************************************************************/
99 static int
100 PPPOEInitDevice(void)
101 {
102     conn = malloc(sizeof(PPPoEConnection));
103     if (!conn) {
104         novm("PPPoE session data");
105     }
106     memset(conn, 0, sizeof(PPPoEConnection));
107     conn->acName = acName;
108     conn->serviceName = pppd_pppoe_service;
109     conn->ifName = devnam;
110     conn->discoverySocket = -1;
111     conn->sessionSocket = -1;
112     conn->useHostUniq = 1;
113     conn->printACNames = printACNames;
114     conn->discoveryTimeout = PADI_TIMEOUT;
115     return 1;
116 }
117
118 /**********************************************************************
119  * %FUNCTION: PPPOEConnectDevice
120  * %ARGUMENTS:
121  * None
122  * %RETURNS:
123  * Non-negative if all goes well; -1 otherwise
124  * %DESCRIPTION:
125  * Connects PPPoE device.
126  ***********************************************************************/
127 static int
128 PPPOEConnectDevice(void)
129 {
130     struct sockaddr_pppox sp;
131
132     strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));
133     if (existingSession) {
134         unsigned int mac[ETH_ALEN];
135         int i, ses;
136         if (sscanf(existingSession, "%d:%x:%x:%x:%x:%x:%x",
137                    &ses, &mac[0], &mac[1], &mac[2],
138                    &mac[3], &mac[4], &mac[5]) != 7) {
139             fatal("Illegal value for rp_pppoe_sess option");
140         }
141         conn->session = htons(ses);
142         for (i=0; i<ETH_ALEN; i++) {
143             conn->peerEth[i] = (unsigned char) mac[i];
144         }
145     } else {
146         discovery(conn);
147         if (conn->discoveryState != STATE_SESSION) {
148             error("Unable to complete PPPoE Discovery");
149             return -1;
150         }
151     }
152
153     /* Set PPPoE session-number for further consumption */
154     ppp_session_number = ntohs(conn->session);
155
156     /* Make the session socket */
157     conn->sessionSocket = socket(AF_PPPOX, SOCK_STREAM, PX_PROTO_OE);
158     if (conn->sessionSocket < 0) {
159         error("Failed to create PPPoE socket: %m");
160         goto errout;
161     }
162     sp.sa_family = AF_PPPOX;
163     sp.sa_protocol = PX_PROTO_OE;
164     sp.sa_addr.pppoe.sid = conn->session;
165     memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ);
166     memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN);
167
168     /* Set remote_number for ServPoET */
169     sprintf(remote_number, "%02X:%02X:%02X:%02X:%02X:%02X",
170             (unsigned) conn->peerEth[0],
171             (unsigned) conn->peerEth[1],
172             (unsigned) conn->peerEth[2],
173             (unsigned) conn->peerEth[3],
174             (unsigned) conn->peerEth[4],
175             (unsigned) conn->peerEth[5]);
176
177     warn("Connected to %02X:%02X:%02X:%02X:%02X:%02X via interface %s",
178          (unsigned) conn->peerEth[0],
179          (unsigned) conn->peerEth[1],
180          (unsigned) conn->peerEth[2],
181          (unsigned) conn->peerEth[3],
182          (unsigned) conn->peerEth[4],
183          (unsigned) conn->peerEth[5],
184          conn->ifName);
185
186     script_setenv("MACREMOTE", remote_number, 0);
187
188     if (connect(conn->sessionSocket, (struct sockaddr *) &sp,
189                 sizeof(struct sockaddr_pppox)) < 0) {
190         error("Failed to connect PPPoE socket: %d %m", errno);
191         close(conn->sessionSocket);
192         goto errout;
193     }
194
195     return conn->sessionSocket;
196
197  errout:
198     if (conn->discoverySocket >= 0) {
199         sendPADT(conn, NULL);
200         close(conn->discoverySocket);
201         conn->discoverySocket = -1;
202     }
203     return -1;
204 }
205
206 static void
207 PPPOERecvConfig(int mru,
208                 u_int32_t asyncmap,
209                 int pcomp,
210                 int accomp)
211 {
212 #if 0 /* broken protocol, but no point harrassing the users I guess... */
213     if (mru > MAX_PPPOE_MTU)
214         warn("Couldn't increase MRU to %d", mru);
215 #endif
216 }
217
218 /**********************************************************************
219  * %FUNCTION: PPPOEDisconnectDevice
220  * %ARGUMENTS:
221  * None
222  * %RETURNS:
223  * Nothing
224  * %DESCRIPTION:
225  * Disconnects PPPoE device
226  ***********************************************************************/
227 static void
228 PPPOEDisconnectDevice(void)
229 {
230     struct sockaddr_pppox sp;
231
232     sp.sa_family = AF_PPPOX;
233     sp.sa_protocol = PX_PROTO_OE;
234     sp.sa_addr.pppoe.sid = 0;
235     memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ);
236     memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN);
237     if (connect(conn->sessionSocket, (struct sockaddr *) &sp,
238                 sizeof(struct sockaddr_pppox)) < 0)
239         error("Failed to disconnect PPPoE socket: %d %m", errno);
240     close(conn->sessionSocket);
241     /* don't send PADT?? */
242     if (conn->discoverySocket >= 0)
243         close(conn->discoverySocket);
244 }
245
246 static void
247 PPPOEDeviceOptions(void)
248 {
249     char buf[256];
250     snprintf(buf, 256, _PATH_ETHOPT "%s", devnam);
251     if (!options_from_file(buf, 0, 0, 1))
252         exit(EXIT_OPTION_ERROR);
253
254 }
255
256 struct channel pppoe_channel;
257
258 /**********************************************************************
259  * %FUNCTION: PPPoEDevnameHook
260  * %ARGUMENTS:
261  * cmd -- the command (actually, the device name
262  * argv -- argument vector
263  * doit -- if non-zero, set device name.  Otherwise, just check if possible
264  * %RETURNS:
265  * 1 if we will handle this device; 0 otherwise.
266  * %DESCRIPTION:
267  * Checks if name is a valid interface name; if so, returns 1.  Also
268  * sets up devnam (string representation of device).
269  ***********************************************************************/
270 static int
271 PPPoEDevnameHook(char *cmd, char **argv, int doit)
272 {
273     int r = 1;
274     int fd;
275     struct ifreq ifr;
276
277     /*
278      * Take any otherwise-unrecognized option as a possible device name,
279      * and test if it is the name of a network interface with a
280      * hardware address whose sa_family is ARPHRD_ETHER.
281      */
282     if (strlen(cmd) > 4 && !strncmp(cmd, "nic-", 4)) {
283         /* Strip off "nic-" */
284         cmd += 4;
285     }
286
287     /* Open a socket */
288     if ((fd = socket(PF_PACKET, SOCK_RAW, 0)) < 0) {
289         r = 0;
290     }
291
292     /* Try getting interface index */
293     if (r) {
294         strncpy(ifr.ifr_name, cmd, sizeof(ifr.ifr_name));
295         if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
296             r = 0;
297         } else {
298             if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
299                 r = 0;
300             } else {
301                 if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
302                     if (doit)
303                         error("Interface %s not Ethernet", cmd);
304                     r = 0;
305                 }
306             }
307         }
308     }
309
310     /* Close socket */
311     close(fd);
312     if (r && doit) {
313         strncpy(devnam, cmd, sizeof(devnam));
314         if (the_channel != &pppoe_channel) {
315
316             the_channel = &pppoe_channel;
317             modem = 0;
318
319             PPPOEInitDevice();
320         }
321         return 1;
322     }
323
324     return r;
325 }
326
327 /**********************************************************************
328  * %FUNCTION: plugin_init
329  * %ARGUMENTS:
330  * None
331  * %RETURNS:
332  * Nothing
333  * %DESCRIPTION:
334  * Initializes hooks for pppd plugin
335  ***********************************************************************/
336 void
337 plugin_init(void)
338 {
339     if (!ppp_available() && !new_style_driver) {
340         fatal("Linux kernel does not support PPPoE -- are you running 2.4.x?");
341     }
342
343     add_options(Options);
344
345     info("RP-PPPoE plugin version %s compiled against pppd %s",
346          RP_VERSION, VERSION);
347 }
348
349 void pppoe_check_options(void)
350 {
351     lcp_allowoptions[0].neg_accompression = 0;
352     lcp_wantoptions[0].neg_accompression = 0;
353
354     lcp_allowoptions[0].neg_asyncmap = 0;
355     lcp_wantoptions[0].neg_asyncmap = 0;
356
357     lcp_allowoptions[0].neg_pcompression = 0;
358     lcp_wantoptions[0].neg_pcompression = 0;
359
360     if (lcp_allowoptions[0].mru > MAX_PPPOE_MTU)
361         lcp_allowoptions[0].mru = MAX_PPPOE_MTU;
362     if (lcp_wantoptions[0].mru > MAX_PPPOE_MTU)
363         lcp_wantoptions[0].mru = MAX_PPPOE_MTU;
364
365     ccp_allowoptions[0].deflate = 0;
366     ccp_wantoptions[0].deflate = 0;
367
368     ipcp_allowoptions[0].neg_vj = 0;
369     ipcp_wantoptions[0].neg_vj = 0;
370
371     ccp_allowoptions[0].bsd_compress = 0;
372     ccp_wantoptions[0].bsd_compress = 0;
373 }
374
375 struct channel pppoe_channel = {
376     .options = Options,
377     .process_extra_options = &PPPOEDeviceOptions,
378     .check_options = pppoe_check_options,
379     .connect = &PPPOEConnectDevice,
380     .disconnect = &PPPOEDisconnectDevice,
381     .establish_ppp = &generic_establish_ppp,
382     .disestablish_ppp = &generic_disestablish_ppp,
383     .send_config = NULL,
384     .recv_config = &PPPOERecvConfig,
385     .close = NULL,
386     .cleanup = NULL
387 };