From adf3b2b9d8db3843ca120e8791ce0ffb36e41c04 Mon Sep 17 00:00:00 2001 From: "Alexander V. Chernikov" Date: Fri, 8 Aug 2014 09:27:49 +0000 Subject: [PATCH] * Add IP_FW_TABLE_XMODIFY opcode * Since there seems to be lack of consensus on strict value typing, remove non-default value types. Use userland-only "value format type" to print values. Kernel changes: * Add IP_FW_XMODIFY to permit table run-time modifications. Currently we support changing limit and value format type. Userland changes: * Support IP_FW_XMODIFY opcode. * Support specifying value format type (ftype) in tablble create/modify req * Fine-print value type/value format type. --- sbin/ipfw/ipfw2.h | 2 + sbin/ipfw/tables.c | 137 ++++++++++++++++++++++++++++--- sys/netinet/ip_fw.h | 18 ++-- sys/netpfil/ipfw/ip_fw_sockopt.c | 4 + sys/netpfil/ipfw/ip_fw_table.c | 66 +++++++++++++-- sys/netpfil/ipfw/ip_fw_table.h | 2 + 6 files changed, 207 insertions(+), 22 deletions(-) diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h index 3fa1b54b5b54..2b068e7265a1 100644 --- a/sbin/ipfw/ipfw2.h +++ b/sbin/ipfw/ipfw2.h @@ -215,6 +215,7 @@ enum tokens { TOK_LIST, TOK_INFO, TOK_DETAIL, + TOK_MODIFY, TOK_FLUSH, TOK_SWAP, TOK_ADD, @@ -222,6 +223,7 @@ enum tokens { TOK_VALTYPE, TOK_ALGO, TOK_TALIST, + TOK_FTYPE, }; /* * the following macro returns an error message if we run out of diff --git a/sbin/ipfw/tables.c b/sbin/ipfw/tables.c index cfc182b3790b..9167137b9535 100644 --- a/sbin/ipfw/tables.c +++ b/sbin/ipfw/tables.c @@ -54,9 +54,11 @@ static void table_modify_record(ipfw_obj_header *oh, int ac, char *av[], static int table_flush(ipfw_obj_header *oh); static int table_destroy(ipfw_obj_header *oh); static int table_do_create(ipfw_obj_header *oh, ipfw_xtable_info *i); -static void table_create(ipfw_obj_header *oh, int ac, char *av[]); -static void table_lookup(ipfw_obj_header *oh, int ac, char *av[]); +static int table_do_modify(ipfw_obj_header *oh, ipfw_xtable_info *i); static int table_do_swap(ipfw_obj_header *oh, char *second); +static void table_create(ipfw_obj_header *oh, int ac, char *av[]); +static void table_modify(ipfw_obj_header *oh, int ac, char *av[]); +static void table_lookup(ipfw_obj_header *oh, int ac, char *av[]); static int table_swap(ipfw_obj_header *oh, char *second); static int table_get_info(ipfw_obj_header *oh, ipfw_xtable_info *i); static int table_show_info(ipfw_xtable_info *i, void *arg); @@ -90,18 +92,23 @@ static struct _s_x tabletypes[] = { }; static struct _s_x tablevaltypes[] = { - { "dscp", IPFW_VTYPE_DSCP }, - { "ip", IPFW_VTYPE_IP }, { "number", IPFW_VTYPE_U32 }, { NULL, 0 } }; +static struct _s_x tablefvaltypes[] = { + { "ip", IPFW_VFTYPE_IP }, + { "number", IPFW_VFTYPE_U32 }, + { NULL, 0 } +}; + static struct _s_x tablecmds[] = { { "add", TOK_ADD }, { "delete", TOK_DEL }, { "create", TOK_CREATE }, { "destroy", TOK_DESTROY }, { "flush", TOK_FLUSH }, + { "modify", TOK_MODIFY }, { "swap", TOK_SWAP }, { "info", TOK_INFO }, { "detail", TOK_DETAIL }, @@ -192,6 +199,10 @@ ipfw_table_handler(int ac, char *av[]) ac--; av++; table_create(&oh, ac, av); break; + case TOK_MODIFY: + ac--; av++; + table_modify(&oh, ac, av); + break; case TOK_DESTROY: if (table_destroy(&oh) != 0) err(EX_OSERR, "failed to destroy table %s", tablename); @@ -265,6 +276,7 @@ table_fill_objheader(ipfw_obj_header *oh, ipfw_xtable_info *i) static struct _s_x tablenewcmds[] = { { "type", TOK_TYPE }, + { "ftype", TOK_FTYPE }, { "valtype", TOK_VALTYPE }, { "algo", TOK_ALGO }, { "limit", TOK_LIMIT }, @@ -330,8 +342,6 @@ table_print_type(char *tbuf, size_t size, uint8_t type, uint8_t tflags) * ipfw table NAME create [ type { cidr | iface | u32 } ] * [ valtype { number | ip | dscp } ] * [ algo algoname ] - * - * Request: [ ipfw_obj_header ipfw_xtable_info ] */ static void table_create(ipfw_obj_header *oh, int ac, char *av[]) @@ -394,6 +404,18 @@ table_create(ipfw_obj_header *oh, int ac, char *av[]) errx(EX_USAGE, "Unknown value type: %s. Supported: %s", *av, tbuf); break; + case TOK_FTYPE: + NEED1("table value format type required"); + val = match_token(tablefvaltypes, *av); + if (val != -1) { + xi.vftype = val; + ac--; av++; + break; + } + concat_tokens(tbuf, sizeof(tbuf), tablefvaltypes, ", "); + errx(EX_USAGE, "Unknown format type: %s. Supported: %s", + *av, tbuf); + break; case TOK_ALGO: NEED1("table algorithm name required"); if (strlen(*av) > sizeof(xi.algoname)) @@ -430,6 +452,79 @@ table_do_create(ipfw_obj_header *oh, ipfw_xtable_info *i) return (error); } +/* + * Modifies existing table + * + * ipfw table NAME modify [ limit number ] [ ftype { number | ip } ] + */ +static void +table_modify(ipfw_obj_header *oh, int ac, char *av[]) +{ + ipfw_xtable_info xi; + int error, tcmd, val; + size_t sz; + char tbuf[128]; + + sz = sizeof(tbuf); + memset(&xi, 0, sizeof(xi)); + + /* Set some defaults to preserve compability */ + xi.type = IPFW_TABLE_CIDR; + xi.vtype = IPFW_VTYPE_U32; + + while (ac > 0) { + if ((tcmd = match_token(tablenewcmds, *av)) == -1) + errx(EX_USAGE, "unknown option: %s", *av); + ac--; av++; + + switch (tcmd) { + case TOK_LIMIT: + NEED1("limit value required"); + xi.limit = strtol(*av, NULL, 10); + xi.mflags |= IPFW_TMFLAGS_LIMIT; + ac--; av++; + break; + case TOK_FTYPE: + NEED1("table value format type required"); + val = match_token(tablefvaltypes, *av); + if (val != -1) { + xi.vftype = val; + xi.mflags |= IPFW_TMFLAGS_FTYPE; + ac--; av++; + break; + } + concat_tokens(tbuf, sizeof(tbuf), tablefvaltypes, ", "); + errx(EX_USAGE, "Unknown value type: %s. Supported: %s", + *av, tbuf); + break; + } + } + + if ((error = table_do_modify(oh, &xi)) != 0) + err(EX_OSERR, "Table modification failed"); +} + +/* + * Modifies existing table. + * + * Request: [ ipfw_obj_header ipfw_xtable_info ] + * + * Returns 0 on success. + */ +static int +table_do_modify(ipfw_obj_header *oh, ipfw_xtable_info *i) +{ + char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)]; + int error; + + memcpy(tbuf, oh, sizeof(*oh)); + memcpy(tbuf + sizeof(*oh), i, sizeof(*i)); + oh = (ipfw_obj_header *)tbuf; + + error = do_set3(IP_FW_TABLE_XMODIFY, &oh->opheader, sizeof(tbuf)); + + return (error); +} /* * Destroys given table specified by @oh->ntlv. * Returns 0 on success. @@ -584,19 +679,25 @@ table_show_tainfo(ipfw_xtable_info *i, struct ta_cldata *d, static int table_show_info(ipfw_xtable_info *i, void *arg) { - const char *vtype; + const char *vtype, *vftype; ipfw_ta_tinfo *tainfo; int afdata, afitem; struct ta_cldata d; - char ttype[64]; + char ttype[64], tvtype[64]; table_print_type(ttype, sizeof(ttype), i->type, i->tflags); if ((vtype = match_value(tablevaltypes, i->vtype)) == NULL) vtype = "unknown"; + if ((vftype = match_value(tablefvaltypes, i->vftype)) == NULL) + vftype = "unknown"; + if (strcmp(vtype, vftype) != 0) + snprintf(tvtype, sizeof(tvtype), "%s(%s)", vtype, vftype); + else + snprintf(tvtype, sizeof(tvtype), "%s", vtype); printf("--- table(%s), set(%u) ---\n", i->tablename, i->set); printf(" kindex: %d, type: %s\n", i->kidx, ttype); - printf(" valtype: %s, references: %u\n", vtype, i->refcnt); + printf(" valtype: %s, references: %u\n", tvtype, i->refcnt); printf(" algorithm: %s\n", i->algoname); printf(" items: %u, size: %u\n", i->count, i->size); if (i->limit > 0) @@ -1092,9 +1193,22 @@ static void tentry_fill_value(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *arg, uint8_t type, uint8_t vtype) { - int code; + uint32_t val; char *p; + /* Try to interpret as number first */ + tent->value = strtoul(arg, &p, 0); + if (*p == '\0') + return; + if (inet_pton(AF_INET, arg, &val) == 1) { + tent->value = ntohl(val); + return; + } + /* Try hostname */ + if (lookup_host(arg, (struct in_addr *)&tent->value) == 0) + return; + errx(EX_OSERR, "Unable to parse value %s", arg); +#if 0 switch (vtype) { case IPFW_VTYPE_U32: tent->value = strtoul(arg, &p, 0); @@ -1122,6 +1236,7 @@ tentry_fill_value(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *arg, default: errx(EX_OSERR, "Unsupported format type %d", vtype); } +#endif } /* @@ -1250,7 +1365,7 @@ table_show_entry(ipfw_xtable_info *i, ipfw_obj_tentry *tent) tval = tent->value; - if (co.do_value_as_ip) { + if (co.do_value_as_ip || i->vftype == IPFW_VFTYPE_IP) { tval = htonl(tval); inet_ntop(AF_INET, &tval, pval, sizeof(pval)); } else diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h index cfcdfd778b43..aa210c7b5634 100644 --- a/sys/netinet/ip_fw.h +++ b/sys/netinet/ip_fw.h @@ -83,7 +83,7 @@ typedef struct _ip_fw3_opheader { #define IP_FW_TABLE_XINFO 93 /* request info for one table */ #define IP_FW_TABLE_XFLUSH 94 /* flush table data */ #define IP_FW_TABLE_XCREATE 95 /* create new table */ -//#define IP_FW_TABLE_XMODIFY 96 /* modify existing table */ +#define IP_FW_TABLE_XMODIFY 96 /* modify existing table */ #define IP_FW_XGET 97 /* Retrieve configuration */ #define IP_FW_XADD 98 /* add rule */ #define IP_FW_XDEL 99 /* del rule */ @@ -686,9 +686,12 @@ struct _ipfw_dyn_rule { #define IPFW_TABLE_FLOW 4 /* Table for holding flow data */ #define IPFW_TABLE_MAXTYPE 4 /* Maximum valid number */ +/* Value types */ #define IPFW_VTYPE_U32 1 /* Skipto/tablearg integer */ -#define IPFW_VTYPE_IP 2 /* Nexthop IP address */ -#define IPFW_VTYPE_DSCP 3 /* DiffServ codepoints */ + +/* Value format types */ +#define IPFW_VFTYPE_U32 0 /* Skipto/tablearg integer */ +#define IPFW_VFTYPE_IP 1 /* Nexthop IP address */ typedef struct _ipfw_table_entry { in_addr_t addr; /* network address */ @@ -844,15 +847,16 @@ typedef struct _ipfw_ta_tinfo { typedef struct _ipfw_xtable_info { uint8_t type; /* table type (cidr,iface,..) */ uint8_t tflags; /* type flags */ - uint8_t ftype; /* table value format type */ - uint8_t vtype; /* value type */ + uint8_t vtype; /* value type (u32) */ + uint8_t vftype; /* value format type (ip,number)*/ + uint16_t mflags; /* modification flags */ + uint16_t spare; uint32_t set; /* set table is in */ uint32_t kidx; /* kernel index */ uint32_t refcnt; /* number of references */ uint32_t count; /* Number of records */ uint32_t size; /* Total size of records(export)*/ uint32_t limit; /* Max number of records */ - uint32_t spare; char tablename[64]; /* table name */ char algoname[64]; /* algorithm name */ ipfw_ta_tinfo ta_info; /* additional algo stats */ @@ -862,6 +866,8 @@ typedef struct _ipfw_xtable_info { #define IPFW_TFFLAG_SRCPORT 0x04 #define IPFW_TFFLAG_DSTPORT 0x08 #define IPFW_TFFLAG_PROTO 0x10 +#define IPFW_TMFLAGS_FTYPE 0x01 /* Change ftype field */ +#define IPFW_TMFLAGS_LIMIT 0x02 /* Change limit value */ typedef struct _ipfw_iface_info { char ifname[64]; /* interface name */ diff --git a/sys/netpfil/ipfw/ip_fw_sockopt.c b/sys/netpfil/ipfw/ip_fw_sockopt.c index 596beaf3bd27..485815c78bd5 100644 --- a/sys/netpfil/ipfw/ip_fw_sockopt.c +++ b/sys/netpfil/ipfw/ip_fw_sockopt.c @@ -2321,6 +2321,10 @@ ipfw_ctl3(struct sockopt *sopt) error = ipfw_flush_table(chain, op3, &sdata); break; + case IP_FW_TABLE_XMODIFY: + error = ipfw_modify_table(chain, op3, &sdata); + break; + case IP_FW_TABLE_XINFO: error = ipfw_describe_table(chain, &sdata); break; diff --git a/sys/netpfil/ipfw/ip_fw_table.c b/sys/netpfil/ipfw/ip_fw_table.c index a56561b5210e..f6a8855d0ae1 100644 --- a/sys/netpfil/ipfw/ip_fw_table.c +++ b/sys/netpfil/ipfw/ip_fw_table.c @@ -73,12 +73,16 @@ __FBSDID("$FreeBSD$"); */ struct table_config { struct named_object no; - uint8_t vtype; /* format table type */ - uint8_t linked; /* 1 if already linked */ + uint8_t vtype; /* value type */ + uint8_t vftype; /* value format type */ uint8_t tflags; /* type flags */ - uint8_t ochanged; /* used by set swapping */ + uint8_t spare0; uint32_t count; /* Number of records */ uint32_t limit; /* Max number of records */ + uint8_t linked; /* 1 if already linked */ + uint8_t ochanged; /* used by set swapping */ + uint16_t spare1; + uint32_t spare2; uint32_t ocount; /* used by set swapping */ uint64_t gencnt; /* generation count */ char tablename[64]; /* table name */ @@ -168,7 +172,7 @@ add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, } /* Try to exit early on limit hit */ - if (tc->limit != 0 && tc->count == tc->limit && + if (tc->limit != 0 && tc->count >= tc->limit && (tei->flags & TEI_FLAGS_UPDATE) == 0) { IPFW_UH_WUNLOCK(ch); return (EFBIG); @@ -239,7 +243,7 @@ add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, tc->no.refcnt--; /* Check limit before adding */ - if (tc->limit != 0 && tc->count == tc->limit) { + if (tc->limit != 0 && tc->count >= tc->limit) { if ((tei->flags & TEI_FLAGS_UPDATE) == 0) { IPFW_UH_WUNLOCK(ch); ta->flush_entry(ch, tei, &ta_buf); @@ -1334,6 +1338,56 @@ ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd) return (0); } +/* + * Modifies existing table. + * Data layout (v0)(current): + * Request: [ ipfw_obj_header ipfw_xtable_info ] + * + * Returns 0 on success + */ +int +ipfw_modify_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, + struct sockopt_data *sd) +{ + struct _ipfw_obj_header *oh; + ipfw_xtable_info *i; + char *tname; + struct tid_info ti; + struct namedobj_instance *ni; + struct table_config *tc; + + if (sd->valsize != sizeof(*oh) + sizeof(ipfw_xtable_info)) + return (EINVAL); + + oh = (struct _ipfw_obj_header *)sd->kbuf; + i = (ipfw_xtable_info *)(oh + 1); + + /* + * Verify user-supplied strings. + * Check for null-terminated/zero-length strings/ + */ + tname = oh->ntlv.name; + if (ipfw_check_table_name(tname) != 0) + return (EINVAL); + + objheader_to_ti(oh, &ti); + ti.type = i->type; + + IPFW_UH_WLOCK(ch); + ni = CHAIN_TO_NI(ch); + if ((tc = find_table(ni, &ti)) == NULL) { + IPFW_UH_WUNLOCK(ch); + return (ESRCH); + } + if ((i->mflags & IPFW_TMFLAGS_FTYPE) != 0) + tc->vftype = i->vftype; + if ((i->mflags & IPFW_TMFLAGS_LIMIT) != 0) + tc->limit = i->limit; + IPFW_UH_WUNLOCK(ch); + + return (0); +} + /* * Creates new table. * Data layout (v0)(current): @@ -1415,6 +1469,7 @@ create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti, if (tc == NULL) return (ENOMEM); + tc->vftype = i->vftype; tc->limit = i->limit; IPFW_UH_WLOCK(ch); @@ -1498,6 +1553,7 @@ export_table_info(struct ip_fw_chain *ch, struct table_config *tc, i->type = tc->no.type; i->tflags = tc->tflags; i->vtype = tc->vtype; + i->vftype = tc->vftype; i->set = tc->no.set; i->kidx = tc->no.kidx; i->refcnt = tc->no.refcnt; diff --git a/sys/netpfil/ipfw/ip_fw_table.h b/sys/netpfil/ipfw/ip_fw_table.h index 5bc5ff8f87ca..4e4d37e2bc66 100644 --- a/sys/netpfil/ipfw/ip_fw_table.h +++ b/sys/netpfil/ipfw/ip_fw_table.h @@ -146,6 +146,8 @@ int ipfw_find_table_entry(struct ip_fw_chain *ch, ip_fw3_opheader *op3, struct sockopt_data *sd); int ipfw_create_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, struct sockopt_data *sd); +int ipfw_modify_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, + struct sockopt_data *sd); int ipfw_manage_table_ent(struct ip_fw_chain *ch, ip_fw3_opheader *op3, struct sockopt_data *sd); int ipfw_flush_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,