quip_client.c revision 1.1 1 /* $KAME: quip_client.c,v 1.2 2000/10/18 09:15:17 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/un.h>
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include <signal.h>
37 #include <errno.h>
38 #include <err.h>
39
40 #include "quip_client.h"
41 #include "altqstat.h"
42
43 /*
44 * quip (queue information protocol) is a http-like protocol
45 * in order to retrieve information from the server.
46 * a unix domain TCP socket "/var/run/altq_quip" is used for
47 * clinet-server style communication.
48 *
49 * there are 2 quip message types: request and response.
50 * request format: (only single-line request message is used at this moment)
51 * request-line
52 *
53 * request-line = <method> <operation>[?<query>] <quip-version>
54 * <method> = GET (only GET is defined at this moment)
55 * <operation> = list | handle-to-name | qdisc | filter
56 * query format is operation dependent but most query takes
57 * <interface> or <class> or <filter>.
58 * <interface> = <if_name>
59 * <class> = <if_name>:<class_path>/<class_name>
60 * <filter> = <if_name>:<class_path>/<class_name>:<filter_name>
61 * "list" operation accepts "*" as a wildcard.
62 *
63 * response format:
64 * status-line
65 * response-headers (0 or more)
66 * <blank line>
67 * body
68 *
69 * status-line = <quip-version> <status-code> <reason phrase>
70 * response-header = Content-Length:<value>
71 *
72 * "Content-Length" specifies the length of the message body.
73 *
74 * example:
75 * to retrieve a list of classes (handle and name) on interface "fxp0":
76 * a request message looks like,
77 * GET list?fxp0:* QUIP/1.0<cr>
78 * a response message looks like,
79 * QUIP/1.0 200 OK<cr>
80 * Content-Length:86<cr>
81 * <cr>
82 * 0000000000 fxp0:/root<cr>
83 * 0xc0d1be00 fxp0:/root/parent<cr>
84 * 0xc0d1ba00 fxp0:/root/parent/child<cr>
85 *
86 * other examples:
87 * list all interfaces, classes, and filters:
88 * GET list QUIP/1.0<cr>
89 * list all interfaces:
90 * GET list?* QUIP/1.0<cr>
91 * list all classes:
92 * GET list?*:* QUIP/1.0<cr>
93 * list all filters:
94 * GET list?*:*:* QUIP/1.0<cr>
95 * convert class handle to class name:
96 * GET handle-to-name?fxp0:0xc0d1be00 QUIP/1.0<cr>
97 * convert filter handle to filter name:
98 * GET handle-to-name?fxp0::0x1000000a QUIP/1.0<cr>
99 */
100
101 enum nametype { INTERFACE, CLASS, FILTER, CONDITIONER };
102
103 static FILE *server = NULL;
104 int quip_echo = 0;
105
106 static char *extract_ifname(const char *name);
107
108 int
109 quip_openserver(void)
110 {
111 struct sockaddr_un addr;
112 int fd;
113
114 if ((fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
115 err(1, "can't open socket");
116
117 bzero(&addr, sizeof(addr));
118 addr.sun_family = AF_LOCAL;
119 strcpy(addr.sun_path, QUIP_PATH);
120
121 if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
122 fprintf(stderr, "can't talk to altqd!\n"
123 "probably, altqd is not running\n");
124 return (-1);
125 }
126
127 if ((server = fdopen(fd, "r+")) == NULL) {
128 warn("fdopen: can't open stream to the quip server");
129 return (-1);
130 }
131 return (0);
132 }
133
134 int
135 quip_closeserver(void)
136 {
137 if (server != NULL)
138 return fclose(server);
139 return (0);
140 }
141
142 void
143 quip_sendrequest(FILE *fp, const char *request)
144 {
145 char buf[1024], *cp;
146 int n;
147
148 if ((cp = strstr(request, "QUIP")) == NULL) {
149 cp = strchr(request, '\n');
150 n = cp - request;
151 strncpy(buf, request, n);
152 n += sprintf(buf + n, " QUIP/1.0");
153 strcpy(buf + n, cp);
154 }
155 else
156 strcpy(buf, request);
157
158 if (fputs(buf, fp) != 0)
159 err(1, "fputs");
160 if (fflush(fp) != 0)
161 err(1, "fflush");
162 if (quip_echo) {
163 fputs("<< ", stdout);
164 fputs(buf, stdout);
165 }
166 }
167
168 /*
169 * recv_response receives a response message from the server
170 * and returns status_code.
171 */
172 int
173 quip_recvresponse(FILE *fp, char *header, char *body, int *blen)
174 {
175 char buf[1024], version[64];
176 int code, resid;
177 int end_of_header = 0;
178
179 if (blen != NULL)
180 *blen = 0;
181 code = 0;
182 resid = 0;
183 while (fgets(buf, 1024, fp) != 0) {
184 if (quip_echo) {
185 fputs("> ", stdout);
186 fputs(buf, stdout);
187 }
188
189 if (!end_of_header) {
190 /* process message header */
191 if (header != NULL)
192 header += sprintf(header, "%s", buf);
193
194 if (code == 0) {
195 /* status line expected */
196 if (buf[0] == '\n') {
197 /* ignore blank lines */
198 }
199 else if (sscanf(buf, "%s %d",
200 version, &code) != 2) {
201 /* can't get result code */
202 fpurge(fp);
203 return (-1);
204 }
205 }
206 else {
207 /* entity header expected */
208 char *field, *cp;
209
210 if (buf[0] == '\n') {
211 /* end of header */
212 end_of_header = 1;
213 if (resid == 0)
214 /* no message body */
215 return (code);
216 }
217
218 cp = buf;
219 field = strsep(&cp, ":");
220 if (strcmp(field, "Content-Length") == 0) {
221 sscanf(cp, "%d", &resid);
222 if (blen != NULL)
223 *blen = resid;
224 }
225 }
226 }
227 else {
228 /* process message body */
229 int len;
230
231 if (body != NULL) {
232 len = sprintf(body, "%s", buf);
233 body += len;
234 }
235 else
236 len = strlen(buf);
237 resid -= len;
238 if (resid <= 0)
239 return (code);
240 }
241 }
242 return (-1);
243 }
244
245 void
246 quip_rawmode(void)
247 {
248 char line[1024];
249 int result_code;
250
251 printf(">>>Entering the raw interactive mode to the server:\n\n");
252 if (server == NULL) {
253 printf("No server available!\n");
254 return;
255 }
256
257 while (1) {
258 printf("%% "); fflush(stdout);
259 /* read a line from stdin */
260 if (fgets(line, 1024, stdin) == NULL)
261 break;
262
263 if (line[0] == '\n') {
264 /* if a blank line, echo locally */
265 fputs(line, stdout);
266 continue;
267 }
268 if (line[0] == 'q') {
269 printf("Exit\n");
270 break;
271 }
272
273 /* send the input line to the server */
274 quip_sendrequest(server, line);
275
276 /* get a response message from the server */
277 result_code = quip_recvresponse(server, NULL, NULL, NULL);
278 }
279 }
280
281 char *
282 quip_selectinterface(char *ifname)
283 {
284 char buf[8192], *cp;
285 int result_code, len;
286 u_int if_index;
287 static char interface[64];
288
289 if (server == NULL)
290 return (ifname);
291
292 /* get an inferface list from the server */
293 quip_sendrequest(server, "GET list?*\n");
294
295 result_code = quip_recvresponse(server, NULL, buf, &len);
296 if (result_code != 200)
297 errx(1, "can't get interface list");
298
299 cp = buf;
300 while (1) {
301 if (sscanf(cp, "%x %s", &if_index, interface) != 2)
302 break;
303 if (ifname == NULL) {
304 /* if name isn't specified, return the 1st entry */
305 return (interface);
306 }
307 if (strcmp(ifname, interface) == 0)
308 /* found the matching entry */
309
310 return (interface);
311 if ((cp = strchr(cp+1, '\n')) == NULL)
312 break;
313 }
314 errx(1, "can't get interface");
315 return (NULL);
316 }
317
318 char *
319 quip_selectqdisc(char *ifname, char *qdisc_name)
320 {
321 char buf[8192], req[256];
322 int result_code, len;
323 static char qdisc[64];
324
325 if (server == NULL) {
326 if (ifname == NULL || qdisc_name == NULL)
327 errx(1, "when disabling server communication,\n"
328 "specify both interface (-i) and qdisc (-q)!");
329 return (qdisc_name);
330 }
331
332 /* get qdisc info from the server */
333 sprintf(req, "GET qdisc?%s\n", ifname);
334 quip_sendrequest(server, req);
335
336 result_code = quip_recvresponse(server, NULL, buf, &len);
337 if (result_code != 200)
338 errx(1, "can't get qdisc info");
339
340 if (sscanf(buf, "%s", qdisc) != 1)
341 errx(1, "can't get qdisc name");
342
343 if (qdisc_name != NULL && strcmp(qdisc, qdisc_name) != 0)
344 errx(1, "qdisc %s on %s doesn't match specified qdisc %s",
345 qdisc, ifname, qdisc_name);
346
347 return (qdisc);
348 }
349
350 void
351 quip_chandle2name(const char *ifname, u_long handle, char *name)
352 {
353 char buf[8192], req[256], *cp;
354 int result_code, len;
355
356 name[0] = '\0';
357 if (server == NULL)
358 return;
359
360 /* get class name from the server */
361 sprintf(req, "GET handle-to-name?%s:%#lx\n", ifname, handle);
362 quip_sendrequest(server, req);
363
364 result_code = quip_recvresponse(server, NULL, buf, &len);
365 if (result_code != 200)
366 errx(1, "can't get class name");
367
368 if ((cp = strchr(buf, '\n')) != NULL)
369 *cp = '\0';
370 if ((cp = strrchr(buf, '/')) != NULL)
371 strcpy(name, cp+1);
372 }
373
374 void
375 quip_printqdisc(const char *ifname)
376 {
377 char buf[8192], req[256], *cp;
378 int result_code, len;
379
380 if (server == NULL) {
381 printf("No server available!\n");
382 return;
383 }
384
385 /* get qdisc info from the server */
386 sprintf(req, "GET qdisc?%s\n", ifname);
387 quip_sendrequest(server, req);
388
389 result_code = quip_recvresponse(server, NULL, buf, &len);
390 if (result_code != 200)
391 errx(1, "can't get qdisc info");
392
393 /* replace newline by space */
394 cp = buf;
395 while ((cp = strchr(cp, '\n')) != NULL)
396 *cp = ' ';
397
398 printf(" qdisc:%s\n", buf);
399 }
400
401 void
402 quip_printfilter(const char *ifname, const u_long handle)
403 {
404 char buf[8192], req[256], *cp;
405 int result_code, len;
406
407 /* get qdisc info from the server */
408 sprintf(req, "GET filter?%s::%#lx\n", ifname, handle);
409 quip_sendrequest(server, req);
410
411 result_code = quip_recvresponse(server, NULL, buf, &len);
412 if (result_code != 200)
413 errx(1, "can't get filter info");
414
415 if ((cp = strchr(buf, '\n')) != NULL)
416 *cp = '\0';
417 printf("%s", buf);
418 }
419
420 static char *
421 extract_ifname(const char *name)
422 {
423 char *cp;
424 int len;
425 static char ifname[64];
426
427 if ((cp = strchr(name, ':')) != NULL)
428 len = cp - name;
429 else
430 len = strlen(name);
431 len = MIN(len, 63);
432 strncpy(ifname, name, len);
433 ifname[len] = '\0';
434 return (ifname);
435 }
436
437 void
438 quip_printconfig(void)
439 {
440 char buf[8192], name[256], *cp, *p, *flname;
441 int result_code, len;
442 enum nametype type;
443 u_long handle;
444
445 /* get a total list from the server */
446 quip_sendrequest(server, "GET list\n");
447
448 result_code = quip_recvresponse(server, NULL, buf, &len);
449 if (result_code != 200)
450 errx(1, "can't get total list");
451
452 printf("------------ current configuration ------------");
453
454 cp = buf;
455 while (1) {
456 if (sscanf(cp, "%lx %s", &handle, name) != 2)
457 break;
458
459 if ((p = strchr(name, ':')) == NULL)
460 type = INTERFACE;
461 else if (strchr(p+1, ':') == NULL)
462 type = CLASS;
463 else
464 type = FILTER;
465
466 switch (type) {
467 case INTERFACE:
468 printf("\ninterface: %s (index:%lu)\n",
469 name, handle);
470 quip_printqdisc(name);
471 break;
472 case CLASS:
473 printf("class: %s (handle:%#lx)\n",
474 name, handle);
475 break;
476 case FILTER:
477 flname = strrchr(name, ':') + 1;
478 printf(" filter: name:%s [", flname);
479 quip_printfilter(extract_ifname(name), handle);
480 printf("] (handle:%#lx)\n", handle);
481 break;
482 case CONDITIONER:
483 break;
484 }
485
486 if ((cp = strchr(cp+1, '\n')) == NULL)
487 break;
488 }
489 printf("-----------------------------------------------\n\n");
490 }
491
492