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 static 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 57 char pppd_version[] = VERSION; 58 59 /* From sys-linux.c in pppd -- MUST FIX THIS! */ 60 extern int new_style_driver; 61 62 char *pppd_pppoe_service = NULL; 63 static char *acName = NULL; 64 static char *existingSession = NULL; 65 static int printACNames = 0; 66 67 static int PPPoEDevnameHook(char *cmd, char **argv, int doit); 68 static 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 84 static PPPoEConnection *conn = NULL; 85 86 /********************************************************************** 87 * %FUNCTION: PPPOEInitDevice 88 * %ARGUMENTS: 89 * None 90 * %RETURNS: 91 * 92 * %DESCRIPTION: 93 * Initializes PPPoE device. 94 ***********************************************************************/ 95 static int 96 PPPOEInitDevice(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 ***********************************************************************/ 126 static int 127 PPPOEConnectDevice(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 184 static void 185 PPPOESendConfig(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 212 static void 213 PPPOERecvConfig(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 ***********************************************************************/ 231 static void 232 PPPOEDisconnectDevice(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 251 static void 252 PPPOEDeviceOptions(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 261 struct 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 ***********************************************************************/ 275 static int 276 PPPoEDevnameHook(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 ***********************************************************************/ 360 void 361 plugin_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 ***********************************************************************/ 382 void 383 fatalSys(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 ***********************************************************************/ 403 void 404 rp_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 ***********************************************************************/ 421 void 422 sysErr(char const *str) 423 { 424 rp_fatal(str); 425 } 426 427 428 struct 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