1 /* $NetBSD: quip_server.c,v 1.7 2024/10/04 15:37:00 rillig 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):(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 */ 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 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 246 static int 247 query_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 260 static int 261 query_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 */ 396 static int 397 string_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 431 static int 432 query_list(const char *cmd, const char *arg, char *msg, size_t maxmsg) 433 { 434 const char *colon; 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 ((colon = strchr(arg, ':')) == NULL) 447 print_if = 1; 448 else if (strchr(colon+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