1/*********************************************************************** 2* 3* sync-pppd.c 4* 5* An LNS handler which starts pppd attached to a PTY in 6* synchronous mode. 7* 8* Copyright (C) 2002 by Roaring Penguin Software Inc. 9* 10* This software may be distributed under the terms of the GNU General 11* Public License, Version 2, or (at your option) any later version. 12* 13* LIC: GPL 14* 15***********************************************************************/ 16 17static char const RCSID[] = 18"$Id: sync-pppd.c,v 1.1.1.1 2008/10/15 03:31:00 james26_jang Exp $"; 19 20#include "l2tp.h" 21#include <signal.h> 22#include <stdio.h> 23#include <string.h> 24#include <fcntl.h> 25#include <linux/if.h> 26#include <linux/if_ether.h> 27#include <linux/if_pppol2tp.h> 28#include <linux/if_pppox.h> 29 30#define HANDLER_NAME "sync-pppd" 31 32#define DEFAULT_PPPD_PATH "/usr/sbin/pppd" 33 34#define MAX_FDS 256 35 36extern int pty_get(int *mfp, int *sfp); 37static int establish_session(l2tp_session *ses); 38static void close_session(l2tp_session *ses, char const *reason, int may_reestablish); 39static void handle_frame(l2tp_session *ses, unsigned char *buf, size_t len); 40 41/* Options for invoking pppd */ 42#define MAX_OPTS 64 43static char *pppd_lns_options[MAX_OPTS+1]; 44static char *pppd_lac_options[MAX_OPTS+1]; 45static int num_pppd_lns_options = 0; 46static int num_pppd_lac_options = 0; 47static int use_unit_option = 0; 48static int kernel_mode = 1; 49static char *pppd_path = NULL; 50 51#define PUSH_LNS_OPT(x) pppd_lns_options[num_pppd_lns_options++] = (x) 52#define PUSH_LAC_OPT(x) pppd_lac_options[num_pppd_lac_options++] = (x) 53 54/* Our call ops */ 55static l2tp_call_ops my_ops = { 56 establish_session, 57 close_session, 58 handle_frame 59}; 60 61/* The slave process */ 62struct slave { 63 EventSelector *es; /* Event selector */ 64 l2tp_session *ses; /* L2TP session we're hooked to */ 65 pid_t pid; /* PID of child PPPD process */ 66 int fd; /* File descriptor for event-handler loop */ 67 EventHandler *event; /* Event handler */ 68}; 69 70static int handle_lac_opts(EventSelector *es, l2tp_opt_descriptor *desc, char const *value); 71static int handle_lns_opts(EventSelector *es, l2tp_opt_descriptor *desc, char const *value); 72 73/* Options */ 74static l2tp_opt_descriptor my_opts[] = { 75 /* name type addr */ 76 { "lac-pppd-opts", OPT_TYPE_CALLFUNC, (void *) handle_lac_opts}, 77 { "lns-pppd-opts", OPT_TYPE_CALLFUNC, (void *) handle_lns_opts}, 78 { "set-ppp-if-name", OPT_TYPE_BOOL, &use_unit_option}, 79 { "kernel-mode", OPT_TYPE_BOOL, &kernel_mode}, 80 { "pppd-path", OPT_TYPE_STRING, &pppd_path}, 81 { NULL, OPT_TYPE_BOOL, NULL } 82}; 83 84static int 85process_option(EventSelector *es, char const *name, char const *value) 86{ 87 if (!strcmp(name, "*begin*")) return 0; 88 if (!strcmp(name, "*end*")) return 0; 89 return l2tp_option_set(es, name, value, my_opts); 90} 91 92static option_handler my_option_handler = { 93 NULL, HANDLER_NAME, process_option 94}; 95 96static int 97handle_lac_opts(EventSelector *es, 98 l2tp_opt_descriptor *desc, char const *value) 99{ 100 char word[512]; 101 while (value && *value) { 102 value = l2tp_chomp_word(value, word); 103 if (!word[0]) break; 104 if (num_pppd_lac_options < MAX_OPTS) { 105 char *x = strdup(word); 106 if (x) PUSH_LAC_OPT(x); 107 pppd_lac_options[num_pppd_lac_options] = NULL; 108 } else { 109 break; 110 } 111 } 112 return 0; 113} 114 115static int 116handle_lns_opts(EventSelector *es, 117 l2tp_opt_descriptor *desc, char const *value) 118{ 119 char word[512]; 120 while (value && *value) { 121 value = l2tp_chomp_word(value, word); 122 if (!word[0]) break; 123 if (num_pppd_lns_options < MAX_OPTS) { 124 char *x = strdup(word); 125 if (x) PUSH_LNS_OPT(x); 126 pppd_lns_options[num_pppd_lns_options] = NULL; 127 } else { 128 break; 129 } 130 } 131 return 0; 132} 133 134/********************************************************************** 135* %FUNCTION: handle_frame 136* %ARGUMENTS: 137* ses -- l2tp session 138* buf -- received PPP frame 139* len -- length of frame 140* %RETURNS: 141* Nothing 142* %DESCRIPTION: 143* Shoots the frame to PPP's pty 144***********************************************************************/ 145static void 146handle_frame(l2tp_session *ses, 147 unsigned char *buf, 148 size_t len) 149{ 150 struct slave *sl = ses->private; 151 int n; 152 153 if (!sl) return; 154 155 /* Add framing bytes */ 156 *--buf = 0x03; 157 *--buf = 0xFF; 158 len += 2; 159 160 /* TODO: Add error checking */ 161 if (sl->fd < 0) { 162 l2tp_set_errmsg("Attempt to write %d bytes to non existent fd.", len); 163 } else n = write(sl->fd, buf, len); 164} 165 166/********************************************************************** 167* %FUNCTION: close_session 168* %ARGUMENTS: 169* ses -- L2TP session 170* reason -- reason why session is closing 171* %RETURNS: 172* Nothing 173* %DESCRIPTION: 174* Kills pppd. 175***********************************************************************/ 176static void 177close_session(l2tp_session *ses, char const *reason, int may_reestablish) 178{ 179 l2tp_tunnel *tunnel = ses->tunnel; 180 struct slave *sl = ses->private; 181 if (!sl) return; 182 183 /* Detach slave */ 184 ses->private = NULL; 185 sl->ses = NULL; 186 187 kill(sl->pid, SIGTERM); 188 if (sl->fd >= 0) close(sl->fd); 189 sl->fd = -1; 190 if (sl->event) Event_DelHandler(sl->es, sl->event); 191 sl->event = NULL; 192 193 /* Re-establish session if desired */ 194 if (may_reestablish && tunnel->peer->persist && 195 (tunnel->peer->maxfail == 0 || tunnel->peer->fail++ < tunnel->peer->maxfail)) { 196 struct timeval t; 197 198 t.tv_sec = tunnel->peer->holdoff; 199 t.tv_usec = 0; 200 Event_AddTimerHandler(tunnel->es, t, l2tp_tunnel_reestablish, tunnel->peer); 201 } 202} 203 204/********************************************************************** 205* %FUNCTION: slave_exited 206* %ARGUMENTS: 207* pid -- PID of exiting slave 208* status -- exit status of slave 209* data -- the slave structure 210* %RETURNS: 211* Nothing 212* %DESCRIPTION: 213* Handles an exiting slave 214***********************************************************************/ 215static void 216slave_exited(pid_t pid, int status, void *data) 217{ 218 l2tp_session *ses; 219 struct slave *sl = (struct slave *) data; 220 if (!sl) return; 221 222 ses = sl->ses; 223 224 if (sl->fd >= 0) close(sl->fd); 225 if (sl->event) Event_DelHandler(sl->es, sl->event); 226 sl->fd = -1; 227 sl->event = NULL; 228 229 if (ses) { 230 l2tp_tunnel *tunnel = ses->tunnel; 231 232 /* Re-establish session if desired */ 233 if (tunnel->peer->persist) { 234 struct timeval t; 235 236 t.tv_sec = tunnel->peer->holdoff; 237 t.tv_usec = 0; 238 Event_AddTimerHandler(tunnel->es, t, l2tp_tunnel_reestablish, tunnel->peer); 239 } 240 241 ses->private = NULL; 242 l2tp_session_send_CDN(ses, RESULT_GENERAL_REQUEST, 0, 243 "pppd process exited"); 244 } 245 free(sl); 246} 247 248/********************************************************************** 249* %FUNCTION: readable 250* %ARGUMENTS: 251* es -- event selector 252* fd -- file descriptor 253* flags -- we ignore 254* data -- the L2TP session 255* %RETURNS: 256* Nothing 257* %DESCRIPTION: 258* Handles readability on PTY; shoots PPP frame over tunnel 259***********************************************************************/ 260static void 261readable(EventSelector *es, int fd, unsigned int flags, void *data) 262{ 263 unsigned char buf[4096+EXTRA_HEADER_ROOM]; 264 int n; 265 l2tp_session *ses = (l2tp_session *) data; 266 int iters = 5; 267 268 /* It seems to be better to read in a loop than to go 269 back to select loop. However, don't loop forever, or 270 we could have a DoS potential */ 271 while(iters--) { 272 /* EXTRA_HEADER_ROOM bytes extra space for l2tp header */ 273 n = read(fd, buf+EXTRA_HEADER_ROOM, sizeof(buf)-EXTRA_HEADER_ROOM); 274 275 /* TODO: Check this.... */ 276 if (n <= 2) return; 277 278 if (!ses) continue; 279 280 /* Chop off framing bytes */ 281 l2tp_dgram_send_ppp_frame(ses, buf+EXTRA_HEADER_ROOM+2, n-2); 282 } 283} 284 285/********************************************************************** 286* %FUNCTION: establish_session 287* %ARGUMENTS: 288* ses -- the L2TP session 289* %RETURNS: 290* 0 if session could be established, -1 otherwise. 291* %DESCRIPTION: 292* Forks a pppd process and connects fd to pty 293***********************************************************************/ 294static int 295establish_session(l2tp_session *ses) 296{ 297 int m_pty = -1, s_pty; 298 struct sockaddr_pppol2tp sax; 299 pid_t pid; 300 EventSelector *es = ses->tunnel->es; 301 struct slave *sl = malloc(sizeof(struct slave)); 302 int i, flags; 303 char unit[32], fdstr[10]; 304 305 ses->private = NULL; 306 if (!sl) return -1; 307 sl->ses = ses; 308 sl->es = es; 309 310 /* Get pty */ 311 if (kernel_mode) { 312 s_pty = socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OL2TP); 313 if (s_pty < 0) { 314 l2tp_set_errmsg("Unable to allocate PPPoL2TP socket."); 315 free(sl); 316 return -1; 317 } 318 flags = fcntl(s_pty, F_GETFL); 319 if (flags == -1 || fcntl(s_pty, F_SETFL, flags | O_NONBLOCK) == -1) { 320 l2tp_set_errmsg("Unable to set PPPoL2TP socket nonblock."); 321 free(sl); 322 return -1; 323 } 324 sax.sa_family = AF_PPPOX; 325 sax.sa_protocol = PX_PROTO_OL2TP; 326 sax.pppol2tp.pid = 0; 327 sax.pppol2tp.fd = Sock; 328 sax.pppol2tp.addr.sin_addr.s_addr = ses->tunnel->peer_addr.sin_addr.s_addr; 329 sax.pppol2tp.addr.sin_port = ses->tunnel->peer_addr.sin_port; 330 sax.pppol2tp.addr.sin_family = AF_INET; 331 sax.pppol2tp.s_tunnel = ses->tunnel->my_id; 332 sax.pppol2tp.s_session = ses->my_id; 333 sax.pppol2tp.d_tunnel = ses->tunnel->assigned_id; 334 sax.pppol2tp.d_session = ses->assigned_id; 335 if (connect(s_pty, (struct sockaddr *)&sax, sizeof(sax)) < 0) { 336 l2tp_set_errmsg("Unable to connect PPPoL2TP socket."); 337 free(sl); 338 return -1; 339 } 340 snprintf (fdstr, sizeof(fdstr), "%d", s_pty); 341 } else { 342 if (pty_get(&m_pty, &s_pty) < 0) { 343 free(sl); 344 return -1; 345 } 346 if (fcntl(m_pty, F_SETFD, FD_CLOEXEC) == -1) { 347 l2tp_set_errmsg("Unable to set FD_CLOEXEC"); 348 close(m_pty); 349 close(s_pty); 350 free(sl); 351 return -1; 352 } 353 } 354 355 /* Fork */ 356 pid = fork(); 357 if (pid == (pid_t) -1) { 358 free(sl); 359 return -1; 360 } 361 362 if (pid) { 363 /* In the parent */ 364 sl->pid = pid; 365 366 /* Set up handler for when pppd exits */ 367 Event_HandleChildExit(es, pid, slave_exited, sl); 368 369 /* Close the slave tty */ 370 close(s_pty); 371 372 sl->fd = m_pty; 373 374 if (!kernel_mode) { 375 /* Set slave FD non-blocking */ 376 flags = fcntl(sl->fd, F_GETFL); 377 if (flags >= 0) fcntl(sl->fd, F_SETFL, (long) flags | O_NONBLOCK); 378 379 /* Handle readability on slave end */ 380 sl->event = Event_AddHandler(es, m_pty, EVENT_FLAG_READABLE, 381 readable, ses); 382 } else 383 sl->event = NULL; 384 385 ses->private = sl; 386 return 0; 387 } 388 389 /* In the child. Exec pppd */ 390 /* Close all file descriptors except s_pty */ 391 for (i=0; i<MAX_FDS; i++) { 392 if (i != s_pty) close(i); 393 } 394 395 /* Dup s_pty onto stdin and stdout */ 396 if (!kernel_mode) { 397 dup2(s_pty, 0); 398 dup2(s_pty, 1); 399 if (s_pty > 1) close(s_pty); 400 } 401 402 /* Create unit */ 403 sprintf(unit, "%d", (int) getpid()); 404 405 if (ses->we_are_lac) { 406 char **lac_opt; 407 408 /* Push a unit option */ 409 if (use_unit_option && num_pppd_lac_options <= MAX_OPTS-2) { 410 PUSH_LAC_OPT("unit"); 411 PUSH_LAC_OPT(unit); 412 } 413 /* Push plugin options */ 414 if (kernel_mode && num_pppd_lac_options <= MAX_OPTS-4) { 415 PUSH_LAC_OPT("plugin"); 416 PUSH_LAC_OPT("pppol2tp.so"); 417 PUSH_LAC_OPT("pppol2tp"); 418 PUSH_LAC_OPT(fdstr); 419 } 420 /* push peer specific options */ 421 lac_opt = ses->tunnel->peer->lac_options; 422 while (*lac_opt) { 423 if (num_pppd_lac_options <= MAX_OPTS-1) { 424 PUSH_LAC_OPT(*lac_opt); 425 ++lac_opt; 426 } else { 427 break; 428 } 429 } 430 if (pppd_path) { 431 execv(pppd_path, pppd_lac_options); 432 } else { 433 execv(DEFAULT_PPPD_PATH, pppd_lac_options); 434 } 435 } else { 436 char **lns_opt; 437 438 /* Push a unit option */ 439 if (use_unit_option && num_pppd_lns_options <= MAX_OPTS-2) { 440 PUSH_LNS_OPT("unit"); 441 PUSH_LNS_OPT(unit); 442 } 443 /* Push plugin options */ 444 if (kernel_mode && num_pppd_lac_options <= MAX_OPTS-5) { 445 PUSH_LNS_OPT("plugin"); 446 PUSH_LNS_OPT("pppol2tp.so"); 447 PUSH_LNS_OPT("pppol2tp"); 448 PUSH_LNS_OPT(fdstr); 449 PUSH_LNS_OPT("pppol2tp_lns_mode"); 450 } 451 /* push peer specific options */ 452 lns_opt = ses->tunnel->peer->lns_options; 453 while (*lns_opt) { 454 if (num_pppd_lns_options <= MAX_OPTS-1) { 455 PUSH_LNS_OPT(*lns_opt); 456 ++lns_opt; 457 } else { 458 break; 459 } 460 } 461 if (pppd_path) { 462 execv(pppd_path, pppd_lns_options); 463 } else { 464 execv(DEFAULT_PPPD_PATH, pppd_lns_options); 465 } 466 } 467 468 /* Doh.. execl failed */ 469 _exit(1); 470} 471 472static l2tp_lns_handler my_lns_handler = { 473 NULL, 474 HANDLER_NAME, 475 &my_ops 476}; 477 478static l2tp_lac_handler my_lac_handler = { 479 NULL, 480 HANDLER_NAME, 481 &my_ops 482}; 483 484void 485handler_init(EventSelector *es) 486{ 487 l2tp_session_register_lns_handler(&my_lns_handler); 488 l2tp_session_register_lac_handler(&my_lac_handler); 489 l2tp_option_register_section(&my_option_handler); 490 491 PUSH_LNS_OPT("pppd"); 492 PUSH_LNS_OPT("sync"); 493 PUSH_LNS_OPT("nodetach"); 494 PUSH_LNS_OPT("noaccomp"); 495 PUSH_LNS_OPT("nobsdcomp"); 496 PUSH_LNS_OPT("nodeflate"); 497 PUSH_LNS_OPT("nopcomp"); 498 PUSH_LNS_OPT("novj"); 499 PUSH_LNS_OPT("novjccomp"); 500#if 0 501 PUSH_LNS_OPT("logfile"); 502 PUSH_LNS_OPT("/dev/null"); 503 PUSH_LNS_OPT("nolog"); 504#endif 505 pppd_lns_options[num_pppd_lns_options] = NULL; 506 507 PUSH_LAC_OPT("pppd"); 508 PUSH_LAC_OPT("sync"); 509 PUSH_LAC_OPT("nodetach"); 510 PUSH_LAC_OPT("noaccomp"); 511 PUSH_LAC_OPT("nobsdcomp"); 512 PUSH_LAC_OPT("nodeflate"); 513 PUSH_LAC_OPT("nopcomp"); 514 PUSH_LAC_OPT("novj"); 515 PUSH_LAC_OPT("novjccomp"); 516#if 0 517 PUSH_LAC_OPT("logfile"); 518 PUSH_LAC_OPT("/dev/null"); 519 PUSH_LAC_OPT("nolog"); 520#endif 521 pppd_lac_options[num_pppd_lac_options] = NULL; 522} 523 524