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 24static char const RCSID[] = 25"$Id: plugin.c,v 1.15 2006/05/29 23:29:16 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 57char pppd_version[] = VERSION; 58 59/* From sys-linux.c in pppd -- MUST FIX THIS! */ 60extern int new_style_driver; 61 62char *pppd_pppoe_service = NULL; 63static char *acName = NULL; 64static char *existingSession = NULL; 65static int printACNames = 0; 66 67static int PPPoEDevnameHook(char *cmd, char **argv, int doit); 68static 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 84static PPPoEConnection *conn = NULL; 85 86/********************************************************************** 87 * %FUNCTION: PPPOEInitDevice 88 * %ARGUMENTS: 89 * None 90 * %RETURNS: 91 * 92 * %DESCRIPTION: 93 * Initializes PPPoE device. 94 ***********************************************************************/ 95static int 96PPPOEInitDevice(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 ***********************************************************************/ 126static int 127PPPOEConnectDevice(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 184static void 185PPPOERecvConfig(int mru, 186 u_int32_t asyncmap, 187 int pcomp, 188 int accomp) 189{ 190#if 0 /* broken protocol, but no point harrassing the users I guess... */ 191 if (mru > MAX_PPPOE_MTU) 192 warn("Couldn't increase MRU to %d", mru); 193#endif 194} 195 196/********************************************************************** 197 * %FUNCTION: PPPOEDisconnectDevice 198 * %ARGUMENTS: 199 * None 200 * %RETURNS: 201 * Nothing 202 * %DESCRIPTION: 203 * Disconnects PPPoE device 204 ***********************************************************************/ 205static void 206PPPOEDisconnectDevice(void) 207{ 208 struct sockaddr_pppox sp; 209 210 sp.sa_family = AF_PPPOX; 211 sp.sa_protocol = PX_PROTO_OE; 212 sp.sa_addr.pppoe.sid = 0; 213 memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ); 214 memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN); 215 if (connect(conn->sessionSocket, (struct sockaddr *) &sp, 216 sizeof(struct sockaddr_pppox)) < 0) { 217 fatal("Failed to disconnect PPPoE socket: %d %m", errno); 218 return; 219 } 220 close(conn->sessionSocket); 221 /* don't send PADT?? */ 222 close(conn->discoverySocket); 223} 224 225static void 226PPPOEDeviceOptions(void) 227{ 228 char buf[256]; 229 snprintf(buf, 256, _PATH_ETHOPT "%s",devnam); 230 if(!options_from_file(buf, 0, 0, 1)) 231 exit(EXIT_OPTION_ERROR); 232 233} 234 235struct channel pppoe_channel; 236 237/********************************************************************** 238 * %FUNCTION: PPPoEDevnameHook 239 * %ARGUMENTS: 240 * cmd -- the command (actually, the device name 241 * argv -- argument vector 242 * doit -- if non-zero, set device name. Otherwise, just check if possible 243 * %RETURNS: 244 * 1 if we will handle this device; 0 otherwise. 245 * %DESCRIPTION: 246 * Checks if name is a valid interface name; if so, returns 1. Also 247 * sets up devnam (string representation of device). 248 ***********************************************************************/ 249static int 250PPPoEDevnameHook(char *cmd, char **argv, int doit) 251{ 252 int r = 1; 253 int fd; 254 struct ifreq ifr; 255 256 /* 257 * Take any otherwise-unrecognized option as a possible device name, 258 * and test if it is the name of a network interface with a 259 * hardware address whose sa_family is ARPHRD_ETHER. 260 */ 261 if (strlen(cmd) > 4 && !strncmp(cmd, "nic-", 4)) { 262 /* Strip off "nic-" */ 263 cmd += 4; 264 } 265 266 /* Open a socket */ 267 if ((fd = socket(PF_PACKET, SOCK_RAW, 0)) < 0) { 268 r = 0; 269 } 270 271 /* Try getting interface index */ 272 if (r) { 273 strncpy(ifr.ifr_name, cmd, sizeof(ifr.ifr_name)); 274 if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { 275 r = 0; 276 } else { 277 if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { 278 r = 0; 279 } else { 280 if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { 281 if (doit) 282 error("Interface %s not Ethernet", cmd); 283 r = 0; 284 } 285 } 286 } 287 } 288 289 /* Close socket */ 290 close(fd); 291 if (r && doit) { 292 strncpy(devnam, cmd, sizeof(devnam)); 293 if (the_channel != &pppoe_channel) { 294 295 the_channel = &pppoe_channel; 296 modem = 0; 297 298 PPPOEInitDevice(); 299 } 300 return 1; 301 } 302 303 return r; 304} 305 306/********************************************************************** 307 * %FUNCTION: plugin_init 308 * %ARGUMENTS: 309 * None 310 * %RETURNS: 311 * Nothing 312 * %DESCRIPTION: 313 * Initializes hooks for pppd plugin 314 ***********************************************************************/ 315void 316plugin_init(void) 317{ 318 if (!ppp_available() && !new_style_driver) { 319 fatal("Linux kernel does not support PPPoE -- are you running 2.4.x?"); 320 } 321 322 add_options(Options); 323 324 info("RP-PPPoE plugin version %s compiled against pppd %s", 325 RP_VERSION, VERSION); 326} 327 328/********************************************************************** 329*%FUNCTION: fatalSys 330*%ARGUMENTS: 331* str -- error message 332*%RETURNS: 333* Nothing 334*%DESCRIPTION: 335* Prints a message plus the errno value to stderr and syslog and exits. 336***********************************************************************/ 337void 338fatalSys(char const *str) 339{ 340 char buf[1024]; 341 int i = errno; 342 sprintf(buf, "%.256s: %.256s", str, strerror(i)); 343 printErr(buf); 344 sprintf(buf, "RP-PPPoE: %.256s: %.256s", str, strerror(i)); 345 sendPADT(conn, buf); 346 exit(1); 347} 348 349/********************************************************************** 350*%FUNCTION: rp_fatal 351*%ARGUMENTS: 352* str -- error message 353*%RETURNS: 354* Nothing 355*%DESCRIPTION: 356* Prints a message to stderr and syslog and exits. 357***********************************************************************/ 358void 359rp_fatal(char const *str) 360{ 361 char buf[1024]; 362 printErr(str); 363 sprintf(buf, "RP-PPPoE: %.256s", str); 364 sendPADT(conn, buf); 365 exit(1); 366} 367/********************************************************************** 368*%FUNCTION: sysErr 369*%ARGUMENTS: 370* str -- error message 371*%RETURNS: 372* Nothing 373*%DESCRIPTION: 374* Prints a message plus the errno value to syslog. 375***********************************************************************/ 376void 377sysErr(char const *str) 378{ 379 rp_fatal(str); 380} 381 382void pppoe_check_options(void) 383{ 384 lcp_allowoptions[0].neg_accompression = 0; 385 lcp_wantoptions[0].neg_accompression = 0; 386 387 lcp_allowoptions[0].neg_asyncmap = 0; 388 lcp_wantoptions[0].neg_asyncmap = 0; 389 390 lcp_allowoptions[0].neg_pcompression = 0; 391 lcp_wantoptions[0].neg_pcompression = 0; 392 393 if (lcp_allowoptions[0].mru > MAX_PPPOE_MTU) 394 lcp_allowoptions[0].mru = MAX_PPPOE_MTU; 395 if (lcp_wantoptions[0].mru > MAX_PPPOE_MTU) 396 lcp_wantoptions[0].mru = MAX_PPPOE_MTU; 397 398 ccp_allowoptions[0].deflate = 0; 399 ccp_wantoptions[0].deflate = 0; 400 401 ipcp_allowoptions[0].neg_vj = 0; 402 ipcp_wantoptions[0].neg_vj = 0; 403 404 ccp_allowoptions[0].bsd_compress = 0; 405 ccp_wantoptions[0].bsd_compress = 0; 406} 407 408struct channel pppoe_channel = { 409 options: Options, 410 process_extra_options: &PPPOEDeviceOptions, 411 check_options: pppoe_check_options, 412 connect: &PPPOEConnectDevice, 413 disconnect: &PPPOEDisconnectDevice, 414 establish_ppp: &generic_establish_ppp, 415 disestablish_ppp: &generic_disestablish_ppp, 416 send_config: NULL, 417 recv_config: &PPPOERecvConfig, 418 close: NULL, 419 cleanup: NULL 420}; 421