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