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