1/*********************************************************************** 2* 3* l2tp/handlers/cmd.c 4* 5* Simple (VERY simple) command-processor for the L2TP daemon. 6* 7* Copyright (C) 2002 Roaring Penguin Software Inc. 8* 9* This software may be distributed under the terms of the GNU General 10* Public License, Version 2, or (at your option) any later version. 11* 12* LIC: GPL 13* 14***********************************************************************/ 15 16static char const RCSID[] = 17"$Id: cmd.c,v 1.1.1.1 2008/10/15 03:31:00 james26_jang Exp $"; 18 19#include "l2tp.h" 20#include "dstring.h" 21#include "event_tcp.h" 22#include <string.h> 23#include <errno.h> 24#include <sys/socket.h> 25#include <sys/un.h> 26#include <stdlib.h> 27#include <stdio.h> 28#include <unistd.h> 29#include <fcntl.h> 30#include <sys/stat.h> 31#include <netdb.h> 32#include <signal.h> 33#include <resolv.h> 34 35#define HANDLER_NAME "cmd" 36 37static int process_option(EventSelector *, char const *, char const *); 38static void cmd_acceptor(EventSelector *es, int fd); 39static void cmd_handler(EventSelector *es, 40 int fd, char *buf, int len, int flag, void *data); 41 42 43static void cmd_exit(EventSelector *es, int fd); 44static void cmd_start_session(EventSelector *es, int fd, char *buf); 45static void cmd_stop_session(EventSelector *es, int fd, char *buf); 46static void cmd_dump_sessions(EventSelector *es, int fd, char *buf); 47static void cmd_reply(EventSelector *es, int fd, char const *msg); 48 49static option_handler my_option_handler = { 50 NULL, HANDLER_NAME, process_option 51}; 52 53/* Socket name for commands */ 54static char const *sockname = NULL; 55 56static l2tp_opt_descriptor my_opts[] = { 57 { "socket-path", OPT_TYPE_STRING, &sockname }, 58 { NULL, OPT_TYPE_BOOL, NULL} 59}; 60 61 62/********************************************************************** 63* %FUNCTION: describe_session 64* %ARGUMENTS: 65* ses -- an L2TP session 66* str -- a dynamic string to which description is appended 67* %RETURNS: 68* Nothing 69* %DESCRIPTION: 70* Dumps session description into str 71***********************************************************************/ 72static void 73describe_session(l2tp_session *ses, 74 dynstring *str) 75{ 76 char buf[1024]; 77 78 sprintf(buf, "Session %s MyID %d AssignedID %d", 79 (ses->we_are_lac ? "LAC" : "LNS"), 80 (int) ses->my_id, (int) ses->assigned_id); 81 dynstring_append(str, buf); 82 sprintf(buf, " State %s\n", 83 l2tp_session_state_name(ses)); 84 dynstring_append(str, buf); 85} 86 87/********************************************************************** 88* %FUNCTION: describe_tunnel 89* %ARGUMENTS: 90* tunnel -- an L2TP tunnel 91* str -- a dynamic string to which description is appended 92* %RETURNS: 93* Nothing 94* %DESCRIPTION: 95* Dumps tunnel description into str 96***********************************************************************/ 97static void 98describe_tunnel(l2tp_tunnel *tunnel, 99 dynstring *str) 100{ 101 char buf[1024]; 102 l2tp_session *ses; 103 void *cursor; 104 105 sprintf(buf, "Tunnel MyID %d AssignedID %d", 106 (int) tunnel->my_id, (int) tunnel->assigned_id); 107 dynstring_append(str, buf); 108 sprintf(buf, " NumSessions %d", (int) hash_num_entries(&tunnel->sessions_by_my_id)); 109 dynstring_append(str, buf); 110 sprintf(buf, " PeerIP %s State %s\n", inet_ntoa(tunnel->peer_addr.sin_addr), 111 l2tp_tunnel_state_name(tunnel)); 112 113 dynstring_append(str, buf); 114 115 /* Describe each session */ 116 for (ses = l2tp_tunnel_first_session(tunnel, &cursor); 117 ses; 118 ses = l2tp_tunnel_next_session(tunnel, &cursor)) { 119 describe_session(ses, str); 120 } 121} 122 123/********************************************************************** 124* %FUNCTION: handler_init 125* %ARGUMENTS: 126* es -- event selector 127* %RETURNS: 128* Nothing 129* %DESCRIPTION: 130* Initializes the command processor's option handler. We do not 131* actually start a command processor until last option has been parsed. 132***********************************************************************/ 133void 134handler_init(EventSelector *es) 135{ 136 l2tp_option_register_section(&my_option_handler); 137} 138 139/********************************************************************** 140* %FUNCTION: process_option 141* %ARGUMENTS: 142* es -- event selector 143* name, value -- name and value of option 144* %RETURNS: 145* 0 on success; -1 on failure. 146* %DESCRIPTION: 147* Processes options. When last option has been processed, begins 148* command handler. 149***********************************************************************/ 150static int 151process_option(EventSelector *es, 152 char const *name, 153 char const *value) 154{ 155 struct sockaddr_un addr; 156 socklen_t len; 157 int fd; 158 159 if (!strcmp(name, "*begin*")) return 0; 160 if (strcmp(name, "*end*")) { 161 return l2tp_option_set(es, name, value, my_opts); 162 } 163 164 /* We have hit the end of our options. Open command socket */ 165 if (!sockname) { 166 sockname = "/var/run/l2tpctrl"; 167 } 168 169 (void) remove(sockname); 170 fd = socket(AF_LOCAL, SOCK_STREAM, 0); 171 if (fd < 0) { 172 l2tp_set_errmsg("cmd: process_option: socket: %s", strerror(errno)); 173 return -1; 174 } 175 176 memset(&addr, 0, sizeof(addr)); 177 addr.sun_family = AF_LOCAL; 178 strncpy(addr.sun_path, sockname, sizeof(addr.sun_path) - 1); 179 180 len = sizeof(addr); 181 if (bind(fd, (struct sockaddr *) &addr, SUN_LEN(&addr)) < 0) { 182 l2tp_set_errmsg("cmd: process_option: bind: %s", strerror(errno)); 183 close(fd); 184 return -1; 185 } 186 (void) chmod(sockname, 0600); 187 if (listen(fd, 5) < 0) { 188 l2tp_set_errmsg("cmd: process_option: listen: %s", strerror(errno)); 189 close(fd); 190 return -1; 191 } 192 193 /* Ignore sigpipe */ 194 signal(SIGPIPE, SIG_IGN); 195 196 /* Add an accept handler */ 197 if (!EventTcp_CreateAcceptor(es, fd, cmd_acceptor)) { 198 l2tp_set_errmsg("cmd: process_option: EventTcp_CreateAcceptor: %s", strerror(errno)); 199 close(fd); 200 return -1; 201 } 202 return 0; 203} 204 205/********************************************************************** 206* %FUNCTION: cmd_acceptor 207* %ARGUMENTS: 208* es -- event selector 209* fd -- file descriptor of accepted connection 210* %RETURNS: 211* Nothing 212* %DESCRIPTION: 213* Accepts a control connection and sets up read event. 214***********************************************************************/ 215static void 216cmd_acceptor(EventSelector *es, int fd) 217{ 218 EventTcp_ReadBuf(es, fd, 512, '\n', cmd_handler, 5, NULL); 219} 220 221/********************************************************************** 222* %FUNCTION: cmd_handler 223* %ARGUMENTS: 224* es -- event selector 225* fd -- file descriptor 226* buf -- buffer which was read 227* len -- length of data 228* flag -- flags 229* data -- not used 230* %RETURNS: 231* Nothing 232* %DESCRIPTION: 233* Processes a command from the user 234***********************************************************************/ 235static void 236cmd_handler(EventSelector *es, 237 int fd, 238 char *buf, 239 int len, 240 int flag, 241 void *data) 242{ 243 char word[512]; 244 245 if (flag == EVENT_TCP_FLAG_IOERROR || flag == EVENT_TCP_FLAG_TIMEOUT) { 246 close(fd); 247 return; 248 } 249 if (len < 511) { 250 buf[len+1] = 0; 251 } else { 252 buf[len] = 0; 253 } 254 255 /* Chop off newline */ 256 if (len && (buf[len-1] == '\n')) { 257 buf[len-1] = 0; 258 } 259 260 /* Get first word */ 261 buf = (char *) l2tp_chomp_word(buf, word); 262 263 if (!strcmp(word, "exit")) { 264 cmd_exit(es, fd); 265 } else if (!strcmp(word, "start-session")) { 266 cmd_start_session(es, fd, buf); 267 } else if (!strcmp(word, "stop-session")) { 268 cmd_stop_session(es, fd, buf); 269 } else if (!strcmp(word, "dump-sessions")) { 270 cmd_dump_sessions(es, fd, buf); 271 } else { 272 cmd_reply(es, fd, "ERR Unknown command"); 273 } 274} 275 276/********************************************************************** 277* %FUNCTION: cmd_reply 278* %ARGUMENTS: 279* es -- event selector 280* fd -- file descriptor 281* msg -- message 282* %RETURNS: 283* Nothing 284* %DESCRIPTION: 285* Schedules reply to be shot back to user 286***********************************************************************/ 287static void 288cmd_reply(EventSelector *es, 289 int fd, 290 char const *msg) 291{ 292 EventTcp_WriteBuf(es, fd, (char *) msg, strlen(msg), NULL, 10, NULL); 293} 294 295/********************************************************************** 296* %FUNCTION: cmd_exit 297* %ARGUMENTS: 298* es -- Event selector 299* fd -- command file descriptor 300* %RETURNS: 301* Nothing 302* %DESCRIPTION: 303* Tears down tunnels and quits 304***********************************************************************/ 305static void 306cmd_exit(EventSelector *es, 307 int fd) 308{ 309 cmd_reply(es, fd, "OK Shutting down"); 310 l2tp_tunnel_stop_all("Stopped by system administrator"); 311 l2tp_cleanup(); 312 exit(0); 313} 314 315/********************************************************************** 316* %FUNCTION: cmd_start_session 317* %ARGUMENTS: 318* es -- event selector 319* fd -- command file descriptor 320* buf -- rest of command from user 321* %RETURNS: 322* Nothing 323* %DESCRIPTION: 324* Starts an L2TP session, if possible 325***********************************************************************/ 326static void 327cmd_start_session(EventSelector *es, 328 int fd, 329 char *buf) 330{ 331 char peer[512]; 332 struct hostent *he; 333 struct sockaddr_in haddr; 334 l2tp_peer *p; 335 l2tp_session *sess; 336 337 buf = (char *) l2tp_chomp_word(buf, peer); 338 he = gethostbyname(peer); 339 if (!he) { 340 cmd_reply(es, fd, "ERR Unknown peer - gethostbyname failed"); 341 return; 342 } 343 memcpy(&haddr.sin_addr, he->h_addr, sizeof(haddr.sin_addr)); 344 p = l2tp_peer_find(&haddr, NULL); 345 if (!p) { 346 cmd_reply(es, fd, "ERR Unknown peer"); 347 return; 348 } 349 sess = l2tp_session_call_lns(p, "foobar", es, NULL); 350 if (!sess) { 351 cmd_reply(es, fd, l2tp_get_errmsg()); 352 return; 353 } 354 355 /* Form reply */ 356 sprintf(peer, "OK %d %d", 357 (int) sess->tunnel->my_id, 358 (int) sess->my_id); 359 cmd_reply(es, fd, peer); 360} 361 362/********************************************************************** 363* %FUNCTION: cmd_stop_session 364* %ARGUMENTS: 365* es -- event selector 366* fd -- command file descriptor 367* buf -- rest of command from user 368* %RETURNS: 369* Nothing 370* %DESCRIPTION: 371* Stops an L2TP session, identified by (Tunnel, Session) pair. 372***********************************************************************/ 373static void 374cmd_stop_session(EventSelector *es, 375 int fd, 376 char *buf) 377{ 378 char junk[512]; 379 l2tp_tunnel *tunnel; 380 l2tp_session *sess; 381 unsigned int x; 382 383 buf = (char *) l2tp_chomp_word(buf, junk); 384 if (sscanf(junk, "%u", &x) != 1) { 385 cmd_reply(es, fd, "ERR Syntax error: stop-session tid sid"); 386 return; 387 } 388 tunnel = l2tp_tunnel_find_by_my_id((uint16_t) x); 389 if (!tunnel) { 390 cmd_reply(es, fd, "ERR No such tunnel"); 391 return; 392 } 393 394 395 buf = (char *) l2tp_chomp_word(buf, junk); 396 if (sscanf(junk, "%u", &x) != 1) { 397 cmd_reply(es, fd, "ERR Syntax error: stop-session tid sid"); 398 return; 399 } 400 sess = l2tp_tunnel_find_session(tunnel, (uint16_t) x); 401 402 if (!sess) { 403 cmd_reply(es, fd, "ERR No such session"); 404 return; 405 } 406 407 /* Stop the session */ 408 l2tp_session_send_CDN(sess, RESULT_GENERAL_REQUEST, ERROR_OK, 409 "Call terminated by operator"); 410 cmd_reply(es, fd, "OK Session stopped"); 411} 412 413/********************************************************************** 414* %FUNCTION: cmd_dump_sessions 415* %ARGUMENTS: 416* es -- event selector 417* fd -- command file descriptor 418* buf -- rest of command from user 419* %RETURNS: 420* Nothing 421* %DESCRIPTION: 422* Dumps info about all currently-active tunnels and sessions 423***********************************************************************/ 424static void 425cmd_dump_sessions(EventSelector *es, 426 int fd, 427 char *buf) 428{ 429 dynstring str; 430 char tmp[256]; 431 void *cursor; 432 char const *ans; 433 l2tp_tunnel *tunnel; 434 435 dynstring_init(&str); 436 437 dynstring_append(&str, "OK\n"); 438 439 /* Print info about each tunnel */ 440 sprintf(tmp, "NumL2TPTunnels %d\n", l2tp_num_tunnels()); 441 dynstring_append(&str, tmp); 442 443 for (tunnel = l2tp_first_tunnel(&cursor); 444 tunnel; 445 tunnel = l2tp_next_tunnel(&cursor)) { 446 describe_tunnel(tunnel, &str); 447 } 448 449 /* If something went wrong, say so... */ 450 ans = dynstring_data(&str); 451 if (!ans) { 452 cmd_reply(es, fd, "ERR Out of memory"); 453 return; 454 } 455 456 cmd_reply(es, fd, ans); 457 dynstring_free(&str); 458} 459 460