1 /* $NetBSD: quip_server.c,v 1.4 2001/08/22 08:52:37 itojun 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 52 extern LIST_HEAD(qop_iflist, ifinfo) qop_iflist; 53 54 #define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0) 55 56 static int next_word(char **, char *); 57 58 static int query_list(const char *, const char *, char *, size_t); 59 static int query_handle2name(const char *, const char *, char *, size_t); 60 static int query_qdisc(const char *, const char *, char *, size_t); 61 static int query_filterspec(const char *, const char *, char *, size_t); 62 63 int 64 quip_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 */ 146 static int 147 next_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):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 */ 170 static int 171 expand_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 */ 198 static int 199 expand_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 208 static int 209 query_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 int len; 217 218 strlcpy(buf, arg, sizeof(buf)); 219 cp = buf; 220 ifname = strsep(&cp, ":"); 221 class_field = strsep(&cp, ":"); 222 fltr_field = cp; 223 224 if (fltr_field != NULL) { 225 if (sscanf(fltr_field, "%lx", &handle) != 1) 226 return (-1); 227 if ((ifinfo = ifname2ifinfo(ifname)) == NULL) 228 return (-1); 229 if ((fltrinfo = flhandle2fltrinfo(ifinfo, handle)) == NULL) 230 return (-1); 231 232 len = expand_filtername(fltrinfo, msg, maxmsg); 233 } else { 234 if (sscanf(class_field, "%lx", &handle) != 1) 235 return (-1); 236 if ((ifinfo = ifname2ifinfo(ifname)) == NULL) 237 return (-1); 238 if ((clinfo = clhandle2clinfo(ifinfo, handle)) == NULL) 239 return (-1); 240 241 len = expand_classname(clinfo, msg, maxmsg); 242 } 243 strlcat(msg, "\n", maxmsg); 244 return (strlen(msg)); 245 } 246 247 static int 248 query_qdisc(const char *cmd, const char *arg, char *msg, size_t maxmsg) 249 { 250 struct ifinfo *ifinfo; 251 252 if ((ifinfo = ifname2ifinfo(arg)) == NULL) 253 return (-1); 254 255 snprintf(msg, maxmsg, "%s\nbandwidth:%.2fMbps\nstatus:%s\n", 256 ifinfo->qdisc->qname, (double)ifinfo->bandwidth/1000000, 257 (ifinfo->enabled ? "enabled" : "disabled")); 258 return (strlen(msg)); 259 } 260 261 static int 262 query_filterspec(const char *cmd, const char *arg, char *msg, size_t maxmsg) 263 { 264 struct ifinfo *ifinfo; 265 struct fltrinfo *fltrinfo; 266 struct flow_filter *filt; 267 char *ifname, *class_field, *fltr_field, buf[256], *cp; 268 u_long handle; 269 270 strlcpy(buf, arg, sizeof(buf)); 271 cp = buf; 272 ifname = strsep(&cp, ":"); 273 class_field = strsep(&cp, ":"); 274 fltr_field = cp; 275 276 if (fltr_field == NULL) 277 return (-1); 278 if (sscanf(fltr_field, "%lx", &handle) != 1) 279 return (-1); 280 281 if ((ifinfo = ifname2ifinfo(ifname)) == NULL) 282 return (-1); 283 if ((fltrinfo = flhandle2fltrinfo(ifinfo, handle)) == NULL) 284 return (-1); 285 286 filt = &fltrinfo->fltr; 287 288 if (filt->ff_flow.fi_family == AF_INET) { 289 char src[128], dst[128], smask[128], dmask[128], tos[128]; 290 291 if (filt->ff_flow.fi_dst.s_addr == 0) { 292 snprintf(dst, sizeof(dst), "0"); 293 dmask[0] = '\0'; 294 } else { 295 snprintf(dst, sizeof(dst), "%s", 296 inet_ntoa(filt->ff_flow.fi_dst)); 297 if (filt->ff_mask.mask_dst.s_addr == 0xffffffff) 298 dmask[0] = '\0'; 299 else 300 snprintf(dmask, sizeof(dmask), " mask %#x", 301 ntoh32(filt->ff_mask.mask_dst.s_addr)); 302 } 303 if (filt->ff_flow.fi_src.s_addr == 0) { 304 snprintf(src, sizeof(src), "0"); 305 smask[0] = '\0'; 306 } else { 307 snprintf(src, sizeof(src), "%s", 308 inet_ntoa(filt->ff_flow.fi_src)); 309 if (filt->ff_mask.mask_src.s_addr == 0xffffffff) 310 smask[0] = '\0'; 311 else 312 snprintf(smask, sizeof(smask), " mask %#x", 313 ntoh32(filt->ff_mask.mask_src.s_addr)); 314 } 315 if (filt->ff_flow.fi_tos == 0) 316 tos[0] = '\0'; 317 else 318 snprintf(tos, sizeof(tos), " tos %#x tosmask %#x", 319 filt->ff_flow.fi_tos, 320 filt->ff_mask.mask_tos); 321 322 snprintf(msg, maxmsg, "inet %s%s %d %s%s %d %d%s\n", 323 dst, dmask, 324 ntoh16(filt->ff_flow.fi_dport), 325 src, smask, 326 ntoh16(filt->ff_flow.fi_sport), 327 filt->ff_flow.fi_proto, tos); 328 } 329 #ifdef INET6 330 else if (filt->ff_flow.fi_family == AF_INET6) { 331 struct flow_filter6 *filt6; 332 char dst6[INET6_ADDRSTRLEN], dmask6[INET6_ADDRSTRLEN]; 333 char src6[INET6_ADDRSTRLEN], smask6[INET6_ADDRSTRLEN]; 334 char tclass6[128]; 335 const struct in6_addr mask128 = 336 {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 337 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}}; 338 339 filt6 = (struct flow_filter6 *)&fltrinfo->fltr; 340 if (IN6_IS_ADDR_UNSPECIFIED(&filt6->ff_flow6.fi6_dst)) { 341 snprintf(dst6, sizeof(dst6), "0"); 342 dmask6[0] = '\0'; 343 } else { 344 inet_ntop(AF_INET6, &filt6->ff_flow6.fi6_dst, 345 dst6, sizeof(dst6)); 346 if (IN6_ARE_ADDR_EQUAL(&mask128, 347 &filt6->ff_mask6.mask6_dst)) 348 dmask6[0] = '\0'; 349 else { 350 snprintf(dmask6, sizeof(dmask6), " mask "); 351 inet_ntop(AF_INET6, &filt6->ff_mask6.mask6_dst, 352 dmask6 + 6, sizeof(dmask6) -6); 353 } 354 } 355 356 if (IN6_IS_ADDR_UNSPECIFIED(&filt6->ff_flow6.fi6_src)) { 357 snprintf(src6, sizeof(src6), "0"); 358 smask6[0] = '\0'; 359 } else { 360 inet_ntop(AF_INET6, &filt6->ff_flow6.fi6_src, 361 src6, sizeof(src6)); 362 if (IN6_ARE_ADDR_EQUAL(&mask128, 363 &filt6->ff_mask6.mask6_src)) 364 smask6[0] = '\0'; 365 else { 366 snprintf(smask6, sizeof(smask6), " mask "); 367 inet_ntop(AF_INET6, &filt6->ff_mask6.mask6_src, 368 smask6 + 6, sizeof(smask6) -6); 369 } 370 } 371 if (filt6->ff_flow6.fi6_tclass == 0) 372 tclass6[0] = '\0'; 373 else 374 snprintf(tclass6, sizeof(tclass6), 375 " tclass %#x tclassmask %#x", 376 filt6->ff_flow6.fi6_tclass, 377 filt6->ff_mask6.mask6_tclass); 378 379 snprintf(msg, maxmsg, "inet6 %s%s %d %s%s %d %d%s\n", 380 dst6, dmask6, 381 ntoh16(filt6->ff_flow6.fi6_dport), 382 src6, smask6, 383 ntoh16(filt6->ff_flow6.fi6_sport), 384 filt6->ff_flow6.fi6_proto, tclass6); 385 } 386 #endif /* INET6 */ 387 388 return (strlen(msg)); 389 } 390 391 392 /* 393 * string_match compares 2 strings and returns 1 when s1 matches s2. 394 * s1: possibly includes wildcards, "*". 395 * s2: must be a full string (should not include "*"). 396 */ 397 static int 398 string_match(const char *s1, const char *s2) 399 { 400 char *ap, *next, sub[256]; 401 int prefixlen, sublen; 402 403 /* if there's no wild card, compare full string */ 404 if ((ap = strchr(s1, '*')) == NULL) 405 return (strcmp(s1, s2) == 0); 406 407 /* compare string prefix */ 408 prefixlen = ap - s1; 409 if (strncmp(s1, s2, prefixlen) != 0) 410 return (0); 411 s2 += prefixlen; 412 413 /* 414 * if there is another wildcard in the rest of the string, 415 * compare the substring between the 2 wildcards. 416 */ 417 while ((next = strchr(ap + 1, '*')) != NULL) { 418 sublen = next - ap - 1; 419 strncpy(sub, ap+1, sublen); 420 sub[sublen] = '\0'; 421 if ((s2 = strstr(s2, sub)) == NULL) 422 return (0); 423 424 s2 += sublen; 425 ap = next; 426 } 427 428 /* no more wildcard, compare the rest of the string */ 429 return (strcmp(ap+1, s2+strlen(s2)-strlen(ap+1)) == 0); 430 } 431 432 static int 433 query_list(const char *cmd, const char *arg, char *msg, size_t maxmsg) 434 { 435 char tmp[256], *cp, *ep; 436 struct ifinfo *ifinfo; 437 struct classinfo *clinfo; 438 struct fltrinfo *fltrinfo; 439 int print_if, print_class, print_fltr, len; 440 441 if (arg == NULL) { 442 /* no arg, print all */ 443 print_if = print_class = print_fltr = 1; 444 } else { 445 print_if = print_class = print_fltr = 0; 446 if ((cp = strchr(arg, ':')) == NULL) 447 print_if = 1; 448 else if (strchr(cp+1, ':') == NULL) 449 print_class = 1; 450 else 451 print_fltr = 1; 452 } 453 454 cp = msg; 455 ep = msg + maxmsg; 456 LIST_FOREACH(ifinfo, &qop_iflist, next) { 457 if (print_if) { 458 strlcpy(tmp, ifinfo->ifname, sizeof(tmp)); 459 if (arg == NULL || string_match(arg, tmp)) { 460 len = snprintf(cp, ep - cp, "%#010x\t%s\n", 461 ifinfo->ifindex, tmp); 462 if (len < 0 || len >= ep - cp) 463 break; 464 cp += len; 465 } 466 } 467 if (!print_class && !print_fltr) 468 continue; 469 for (clinfo = get_rootclass(ifinfo); 470 clinfo != NULL; clinfo = get_nextclass(clinfo)) { 471 if (print_class) { 472 expand_classname(clinfo, tmp, sizeof(tmp)); 473 if (arg == NULL || string_match(arg, tmp)) { 474 len = snprintf(cp, ep - cp, 475 "%#010lx\t%s\n", 476 clinfo->handle, tmp); 477 if (len < 0 || len >= ep - cp) 478 break; 479 cp += len; 480 } 481 } 482 if (!print_fltr) 483 continue; 484 LIST_FOREACH(fltrinfo, &clinfo->fltrlist, next) { 485 expand_filtername(fltrinfo, tmp, sizeof(tmp)); 486 if (arg == NULL || string_match(arg, tmp)) { 487 len = snprintf(cp, ep - cp, "%#010lx\t%s\n", 488 fltrinfo->handle, tmp); 489 if (len < 0 || len >= ep - cp) 490 break; 491 cp += len; 492 } 493 } 494 } 495 } 496 return (strlen(msg)); 497 } 498 499