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.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 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 185PPPOESendConfig(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 212static void 213PPPOERecvConfig(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 ***********************************************************************/ 231static void 232PPPOEDisconnectDevice(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 251static void 252PPPOEDeviceOptions(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 261struct 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 ***********************************************************************/ 275static int 276PPPoEDevnameHook(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 ***********************************************************************/ 360void 361plugin_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***********************************************************************/ 382void 383fatalSys(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***********************************************************************/ 403void 404rp_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***********************************************************************/ 421void 422sysErr(char const *str) 423{ 424 rp_fatal(str); 425} 426 427 428struct 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