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