* Prepare to pass other dynamic states via ipfw_dump_config()

Kernel changes:
* Change dump format for dynamic states:
  each state is now stored inside ipfw_obj_dyntlv
  last dynamic state is indicated by IPFW_DF_LAST flag
* Do not perform sooptcopyout() for !SOPT_GET requests.

Userland changes:
* Introduce foreach_state() function handler to ease work
  with different states passed by ipfw_dump_config().
This commit is contained in:
melifaro 2014-07-06 23:26:34 +00:00
parent 0eba52a18e
commit 7189aec01e
6 changed files with 169 additions and 79 deletions

View File

@ -64,6 +64,7 @@ struct format_opts {
uint32_t flags; /* request flags */
uint32_t first; /* first rule to request */
uint32_t last; /* last rule to request */
uint32_t dcnt; /* number of dynamic states */
ipfw_obj_ctlv *tstate; /* table state data */
};
@ -2161,13 +2162,85 @@ ipfw_sysctl_handler(char *av[], int which)
}
}
typedef void state_cb(struct cmdline_opts *co, struct format_opts *fo,
void *arg, void *state);
static void
prepare_format_dyn(struct cmdline_opts *co, struct format_opts *fo,
void *arg, void *_state)
{
ipfw_dyn_rule *d;
int width;
uint8_t set;
d = (ipfw_dyn_rule *)_state;
/* Count _ALL_ states */
fo->dcnt++;
if (co->use_set) {
/* skip states from another set */
bcopy((char *)&d->rule + sizeof(uint16_t), &set,
sizeof(uint8_t));
if (set != co->use_set - 1)
return;
}
width = pr_u64(NULL, &d->pcnt, 0);
if (width > fo->pcwidth)
fo->pcwidth = width;
width = pr_u64(NULL, &d->bcnt, 0);
if (width > fo->bcwidth)
fo->bcwidth = width;
}
static int
foreach_state(struct cmdline_opts *co, struct format_opts *fo,
caddr_t base, size_t sz, state_cb dyn_bc, void *dyn_arg)
{
int ttype;
state_cb *fptr;
void *farg;
ipfw_obj_tlv *tlv;
ipfw_obj_ctlv *ctlv;
fptr = NULL;
ttype = 0;
while (sz > 0) {
ctlv = (ipfw_obj_ctlv *)base;
switch (ctlv->head.type) {
case IPFW_TLV_DYNSTATE_LIST:
base += sizeof(*ctlv);
sz -= sizeof(*ctlv);
ttype = IPFW_TLV_DYN_ENT;
fptr = dyn_bc;
farg = dyn_arg;
break;
default:
return (sz);
}
while (sz > 0) {
tlv = (ipfw_obj_tlv *)base;
if (tlv->type != ttype)
break;
fptr(co, fo, farg, tlv + 1);
sz -= tlv->length;
base += tlv->length;
}
}
return (sz);
}
static void
prepare_format_opts(struct cmdline_opts *co, struct format_opts *fo,
struct ip_fw *r, ipfw_dyn_rule *d, int rcnt, int dcnt)
struct ip_fw *r, int rcnt, caddr_t base, size_t sz)
{
int bcwidth, pcwidth, width;
int n;
uint32_t set;
#define NEXT(r) ((struct ip_fw *)((char *)r + RULESIZE(r)))
bcwidth = 0;
@ -2189,31 +2262,16 @@ prepare_format_opts(struct cmdline_opts *co, struct format_opts *fo,
bcwidth = width;
}
}
if (co->do_dynamic && dcnt > 0) {
for (n = 0; n < dcnt; n++, d++) {
if (co->use_set) {
/* skip rules from another set */
bcopy((char *)&d->rule + sizeof(uint16_t),
&set, sizeof(uint8_t));
if (set != co->use_set - 1)
continue;
}
width = pr_u64(NULL, &d->pcnt, 0);
if (width > pcwidth)
pcwidth = width;
width = pr_u64(NULL, &d->bcnt, 0);
if (width > bcwidth)
bcwidth = width;
}
}
fo->bcwidth = bcwidth;
fo->pcwidth = pcwidth;
fo->dcnt = 0;
if (co->do_dynamic && sz > 0)
sz = foreach_state(co, fo, base, sz, prepare_format_dyn, NULL);
}
static int
ipfw_list_static_range(struct cmdline_opts *co, struct format_opts *fo,
list_static_range(struct cmdline_opts *co, struct format_opts *fo,
struct buf_pr *bp, struct ip_fw *r, int rcnt)
{
int n, seen;
@ -2234,33 +2292,40 @@ ipfw_list_static_range(struct cmdline_opts *co, struct format_opts *fo,
}
static void
ipfw_list_dyn_range(struct cmdline_opts *co, struct format_opts *fo,
struct buf_pr *bp, ipfw_dyn_rule *d, int dcnt, int objsize)
list_dyn_state(struct cmdline_opts *co, struct format_opts *fo,
void *_arg, void *_state)
{
int n;
uint8_t set;
uint16_t rulenum;
uint8_t set;
ipfw_dyn_rule *d;
struct buf_pr *bp;
for (n = 0; n < dcnt; n++) {
bcopy(&d->rule, &rulenum, sizeof(rulenum));
if (rulenum > fo->last)
break;
if (co->use_set) {
bcopy((char *)&d->rule + sizeof(uint16_t),
&set, sizeof(uint8_t));
if (set != co->use_set - 1) {
d = (ipfw_dyn_rule *)((caddr_t)d + objsize);
continue;
}
}
if (rulenum >= fo->first) {
show_dyn_state(co, fo, bp, d);
printf("%s\n", bp->buf);
bp_flush(bp);
}
d = (ipfw_dyn_rule *)_state;
bp = (struct buf_pr *)_arg;
d = (ipfw_dyn_rule *)((caddr_t)d + objsize);
bcopy(&d->rule, &rulenum, sizeof(rulenum));
if (rulenum > fo->last)
return;
if (co->use_set) {
bcopy((char *)&d->rule + sizeof(uint16_t),
&set, sizeof(uint8_t));
if (set != co->use_set - 1)
return;
}
if (rulenum >= fo->first) {
show_dyn_state(co, fo, bp, d);
printf("%s\n", bp->buf);
bp_flush(bp);
}
}
static int
list_dyn_range(struct cmdline_opts *co, struct format_opts *fo,
struct buf_pr *bp, caddr_t base, size_t sz)
{
sz = foreach_state(co, fo, base, sz, list_dyn_state, bp);
return (sz);
}
void
@ -2325,13 +2390,14 @@ ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo,
ipfw_cfg_lheader *cfg, size_t sz, int ac, char *av[])
{
struct ip_fw *rbase;
ipfw_dyn_rule *dynbase;
int rcnt, dcnt;
caddr_t dynbase;
size_t dynsz;
int rcnt;
int exitval = EX_OK;
int lac;
char **lav;
char *endptr;
size_t dobjsz, read;
size_t read;
struct buf_pr bp;
ipfw_obj_ctlv *ctlv, *tstate;
@ -2341,7 +2407,7 @@ ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo,
tstate = NULL;
rbase = NULL;
dynbase = NULL;
dobjsz = 0;
dynsz = 0;
read = 0;
ctlv = (ipfw_obj_ctlv *)(cfg + 1);
@ -2365,26 +2431,23 @@ ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo,
}
if ((cfg->flags & IPFW_CFG_GET_STATES) && (read != sz)) {
/* We may have some dynamic rules */
read += sizeof(ipfw_obj_ctlv);
dobjsz = ctlv->objsize;
dcnt = (sz - read) / dobjsz;
if (dcnt != 0)
dynbase = (ipfw_dyn_rule *)(ctlv + 1);
/* We may have some dynamic states */
dynbase = (caddr_t)ctlv;
dynsz = sz - read;
}
prepare_format_opts(co, fo, rbase, dynbase, rcnt, dcnt);
prepare_format_opts(co, fo, rbase, rcnt, dynbase, dynsz);
bp_alloc(&bp, 4096);
/* if no rule numbers were specified, list all rules */
if (ac == 0) {
fo->first = 0;
fo->last = IPFW_DEFAULT_RULE;
ipfw_list_static_range(co, fo, &bp, rbase, rcnt);
list_static_range(co, fo, &bp, rbase, rcnt);
if (co->do_dynamic && dcnt) {
printf("## Dynamic rules (%d):\n", dcnt);
ipfw_list_dyn_range(co, fo, &bp, dynbase, dcnt, dobjsz);
if (co->do_dynamic && dynsz > 0) {
printf("## Dynamic rules (%d):\n", fo->dcnt);
list_dyn_range(co, fo, &bp, dynbase, dynsz);
}
bp_free(&bp);
@ -2403,7 +2466,7 @@ ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo,
continue;
}
if (ipfw_list_static_range(co, fo, &bp, rbase, rcnt) == 0) {
if (list_static_range(co, fo, &bp, rbase, rcnt) == 0) {
/* give precedence to other error(s) */
if (exitval == EX_OK)
exitval = EX_UNAVAILABLE;
@ -2415,7 +2478,7 @@ ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo,
}
}
if (co->do_dynamic && dcnt > 0) {
if (co->do_dynamic && dynsz > 0) {
printf("## Dynamic rules:\n");
for (lac = ac, lav = av; lac != 0; lac--) {
fo->last = fo->first = strtoul(*lav++, &endptr, 10);
@ -2424,7 +2487,7 @@ ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo,
if (*endptr)
/* already warned */
continue;
ipfw_list_dyn_range(co, fo, &bp, dynbase, dcnt, dobjsz);
list_dyn_range(co, fo, &bp, dynbase, dynsz);
}
}

View File

@ -695,8 +695,9 @@ typedef struct _ipfw_obj_tlv {
#define IPFW_TLV_TBL_NAME 1
#define IPFW_TLV_TBLNAME_LIST 2
#define IPFW_TLV_RULE_LIST 3
#define IPFW_TLV_STATE_LIST 4
#define IPFW_TLV_DYNSTATE_LIST 4
#define IPFW_TLV_TBL_ENT 5
#define IPFW_TLV_DYN_ENT 6
/* Object name TLV */
typedef struct _ipfw_obj_ntlv {
@ -726,6 +727,12 @@ typedef struct _ipfw_obj_tentry {
} ipfw_obj_tentry;
#define IPFW_TF_UPDATE 0x01 /* Update record if exists */
typedef struct _ipfw_obj_dyntlv {
ipfw_obj_tlv head;
ipfw_dyn_rule state;
} ipfw_obj_dyntlv;
#define IPFW_DF_LAST 0x01 /* Last state in chain */
/* Containter TLVs */
typedef struct _ipfw_obj_ctlv {
ipfw_obj_tlv head; /* TLV header */

View File

@ -1444,7 +1444,7 @@ sysctl_ipfw_dyn_count(SYSCTL_HANDLER_ARGS)
#endif
/*
* Returns number of dynamic rules.
* Returns size of dynamic states in legacy format
*/
int
ipfw_dyn_len(void)
@ -1454,6 +1454,17 @@ ipfw_dyn_len(void)
(DYN_COUNT * sizeof(ipfw_dyn_rule));
}
/*
* Returns number of dynamic states.
* Used by dump format v1 (current).
*/
int
ipfw_dyn_get_count(void)
{
return (V_ipfw_dyn_v == NULL) ? 0 : DYN_COUNT;
}
static void
export_dyn_rule(ipfw_dyn_rule *src, ipfw_dyn_rule *dst)
{
@ -1479,15 +1490,18 @@ export_dyn_rule(ipfw_dyn_rule *src, ipfw_dyn_rule *dst)
/*
* Fills int buffer given by @sd with dynamic states.
* Used by dump format v1 (current).
*
* Returns 0 on success.
*/
int
ipfw_dump_states(struct ip_fw_chain *chain, struct sockopt_data *sd)
{
ipfw_dyn_rule *p, *dst, *last = NULL;
ipfw_dyn_rule *p;
ipfw_obj_dyntlv *dst, *last;
ipfw_obj_ctlv *ctlv;
int i;
size_t sz;
if (V_ipfw_dyn_v == NULL)
return (0);
@ -1497,33 +1511,36 @@ ipfw_dump_states(struct ip_fw_chain *chain, struct sockopt_data *sd)
ctlv = (ipfw_obj_ctlv *)ipfw_get_sopt_space(sd, sizeof(*ctlv));
if (ctlv == NULL)
return (ENOMEM);
ctlv->head.type = IPFW_TLV_TBLNAME_LIST;
ctlv->objsize = sizeof(ipfw_dyn_rule);
sz = sizeof(ipfw_obj_dyntlv);
ctlv->head.type = IPFW_TLV_DYNSTATE_LIST;
ctlv->objsize = sz;
last = NULL;
for (i = 0 ; i < V_curr_dyn_buckets; i++) {
IPFW_BUCK_LOCK(i);
for (p = V_ipfw_dyn_v[i].head ; p != NULL; p = p->next) {
dst = (ipfw_dyn_rule *)ipfw_get_sopt_space(sd,
sizeof(*dst));
dst = (ipfw_obj_dyntlv *)ipfw_get_sopt_space(sd, sz);
if (dst == NULL) {
IPFW_BUCK_UNLOCK(i);
return (ENOMEM);
}
export_dyn_rule(p, dst);
export_dyn_rule(p, &dst->state);
dst->head.length = sz;
dst->head.type = IPFW_TLV_DYN_ENT;
last = dst;
}
IPFW_BUCK_UNLOCK(i);
}
if (last != NULL) /* mark last dynamic rule */
bzero(&last->next, sizeof(last));
last->head.flags = IPFW_DF_LAST;
return (0);
}
/*
* Fill given buffer with dynamic states.
* Fill given buffer with dynamic states (legacy format).
* IPFW_UH_RLOCK has to be held while calling.
*/
void

View File

@ -194,6 +194,7 @@ int ipfw_dump_states(struct ip_fw_chain *chain, struct sockopt_data *sd);
void ipfw_dyn_init(struct ip_fw_chain *); /* per-vnet initialization */
void ipfw_dyn_uninit(int); /* per-vnet deinitialization */
int ipfw_dyn_len(void);
int ipfw_dyn_get_count(void);
/* common variables */
VNET_DECLARE(int, fw_one_pass);

View File

@ -1201,9 +1201,8 @@ dump_config(struct ip_fw_chain *chain, struct sockopt_data *sd)
sz += da.rsize + sizeof(ipfw_obj_ctlv);
}
if (hdr->flags & IPFW_CFG_GET_STATES) {
sz += ipfw_dyn_len();
}
if (hdr->flags & IPFW_CFG_GET_STATES)
sz += ipfw_dyn_get_count() * sizeof(ipfw_obj_dyntlv);
/* Fill header anyway */
hdr->size = sz;
@ -1916,8 +1915,11 @@ ipfw_flush_sopt_data(struct sockopt_data *sd)
if (sd->koff == 0)
return (0);
if ((error = sooptcopyout(sd->sopt, sd->kbuf, sd->koff)) != 0)
return (error);
if (sd->sopt->sopt_dir == SOPT_GET) {
error = sooptcopyout(sd->sopt, sd->kbuf, sd->koff);
if (error != 0)
return (error);
}
memset(sd->kbuf, 0, sd->ksize);
sd->ktotal += sd->koff;

View File

@ -800,13 +800,13 @@ struct table_algo radix_iface = {
.name = "radix_iface",
.lookup = ta_lookup_iface,
.init = ta_init_iface,
.destroy = ta_destroy_iface,
.destroy = ta_destroy_iface,
.prepare_add = ta_prepare_add_iface,
.prepare_del = ta_prepare_del_iface,
.add = ta_add_iface,
.del = ta_del_iface,
.flush_entry = ta_flush_iface_entry,
.foreach = ta_foreach_iface,
.foreach = ta_foreach_iface,
.dump_tentry = ta_dump_iface_tentry,
.find_tentry = ta_find_iface_tentry,
};