Use different approach for filling large datasets to userspace:
Instead of trying to allocate bing contiguous chunk of memory, use intermediate-sized (page size) buffer as sliding window reducing number of sooptcopyout() calls to perform. This reduces dump functions complexity and provides additional layer of abstraction. User-visible api consists of 2 functions: ipfw_get_sopt_space() - gets contigious amount of storage (or NULL) and ipfw_get_sopt_header() - the same, but zeroes the rest of the buffer.
This commit is contained in:
parent
9490a62716
commit
2d99a3497d
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/projects/ipfw/; revision=267954
@ -241,6 +241,15 @@ struct ip_fw_chain {
|
||||
};
|
||||
|
||||
struct sockopt; /* used by tcp_var.h */
|
||||
struct sockopt_data {
|
||||
caddr_t kbuf; /* allocated buffer */
|
||||
size_t ksize; /* given buffer size */
|
||||
size_t koff; /* data already used */
|
||||
size_t kavail; /* number of bytes available */
|
||||
size_t ktotal; /* total bytes pushed */
|
||||
struct sockopt *sopt; /* socket data */
|
||||
size_t valsize; /* original data size */
|
||||
};
|
||||
|
||||
/* Macro for working with various counters */
|
||||
#define IPFW_INC_RULE_COUNTER(_cntr, _bytes) do { \
|
||||
@ -333,6 +342,9 @@ int ipfw_ctl(struct sockopt *sopt);
|
||||
int ipfw_chk(struct ip_fw_args *args);
|
||||
void ipfw_reap_rules(struct ip_fw *head);
|
||||
|
||||
caddr_t ipfw_get_sopt_space(struct sockopt_data *sd, size_t needed);
|
||||
caddr_t ipfw_get_sopt_header(struct sockopt_data *sd, size_t needed);
|
||||
|
||||
struct namedobj_instance;
|
||||
|
||||
struct named_object {
|
||||
|
@ -88,6 +88,7 @@ static uint32_t objhash_hash_name(struct namedobj_instance *ni, uint32_t set,
|
||||
static uint32_t objhash_hash_val(struct namedobj_instance *ni, uint32_t set,
|
||||
uint32_t val);
|
||||
|
||||
static int ipfw_flush_sopt_data(struct sockopt_data *sd);
|
||||
|
||||
MALLOC_DEFINE(M_IPFW, "IpFw/IpAcct", "IpFw/IpAcct chain's");
|
||||
|
||||
@ -989,6 +990,7 @@ ipfw_getrules(struct ip_fw_chain *chain, void *buf, size_t space)
|
||||
|
||||
|
||||
#define IP_FW3_OPLENGTH(x) ((x)->sopt_valsize - sizeof(ip_fw3_opheader))
|
||||
#define IP_FW3_OPTBUF 4096 /* page-size */
|
||||
/**
|
||||
* {set|get}sockopt parser.
|
||||
*/
|
||||
@ -1002,7 +1004,8 @@ ipfw_ctl(struct sockopt *sopt)
|
||||
struct ip_fw_chain *chain;
|
||||
u_int32_t rulenum[2];
|
||||
uint32_t opt;
|
||||
char xbuf[256];
|
||||
char xbuf[128];
|
||||
struct sockopt_data sdata;
|
||||
ip_fw3_opheader *op3 = NULL;
|
||||
struct rule_check_info ci;
|
||||
|
||||
@ -1026,15 +1029,40 @@ ipfw_ctl(struct sockopt *sopt)
|
||||
|
||||
/* Save original valsize before it is altered via sooptcopyin() */
|
||||
valsize = sopt->sopt_valsize;
|
||||
memset(&sdata, 0, sizeof(sdata));
|
||||
if ((opt = sopt->sopt_name) == IP_FW3) {
|
||||
/*
|
||||
* Copy not less than sizeof(ip_fw3_opheader).
|
||||
* We hope any IP_FW3 command will fit into 128-byte buffer.
|
||||
/*
|
||||
* Fill in sockopt_data structure that may be useful for
|
||||
* IP_FW3 get requests
|
||||
*/
|
||||
if ((error = sooptcopyin(sopt, xbuf, sizeof(xbuf),
|
||||
sizeof(ip_fw3_opheader))) != 0)
|
||||
if (valsize <= sizeof(xbuf)) {
|
||||
sdata.kbuf = xbuf;
|
||||
sdata.ksize = sizeof(xbuf);
|
||||
sdata.kavail = valsize;
|
||||
} else {
|
||||
if (valsize < IP_FW3_OPTBUF)
|
||||
size = valsize;
|
||||
else
|
||||
size = IP_FW3_OPTBUF;
|
||||
|
||||
sdata.kbuf = malloc(size, M_TEMP, M_WAITOK | M_ZERO);
|
||||
sdata.ksize = size;
|
||||
sdata.kavail = size;
|
||||
}
|
||||
|
||||
sdata.sopt = sopt;
|
||||
sdata.valsize = valsize;
|
||||
|
||||
/*
|
||||
* Copy either all request (if valsize < IP_FW3_OPTBUF)
|
||||
* or first IP_FW3_OPTBUF bytes to guarantee most consumers
|
||||
* that all necessary data has been copied).
|
||||
* Anyway, copy not less than sizeof(ip_fw3_opheader).
|
||||
*/
|
||||
if ((error = sooptcopyin(sopt, sdata.kbuf, sdata.ksize,
|
||||
sizeof(ip_fw3_opheader))) != 0)
|
||||
return (error);
|
||||
op3 = (ip_fw3_opheader *)xbuf;
|
||||
op3 = (ip_fw3_opheader *)sdata.kbuf;
|
||||
opt = op3->opcode;
|
||||
}
|
||||
|
||||
@ -1205,19 +1233,19 @@ ipfw_ctl(struct sockopt *sopt)
|
||||
}
|
||||
|
||||
case IP_FW_TABLE_XINFO: /* IP_FW3 */
|
||||
error = ipfw_describe_table(chain, sopt, op3, valsize);
|
||||
error = ipfw_describe_table(chain, &sdata);
|
||||
break;
|
||||
|
||||
case IP_FW_TABLES_XGETSIZE: /* IP_FW3 */
|
||||
error = ipfw_listsize_tables(chain, sopt, op3, valsize);
|
||||
error = ipfw_listsize_tables(chain, &sdata);
|
||||
break;
|
||||
|
||||
case IP_FW_TABLES_XLIST: /* IP_FW3 */
|
||||
error = ipfw_list_tables(chain, sopt, op3, valsize);
|
||||
error = ipfw_list_tables(chain, &sdata);
|
||||
break;
|
||||
|
||||
case IP_FW_TABLE_XLIST: /* IP_FW3 */
|
||||
error = ipfw_dump_table(chain, sopt, op3, valsize);
|
||||
error = ipfw_dump_table(chain, op3, &sdata);
|
||||
break;
|
||||
|
||||
case IP_FW_TABLE_XADD: /* IP_FW3 */
|
||||
@ -1425,10 +1453,78 @@ ipfw_ctl(struct sockopt *sopt)
|
||||
error = EINVAL;
|
||||
}
|
||||
|
||||
if (op3 != NULL) {
|
||||
/* Flush state and free buffers */
|
||||
if (error == 0)
|
||||
error = ipfw_flush_sopt_data(&sdata);
|
||||
else
|
||||
ipfw_flush_sopt_data(&sdata);
|
||||
|
||||
if (sdata.kbuf != xbuf)
|
||||
free(sdata.kbuf, M_TEMP);
|
||||
}
|
||||
|
||||
return (error);
|
||||
#undef RULE_MAXSIZE
|
||||
}
|
||||
|
||||
static int
|
||||
ipfw_flush_sopt_data(struct sockopt_data *sd)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (sd->koff == 0)
|
||||
return (0);
|
||||
|
||||
if ((error = sooptcopyout(sd->sopt, sd->kbuf, sd->koff)) != 0)
|
||||
return (error);
|
||||
|
||||
memset(sd->kbuf, 0, sd->ksize);
|
||||
sd->ktotal += sd->koff;
|
||||
sd->koff = 0;
|
||||
if (sd->ktotal + sd->ksize < sd->valsize)
|
||||
sd->kavail = sd->ksize;
|
||||
else
|
||||
sd->kavail = sd->valsize - sd->ktotal;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
caddr_t
|
||||
ipfw_get_sopt_space(struct sockopt_data *sd, size_t needed)
|
||||
{
|
||||
int error;
|
||||
caddr_t addr;
|
||||
|
||||
if (sd->kavail < needed) {
|
||||
/*
|
||||
* Flush data and try another time.
|
||||
*/
|
||||
error = ipfw_flush_sopt_data(sd);
|
||||
|
||||
if (sd->kavail < needed || error != 0)
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
addr = sd->kbuf + sd->koff;
|
||||
sd->koff += needed;
|
||||
sd->kavail -= needed;
|
||||
return (addr);
|
||||
}
|
||||
|
||||
caddr_t
|
||||
ipfw_get_sopt_header(struct sockopt_data *sd, size_t needed)
|
||||
{
|
||||
caddr_t addr;
|
||||
|
||||
if ((addr = ipfw_get_sopt_space(sd, needed)) == NULL)
|
||||
return (NULL);
|
||||
|
||||
if (sd->kavail > 0)
|
||||
memset(sd->kbuf + sd->koff, 0, sd->kavail);
|
||||
|
||||
return (addr);
|
||||
}
|
||||
|
||||
#define RULE_MAXSIZE (256*sizeof(u_int32_t))
|
||||
|
||||
|
@ -102,17 +102,13 @@ static void free_table_config(struct namedobj_instance *ni,
|
||||
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);
|
||||
static int export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh);
|
||||
static int export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh,
|
||||
struct sockopt_data *sd);
|
||||
static void export_table_info(struct table_config *tc, ipfw_xtable_info *i);
|
||||
static int dump_table_xentry(void *e, void *arg);
|
||||
|
||||
static int check_buffer(size_t items, size_t item_size, size_t header,
|
||||
size_t bufsize);
|
||||
|
||||
static int ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
ip_fw3_opheader *op3, size_t valsize);
|
||||
static int ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
ip_fw3_opheader *op3, size_t valsize);
|
||||
static int ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd);
|
||||
static int ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd);
|
||||
|
||||
static struct table_algo *find_table_algo(struct tables_config *tableconf,
|
||||
struct tid_info *ti, char *name);
|
||||
@ -560,24 +556,21 @@ ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen,
|
||||
* Returns 0 on success
|
||||
*/
|
||||
int
|
||||
ipfw_listsize_tables(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
ip_fw3_opheader *op3, size_t valsize)
|
||||
ipfw_listsize_tables(struct ip_fw_chain *ch, struct sockopt_data *sd)
|
||||
{
|
||||
struct _ipfw_obj_lheader *olh;
|
||||
|
||||
if (sopt->sopt_valsize < sizeof(*olh))
|
||||
olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
|
||||
if (olh == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
olh = (struct _ipfw_obj_lheader *)op3;
|
||||
|
||||
olh->size = sizeof(*olh); /* Make export_table store needed size */
|
||||
|
||||
IPFW_UH_RLOCK(ch);
|
||||
export_tables(ch, olh);
|
||||
export_tables(ch, olh, sd);
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
|
||||
sopt->sopt_valsize = sizeof(*olh);
|
||||
return (sooptcopyout(sopt, olh, sopt->sopt_valsize));
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -589,61 +582,32 @@ ipfw_listsize_tables(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
* Returns 0 on success
|
||||
*/
|
||||
int
|
||||
ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
ip_fw3_opheader *op3, size_t valsize)
|
||||
ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt_data *sd)
|
||||
{
|
||||
struct _ipfw_obj_lheader *olh;
|
||||
uint32_t sz;
|
||||
int error;
|
||||
|
||||
if (sopt->sopt_valsize < sizeof(*olh))
|
||||
olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
|
||||
if (olh == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
olh = (struct _ipfw_obj_lheader *)op3;
|
||||
|
||||
if (valsize != olh->size)
|
||||
return (EINVAL);
|
||||
|
||||
/*
|
||||
* Check if array size is "reasonable":
|
||||
* Permit valsize between current size and
|
||||
* 2x current size + 1
|
||||
*/
|
||||
IPFW_UH_RLOCK(ch);
|
||||
sz = ipfw_objhash_count(CHAIN_TO_NI(ch));
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
|
||||
if (check_buffer(sz, sizeof(ipfw_xtable_info),
|
||||
sizeof(*olh), valsize) != 0)
|
||||
return (EINVAL);
|
||||
|
||||
olh = malloc(valsize, M_TEMP, M_ZERO | M_WAITOK);
|
||||
/* Copy header to new storage */
|
||||
memcpy(olh, op3, sizeof(*olh));
|
||||
|
||||
IPFW_UH_RLOCK(ch);
|
||||
error = export_tables(ch, olh);
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
|
||||
if (error != 0) {
|
||||
free(olh, M_TEMP);
|
||||
return (error);
|
||||
if (sd->valsize < sz) {
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
return (ENOMEM);
|
||||
}
|
||||
|
||||
/*
|
||||
* Since we call sooptcopyin() with small buffer,
|
||||
* sopt_valsize is decreased to reflect supplied
|
||||
* buffer size. Set it back to original value.
|
||||
*/
|
||||
sopt->sopt_valsize = valsize;
|
||||
error = sooptcopyout(sopt, olh, olh->size);
|
||||
free(olh, M_TEMP);
|
||||
error = export_tables(ch, olh, sd);
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
|
||||
return (0);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Store table info to buffer provided by @op3.
|
||||
* Store table info to buffer provided by @sd.
|
||||
* Data layout:
|
||||
* Request: [ ipfw_obj_header ipfw_xtable_info(empty)]
|
||||
* Reply: [ ipfw_obj_header ipfw_xtable_info ]
|
||||
@ -651,21 +615,18 @@ ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int
|
||||
ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
ip_fw3_opheader *op3, size_t valsize)
|
||||
ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt_data *sd)
|
||||
{
|
||||
struct _ipfw_obj_header *oh;
|
||||
struct table_config *tc;
|
||||
struct tid_info ti;
|
||||
size_t sz;
|
||||
int error;
|
||||
|
||||
sz = sizeof(*oh) + sizeof(ipfw_xtable_info);
|
||||
if (sopt->sopt_valsize < sz)
|
||||
oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
|
||||
if (oh == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
oh = (struct _ipfw_obj_header *)op3;
|
||||
|
||||
objheader_to_ti(oh, &ti);
|
||||
|
||||
IPFW_UH_RLOCK(ch);
|
||||
@ -677,33 +638,31 @@ ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
export_table_info(tc, (ipfw_xtable_info *)(oh + 1));
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
|
||||
error = sooptcopyout(sopt, oh, sz);
|
||||
|
||||
return (error);
|
||||
return (0);
|
||||
}
|
||||
|
||||
struct dump_args {
|
||||
struct table_info *ti;
|
||||
struct table_config *tc;
|
||||
ipfw_table_entry *ent;
|
||||
ipfw_table_xentry *xent;
|
||||
struct sockopt_data *sd;
|
||||
uint32_t cnt;
|
||||
uint32_t size;
|
||||
uint16_t uidx;
|
||||
ipfw_table_entry *ent;
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
int
|
||||
ipfw_dump_table(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
ip_fw3_opheader *op3, size_t valsize)
|
||||
ipfw_dump_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
||||
struct sockopt_data *sd)
|
||||
{
|
||||
int error;
|
||||
|
||||
switch (op3->version) {
|
||||
case 0:
|
||||
error = ipfw_dump_table_v0(ch, sopt, op3, valsize);
|
||||
error = ipfw_dump_table_v0(ch, sd);
|
||||
break;
|
||||
case 1:
|
||||
error = ipfw_dump_table_v1(ch, sopt, op3, valsize);
|
||||
error = ipfw_dump_table_v1(ch, sd);
|
||||
break;
|
||||
default:
|
||||
error = ENOTSUP;
|
||||
@ -714,15 +673,14 @@ ipfw_dump_table(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
|
||||
/*
|
||||
* Dumps all table data
|
||||
* Data layout (version 1):
|
||||
* Request: [ ipfw_obj_lheader ], size = ipfw_xtable_info.size
|
||||
* Reply: [ ipfw_obj_lheader ipfw_xtable_info ipfw_table_xentry x N ]
|
||||
* Data layout (version 1)(current):
|
||||
* Request: [ ipfw_obj_header ], size = ipfw_xtable_info.size
|
||||
* Reply: [ ipfw_obj_header ipfw_xtable_info ipfw_table_xentry x N ]
|
||||
*
|
||||
* Returns 0 on success
|
||||
*/
|
||||
static int
|
||||
ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
ip_fw3_opheader *op3, size_t valsize)
|
||||
ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd)
|
||||
{
|
||||
struct _ipfw_obj_header *oh;
|
||||
ipfw_xtable_info *i;
|
||||
@ -731,13 +689,13 @@ ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
struct table_algo *ta;
|
||||
struct dump_args da;
|
||||
uint32_t sz;
|
||||
int error;
|
||||
|
||||
if (sopt->sopt_valsize < sizeof(*oh))
|
||||
sz = sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info);
|
||||
oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
|
||||
if (oh == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
oh = (struct _ipfw_obj_header *)op3;
|
||||
|
||||
i = (ipfw_xtable_info *)(oh + 1);
|
||||
objheader_to_ti(oh, &ti);
|
||||
|
||||
IPFW_UH_RLOCK(ch);
|
||||
@ -745,32 +703,19 @@ ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
return (ESRCH);
|
||||
}
|
||||
sz = tc->count;
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
|
||||
if (check_buffer(sz, sizeof(ipfw_table_xentry),
|
||||
sizeof(ipfw_xtable_info) + sizeof(*oh), valsize) != 0)
|
||||
return (EINVAL);
|
||||
|
||||
oh = malloc(valsize, M_TEMP, M_ZERO | M_WAITOK);
|
||||
i = (ipfw_xtable_info *)(oh + 1);
|
||||
/* Copy header to new storage */
|
||||
memcpy(oh, op3, sizeof(*oh));
|
||||
|
||||
IPFW_UH_RLOCK(ch);
|
||||
/* Find table and export info */
|
||||
if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
free(oh, M_TEMP);
|
||||
return (ESRCH);
|
||||
}
|
||||
|
||||
export_table_info(tc, i);
|
||||
if (i->size > valsize) {
|
||||
sz = tc->count;
|
||||
|
||||
if (sd->valsize < sz + tc->count * sizeof(ipfw_table_xentry)) {
|
||||
|
||||
/*
|
||||
* Submitted buffer size is not enough.
|
||||
* WE've already filled in @i structure with
|
||||
* relevant table info including size, so we
|
||||
* can return. Buffer will be flushed automatically.
|
||||
*/
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
/* XXX: Should we pass size structure back ? */
|
||||
free(oh, M_TEMP);
|
||||
return (EINVAL);
|
||||
return (ENOMEM);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -779,52 +724,38 @@ ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
memset(&da, 0, sizeof(da));
|
||||
da.ti = KIDX_TO_TI(ch, tc->no.kidx);
|
||||
da.tc = tc;
|
||||
da.xent = (ipfw_table_xentry *)(i + 1);
|
||||
da.size = (valsize - sizeof(*oh) - sizeof(ipfw_xtable_info)) /
|
||||
sizeof(ipfw_table_xentry);
|
||||
da.sd = sd;
|
||||
|
||||
ta = tc->ta;
|
||||
|
||||
ta->foreach(tc->astate, da.ti, dump_table_xentry, &da);
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
|
||||
/*
|
||||
* Since we call sooptcopyin() with small buffer,
|
||||
* sopt_valsize is decreased to reflect supplied
|
||||
* buffer size. Set it back to original value.
|
||||
*/
|
||||
sopt->sopt_valsize = valsize;
|
||||
error = sooptcopyout(sopt, oh, i->size);
|
||||
free(oh, M_TEMP);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Dumps all table data
|
||||
* Data layout (version 0):
|
||||
* Data layout (version 0)(legacy):
|
||||
* Request: [ ipfw_xtable ], size = IP_FW_TABLE_XGETSIZE()
|
||||
* Reply: [ ipfw_xtable ipfw_table_xentry x N ]
|
||||
*
|
||||
* Returns 0 on success
|
||||
*/
|
||||
static int
|
||||
ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
ip_fw3_opheader *op3, size_t valsize)
|
||||
ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd)
|
||||
{
|
||||
ipfw_xtable *xtbl;
|
||||
struct tid_info ti;
|
||||
struct table_config *tc;
|
||||
struct table_algo *ta;
|
||||
struct dump_args da;
|
||||
int error;
|
||||
size_t sz;
|
||||
|
||||
if (valsize < sizeof(ipfw_xtable))
|
||||
xtbl = (ipfw_xtable *)ipfw_get_sopt_header(sd, sizeof(ipfw_xtable));
|
||||
if (xtbl == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
xtbl = (ipfw_xtable *)op3;
|
||||
|
||||
memset(&ti, 0, sizeof(ti));
|
||||
ti.set = 0; /* XXX: No way to specify set */
|
||||
ti.uidx = xtbl->tbl;
|
||||
@ -834,56 +765,37 @@ ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
return (0);
|
||||
}
|
||||
sz = tc->count;
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
|
||||
if (check_buffer(sz, sizeof(ipfw_table_xentry),
|
||||
sizeof(ipfw_xtable) - sizeof(ipfw_table_xentry), valsize) != 0)
|
||||
return (EINVAL);
|
||||
|
||||
xtbl = malloc(valsize, M_TEMP, M_ZERO | M_WAITOK);
|
||||
memcpy(xtbl, op3, sizeof(ipfw_xtable));
|
||||
|
||||
IPFW_UH_RLOCK(ch);
|
||||
if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
free(xtbl, M_TEMP);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Check size another time */
|
||||
sz = tc->count * sizeof(ipfw_table_xentry) + sizeof(ipfw_xtable);
|
||||
if (sz > valsize) {
|
||||
|
||||
xtbl->cnt = tc->count;
|
||||
xtbl->size = sz;
|
||||
xtbl->type = tc->no.type;
|
||||
xtbl->tbl = ti.uidx;
|
||||
|
||||
if (sd->valsize < sz) {
|
||||
|
||||
/*
|
||||
* Submitted buffer size is not enough.
|
||||
* WE've already filled in @i structure with
|
||||
* relevant table info including size, so we
|
||||
* can return. Buffer will be flushed automatically.
|
||||
*/
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
free(xtbl, M_TEMP);
|
||||
return (EINVAL);
|
||||
return (ENOMEM);
|
||||
}
|
||||
|
||||
/* Do the actual dump in eXtended format */
|
||||
memset(&da, 0, sizeof(da));
|
||||
da.ti = KIDX_TO_TI(ch, tc->no.kidx);
|
||||
da.tc = tc;
|
||||
da.xent = &xtbl->xent[0];
|
||||
da.size = tc->count;
|
||||
xtbl->type = tc->no.type;
|
||||
xtbl->tbl = ti.uidx;
|
||||
da.sd = sd;
|
||||
|
||||
ta = tc->ta;
|
||||
|
||||
ta->foreach(tc->astate, da.ti, dump_table_xentry, &da);
|
||||
xtbl->cnt = da.cnt;
|
||||
xtbl->size = sz;
|
||||
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
|
||||
/*
|
||||
* Since we call sooptcopyin() with small buffer, sopt_valsize is
|
||||
* decreased to reflect supplied buffer size. Set it back to original value
|
||||
*/
|
||||
sopt->sopt_valsize = valsize;
|
||||
error = sooptcopyout(sopt, xtbl, sz);
|
||||
free(xtbl, M_TEMP);
|
||||
|
||||
return (error);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -925,7 +837,7 @@ ipfw_create_table(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
|
||||
/*
|
||||
* Verify user-supplied strings.
|
||||
* Check for null-terminated/zero-lenght strings/
|
||||
* Check for null-terminated/zero-length strings/
|
||||
*/
|
||||
tname = i->tablename;
|
||||
aname = i->algoname;
|
||||
@ -978,26 +890,6 @@ ipfw_create_table(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Checks if supplied buffer size is "reasonable".
|
||||
* Permit valsize between current needed size and
|
||||
* 2x needed size + 1
|
||||
*/
|
||||
static int
|
||||
check_buffer(size_t items, size_t item_size, size_t header, size_t bufsize)
|
||||
{
|
||||
size_t sz_min, sz_max;
|
||||
|
||||
sz_min = items * item_size + header;
|
||||
sz_max = (2 * items + 1) * item_size + header;
|
||||
|
||||
if (bufsize < sz_min || bufsize > sz_max)
|
||||
return (EINVAL);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti)
|
||||
{
|
||||
@ -1029,43 +921,43 @@ static void
|
||||
export_table_internal(struct namedobj_instance *ni, struct named_object *no,
|
||||
void *arg)
|
||||
{
|
||||
ipfw_obj_lheader *olh;
|
||||
ipfw_xtable_info *i;
|
||||
struct sockopt_data *sd;
|
||||
|
||||
olh = (ipfw_obj_lheader *)arg;
|
||||
i = (ipfw_xtable_info *)(caddr_t)(olh + 1) + olh->count;
|
||||
olh->count++;
|
||||
sd = (struct sockopt_data *)arg;
|
||||
i = (ipfw_xtable_info *)ipfw_get_sopt_space(sd, sizeof(*i));
|
||||
KASSERT(i == 0, ("previously checked buffer is not enough"));
|
||||
|
||||
export_table_info((struct table_config *)no, i);
|
||||
}
|
||||
|
||||
/*
|
||||
* Export all tables as ipfw_xtable_info structures to
|
||||
* storage provided by @olh.
|
||||
* storage provided by @sd.
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
static int
|
||||
export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh)
|
||||
export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh,
|
||||
struct sockopt_data *sd)
|
||||
{
|
||||
uint32_t size;
|
||||
uint32_t count;
|
||||
|
||||
count = ipfw_objhash_count(CHAIN_TO_NI(ch));
|
||||
size = count * sizeof(ipfw_xtable_info) + sizeof(ipfw_obj_lheader);
|
||||
|
||||
/* Fill in header regadless of buffer size */
|
||||
olh->count = count;
|
||||
olh->objsize = sizeof(ipfw_xtable_info);
|
||||
|
||||
if (size > olh->size) {
|
||||
/* Store new values anyway */
|
||||
olh->count = count;
|
||||
/* Store necessary size */
|
||||
olh->size = size;
|
||||
olh->objsize = sizeof(ipfw_xtable_info);
|
||||
return (ENOMEM);
|
||||
}
|
||||
|
||||
olh->count = 0;
|
||||
ipfw_objhash_foreach(CHAIN_TO_NI(ch), export_table_internal, olh);
|
||||
|
||||
olh->count = count;
|
||||
olh->size = size;
|
||||
olh->objsize = sizeof(ipfw_xtable_info);
|
||||
|
||||
ipfw_objhash_foreach(CHAIN_TO_NI(ch), export_table_internal, sd);
|
||||
|
||||
return (0);
|
||||
}
|
||||
@ -1170,13 +1062,12 @@ dump_table_xentry(void *e, void *arg)
|
||||
tc = da->tc;
|
||||
ta = tc->ta;
|
||||
|
||||
xent = (ipfw_table_xentry *)ipfw_get_sopt_space(da->sd, sizeof(*xent));
|
||||
/* Out of memory, returning */
|
||||
if (da->cnt == da->size)
|
||||
if (xent == NULL)
|
||||
return (1);
|
||||
xent = da->xent++;
|
||||
xent->len = sizeof(ipfw_table_xentry);
|
||||
xent->tbl = da->uidx;
|
||||
da->cnt++;
|
||||
|
||||
return (ta->dump_xentry(tc->astate, da->ti, e, xent));
|
||||
}
|
||||
|
@ -91,14 +91,11 @@ void ipfw_table_algo_destroy(struct ip_fw_chain *chain);
|
||||
|
||||
|
||||
/* direct ipfw_ctl handlers */
|
||||
int ipfw_listsize_tables(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
ip_fw3_opheader *op3, size_t valsize);
|
||||
int ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
ip_fw3_opheader *op3, size_t valsize);
|
||||
int ipfw_dump_table(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
ip_fw3_opheader *op3, size_t valsize);
|
||||
int ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
ip_fw3_opheader *op3, size_t valsize);
|
||||
int ipfw_listsize_tables(struct ip_fw_chain *ch, struct sockopt_data *sd);
|
||||
int ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt_data *sd);
|
||||
int ipfw_dump_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
||||
struct sockopt_data *sd);
|
||||
int ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt_data *sd);
|
||||
|
||||
int ipfw_create_table(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
ip_fw3_opheader *op3);
|
||||
|
Loading…
Reference in New Issue
Block a user