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.1.1.1 2008/10/15 03:30:50 james26_jang 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 "/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}; 83int (*OldDevnameHook)(char *cmd, char **argv, int doit) = NULL; 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 if (mtu != PPP_MTU) 195 warn("Couldn't increase MTU to %d", mtu); 196 mtu = MAX_PPPOE_MTU; 197 } 198 sock = socket(AF_INET, SOCK_DGRAM, 0); 199 if (sock < 0) { 200 error("Couldn't create IP socket: %m"); 201 return; 202 } 203 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 204 ifr.ifr_mtu = mtu; 205 if (ioctl(sock, SIOCSIFMTU, &ifr) < 0) { 206 error("Couldn't set interface MTU to %d: %m", mtu); 207 return; 208 } 209 (void) close (sock); 210} 211 212 213static void 214PPPOERecvConfig(int mru, 215 u_int32_t asyncmap, 216 int pcomp, 217 int accomp) 218{ 219 if (mru > MAX_PPPOE_MTU && mru != PPP_MRU) 220 warn("Couldn't increase MRU to %d", mru); 221} 222 223/********************************************************************** 224 * %FUNCTION: PPPOEDisconnectDevice 225 * %ARGUMENTS: 226 * None 227 * %RETURNS: 228 * Nothing 229 * %DESCRIPTION: 230 * Disconnects PPPoE device 231 ***********************************************************************/ 232static void 233PPPOEDisconnectDevice(void) 234{ 235 struct sockaddr_pppox sp; 236 237 sp.sa_family = AF_PPPOX; 238 sp.sa_protocol = PX_PROTO_OE; 239 sp.sa_addr.pppoe.sid = 0; 240 memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ); 241 memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN); 242 if (connect(conn->sessionSocket, (struct sockaddr *) &sp, 243 sizeof(struct sockaddr_pppox)) < 0) { 244 fatal("Failed to disconnect PPPoE socket: %d %m", errno); 245 return; 246 } 247 close(conn->sessionSocket); 248} 249 250static void 251PPPOEDeviceOptions(void) 252{ 253 char buf[256]; 254 snprintf(buf, 256, _PATH_ETHOPT "%s",devnam); 255 if(!options_from_file(buf, 0, 0, 1)) 256 exit(EXIT_OPTION_ERROR); 257 258} 259 260struct channel pppoe_channel; 261 262/********************************************************************** 263 * %FUNCTION: PPPoEDevnameHook 264 * %ARGUMENTS: 265 * cmd -- the command (actually, the device name 266 * argv -- argument vector 267 * doit -- if non-zero, set device name. Otherwise, just check if possible 268 * %RETURNS: 269 * 1 if we will handle this device; 0 otherwise. 270 * %DESCRIPTION: 271 * Checks if name is a valid interface name; if so, returns 1. Also 272 * sets up devnam (string representation of device). 273 ***********************************************************************/ 274static int 275PPPoEDevnameHook(char *cmd, char **argv, int doit) 276{ 277 int r = 1; 278 int fd; 279 struct ifreq ifr; 280 281 /* Only do it if name is "ethXXX", "nasXXX", "tapXXX" or "nic-XXXX. 282 In latter case strip off the "nic-" */ 283 /* Thanks to Russ Couturier for this fix */ 284 if (strlen(cmd) > 4 && !strncmp(cmd, "nic-", 4)) { 285 /* Strip off "nic-" */ 286 cmd += 4; 287 } else if (strlen(cmd) < 4 || (strncmp(cmd, "eth", 3) && 288 strncmp(cmd, "nas", 3) && strncmp(cmd, "tap", 3))) { 289 if (OldDevnameHook) return OldDevnameHook(cmd, argv, doit); 290 return 0; 291 } 292 293 /* Open a socket */ 294 if ((fd = socket(PF_PACKET, SOCK_RAW, 0)) < 0) { 295 r = 0; 296 } 297 298 /* Try getting interface index */ 299 if (r) { 300 strncpy(ifr.ifr_name, cmd, sizeof(ifr.ifr_name)); 301 if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { 302 r = 0; 303 } else { 304 if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { 305 r = 0; 306 } else { 307 if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { 308 error("Interface %s not Ethernet", cmd); 309 r=0; 310 } 311 } 312 } 313 } 314 315 /* Close socket */ 316 close(fd); 317 if (r) { 318 strncpy(devnam, cmd, sizeof(devnam)); 319 if (the_channel != &pppoe_channel) { 320 321 the_channel = &pppoe_channel; 322 modem = 0; 323 324 lcp_allowoptions[0].neg_accompression = 0; 325 lcp_wantoptions[0].neg_accompression = 0; 326 327 lcp_allowoptions[0].neg_asyncmap = 0; 328 lcp_wantoptions[0].neg_asyncmap = 0; 329 330 lcp_allowoptions[0].neg_pcompression = 0; 331 lcp_wantoptions[0].neg_pcompression = 0; 332 333 ccp_allowoptions[0].deflate = 0 ; 334 ccp_wantoptions[0].deflate = 0 ; 335 336 ipcp_allowoptions[0].neg_vj=0; 337 ipcp_wantoptions[0].neg_vj=0; 338 339 ccp_allowoptions[0].bsd_compress = 0; 340 ccp_wantoptions[0].bsd_compress = 0; 341 342 PPPOEInitDevice(); 343 } 344 return 1; 345 } 346 347 if (OldDevnameHook) r = OldDevnameHook(cmd, argv, doit); 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