1 /* $NetBSD: ldp_command.c,v 1.7 2011/12/24 23:54:26 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@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 char lma[256]; 490 strlcpy(lma, inet_ntoa(lm->address), sizeof(lma)); 491 snprintf(sendspace, MAXSEND, "%s:%d\t%s/%d\n", 492 inet_ntoa(p->ldp_id), lm->label, lma, lm->prefix); 493 writestr(s, sendspace); 494 } 495 } 496 return 1; 497 } 498 499 static int 500 show_bindings(int s, char *recvspace) 501 { 502 struct label *l; 503 504 snprintf(sendspace, MAXSEND, "Local label\tNetwork\t\t\t\tNexthop\n"); 505 writestr(s, sendspace); 506 SLIST_FOREACH (l, &label_head, labels) { 507 snprintf(sendspace, MAXSEND, "%d\t\t%s/", l->binding, 508 union_ntoa(&l->so_dest)); 509 writestr(s, sendspace); 510 snprintf(sendspace, MAXSEND, "%s", union_ntoa(&l->so_pref)); 511 writestr(s, sendspace); 512 if (l->p) 513 snprintf(sendspace, MAXSEND, "\t%s:%d\n", 514 inet_ntoa(l->p->address), l->label); 515 else 516 snprintf(sendspace, MAXSEND, "\n"); 517 writestr(s, sendspace); 518 } 519 return 1; 520 } 521 522 static int 523 show_debug(int s, char *recvspace) 524 { 525 if (recvspace) { 526 writestr(s, "Invalid command\n"); 527 return 1; 528 } 529 530 snprintf(sendspace, MAXSEND, "Debug: %s\n", 531 debug_f ? "YES" : "NO"); 532 writestr(s, sendspace); 533 return 1; 534 } 535 536 static int 537 show_hellos(int s, char *recvspace) 538 { 539 struct hello_info *hi; 540 541 SLIST_FOREACH(hi, &hello_info_head, infos) { 542 snprintf(sendspace, MAXSEND, "%s: %ds\n", inet_ntoa(hi->ldp_id), 543 hi->keepalive); 544 writestr(s, sendspace); 545 } 546 return 1; 547 } 548 549 static int 550 show_parameters(int s, char *recvspace) 551 { 552 snprintf(sendspace, MAXSEND, "LDP ID: %s\nProtocol version: %d\n" 553 "Hello time: %d\nKeepalive time: %d\nHoldtime: %d\n" 554 "Minimum label: %d\nMaximum label: %d\n", 555 my_ldp_id, 556 LDP_VERSION, 557 ldp_hello_time, 558 ldp_keepalive_time, 559 ldp_holddown_time, 560 min_label, 561 max_label); 562 writestr(s, sendspace); 563 return 1; 564 } 565 566 static int 567 show_version(int s, char *recvspace) 568 { 569 if (recvspace) { /* Nothing more after this */ 570 writestr(s, "Invalid command\n"); 571 return 1; 572 } 573 574 snprintf(sendspace, MAXSEND, "NetBSD LDP daemon version: %s\n", 575 LDPD_VER); 576 writestr(s, sendspace); 577 return 1; 578 } 579 580 static int 581 show_warning(int s, char *recvspace) 582 { 583 if (recvspace) { 584 writestr(s, "Invalid command\n"); 585 return 1; 586 } 587 588 snprintf(sendspace, MAXSEND, "Warnings: %s\n", 589 warn_f ? "YES" : "NO"); 590 writestr(s, sendspace); 591 return 1; 592 } 593 594 /* Set commands */ 595 static int 596 set_hello_time(int s, char *recvspace) 597 { 598 if (!recvspace || atoi(recvspace) < 1) { 599 writestr(s, "Invalid timeout\n"); 600 return 1; 601 } 602 603 ldp_hello_time = atoi(recvspace); 604 return 1; 605 } 606 607 static int 608 set_debug(int s, char *recvspace) 609 { 610 if (!recvspace || atoi(recvspace) < 0) { 611 writestr(s, "Invalid command\n"); 612 return 1; 613 } 614 615 debug_f = atoi(recvspace); 616 return 1; 617 } 618 619 static int 620 set_warning(int s, char *recvspace) 621 { 622 if (!recvspace || atoi(recvspace) < 0) { 623 writestr(s, "Invalid command\n"); 624 return 1; 625 } 626 627 warn_f = atoi(recvspace); 628 return 1; 629 } 630