ldp_command.c revision 1.9 1 /* $NetBSD: ldp_command.c,v 1.9 2013/01/28 20:06:52 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 char traddress[39], nhaddress[39];
415
416 SLIST_FOREACH(p, &ldp_peer_head, peers) {
417 snprintf(sendspace, MAXSEND, "LDP peer: %s\n",
418 inet_ntoa(p->ldp_id));
419 writestr(s, sendspace);
420 inet_ntop(p->transport_address->sa_family,
421 p->transport_address->sa_data, traddress, 39);
422 snprintf(sendspace, MAXSEND, "Transport address: %s\n",
423 traddress);
424 writestr(s, sendspace);
425 inet_ntop(p->address->sa_family, p->address->sa_data,
426 nhaddress, 39);
427 snprintf(sendspace, MAXSEND, "Next-hop address: %s\n",
428 nhaddress);
429 writestr(s, sendspace);
430 snprintf(sendspace, MAXSEND, "State: %s\n",
431 ldp_state_to_name(p->state));
432 writestr(s, sendspace);
433 if (p->state == LDP_PEER_ESTABLISHED) {
434 snprintf(sendspace, MAXSEND, "Since: %s",
435 ctime(&p->established_t));
436 writestr(s, sendspace);
437 }
438 snprintf(sendspace, MAXSEND, "Holdtime: %d\nTimeout: %d\n",
439 p->holdtime, p->timeout);
440 writestr(s, sendspace);
441
442 switch(p->state) {
443 case LDP_PEER_CONNECTING:
444 case LDP_PEER_CONNECTED:
445 case LDP_PEER_ESTABLISHED:
446 if (getsockname(p->socket,(struct sockaddr *) &ssin,
447 &sin_len))
448 break;
449
450 if (getsockopt(p->socket, IPPROTO_TCP, TCP_MD5SIG,
451 &enc, &enclen) == 0) {
452 snprintf(sendspace, MAXSEND,
453 "Authenticated: %s\n",
454 enc != 0 ? "YES" : "NO");
455 writestr(s, sendspace);
456 }
457
458 snprintf(sendspace, MAXSEND,"Socket: %d\nLocal %s:%d\n",
459 p->socket, inet_ntoa(ssin.sin_addr),
460 ntohs(ssin.sin_port));
461 writestr(s, sendspace);
462
463 if (getpeername(p->socket,(struct sockaddr *) &ssin,
464 &sin_len))
465 break;
466 snprintf(sendspace, MAXSEND, "Remote %s:%d\n",
467 inet_ntoa(ssin.sin_addr), ntohs(ssin.sin_port));
468 writestr(s, sendspace);
469 }
470
471 snprintf(sendspace, MAXSEND,"Addresses bounded to this peer: ");
472 writestr(s, sendspace);
473 SLIST_FOREACH(wp, &p->ldp_peer_address_head, addresses) {
474 /* XXX: TODO */
475 if (wp->address.sa.sa_family != AF_INET)
476 continue;
477 snprintf(sendspace, MAXSEND, "%s ",
478 inet_ntoa(wp->address.sin.sin_addr));
479 writestr(s, sendspace);
480 }
481 sendspace[0] = sendspace[1] = '\n';
482 write(s, sendspace, 2);
483 }
484 return 1;
485 }
486
487 /* Shows labels grabbed from unsolicited label maps */
488 static int
489 show_labels(int s, char *recvspace)
490 {
491 struct ldp_peer *p;
492 struct label_mapping *lm;
493
494 SLIST_FOREACH(p, &ldp_peer_head, peers) {
495 if (p->state != LDP_PEER_ESTABLISHED)
496 continue;
497 SLIST_FOREACH(lm, &p->label_mapping_head, mappings) {
498 char lma[256];
499 /* XXX: TODO */
500 if (lm->address.sa.sa_family != AF_INET)
501 continue;
502 strlcpy(lma, inet_ntoa(lm->address.sin.sin_addr),
503 sizeof(lma));
504 snprintf(sendspace, MAXSEND, "%s:%d\t%s/%d\n",
505 inet_ntoa(p->ldp_id), lm->label, lma, lm->prefix);
506 writestr(s, sendspace);
507 }
508 }
509 return 1;
510 }
511
512 static int
513 show_bindings(int s, char *recvspace)
514 {
515 struct label *l;
516 char labelgw[39];
517
518 snprintf(sendspace, MAXSEND, "Local label\tNetwork\t\t\t\tNexthop\n");
519 writestr(s, sendspace);
520 SLIST_FOREACH (l, &label_head, labels) {
521 snprintf(sendspace, MAXSEND, "%d\t\t%s/", l->binding,
522 union_ntoa(&l->so_dest));
523 writestr(s, sendspace);
524 snprintf(sendspace, MAXSEND, "%s", union_ntoa(&l->so_pref));
525 writestr(s, sendspace);
526 if (l->p) {
527 inet_ntop(l->p->address->sa_family,
528 l->p->address->sa_data, labelgw, 39);
529 snprintf(sendspace, MAXSEND, "\t%s:%d\n",
530 labelgw, l->label);
531 } else
532 snprintf(sendspace, MAXSEND, "\n");
533 writestr(s, sendspace);
534 }
535 return 1;
536 }
537
538 static int
539 show_debug(int s, char *recvspace)
540 {
541 if (recvspace) {
542 writestr(s, "Invalid command\n");
543 return 1;
544 }
545
546 snprintf(sendspace, MAXSEND, "Debug: %s\n",
547 debug_f ? "YES" : "NO");
548 writestr(s, sendspace);
549 return 1;
550 }
551
552 static int
553 show_hellos(int s, char *recvspace)
554 {
555 struct hello_info *hi;
556
557 SLIST_FOREACH(hi, &hello_info_head, infos) {
558 snprintf(sendspace, MAXSEND,
559 "ID: %s\nKeepalive: %ds\nTransport address: %s\n\n",
560 inet_ntoa(hi->ldp_id),
561 hi->keepalive,
562 hi->transport_address.sa.sa_family != 0 ?
563 satos(&hi->transport_address.sa) : "None");
564 writestr(s, sendspace);
565 }
566 return 1;
567 }
568
569 static int
570 show_parameters(int s, char *recvspace)
571 {
572 snprintf(sendspace, MAXSEND, "LDP ID: %s\nProtocol version: %d\n"
573 "Hello time: %d\nKeepalive time: %d\nHoldtime: %d\n"
574 "Minimum label: %d\nMaximum label: %d\n",
575 my_ldp_id,
576 LDP_VERSION,
577 ldp_hello_time,
578 ldp_keepalive_time,
579 ldp_holddown_time,
580 min_label,
581 max_label);
582 writestr(s, sendspace);
583 return 1;
584 }
585
586 static int
587 show_version(int s, char *recvspace)
588 {
589 if (recvspace) { /* Nothing more after this */
590 writestr(s, "Invalid command\n");
591 return 1;
592 }
593
594 snprintf(sendspace, MAXSEND, "NetBSD LDP daemon version: %s\n",
595 LDPD_VER);
596 writestr(s, sendspace);
597 return 1;
598 }
599
600 static int
601 show_warning(int s, char *recvspace)
602 {
603 if (recvspace) {
604 writestr(s, "Invalid command\n");
605 return 1;
606 }
607
608 snprintf(sendspace, MAXSEND, "Warnings: %s\n",
609 warn_f ? "YES" : "NO");
610 writestr(s, sendspace);
611 return 1;
612 }
613
614 /* Set commands */
615 static int
616 set_hello_time(int s, char *recvspace)
617 {
618 if (!recvspace || atoi(recvspace) < 1) {
619 writestr(s, "Invalid timeout\n");
620 return 1;
621 }
622
623 ldp_hello_time = atoi(recvspace);
624 return 1;
625 }
626
627 static int
628 set_debug(int s, char *recvspace)
629 {
630 if (!recvspace || atoi(recvspace) < 0) {
631 writestr(s, "Invalid command\n");
632 return 1;
633 }
634
635 debug_f = atoi(recvspace);
636 return 1;
637 }
638
639 static int
640 set_warning(int s, char *recvspace)
641 {
642 if (!recvspace || atoi(recvspace) < 0) {
643 writestr(s, "Invalid command\n");
644 return 1;
645 }
646
647 warn_f = atoi(recvspace);
648 return 1;
649 }
650