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