1/* $NetBSD: quip_server.c,v 1.5 2009/04/15 00:13:51 lukem Exp $ */ 2/* $KAME: quip_server.c,v 1.6 2001/08/20 06:41:32 kjc Exp $ */ 3/* 4 * Copyright (C) 1999-2000 5 * Sony Computer Science Laboratories, Inc. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29#include <sys/param.h> 30#include <sys/socket.h> 31#include <sys/queue.h> 32 33#include <net/if.h> 34#include <netinet/in.h> 35#include <arpa/inet.h> 36 37#include <stdio.h> 38#include <stdlib.h> 39#include <unistd.h> 40#include <stddef.h> 41#include <string.h> 42#include <errno.h> 43#include <err.h> 44 45#include <altq/altq.h> 46#include <altq/altq_red.h> 47#include <altq/altq_rio.h> 48 49#include "altq_qop.h" 50#include "quip_server.h" 51 52extern LIST_HEAD(qop_iflist, ifinfo) qop_iflist; 53 54#define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0) 55 56static int next_word(char **, char *); 57 58static int query_list(const char *, const char *, char *, size_t); 59static int query_handle2name(const char *, const char *, char *, size_t); 60static int query_qdisc(const char *, const char *, char *, size_t); 61static int query_filterspec(const char *, const char *, char *, size_t); 62 63int 64quip_input(FILE *fp) 65{ 66 char request[REQ_MAXSIZE], result[RES_MAXSIZE], body[BODY_MAXSIZE], 67 w[REQ_MAXSIZE], *cp, *query; 68 int n = 0; 69 70 while (1) { 71 if (fgets(request, REQ_MAXSIZE, fp) == NULL) /* EOF */ 72 return (-1); 73 /* skip preceding blank lines */ 74 if (request[0] == '\n') 75 continue; 76 break; 77 } 78 79 /* remove trailing newline and white space */ 80 if ((cp = strrchr(request, '\n')) != NULL) { 81 *cp-- = '\0'; 82 while (*cp == ' ' || *cp == '\t') 83 *cp-- = '\0'; 84 } 85 86 body[0] = '\0'; 87 cp = request; 88 if (!next_word(&cp, w)) { 89 snprintf(result, sizeof(result), "400 Bad request\n"); 90 goto done; 91 } 92 if (EQUAL(w, "GET")) { 93 if (!next_word(&cp, w)) { 94 snprintf(result, sizeof(result), "400 Bad request\n"); 95 goto done; 96 } 97 if ((query = strchr(w, '?')) != NULL) { 98 /* request has a query string */ 99 *query = '\0'; 100 query++; 101 } 102 103 if (EQUAL(w, "list")) { 104 n = query_list(w, query, body, BODY_MAXSIZE); 105 } else if (EQUAL(w, "handle-to-name")) { 106 n = query_handle2name(w, query, body, BODY_MAXSIZE); 107 } else if (EQUAL(w, "qdisc")) { 108 n = query_qdisc(w, query, body, BODY_MAXSIZE); 109 } else if (EQUAL(w, "filter")) { 110 n = query_filterspec(w, query, body, BODY_MAXSIZE); 111 } else { 112 snprintf(result, sizeof(result), "400 Bad request\n"); 113 goto done; 114 } 115 } else { 116 snprintf(result, sizeof(result), "400 Bad request\n"); 117 goto done; 118 } 119 120 if (n == 0) { 121 snprintf(result, sizeof(result), "204 No content\n"); 122 } else if (n < 0) { 123 snprintf(result, sizeof(result), "400 Bad request\n"); 124 } else { 125 snprintf(result, sizeof(result), "200 OK\nContent-Length:%d\n", n); 126 } 127 128 done: 129 /* send a result line and a blank line */ 130 if (fputs ("QUIP/1.0 ", fp) != 0 || 131 fputs(result, fp) != 0 || fputs("\n", fp) != 0) 132 return (-1); 133 134 /* send message body */ 135 if (fputs(body, fp) != 0) 136 return (-1); 137 return (0); 138} 139 140/* 141 * Skip leading blanks, then copy next word (delimited by blank or zero, but 142 * no longer than 63 bytes) into buffer b, set scan pointer to following 143 * non-blank (or end of string), and return 1. If there is no non-blank text, 144 * set scan ptr to point to 0 byte and return 0. 145 */ 146static int 147next_word(char **cpp, char *b) 148{ 149 char *tp; 150 int L; 151 152 *cpp += strspn(*cpp, " \t"); 153 if (**cpp == '\0' || **cpp == '\n' || **cpp == '#') 154 return(0); 155 156 tp = strpbrk(*cpp, " \t\n#"); 157 L = MIN((tp)?(tp-*cpp):(int)strlen(*cpp), 63); 158 strncpy(b, *cpp, L); 159 *(b + L) = '\0'; 160 *cpp += L; 161 *cpp += strspn(*cpp, " \t"); 162 return (1); 163} 164 165 166/* 167 * expand_classname creates a long class name. 168 * <ifname>:/<root_name>/../<parent_name>/<class_name> 169 */ 170static int 171expand_classname(struct classinfo *clinfo, char *name, size_t maxname) 172{ 173 struct classinfo *ci = clinfo; 174#define CLASSNAMEMAX 256 175 char buf[2][CLASSNAMEMAX], *b0, *b1, *tmp; 176 177 b0 = buf[0]; b1 = buf[1]; 178 b1[0] = '\0'; 179 while (ci != NULL) { 180 strlcpy(b0, "/", CLASSNAMEMAX); 181 strlcat(b0, ci->clname, CLASSNAMEMAX); 182 strlcat(b0, b1, CLASSNAMEMAX); 183 184 ci = ci->parent; 185 tmp = b0; b0 = b1; b1 = tmp; 186 } 187 snprintf(b0, CLASSNAMEMAX, "%s:", clinfo->ifinfo->ifname); 188 strlcat(b0, b1, CLASSNAMEMAX); 189 strlcpy(name, b0, CLASSNAMEMAX); 190 return (strlen(name)); 191#undef CLASSNAMEMAX 192} 193 194/* 195 * expand_filtername creates a long filter name. 196 * <ifname>:/<root_name>/../<parent_name>/<class_name>:<fltr_name> 197 */ 198static int 199expand_filtername(struct fltrinfo *fltrinfo, char *name, size_t maxname) 200{ 201 int len; 202 203 len = expand_classname(fltrinfo->clinfo, name, maxname); 204 snprintf(name + len, maxname - len, ":%s", fltrinfo->flname); 205 return (strlen(name)); 206} 207 208static int 209query_handle2name(const char *cmd, const char *arg, char *msg, size_t maxmsg) 210{ 211 struct ifinfo *ifinfo; 212 struct classinfo *clinfo; 213 struct fltrinfo *fltrinfo; 214 char *ifname, *class_field, *fltr_field, buf[256], *cp; 215 u_long handle; 216 217 strlcpy(buf, arg, sizeof(buf)); 218 cp = buf; 219 ifname = strsep(&cp, ":"); 220 class_field = strsep(&cp, ":"); 221 fltr_field = cp; 222 223 if (fltr_field != NULL) { 224 if (sscanf(fltr_field, "%lx", &handle) != 1) 225 return (-1); 226 if ((ifinfo = ifname2ifinfo(ifname)) == NULL) 227 return (-1); 228 if ((fltrinfo = flhandle2fltrinfo(ifinfo, handle)) == NULL) 229 return (-1); 230 231 (void)expand_filtername(fltrinfo, msg, maxmsg); 232 } else { 233 if (sscanf(class_field, "%lx", &handle) != 1) 234 return (-1); 235 if ((ifinfo = ifname2ifinfo(ifname)) == NULL) 236 return (-1); 237 if ((clinfo = clhandle2clinfo(ifinfo, handle)) == NULL) 238 return (-1); 239 240 (void)expand_classname(clinfo, msg, maxmsg); 241 } 242 strlcat(msg, "\n", maxmsg); 243 return (strlen(msg)); 244} 245 246static int 247query_qdisc(const char *cmd, const char *arg, char *msg, size_t maxmsg) 248{ 249 struct ifinfo *ifinfo; 250 251 if ((ifinfo = ifname2ifinfo(arg)) == NULL) 252 return (-1); 253 254 snprintf(msg, maxmsg, "%s\nbandwidth:%.2fMbps\nstatus:%s\n", 255 ifinfo->qdisc->qname, (double)ifinfo->bandwidth/1000000, 256 (ifinfo->enabled ? "enabled" : "disabled")); 257 return (strlen(msg)); 258} 259 260static int 261query_filterspec(const char *cmd, const char *arg, char *msg, size_t maxmsg) 262{ 263 struct ifinfo *ifinfo; 264 struct fltrinfo *fltrinfo; 265 struct flow_filter *filt; 266 char *ifname, *fltr_field, buf[256], *cp; 267 u_long handle; 268 269 strlcpy(buf, arg, sizeof(buf)); 270 cp = buf; 271 ifname = strsep(&cp, ":"); 272 (void)strsep(&cp, ":"); 273 fltr_field = cp; 274 275 if (fltr_field == NULL) 276 return (-1); 277 if (sscanf(fltr_field, "%lx", &handle) != 1) 278 return (-1); 279 280 if ((ifinfo = ifname2ifinfo(ifname)) == NULL) 281 return (-1); 282 if ((fltrinfo = flhandle2fltrinfo(ifinfo, handle)) == NULL) 283 return (-1); 284 285 filt = &fltrinfo->fltr; 286 287 if (filt->ff_flow.fi_family == AF_INET) { 288 char src[128], dst[128], smask[128], dmask[128], tos[128]; 289 290 if (filt->ff_flow.fi_dst.s_addr == 0) { 291 snprintf(dst, sizeof(dst), "0"); 292 dmask[0] = '\0'; 293 } else { 294 snprintf(dst, sizeof(dst), "%s", 295 inet_ntoa(filt->ff_flow.fi_dst)); 296 if (filt->ff_mask.mask_dst.s_addr == 0xffffffff) 297 dmask[0] = '\0'; 298 else 299 snprintf(dmask, sizeof(dmask), " mask %#x", 300 ntoh32(filt->ff_mask.mask_dst.s_addr)); 301 } 302 if (filt->ff_flow.fi_src.s_addr == 0) { 303 snprintf(src, sizeof(src), "0"); 304 smask[0] = '\0'; 305 } else { 306 snprintf(src, sizeof(src), "%s", 307 inet_ntoa(filt->ff_flow.fi_src)); 308 if (filt->ff_mask.mask_src.s_addr == 0xffffffff) 309 smask[0] = '\0'; 310 else 311 snprintf(smask, sizeof(smask), " mask %#x", 312 ntoh32(filt->ff_mask.mask_src.s_addr)); 313 } 314 if (filt->ff_flow.fi_tos == 0) 315 tos[0] = '\0'; 316 else 317 snprintf(tos, sizeof(tos), " tos %#x tosmask %#x", 318 filt->ff_flow.fi_tos, 319 filt->ff_mask.mask_tos); 320 321 snprintf(msg, maxmsg, "inet %s%s %d %s%s %d %d%s\n", 322 dst, dmask, 323 ntoh16(filt->ff_flow.fi_dport), 324 src, smask, 325 ntoh16(filt->ff_flow.fi_sport), 326 filt->ff_flow.fi_proto, tos); 327 } 328#ifdef INET6 329 else if (filt->ff_flow.fi_family == AF_INET6) { 330 struct flow_filter6 *filt6; 331 char dst6[INET6_ADDRSTRLEN], dmask6[INET6_ADDRSTRLEN]; 332 char src6[INET6_ADDRSTRLEN], smask6[INET6_ADDRSTRLEN]; 333 char tclass6[128]; 334 const struct in6_addr mask128 = 335 {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 336 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}}; 337 338 filt6 = (struct flow_filter6 *)&fltrinfo->fltr; 339 if (IN6_IS_ADDR_UNSPECIFIED(&filt6->ff_flow6.fi6_dst)) { 340 snprintf(dst6, sizeof(dst6), "0"); 341 dmask6[0] = '\0'; 342 } else { 343 inet_ntop(AF_INET6, &filt6->ff_flow6.fi6_dst, 344 dst6, sizeof(dst6)); 345 if (IN6_ARE_ADDR_EQUAL(&mask128, 346 &filt6->ff_mask6.mask6_dst)) 347 dmask6[0] = '\0'; 348 else { 349 snprintf(dmask6, sizeof(dmask6), " mask "); 350 inet_ntop(AF_INET6, &filt6->ff_mask6.mask6_dst, 351 dmask6 + 6, sizeof(dmask6) -6); 352 } 353 } 354 355 if (IN6_IS_ADDR_UNSPECIFIED(&filt6->ff_flow6.fi6_src)) { 356 snprintf(src6, sizeof(src6), "0"); 357 smask6[0] = '\0'; 358 } else { 359 inet_ntop(AF_INET6, &filt6->ff_flow6.fi6_src, 360 src6, sizeof(src6)); 361 if (IN6_ARE_ADDR_EQUAL(&mask128, 362 &filt6->ff_mask6.mask6_src)) 363 smask6[0] = '\0'; 364 else { 365 snprintf(smask6, sizeof(smask6), " mask "); 366 inet_ntop(AF_INET6, &filt6->ff_mask6.mask6_src, 367 smask6 + 6, sizeof(smask6) -6); 368 } 369 } 370 if (filt6->ff_flow6.fi6_tclass == 0) 371 tclass6[0] = '\0'; 372 else 373 snprintf(tclass6, sizeof(tclass6), 374 " tclass %#x tclassmask %#x", 375 filt6->ff_flow6.fi6_tclass, 376 filt6->ff_mask6.mask6_tclass); 377 378 snprintf(msg, maxmsg, "inet6 %s%s %d %s%s %d %d%s\n", 379 dst6, dmask6, 380 ntoh16(filt6->ff_flow6.fi6_dport), 381 src6, smask6, 382 ntoh16(filt6->ff_flow6.fi6_sport), 383 filt6->ff_flow6.fi6_proto, tclass6); 384 } 385#endif /* INET6 */ 386 387 return (strlen(msg)); 388} 389 390 391/* 392 * string_match compares 2 strings and returns 1 when s1 matches s2. 393 * s1: possibly includes wildcards, "*". 394 * s2: must be a full string (should not include "*"). 395 */ 396static int 397string_match(const char *s1, const char *s2) 398{ 399 char *ap, *next, sub[256]; 400 int prefixlen, sublen; 401 402 /* if there's no wild card, compare full string */ 403 if ((ap = strchr(s1, '*')) == NULL) 404 return (strcmp(s1, s2) == 0); 405 406 /* compare string prefix */ 407 prefixlen = ap - s1; 408 if (strncmp(s1, s2, prefixlen) != 0) 409 return (0); 410 s2 += prefixlen; 411 412 /* 413 * if there is another wildcard in the rest of the string, 414 * compare the substring between the 2 wildcards. 415 */ 416 while ((next = strchr(ap + 1, '*')) != NULL) { 417 sublen = next - ap - 1; 418 strncpy(sub, ap+1, sublen); 419 sub[sublen] = '\0'; 420 if ((s2 = strstr(s2, sub)) == NULL) 421 return (0); 422 423 s2 += sublen; 424 ap = next; 425 } 426 427 /* no more wildcard, compare the rest of the string */ 428 return (strcmp(ap+1, s2+strlen(s2)-strlen(ap+1)) == 0); 429} 430 431static int 432query_list(const char *cmd, const char *arg, char *msg, size_t maxmsg) 433{ 434 char tmp[256], *cp, *ep; 435 struct ifinfo *ifinfo; 436 struct classinfo *clinfo; 437 struct fltrinfo *fltrinfo; 438 int print_if, print_class, print_fltr, len; 439 440 if (arg == NULL) { 441 /* no arg, print all */ 442 print_if = print_class = print_fltr = 1; 443 } else { 444 print_if = print_class = print_fltr = 0; 445 if ((cp = strchr(arg, ':')) == NULL) 446 print_if = 1; 447 else if (strchr(cp+1, ':') == NULL) 448 print_class = 1; 449 else 450 print_fltr = 1; 451 } 452 453 cp = msg; 454 ep = msg + maxmsg; 455 LIST_FOREACH(ifinfo, &qop_iflist, next) { 456 if (print_if) { 457 strlcpy(tmp, ifinfo->ifname, sizeof(tmp)); 458 if (arg == NULL || string_match(arg, tmp)) { 459 len = snprintf(cp, ep - cp, "%#010x\t%s\n", 460 ifinfo->ifindex, tmp); 461 if (len < 0 || len >= ep - cp) 462 break; 463 cp += len; 464 } 465 } 466 if (!print_class && !print_fltr) 467 continue; 468 for (clinfo = get_rootclass(ifinfo); 469 clinfo != NULL; clinfo = get_nextclass(clinfo)) { 470 if (print_class) { 471 expand_classname(clinfo, tmp, sizeof(tmp)); 472 if (arg == NULL || string_match(arg, tmp)) { 473 len = snprintf(cp, ep - cp, 474 "%#010lx\t%s\n", 475 clinfo->handle, tmp); 476 if (len < 0 || len >= ep - cp) 477 break; 478 cp += len; 479 } 480 } 481 if (!print_fltr) 482 continue; 483 LIST_FOREACH(fltrinfo, &clinfo->fltrlist, next) { 484 expand_filtername(fltrinfo, tmp, sizeof(tmp)); 485 if (arg == NULL || string_match(arg, tmp)) { 486 len = snprintf(cp, ep - cp, "%#010lx\t%s\n", 487 fltrinfo->handle, tmp); 488 if (len < 0 || len >= ep - cp) 489 break; 490 cp += len; 491 } 492 } 493 } 494 } 495 return (strlen(msg)); 496} 497 498