summaryrefslogtreecommitdiff
path: root/usr.sbin/altq/altqstat/quip_client.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/altq/altqstat/quip_client.c')
-rw-r--r--usr.sbin/altq/altqstat/quip_client.c492
1 files changed, 492 insertions, 0 deletions
diff --git a/usr.sbin/altq/altqstat/quip_client.c b/usr.sbin/altq/altqstat/quip_client.c
new file mode 100644
index 00000000000..10d4cd16271
--- /dev/null
+++ b/usr.sbin/altq/altqstat/quip_client.c
@@ -0,0 +1,492 @@
+/* $OpenBSD: quip_client.c,v 1.1 2001/06/27 18:23:22 kjc Exp $ */
+/* $KAME: quip_client.c,v 1.2 2000/10/18 09:15:17 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <err.h>
+
+#include "quip_client.h"
+#include "altqstat.h"
+
+/*
+ * quip (queue information protocol) is a http-like protocol
+ * in order to retrieve information from the server.
+ * a unix domain TCP socket "/var/run/altq_quip" is used for
+ * clinet-server style communication.
+ *
+ * there are 2 quip message types: request and response.
+ * request format: (only single-line request message is used at this moment)
+ * request-line
+ *
+ * request-line = <method> <operation>[?<query>] <quip-version>
+ * <method> = GET (only GET is defined at this moment)
+ * <operation> = list | handle-to-name | qdisc | filter
+ * query format is operation dependent but most query takes
+ * <interface> or <class> or <filter>.
+ * <interface> = <if_name>
+ * <class> = <if_name>:<class_path>/<class_name>
+ * <filter> = <if_name>:<class_path>/<class_name>:<filter_name>
+ * "list" operation accepts "*" as a wildcard.
+ *
+ * response format:
+ * status-line
+ * response-headers (0 or more)
+ * <blank line>
+ * body
+ *
+ * status-line = <quip-version> <status-code> <reason phrase>
+ * response-header = Content-Length:<value>
+ *
+ * "Content-Length" specifies the length of the message body.
+ *
+ * example:
+ * to retrieve a list of classes (handle and name) on interface "fxp0":
+ * a request message looks like,
+ * GET list?fxp0:* QUIP/1.0<cr>
+ * a response message looks like,
+ * QUIP/1.0 200 OK<cr>
+ * Content-Length:86<cr>
+ * <cr>
+ * 0000000000 fxp0:/root<cr>
+ * 0xc0d1be00 fxp0:/root/parent<cr>
+ * 0xc0d1ba00 fxp0:/root/parent/child<cr>
+ *
+ * other examples:
+ * list all interfaces, classes, and filters:
+ * GET list QUIP/1.0<cr>
+ * list all interfaces:
+ * GET list?* QUIP/1.0<cr>
+ * list all classes:
+ * GET list?*:* QUIP/1.0<cr>
+ * list all filters:
+ * GET list?*:*:* QUIP/1.0<cr>
+ * convert class handle to class name:
+ * GET handle-to-name?fxp0:0xc0d1be00 QUIP/1.0<cr>
+ * convert filter handle to filter name:
+ * GET handle-to-name?fxp0::0x1000000a QUIP/1.0<cr>
+ */
+
+enum nametype { INTERFACE, CLASS, FILTER, CONDITIONER };
+
+static FILE *server = NULL;
+int quip_echo = 0;
+
+static char *extract_ifname(const char *name);
+
+int
+quip_openserver(void)
+{
+ struct sockaddr_un addr;
+ int fd;
+
+ if ((fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
+ err(1, "can't open socket");
+
+ bzero(&addr, sizeof(addr));
+ addr.sun_family = AF_LOCAL;
+ strcpy(addr.sun_path, QUIP_PATH);
+
+ if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ fprintf(stderr, "can't talk to altqd!\n"
+ "probably, altqd is not running\n");
+ return (-1);
+ }
+
+ if ((server = fdopen(fd, "r+")) == NULL) {
+ warn("fdopen: can't open stream to the quip server");
+ return (-1);
+ }
+ return (0);
+}
+
+int
+quip_closeserver(void)
+{
+ if (server != NULL)
+ return fclose(server);
+ return (0);
+}
+
+void
+quip_sendrequest(FILE *fp, const char *request)
+{
+ char buf[1024], *cp;
+ int n;
+
+ if ((cp = strstr(request, "QUIP")) == NULL) {
+ cp = strchr(request, '\n');
+ n = cp - request;
+ strncpy(buf, request, n);
+ n += sprintf(buf + n, " QUIP/1.0");
+ strcpy(buf + n, cp);
+ }
+ else
+ strcpy(buf, request);
+
+ if (fputs(buf, fp) != 0)
+ err(1, "fputs");
+ if (fflush(fp) != 0)
+ err(1, "fflush");
+ if (quip_echo) {
+ fputs("<< ", stdout);
+ fputs(buf, stdout);
+ }
+}
+
+/*
+ * recv_response receives a response message from the server
+ * and returns status_code.
+ */
+int
+quip_recvresponse(FILE *fp, char *header, char *body, int *blen)
+{
+ char buf[1024], version[64];
+ int code, resid;
+ int end_of_header = 0;
+
+ if (blen != NULL)
+ *blen = 0;
+ code = 0;
+ resid = 0;
+ while (fgets(buf, 1024, fp) != 0) {
+ if (quip_echo) {
+ fputs("> ", stdout);
+ fputs(buf, stdout);
+ }
+
+ if (!end_of_header) {
+ /* process message header */
+ if (header != NULL)
+ header += sprintf(header, "%s", buf);
+
+ if (code == 0) {
+ /* status line expected */
+ if (buf[0] == '\n') {
+ /* ignore blank lines */
+ }
+ else if (sscanf(buf, "%s %d",
+ version, &code) != 2) {
+ /* can't get result code */
+ fpurge(fp);
+ return (-1);
+ }
+ }
+ else {
+ /* entity header expected */
+ char *field, *cp;
+
+ if (buf[0] == '\n') {
+ /* end of header */
+ end_of_header = 1;
+ if (resid == 0)
+ /* no message body */
+ return (code);
+ }
+
+ cp = buf;
+ field = strsep(&cp, ":");
+ if (strcmp(field, "Content-Length") == 0) {
+ sscanf(cp, "%d", &resid);
+ if (blen != NULL)
+ *blen = resid;
+ }
+ }
+ }
+ else {
+ /* process message body */
+ int len;
+
+ if (body != NULL) {
+ len = sprintf(body, "%s", buf);
+ body += len;
+ }
+ else
+ len = strlen(buf);
+ resid -= len;
+ if (resid <= 0)
+ return (code);
+ }
+ }
+ return (-1);
+}
+
+void
+quip_rawmode(void)
+{
+ char line[1024];
+ int result_code;
+
+ printf(">>>Entering the raw interactive mode to the server:\n\n");
+ if (server == NULL) {
+ printf("No server available!\n");
+ return;
+ }
+
+ while (1) {
+ printf("%% "); fflush(stdout);
+ /* read a line from stdin */
+ if (fgets(line, 1024, stdin) == NULL)
+ break;
+
+ if (line[0] == '\n') {
+ /* if a blank line, echo locally */
+ fputs(line, stdout);
+ continue;
+ }
+ if (line[0] == 'q') {
+ printf("Exit\n");
+ break;
+ }
+
+ /* send the input line to the server */
+ quip_sendrequest(server, line);
+
+ /* get a response message from the server */
+ result_code = quip_recvresponse(server, NULL, NULL, NULL);
+ }
+}
+
+char *
+quip_selectinterface(char *ifname)
+{
+ char buf[8192], *cp;
+ int result_code, len;
+ u_int if_index;
+ static char interface[64];
+
+ if (server == NULL)
+ return (ifname);
+
+ /* get an inferface list from the server */
+ quip_sendrequest(server, "GET list?*\n");
+
+ result_code = quip_recvresponse(server, NULL, buf, &len);
+ if (result_code != 200)
+ errx(1, "can't get interface list");
+
+ cp = buf;
+ while (1) {
+ if (sscanf(cp, "%x %s", &if_index, interface) != 2)
+ break;
+ if (ifname == NULL) {
+ /* if name isn't specified, return the 1st entry */
+ return (interface);
+ }
+ if (strcmp(ifname, interface) == 0)
+ /* found the matching entry */
+
+ return (interface);
+ if ((cp = strchr(cp+1, '\n')) == NULL)
+ break;
+ }
+ errx(1, "can't get interface");
+ return (NULL);
+}
+
+char *
+quip_selectqdisc(char *ifname, char *qdisc_name)
+{
+ char buf[8192], req[256];
+ int result_code, len;
+ static char qdisc[64];
+
+ if (server == NULL) {
+ if (ifname == NULL || qdisc_name == NULL)
+ errx(1, "when disabling server communication,\n"
+ "specify both interface (-i) and qdisc (-q)!");
+ return (qdisc_name);
+ }
+
+ /* get qdisc info from the server */
+ sprintf(req, "GET qdisc?%s\n", ifname);
+ quip_sendrequest(server, req);
+
+ result_code = quip_recvresponse(server, NULL, buf, &len);
+ if (result_code != 200)
+ errx(1, "can't get qdisc info");
+
+ if (sscanf(buf, "%s", qdisc) != 1)
+ errx(1, "can't get qdisc name");
+
+ if (qdisc_name != NULL && strcmp(qdisc, qdisc_name) != 0)
+ errx(1, "qdisc %s on %s doesn't match specified qdisc %s",
+ qdisc, ifname, qdisc_name);
+
+ return (qdisc);
+}
+
+void
+quip_chandle2name(const char *ifname, u_long handle, char *name)
+{
+ char buf[8192], req[256], *cp;
+ int result_code, len;
+
+ name[0] = '\0';
+ if (server == NULL)
+ return;
+
+ /* get class name from the server */
+ sprintf(req, "GET handle-to-name?%s:%#lx\n", ifname, handle);
+ quip_sendrequest(server, req);
+
+ result_code = quip_recvresponse(server, NULL, buf, &len);
+ if (result_code != 200)
+ errx(1, "can't get class name");
+
+ if ((cp = strchr(buf, '\n')) != NULL)
+ *cp = '\0';
+ if ((cp = strrchr(buf, '/')) != NULL)
+ strcpy(name, cp+1);
+}
+
+void
+quip_printqdisc(const char *ifname)
+{
+ char buf[8192], req[256], *cp;
+ int result_code, len;
+
+ if (server == NULL) {
+ printf("No server available!\n");
+ return;
+ }
+
+ /* get qdisc info from the server */
+ sprintf(req, "GET qdisc?%s\n", ifname);
+ quip_sendrequest(server, req);
+
+ result_code = quip_recvresponse(server, NULL, buf, &len);
+ if (result_code != 200)
+ errx(1, "can't get qdisc info");
+
+ /* replace newline by space */
+ cp = buf;
+ while ((cp = strchr(cp, '\n')) != NULL)
+ *cp = ' ';
+
+ printf(" qdisc:%s\n", buf);
+}
+
+void
+quip_printfilter(const char *ifname, const u_long handle)
+{
+ char buf[8192], req[256], *cp;
+ int result_code, len;
+
+ /* get qdisc info from the server */
+ sprintf(req, "GET filter?%s::%#lx\n", ifname, handle);
+ quip_sendrequest(server, req);
+
+ result_code = quip_recvresponse(server, NULL, buf, &len);
+ if (result_code != 200)
+ errx(1, "can't get filter info");
+
+ if ((cp = strchr(buf, '\n')) != NULL)
+ *cp = '\0';
+ printf("%s", buf);
+}
+
+static char *
+extract_ifname(const char *name)
+{
+ char *cp;
+ int len;
+ static char ifname[64];
+
+ if ((cp = strchr(name, ':')) != NULL)
+ len = cp - name;
+ else
+ len = strlen(name);
+ len = MIN(len, 63);
+ strncpy(ifname, name, len);
+ ifname[len] = '\0';
+ return (ifname);
+}
+
+void
+quip_printconfig(void)
+{
+ char buf[8192], name[256], *cp, *p, *flname;
+ int result_code, len;
+ enum nametype type;
+ u_long handle;
+
+ /* get a total list from the server */
+ quip_sendrequest(server, "GET list\n");
+
+ result_code = quip_recvresponse(server, NULL, buf, &len);
+ if (result_code != 200)
+ errx(1, "can't get total list");
+
+ printf("------------ current configuration ------------");
+
+ cp = buf;
+ while (1) {
+ if (sscanf(cp, "%lx %s", &handle, name) != 2)
+ break;
+
+ if ((p = strchr(name, ':')) == NULL)
+ type = INTERFACE;
+ else if (strchr(p+1, ':') == NULL)
+ type = CLASS;
+ else
+ type = FILTER;
+
+ switch (type) {
+ case INTERFACE:
+ printf("\ninterface: %s (index:%lu)\n",
+ name, handle);
+ quip_printqdisc(name);
+ break;
+ case CLASS:
+ printf("class: %s (handle:%#lx)\n",
+ name, handle);
+ break;
+ case FILTER:
+ flname = strrchr(name, ':') + 1;
+ printf(" filter: name:%s [", flname);
+ quip_printfilter(extract_ifname(name), handle);
+ printf("] (handle:%#lx)\n", handle);
+ break;
+ case CONDITIONER:
+ break;
+ }
+
+ if ((cp = strchr(cp+1, '\n')) == NULL)
+ break;
+ }
+ printf("-----------------------------------------------\n\n");
+}
+