Date: Fri, 1 Aug 2014 15:17:46 +0000 (UTC) From: "Alexander V. Chernikov" <melifaro@FreeBSD.org> To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r269386 - in projects/ipfw: sbin/ipfw sys/netinet sys/netpfil/ipfw Message-ID: <201408011517.s71FHkkI044319@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: melifaro Date: Fri Aug 1 15:17:46 2014 New Revision: 269386 URL: http://svnweb.freebsd.org/changeset/base/269386 Log: * Permit limiting number of items in table. Kernel changes: * Add TEI_FLAGS_DONTADD entry flag to indicate that insert is not possible * Support given flag in all algorithms * Add "limit" field to ipfw_xtable_info * Add actual limiting code into add_table_entry() Userland changes: * Add "limit" option as "create" table sub-option. Limit modification is currently impossible. * Print human-readable errors in table enry addition/deletion code. Modified: projects/ipfw/sbin/ipfw/ipfw2.c projects/ipfw/sbin/ipfw/ipfw2.h projects/ipfw/sbin/ipfw/tables.c projects/ipfw/sys/netinet/ip_fw.h projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h projects/ipfw/sys/netpfil/ipfw/ip_fw_table_algo.c Modified: projects/ipfw/sbin/ipfw/ipfw2.c ============================================================================== --- projects/ipfw/sbin/ipfw/ipfw2.c Fri Aug 1 15:10:55 2014 (r269385) +++ projects/ipfw/sbin/ipfw/ipfw2.c Fri Aug 1 15:17:46 2014 (r269386) @@ -580,11 +580,12 @@ do_cmd(int optname, void *optval, uintpt * * Assumes op3 header is already embedded. * Calls setsockopt() with IP_FW3 as kernel-visible opcode. - * Returns 0 on success or -1 otherwise. + * Returns 0 on success or errno otherwise. */ int do_set3(int optname, ip_fw3_opheader *op3, uintptr_t optlen) { + int errno; if (co.test_only) return (0); @@ -596,7 +597,10 @@ do_set3(int optname, ip_fw3_opheader *op op3->opcode = optname; - return (setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, optlen)); + if (setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, optlen) != 0) + return (errno); + + return (0); } int Modified: projects/ipfw/sbin/ipfw/ipfw2.h ============================================================================== --- projects/ipfw/sbin/ipfw/ipfw2.h Fri Aug 1 15:10:55 2014 (r269385) +++ projects/ipfw/sbin/ipfw/ipfw2.h Fri Aug 1 15:17:46 2014 (r269386) @@ -255,8 +255,6 @@ char const *match_value(struct _s_x *p, size_t concat_tokens(char *buf, size_t bufsize, struct _s_x *table, char *delimiter); void fill_flags(struct _s_x *flags, char *p, uint8_t *set, uint8_t *clear); -void print_flags(char const *name, struct _s_x *list, uint8_t set, - uint8_t clear); void print_flags_buffer(char *buf, size_t sz, struct _s_x *list, uint8_t set); struct _ip_fw3_opheader; Modified: projects/ipfw/sbin/ipfw/tables.c ============================================================================== --- projects/ipfw/sbin/ipfw/tables.c Fri Aug 1 15:10:55 2014 (r269385) +++ projects/ipfw/sbin/ipfw/tables.c Fri Aug 1 15:17:46 2014 (r269386) @@ -251,9 +251,10 @@ table_fill_objheader(ipfw_obj_header *oh } static struct _s_x tablenewcmds[] = { - { "type", TOK_TYPE}, + { "type", TOK_TYPE }, { "valtype", TOK_VALTYPE }, { "algo", TOK_ALGO }, + { "limit", TOK_LIMIT }, { NULL, 0 } }; @@ -341,6 +342,11 @@ table_create(ipfw_obj_header *oh, int ac ac--; av++; switch (tcmd) { + case TOK_LIMIT: + NEED1("limit value required"); + xi.limit = strtol(*av, NULL, 10); + ac--; av++; + break; case TOK_TYPE: NEED1("table type required"); /* Type may have suboptions after ':' */ @@ -485,6 +491,8 @@ table_show_info(ipfw_xtable_info *i, voi printf(" valtype: %s, references: %u\n", vtype, i->refcnt); printf(" algorithm: %s\n", i->algoname); printf(" items: %u, size: %u\n", i->count, i->size); + if (i->limit > 0) + printf(" limit: %u\n", i->limit); return (0); } @@ -561,8 +569,8 @@ table_modify_record(ipfw_obj_header *oh, ipfw_obj_tentry tent; ipfw_xtable_info xi; uint8_t type, vtype; - int cmd; - char *texterr; + int cmd, error; + char *texterr, *etxt; if (ac == 0) errx(EX_USAGE, "address required"); @@ -592,14 +600,34 @@ table_modify_record(ipfw_obj_header *oh, if (ac > 0) tentry_fill_value(oh, &tent, *av, type, vtype); cmd = IP_FW_TABLE_XADD; - texterr = "setsockopt(IP_FW_TABLE_XADD)"; + texterr = "Adding record failed"; } else { cmd = IP_FW_TABLE_XDEL; - texterr = "setsockopt(IP_FW_TABLE_XDEL)"; + texterr = "Deleting record failed"; + } + + if ((error = table_do_modify_record(cmd, oh, &tent, update)) == 0) + return; + + /* Try to provide more human-readable error */ + switch (error) { + case EEXIST: + etxt = "record already exists"; + break; + case EFBIG: + etxt = "limit hit"; + break; + case ESRCH: + etxt = "table not found"; + break; + case ENOENT: + etxt = "record not found"; + break; + default: + etxt = strerror(error); } - if (table_do_modify_record(cmd, oh, &tent, update) != 0) - err(EX_OSERR, "%s", texterr); + errx(EX_OSERR, "%s: %s", texterr, etxt); } static int Modified: projects/ipfw/sys/netinet/ip_fw.h ============================================================================== --- projects/ipfw/sys/netinet/ip_fw.h Fri Aug 1 15:10:55 2014 (r269385) +++ projects/ipfw/sys/netinet/ip_fw.h Fri Aug 1 15:17:46 2014 (r269386) @@ -827,6 +827,8 @@ typedef struct _ipfw_xtable_info { 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 */ ifpw_ta_tinfo ta_info; /* additional algo stats */ Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c ============================================================================== --- projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c Fri Aug 1 15:10:55 2014 (r269385) +++ projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c Fri Aug 1 15:17:46 2014 (r269386) @@ -78,8 +78,9 @@ struct table_config { uint8_t vtype; /* format table type */ uint8_t linked; /* 1 if already linked */ uint8_t tflags; /* type flags */ - uint8_t spare; + uint8_t spare; uint32_t count; /* Number of records */ + uint32_t limit; /* Max number of records */ uint64_t flags; /* state flags */ char tablename[64]; /* table name */ struct table_algo *ta; /* Callbacks for given algo */ @@ -102,7 +103,7 @@ static struct table_config *alloc_table_ static void free_table_config(struct namedobj_instance *ni, struct table_config *tc); static int create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti, - char *aname, uint8_t tflags, uint8_t vtype); + char *aname, ipfw_xtable_info *i); static void link_table(struct ip_fw_chain *chain, struct table_config *tc); static void unlink_table(struct ip_fw_chain *chain, struct table_config *tc); static void free_table_state(void **state, void **xstate, uint8_t type); @@ -132,7 +133,6 @@ static struct table_algo *find_table_alg #define KIDX_TO_TI(ch, k) (&(((struct table_info *)(ch)->tablestate)[k])) - int add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, struct tentry_info *tei) @@ -144,6 +144,7 @@ add_table_entry(struct ip_fw_chain *ch, int error; uint32_t num; uint64_t aflags; + ipfw_xtable_info xi; char ta_buf[128]; IPFW_UH_WLOCK(ch); @@ -160,6 +161,13 @@ add_table_entry(struct ip_fw_chain *ch, return (EINVAL); } + /* Try to exit early on limit hit */ + if (tc->limit != 0 && tc->count == tc->limit && + (tei->flags & TEI_FLAGS_UPDATE) == 0) { + IPFW_UH_WUNLOCK(ch); + return (EFBIG); + } + /* Reference and unlock */ tc->no.refcnt++; ta = tc->ta; @@ -172,7 +180,10 @@ add_table_entry(struct ip_fw_chain *ch, if ((tei->flags & TEI_FLAGS_COMPAT) == 0) return (ESRCH); - error = create_table_internal(ch, ti, NULL, 0, IPFW_VTYPE_U32); + memset(&xi, 0, sizeof(xi)); + xi.vtype = IPFW_VTYPE_U32; + + error = create_table_internal(ch, ti, NULL, &xi); if (error != 0) return (error); @@ -223,6 +234,22 @@ add_table_entry(struct ip_fw_chain *ch, /* Update aflags since it can be changed after previous read */ aflags = tc->flags; + /* Check limit before adding */ + if (tc->limit != 0 && tc->count == tc->limit) { + if ((tei->flags & TEI_FLAGS_UPDATE) == 0) { + IPFW_UH_WUNLOCK(ch); + return (EFBIG); + } + + /* + * We have UPDATE flag set. + * Permit updating record (if found), + * but restrict adding new one since we've + * already hit the limit. + */ + tei->flags |= TEI_FLAGS_DONTADD; + } + /* We've got valid table in @tc. Let's add data */ kidx = tc->no.kidx; ta = tc->ta; @@ -1187,7 +1214,7 @@ ipfw_create_table(struct ip_fw_chain *ch } IPFW_UH_RUNLOCK(ch); - return (create_table_internal(ch, &ti, aname, i->tflags, i->vtype)); + return (create_table_internal(ch, &ti, aname, i)); } /* @@ -1200,7 +1227,7 @@ ipfw_create_table(struct ip_fw_chain *ch */ static int create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti, - char *aname, uint8_t tflags, uint8_t vtype) + char *aname, ipfw_xtable_info *i) { struct namedobj_instance *ni; struct table_config *tc; @@ -1212,10 +1239,13 @@ create_table_internal(struct ip_fw_chain ta = find_table_algo(CHAIN_TO_TCFG(ch), ti, aname); if (ta == NULL) return (ENOTSUP); - - if ((tc = alloc_table_config(ch, ti, ta, aname, tflags, vtype)) == NULL) + + tc = alloc_table_config(ch, ti, ta, aname, i->tflags, i->vtype); + if (tc == NULL) return (ENOMEM); + tc->limit = i->limit; + IPFW_UH_WLOCK(ch); /* Check if table has been already created */ @@ -1293,6 +1323,7 @@ export_table_info(struct ip_fw_chain *ch i->kidx = tc->no.kidx; i->refcnt = tc->no.refcnt; i->count = tc->count; + i->limit = tc->limit; i->size = tc->count * sizeof(ipfw_obj_tentry); i->size += sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info); strlcpy(i->tablename, tc->tablename, sizeof(i->tablename)); Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h ============================================================================== --- projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h Fri Aug 1 15:10:55 2014 (r269385) +++ projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h Fri Aug 1 15:17:46 2014 (r269386) @@ -58,9 +58,10 @@ struct tentry_info { uint16_t flags; /* record flags */ uint32_t value; /* value */ }; -#define TEI_FLAGS_UPDATE 0x01 /* Update record if exists */ +#define TEI_FLAGS_UPDATE 0x01 /* Add or update rec if exists */ #define TEI_FLAGS_UPDATED 0x02 /* Entry has been updated */ #define TEI_FLAGS_COMPAT 0x04 /* Called from old ABI */ +#define TEI_FLAGS_DONTADD 0x08 /* Do not create new rec */ typedef int (ta_init)(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti, char *data, uint8_t tflags); Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_table_algo.c ============================================================================== --- projects/ipfw/sys/netpfil/ipfw/ip_fw_table_algo.c Fri Aug 1 15:10:55 2014 (r269385) +++ projects/ipfw/sys/netpfil/ipfw/ip_fw_table_algo.c Fri Aug 1 15:17:46 2014 (r269386) @@ -411,22 +411,12 @@ ta_add_cidr(void *ta_state, struct table else rnh = ti->xstate; - rn = rnh->rnh_addaddr(tb->addr_ptr, tb->mask_ptr, rnh, tb->ent_ptr); - - if (rn == NULL) { + /* Search for an entry first */ + rn = rnh->rnh_lookup(tb->addr_ptr, tb->mask_ptr, rnh); + if (rn != NULL) { if ((tei->flags & TEI_FLAGS_UPDATE) == 0) return (EEXIST); /* Record already exists. Update value if we're asked to */ - rn = rnh->rnh_lookup(tb->addr_ptr, tb->mask_ptr, rnh); - if (rn == NULL) { - - /* - * Radix may have failed addition for other reasons - * like failure in mask allocation code. - */ - return (EINVAL); - } - if (tei->subtype == AF_INET) { /* IPv4. */ value = ((struct radix_cidr_entry *)tb->ent_ptr)->value; @@ -444,6 +434,15 @@ ta_add_cidr(void *ta_state, struct table return (0); } + if ((tei->flags & TEI_FLAGS_DONTADD) != 0) + return (EFBIG); + + rn = rnh->rnh_addaddr(tb->addr_ptr, tb->mask_ptr, rnh, tb->ent_ptr); + if (rn == NULL) { + /* Unknown error */ + return (EINVAL); + } + tb->ent_ptr = NULL; *pnum = 1; @@ -1167,6 +1166,8 @@ ta_add_chash(void *ta_state, struct tabl tei->flags |= TEI_FLAGS_UPDATED; *pnum = 0; } else { + if ((tei->flags & TEI_FLAGS_DONTADD) != 0) + return (EFBIG); SLIST_INSERT_HEAD(&head[hash], ent, next); tb->ent_ptr = NULL; *pnum = 1; @@ -1715,6 +1716,9 @@ ta_add_ifidx(void *ta_state, struct tabl return (0); } + if ((tei->flags & TEI_FLAGS_DONTADD) != 0) + return (EFBIG); + /* Link to internal list */ ipfw_objhash_add(icfg->ii, &ife->no); @@ -2206,6 +2210,9 @@ ta_add_numarray(void *ta_state, struct t return (0); } + if ((tei->flags & TEI_FLAGS_DONTADD) != 0) + return (EFBIG); + res = badd(&tb->na.number, &tb->na, cfg->main_ptr, cfg->used, sizeof(struct numarray), compare_numarray); @@ -2891,6 +2898,9 @@ ta_add_fhash(void *ta_state, struct tabl tei->flags |= TEI_FLAGS_UPDATED; *pnum = 0; } else { + if ((tei->flags & TEI_FLAGS_DONTADD) != 0) + return (EFBIG); + SLIST_INSERT_HEAD(&head[hash], ent, next); tb->ent_ptr = NULL; *pnum = 1;
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201408011517.s71FHkkI044319>