1 /* $NetBSD: ldp_command.c,v 1.10 2013/01/28 21:08:14 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_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 union_ntoa(&l->so_dest)); 517 writestr(s, sendspace); 518 snprintf(sendspace, MAXSEND, "%s", union_ntoa(&l->so_pref)); 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