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