• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt/router/accel-pptp/src/pppd/plugins/rp-pppoe/
1/***********************************************************************
2*
3* plugin.c
4*
5* pppd plugin for kernel-mode PPPoE on Linux
6*
7* Copyright (C) 2001-2012 by Roaring Penguin Software Inc.
8* Portions copyright 2000 Michal Ostrowski 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 "/ppp/options."
62
63char pppd_version[] = VERSION;
64
65static int seen_devnam[2] = {0, 0};
66static char *pppoe_reqd_mac = NULL;
67
68/* From sys-linux.c in pppd -- MUST FIX THIS! */
69extern int new_style_driver;
70
71char *pppd_pppoe_service = NULL;
72static char *acName = NULL;
73static char *existingSession = NULL;
74static int printACNames = 0;
75
76static int PPPoEDevnameHook(char *cmd, char **argv, int doit);
77static 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 }
93};
94int (*OldDevnameHook)(char *cmd, char **argv, int doit) = NULL;
95static PPPoEConnection *conn = NULL;
96
97/**********************************************************************
98 * %FUNCTION: PPPOEInitDevice
99 * %ARGUMENTS:
100 * None
101 * %RETURNS:
102 *
103 * %DESCRIPTION:
104 * Initializes PPPoE device.
105 ***********************************************************************/
106static int
107PPPOEInitDevice(void)
108{
109    conn = malloc(sizeof(PPPoEConnection));
110    if (!conn) {
111	fatal("Could not allocate memory for PPPoE session");
112    }
113    memset(conn, 0, sizeof(PPPoEConnection));
114    if (acName) {
115	SET_STRING(conn->acName, acName);
116    }
117    if (pppd_pppoe_service) {
118	SET_STRING(conn->serviceName, pppd_pppoe_service);
119    }
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;
127}
128
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 ***********************************************************************/
138static int
139PPPOEConnectDevice(void)
140{
141    struct sockaddr_pppox sp;
142    struct ifreq ifr;
143    int s;
144
145    /* Restore configuration */
146    lcp_allowoptions[0].mru = conn->mtu;
147    lcp_wantoptions[0].mru = conn->mru;
148
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;
154    }
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;
160    }
161    close(s);
162
163    if (lcp_allowoptions[0].mru > ifr.ifr_mtu - TOTAL_OVERHEAD) {
164	lcp_allowoptions[0].mru = ifr.ifr_mtu - TOTAL_OVERHEAD;
165    }
166    if (lcp_wantoptions[0].mru > ifr.ifr_mtu - TOTAL_OVERHEAD) {
167	lcp_wantoptions[0].mru = ifr.ifr_mtu - TOTAL_OVERHEAD;
168    }
169
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;
179    }
180
181    if (acName) {
182	SET_STRING(conn->acName, acName);
183    }
184    if (pppd_pppoe_service) {
185	SET_STRING(conn->serviceName, pppd_pppoe_service);
186    }
187
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");
196	}
197	conn->session = htons(ses);
198	for (i=0; i<ETH_ALEN; i++) {
199	    conn->peerEth[i] = (unsigned char) mac[i];
200	}
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	    goto ERROR;
208	}
209    }
210
211    /* Set PPPoE session-number for further consumption */
212    ppp_session_number = ntohs(conn->session);
213
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);
219
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]);
228
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);
237
238    script_setenv("MACREMOTE", remote_number, 0);
239
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	goto ERROR;
244    }
245
246    return conn->sessionSocket;
247
248 ERROR:
249    close(conn->sessionSocket);
250    conn->sessionSocket = -1;
251    /* Send PADT to reset the session unresponsive at buggy nas */
252    sendPADT(conn, NULL);
253    if (!existingSession) {
254	close(conn->discoverySocket);
255	conn->discoverySocket = -1;
256    }
257    return -1;
258}
259
260static void
261PPPOESendConfig(int mtu,
262		u_int32_t asyncmap,
263		int pcomp,
264		int accomp)
265{
266    int sock;
267    struct ifreq ifr;
268
269    if (mtu > MAX_PPPOE_MTU) {
270	if (debug) warn("Couldn't increase MTU to %d", mtu);
271	mtu = MAX_PPPOE_MTU;
272    }
273    sock = socket(AF_INET, SOCK_DGRAM, 0);
274    if (sock < 0) {
275	warn("Couldn't create IP socket: %m");
276	return;
277    }
278    strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
279    ifr.ifr_mtu = mtu;
280    if (ioctl(sock, SIOCSIFMTU, &ifr) < 0) {
281	warn("ioctl(SIOCSIFMTU): %m");
282	return;
283    }
284    (void) close (sock);
285}
286
287
288static void
289PPPOERecvConfig(int mru,
290		u_int32_t asyncmap,
291		int pcomp,
292		int accomp)
293{
294    if (mru > MAX_PPPOE_MTU && debug) {
295	warn("Couldn't increase MRU to %d", mru);
296    }
297}
298
299/**********************************************************************
300 * %FUNCTION: PPPOEDisconnectDevice
301 * %ARGUMENTS:
302 * None
303 * %RETURNS:
304 * Nothing
305 * %DESCRIPTION:
306 * Disconnects PPPoE device
307 ***********************************************************************/
308static void
309PPPOEDisconnectDevice(void)
310{
311    struct sockaddr_pppox sp;
312
313    if (conn->sessionSocket < 0)
314	goto ERROR;
315
316    sp.sa_family = AF_PPPOX;
317    sp.sa_protocol = PX_PROTO_OE;
318    sp.sa_addr.pppoe.sid = 0;
319    memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ);
320    memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN);
321    if (connect(conn->sessionSocket, (struct sockaddr *) &sp,
322		sizeof(struct sockaddr_pppox)) < 0) {
323	warn("Failed to disconnect PPPoE socket: %d %m", errno);
324    }
325    close(conn->sessionSocket);
326    conn->sessionSocket = -1;
327
328ERROR:
329    /* Send PADT to reset the session unresponsive at buggy nas */
330    sendPADT(conn, NULL);
331    if (!existingSession) {
332	close(conn->discoverySocket);
333	conn->discoverySocket = -1;
334    }
335}
336
337static void
338PPPOEDeviceOptions(void)
339{
340    char buf[256];
341    snprintf(buf, 256, _PATH_ETHOPT "%s",devnam);
342    if(!options_from_file(buf, 0, 0, 1))
343	exit(EXIT_OPTION_ERROR);
344
345}
346
347struct channel pppoe_channel;
348
349/**********************************************************************
350 * %FUNCTION: PPPoEDevnameHook
351 * %ARGUMENTS:
352 * cmd -- the command (actually, the device name
353 * argv -- argument vector
354 * doit -- if non-zero, set device name.  Otherwise, just check if possible
355 * %RETURNS:
356 * 1 if we will handle this device; 0 otherwise.
357 * %DESCRIPTION:
358 * Checks if name is a valid interface name; if so, returns 1.  Also
359 * sets up devnam (string representation of device).
360 ***********************************************************************/
361static int
362PPPoEDevnameHook(char *cmd, char **argv, int doit)
363{
364    int r = 1;
365    int fd;
366    struct ifreq ifr;
367    int seen_idx = doit ? 1 : 0;
368
369    /* If "devnam" has already been set, ignore.
370       This prevents kernel from doing modprobes against random
371       pppd arguments that happen to begin with "nic-", "eth" or "br"
372
373       Ideally, "nix-ethXXX" should be supplied immediately after
374       "plugin rp-pppoe.so"
375
376       Patch based on suggestion from Mike Ireton.
377    */
378    if (seen_devnam[seen_idx]) {
379	if (OldDevnameHook) return OldDevnameHook(cmd, argv, doit);
380	return 0;
381    }
382
383    /* Only do it if name is "ethXXX" or "brXXX" or what was specified
384       by rp_pppoe_dev option (ugh). */
385    /* Can also specify nic-XXXX in which case the nic- is stripped off. */
386    if (!strncmp(cmd, "nic-", 4)) {
387	cmd += 4;
388    } else {
389	if (strncmp(cmd, "eth", 3) &&
390	    strncmp(cmd, "br", 2)) {
391	    if (OldDevnameHook) return OldDevnameHook(cmd, argv, doit);
392	    return 0;
393	}
394    }
395
396    /* Open a socket */
397    if ((fd = socket(PF_PACKET, SOCK_RAW, 0)) < 0) {
398	r = 0;
399    }
400
401    /* Try getting interface index */
402    if (r) {
403	strncpy(ifr.ifr_name, cmd, IFNAMSIZ);
404	if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
405	    r = 0;
406	} else {
407	    if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
408		r = 0;
409	    } else {
410		if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
411		    error("Interface %s not Ethernet", cmd);
412		    r=0;
413		}
414	    }
415	}
416    }
417
418    /* Close socket */
419    close(fd);
420    if (r) {
421	seen_devnam[seen_idx] = 1;
422	if (doit) {
423	    strncpy(devnam, cmd, sizeof(devnam));
424	    if (the_channel != &pppoe_channel) {
425
426		the_channel = &pppoe_channel;
427		modem = 0;
428
429		lcp_allowoptions[0].neg_accompression = 0;
430		lcp_wantoptions[0].neg_accompression = 0;
431
432		lcp_allowoptions[0].neg_asyncmap = 0;
433		lcp_wantoptions[0].neg_asyncmap = 0;
434
435		lcp_allowoptions[0].neg_pcompression = 0;
436		lcp_wantoptions[0].neg_pcompression = 0;
437
438		ipcp_allowoptions[0].neg_vj=0;
439		ipcp_wantoptions[0].neg_vj=0;
440
441		ccp_allowoptions[0].deflate = 0 ;
442		ccp_wantoptions[0].deflate = 0 ;
443
444		ccp_allowoptions[0].bsd_compress = 0;
445		ccp_wantoptions[0].bsd_compress = 0;
446
447
448		PPPOEInitDevice();
449	    }
450	}
451	return 1;
452    }
453
454    if (OldDevnameHook) r = OldDevnameHook(cmd, argv, doit);
455    return r;
456}
457
458/**********************************************************************
459 * %FUNCTION: plugin_init
460 * %ARGUMENTS:
461 * None
462 * %RETURNS:
463 * Nothing
464 * %DESCRIPTION:
465 * Initializes hooks for pppd plugin
466 ***********************************************************************/
467void
468plugin_init(void)
469{
470    if (!ppp_available() && !new_style_driver) {
471	fatal("Linux kernel does not support PPPoE -- are you running 2.4.x?");
472    }
473
474    add_options(Options);
475
476    info("RP-PPPoE plugin version %s compiled against pppd %s",
477	 RP_VERSION, VERSION);
478}
479
480/**********************************************************************
481*%FUNCTION: fatalSys
482*%ARGUMENTS:
483* str -- error message
484*%RETURNS:
485* Nothing
486*%DESCRIPTION:
487* Prints a message plus the errno value to stderr and syslog and exits.
488*
489***********************************************************************/
490void
491fatalSys(char const *str)
492{
493    char buf[1024];
494    int i = errno;
495    sprintf(buf, "%.256s: %.256s", str, strerror(i));
496    printErr(buf);
497    sprintf(buf, "RP-PPPoE: %.256s: %.256s", str, strerror(i));
498    sendPADT(conn, buf);
499    exit(1);
500}
501
502/**********************************************************************
503*%FUNCTION: rp_fatal
504*%ARGUMENTS:
505* str -- error message
506*%RETURNS:
507* Nothing
508*%DESCRIPTION:
509* Prints a message to stderr and syslog and exits.
510***********************************************************************/
511void
512rp_fatal(char const *str)
513{
514    printErr(str);
515    sendPADTf(conn, "RP-PPPoE: %.256s", str);
516    exit(1);
517}
518
519/**********************************************************************
520*%FUNCTION: sysErr
521*%ARGUMENTS:
522* str -- error message
523*%RETURNS:
524* Nothing
525*%DESCRIPTION:
526* Prints a message plus the errno value to syslog.
527***********************************************************************/
528void
529sysErr(char const *str)
530{
531    char buf[1024];
532    sprintf(buf, "%.256s: %.256s", str, strerror(errno));
533    printErr(buf);
534}
535
536void pppoe_check_options(void)
537{
538    unsigned int mac[ETH_ALEN];
539    int i;
540
541    if (pppoe_reqd_mac != NULL) {
542        if (sscanf(pppoe_reqd_mac, "%x:%x:%x:%x:%x:%x",
543                   &mac[0], &mac[1], &mac[2], &mac[3],
544                   &mac[4], &mac[5]) != ETH_ALEN) {
545            option_error("cannot parse pppoe-mac option value");
546            exit(EXIT_OPTION_ERROR);
547        }
548        for (i = 0; i < 6; ++i)
549            conn->req_peer_mac[i] = mac[i];
550        conn->req_peer = 1;
551    }
552
553    lcp_allowoptions[0].neg_accompression = 0;
554    lcp_wantoptions[0].neg_accompression = 0;
555
556    lcp_allowoptions[0].neg_asyncmap = 0;
557    lcp_wantoptions[0].neg_asyncmap = 0;
558
559    lcp_allowoptions[0].neg_pcompression = 0;
560    lcp_wantoptions[0].neg_pcompression = 0;
561
562    if (lcp_allowoptions[0].mru > MAX_PPPOE_MTU) {
563        lcp_allowoptions[0].mru = MAX_PPPOE_MTU;
564    }
565    if (lcp_wantoptions[0].mru > MAX_PPPOE_MTU) {
566        lcp_wantoptions[0].mru = MAX_PPPOE_MTU;
567    }
568
569    /* Save configuration */
570    conn->mtu = lcp_allowoptions[0].mru;
571    conn->mru = lcp_wantoptions[0].mru;
572
573    ccp_allowoptions[0].deflate = 0;
574    ccp_wantoptions[0].deflate = 0;
575
576    ipcp_allowoptions[0].neg_vj = 0;
577    ipcp_wantoptions[0].neg_vj = 0;
578
579    ccp_allowoptions[0].bsd_compress = 0;
580    ccp_wantoptions[0].bsd_compress = 0;
581}
582
583struct channel pppoe_channel = {
584    .options = Options,
585    .process_extra_options = &PPPOEDeviceOptions,
586    .check_options = &pppoe_check_options,
587    .connect = &PPPOEConnectDevice,
588    .disconnect = &PPPOEDisconnectDevice,
589    .establish_ppp = &generic_establish_ppp,
590    .disestablish_ppp = &generic_disestablish_ppp,
591    .send_config = &PPPOESendConfig,
592    .recv_config = &PPPOERecvConfig,
593    .close = NULL,
594    .cleanup = NULL
595};
596