Optimisations
[l2tpns.git] / cli.c
1 // L2TPNS Command Line Interface
2 // $Id: cli.c,v 1.4 2004/05/24 04:12:02 fred_nerk Exp $
3 // vim: sw=4 ts=8
4
5 #include <stdio.h>
6 #include <sys/file.h>
7 #include <sys/stat.h>
8 #include <syslog.h>
9 #include <malloc.h>
10 #include <string.h>
11 #include <stdlib.h>
12 #include <time.h>
13 #include <arpa/inet.h>
14 #include <errno.h>
15 #include <sys/socket.h>
16 #include <sys/types.h>
17 #include <signal.h>
18 #include <unistd.h>
19 #include "l2tpns.h"
20 #include "libcli.h"
21 #include "util.h"
22
23 extern tunnelt *tunnel;
24 extern sessiont *session;
25 extern radiust *radius;
26 extern ippoolt *ip_address_pool;
27 extern struct Tstats *_statistics;
28 extern int cli_pid;
29 struct cli_def *cli = NULL;
30 int cli_quit = 0;
31 extern int clifd, udpfd, tapfd, snoopfd, ifrfd, cluster_sockfd;
32 extern int *radfds;
33 extern sessionidt *cli_session_kill;
34 extern tunnelidt *cli_tunnel_kill;
35 extern tbft *filter_buckets;
36 extern struct configt *config;
37 extern struct config_descriptt config_values[];
38 extern char hostname[];
39 #ifdef RINGBUFFER
40 extern struct Tringbuffer *ringbuffer;
41 #endif
42
43 char *rcs_id = "$Id: cli.c,v 1.4 2004/05/24 04:12:02 fred_nerk Exp $";
44
45 char *debug_levels[] = {
46 "CRIT",
47 "ERROR",
48 "WARN",
49 "INFO",
50 "CALL",
51 "DATA",
52 };
53
54 struct
55 {
56 char critical;
57 char error;
58 char warning;
59 char info;
60 char calls;
61 char data;
62 } debug_flags;
63
64 int debug_session;
65 int debug_tunnel;
66 int debug_rb_tail;
67 FILE *save_config_fh;
68
69 int cmd_show_session(struct cli_def *cli, char *command, char **argv, int argc);
70 int cmd_show_tunnels(struct cli_def *cli, char *command, char **argv, int argc);
71 int cmd_show_users(struct cli_def *cli, char *command, char **argv, int argc);
72 int cmd_show_radius(struct cli_def *cli, char *command, char **argv, int argc);
73 int cmd_show_counters(struct cli_def *cli, char *command, char **argv, int argc);
74 int cmd_show_version(struct cli_def *cli, char *command, char **argv, int argc);
75 int cmd_show_pool(struct cli_def *cli, char *command, char **argv, int argc);
76 int cmd_show_run(struct cli_def *cli, char *command, char **argv, int argc);
77 int cmd_show_banana(struct cli_def *cli, char *command, char **argv, int argc);
78 int cmd_show_plugins(struct cli_def *cli, char *command, char **argv, int argc);
79 int cmd_show_throttle(struct cli_def *cli, char *command, char **argv, int argc);
80 int cmd_write_memory(struct cli_def *cli, char *command, char **argv, int argc);
81 int cmd_clear_counters(struct cli_def *cli, char *command, char **argv, int argc);
82 int cmd_drop_user(struct cli_def *cli, char *command, char **argv, int argc);
83 int cmd_drop_tunnel(struct cli_def *cli, char *command, char **argv, int argc);
84 int cmd_drop_session(struct cli_def *cli, char *command, char **argv, int argc);
85 int cmd_snoop(struct cli_def *cli, char *command, char **argv, int argc);
86 int cmd_no_snoop(struct cli_def *cli, char *command, char **argv, int argc);
87 int cmd_throttle(struct cli_def *cli, char *command, char **argv, int argc);
88 int cmd_no_throttle(struct cli_def *cli, char *command, char **argv, int argc);
89 int cmd_debug(struct cli_def *cli, char *command, char **argv, int argc);
90 int cmd_no_debug(struct cli_def *cli, char *command, char **argv, int argc);
91 int cmd_set(struct cli_def *cli, char *command, char **argv, int argc);
92 int cmd_load_plugin(struct cli_def *cli, char *command, char **argv, int argc);
93 int cmd_remove_plugin(struct cli_def *cli, char *command, char **argv, int argc);
94 int cmd_uptime(struct cli_def *cli, char *command, char **argv, int argc);
95 int regular_stuff(struct cli_def *cli);
96
97 void init_cli()
98 {
99 FILE *f;
100 char buf[4096];
101 struct cli_command *c;
102 int on = 1;
103 struct sockaddr_in addr;
104
105 cli = cli_init();
106
107 c = cli_register_command(cli, NULL, "show", NULL, NULL);
108 cli_register_command(cli, c, "session", cmd_show_session, "Show a list of sessions or details for a single session");
109 cli_register_command(cli, c, "tunnels", cmd_show_tunnels, "Show a list of tunnels or details for a single tunnel");
110 cli_register_command(cli, c, "users", cmd_show_users, "Show a list of all connected users or details of selected user");
111 cli_register_command(cli, c, "version", cmd_show_version, "Show currently running software version");
112 cli_register_command(cli, c, "banana", cmd_show_banana, "Show a banana");
113 cli_register_command(cli, c, "pool", cmd_show_pool, "Show the IP address allocation pool");
114 cli_register_command(cli, c, "running-config", cmd_show_run, "Show the currently running configuration");
115 cli_register_command(cli, c, "radius", cmd_show_radius, "Show active radius queries");
116 cli_register_command(cli, c, "plugins", cmd_show_plugins, "List all installed plugins");
117 cli_register_command(cli, c, "throttle", cmd_show_throttle, "List all token bucket filters in use");
118
119 #ifdef STATISTICS
120 cli_register_command(cli, c, "counters", cmd_show_counters, "Display all the internal counters and running totals");
121
122 c = cli_register_command(cli, NULL, "clear", NULL, NULL);
123 cli_register_command(cli, c, "counters", cmd_clear_counters, "Clear internal counters");
124 #endif
125
126 cli_register_command(cli, NULL, "uptime", cmd_uptime, "Show uptime and bandwidth utilisation");
127
128 c = cli_register_command(cli, NULL, "write", NULL, NULL);
129 cli_register_command(cli, c, "memory", cmd_write_memory, "Save the running config to flash");
130 cli_register_command(cli, c, "terminal", cmd_show_run, "Show the running config");
131
132 cli_register_command(cli, NULL, "snoop", cmd_snoop, "Temporarily enable interception for a user");
133 cli_register_command(cli, NULL, "throttle", cmd_throttle, "Temporarily enable throttling for a user");
134
135 c = cli_register_command(cli, NULL, "no", NULL, NULL);
136 cli_register_command(cli, c, "snoop", cmd_no_snoop, "Temporarily disable interception for a user");
137 cli_register_command(cli, c, "throttle", cmd_no_throttle, "Temporarily disable throttling for a user");
138 cli_register_command(cli, c, "debug", cmd_no_debug, "Turn off logging of a certain level of debugging");
139
140 c = cli_register_command(cli, NULL, "drop", NULL, NULL);
141 cli_register_command(cli, c, "user", cmd_drop_user, "Disconnect a user");
142 cli_register_command(cli, c, "tunnel", cmd_drop_tunnel, "Disconnect a tunnel and all sessions on that tunnel");
143 cli_register_command(cli, c, "session", cmd_drop_session, "Disconnect a session");
144
145 cli_register_command(cli, NULL, "debug", cmd_debug, "Set the level of logging that is shown on the console");
146
147 c = cli_register_command(cli, NULL, "load", NULL, NULL);
148 cli_register_command(cli, c, "plugin", cmd_load_plugin, "Load a plugin");
149
150 c = cli_register_command(cli, NULL, "remove", NULL, NULL);
151 cli_register_command(cli, c, "plugin", cmd_remove_plugin, "Remove a plugin");
152
153 cli_register_command(cli, NULL, "set", cmd_set, "Set a configuration variable");
154
155 // Enable regular processing
156 cli_regular(cli, regular_stuff);
157
158 if (!(f = fopen(CLIUSERS, "r")))
159 {
160 log(0, 0, 0, 0, "WARNING! No users specified. Command-line access is open to all\n");
161 }
162 else
163 {
164 while (fgets(buf, 4096, f))
165 {
166 char *p;
167 if (*buf == '#') continue;
168 if ((p = strchr(buf, '\r'))) *p = 0;
169 if ((p = strchr(buf, '\n'))) *p = 0;
170 if (!*buf) continue;
171 if (!(p = strchr((char *)buf, ':'))) continue;
172 *p++ = 0;
173 cli_allow_user(cli, buf, p);
174 log(3, 0, 0, 0, "Allowing user %s to connect to the CLI\n", buf);
175 }
176 fclose(f);
177 }
178
179 memset(&addr, 0, sizeof(addr));
180 clifd = socket(PF_INET, SOCK_STREAM, 6);
181 setsockopt(clifd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
182 {
183 int flags;
184 // Set cli fd as non-blocking
185 flags = fcntl(clifd, F_GETFL, 0);
186 fcntl(clifd, F_SETFL, flags | O_NONBLOCK);
187 }
188 addr.sin_family = AF_INET;
189 addr.sin_port = htons(23);
190 if (bind(clifd, (void *) &addr, sizeof(addr)) < 0)
191 {
192 log(0, 0, 0, 0, "Error listening on cli port 23: %s\n", strerror(errno));
193 return;
194 }
195 listen(clifd, 10);
196 }
197
198 void cli_do(int sockfd)
199 {
200 int i;
201
202 if (fork()) return;
203
204 // Close sockets
205 if (udpfd) close(udpfd); udpfd = 0;
206 if (tapfd) close(tapfd); tapfd = 0;
207 if (snoopfd) close(snoopfd); snoopfd = 0;
208 for (i = 0; i < config->num_radfds; i++)
209 if (radfds[i]) close(radfds[i]);
210 if (ifrfd) close(ifrfd); ifrfd = 0;
211 if (cluster_sockfd) close(cluster_sockfd); cluster_sockfd = 0;
212 if (clifd) close(clifd); clifd = 0;
213
214 signal(SIGPIPE, SIG_DFL);
215 signal(SIGCHLD, SIG_DFL);
216 signal(SIGHUP, SIG_DFL);
217 signal(SIGUSR1, SIG_DFL);
218 signal(SIGQUIT, SIG_DFL);
219 signal(SIGKILL, SIG_DFL);
220 signal(SIGALRM, SIG_DFL);
221
222 log(3, 0, 0, 0, "Accepted connection to CLI\n");
223
224 debug_session = 0;
225 debug_tunnel = 0;
226 #ifdef RINGBUFFER
227 debug_rb_tail = ringbuffer->tail;
228 #endif
229 memset(&debug_flags, 0, sizeof(debug_flags));
230 debug_flags.critical = 1;
231
232 {
233 char prompt[1005];
234 snprintf(prompt, 1005, "%s> ", hostname);
235 cli_loop(cli, sockfd, prompt);
236 }
237
238 close(sockfd);
239 log(3, 0, 0, 0, "Closed CLI connection\n");
240 exit(0);
241 }
242
243 void cli_print_log(struct cli_def *cli, char *string)
244 {
245 log(3, 0, 0, 0, "%s\n", string);
246 }
247
248 void cli_do_file(FILE *fh)
249 {
250 log(3, 0, 0, 0, "Reading configuration file\n");
251 cli_print_callback(cli, cli_print_log);
252 cli_file(cli, fh);
253 cli_print_callback(cli, NULL);
254 }
255
256 int cmd_show_session(struct cli_def *cli, char *command, char **argv, int argc)
257 {
258 int i;
259 time_t time_now;
260
261 time(&time_now);
262 if (argc > 0)
263 {
264 // Show individual session
265 for (i = 0; i < argc; i++)
266 {
267 unsigned int s;
268 s = atoi(argv[i]);
269 if (!s || s > MAXSESSION)
270 {
271 cli_print(cli, "Invalid session id \"%s\"", argv[i]);
272 continue;
273 }
274 cli_print(cli, "\r\nSession %d:", s);
275 cli_print(cli, " User: %s", session[s].user[0] ? session[s].user : "none");
276 cli_print(cli, " Calling Num: %s", session[s].calling);
277 cli_print(cli, " Called Num: %s", session[s].called);
278 cli_print(cli, " Tunnel ID: %d", session[s].tunnel);
279 cli_print(cli, " IP address: %s", inet_toa(htonl(session[s].ip)));
280 cli_print(cli, " HSD sid: %lu", session[s].sid);
281 cli_print(cli, " Idle time: %u seconds", abs(time_now - session[s].last_packet));
282 cli_print(cli, " Next Recv: %u", session[s].nr);
283 cli_print(cli, " Next Send: %u", session[s].ns);
284 cli_print(cli, " Bytes In/Out: %lu/%lu", (unsigned long)session[s].cin, (unsigned long)session[s].total_cout);
285 cli_print(cli, " Pkts In/Out: %lu/%lu", (unsigned long)session[s].pin, (unsigned long)session[s].pout);
286 cli_print(cli, " MRU: %d", session[s].mru);
287 cli_print(cli, " Radius Session: %u", session[s].radius);
288 cli_print(cli, " Rx Speed: %lu", session[s].rx_connect_speed);
289 cli_print(cli, " Tx Speed: %lu", session[s].tx_connect_speed);
290 cli_print(cli, " Intercepted: %s", session[s].snoop ? "YES" : "no");
291 cli_print(cli, " Throttled: %s", session[s].throttle ? "YES" : "no");
292 cli_print(cli, " Walled Garden: %s", session[s].walled_garden ? "YES" : "no");
293 cli_print(cli, " Filter Bucket: %s", session[s].tbf ? filter_buckets[session[s].tbf].handle : "none");
294 }
295 return CLI_OK;
296 }
297
298 // Show Summary
299 cli_print(cli, " %s %4s %-32s %-15s %s %s %s %10s %10s %10s %4s %-15s %s",
300 "SID",
301 "TID",
302 "Username",
303 "IP",
304 "I",
305 "T",
306 "G",
307 "opened",
308 "downloaded",
309 "uploaded",
310 "idle",
311 "LAC",
312 "CLI");
313 for (i = 1; i < MAXSESSION; i++)
314 {
315 char *userip, *tunnelip;
316 if (!session[i].opened) continue;
317 userip = strdup(inet_toa(htonl(session[i].ip)));
318 tunnelip = strdup(inet_toa(htonl(tunnel[ session[i].tunnel ].ip)));
319 cli_print(cli, "%5d %4d %-32s %-15s %s %s %s %10u %10lu %10lu %4u %-15s %s",
320 i,
321 session[i].tunnel,
322 session[i].user[0] ? session[i].user : "*",
323 userip,
324 (session[i].snoop) ? "Y" : "N",
325 (session[i].throttle) ? "Y" : "N",
326 (session[i].walled_garden) ? "Y" : "N",
327 abs(time_now - (unsigned long)session[i].opened),
328 (unsigned long)session[i].total_cout,
329 (unsigned long)session[i].total_cin,
330 abs(time_now - (session[i].last_packet ? session[i].last_packet : time_now)),
331 tunnelip,
332 session[i].calling[0] ? session[i].calling : "*");
333 if (userip) free(userip);
334 if (tunnelip) free(tunnelip);
335 }
336 return CLI_OK;
337 }
338
339 int cmd_show_tunnels(struct cli_def *cli, char *command, char **argv, int argc)
340 {
341 int i, x, show_all = 0;
342 time_t time_now;
343 char *states[] = {
344 "Free",
345 "Open",
346 "Closing",
347 "Opening",
348 };
349
350 time(&time_now);
351 if (argc > 0)
352 {
353 if (strcmp(argv[0], "all") == 0)
354 {
355 show_all = 1;
356 }
357 else
358 {
359 // Show individual tunnel
360 for (i = 0; i < argc; i++)
361 {
362 char s[65535] = {0};
363 unsigned int t;
364 t = atoi(argv[i]);
365 if (!t || t > MAXTUNNEL)
366 {
367 cli_print(cli, "Invalid tunnel id \"%s\"", argv[i]);
368 continue;
369 }
370 cli_print(cli, "\r\nTunnel %d:", t);
371 cli_print(cli, " State: %s", states[tunnel[t].state]);
372 cli_print(cli, " Hostname: %s", tunnel[t].hostname[0] ? tunnel[t].hostname : "(none)");
373 cli_print(cli, " Remote IP: %s", inet_toa(htonl(tunnel[t].ip)));
374 cli_print(cli, " Remote Port: %d", tunnel[t].port);
375 cli_print(cli, " Rx Window: %u", tunnel[t].window);
376 cli_print(cli, " Next Recv: %u", tunnel[t].nr);
377 cli_print(cli, " Next Send: %u", tunnel[t].ns);
378 cli_print(cli, " Queue Len: %u", tunnel[t].controlc);
379 cli_print(cli, " Last Packet Age:%u", (unsigned)(time_now - tunnel[t].last));
380
381 for (x = 0; x < MAXSESSION; x++)
382 if (session[x].tunnel == t && session[x].opened && !session[x].die)
383 sprintf(s, "%s%u ", s, x);
384 cli_print(cli, " Sessions: %s", s);
385 }
386 return CLI_OK;
387 }
388 }
389
390 // Show tunnel summary
391 cli_print(cli, "%s %s %s %s %s",
392 "TID",
393 "Hostname",
394 "IP",
395 "State",
396 "Sessions");
397 for (i = 1; i < MAXTUNNEL; i++)
398 {
399 int sessions = 0;
400 if (!show_all && (!tunnel[i].ip || tunnel[i].die || !tunnel[i].hostname[0])) continue;
401
402 for (x = 0; x < MAXSESSION; x++) if (session[x].tunnel == i && session[x].opened && !session[x].die) sessions++;
403 cli_print(cli, "%d %s %s %s %d",
404 i,
405 *tunnel[i].hostname ? tunnel[i].hostname : "(null)",
406 inet_toa(htonl(tunnel[i].ip)),
407 states[tunnel[i].state],
408 sessions);
409 }
410 return CLI_OK;
411 }
412
413 int cmd_show_users(struct cli_def *cli, char *command, char **argv, int argc)
414 {
415 char sid[32][8];
416 char *sargv[32];
417 int sargc = 0;
418 int i;
419 for (i = 0; i < MAXSESSION; i++)
420 {
421 if (!session[i].opened) continue;
422 if (!session[i].user[0]) continue;
423 if (argc > 0)
424 {
425 int j;
426 for (j = 0; j < argc && sargc < 32; j++)
427 {
428 if (strcmp(argv[j], session[i].user) == 0)
429 {
430 snprintf(sid[sargc], sizeof(sid[0]), "%d", i);
431 sargv[sargc] = sid[sargc];
432 sargc++;
433 }
434 }
435
436 continue;
437 }
438
439 cli_print(cli, "%s", session[i].user);
440 }
441
442 if (sargc > 0)
443 return cmd_show_session(cli, "users", sargv, sargc);
444
445 return CLI_OK;
446 }
447
448 int cmd_show_counters(struct cli_def *cli, char *command, char **argv, int argc)
449 {
450 cli_print(cli, "%-10s %-8s %-10s %-8s", "Ethernet", "Bytes", "Packets", "Errors");
451 cli_print(cli, "%-10s %8lu %8lu %8lu", "RX",
452 GET_STAT(tap_rx_bytes),
453 GET_STAT(tap_rx_packets),
454 GET_STAT(tap_rx_errors));
455 cli_print(cli, "%-10s %8lu %8lu %8lu", "TX",
456 GET_STAT(tap_tx_bytes),
457 GET_STAT(tap_tx_packets),
458 GET_STAT(tap_tx_errors));
459 cli_print(cli, "");
460
461 cli_print(cli, "%-10s %-8s %-10s %-8s %-8s", "Tunnel", "Bytes", "Packets", "Errors", "Retries");
462 cli_print(cli, "%-10s %8lu %8lu %8lu %8lu", "RX",
463 GET_STAT(tunnel_rx_bytes),
464 GET_STAT(tunnel_rx_packets),
465 GET_STAT(tunnel_rx_errors),
466 0L);
467 cli_print(cli, "%-10s %8lu %8lu %8lu %8lu", "TX",
468 GET_STAT(tunnel_tx_bytes),
469 GET_STAT(tunnel_tx_packets),
470 GET_STAT(tunnel_tx_errors),
471 GET_STAT(tunnel_retries));
472 cli_print(cli, "");
473
474 cli_print(cli, "%-30s%-10s", "Counter", "Value");
475 cli_print(cli, "-----------------------------------------");
476 cli_print(cli, "%-30s%lu", "radius_retries", GET_STAT(radius_retries));
477 cli_print(cli, "%-30s%lu", "arp_errors", GET_STAT(arp_errors));
478 cli_print(cli, "%-30s%lu", "arp_replies", GET_STAT(arp_replies));
479 cli_print(cli, "%-30s%lu", "arp_discarded", GET_STAT(arp_discarded));
480 cli_print(cli, "%-30s%lu", "arp_sent", GET_STAT(arp_sent));
481 cli_print(cli, "%-30s%lu", "arp_recv", GET_STAT(arp_recv));
482 cli_print(cli, "%-30s%lu", "packets_snooped", GET_STAT(packets_snooped));
483 cli_print(cli, "%-30s%lu", "tunnel_created", GET_STAT(tunnel_created));
484 cli_print(cli, "%-30s%lu", "session_created", GET_STAT(session_created));
485 cli_print(cli, "%-30s%lu", "tunnel_timeout", GET_STAT(tunnel_timeout));
486 cli_print(cli, "%-30s%lu", "session_timeout", GET_STAT(session_timeout));
487 cli_print(cli, "%-30s%lu", "radius_timeout", GET_STAT(radius_timeout));
488 cli_print(cli, "%-30s%lu", "radius_overflow", GET_STAT(radius_overflow));
489 cli_print(cli, "%-30s%lu", "tunnel_overflow", GET_STAT(tunnel_overflow));
490 cli_print(cli, "%-30s%lu", "session_overflow", GET_STAT(session_overflow));
491 cli_print(cli, "%-30s%lu", "ip_allocated", GET_STAT(ip_allocated));
492 cli_print(cli, "%-30s%lu", "ip_freed", GET_STAT(ip_freed));
493
494 #ifdef STAT_CALLS
495 cli_print(cli, "\n%-30s%-10s", "Counter", "Value");
496 cli_print(cli, "-----------------------------------------");
497 cli_print(cli, "%-30s%lu", "call_processtap", GET_STAT(call_processtap));
498 cli_print(cli, "%-30s%lu", "call_processarp", GET_STAT(call_processarp));
499 cli_print(cli, "%-30s%lu", "call_processipout", GET_STAT(call_processipout));
500 cli_print(cli, "%-30s%lu", "call_processudp", GET_STAT(call_processudp));
501 cli_print(cli, "%-30s%lu", "call_processpap", GET_STAT(call_processpap));
502 cli_print(cli, "%-30s%lu", "call_processchap", GET_STAT(call_processchap));
503 cli_print(cli, "%-30s%lu", "call_processlcp", GET_STAT(call_processlcp));
504 cli_print(cli, "%-30s%lu", "call_processipcp", GET_STAT(call_processipcp));
505 cli_print(cli, "%-30s%lu", "call_processipin", GET_STAT(call_processipin));
506 cli_print(cli, "%-30s%lu", "call_processccp", GET_STAT(call_processccp));
507 cli_print(cli, "%-30s%lu", "call_processrad", GET_STAT(call_processrad));
508 cli_print(cli, "%-30s%lu", "call_sendarp", GET_STAT(call_sendarp));
509 cli_print(cli, "%-30s%lu", "call_sendipcp", GET_STAT(call_sendipcp));
510 cli_print(cli, "%-30s%lu", "call_sendchap", GET_STAT(call_sendchap));
511 cli_print(cli, "%-30s%lu", "call_sessionbyip", GET_STAT(call_sessionbyip));
512 cli_print(cli, "%-30s%lu", "call_sessionbyuser", GET_STAT(call_sessionbyuser));
513 cli_print(cli, "%-30s%lu", "call_tunnelsend", GET_STAT(call_tunnelsend));
514 cli_print(cli, "%-30s%lu", "call_tunnelkill", GET_STAT(call_tunnelkill));
515 cli_print(cli, "%-30s%lu", "call_tunnelshutdown", GET_STAT(call_tunnelshutdown));
516 cli_print(cli, "%-30s%lu", "call_sessionkill", GET_STAT(call_sessionkill));
517 cli_print(cli, "%-30s%lu", "call_sessionshutdown", GET_STAT(call_sessionshutdown));
518 cli_print(cli, "%-30s%lu", "call_sessionsetup", GET_STAT(call_sessionsetup));
519 cli_print(cli, "%-30s%lu", "call_assign_ip_address",GET_STAT(call_assign_ip_address));
520 cli_print(cli, "%-30s%lu", "call_free_ip_address", GET_STAT(call_free_ip_address));
521 cli_print(cli, "%-30s%lu", "call_dump_acct_info", GET_STAT(call_dump_acct_info));
522 cli_print(cli, "%-30s%lu", "call_radiussend", GET_STAT(call_radiussend));
523 cli_print(cli, "%-30s%lu", "call_radiusretry", GET_STAT(call_radiusretry));
524 #endif
525 return CLI_OK;
526 }
527
528 int cmd_show_version(struct cli_def *cli, char *command, char **argv, int argc)
529 {
530 cli_print(cli, "L2TPNS %s", VERSION);
531 cli_print(cli, "ID: %s", rcs_id);
532 return CLI_OK;
533 }
534
535 int cmd_show_pool(struct cli_def *cli, char *command, char **argv, int argc)
536 {
537 int i;
538 int used = 0, free = 0, show_all = 0;
539 time_t time_now;
540
541 if (argc > 0 && strcmp(argv[0], "all") == 0)
542 show_all = 1;
543
544 time(&time_now);
545 cli_print(cli, "%-15s %4s %8s %s", "IP Address", "Used", "Session", "User");
546 for (i = 0; i < MAXIPPOOL; i++)
547 {
548 if (!ip_address_pool[i].address) continue;
549 if (ip_address_pool[i].assigned)
550 {
551 cli_print(cli, "%-15s Y %8d %s",
552 inet_toa(ip_address_pool[i].address), ip_address_pool[i].session, session[ip_address_pool[i].session].user);
553
554 used++;
555 }
556 else
557 {
558 if (ip_address_pool[i].last)
559 cli_print(cli, "%-15s N %8s [%s] %ds",
560 inet_toa(ip_address_pool[i].address), "",
561 ip_address_pool[i].user, time_now - ip_address_pool[i].last);
562 else if (show_all)
563 cli_print(cli, "%-15s N", inet_toa(ip_address_pool[i].address));
564
565 free++;
566 }
567 }
568
569 if (!show_all)
570 cli_print(cli, "(Not displaying unused addresses)");
571
572 cli_print(cli, "\r\nFree: %d\r\nUsed: %d", free, used);
573 return CLI_OK;
574 }
575
576 void print_save_config(struct cli_def *cli, char *string)
577 {
578 if (save_config_fh)
579 fprintf(save_config_fh, "%s\n", string);
580 }
581
582 int cmd_write_memory(struct cli_def *cli, char *command, char **argv, int argc)
583 {
584 if ((save_config_fh = fopen(config->config_file, "w")))
585 {
586 cli_print(cli, "Writing configuration");
587 cli_print_callback(cli, print_save_config);
588 cmd_show_run(cli, command, argv, argc);
589 cli_print_callback(cli, NULL);
590 fclose(save_config_fh);
591 sleep(1);
592 }
593 else
594 {
595 cli_print(cli, "Error writing configuration: %s", strerror(errno));
596 }
597 return CLI_OK;
598 }
599
600 int cmd_show_run(struct cli_def *cli, char *command, char **argv, int argc)
601 {
602 int i;
603
604 cli_print(cli, "# Current configuration:");
605
606 for (i = 0; config_values[i].key; i++)
607 {
608 void *value = ((void *)config) + config_values[i].offset;
609 if (config_values[i].type == STRING)
610 cli_print(cli, "set %s \"%.*s\"", config_values[i].key, config_values[i].size, (char *)value);
611 else if (config_values[i].type == IP)
612 cli_print(cli, "set %s %s", config_values[i].key, inet_toa(*(unsigned *)value));
613 else if (config_values[i].type == SHORT)
614 cli_print(cli, "set %s %hu", config_values[i].key, *(short *)value);
615 else if (config_values[i].type == BOOL)
616 cli_print(cli, "set %s %s", config_values[i].key, (*(int *)value) ? "yes" : "no");
617 else if (config_values[i].type == INT)
618 cli_print(cli, "set %s %d", config_values[i].key, *(int *)value);
619 else if (config_values[i].type == UNSIGNED_LONG)
620 cli_print(cli, "set %s %lu", config_values[i].key, *(unsigned long *)value);
621 }
622
623 cli_print(cli, "# Plugins");
624 for (i = 0; i < MAXPLUGINS; i++)
625 {
626 if (*config->plugins[i])
627 {
628 cli_print(cli, "load plugin \"%s\"", config->plugins[i]);
629 }
630 }
631
632 cli_print(cli, "# end");
633 return CLI_OK;
634 }
635
636 int cmd_show_radius(struct cli_def *cli, char *command, char **argv, int argc)
637 {
638 char *states[] = {
639 "NULL",
640 "CHAP",
641 "AUTH",
642 "IPCP",
643 "START",
644 "STOP",
645 "WAIT",
646 };
647 int i, free = 0, used = 0, show_all = 0;
648 time_t time_now;
649
650 cli_print(cli, "%6s%5s%6s%9s%9s%4s", "Radius", "Sock", "State", "Session", "Retry", "Try");
651
652 time(&time_now);
653
654 if (argc > 0 && strcmp(argv[0], "all") == 0)
655 show_all = 1;
656
657 for (i = 1; i < MAXRADIUS; i++)
658 {
659 if (radius[i].state == RADIUSNULL)
660 free++;
661 else
662 used++;
663
664 if (!show_all && radius[i].state == RADIUSNULL) continue;
665
666 cli_print(cli, "%6d%5d%6s%9d%9u%4d",
667 i >> RADIUS_SHIFT,
668 i & RADIUS_MASK,
669 states[radius[i].state],
670 radius[i].session,
671 radius[i].retry,
672 radius[i].try);
673 }
674
675 cli_print(cli, "\r\nFree: %d\r\nUsed: %d", free, used);
676
677 return CLI_OK;
678 }
679
680 int cmd_show_plugins(struct cli_def *cli, char *command, char **argv, int argc)
681 {
682 int i;
683 cli_print(cli, "Plugins currently loaded:");
684 for (i = 0; i < MAXPLUGINS; i++)
685 {
686 if (*config->plugins[i])
687 {
688 cli_print(cli, " %s", config->plugins[i]);
689 }
690 }
691 return CLI_OK;
692 }
693
694 int cmd_show_throttle(struct cli_def *cli, char *command, char **argv, int argc)
695 {
696 int i;
697 cli_print(cli, "Token bucket filters:");
698 cli_print(cli, "%-6s %8s %-4s", "ID", "Handle", "Used");
699 for (i = 0; i < MAXSESSION; i++)
700 {
701 if (!*filter_buckets[i].handle)
702 continue;
703
704 cli_print(cli, "%-6d %8s %c",
705 i,
706 filter_buckets[i].handle,
707 (filter_buckets[i].in_use) ? 'Y' : 'N');
708 }
709 return CLI_OK;
710 }
711
712 int cmd_show_banana(struct cli_def *cli, char *command, char **argv, int argc)
713 {
714 cli_print(cli, " _\n"
715 "//\\\n"
716 "V \\\n"
717 " \\ \\_\n"
718 " \\,'.`-.\n"
719 " |\\ `. `.\n"
720 " ( \\ `. `-. _,.-:\\\n"
721 " \\ \\ `. `-._ __..--' ,-';/\n"
722 " \\ `. `-. `-..___..---' _.--' ,'/\n"
723 " `. `. `-._ __..--' ,' /\n"
724 " `. `-_ ``--..'' _.-' ,'\n"
725 " `-_ `-.___ __,--' ,'\n"
726 " `-.__ `----\"\"\" __.-'\n"
727 "hh `--..____..--'");
728
729 return CLI_OK;
730 }
731
732 int cmd_clear_counters(struct cli_def *cli, char *command, char **argv, int argc)
733 {
734 cli_print(cli, "Counters cleared");
735 SET_STAT(last_reset, time(NULL));
736 return CLI_OK;
737 }
738
739 int cmd_drop_user(struct cli_def *cli, char *command, char **argv, int argc)
740 {
741 int i;
742 sessionidt s;
743
744 if (!argc)
745 {
746 cli_print(cli, "Specify a user to drop");
747 return CLI_OK;
748 }
749 for (i = 0; i < argc; i++)
750 {
751 if (strchr(argv[i], '?'))
752 {
753 cli_print(cli, "username ...");
754 return CLI_OK;
755 }
756 }
757
758 for (i = 0; i < argc; i++)
759 {
760 if (!(s = sessionbyuser(argv[i])))
761 {
762 cli_print(cli, "User %s is not connected", argv[i]);
763 continue;
764 }
765
766 if (session[s].ip && session[s].opened && !session[s].die)
767 {
768 int x;
769
770 cli_print(cli, "Dropping user %s", session[s].user);
771 for (x = 0; x < MAXSESSION; x++)
772 {
773 if (!cli_session_kill[x])
774 {
775 cli_session_kill[x] = s;
776 break;
777 }
778 }
779 }
780 }
781
782 return CLI_OK;
783 }
784
785 int cmd_drop_tunnel(struct cli_def *cli, char *command, char **argv, int argc)
786 {
787 int i;
788 tunnelidt tid;
789
790 if (!argc)
791 {
792 cli_print(cli, "Specify a tunnel to drop");
793 return CLI_OK;
794 }
795 for (i = 0; i < argc; i++)
796 {
797 if (strchr(argv[i], '?'))
798 {
799 cli_print(cli, "tunnel_id ...");
800 return CLI_OK;
801 }
802 }
803
804 for (i = 0; i < argc; i++)
805 {
806 int x;
807
808 if ((tid = atol(argv[i])) <= 0 || (tid > MAXTUNNEL))
809 {
810 cli_print(cli, "Invalid tunnel ID (%d - %d)", 0, MAXTUNNEL);
811 continue;
812 }
813
814 if (!tunnel[tid].ip)
815 {
816 cli_print(cli, "Tunnel %d is not connected", tid);
817 continue;
818 }
819
820 if (tunnel[tid].die)
821 {
822 cli_print(cli, "Tunnel %d is already being shut down", tid);
823 continue;
824 }
825
826 for (x = 0; x < MAXTUNNEL; x++)
827 {
828 if (!cli_tunnel_kill[x])
829 {
830 cli_tunnel_kill[x] = tid;
831 cli_print(cli, "Tunnel %d shut down (%s)", tid, tunnel[tid].hostname);
832 break;
833 }
834 }
835 }
836
837 return CLI_OK;
838 }
839
840 int cmd_drop_session(struct cli_def *cli, char *command, char **argv, int argc)
841 {
842 int i;
843 sessionidt s;
844
845 if (!argc)
846 {
847 cli_print(cli, "Specify a session id to drop");
848 return CLI_OK;
849 }
850 for (i = 0; i < argc; i++)
851 {
852 if (strchr(argv[i], '?'))
853 {
854 cli_print(cli, "session_id ...");
855 return CLI_OK;
856 }
857 }
858
859 for (i = 0; i < argc; i++)
860 {
861 if ((s = atol(argv[i])) <= 0 || (s > MAXSESSION))
862 {
863 cli_print(cli, "Invalid session ID (%d - %d)", 0, MAXSESSION);
864 continue;
865 }
866
867 if (session[s].opened && !session[s].die)
868 {
869 int x;
870 for (x = 0; x < MAXSESSION; x++)
871 {
872 if (!cli_session_kill[x])
873 {
874 cli_session_kill[x] = s;
875 break;
876 }
877 }
878 cli_print(cli, "Dropping session %d", s);
879 }
880 else
881 {
882 cli_print(cli, "Session %d is not active.", s);
883 }
884 }
885
886 return CLI_OK;
887 }
888
889 int cmd_snoop(struct cli_def *cli, char *command, char **argv, int argc)
890 {
891 int i;
892 sessionidt s;
893
894 if (!argc)
895 {
896 cli_print(cli, "Specify a user");
897 return CLI_OK;
898 }
899 for (i = 0; i < argc; i++)
900 {
901 if (strchr(argv[i], '?'))
902 {
903 cli_print(cli, "username ...");
904 return CLI_OK;
905 }
906 }
907
908 for (i = 0; i < argc; i++)
909 {
910 if (!(s = sessionbyuser(argv[i])))
911 {
912 cli_print(cli, "User %s is not connected", argv[i]);
913 continue;
914 }
915 session[s].snoop = 1;
916
917 cli_print(cli, "Snooping user %s", argv[i]);
918 }
919 return CLI_OK;
920 }
921
922 int cmd_no_snoop(struct cli_def *cli, char *command, char **argv, int argc)
923 {
924 int i;
925 sessionidt s;
926
927 if (!argc)
928 {
929 cli_print(cli, "Specify a user");
930 return CLI_OK;
931 }
932 for (i = 0; i < argc; i++)
933 {
934 if (strchr(argv[i], '?'))
935 {
936 cli_print(cli, "username ...");
937 return CLI_OK;
938 }
939 }
940
941 for (i = 0; i < argc; i++)
942 {
943 if (!(s = sessionbyuser(argv[i])))
944 {
945 cli_print(cli, "User %s is not connected", argv[i]);
946 continue;
947 }
948 session[s].snoop = 0;
949
950 cli_print(cli, "Not snooping user %s", argv[i]);
951 }
952 return CLI_OK;
953 }
954
955 int cmd_throttle(struct cli_def *cli, char *command, char **argv, int argc)
956 {
957 int i;
958 sessionidt s;
959
960 if (!argc)
961 {
962 cli_print(cli, "Specify a user");
963 return CLI_OK;
964 }
965 for (i = 0; i < argc; i++)
966 {
967 if (strchr(argv[i], '?'))
968 {
969 cli_print(cli, "username ...");
970 return CLI_OK;
971 }
972 }
973
974 for (i = 0; i < argc; i++)
975 {
976 if (!(s = sessionbyuser(argv[i])))
977 {
978 cli_print(cli, "User %s is not connected", argv[i]);
979 continue;
980 }
981 if (!throttle_session(s, 1))
982 cli_print(cli, "error throttling %s", argv[i]);
983 else
984 cli_print(cli, "throttling user %s", argv[i]);
985 }
986 return CLI_OK;
987 }
988
989 int cmd_no_throttle(struct cli_def *cli, char *command, char **argv, int argc)
990 {
991 int i;
992 sessionidt s;
993
994 if (!argc)
995 {
996 cli_print(cli, "Specify a user");
997 return CLI_OK;
998 }
999 for (i = 0; i < argc; i++)
1000 {
1001 if (strchr(argv[i], '?'))
1002 {
1003 cli_print(cli, "username ...");
1004 return CLI_OK;
1005 }
1006 }
1007
1008 for (i = 0; i < argc; i++)
1009 {
1010 if (!(s = sessionbyuser(argv[i])))
1011 {
1012 cli_print(cli, "User %s is not connected", argv[i]);
1013 continue;
1014 }
1015 throttle_session(s, 0);
1016
1017 cli_print(cli, "unthrottling user %s", argv[i]);
1018 }
1019 return CLI_OK;
1020 }
1021
1022 int cmd_debug(struct cli_def *cli, char *command, char **argv, int argc)
1023 {
1024 int i;
1025
1026 if (!argc)
1027 {
1028 cli_print(cli, "Currently debugging: ");
1029 if (debug_flags.critical) cli_print(cli, "critical ");
1030 if (debug_flags.error) cli_print(cli, "error ");
1031 if (debug_flags.warning) cli_print(cli, "warning ");
1032 if (debug_flags.info) cli_print(cli, "info ");
1033 if (debug_flags.calls) cli_print(cli, "calls ");
1034 if (debug_flags.data) cli_print(cli, "data ");
1035 cli_print(cli, "");
1036 return CLI_OK;
1037 }
1038
1039 for (i = 0; i < argc; i++)
1040 {
1041 if (*argv[i] == '?')
1042 {
1043 cli_print(cli, "Possible debugging states are:");
1044 cli_print(cli, " critical");
1045 cli_print(cli, " error");
1046 cli_print(cli, " warning");
1047 cli_print(cli, " info");
1048 cli_print(cli, " calls");
1049 cli_print(cli, " data");
1050 return CLI_OK;
1051 }
1052 }
1053
1054 for (i = 0; i < argc; i++)
1055 {
1056 if (strcasecmp(argv[i], "critical") == 0) debug_flags.critical = 1;
1057 if (strcasecmp(argv[i], "error") == 0) debug_flags.error = 1;
1058 if (strcasecmp(argv[i], "warning") == 0) debug_flags.warning = 1;
1059 if (strcasecmp(argv[i], "info") == 0) debug_flags.info = 1;
1060 if (strcasecmp(argv[i], "calls") == 0) debug_flags.calls = 1;
1061 if (strcasecmp(argv[i], "data") == 0) debug_flags.data = 1;
1062 if (strcasecmp(argv[i], "all") == 0)
1063 {
1064 memset(&debug_flags, 1, sizeof(debug_flags));
1065 debug_flags.data = 0;
1066 }
1067 }
1068
1069 return CLI_OK;
1070 }
1071
1072 int cmd_no_debug(struct cli_def *cli, char *command, char **argv, int argc)
1073 {
1074 int i;
1075
1076 for (i = 0; i < argc; i++)
1077 {
1078 if (strcasecmp(argv[i], "critical") == 0) debug_flags.critical = 0;
1079 if (strcasecmp(argv[i], "error") == 0) debug_flags.error = 0;
1080 if (strcasecmp(argv[i], "warning") == 0) debug_flags.warning = 0;
1081 if (strcasecmp(argv[i], "info") == 0) debug_flags.info = 0;
1082 if (strcasecmp(argv[i], "calls") == 0) debug_flags.calls = 0;
1083 if (strcasecmp(argv[i], "data") == 0) debug_flags.data = 0;
1084 if (strcasecmp(argv[i], "all") == 0) memset(&debug_flags, 0, sizeof(debug_flags));
1085 }
1086
1087 return CLI_OK;
1088 }
1089
1090 int cmd_load_plugin(struct cli_def *cli, char *command, char **argv, int argc)
1091 {
1092 int i, firstfree = 0;
1093 if (argc != 1)
1094 {
1095 cli_print(cli, "Specify a plugin to load");
1096 return CLI_OK;
1097 }
1098
1099 for (i = 0; i < MAXPLUGINS; i++)
1100 {
1101 if (!*config->plugins[i] && !firstfree)
1102 firstfree = i;
1103 if (strcmp(config->plugins[i], argv[0]) == 0)
1104 {
1105 cli_print(cli, "Plugin is already loaded");
1106 return CLI_OK;
1107 }
1108 }
1109
1110 if (firstfree)
1111 {
1112 strncpy(config->plugins[firstfree], argv[0], sizeof(config->plugins[firstfree]) - 1);
1113 config->reload_config = 1;
1114 cli_print(cli, "Loading plugin %s", argv[0]);
1115 }
1116
1117 return CLI_OK;
1118 }
1119
1120 int cmd_remove_plugin(struct cli_def *cli, char *command, char **argv, int argc)
1121 {
1122 int i;
1123
1124 if (argc != 1)
1125 {
1126 cli_print(cli, "Specify a plugin to remove");
1127 return CLI_OK;
1128 }
1129
1130 for (i = 0; i < MAXPLUGINS; i++)
1131 {
1132 if (strcmp(config->plugins[i], argv[0]) == 0)
1133 {
1134 config->reload_config = 1;
1135 memset(config->plugins[i], 0, sizeof(config->plugins[i]));
1136 return CLI_OK;
1137 }
1138 }
1139
1140 cli_print(cli, "Plugin is not loaded");
1141 return CLI_OK;
1142 }
1143
1144 char *duration(time_t seconds)
1145 {
1146 static char *buf = NULL;
1147 if (!buf) buf = calloc(64, 1);
1148
1149 if (seconds > 86400)
1150 sprintf(buf, "%d days", (int)(seconds / 86400.0));
1151 else if (seconds > 60)
1152 sprintf(buf, "%02d:%02lu", (int)(seconds / 3600.0), seconds % 60);
1153 else
1154 sprintf(buf, "%lu sec", seconds);
1155 return buf;
1156 }
1157
1158 int cmd_uptime(struct cli_def *cli, char *command, char **argv, int argc)
1159 {
1160 FILE *fh;
1161 char buf[100], *p = buf, *loads[3];
1162 int i, num_sessions = 0;
1163 time_t time_now;
1164
1165 fh = fopen("/proc/loadavg", "r");
1166 fgets(buf, 100, fh);
1167 fclose(fh);
1168
1169 for (i = 0; i < 3; i++)
1170 loads[i] = strdup(strsep(&p, " "));
1171
1172 time(&time_now);
1173 strftime(buf, 99, "%H:%M:%S", localtime(&time_now));
1174
1175 for (i = 1; i < MAXSESSION; i++)
1176 if (session[i].opened) num_sessions++;
1177
1178 cli_print(cli, "%s up %s, %d users, load average: %s, %s, %s",
1179 buf,
1180 duration(abs(time_now - config->start_time)),
1181 num_sessions,
1182 loads[0], loads[1], loads[2]
1183 );
1184 for (i = 0; i < 3; i++)
1185 if (loads[i]) free(loads[i]);
1186
1187 cli_print(cli, "Bandwidth: %s", config->bandwidth);
1188
1189 return CLI_OK;
1190 }
1191
1192 int cmd_set(struct cli_def *cli, char *command, char **argv, int argc)
1193 {
1194 int i;
1195
1196 if (argc != 2)
1197 {
1198 cli_print(cli, "Usage: set <variable> <value>");
1199 return CLI_OK;
1200 }
1201
1202 for (i = 0; config_values[i].key; i++)
1203 {
1204 void *value = ((void *)config) + config_values[i].offset;
1205 if (strcmp(config_values[i].key, argv[0]) == 0)
1206 {
1207 // Found a value to set
1208 cli_print(cli, "Setting \"%s\" to \"%s\"", argv[0], argv[1]);
1209 switch (config_values[i].type)
1210 {
1211 case STRING:
1212 strncpy((char *)value, argv[1], config_values[i].size - 1);
1213 break;
1214 case INT:
1215 *(int *)value = atoi(argv[1]);
1216 break;
1217 case UNSIGNED_LONG:
1218 *(unsigned long *)value = atol(argv[1]);
1219 break;
1220 case SHORT:
1221 *(short *)value = atoi(argv[1]);
1222 break;
1223 case IP:
1224 *(unsigned *)value = inet_addr(argv[1]);
1225 break;
1226 case BOOL:
1227 if (strcasecmp(argv[1], "yes") == 0 || strcasecmp(argv[1], "true") == 0 || strcasecmp(argv[1], "1") == 0)
1228 *(int *)value = 1;
1229 else
1230 *(int *)value = 0;
1231 break;
1232 default:
1233 cli_print(cli, "Unknown variable type");
1234 break;
1235 }
1236 config->reload_config = 1;
1237 return CLI_OK;
1238 }
1239 }
1240
1241 cli_print(cli, "Unknown variable \"%s\"", argv[0]);
1242 return CLI_OK;
1243 }
1244
1245 int regular_stuff(struct cli_def *cli)
1246 {
1247 int i = debug_rb_tail;
1248 int reprompt = 0;
1249
1250 #ifdef RINGBUFFER
1251 while (i != ringbuffer->tail)
1252 {
1253 int show_message = 0;
1254
1255 if (*ringbuffer->buffer[i].message)
1256 {
1257 // Always show messages if we are doing general debug
1258 if (ringbuffer->buffer[i].level == 0 && debug_flags.critical) show_message = 1;
1259 if (ringbuffer->buffer[i].level == 1 && debug_flags.error) show_message = 1;
1260 if (ringbuffer->buffer[i].level == 2 && debug_flags.warning) show_message = 1;
1261 if (ringbuffer->buffer[i].level == 3 && debug_flags.info) show_message = 1;
1262 if (ringbuffer->buffer[i].level == 4 && debug_flags.calls) show_message = 1;
1263 if (ringbuffer->buffer[i].level == 5 && debug_flags.data) show_message = 1;
1264 }
1265
1266 if (show_message)
1267 {
1268 ipt address = ntohl(ringbuffer->buffer[i].address);
1269 char *ipaddr;
1270 struct in_addr addr;
1271
1272 memcpy(&addr, &address, sizeof(ringbuffer->buffer[i].address));
1273 ipaddr = inet_ntoa(addr);
1274
1275 cli_print(cli, "\r%s-%s-%u-%u %s",
1276 debug_levels[(int)ringbuffer->buffer[i].level],
1277 ipaddr,
1278 ringbuffer->buffer[i].tunnel,
1279 ringbuffer->buffer[i].session,
1280 ringbuffer->buffer[i].message);
1281
1282 reprompt = 1;
1283 }
1284
1285 if (++i == ringbuffer->tail) break;
1286 if (i == RINGBUFFER_SIZE) i = 0;
1287 }
1288
1289 debug_rb_tail = ringbuffer->tail;
1290 if (reprompt)
1291 cli_reprompt(cli);
1292 #endif
1293 return CLI_OK;
1294 }