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