ldp_command.c revision 1.6 1 /* $NetBSD: ldp_command.c,v 1.6 2011/12/24 23:51:27 christos 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 void send_prompt(int);
64 static void send_pwd_prompt(int);
65 static int command_match(struct com_func*, int, char*, char*);
66
67 static int verify_root_pwd(char *);
68 static void echo_on(int s);
69 static void echo_off(int s);
70
71 /* Main functions */
72 static int show_func(int, char *);
73 static int set_func(int, char *);
74 static int exit_func(int, char *);
75
76 /* Show functions */
77 static int show_neighbours(int, char *);
78 static int show_bindings(int, char *);
79 static int show_debug(int, char *);
80 static int show_hellos(int, char *);
81 static int show_labels(int, char *);
82 static int show_parameters(int, char *);
83 static int show_version(int, char *);
84 static int show_warning(int, char *);
85
86 /* Set functions */
87 static int set_hello_time(int, char *);
88 static int set_debug(int, char *);
89 static int set_warning(int, char *);
90
91 static struct com_func main_commands[] = {
92 { "show", show_func },
93 { "set", set_func },
94 { "quit", exit_func },
95 { "exit", exit_func },
96 { "", NULL }
97 };
98
99 static struct com_func show_commands[] = {
100 { "neighbours", show_neighbours },
101 { "bindings", show_bindings },
102 { "debug", show_debug },
103 { "hellos", show_hellos },
104 { "labels", show_labels },
105 { "parameters", show_parameters },
106 { "version", show_version },
107 { "warning", show_warning },
108 { "", NULL }
109 };
110
111 struct com_func set_commands[] = {
112 { "debug", set_debug },
113 { "hello-time", set_hello_time },
114 { "warning", set_warning },
115 { "", NULL }
116 };
117
118 static int
119 verify_root_pwd(char *pw)
120 {
121 struct passwd *p;
122
123 if ((p = getpwuid(0)) == NULL)
124 return 0;
125
126 if (strcmp(crypt(pw, p->pw_passwd), p->pw_passwd))
127 return 0;
128
129 return 1;
130 }
131
132
133 void
134 init_command_sockets()
135 {
136 int i;
137
138 for (i = 0; i<MAX_COMMAND_SOCKETS; i++) {
139 csockets[i].socket = -1;
140 csockets[i].auth = 0;
141 }
142 }
143
144 int
145 create_command_socket(int port)
146 {
147 struct sockaddr_in sin;
148 int s;
149
150 sin.sin_len = sizeof(sin);
151 sin.sin_family = AF_INET;
152 sin.sin_port = htons(port);
153 sin.sin_addr.s_addr = ntohl(INADDR_LOOPBACK);
154
155 s = socket(PF_INET, SOCK_STREAM, 6);
156 if (s < 0)
157 return s;
158
159 if (bind(s, (struct sockaddr *) &sin, sizeof(sin))) {
160 fatalp("bind: %s", strerror(errno));
161 close(s);
162 return -1;
163 }
164
165 if (listen(s, 5) == -1) {
166 fatalp("listen: %s", strerror(errno));
167 close(s);
168 return -1;
169 }
170 return s;
171 }
172
173 void
174 command_accept(int s)
175 {
176 int as = accept(s, NULL, 0);
177
178 if (as < 0) {
179 fatalp("Cannot accept new command socket %s",
180 strerror(errno));
181 return;
182 }
183
184 if (add_command_socket(as) != 0) {
185 fatalp("Cannot accept command. Too many connections\n");
186 close(as);
187 return;
188 }
189
190 /* auth */
191 send_pwd_prompt(as);
192 }
193
194 struct com_sock *
195 is_command_socket(int s)
196 {
197 int i;
198
199 if (s == -1)
200 return NULL;
201 for (i=0; i<MAX_COMMAND_SOCKETS; i++)
202 if (s == csockets[i].socket)
203 return &csockets[i];
204 return NULL;
205 }
206
207 int
208 add_command_socket(int s)
209 {
210 int i;
211
212 for (i=0; i<MAX_COMMAND_SOCKETS; i++)
213 if (csockets[i].socket == -1) {
214 csockets[i].socket = s;
215 csockets[i].auth = 0;
216 return 0;
217 }
218 return -1;
219 }
220
221 void
222 command_dispatch(struct com_sock *cs)
223 {
224 char recvspace[MAX_COMMAND_SIZE + 1];
225 char *nextc = recvspace;
226 int r = recv(cs->socket, recvspace, MAX_COMMAND_SIZE, MSG_PEEK);
227
228 if (r < 0) {
229 command_close(cs->socket);
230 return;
231 }
232
233 recv(cs->socket, recvspace, r, MSG_WAITALL);
234
235 if (r < 3) { /*at least \r\n */
236 if (cs->auth) {
237 /*writestr(cs->socket, "Unknown command. Use ? for help\n");*/
238 send_prompt(cs->socket);
239 } else {
240 writestr(cs->socket, "Bad password\n");
241 command_close(cs->socket);
242 }
243 return;
244 }
245
246 recvspace[r - 2] = '\0';
247
248 if (!cs->auth) {
249 if (verify_root_pwd(recvspace)) {
250 echo_on(cs->socket);
251 cs->auth = 1;
252 writestr(cs->socket, "\n");
253 send_prompt(cs->socket);
254 } else {
255 echo_on(cs->socket);
256 writestr(cs->socket, "Bad password\n");
257 command_close(cs->socket);
258 }
259 return;
260 }
261
262 strsep(&nextc, " ");
263
264 command_match(main_commands, cs->socket, recvspace, nextc);
265
266 }
267
268 void
269 command_close(int s)
270 {
271 int i;
272
273 for (i=0; i<MAX_COMMAND_SOCKETS; i++)
274 if (s == csockets[i].socket) {
275 close(s);
276 csockets[i].socket = -1;
277 csockets[i].auth = 0;
278 break;
279 }
280 }
281
282 static void
283 send_prompt(int s) {
284 writestr(s, "LDP> ");
285 }
286
287 static void
288 send_pwd_prompt(int s) {
289 echo_off(s);
290 writestr(s, "Password: ");
291 }
292
293 static void
294 echo_off(int s)
295 {
296 char iac_will_echo[3] = { 0xff, 0xfb, 0x01 }, bf[32];
297 write(s, iac_will_echo, sizeof(iac_will_echo));
298 read(s, bf, sizeof(bf));
299 }
300
301 static void
302 echo_on(int s)
303 {
304 char iac_wont_echo[3] = { 0xff, 0xfc, 0x01 }, bf[32];
305 write(s, iac_wont_echo, sizeof(iac_wont_echo));
306 read(s, bf, sizeof(bf));
307 }
308
309 /*
310 * Matching function
311 * Returns 1 if matched anything
312 */
313 static int
314 command_match(struct com_func *cf, int s, char *orig, char *next)
315 {
316 size_t i, len;
317 int last_match = -1;
318 const char *msg = NULL;
319
320 if (orig == NULL || orig[0] == '\0')
321 goto out;
322
323 if (!strcmp(orig, "?")) {
324 for (i = 0; cf[i].func != NULL; i++) {
325 snprintf(sendspace, MAXSEND, "\t%s\n", cf[i].com);
326 writestr(s, sendspace);
327 }
328 goto out;
329 }
330
331 len = strlen(orig);
332 for (i = 0; cf[i].func != NULL; i++) {
333 if (strncasecmp(orig, cf[i].com, len) == 0) {
334 if (last_match != -1) {
335 msg = "Ambiguous";
336 goto out;
337 } else
338 last_match = i;
339 }
340 }
341
342 if (last_match == -1) {
343 msg = "Unknown";
344 goto out;
345 }
346
347 if (cf[last_match].func(s, next) != 0)
348 send_prompt(s);
349 return 1;
350 out:
351 if (msg) {
352 writestr(s, msg);
353 writestr(s, " command. Use ? for help\n");
354 }
355 send_prompt(s);
356 return 0;
357 }
358
359 /*
360 * Main CLI functions
361 */
362 static int
363 set_func(int s, char *recvspace)
364 {
365 char *nextc = recvspace;
366
367 if (recvspace == NULL || recvspace[0] == '\0') {
368 writestr(s, "Unknown set command. Use set ? for help\n");
369 return 1;
370 }
371
372 strsep(&nextc, " ");
373
374 command_match(set_commands, s, recvspace, nextc);
375 return 0;
376 }
377
378 static int
379 show_func(int s, char *recvspace)
380 {
381 char *nextc = recvspace;
382
383 if (recvspace == NULL || recvspace[0] == '\0') {
384 writestr(s, "Unknown show command. Use show ? for help\n");
385 return 1;
386 }
387
388 strsep(&nextc, " ");
389
390 command_match(show_commands, s, recvspace, nextc);
391 return 0;
392 }
393
394 static int
395 exit_func(int s, char *recvspace)
396 {
397 command_close(s);
398 return 0;
399 }
400
401 /*
402 * Show functions
403 */
404 static int
405 show_neighbours(int s, char *recvspace)
406 {
407 struct ldp_peer *p;
408 struct ldp_peer_address *wp;
409 struct sockaddr_in ssin;
410 socklen_t sin_len = sizeof(struct sockaddr_in);
411 int enc;
412 socklen_t enclen = sizeof(enc);
413
414 SLIST_FOREACH(p, &ldp_peer_head, peers) {
415 snprintf(sendspace, MAXSEND, "LDP peer: %s\n",
416 inet_ntoa(p->ldp_id));
417 writestr(s, sendspace);
418 snprintf(sendspace, MAXSEND, "Transport address: %s\n",
419 inet_ntoa(p->transport_address));
420 writestr(s, sendspace);
421 snprintf(sendspace, MAXSEND, "Next-hop address: %s\n",
422 inet_ntoa(p->address));
423 writestr(s, sendspace);
424 snprintf(sendspace, MAXSEND, "State: %s\n",
425 ldp_state_to_name(p->state));
426 writestr(s, sendspace);
427 if (p->state == LDP_PEER_ESTABLISHED) {
428 snprintf(sendspace, MAXSEND, "Since: %s",
429 ctime(&p->established_t));
430 writestr(s, sendspace);
431 }
432 snprintf(sendspace, MAXSEND, "Holdtime: %d\nTimeout: %d\n",
433 p->holdtime, p->timeout);
434 writestr(s, sendspace);
435
436 switch(p->state) {
437 case LDP_PEER_CONNECTING:
438 case LDP_PEER_CONNECTED:
439 case LDP_PEER_ESTABLISHED:
440 if (getsockname(p->socket,(struct sockaddr *) &ssin,
441 &sin_len))
442 break;
443
444 if (getsockopt(p->socket, IPPROTO_TCP, TCP_MD5SIG,
445 &enc, &enclen) == 0) {
446 snprintf(sendspace, MAXSEND,
447 "Authenticated: %s\n",
448 enc != 0 ? "YES" : "NO");
449 writestr(s, sendspace);
450 }
451
452 snprintf(sendspace, MAXSEND,"Socket: %d\nLocal %s:%d\n",
453 p->socket, inet_ntoa(ssin.sin_addr),
454 ntohs(ssin.sin_port));
455 writestr(s, sendspace);
456
457 if (getpeername(p->socket,(struct sockaddr *) &ssin,
458 &sin_len))
459 break;
460 snprintf(sendspace, MAXSEND, "Remote %s:%d\n",
461 inet_ntoa(ssin.sin_addr), ntohs(ssin.sin_port));
462 writestr(s, sendspace);
463 }
464
465 snprintf(sendspace, MAXSEND,"Addresses bounded to this peer: ");
466 writestr(s, sendspace);
467 SLIST_FOREACH(wp, &p->ldp_peer_address_head, addresses) {
468 snprintf(sendspace, MAXSEND, "%s ",
469 inet_ntoa(wp->address));
470 writestr(s, sendspace);
471 }
472 sendspace[0] = sendspace[1] = '\n';
473 write(s, sendspace, 2);
474 }
475 return 1;
476 }
477
478 /* Shows labels grabbed from unsolicited label maps */
479 static int
480 show_labels(int s, char *recvspace)
481 {
482 struct ldp_peer *p;
483 struct label_mapping *lm;
484
485 SLIST_FOREACH(p, &ldp_peer_head, peers) {
486 if (p->state != LDP_PEER_ESTABLISHED)
487 continue;
488 SLIST_FOREACH(lm, &p->label_mapping_head, mappings) {
489 snprintf(sendspace, MAXSEND, "%s:%d\t%s/%d\n",
490 inet_ntoa(p->ldp_id), lm->label,
491 inet_ntoa(lm->address), lm->prefix);
492 writestr(s, sendspace);
493 }
494 }
495 return 1;
496 }
497
498 static int
499 show_bindings(int s, char *recvspace)
500 {
501 struct label *l;
502
503 snprintf(sendspace, MAXSEND, "Local label\tNetwork\t\t\t\tNexthop\n");
504 writestr(s, sendspace);
505 SLIST_FOREACH (l, &label_head, labels) {
506 snprintf(sendspace, MAXSEND, "%d\t\t%s/", l->binding,
507 union_ntoa(&l->so_dest));
508 writestr(s, sendspace);
509 snprintf(sendspace, MAXSEND, "%s", union_ntoa(&l->so_pref));
510 writestr(s, sendspace);
511 if (l->p)
512 snprintf(sendspace, MAXSEND, "\t%s:%d\n",
513 inet_ntoa(l->p->address), l->label);
514 else
515 snprintf(sendspace, MAXSEND, "\n");
516 writestr(s, sendspace);
517 }
518 return 1;
519 }
520
521 static int
522 show_debug(int s, char *recvspace)
523 {
524 if (recvspace) {
525 writestr(s, "Invalid command\n");
526 return 1;
527 }
528
529 snprintf(sendspace, MAXSEND, "Debug: %s\n",
530 debug_f ? "YES" : "NO");
531 writestr(s, sendspace);
532 return 1;
533 }
534
535 static int
536 show_hellos(int s, char *recvspace)
537 {
538 struct hello_info *hi;
539
540 SLIST_FOREACH(hi, &hello_info_head, infos) {
541 snprintf(sendspace, MAXSEND, "%s: %ds\n", inet_ntoa(hi->ldp_id),
542 hi->keepalive);
543 writestr(s, sendspace);
544 }
545 return 1;
546 }
547
548 static int
549 show_parameters(int s, char *recvspace)
550 {
551 snprintf(sendspace, MAXSEND, "LDP ID: %s\nProtocol version: %d\n"
552 "Hello time: %d\nKeepalive time: %d\nHoldtime: %d\n"
553 "Minimum label: %d\nMaximum label: %d\n",
554 my_ldp_id,
555 LDP_VERSION,
556 ldp_hello_time,
557 ldp_keepalive_time,
558 ldp_holddown_time,
559 min_label,
560 max_label);
561 writestr(s, sendspace);
562 return 1;
563 }
564
565 static int
566 show_version(int s, char *recvspace)
567 {
568 if (recvspace) { /* Nothing more after this */
569 writestr(s, "Invalid command\n");
570 return 1;
571 }
572
573 snprintf(sendspace, MAXSEND, "NetBSD LDP daemon version: %s\n",
574 LDPD_VER);
575 writestr(s, sendspace);
576 return 1;
577 }
578
579 static int
580 show_warning(int s, char *recvspace)
581 {
582 if (recvspace) {
583 writestr(s, "Invalid command\n");
584 return 1;
585 }
586
587 snprintf(sendspace, MAXSEND, "Warnings: %s\n",
588 warn_f ? "YES" : "NO");
589 writestr(s, sendspace);
590 return 1;
591 }
592
593 /* Set commands */
594 static int
595 set_hello_time(int s, char *recvspace)
596 {
597 if (!recvspace || atoi(recvspace) < 1) {
598 writestr(s, "Invalid timeout\n");
599 return 1;
600 }
601
602 ldp_hello_time = atoi(recvspace);
603 return 1;
604 }
605
606 static int
607 set_debug(int s, char *recvspace)
608 {
609 if (!recvspace || atoi(recvspace) < 0) {
610 writestr(s, "Invalid command\n");
611 return 1;
612 }
613
614 debug_f = atoi(recvspace);
615 return 1;
616 }
617
618 static int
619 set_warning(int s, char *recvspace)
620 {
621 if (!recvspace || atoi(recvspace) < 0) {
622 writestr(s, "Invalid command\n");
623 return 1;
624 }
625
626 warn_f = atoi(recvspace);
627 return 1;
628 }
629