quip_client.c revision 1.2 1 /* $KAME: quip_client.c,v 1.3 2001/08/15 12:51:59 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 *);
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 strlcpy(addr.sun_path, QUIP_PATH,sizeof(addr.sun_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[QUIPMSG_MAXSIZE], *cp;
146 int n;
147
148 if ((cp = strstr(request, "QUIP")) == NULL) {
149 cp = strchr(request, '\n');
150 n = cp - request;
151 if (cp == NULL || n > REQ_MAXSIZE - 10)
152 return;
153 strncpy(buf, request, n);
154 n += snprintf(buf + n, REQ_MAXSIZE - n, " QUIP/1.0");
155 strlcpy(buf + n, cp, REQ_MAXSIZE - n);
156 }
157 else
158 strlcpy(buf, request, REQ_MAXSIZE);
159
160 if (fputs(buf, fp) != 0)
161 err(1, "fputs");
162 if (fflush(fp) != 0)
163 err(1, "fflush");
164 if (quip_echo) {
165 fputs("<< ", stdout);
166 fputs(buf, stdout);
167 }
168 }
169
170 /*
171 * recv_response receives a response message from the server
172 * and returns status_code.
173 */
174 int
175 quip_recvresponse(FILE *fp, char *header, char *body, int *blen)
176 {
177 char buf[QUIPMSG_MAXSIZE], version[64];
178 int code, resid;
179 int end_of_header = 0;
180
181 if (blen != NULL)
182 *blen = 0;
183 code = 0;
184 resid = 0;
185 while (fgets(buf, sizeof(buf), fp) != 0) {
186 if (quip_echo) {
187 fputs("> ", stdout);
188 fputs(buf, stdout);
189 }
190
191 if (!end_of_header) {
192 /* process message header */
193 if (header != NULL)
194 header += snprintf(header, RES_MAXSIZE, "%s", buf);
195
196 if (code == 0) {
197 /* status line expected */
198 if (buf[0] == '\n') {
199 /* ignore blank lines */
200 }
201 else if (sscanf(buf, "%s %d",
202 version, &code) != 2) {
203 /* can't get result code */
204 fpurge(fp);
205 return (-1);
206 }
207 }
208 else {
209 /* entity header expected */
210 char *field, *cp;
211
212 if (buf[0] == '\n') {
213 /* end of header */
214 end_of_header = 1;
215 if (resid == 0)
216 /* no message body */
217 return (code);
218 }
219
220 cp = buf;
221 field = strsep(&cp, ":");
222 if (strcmp(field, "Content-Length") == 0) {
223 if (sscanf(cp, "%d", &resid) != 1) {
224 fpurge(fp);
225 return (-1);
226 }
227 if (blen != NULL)
228 *blen = resid;
229 }
230 }
231 }
232 else {
233 /* process message body */
234 int len;
235
236 if (body != NULL) {
237 len = snprintf(body, BODY_MAXSIZE, "%s", buf);
238 body += len;
239 }
240 else
241 len = strlen(buf);
242 resid -= len;
243 if (resid <= 0)
244 return (code);
245 }
246 }
247 return (-1);
248 }
249
250 void
251 quip_rawmode(void)
252 {
253 char line[1024];
254 int result_code;
255
256 printf(">>>Entering the raw interactive mode to the server:\n\n");
257 if (server == NULL) {
258 printf("No server available!\n");
259 return;
260 }
261
262 while (1) {
263 printf("%% "); fflush(stdout);
264 /* read a line from stdin */
265 if (fgets(line, 1024, stdin) == NULL)
266 break;
267
268 if (line[0] == '\n') {
269 /* if a blank line, echo locally */
270 fputs(line, stdout);
271 continue;
272 }
273 if (line[0] == 'q') {
274 printf("Exit\n");
275 break;
276 }
277
278 /* send the input line to the server */
279 quip_sendrequest(server, line);
280
281 /* get a response message from the server */
282 result_code = quip_recvresponse(server, NULL, NULL, NULL);
283 }
284 }
285
286 char *
287 quip_selectinterface(char *ifname)
288 {
289 char buf[8192], *cp;
290 int result_code, len;
291 u_int if_index;
292 static char interface[64];
293
294 if (server == NULL)
295 return (ifname);
296
297 /* get an inferface list from the server */
298 quip_sendrequest(server, "GET list?*\n");
299
300 result_code = quip_recvresponse(server, NULL, buf, &len);
301 if (result_code != 200)
302 errx(1, "can't get interface list");
303
304 cp = buf;
305 while (1) {
306 if (sscanf(cp, "%x %s", &if_index, interface) != 2)
307 break;
308 if (ifname == NULL) {
309 /* if name isn't specified, return the 1st entry */
310 return (interface);
311 }
312 if (strcmp(ifname, interface) == 0)
313 /* found the matching entry */
314
315 return (interface);
316 if ((cp = strchr(cp+1, '\n')) == NULL)
317 break;
318 }
319 errx(1, "can't get interface");
320 return (NULL);
321 }
322
323 char *
324 quip_selectqdisc(char *ifname, char *qdisc_name)
325 {
326 char buf[BODY_MAXSIZE], req[REQ_MAXSIZE];
327 int result_code, len;
328 static char qdisc[64];
329
330 if (server == NULL) {
331 if (ifname == NULL || qdisc_name == NULL)
332 errx(1, "when disabling server communication,\n"
333 "specify both interface (-i) and qdisc (-q)!");
334 return (qdisc_name);
335 }
336
337 /* get qdisc info from the server */
338 snprintf(req, sizeof(req), "GET qdisc?%s\n", ifname);
339 quip_sendrequest(server, req);
340
341 result_code = quip_recvresponse(server, NULL, buf, &len);
342 if (result_code != 200)
343 errx(1, "can't get qdisc info");
344
345 if (sscanf(buf, "%s", qdisc) != 1)
346 errx(1, "can't get qdisc name");
347
348 if (qdisc_name != NULL && strcmp(qdisc, qdisc_name) != 0)
349 errx(1, "qdisc %s on %s doesn't match specified qdisc %s",
350 qdisc, ifname, qdisc_name);
351
352 return (qdisc);
353 }
354
355 void
356 quip_chandle2name(const char *ifname, u_long handle, char *name, size_t size)
357 {
358 char buf[BODY_MAXSIZE], req[REQ_MAXSIZE], *cp;
359 int result_code, len;
360
361 name[0] = '\0';
362 if (server == NULL)
363 return;
364
365 /* get class name from the server */
366 snprintf(req, sizeof(req), "GET handle-to-name?%s:%#lx\n", ifname, handle);
367 quip_sendrequest(server, req);
368
369 result_code = quip_recvresponse(server, NULL, buf, &len);
370 if (result_code != 200)
371 errx(1, "can't get class name");
372
373 if ((cp = strchr(buf, '\n')) != NULL)
374 *cp = '\0';
375 if ((cp = strrchr(buf, '/')) != NULL)
376 strlcpy(name, cp+1, size);
377 }
378
379 void
380 quip_printqdisc(const char *ifname)
381 {
382 char buf[BODY_MAXSIZE], req[REQ_MAXSIZE], *cp;
383 int result_code, len;
384
385 if (server == NULL) {
386 printf("No server available!\n");
387 return;
388 }
389
390 /* get qdisc info from the server */
391 snprintf(req, sizeof(req), "GET qdisc?%s\n", ifname);
392 quip_sendrequest(server, req);
393
394 result_code = quip_recvresponse(server, NULL, buf, &len);
395 if (result_code != 200)
396 errx(1, "can't get qdisc info");
397
398 /* replace newline by space */
399 cp = buf;
400 while ((cp = strchr(cp, '\n')) != NULL)
401 *cp = ' ';
402
403 printf(" qdisc:%s\n", buf);
404 }
405
406 void
407 quip_printfilter(const char *ifname, const u_long handle)
408 {
409 char buf[BODY_MAXSIZE], req[REQ_MAXSIZE], *cp;
410 int result_code, len;
411
412 /* get qdisc info from the server */
413 snprintf(req, sizeof(req), "GET filter?%s::%#lx\n", ifname, handle);
414 quip_sendrequest(server, req);
415
416 result_code = quip_recvresponse(server, NULL, buf, &len);
417 if (result_code != 200)
418 errx(1, "can't get filter info");
419
420 if ((cp = strchr(buf, '\n')) != NULL)
421 *cp = '\0';
422 printf("%s", buf);
423 }
424
425 static char *
426 extract_ifname(const char *name)
427 {
428 char *cp;
429 int len;
430 static char ifname[64];
431
432 if ((cp = strchr(name, ':')) != NULL)
433 len = cp - name;
434 else
435 len = strlen(name);
436 len = MIN(len, 63);
437 strncpy(ifname, name, len);
438 ifname[len] = '\0';
439 return (ifname);
440 }
441
442 void
443 quip_printconfig(void)
444 {
445 char buf[8192], name[256], *cp, *p, *flname;
446 int result_code, len;
447 enum nametype type;
448 u_long handle;
449
450 /* get a total list from the server */
451 quip_sendrequest(server, "GET list\n");
452
453 result_code = quip_recvresponse(server, NULL, buf, &len);
454 if (result_code != 200)
455 errx(1, "can't get total list");
456
457 printf("------------ current configuration ------------");
458
459 cp = buf;
460 while (1) {
461 if (sscanf(cp, "%lx %s", &handle, name) != 2)
462 break;
463
464 if ((p = strchr(name, ':')) == NULL)
465 type = INTERFACE;
466 else if (strchr(p+1, ':') == NULL)
467 type = CLASS;
468 else
469 type = FILTER;
470
471 switch (type) {
472 case INTERFACE:
473 printf("\ninterface: %s (index:%lu)\n",
474 name, handle);
475 quip_printqdisc(name);
476 break;
477 case CLASS:
478 printf("class: %s (handle:%#lx)\n",
479 name, handle);
480 break;
481 case FILTER:
482 flname = strrchr(name, ':') + 1;
483 printf(" filter: name:%s [", flname);
484 quip_printfilter(extract_ifname(name), handle);
485 printf("] (handle:%#lx)\n", handle);
486 break;
487 case CONDITIONER:
488 break;
489 }
490
491 if ((cp = strchr(cp+1, '\n')) == NULL)
492 break;
493 }
494 printf("-----------------------------------------------\n\n");
495 }
496
497