summaryrefslogtreecommitdiffstats
path: root/sbin/ipfw/tables.c
diff options
context:
space:
mode:
authormelifaro <melifaro@FreeBSD.org>2014-06-14 22:47:25 +0000
committermelifaro <melifaro@FreeBSD.org>2014-06-14 22:47:25 +0000
commitfe9646e6ff1b3a57a7e6178adbd24217fe42d8a1 (patch)
treed694e53bf952a5685dabed4473ffb07855a9642d /sbin/ipfw/tables.c
parent0001953a3587f852c2a6b4d935071dc2523dff84 (diff)
downloadFreeBSD-src-fe9646e6ff1b3a57a7e6178adbd24217fe42d8a1.zip
FreeBSD-src-fe9646e6ff1b3a57a7e6178adbd24217fe42d8a1.tar.gz
Move further to eliminate next pieces of number-assuming code inside tables.
Kernel changes: * Add IP_FW_OBJ_FLUSH opcode (flush table based on its name/set) * Add IP_FW_OBJ_DUMP opcode (dumps table data based on its names/set) * Add IP_FW_OBJ_LISTSIZE / IP_FW_OBJ_LIST opcodes (get list of kernel tables) Userland changes: * move tables code to separate tables.c file * get rid of tables_max * switch "all"/list handling to new opcodes
Diffstat (limited to 'sbin/ipfw/tables.c')
-rw-r--r--sbin/ipfw/tables.c597
1 files changed, 597 insertions, 0 deletions
diff --git a/sbin/ipfw/tables.c b/sbin/ipfw/tables.c
new file mode 100644
index 0000000..0d16bec7
--- /dev/null
+++ b/sbin/ipfw/tables.c
@@ -0,0 +1,597 @@
+/*
+ * Copyright (c) 2002-2003 Luigi Rizzo
+ * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
+ * Copyright (c) 1994 Ugen J.S.Antsilevich
+ *
+ * Idea and grammar partially left from:
+ * Copyright (c) 1993 Daniel Boulet
+ *
+ * Redistribution and use in source forms, with and without modification,
+ * are permitted provided that this entire comment appears intact.
+ *
+ * Redistribution in binary form may occur without any restrictions.
+ * Obviously, it would be nice if you gave credit where credit is due
+ * but requiring it would be too onerous.
+ *
+ * This software is provided ``AS IS'' without any warranties of any kind.
+ *
+ * in-kernel tables support
+ *
+ * $FreeBSD: projects/ipfw/sbin/ipfw/ipfw2.c 267467 2014-06-14 10:58:39Z melifaro $
+ */
+
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stddef.h> /* offsetof */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#define IPFW_INTERNAL /* Access to protected structures in ip_fw.h. */
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h> /* def. of struct route */
+#include <netinet/in.h>
+#include <netinet/ip_fw.h>
+#include <arpa/inet.h>
+#include <alias.h>
+
+#include "ipfw2.h"
+
+static void table_list(ipfw_xtable_info *i, int need_header);
+static void table_fill_xentry(char *arg, ipfw_table_xentry *xent);
+static int table_flush(char *name, uint32_t set);
+static int table_destroy(char *name, uint32_t set);
+static int table_get_info(char *name, uint32_t set, ipfw_xtable_info *i);
+static int table_show_info(ipfw_xtable_info *i, void *arg);
+static void table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint16_t uidx);
+
+static int table_flush_one(ipfw_xtable_info *i, void *arg);
+static int table_show_one(ipfw_xtable_info *i, void *arg);
+static int table_get_list(ipfw_xtable_info *i, ipfw_obj_header *oh);
+static void table_show_list(ipfw_obj_header *oh, int need_header);
+
+typedef int (table_cb_t)(ipfw_xtable_info *i, void *arg);
+static int tables_foreach(table_cb_t *f, void *arg, int sort);
+
+#ifndef s6_addr32
+#define s6_addr32 __u6_addr.__u6_addr32
+#endif
+
+static int
+lookup_host (char *host, struct in_addr *ipaddr)
+{
+ struct hostent *he;
+
+ if (!inet_aton(host, ipaddr)) {
+ if ((he = gethostbyname(host)) == NULL)
+ return(-1);
+ *ipaddr = *(struct in_addr *)he->h_addr_list[0];
+ }
+ return(0);
+}
+
+/*
+ * This one handles all table-related commands
+ * ipfw table N add addr[/masklen] [value]
+ * ipfw table N delete addr[/masklen]
+ * ipfw table {N | all} flush
+ * ipfw table {N | all} list
+ * ipfw table {N | all} info
+ */
+void
+ipfw_table_handler(int ac, char *av[])
+{
+ ipfw_table_xentry *xent;
+ int do_add;
+ int is_all;
+ uint32_t set;
+ int error;
+ char xbuf[sizeof(ip_fw3_opheader) + sizeof(ipfw_table_xentry)];
+ ip_fw3_opheader *op3;
+ char *tablename;
+
+ memset(xbuf, 0, sizeof(xbuf));
+ op3 = (ip_fw3_opheader *)xbuf;
+ xent = (ipfw_table_xentry *)(op3 + 1);
+
+ ac--; av++;
+ tablename = *av;
+ set = 0;
+ if (ac && isdigit(**av)) {
+ xent->tbl = atoi(*av);
+ is_all = 0;
+ ac--; av++;
+ } else if (ac && _substrcmp(*av, "all") == 0) {
+ xent->tbl = 0;
+ is_all = 1;
+ ac--; av++;
+ } else
+ errx(EX_USAGE, "table number or 'all' keyword required");
+ NEED1("table needs command");
+ if (is_all && _substrcmp(*av, "list") != 0
+ && _substrcmp(*av, "info") != 0
+ && _substrcmp(*av, "flush") != 0)
+ errx(EX_USAGE, "table number required");
+
+ if (_substrcmp(*av, "add") == 0 ||
+ _substrcmp(*av, "delete") == 0) {
+ do_add = **av == 'a';
+ ac--; av++;
+ if (!ac)
+ errx(EX_USAGE, "address required");
+
+ table_fill_xentry(*av, xent);
+
+ ac--; av++;
+ if (do_add && ac) {
+ unsigned int tval;
+ /* isdigit is a bit of a hack here.. */
+ if (strchr(*av, (int)'.') == NULL && isdigit(**av)) {
+ xent->value = strtoul(*av, NULL, 0);
+ } else {
+ if (lookup_host(*av, (struct in_addr *)&tval) == 0) {
+ /* The value must be stored in host order *
+ * so that the values < 65k can be distinguished */
+ xent->value = ntohl(tval);
+ } else {
+ errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
+ }
+ }
+ } else
+ xent->value = 0;
+ if (do_set3(do_add ? IP_FW_TABLE_XADD : IP_FW_TABLE_XDEL,
+ op3, sizeof(xbuf)) < 0) {
+ /* If running silent, don't bomb out on these errors. */
+ if (!(co.do_quiet && (errno == (do_add ? EEXIST : ESRCH))))
+ err(EX_OSERR, "setsockopt(IP_FW_TABLE_%s)",
+ do_add ? "XADD" : "XDEL");
+ /* In silent mode, react to a failed add by deleting */
+ if (do_add) {
+ do_set3(IP_FW_TABLE_XDEL, op3, sizeof(xbuf));
+ if (do_set3(IP_FW_TABLE_XADD, op3, sizeof(xbuf)) < 0)
+ err(EX_OSERR,
+ "setsockopt(IP_FW_TABLE_XADD)");
+ }
+ }
+ } else if (_substrcmp(*av, "flush") == 0) {
+ if (is_all == 0) {
+ if ((error = table_flush(tablename, set)) != 0)
+ err(EX_OSERR, "failed to flush table %s info",
+ tablename);
+ } else {
+ error = tables_foreach(table_flush_one, NULL, 1);
+ if (error != 0)
+ err(EX_OSERR, "failed to flush tables list");
+ }
+ } else if (_substrcmp(*av, "list") == 0) {
+ if (is_all == 0) {
+ ipfw_xtable_info i;
+ if ((error = table_get_info(tablename, set, &i)) != 0)
+ err(EX_OSERR, "failed to request table info");
+ table_show_one(&i, NULL);
+ } else {
+ error = tables_foreach(table_show_one, NULL, 1);
+ if (error != 0)
+ err(EX_OSERR, "failed to request tables list");
+ }
+ } else if (_substrcmp(*av, "destroy") == 0) {
+ if (table_destroy(tablename, set) != 0)
+ err(EX_OSERR, "failed to destroy table %s", tablename);
+ } else if (_substrcmp(*av, "info") == 0) {
+ if (is_all == 0) {
+ ipfw_xtable_info i;
+ if ((error = table_get_info(tablename, set, &i)) != 0)
+ err(EX_OSERR, "failed to request table info");
+ table_show_info(&i, NULL);
+ } else {
+ error = tables_foreach(table_show_info, NULL, 1);
+ if (error != 0)
+ err(EX_OSERR, "failed to request tables list");
+ }
+ } else
+ errx(EX_USAGE, "invalid table command %s", *av);
+}
+
+static void
+table_fill_xentry(char *arg, ipfw_table_xentry *xent)
+{
+ int addrlen, mask, masklen, type;
+ struct in6_addr *paddr;
+ uint32_t *pkey;
+ char *p;
+ uint32_t key;
+
+ mask = 0;
+ type = 0;
+ addrlen = 0;
+ masklen = 0;
+
+ /*
+ * Let's try to guess type by agrument.
+ * Possible types:
+ * 1) IPv4[/mask]
+ * 2) IPv6[/mask]
+ * 3) interface name
+ * 4) port, uid/gid or other u32 key (base 10 format)
+ * 5) hostname
+ */
+ paddr = &xent->k.addr6;
+ if (ishexnumber(*arg) != 0 || *arg == ':') {
+ /* Remove / if exists */
+ if ((p = strchr(arg, '/')) != NULL) {
+ *p = '\0';
+ mask = atoi(p + 1);
+ }
+
+ if (inet_pton(AF_INET, arg, paddr) == 1) {
+ if (p != NULL && mask > 32)
+ errx(EX_DATAERR, "bad IPv4 mask width: %s",
+ p + 1);
+
+ type = IPFW_TABLE_CIDR;
+ masklen = p ? mask : 32;
+ addrlen = sizeof(struct in_addr);
+ } else if (inet_pton(AF_INET6, arg, paddr) == 1) {
+ if (IN6_IS_ADDR_V4COMPAT(paddr))
+ errx(EX_DATAERR,
+ "Use IPv4 instead of v4-compatible");
+ if (p != NULL && mask > 128)
+ errx(EX_DATAERR, "bad IPv6 mask width: %s",
+ p + 1);
+
+ type = IPFW_TABLE_CIDR;
+ masklen = p ? mask : 128;
+ addrlen = sizeof(struct in6_addr);
+ } else {
+ /* Port or any other key */
+ /* Skip non-base 10 entries like 'fa1' */
+ key = strtol(arg, &p, 10);
+ if (*p == '\0') {
+ pkey = (uint32_t *)paddr;
+ *pkey = htonl(key);
+ type = IPFW_TABLE_CIDR;
+ masklen = 32;
+ addrlen = sizeof(uint32_t);
+ } else if ((p != arg) && (*p == '.')) {
+ /*
+ * Warn on IPv4 address strings
+ * which are "valid" for inet_aton() but not
+ * in inet_pton().
+ *
+ * Typical examples: '10.5' or '10.0.0.05'
+ */
+ errx(EX_DATAERR,
+ "Invalid IPv4 address: %s", arg);
+ }
+ }
+ }
+
+ if (type == 0 && strchr(arg, '.') == NULL) {
+ /* Assume interface name. Copy significant data only */
+ mask = MIN(strlen(arg), IF_NAMESIZE - 1);
+ memcpy(xent->k.iface, arg, mask);
+ /* Set mask to exact match */
+ masklen = 8 * IF_NAMESIZE;
+ type = IPFW_TABLE_INTERFACE;
+ addrlen = IF_NAMESIZE;
+ }
+
+ if (type == 0) {
+ if (lookup_host(arg, (struct in_addr *)paddr) != 0)
+ errx(EX_NOHOST, "hostname ``%s'' unknown", arg);
+
+ masklen = 32;
+ type = IPFW_TABLE_CIDR;
+ addrlen = sizeof(struct in_addr);
+ }
+
+ xent->type = type;
+ xent->masklen = masklen;
+ xent->len = offsetof(ipfw_table_xentry, k) + addrlen;
+}
+
+static void
+table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint16_t uidx)
+{
+
+ ntlv->head.type = IPFW_TLV_NAME;
+ ntlv->head.length = sizeof(ipfw_obj_ntlv);
+ ntlv->idx = uidx;
+ strlcpy(ntlv->name, name, sizeof(ntlv->name));
+}
+
+static void
+table_fill_objheader(ipfw_obj_header *oh, ipfw_xtable_info *i)
+{
+
+ oh->set = i->set;
+ oh->idx = 1;
+ oh->objtype = IPFW_OBJTYPE_TABLE;
+ table_fill_ntlv(&oh->ntlv, i->tablename, 1);
+}
+
+/*
+ * Destroys given table @name in given @set.
+ * Returns 0 on success.
+ */
+static int
+table_destroy(char *name, uint32_t set)
+{
+ ipfw_obj_header oh;
+
+ memset(&oh, 0, sizeof(oh));
+ oh.idx = 1;
+ oh.objtype = IPFW_OBJTYPE_TABLE;
+ table_fill_ntlv(&oh.ntlv, name, 1);
+ if (do_set3(IP_FW_OBJ_DEL, &oh.opheader, sizeof(oh)) != 0)
+ return (-1);
+
+ return (0);
+}
+
+/*
+ * Flushes given table @name in given @set.
+ * Returns 0 on success.
+ */
+static int
+table_flush(char *name, uint32_t set)
+{
+ ipfw_obj_header oh;
+
+ memset(&oh, 0, sizeof(oh));
+ oh.idx = 1;
+ oh.objtype = IPFW_OBJTYPE_TABLE;
+ table_fill_ntlv(&oh.ntlv, name, 1);
+ if (do_set3(IP_FW_OBJ_FLUSH, &oh.opheader, sizeof(oh)) != 0)
+ return (-1);
+
+ return (0);
+}
+
+/*
+ * Retrieves info for given table @name in given @set and stores
+ * it inside @i.
+ * Returns 0 on success.
+ */
+static int
+table_get_info(char *name, uint32_t set, ipfw_xtable_info *i)
+{
+ char tbuf[sizeof(ipfw_obj_header)+sizeof(ipfw_xtable_info)];
+ ipfw_obj_header *oh;
+ size_t sz;
+
+ sz = sizeof(tbuf);
+ memset(tbuf, 0, sizeof(tbuf));
+ oh = (ipfw_obj_header *)tbuf;
+
+ i->set = set;
+ strlcpy(i->tablename, name, sizeof(i->tablename));
+
+ table_fill_objheader(oh, i);
+
+ if (do_get3(IP_FW_OBJ_INFO, &oh->opheader, &sz) < 0)
+ return (-1);
+
+ if (sz < sizeof(tbuf))
+ return (-1);
+
+ *i = *(ipfw_xtable_info *)(oh + 1);
+
+ return (0);
+}
+
+/*
+ * Prints table info struct @i in human-readable form.
+ */
+static int
+table_show_info(ipfw_xtable_info *i, void *arg)
+{
+ char *type;
+
+ printf("--- table(%s), set(%u) ---\n", i->tablename, i->set);
+ switch (i->type) {
+ case IPFW_TABLE_CIDR:
+ type = "cidr";
+ break;
+ case IPFW_TABLE_INTERFACE:
+ type = "iface";
+ break;
+ default:
+ type = "unknown";
+ }
+ printf(" type: %s, kindex: %d\n", type, i->kidx);
+ printf(" ftype: %d, algorithm: %d\n", i->ftype, i->atype);
+ printf(" references: %u\n", i->refcnt);
+ printf(" items: %u, size: %u\n", i->count, i->size);
+
+ return (0);
+}
+
+
+/*
+ * Function wrappers which can be used either
+ * as is or as foreach function parameter.
+ */
+
+static int
+table_show_one(ipfw_xtable_info *i, void *arg)
+{
+ ipfw_obj_header *oh;
+
+ if ((oh = malloc(i->size)) == NULL)
+ return (ENOMEM);
+
+ if (table_get_list(i, oh) == 0)
+ table_show_list(oh, 1);
+
+ free(oh);
+ return (0);
+}
+
+static int
+table_flush_one(ipfw_xtable_info *i, void *arg)
+{
+
+ return (table_flush(i->tablename, i->set));
+}
+
+
+/*
+ * Compare table names.
+ * Honor number comparison.
+ */
+static int
+tablename_cmp(const void *a, const void *b)
+{
+ ipfw_xtable_info *ia, *ib;
+ int la, lb;
+
+ ia = (ipfw_xtable_info *)a;
+ ib = (ipfw_xtable_info *)b;
+ la = strlen(ia->tablename);
+ lb = strlen(ib->tablename);
+
+ if (la > lb)
+ return (1);
+ else if (la < lb)
+ return (-01);
+
+ return (strcmp(ia->tablename, ib->tablename));
+}
+
+/*
+ * Retrieves table list from kernel,
+ * optionally sorts it and calls requested function for each table.
+ * Returns 0 on success.
+ */
+static int
+tables_foreach(table_cb_t *f, void *arg, int sort)
+{
+ ipfw_obj_lheader req, *olh;
+ ipfw_xtable_info *info;
+ size_t sz;
+ int i, error;
+
+ memset(&req, 0, sizeof(req));
+ sz = sizeof(req);
+
+ req.objtype = IPFW_OBJTYPE_TABLE;
+ if ((error = do_get3(IP_FW_OBJ_LISTSIZE, &req.opheader, &sz)) != 0)
+ return (errno);
+
+ sz = req.size;
+ if ((olh = calloc(1, sz)) == NULL)
+ return (ENOMEM);
+
+ olh->objtype = IPFW_OBJTYPE_TABLE;
+ olh->size = sz;
+ if ((error = do_get3(IP_FW_OBJ_LIST, &olh->opheader, &sz)) != 0) {
+ free(olh);
+ return (errno);
+ }
+
+ if (sort != 0)
+ qsort(olh + 1, olh->count, olh->objsize, tablename_cmp);
+
+ info = (ipfw_xtable_info *)(olh + 1);
+ for (i = 0; i < olh->count; i++) {
+ error = f(info, arg); /* Ignore errors for now */
+ info = (ipfw_xtable_info *)((caddr_t)info + olh->objsize);
+ }
+
+ free(olh);
+
+ return (0);
+}
+
+/*
+ * Retrieves all entries for given table @i in
+ * eXtended format, returning pointer vi @ooh.
+ *
+ * Returns 0 on success.
+ */
+static int
+table_get_list(ipfw_xtable_info *i, ipfw_obj_header *oh)
+{
+ size_t sz;
+ int error;
+
+ table_fill_objheader(oh, i);
+ sz = i->size;
+
+ if ((error = do_get3(IP_FW_OBJ_DUMP, &oh->opheader, &sz)) != 0)
+ return (errno);
+
+ return (0);
+}
+
+/*
+ * Shows all entries from @oh in human-readable format
+ */
+static void
+table_show_list(ipfw_obj_header *oh, int need_header)
+{
+ ipfw_table_xentry *xent;
+ uint32_t count, tval;
+ char tbuf[128];
+ struct in6_addr *addr6;
+ ipfw_xtable_info *i;
+
+ i = (ipfw_xtable_info *)(oh + 1);
+ xent = (ipfw_table_xentry *)(i + 1);
+
+ if (need_header)
+ printf("--- table(%s), set(%u) ---\n", i->tablename, i->set);
+
+ count = i->count;
+ while (count > 0) {
+ switch (i->type) {
+ case IPFW_TABLE_CIDR:
+ /* IPv4 or IPv6 prefixes */
+ tval = xent->value;
+ addr6 = &xent->k.addr6;
+
+
+ if ((xent->flags & IPFW_TCF_INET) != 0) {
+ /* IPv4 address */
+ inet_ntop(AF_INET, &addr6->s6_addr32[3], tbuf,
+ sizeof(tbuf));
+ } else {
+ /* IPv6 address */
+ inet_ntop(AF_INET6, addr6, tbuf, sizeof(tbuf));
+ }
+
+ if (co.do_value_as_ip) {
+ tval = htonl(tval);
+ printf("%s/%u %s\n", tbuf, xent->masklen,
+ inet_ntoa(*(struct in_addr *)&tval));
+ } else
+ printf("%s/%u %u\n", tbuf, xent->masklen, tval);
+ break;
+ case IPFW_TABLE_INTERFACE:
+ /* Interface names */
+ tval = xent->value;
+ if (co.do_value_as_ip) {
+ tval = htonl(tval);
+ printf("%s %s\n", xent->k.iface,
+ inet_ntoa(*(struct in_addr *)&tval));
+ } else
+ printf("%s %u\n", xent->k.iface, tval);
+ }
+
+ xent = (ipfw_table_xentry *)((caddr_t)xent + xent->len);
+ count--;
+ }
+}
+
OpenPOWER on IntegriCloud