1/*********************************************************************** 2* 3* plugin.c 4* 5* pppd plugin for kernel-mode PPPoE on Linux 6* 7* Copyright (C) 2001-2012 by Roaring Penguin Software Inc. 8* Portions copyright 2000 Michal Ostrowski 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* LIC: GPL 24* 25***********************************************************************/ 26 27static char const RCSID[] = 28"$Id$"; 29 30#define _GNU_SOURCE 1 31#include "pppoe.h" 32 33#include "pppd/pppd.h" 34#include "pppd/fsm.h" 35#include "pppd/lcp.h" 36#include "pppd/ipcp.h" 37#include "pppd/ccp.h" 38/* #include "pppd/pathnames.h" */ 39 40#include <linux/types.h> 41#include <syslog.h> 42#include <sys/ioctl.h> 43#include <sys/types.h> 44#include <sys/socket.h> 45#include <sys/stat.h> 46#include <string.h> 47#include <stdlib.h> 48#include <errno.h> 49#include <unistd.h> 50#include <fcntl.h> 51#include <signal.h> 52#include <net/ethernet.h> 53#include <net/if_arp.h> 54#include <linux/ppp_defs.h> 55#include <linux/if_pppox.h> 56 57#ifndef _ROOT_PATH 58#define _ROOT_PATH "" 59#endif 60 61#define _PATH_ETHOPT _ROOT_PATH "/ppp/options." 62 63char pppd_version[] = VERSION; 64 65static int seen_devnam[2] = {0, 0}; 66static char *pppoe_reqd_mac = NULL; 67 68/* From sys-linux.c in pppd -- MUST FIX THIS! */ 69extern int new_style_driver; 70 71char *pppd_pppoe_service = NULL; 72static char *acName = NULL; 73static char *existingSession = NULL; 74static int printACNames = 0; 75 76static int PPPoEDevnameHook(char *cmd, char **argv, int doit); 77static option_t Options[] = { 78 { "device name", o_wild, (void *) &PPPoEDevnameHook, 79 "PPPoE device name", 80 OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG | OPT_A2STRVAL | OPT_STATIC, 81 devnam}, 82 { "rp_pppoe_service", o_string, &pppd_pppoe_service, 83 "Desired PPPoE service name" }, 84 { "rp_pppoe_ac", o_string, &acName, 85 "Desired PPPoE access concentrator name" }, 86 { "rp_pppoe_sess", o_string, &existingSession, 87 "Attach to existing session (sessid:macaddr)" }, 88 { "rp_pppoe_verbose", o_int, &printACNames, 89 "Be verbose about discovered access concentrators"}, 90 { "rp_pppoe_mac", o_string, &pppoe_reqd_mac, 91 "Only connect to specified MAC address" }, 92 { NULL } 93}; 94int (*OldDevnameHook)(char *cmd, char **argv, int doit) = NULL; 95static PPPoEConnection *conn = NULL; 96 97/********************************************************************** 98 * %FUNCTION: PPPOEInitDevice 99 * %ARGUMENTS: 100 * None 101 * %RETURNS: 102 * 103 * %DESCRIPTION: 104 * Initializes PPPoE device. 105 ***********************************************************************/ 106static int 107PPPOEInitDevice(void) 108{ 109 conn = malloc(sizeof(PPPoEConnection)); 110 if (!conn) { 111 fatal("Could not allocate memory for PPPoE session"); 112 } 113 memset(conn, 0, sizeof(PPPoEConnection)); 114 if (acName) { 115 SET_STRING(conn->acName, acName); 116 } 117 if (pppd_pppoe_service) { 118 SET_STRING(conn->serviceName, pppd_pppoe_service); 119 } 120 SET_STRING(conn->ifName, devnam); 121 conn->discoverySocket = -1; 122 conn->sessionSocket = -1; 123 conn->useHostUniq = 1; 124 conn->printACNames = printACNames; 125 conn->discoveryTimeout = PADI_TIMEOUT; 126 return 1; 127} 128 129/********************************************************************** 130 * %FUNCTION: PPPOEConnectDevice 131 * %ARGUMENTS: 132 * None 133 * %RETURNS: 134 * Non-negative if all goes well; -1 otherwise 135 * %DESCRIPTION: 136 * Connects PPPoE device. 137 ***********************************************************************/ 138static int 139PPPOEConnectDevice(void) 140{ 141 struct sockaddr_pppox sp; 142 struct ifreq ifr; 143 int s; 144 145 /* Restore configuration */ 146 lcp_allowoptions[0].mru = conn->mtu; 147 lcp_wantoptions[0].mru = conn->mru; 148 149 /* Update maximum MRU */ 150 s = socket(AF_INET, SOCK_DGRAM, 0); 151 if (s < 0) { 152 error("Can't get MTU for %s: %m", conn->ifName); 153 return -1; 154 } 155 strncpy(ifr.ifr_name, conn->ifName, sizeof(ifr.ifr_name)); 156 if (ioctl(s, SIOCGIFMTU, &ifr) < 0) { 157 error("Can't get MTU for %s: %m", conn->ifName); 158 close(s); 159 return -1; 160 } 161 close(s); 162 163 if (lcp_allowoptions[0].mru > ifr.ifr_mtu - TOTAL_OVERHEAD) { 164 lcp_allowoptions[0].mru = ifr.ifr_mtu - TOTAL_OVERHEAD; 165 } 166 if (lcp_wantoptions[0].mru > ifr.ifr_mtu - TOTAL_OVERHEAD) { 167 lcp_wantoptions[0].mru = ifr.ifr_mtu - TOTAL_OVERHEAD; 168 } 169 170 /* Open session socket before discovery phase, to avoid losing session */ 171 /* packets sent by peer just after PADS packet (noted on some Cisco */ 172 /* server equipment). */ 173 /* Opening this socket just before waitForPADS in the discovery() */ 174 /* function would be more appropriate, but it would mess-up the code */ 175 conn->sessionSocket = socket(AF_PPPOX, SOCK_STREAM, PX_PROTO_OE); 176 if (conn->sessionSocket < 0) { 177 error("Failed to create PPPoE socket: %m"); 178 return -1; 179 } 180 181 if (acName) { 182 SET_STRING(conn->acName, acName); 183 } 184 if (pppd_pppoe_service) { 185 SET_STRING(conn->serviceName, pppd_pppoe_service); 186 } 187 188 strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam)); 189 if (existingSession) { 190 unsigned int mac[ETH_ALEN]; 191 int i, ses; 192 if (sscanf(existingSession, "%d:%x:%x:%x:%x:%x:%x", 193 &ses, &mac[0], &mac[1], &mac[2], 194 &mac[3], &mac[4], &mac[5]) != 7) { 195 fatal("Illegal value for rp_pppoe_sess option"); 196 } 197 conn->session = htons(ses); 198 for (i=0; i<ETH_ALEN; i++) { 199 conn->peerEth[i] = (unsigned char) mac[i]; 200 } 201 } else { 202 conn->discoverySocket = 203 openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth, NULL); 204 discovery(conn); 205 if (conn->discoveryState != STATE_SESSION) { 206 error("Unable to complete PPPoE Discovery"); 207 goto ERROR; 208 } 209 } 210 211 /* Set PPPoE session-number for further consumption */ 212 ppp_session_number = ntohs(conn->session); 213 214 sp.sa_family = AF_PPPOX; 215 sp.sa_protocol = PX_PROTO_OE; 216 sp.sa_addr.pppoe.sid = conn->session; 217 memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ); 218 memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN); 219 220 /* Set remote_number for ServPoET */ 221 sprintf(remote_number, "%02X:%02X:%02X:%02X:%02X:%02X", 222 (unsigned) conn->peerEth[0], 223 (unsigned) conn->peerEth[1], 224 (unsigned) conn->peerEth[2], 225 (unsigned) conn->peerEth[3], 226 (unsigned) conn->peerEth[4], 227 (unsigned) conn->peerEth[5]); 228 229 warn("Connected to %02X:%02X:%02X:%02X:%02X:%02X via interface %s", 230 (unsigned) conn->peerEth[0], 231 (unsigned) conn->peerEth[1], 232 (unsigned) conn->peerEth[2], 233 (unsigned) conn->peerEth[3], 234 (unsigned) conn->peerEth[4], 235 (unsigned) conn->peerEth[5], 236 conn->ifName); 237 238 script_setenv("MACREMOTE", remote_number, 0); 239 240 if (connect(conn->sessionSocket, (struct sockaddr *) &sp, 241 sizeof(struct sockaddr_pppox)) < 0) { 242 error("Failed to connect PPPoE socket: %d %m", errno); 243 goto ERROR; 244 } 245 246 return conn->sessionSocket; 247 248 ERROR: 249 close(conn->sessionSocket); 250 conn->sessionSocket = -1; 251 /* Send PADT to reset the session unresponsive at buggy nas */ 252 sendPADT(conn, NULL); 253 if (!existingSession) { 254 close(conn->discoverySocket); 255 conn->discoverySocket = -1; 256 } 257 return -1; 258} 259 260static void 261PPPOESendConfig(int mtu, 262 u_int32_t asyncmap, 263 int pcomp, 264 int accomp) 265{ 266 int sock; 267 struct ifreq ifr; 268 269 if (mtu > MAX_PPPOE_MTU) { 270 if (debug) warn("Couldn't increase MTU to %d", mtu); 271 mtu = MAX_PPPOE_MTU; 272 } 273 sock = socket(AF_INET, SOCK_DGRAM, 0); 274 if (sock < 0) { 275 warn("Couldn't create IP socket: %m"); 276 return; 277 } 278 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 279 ifr.ifr_mtu = mtu; 280 if (ioctl(sock, SIOCSIFMTU, &ifr) < 0) { 281 warn("ioctl(SIOCSIFMTU): %m"); 282 return; 283 } 284 (void) close (sock); 285} 286 287 288static void 289PPPOERecvConfig(int mru, 290 u_int32_t asyncmap, 291 int pcomp, 292 int accomp) 293{ 294 if (mru > MAX_PPPOE_MTU && debug) { 295 warn("Couldn't increase MRU to %d", mru); 296 } 297} 298 299/********************************************************************** 300 * %FUNCTION: PPPOEDisconnectDevice 301 * %ARGUMENTS: 302 * None 303 * %RETURNS: 304 * Nothing 305 * %DESCRIPTION: 306 * Disconnects PPPoE device 307 ***********************************************************************/ 308static void 309PPPOEDisconnectDevice(void) 310{ 311 struct sockaddr_pppox sp; 312 313 if (conn->sessionSocket < 0) 314 goto ERROR; 315 316 sp.sa_family = AF_PPPOX; 317 sp.sa_protocol = PX_PROTO_OE; 318 sp.sa_addr.pppoe.sid = 0; 319 memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ); 320 memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN); 321 if (connect(conn->sessionSocket, (struct sockaddr *) &sp, 322 sizeof(struct sockaddr_pppox)) < 0) { 323 warn("Failed to disconnect PPPoE socket: %d %m", errno); 324 } 325 close(conn->sessionSocket); 326 conn->sessionSocket = -1; 327 328ERROR: 329 /* Send PADT to reset the session unresponsive at buggy nas */ 330 sendPADT(conn, NULL); 331 if (!existingSession) { 332 close(conn->discoverySocket); 333 conn->discoverySocket = -1; 334 } 335} 336 337static void 338PPPOEDeviceOptions(void) 339{ 340 char buf[256]; 341 snprintf(buf, 256, _PATH_ETHOPT "%s",devnam); 342 if(!options_from_file(buf, 0, 0, 1)) 343 exit(EXIT_OPTION_ERROR); 344 345} 346 347struct channel pppoe_channel; 348 349/********************************************************************** 350 * %FUNCTION: PPPoEDevnameHook 351 * %ARGUMENTS: 352 * cmd -- the command (actually, the device name 353 * argv -- argument vector 354 * doit -- if non-zero, set device name. Otherwise, just check if possible 355 * %RETURNS: 356 * 1 if we will handle this device; 0 otherwise. 357 * %DESCRIPTION: 358 * Checks if name is a valid interface name; if so, returns 1. Also 359 * sets up devnam (string representation of device). 360 ***********************************************************************/ 361static int 362PPPoEDevnameHook(char *cmd, char **argv, int doit) 363{ 364 int r = 1; 365 int fd; 366 struct ifreq ifr; 367 int seen_idx = doit ? 1 : 0; 368 369 /* If "devnam" has already been set, ignore. 370 This prevents kernel from doing modprobes against random 371 pppd arguments that happen to begin with "nic-", "eth" or "br" 372 373 Ideally, "nix-ethXXX" should be supplied immediately after 374 "plugin rp-pppoe.so" 375 376 Patch based on suggestion from Mike Ireton. 377 */ 378 if (seen_devnam[seen_idx]) { 379 if (OldDevnameHook) return OldDevnameHook(cmd, argv, doit); 380 return 0; 381 } 382 383 /* Only do it if name is "ethXXX" or "brXXX" or what was specified 384 by rp_pppoe_dev option (ugh). */ 385 /* Can also specify nic-XXXX in which case the nic- is stripped off. */ 386 if (!strncmp(cmd, "nic-", 4)) { 387 cmd += 4; 388 } else { 389 if (strncmp(cmd, "eth", 3) && 390 strncmp(cmd, "br", 2)) { 391 if (OldDevnameHook) return OldDevnameHook(cmd, argv, doit); 392 return 0; 393 } 394 } 395 396 /* Open a socket */ 397 if ((fd = socket(PF_PACKET, SOCK_RAW, 0)) < 0) { 398 r = 0; 399 } 400 401 /* Try getting interface index */ 402 if (r) { 403 strncpy(ifr.ifr_name, cmd, IFNAMSIZ); 404 if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { 405 r = 0; 406 } else { 407 if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { 408 r = 0; 409 } else { 410 if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { 411 error("Interface %s not Ethernet", cmd); 412 r=0; 413 } 414 } 415 } 416 } 417 418 /* Close socket */ 419 close(fd); 420 if (r) { 421 seen_devnam[seen_idx] = 1; 422 if (doit) { 423 strncpy(devnam, cmd, sizeof(devnam)); 424 if (the_channel != &pppoe_channel) { 425 426 the_channel = &pppoe_channel; 427 modem = 0; 428 429 lcp_allowoptions[0].neg_accompression = 0; 430 lcp_wantoptions[0].neg_accompression = 0; 431 432 lcp_allowoptions[0].neg_asyncmap = 0; 433 lcp_wantoptions[0].neg_asyncmap = 0; 434 435 lcp_allowoptions[0].neg_pcompression = 0; 436 lcp_wantoptions[0].neg_pcompression = 0; 437 438 ipcp_allowoptions[0].neg_vj=0; 439 ipcp_wantoptions[0].neg_vj=0; 440 441 ccp_allowoptions[0].deflate = 0 ; 442 ccp_wantoptions[0].deflate = 0 ; 443 444 ccp_allowoptions[0].bsd_compress = 0; 445 ccp_wantoptions[0].bsd_compress = 0; 446 447 448 PPPOEInitDevice(); 449 } 450 } 451 return 1; 452 } 453 454 if (OldDevnameHook) r = OldDevnameHook(cmd, argv, doit); 455 return r; 456} 457 458/********************************************************************** 459 * %FUNCTION: plugin_init 460 * %ARGUMENTS: 461 * None 462 * %RETURNS: 463 * Nothing 464 * %DESCRIPTION: 465 * Initializes hooks for pppd plugin 466 ***********************************************************************/ 467void 468plugin_init(void) 469{ 470 if (!ppp_available() && !new_style_driver) { 471 fatal("Linux kernel does not support PPPoE -- are you running 2.4.x?"); 472 } 473 474 add_options(Options); 475 476 info("RP-PPPoE plugin version %s compiled against pppd %s", 477 RP_VERSION, VERSION); 478} 479 480/********************************************************************** 481*%FUNCTION: fatalSys 482*%ARGUMENTS: 483* str -- error message 484*%RETURNS: 485* Nothing 486*%DESCRIPTION: 487* Prints a message plus the errno value to stderr and syslog and exits. 488* 489***********************************************************************/ 490void 491fatalSys(char const *str) 492{ 493 char buf[1024]; 494 int i = errno; 495 sprintf(buf, "%.256s: %.256s", str, strerror(i)); 496 printErr(buf); 497 sprintf(buf, "RP-PPPoE: %.256s: %.256s", str, strerror(i)); 498 sendPADT(conn, buf); 499 exit(1); 500} 501 502/********************************************************************** 503*%FUNCTION: rp_fatal 504*%ARGUMENTS: 505* str -- error message 506*%RETURNS: 507* Nothing 508*%DESCRIPTION: 509* Prints a message to stderr and syslog and exits. 510***********************************************************************/ 511void 512rp_fatal(char const *str) 513{ 514 printErr(str); 515 sendPADTf(conn, "RP-PPPoE: %.256s", str); 516 exit(1); 517} 518 519/********************************************************************** 520*%FUNCTION: sysErr 521*%ARGUMENTS: 522* str -- error message 523*%RETURNS: 524* Nothing 525*%DESCRIPTION: 526* Prints a message plus the errno value to syslog. 527***********************************************************************/ 528void 529sysErr(char const *str) 530{ 531 char buf[1024]; 532 sprintf(buf, "%.256s: %.256s", str, strerror(errno)); 533 printErr(buf); 534} 535 536void pppoe_check_options(void) 537{ 538 unsigned int mac[ETH_ALEN]; 539 int i; 540 541 if (pppoe_reqd_mac != NULL) { 542 if (sscanf(pppoe_reqd_mac, "%x:%x:%x:%x:%x:%x", 543 &mac[0], &mac[1], &mac[2], &mac[3], 544 &mac[4], &mac[5]) != ETH_ALEN) { 545 option_error("cannot parse pppoe-mac option value"); 546 exit(EXIT_OPTION_ERROR); 547 } 548 for (i = 0; i < 6; ++i) 549 conn->req_peer_mac[i] = mac[i]; 550 conn->req_peer = 1; 551 } 552 553 lcp_allowoptions[0].neg_accompression = 0; 554 lcp_wantoptions[0].neg_accompression = 0; 555 556 lcp_allowoptions[0].neg_asyncmap = 0; 557 lcp_wantoptions[0].neg_asyncmap = 0; 558 559 lcp_allowoptions[0].neg_pcompression = 0; 560 lcp_wantoptions[0].neg_pcompression = 0; 561 562 if (lcp_allowoptions[0].mru > MAX_PPPOE_MTU) { 563 lcp_allowoptions[0].mru = MAX_PPPOE_MTU; 564 } 565 if (lcp_wantoptions[0].mru > MAX_PPPOE_MTU) { 566 lcp_wantoptions[0].mru = MAX_PPPOE_MTU; 567 } 568 569 /* Save configuration */ 570 conn->mtu = lcp_allowoptions[0].mru; 571 conn->mru = lcp_wantoptions[0].mru; 572 573 ccp_allowoptions[0].deflate = 0; 574 ccp_wantoptions[0].deflate = 0; 575 576 ipcp_allowoptions[0].neg_vj = 0; 577 ipcp_wantoptions[0].neg_vj = 0; 578 579 ccp_allowoptions[0].bsd_compress = 0; 580 ccp_wantoptions[0].bsd_compress = 0; 581} 582 583struct channel pppoe_channel = { 584 .options = Options, 585 .process_extra_options = &PPPOEDeviceOptions, 586 .check_options = &pppoe_check_options, 587 .connect = &PPPOEConnectDevice, 588 .disconnect = &PPPOEDisconnectDevice, 589 .establish_ppp = &generic_establish_ppp, 590 .disestablish_ppp = &generic_disestablish_ppp, 591 .send_config = &PPPOESendConfig, 592 .recv_config = &PPPOERecvConfig, 593 .close = NULL, 594 .cleanup = NULL 595}; 596