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***********************************************************************/ 24 25static char const RCSID[] = 26"Id: plugin.c,v 1.17 2008/06/15 04:35:50 paulus Exp "; 27 28#define _GNU_SOURCE 1 29#include "pppoe.h" 30 31#include "pppd/pppd.h" 32#include "pppd/fsm.h" 33#include "pppd/lcp.h" 34#include "pppd/ipcp.h" 35#include "pppd/ccp.h" 36/* #include "pppd/pathnames.h" */ 37 38#include <linux/types.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/if_arp.h> 50#include <linux/ppp_defs.h> 51#include <linux/if_pppox.h> 52 53#ifndef _ROOT_PATH 54#define _ROOT_PATH "" 55#endif 56 57#define _PATH_ETHOPT _ROOT_PATH "/etc/ppp/options." 58 59char pppd_version[] = VERSION; 60 61/* From sys-linux.c in pppd -- MUST FIX THIS! */ 62extern int new_style_driver; 63 64char *pppd_pppoe_service = NULL; 65static char *acName = NULL; 66static char *existingSession = NULL; 67static int printACNames = 0; 68static char *pppoe_reqd_mac = NULL; 69unsigned char pppoe_reqd_mac_addr[6]; 70static char *pppoe_host_uniq; 71static int pppoe_padi_timeout = PADI_TIMEOUT; 72static int pppoe_padi_attempts = MAX_PADI_ATTEMPTS; 73 74static int PPPoEDevnameHook(char *cmd, char **argv, int doit); 75static option_t Options[] = { 76 { "device name", o_wild, (void *) &PPPoEDevnameHook, 77 "PPPoE device name", 78 OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG | OPT_A2STRVAL | OPT_STATIC, 79 devnam}, 80 { "pppoe-service", o_string, &pppd_pppoe_service, 81 "Desired PPPoE service name" }, 82 { "rp_pppoe_service", o_string, &pppd_pppoe_service, 83 "Legacy alias for pppoe-service", OPT_ALIAS }, 84 { "pppoe-ac", o_string, &acName, 85 "Desired PPPoE access concentrator name" }, 86 { "rp_pppoe_ac", o_string, &acName, 87 "Legacy alias for pppoe-ac", OPT_ALIAS }, 88 { "pppoe-sess", o_string, &existingSession, 89 "Attach to existing session (sessid:macaddr)" }, 90 { "rp_pppoe_sess", o_string, &existingSession, 91 "Legacy alias for pppoe-sess", OPT_ALIAS }, 92 { "pppoe-verbose", o_int, &printACNames, 93 "Be verbose about discovered access concentrators" }, 94 { "rp_pppoe_verbose", o_int, &printACNames, 95 "Legacy alias for pppoe-verbose", OPT_ALIAS }, 96 { "pppoe-mac", o_string, &pppoe_reqd_mac, 97 "Only connect to specified MAC address" }, 98 { "pppoe-host-uniq", o_string, &pppoe_host_uniq, 99 "Set the Host-Uniq to the supplied hex string" }, 100 { "host-uniq", o_string, &pppoe_host_uniq, 101 "Legacy alias for pppoe-host-uniq", OPT_ALIAS }, 102 { "pppoe-padi-timeout", o_int, &pppoe_padi_timeout, 103 "Initial timeout for discovery packets in seconds" }, 104 { "pppoe-padi-attempts", o_int, &pppoe_padi_attempts, 105 "Number of discovery attempts" }, 106 { NULL } 107}; 108int (*OldDevnameHook)(char *cmd, char **argv, int doit) = NULL; 109static PPPoEConnection *conn = NULL; 110 111/********************************************************************** 112 * %FUNCTION: PPPOEInitDevice 113 * %ARGUMENTS: 114 * None 115 * %RETURNS: 116 * 117 * %DESCRIPTION: 118 * Initializes PPPoE device. 119 ***********************************************************************/ 120static int 121PPPOEInitDevice(void) 122{ 123 conn = malloc(sizeof(PPPoEConnection)); 124 if (!conn) { 125 novm("PPPoE session data"); 126 } 127 memset(conn, 0, sizeof(PPPoEConnection)); 128 conn->ifName = devnam; 129 conn->discoverySocket = -1; 130 conn->sessionSocket = -1; 131 conn->printACNames = printACNames; 132 conn->discoveryTimeout = pppoe_padi_timeout; 133 conn->discoveryAttempts = pppoe_padi_attempts; 134 return 1; 135} 136 137/********************************************************************** 138 * %FUNCTION: PPPOEConnectDevice 139 * %ARGUMENTS: 140 * None 141 * %RETURNS: 142 * Non-negative if all goes well; -1 otherwise 143 * %DESCRIPTION: 144 * Connects PPPoE device. 145 ***********************************************************************/ 146static int 147PPPOEConnectDevice(void) 148{ 149 struct sockaddr_pppox sp; 150 struct ifreq ifr; 151 int s; 152 153 /* Open session socket before discovery phase, to avoid losing session */ 154 /* packets sent by peer just after PADS packet (noted on some Cisco */ 155 /* server equipment). */ 156 /* Opening this socket just before waitForPADS in the discovery() */ 157 /* function would be more appropriate, but it would mess-up the code */ 158 conn->sessionSocket = socket(AF_PPPOX, SOCK_STREAM, PX_PROTO_OE); 159 if (conn->sessionSocket < 0) { 160 error("Failed to create PPPoE socket: %m"); 161 return -1; 162 } 163 164 /* Restore configuration */ 165 lcp_allowoptions[0].mru = conn->mtu; 166 lcp_wantoptions[0].mru = conn->mru; 167 168 /* Update maximum MRU */ 169 s = socket(AF_INET, SOCK_DGRAM, 0); 170 if (s < 0) { 171 error("Can't get MTU for %s: %m", conn->ifName); 172 goto errout; 173 } 174 strlcpy(ifr.ifr_name, conn->ifName, sizeof(ifr.ifr_name)); 175 if (ioctl(s, SIOCGIFMTU, &ifr) < 0) { 176 error("Can't get MTU for %s: %m", conn->ifName); 177 close(s); 178 goto errout; 179 } 180 close(s); 181 182 if (lcp_allowoptions[0].mru > ifr.ifr_mtu - TOTAL_OVERHEAD) 183 lcp_allowoptions[0].mru = ifr.ifr_mtu - TOTAL_OVERHEAD; 184 if (lcp_wantoptions[0].mru > ifr.ifr_mtu - TOTAL_OVERHEAD) 185 lcp_wantoptions[0].mru = ifr.ifr_mtu - TOTAL_OVERHEAD; 186 187 if (pppoe_host_uniq) { 188 if (!parseHostUniq(pppoe_host_uniq, &conn->hostUniq)) 189 fatal("Illegal value for pppoe-host-uniq option"); 190 } else { 191 /* if a custom host-uniq is not supplied, use our PID */ 192 pid_t pid = getpid(); 193 conn->hostUniq.type = htons(TAG_HOST_UNIQ); 194 conn->hostUniq.length = htons(sizeof(pid)); 195 memcpy(conn->hostUniq.payload, &pid, sizeof(pid)); 196 } 197 198 conn->acName = acName; 199 conn->serviceName = pppd_pppoe_service; 200 strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam)); 201 if (existingSession) { 202 unsigned int mac[ETH_ALEN]; 203 int i, ses; 204 if (sscanf(existingSession, "%d:%x:%x:%x:%x:%x:%x", 205 &ses, &mac[0], &mac[1], &mac[2], 206 &mac[3], &mac[4], &mac[5]) != 7) { 207 fatal("Illegal value for pppoe-sess option"); 208 } 209 conn->session = htons(ses); 210 for (i=0; i<ETH_ALEN; i++) { 211 conn->peerEth[i] = (unsigned char) mac[i]; 212 } 213 } else { 214 conn->discoverySocket = 215 openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth); 216 if (conn->discoverySocket < 0) { 217 error("Failed to create PPPoE discovery socket: %m"); 218 goto errout; 219 } 220 discovery(conn); 221 if (conn->discoveryState != STATE_SESSION) { 222 error("Unable to complete PPPoE Discovery"); 223 goto errout; 224 } 225 } 226 227 /* Set PPPoE session-number for further consumption */ 228 ppp_session_number = ntohs(conn->session); 229 230 sp.sa_family = AF_PPPOX; 231 sp.sa_protocol = PX_PROTO_OE; 232 sp.sa_addr.pppoe.sid = conn->session; 233 memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ); 234 memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN); 235 236 /* Set remote_number for ServPoET */ 237 sprintf(remote_number, "%02X:%02X:%02X:%02X:%02X:%02X", 238 (unsigned) conn->peerEth[0], 239 (unsigned) conn->peerEth[1], 240 (unsigned) conn->peerEth[2], 241 (unsigned) conn->peerEth[3], 242 (unsigned) conn->peerEth[4], 243 (unsigned) conn->peerEth[5]); 244 245 warn("Connected to %02X:%02X:%02X:%02X:%02X:%02X via interface %s", 246 (unsigned) conn->peerEth[0], 247 (unsigned) conn->peerEth[1], 248 (unsigned) conn->peerEth[2], 249 (unsigned) conn->peerEth[3], 250 (unsigned) conn->peerEth[4], 251 (unsigned) conn->peerEth[5], 252 conn->ifName); 253 254 script_setenv("MACREMOTE", remote_number, 0); 255 256 if (connect(conn->sessionSocket, (struct sockaddr *) &sp, 257 sizeof(struct sockaddr_pppox)) < 0) { 258 error("Failed to connect PPPoE socket: %d %m", errno); 259 goto errout; 260 } 261 262 return conn->sessionSocket; 263 264 errout: 265 if (conn->discoverySocket >= 0) { 266 sendPADT(conn, NULL); 267 close(conn->discoverySocket); 268 conn->discoverySocket = -1; 269 } 270 close(conn->sessionSocket); 271 return -1; 272} 273 274static void 275PPPOERecvConfig(int mru, 276 u_int32_t asyncmap, 277 int pcomp, 278 int accomp) 279{ 280#if 0 /* broken protocol, but no point harrassing the users I guess... */ 281 if (mru > MAX_PPPOE_MTU) 282 warn("Couldn't increase MRU to %d", mru); 283#endif 284} 285 286/********************************************************************** 287 * %FUNCTION: PPPOEDisconnectDevice 288 * %ARGUMENTS: 289 * None 290 * %RETURNS: 291 * Nothing 292 * %DESCRIPTION: 293 * Disconnects PPPoE device 294 ***********************************************************************/ 295static void 296PPPOEDisconnectDevice(void) 297{ 298 struct sockaddr_pppox sp; 299 300 sp.sa_family = AF_PPPOX; 301 sp.sa_protocol = PX_PROTO_OE; 302 sp.sa_addr.pppoe.sid = 0; 303 memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ); 304 memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN); 305 if (connect(conn->sessionSocket, (struct sockaddr *) &sp, 306 sizeof(struct sockaddr_pppox)) < 0 && errno != EALREADY) 307 error("Failed to disconnect PPPoE socket: %d %m", errno); 308 close(conn->sessionSocket); 309 if (conn->discoverySocket >= 0) { 310 sendPADT(conn, NULL); 311 close(conn->discoverySocket); 312 } 313} 314 315static void 316PPPOEDeviceOptions(void) 317{ 318 char buf[MAXPATHLEN]; 319 320 strlcpy(buf, _PATH_ETHOPT, MAXPATHLEN); 321 strlcat(buf, devnam, MAXPATHLEN); 322 if (!options_from_file(buf, 0, 0, 1)) 323 exit(EXIT_OPTION_ERROR); 324 325} 326 327struct channel pppoe_channel; 328 329/********************************************************************** 330 * %FUNCTION: PPPoEDevnameHook 331 * %ARGUMENTS: 332 * cmd -- the command (actually, the device name 333 * argv -- argument vector 334 * doit -- if non-zero, set device name. Otherwise, just check if possible 335 * %RETURNS: 336 * 1 if we will handle this device; 0 otherwise. 337 * %DESCRIPTION: 338 * Checks if name is a valid interface name; if so, returns 1. Also 339 * sets up devnam (string representation of device). 340 ***********************************************************************/ 341static int 342PPPoEDevnameHook(char *cmd, char **argv, int doit) 343{ 344 int r = 1; 345 int fd; 346 struct ifreq ifr; 347 348 /* 349 * Take any otherwise-unrecognized option as a possible device name, 350 * and test if it is the name of a network interface with a 351 * hardware address whose sa_family is ARPHRD_ETHER. 352 */ 353 if (strlen(cmd) > 4 && !strncmp(cmd, "nic-", 4)) { 354 /* Strip off "nic-" */ 355 cmd += 4; 356 } 357 358 /* Open a socket */ 359 if ((fd = socket(PF_PACKET, SOCK_RAW, 0)) < 0) { 360 r = 0; 361 } 362 363 /* Try getting interface index */ 364 if (r) { 365 strlcpy(ifr.ifr_name, cmd, sizeof(ifr.ifr_name)); 366 if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { 367 r = 0; 368 } else { 369 if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { 370 r = 0; 371 } else { 372 if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { 373 if (doit) 374 error("Interface %s not Ethernet", cmd); 375 r = 0; 376 } 377 } 378 } 379 } 380 381 /* Close socket */ 382 close(fd); 383 if (r && doit) { 384 strlcpy(devnam, cmd, sizeof(devnam)); 385 if (the_channel != &pppoe_channel) { 386 387 the_channel = &pppoe_channel; 388 modem = 0; 389 390 PPPOEInitDevice(); 391 } 392 return 1; 393 } 394 395 return r; 396} 397 398/********************************************************************** 399 * %FUNCTION: plugin_init 400 * %ARGUMENTS: 401 * None 402 * %RETURNS: 403 * Nothing 404 * %DESCRIPTION: 405 * Initializes hooks for pppd plugin 406 ***********************************************************************/ 407void 408plugin_init(void) 409{ 410 if (!ppp_available() && !new_style_driver) { 411 fatal("Linux kernel does not support PPPoE -- are you running 2.4.x?"); 412 } 413 414 add_options(Options); 415 416 info("PPPoE plugin from pppd %s", VERSION); 417} 418 419void pppoe_check_options(void) 420{ 421 unsigned int mac[6]; 422 int i; 423 424 if (pppoe_reqd_mac != NULL) { 425 if (sscanf(pppoe_reqd_mac, "%x:%x:%x:%x:%x:%x", 426 &mac[0], &mac[1], &mac[2], &mac[3], 427 &mac[4], &mac[5]) != 6) { 428 option_error("cannot parse pppoe-mac option value"); 429 exit(EXIT_OPTION_ERROR); 430 } 431 for (i = 0; i < 6; ++i) 432 conn->req_peer_mac[i] = mac[i]; 433 conn->req_peer = 1; 434 } 435 436 lcp_allowoptions[0].neg_accompression = 0; 437 lcp_wantoptions[0].neg_accompression = 0; 438 439 lcp_allowoptions[0].neg_asyncmap = 0; 440 lcp_wantoptions[0].neg_asyncmap = 0; 441 442 lcp_allowoptions[0].neg_pcompression = 0; 443 lcp_wantoptions[0].neg_pcompression = 0; 444 445 if (lcp_allowoptions[0].mru > MAX_PPPOE_MTU) 446 lcp_allowoptions[0].mru = MAX_PPPOE_MTU; 447 if (lcp_wantoptions[0].mru > MAX_PPPOE_MTU) 448 lcp_wantoptions[0].mru = MAX_PPPOE_MTU; 449 450 /* Save configuration */ 451 conn->mtu = lcp_allowoptions[0].mru; 452 conn->mru = lcp_wantoptions[0].mru; 453 454 ccp_allowoptions[0].deflate = 0; 455 ccp_wantoptions[0].deflate = 0; 456 457 ipcp_allowoptions[0].neg_vj = 0; 458 ipcp_wantoptions[0].neg_vj = 0; 459 460 ccp_allowoptions[0].bsd_compress = 0; 461 ccp_wantoptions[0].bsd_compress = 0; 462} 463 464struct channel pppoe_channel = { 465 .options = Options, 466 .process_extra_options = &PPPOEDeviceOptions, 467 .check_options = pppoe_check_options, 468 .connect = &PPPOEConnectDevice, 469 .disconnect = &PPPOEDisconnectDevice, 470 .establish_ppp = &generic_establish_ppp, 471 .disestablish_ppp = &generic_disestablish_ppp, 472 .send_config = NULL, 473 .recv_config = &PPPOERecvConfig, 474 .close = NULL, 475 .cleanup = NULL 476}; 477