Home | History | Annotate | Download | only in rp-pppoe
      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 (at) styx.uwaterloo.ca>,
     14 *                Jamal Hadi Salim <hadi (at) 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 static char const RCSID[] =
     25 "$Id: plugin.c,v 1.12 2004/11/04 10:07:37 paulus Exp $";
     26 
     27 #define _GNU_SOURCE 1
     28 #include "pppoe.h"
     29 
     30 #include "pppd/pppd.h"
     31 #include "pppd/fsm.h"
     32 #include "pppd/lcp.h"
     33 #include "pppd/ipcp.h"
     34 #include "pppd/ccp.h"
     35 #include "pppd/pathnames.h"
     36 
     37 #include <linux/types.h>
     38 #include <syslog.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 "ppp_defs.h"
     52 #include "if_ppp.h"
     53 #include "if_pppox.h"
     54 
     55 #define _PATH_ETHOPT         _ROOT_PATH "/etc/ppp/options."
     56 
     57 char pppd_version[] = VERSION;
     58 
     59 /* From sys-linux.c in pppd -- MUST FIX THIS! */
     60 extern int new_style_driver;
     61 
     62 char *pppd_pppoe_service = NULL;
     63 static char *acName = NULL;
     64 static char *existingSession = NULL;
     65 static int printACNames = 0;
     66 
     67 static int PPPoEDevnameHook(char *cmd, char **argv, int doit);
     68 static option_t Options[] = {
     69     { "device name", o_wild, (void *) &PPPoEDevnameHook,
     70       "PPPoE device name",
     71       OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG  | OPT_A2STRVAL | OPT_STATIC,
     72       devnam},
     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"},
     81     { NULL }
     82 };
     83 
     84 static PPPoEConnection *conn = NULL;
     85 
     86 /**********************************************************************
     87  * %FUNCTION: PPPOEInitDevice
     88  * %ARGUMENTS:
     89  * None
     90  * %RETURNS:
     91  *
     92  * %DESCRIPTION:
     93  * Initializes PPPoE device.
     94  ***********************************************************************/
     95 static int
     96 PPPOEInitDevice(void)
     97 {
     98     conn = malloc(sizeof(PPPoEConnection));
     99     if (!conn) {
    100 	fatal("Could not allocate memory for PPPoE session");
    101     }
    102     memset(conn, 0, sizeof(PPPoEConnection));
    103     if (acName) {
    104 	SET_STRING(conn->acName, acName);
    105     }
    106     if (pppd_pppoe_service) {
    107 	SET_STRING(conn->serviceName, pppd_pppoe_service);
    108     }
    109     SET_STRING(conn->ifName, devnam);
    110     conn->discoverySocket = -1;
    111     conn->sessionSocket = -1;
    112     conn->useHostUniq = 1;
    113     conn->printACNames = printACNames;
    114     return 1;
    115 }
    116 
    117 /**********************************************************************
    118  * %FUNCTION: PPPOEConnectDevice
    119  * %ARGUMENTS:
    120  * None
    121  * %RETURNS:
    122  * Non-negative if all goes well; -1 otherwise
    123  * %DESCRIPTION:
    124  * Connects PPPoE device.
    125  ***********************************************************************/
    126 static int
    127 PPPOEConnectDevice(void)
    128 {
    129     struct sockaddr_pppox sp;
    130 
    131     strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));
    132     if (existingSession) {
    133 	unsigned int mac[ETH_ALEN];
    134 	int i, ses;
    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");
    139 	}
    140 	conn->session = htons(ses);
    141 	for (i=0; i<ETH_ALEN; i++) {
    142 	    conn->peerEth[i] = (unsigned char) mac[i];
    143 	}
    144     } else {
    145 	discovery(conn);
    146 	if (conn->discoveryState != STATE_SESSION) {
    147 	    error("Unable to complete PPPoE Discovery");
    148 	    return -1;
    149 	}
    150     }
    151 
    152     /* Set PPPoE session-number for further consumption */
    153     ppp_session_number = ntohs(conn->session);
    154 
    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");
    159     }
    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);
    165 
    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]);
    174 
    175     if (connect(conn->sessionSocket, (struct sockaddr *) &sp,
    176 		sizeof(struct sockaddr_pppox)) < 0) {
    177 	fatal("Failed to connect PPPoE socket: %d %m", errno);
    178 	return -1;
    179     }
    180 
    181     return conn->sessionSocket;
    182 }
    183 
    184 static void
    185 PPPOESendConfig(int mtu,
    186 		u_int32_t asyncmap,
    187 		int pcomp,
    188 		int accomp)
    189 {
    190     int sock;
    191     struct ifreq ifr;
    192 
    193     if (mtu > MAX_PPPOE_MTU) {
    194 	warn("Couldn't increase MTU to %d", mtu);
    195 	mtu = MAX_PPPOE_MTU;
    196     }
    197     sock = socket(AF_INET, SOCK_DGRAM, 0);
    198     if (sock < 0) {
    199 	error("Couldn't create IP socket: %m");
    200 	return;
    201     }
    202     strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
    203     ifr.ifr_mtu = mtu;
    204     if (ioctl(sock, SIOCSIFMTU, &ifr) < 0) {
    205 	error("Couldn't set interface MTU to %d: %m", mtu);
    206 	return;
    207     }
    208     (void) close (sock);
    209 }
    210 
    211 
    212 static void
    213 PPPOERecvConfig(int mru,
    214 		u_int32_t asyncmap,
    215 		int pcomp,
    216 		int accomp)
    217 {
    218     if (mru > MAX_PPPOE_MTU)
    219 	warn("Couldn't increase MRU to %d", mru);
    220 }
    221 
    222 /**********************************************************************
    223  * %FUNCTION: PPPOEDisconnectDevice
    224  * %ARGUMENTS:
    225  * None
    226  * %RETURNS:
    227  * Nothing
    228  * %DESCRIPTION:
    229  * Disconnects PPPoE device
    230  ***********************************************************************/
    231 static void
    232 PPPOEDisconnectDevice(void)
    233 {
    234     struct sockaddr_pppox sp;
    235 
    236     sp.sa_family = AF_PPPOX;
    237     sp.sa_protocol = PX_PROTO_OE;
    238     sp.sa_addr.pppoe.sid = 0;
    239     memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ);
    240     memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN);
    241     if (connect(conn->sessionSocket, (struct sockaddr *) &sp,
    242 		sizeof(struct sockaddr_pppox)) < 0) {
    243 	fatal("Failed to disconnect PPPoE socket: %d %m", errno);
    244 	return;
    245     }
    246     close(conn->sessionSocket);
    247     /* don't send PADT?? */
    248     close(conn->discoverySocket);
    249 }
    250 
    251 static void
    252 PPPOEDeviceOptions(void)
    253 {
    254     char buf[256];
    255     snprintf(buf, 256, _PATH_ETHOPT "%s",devnam);
    256     if(!options_from_file(buf, 0, 0, 1))
    257 	exit(EXIT_OPTION_ERROR);
    258 
    259 }
    260 
    261 struct channel pppoe_channel;
    262 
    263 /**********************************************************************
    264  * %FUNCTION: PPPoEDevnameHook
    265  * %ARGUMENTS:
    266  * cmd -- the command (actually, the device name
    267  * argv -- argument vector
    268  * doit -- if non-zero, set device name.  Otherwise, just check if possible
    269  * %RETURNS:
    270  * 1 if we will handle this device; 0 otherwise.
    271  * %DESCRIPTION:
    272  * Checks if name is a valid interface name; if so, returns 1.  Also
    273  * sets up devnam (string representation of device).
    274  ***********************************************************************/
    275 static int
    276 PPPoEDevnameHook(char *cmd, char **argv, int doit)
    277 {
    278     int r = 1;
    279     int fd;
    280     struct ifreq ifr;
    281 
    282     /* Only do it if name is "ethXXX", "nasXXX", "tapXXX" or "nic-XXXX.
    283        In latter case strip off the "nic-" */
    284     /* Thanks to Russ Couturier for this fix */
    285     if (strlen(cmd) > 4 && !strncmp(cmd, "nic-", 4)) {
    286 	/* Strip off "nic-" */
    287 	cmd += 4;
    288     } else if (strlen(cmd) < 4
    289 	       || (strncmp(cmd, "eth", 3) && strncmp(cmd, "nas", 3)
    290 		   && strncmp(cmd, "tap", 3) && strncmp(cmd, "br", 2))) {
    291 	return 0;
    292     }
    293 
    294     /* Open a socket */
    295     if ((fd = socket(PF_PACKET, SOCK_RAW, 0)) < 0) {
    296 	r = 0;
    297     }
    298 
    299     /* Try getting interface index */
    300     if (r) {
    301 	strncpy(ifr.ifr_name, cmd, sizeof(ifr.ifr_name));
    302 	if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
    303 	    r = 0;
    304 	} else {
    305 	    if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
    306 		r = 0;
    307 	    } else {
    308 		if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
    309 		    error("Interface %s not Ethernet", cmd);
    310 		    r=0;
    311 		}
    312 	    }
    313 	}
    314     }
    315 
    316     /* Close socket */
    317     close(fd);
    318     if (r) {
    319 	strncpy(devnam, cmd, sizeof(devnam));
    320 	if (the_channel != &pppoe_channel) {
    321 
    322 	    the_channel = &pppoe_channel;
    323 	    modem = 0;
    324 
    325 	    lcp_allowoptions[0].neg_accompression = 0;
    326 	    lcp_wantoptions[0].neg_accompression = 0;
    327 
    328 	    lcp_allowoptions[0].neg_asyncmap = 0;
    329 	    lcp_wantoptions[0].neg_asyncmap = 0;
    330 
    331 	    lcp_allowoptions[0].neg_pcompression = 0;
    332 	    lcp_wantoptions[0].neg_pcompression = 0;
    333 
    334 	    ccp_allowoptions[0].deflate = 0 ;
    335 	    ccp_wantoptions[0].deflate = 0 ;
    336 
    337 	    ipcp_allowoptions[0].neg_vj=0;
    338 	    ipcp_wantoptions[0].neg_vj=0;
    339 
    340 	    ccp_allowoptions[0].bsd_compress = 0;
    341 	    ccp_wantoptions[0].bsd_compress = 0;
    342 
    343 	    PPPOEInitDevice();
    344 	}
    345 	return 1;
    346     }
    347 
    348     return r;
    349 }
    350 
    351 /**********************************************************************
    352  * %FUNCTION: plugin_init
    353  * %ARGUMENTS:
    354  * None
    355  * %RETURNS:
    356  * Nothing
    357  * %DESCRIPTION:
    358  * Initializes hooks for pppd plugin
    359  ***********************************************************************/
    360 void
    361 plugin_init(void)
    362 {
    363     if (!ppp_available() && !new_style_driver) {
    364 	fatal("Linux kernel does not support PPPoE -- are you running 2.4.x?");
    365     }
    366 
    367     add_options(Options);
    368 
    369     info("RP-PPPoE plugin version %s compiled against pppd %s",
    370 	 RP_VERSION, VERSION);
    371 }
    372 
    373 /**********************************************************************
    374 *%FUNCTION: fatalSys
    375 *%ARGUMENTS:
    376 * str -- error message
    377 *%RETURNS:
    378 * Nothing
    379 *%DESCRIPTION:
    380 * Prints a message plus the errno value to stderr and syslog and exits.
    381 ***********************************************************************/
    382 void
    383 fatalSys(char const *str)
    384 {
    385     char buf[1024];
    386     int i = errno;
    387     sprintf(buf, "%.256s: %.256s", str, strerror(i));
    388     printErr(buf);
    389     sprintf(buf, "RP-PPPoE: %.256s: %.256s", str, strerror(i));
    390     sendPADT(conn, buf);
    391     exit(1);
    392 }
    393 
    394 /**********************************************************************
    395 *%FUNCTION: rp_fatal
    396 *%ARGUMENTS:
    397 * str -- error message
    398 *%RETURNS:
    399 * Nothing
    400 *%DESCRIPTION:
    401 * Prints a message to stderr and syslog and exits.
    402 ***********************************************************************/
    403 void
    404 rp_fatal(char const *str)
    405 {
    406     char buf[1024];
    407     printErr(str);
    408     sprintf(buf, "RP-PPPoE: %.256s", str);
    409     sendPADT(conn, buf);
    410     exit(1);
    411 }
    412 /**********************************************************************
    413 *%FUNCTION: sysErr
    414 *%ARGUMENTS:
    415 * str -- error message
    416 *%RETURNS:
    417 * Nothing
    418 *%DESCRIPTION:
    419 * Prints a message plus the errno value to syslog.
    420 ***********************************************************************/
    421 void
    422 sysErr(char const *str)
    423 {
    424     rp_fatal(str);
    425 }
    426 
    427 
    428 struct channel pppoe_channel = {
    429     options: Options,
    430     process_extra_options: &PPPOEDeviceOptions,
    431     check_options: NULL,
    432     connect: &PPPOEConnectDevice,
    433     disconnect: &PPPOEDisconnectDevice,
    434     establish_ppp: &generic_establish_ppp,
    435     disestablish_ppp: &generic_disestablish_ppp,
    436     send_config: &PPPOESendConfig,
    437     recv_config: &PPPOERecvConfig,
    438     close: NULL,
    439     cleanup: NULL
    440 };
    441