1 /* $KAME: quip_client.c,v 1.2 2000/10/18 09:15:17 kjc Exp $ */ 2 /* 3 * Copyright (C) 1999-2000 4 * Sony Computer Science Laboratories, Inc. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/param.h> 29 #include <sys/socket.h> 30 #include <sys/un.h> 31 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <unistd.h> 35 #include <string.h> 36 #include <signal.h> 37 #include <errno.h> 38 #include <err.h> 39 40 #include "quip_client.h" 41 #include "altqstat.h" 42 43 /* 44 * quip (queue information protocol) is a http-like protocol 45 * in order to retrieve information from the server. 46 * a unix domain TCP socket "/var/run/altq_quip" is used for 47 * clinet-server style communication. 48 * 49 * there are 2 quip message types: request and response. 50 * request format: (only single-line request message is used at this moment) 51 * request-line 52 * 53 * request-line = <method> <operation>[?<query>] <quip-version> 54 * <method> = GET (only GET is defined at this moment) 55 * <operation> = list | handle-to-name | qdisc | filter 56 * query format is operation dependent but most query takes 57 * <interface> or <class> or <filter>. 58 * <interface> = <if_name> 59 * <class> = <if_name>:<class_path>/<class_name> 60 * <filter> = <if_name>:<class_path>/<class_name>:<filter_name> 61 * "list" operation accepts "*" as a wildcard. 62 * 63 * response format: 64 * status-line 65 * response-headers (0 or more) 66 * <blank line> 67 * body 68 * 69 * status-line = <quip-version> <status-code> <reason phrase> 70 * response-header = Content-Length:<value> 71 * 72 * "Content-Length" specifies the length of the message body. 73 * 74 * example: 75 * to retrieve a list of classes (handle and name) on interface "fxp0": 76 * a request message looks like, 77 * GET list?fxp0:* QUIP/1.0<cr> 78 * a response message looks like, 79 * QUIP/1.0 200 OK<cr> 80 * Content-Length:86<cr> 81 * <cr> 82 * 0000000000 fxp0:/root<cr> 83 * 0xc0d1be00 fxp0:/root/parent<cr> 84 * 0xc0d1ba00 fxp0:/root/parent/child<cr> 85 * 86 * other examples: 87 * list all interfaces, classes, and filters: 88 * GET list QUIP/1.0<cr> 89 * list all interfaces: 90 * GET list?* QUIP/1.0<cr> 91 * list all classes: 92 * GET list?*:* QUIP/1.0<cr> 93 * list all filters: 94 * GET list?*:*:* QUIP/1.0<cr> 95 * convert class handle to class name: 96 * GET handle-to-name?fxp0:0xc0d1be00 QUIP/1.0<cr> 97 * convert filter handle to filter name: 98 * GET handle-to-name?fxp0::0x1000000a QUIP/1.0<cr> 99 */ 100 101 enum nametype { INTERFACE, CLASS, FILTER, CONDITIONER }; 102 103 static FILE *server = NULL; 104 int quip_echo = 0; 105 106 static char *extract_ifname(const char *name); 107 108 int 109 quip_openserver(void) 110 { 111 struct sockaddr_un addr; 112 int fd; 113 114 if ((fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) 115 err(1, "can't open socket"); 116 117 bzero(&addr, sizeof(addr)); 118 addr.sun_family = AF_LOCAL; 119 strcpy(addr.sun_path, QUIP_PATH); 120 121 if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { 122 fprintf(stderr, "can't talk to altqd!\n" 123 "probably, altqd is not running\n"); 124 return (-1); 125 } 126 127 if ((server = fdopen(fd, "r+")) == NULL) { 128 warn("fdopen: can't open stream to the quip server"); 129 return (-1); 130 } 131 return (0); 132 } 133 134 int 135 quip_closeserver(void) 136 { 137 if (server != NULL) 138 return fclose(server); 139 return (0); 140 } 141 142 void 143 quip_sendrequest(FILE *fp, const char *request) 144 { 145 char buf[1024], *cp; 146 int n; 147 148 if ((cp = strstr(request, "QUIP")) == NULL) { 149 cp = strchr(request, '\n'); 150 n = cp - request; 151 strncpy(buf, request, n); 152 n += sprintf(buf + n, " QUIP/1.0"); 153 strcpy(buf + n, cp); 154 } 155 else 156 strcpy(buf, request); 157 158 if (fputs(buf, fp) != 0) 159 err(1, "fputs"); 160 if (fflush(fp) != 0) 161 err(1, "fflush"); 162 if (quip_echo) { 163 fputs("<< ", stdout); 164 fputs(buf, stdout); 165 } 166 } 167 168 /* 169 * recv_response receives a response message from the server 170 * and returns status_code. 171 */ 172 int 173 quip_recvresponse(FILE *fp, char *header, char *body, int *blen) 174 { 175 char buf[1024], version[64]; 176 int code, resid; 177 int end_of_header = 0; 178 179 if (blen != NULL) 180 *blen = 0; 181 code = 0; 182 resid = 0; 183 while (fgets(buf, 1024, fp) != 0) { 184 if (quip_echo) { 185 fputs("> ", stdout); 186 fputs(buf, stdout); 187 } 188 189 if (!end_of_header) { 190 /* process message header */ 191 if (header != NULL) 192 header += sprintf(header, "%s", buf); 193 194 if (code == 0) { 195 /* status line expected */ 196 if (buf[0] == '\n') { 197 /* ignore blank lines */ 198 } 199 else if (sscanf(buf, "%s %d", 200 version, &code) != 2) { 201 /* can't get result code */ 202 fpurge(fp); 203 return (-1); 204 } 205 } 206 else { 207 /* entity header expected */ 208 char *field, *cp; 209 210 if (buf[0] == '\n') { 211 /* end of header */ 212 end_of_header = 1; 213 if (resid == 0) 214 /* no message body */ 215 return (code); 216 } 217 218 cp = buf; 219 field = strsep(&cp, ":"); 220 if (strcmp(field, "Content-Length") == 0) { 221 sscanf(cp, "%d", &resid); 222 if (blen != NULL) 223 *blen = resid; 224 } 225 } 226 } 227 else { 228 /* process message body */ 229 int len; 230 231 if (body != NULL) { 232 len = sprintf(body, "%s", buf); 233 body += len; 234 } 235 else 236 len = strlen(buf); 237 resid -= len; 238 if (resid <= 0) 239 return (code); 240 } 241 } 242 return (-1); 243 } 244 245 void 246 quip_rawmode(void) 247 { 248 char line[1024]; 249 int result_code; 250 251 printf(">>>Entering the raw interactive mode to the server:\n\n"); 252 if (server == NULL) { 253 printf("No server available!\n"); 254 return; 255 } 256 257 while (1) { 258 printf("%% "); fflush(stdout); 259 /* read a line from stdin */ 260 if (fgets(line, 1024, stdin) == NULL) 261 break; 262 263 if (line[0] == '\n') { 264 /* if a blank line, echo locally */ 265 fputs(line, stdout); 266 continue; 267 } 268 if (line[0] == 'q') { 269 printf("Exit\n"); 270 break; 271 } 272 273 /* send the input line to the server */ 274 quip_sendrequest(server, line); 275 276 /* get a response message from the server */ 277 result_code = quip_recvresponse(server, NULL, NULL, NULL); 278 } 279 } 280 281 char * 282 quip_selectinterface(char *ifname) 283 { 284 char buf[8192], *cp; 285 int result_code, len; 286 u_int if_index; 287 static char interface[64]; 288 289 if (server == NULL) 290 return (ifname); 291 292 /* get an inferface list from the server */ 293 quip_sendrequest(server, "GET list?*\n"); 294 295 result_code = quip_recvresponse(server, NULL, buf, &len); 296 if (result_code != 200) 297 errx(1, "can't get interface list"); 298 299 cp = buf; 300 while (1) { 301 if (sscanf(cp, "%x %s", &if_index, interface) != 2) 302 break; 303 if (ifname == NULL) { 304 /* if name isn't specified, return the 1st entry */ 305 return (interface); 306 } 307 if (strcmp(ifname, interface) == 0) 308 /* found the matching entry */ 309 310 return (interface); 311 if ((cp = strchr(cp+1, '\n')) == NULL) 312 break; 313 } 314 errx(1, "can't get interface"); 315 return (NULL); 316 } 317 318 char * 319 quip_selectqdisc(char *ifname, char *qdisc_name) 320 { 321 char buf[8192], req[256]; 322 int result_code, len; 323 static char qdisc[64]; 324 325 if (server == NULL) { 326 if (ifname == NULL || qdisc_name == NULL) 327 errx(1, "when disabling server communication,\n" 328 "specify both interface (-i) and qdisc (-q)!"); 329 return (qdisc_name); 330 } 331 332 /* get qdisc info from the server */ 333 sprintf(req, "GET qdisc?%s\n", ifname); 334 quip_sendrequest(server, req); 335 336 result_code = quip_recvresponse(server, NULL, buf, &len); 337 if (result_code != 200) 338 errx(1, "can't get qdisc info"); 339 340 if (sscanf(buf, "%s", qdisc) != 1) 341 errx(1, "can't get qdisc name"); 342 343 if (qdisc_name != NULL && strcmp(qdisc, qdisc_name) != 0) 344 errx(1, "qdisc %s on %s doesn't match specified qdisc %s", 345 qdisc, ifname, qdisc_name); 346 347 return (qdisc); 348 } 349 350 void 351 quip_chandle2name(const char *ifname, u_long handle, char *name) 352 { 353 char buf[8192], req[256], *cp; 354 int result_code, len; 355 356 name[0] = '\0'; 357 if (server == NULL) 358 return; 359 360 /* get class name from the server */ 361 sprintf(req, "GET handle-to-name?%s:%#lx\n", ifname, handle); 362 quip_sendrequest(server, req); 363 364 result_code = quip_recvresponse(server, NULL, buf, &len); 365 if (result_code != 200) 366 errx(1, "can't get class name"); 367 368 if ((cp = strchr(buf, '\n')) != NULL) 369 *cp = '\0'; 370 if ((cp = strrchr(buf, '/')) != NULL) 371 strcpy(name, cp+1); 372 } 373 374 void 375 quip_printqdisc(const char *ifname) 376 { 377 char buf[8192], req[256], *cp; 378 int result_code, len; 379 380 if (server == NULL) { 381 printf("No server available!\n"); 382 return; 383 } 384 385 /* get qdisc info from the server */ 386 sprintf(req, "GET qdisc?%s\n", ifname); 387 quip_sendrequest(server, req); 388 389 result_code = quip_recvresponse(server, NULL, buf, &len); 390 if (result_code != 200) 391 errx(1, "can't get qdisc info"); 392 393 /* replace newline by space */ 394 cp = buf; 395 while ((cp = strchr(cp, '\n')) != NULL) 396 *cp = ' '; 397 398 printf(" qdisc:%s\n", buf); 399 } 400 401 void 402 quip_printfilter(const char *ifname, const u_long handle) 403 { 404 char buf[8192], req[256], *cp; 405 int result_code, len; 406 407 /* get qdisc info from the server */ 408 sprintf(req, "GET filter?%s::%#lx\n", ifname, handle); 409 quip_sendrequest(server, req); 410 411 result_code = quip_recvresponse(server, NULL, buf, &len); 412 if (result_code != 200) 413 errx(1, "can't get filter info"); 414 415 if ((cp = strchr(buf, '\n')) != NULL) 416 *cp = '\0'; 417 printf("%s", buf); 418 } 419 420 static char * 421 extract_ifname(const char *name) 422 { 423 char *cp; 424 int len; 425 static char ifname[64]; 426 427 if ((cp = strchr(name, ':')) != NULL) 428 len = cp - name; 429 else 430 len = strlen(name); 431 len = MIN(len, 63); 432 strncpy(ifname, name, len); 433 ifname[len] = '\0'; 434 return (ifname); 435 } 436 437 void 438 quip_printconfig(void) 439 { 440 char buf[8192], name[256], *cp, *p, *flname; 441 int result_code, len; 442 enum nametype type; 443 u_long handle; 444 445 /* get a total list from the server */ 446 quip_sendrequest(server, "GET list\n"); 447 448 result_code = quip_recvresponse(server, NULL, buf, &len); 449 if (result_code != 200) 450 errx(1, "can't get total list"); 451 452 printf("------------ current configuration ------------"); 453 454 cp = buf; 455 while (1) { 456 if (sscanf(cp, "%lx %s", &handle, name) != 2) 457 break; 458 459 if ((p = strchr(name, ':')) == NULL) 460 type = INTERFACE; 461 else if (strchr(p+1, ':') == NULL) 462 type = CLASS; 463 else 464 type = FILTER; 465 466 switch (type) { 467 case INTERFACE: 468 printf("\ninterface: %s (index:%lu)\n", 469 name, handle); 470 quip_printqdisc(name); 471 break; 472 case CLASS: 473 printf("class: %s (handle:%#lx)\n", 474 name, handle); 475 break; 476 case FILTER: 477 flname = strrchr(name, ':') + 1; 478 printf(" filter: name:%s [", flname); 479 quip_printfilter(extract_ifname(name), handle); 480 printf("] (handle:%#lx)\n", handle); 481 break; 482 case CONDITIONER: 483 break; 484 } 485 486 if ((cp = strchr(cp+1, '\n')) == NULL) 487 break; 488 } 489 printf("-----------------------------------------------\n\n"); 490 } 491 492