ldp_command.c revision 1.4 1 /* $NetBSD: ldp_command.c,v 1.4 2010/12/31 11:29:33 kefren Exp $ */
2
3 /*-
4 * Copyright (c) 2010 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Mihai Chelaru <kefren (at) NetBSD.org>
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <arpa/inet.h>
33
34 #include <netinet/in.h>
35 #include <netinet/tcp.h>
36
37 #include <sys/socket.h>
38 #include <sys/queue.h>
39
40 #include <errno.h>
41 #include <pwd.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46
47 #include "label.h"
48 #include "ldp.h"
49 #include "ldp_command.h"
50 #include "ldp_errors.h"
51 #include "ldp_peer.h"
52 #include "socketops.h"
53
54 struct com_sock csockets[MAX_COMMAND_SOCKETS];
55 extern int ldp_hello_time, ldp_keepalive_time, ldp_holddown_time,
56 min_label, max_label, debug_f, warn_f;
57
58 #define writestr(soc, str) write(soc, str, strlen(str))
59
60 #define MAXSEND 1024
61 char sendspace[MAXSEND];
62
63 static int verify_root_pwd(char *);
64 static void echo_on(int s);
65 static void echo_off(int s);
66
67 static struct com_func main_commands[] = {
68 { "show", show_func },
69 { "set", set_func },
70 { "quit", exit_func },
71 { "exit", exit_func },
72 { "", NULL }
73 };
74
75 static struct com_func show_commands[] = {
76 { "neighbours", show_neighbours },
77 { "bindings", show_bindings },
78 { "debug", show_debug },
79 { "hellos", show_hellos },
80 { "parameters", show_parameters },
81 { "version", show_version },
82 { "warning", show_warning },
83 { "", NULL }
84 };
85
86 struct com_func set_commands[] = {
87 { "debug", set_debug },
88 { "hello-time", set_hello_time },
89 { "warning", set_warning },
90 { "", NULL }
91 };
92
93 int
94 verify_root_pwd(char *pw)
95 {
96 struct passwd *p;
97
98 if ((p = getpwuid(0)) == NULL)
99 return 0;
100
101 if (strcmp(crypt(pw, p->pw_passwd), p->pw_passwd))
102 return 0;
103
104 return 1;
105 }
106
107
108 void
109 init_command_sockets()
110 {
111 int i;
112
113 for (i = 0; i<MAX_COMMAND_SOCKETS; i++) {
114 csockets[i].socket = -1;
115 csockets[i].auth = 0;
116 }
117 }
118
119 int
120 create_command_socket(int port)
121 {
122 struct sockaddr_in sin;
123 int s;
124
125 sin.sin_len = sizeof(sin);
126 sin.sin_family = AF_INET;
127 sin.sin_port = htons(port);
128 sin.sin_addr.s_addr = ntohl(INADDR_LOOPBACK);
129
130 s = socket(PF_INET, SOCK_STREAM, 6);
131 if (s < 0)
132 return s;
133
134 if (bind(s, (struct sockaddr *) &sin, sizeof(sin))) {
135 fatalp("bind: %s", strerror(errno));
136 close(s);
137 return -1;
138 }
139
140 if (listen(s, 5) == -1) {
141 fatalp("listen: %s", strerror(errno));
142 close(s);
143 return -1;
144 }
145 return s;
146 }
147
148 void
149 command_accept(int s)
150 {
151 int as = accept(s, NULL, 0);
152
153 if (as < 0) {
154 fatalp("Cannot accept new command socket %s",
155 strerror(errno));
156 return;
157 }
158
159 if (add_command_socket(as) != 0) {
160 fatalp("Cannot accept command. Too many connections\n");
161 close(as);
162 return;
163 }
164
165 /* auth */
166 send_pwd_prompt(as);
167 }
168
169 struct com_sock *
170 is_command_socket(int s)
171 {
172 int i;
173
174 if (s == -1)
175 return NULL;
176 for (i=0; i<MAX_COMMAND_SOCKETS; i++)
177 if (s == csockets[i].socket)
178 return &csockets[i];
179 return NULL;
180 }
181
182 int
183 add_command_socket(int s)
184 {
185 int i;
186
187 for (i=0; i<MAX_COMMAND_SOCKETS; i++)
188 if (csockets[i].socket == -1) {
189 csockets[i].socket = s;
190 csockets[i].auth = 0;
191 return 0;
192 }
193 return -1;
194 }
195
196 void
197 command_dispatch(struct com_sock *cs)
198 {
199 char recvspace[MAX_COMMAND_SIZE + 1];
200 char *nextc = recvspace;
201 int r = recv(cs->socket, recvspace, MAX_COMMAND_SIZE, MSG_PEEK);
202
203 if (r < 0) {
204 command_close(cs->socket);
205 return;
206 }
207
208 recv(cs->socket, recvspace, r, MSG_WAITALL);
209
210 if (r < 3) { /*at least \r\n */
211 if (cs->auth) {
212 /*writestr(cs->socket, "Unknown command. Use ? for help\n");*/
213 send_prompt(cs->socket);
214 } else {
215 writestr(cs->socket, "Bad password\n");
216 command_close(cs->socket);
217 }
218 return;
219 }
220
221 recvspace[r - 2] = '\0';
222
223 if (!cs->auth) {
224 if (verify_root_pwd(recvspace)) {
225 echo_on(cs->socket);
226 cs->auth = 1;
227 writestr(cs->socket, "\n");
228 send_prompt(cs->socket);
229 } else {
230 echo_on(cs->socket);
231 writestr(cs->socket, "Bad password\n");
232 command_close(cs->socket);
233 }
234 return;
235 }
236
237 strsep(&nextc, " ");
238
239 command_match(main_commands, cs->socket, recvspace, nextc);
240
241 }
242
243 void
244 command_close(int s)
245 {
246 int i;
247
248 for (i=0; i<MAX_COMMAND_SOCKETS; i++)
249 if (s == csockets[i].socket) {
250 close(s);
251 csockets[i].socket = -1;
252 csockets[i].auth = 0;
253 break;
254 }
255 }
256
257 void
258 send_prompt(int s) {
259 writestr(s, "LDP> ");
260 }
261
262 void
263 send_pwd_prompt(int s) {
264 echo_off(s);
265 writestr(s, "Password: ");
266 }
267
268 static void echo_off(int s)
269 {
270 char iac_will_echo[3] = { 0xff, 0xfb, 0x01 }, bf[32];
271 write(s, iac_will_echo, sizeof(iac_will_echo));
272 read(s, bf, sizeof(bf));
273 }
274
275 static void echo_on(int s)
276 {
277 char iac_wont_echo[3] = { 0xff, 0xfc, 0x01 }, bf[32];
278 write(s, iac_wont_echo, sizeof(iac_wont_echo));
279 read(s, bf, sizeof(bf));
280 }
281
282 /*
283 * Matching function
284 * Returns 1 if matched anything
285 */
286 int
287 command_match(struct com_func *cf, int s, char *orig, char *next)
288 {
289 size_t i, len;
290 int last_match = -1;
291 const char *msg = NULL;
292
293 if (orig == NULL || orig[0] == '\0')
294 goto out;
295
296 if (!strcmp(orig, "?")) {
297 for (i = 0; cf[i].func != NULL; i++) {
298 snprintf(sendspace, MAXSEND, "\t%s\n", cf[i].com);
299 writestr(s, sendspace);
300 }
301 goto out;
302 }
303
304 len = strlen(orig);
305 for (i = 0; cf[i].func != NULL; i++) {
306 if (strncasecmp(orig, cf[i].com, len) == 0) {
307 if (last_match != -1) {
308 msg = "Ambiguous";
309 goto out;
310 } else
311 last_match = i;
312 }
313 }
314
315 if (last_match == -1) {
316 msg = "Unknown";
317 goto out;
318 }
319
320 if (cf[last_match].func(s, next) != 0)
321 send_prompt(s);
322 return 1;
323 out:
324 if (msg) {
325 writestr(s, msg);
326 writestr(s, " command. Use ? for help\n");
327 }
328 send_prompt(s);
329 return 0;
330 }
331
332 /*
333 * Main CLI functions
334 */
335 int
336 set_func(int s, char *recvspace)
337 {
338 char *nextc = recvspace;
339
340 if (recvspace == NULL || recvspace[0] == '\0') {
341 writestr(s, "Unknown set command. Use set ? for help\n");
342 return 1;
343 }
344
345 strsep(&nextc, " ");
346
347 command_match(set_commands, s, recvspace, nextc);
348 return 0;
349 }
350
351 int
352 show_func(int s, char *recvspace)
353 {
354 char *nextc = recvspace;
355
356 if (recvspace == NULL || recvspace[0] == '\0') {
357 writestr(s, "Unknown show command. Use show ? for help\n");
358 return 1;
359 }
360
361 strsep(&nextc, " ");
362
363 command_match(show_commands, s, recvspace, nextc);
364 return 0;
365 }
366
367 int
368 exit_func(int s, char *recvspace)
369 {
370 command_close(s);
371 return 0;
372 }
373
374 /*
375 * Show functions
376 */
377 int
378 show_neighbours(int s, char *recvspace)
379 {
380 struct ldp_peer *p;
381 struct ldp_peer_address *wp;
382 struct sockaddr_in ssin;
383 socklen_t sin_len = sizeof(struct sockaddr_in);
384 int enc;
385 socklen_t enclen = sizeof(enc);
386
387 SLIST_FOREACH(p, &ldp_peer_head, peers) {
388 snprintf(sendspace, MAXSEND, "LDP peer: %s\n",
389 inet_ntoa(p->ldp_id));
390 writestr(s, sendspace);
391 snprintf(sendspace, MAXSEND, "Transport address: %s\n",
392 inet_ntoa(p->transport_address));
393 writestr(s, sendspace);
394 snprintf(sendspace, MAXSEND, "Next-hop address: %s\n",
395 inet_ntoa(p->address));
396 writestr(s, sendspace);
397 snprintf(sendspace, MAXSEND, "State: %s\n",
398 ldp_state_to_name(p->state));
399 writestr(s, sendspace);
400 if (p->state == LDP_PEER_ESTABLISHED) {
401 snprintf(sendspace, MAXSEND, "Since: %s",
402 ctime(&p->established_t));
403 writestr(s, sendspace);
404 }
405 snprintf(sendspace, MAXSEND, "Holdtime: %d\nTimeout: %d\n",
406 p->holdtime, p->timeout);
407 writestr(s, sendspace);
408
409 switch(p->state) {
410 case LDP_PEER_CONNECTING:
411 case LDP_PEER_CONNECTED:
412 case LDP_PEER_ESTABLISHED:
413 if (getsockname(p->socket,(struct sockaddr *) &ssin,
414 &sin_len))
415 break;
416
417 if (getsockopt(p->socket, IPPROTO_TCP, TCP_MD5SIG,
418 &enc, &enclen) == 0) {
419 snprintf(sendspace, MAXSEND,
420 "Authenticated: %s\n",
421 enc != 0 ? "YES" : "NO");
422 writestr(s, sendspace);
423 }
424
425 snprintf(sendspace, MAXSEND,"Socket: %d\nLocal %s:%d\n",
426 p->socket, inet_ntoa(ssin.sin_addr),
427 ntohs(ssin.sin_port));
428 writestr(s, sendspace);
429
430 if (getpeername(p->socket,(struct sockaddr *) &ssin,
431 &sin_len))
432 break;
433 snprintf(sendspace, MAXSEND, "Remote %s:%d\n",
434 inet_ntoa(ssin.sin_addr), ntohs(ssin.sin_port));
435 writestr(s, sendspace);
436 }
437
438 snprintf(sendspace, MAXSEND,"Addresses bounded to this peer: ");
439 writestr(s, sendspace);
440 SLIST_FOREACH(wp, &p->ldp_peer_address_head, addresses) {
441 snprintf(sendspace, MAXSEND, "%s ",
442 inet_ntoa(wp->address));
443 writestr(s, sendspace);
444 }
445 sendspace[0] = sendspace[1] = '\n';
446 write(s, sendspace, 2);
447 }
448 return 1;
449 }
450
451 int
452 show_bindings(int s, char *recvspace)
453 {
454 struct label *l;
455
456 snprintf(sendspace, MAXSEND, "Local label\tNetwork\t\t\t\tNexthop\n");
457 writestr(s, sendspace);
458 SLIST_FOREACH (l, &label_head, labels) {
459 snprintf(sendspace, MAXSEND, "%d\t\t%s/", l->binding,
460 union_ntoa(&l->so_dest));
461 writestr(s, sendspace);
462 snprintf(sendspace, MAXSEND, "%s", union_ntoa(&l->so_pref));
463 writestr(s, sendspace);
464 if (l->p)
465 snprintf(sendspace, MAXSEND, "\t%s:%d\n",
466 inet_ntoa(l->p->address), l->label);
467 else
468 snprintf(sendspace, MAXSEND, "\n");
469 writestr(s, sendspace);
470 }
471 return 1;
472 }
473
474 int
475 show_debug(int s, char *recvspace)
476 {
477 if (recvspace) {
478 writestr(s, "Invalid command\n");
479 return 1;
480 }
481
482 snprintf(sendspace, MAXSEND, "Debug: %s\n",
483 debug_f ? "YES" : "NO");
484 writestr(s, sendspace);
485 return 1;
486 }
487
488 int
489 show_hellos(int s, char *recvspace)
490 {
491 struct hello_info *hi;
492
493 SLIST_FOREACH(hi, &hello_info_head, infos) {
494 snprintf(sendspace, MAXSEND, "%s: %ds\n", inet_ntoa(hi->ldp_id),
495 hi->keepalive);
496 writestr(s, sendspace);
497 }
498 return 1;
499 }
500
501 int
502 show_parameters(int s, char *recvspace)
503 {
504 snprintf(sendspace, MAXSEND, "LDP ID: %s\nProtocol version: %d\n"
505 "Hello time: %d\nKeepalive time: %d\nHoldtime: %d\n"
506 "Minimum label: %d\nMaximum label: %d\n",
507 my_ldp_id,
508 LDP_VERSION,
509 ldp_hello_time,
510 ldp_keepalive_time,
511 ldp_holddown_time,
512 min_label,
513 max_label);
514 writestr(s, sendspace);
515 return 1;
516 }
517
518 int
519 show_version(int s, char *recvspace)
520 {
521 if (recvspace) { /* Nothing more after this */
522 writestr(s, "Invalid command\n");
523 return 1;
524 }
525
526 snprintf(sendspace, MAXSEND, "NetBSD LDP daemon version: %s\n",
527 LDPD_VER);
528 writestr(s, sendspace);
529 return 1;
530 }
531
532 int
533 show_warning(int s, char *recvspace)
534 {
535 if (recvspace) {
536 writestr(s, "Invalid command\n");
537 return 1;
538 }
539
540 snprintf(sendspace, MAXSEND, "Warnings: %s\n",
541 warn_f ? "YES" : "NO");
542 writestr(s, sendspace);
543 return 1;
544 }
545
546 /* Set commands */
547 int
548 set_hello_time(int s, char *recvspace)
549 {
550 if (!recvspace || atoi(recvspace) < 1) {
551 writestr(s, "Invalid timeout\n");
552 return 1;
553 }
554
555 ldp_hello_time = atoi(recvspace);
556 return 1;
557 }
558
559 int
560 set_debug(int s, char *recvspace)
561 {
562 if (!recvspace || atoi(recvspace) < 0) {
563 writestr(s, "Invalid command\n");
564 return 1;
565 }
566
567 debug_f = atoi(recvspace);
568 return 1;
569 }
570
571 int
572 set_warning(int s, char *recvspace)
573 {
574 if (!recvspace || atoi(recvspace) < 0) {
575 writestr(s, "Invalid command\n");
576 return 1;
577 }
578
579 warn_f = atoi(recvspace);
580 return 1;
581 }
582