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