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