Suppord showing named tables in ipfw(8) rule listing.

Kernel changes:
* change base TLV header to be u64 (so size can be u32).
* Introduce ipfw_obj_ctlv generc container TLV.
* Add IP_FW_XGET opcode which is now used for atomic configuration
  retrieval. One can specify needed configuration pieces to retrieve
  via flags field. Currently supported are
  IPFW_CFG_GET_STATIC (static rules) and
  IPFW_CFG_GET_STATES (dynamic states).
  Other configuration pieces (tables, pipes, etc..) support is planned.

Userland changes:
* Switch ipfw(8) to use new IP_FW_XGET for rule listing.
* Split rule listing code get and show pieces.
* Make several steps forward towards libipfw:
  permit printing states and rules(paritally) to supplied buffer.
  do not die on malloc/kernel failure inside given printing functions.
  stop assuming cmdline_opts is global symbol.
This commit is contained in:
Alexander V. Chernikov 2014-06-28 23:20:24 +00:00
parent 2d99a3497d
commit 563b5ab132
11 changed files with 890 additions and 281 deletions

View File

@ -137,15 +137,15 @@ altq_qid_to_name(u_int32_t qid)
} }
void void
print_altq_cmd(ipfw_insn_altq *altqptr) print_altq_cmd(struct buf_pr *bp, ipfw_insn_altq *altqptr)
{ {
if (altqptr) { if (altqptr) {
const char *qname; const char *qname;
qname = altq_qid_to_name(altqptr->qid); qname = altq_qid_to_name(altqptr->qid);
if (qname == NULL) if (qname == NULL)
printf(" altq ?<%u>", altqptr->qid); bprintf(bp, " altq ?<%u>", altqptr->qid);
else else
printf(" altq %s", qname); bprintf(bp, " altq %s", qname);
} }
} }

View File

@ -174,48 +174,44 @@ print_header(struct ipfw_flow_id *id)
} }
static void static void
list_flow(struct dn_flow *ni, int *print) list_flow(struct buf_pr *bp, struct dn_flow *ni)
{ {
char buff[255]; char buff[255];
struct protoent *pe = NULL; struct protoent *pe = NULL;
struct in_addr ina; struct in_addr ina;
struct ipfw_flow_id *id = &ni->fid; struct ipfw_flow_id *id = &ni->fid;
if (*print) {
print_header(&ni->fid);
*print = 0;
}
pe = getprotobynumber(id->proto); pe = getprotobynumber(id->proto);
/* XXX: Should check for IPv4 flows */ /* XXX: Should check for IPv4 flows */
printf("%3u%c", (ni->oid.id) & 0xff, bprintf(bp, "%3u%c", (ni->oid.id) & 0xff,
id->extra ? '*' : ' '); id->extra ? '*' : ' ');
if (!IS_IP6_FLOW_ID(id)) { if (!IS_IP6_FLOW_ID(id)) {
if (pe) if (pe)
printf("%-4s ", pe->p_name); bprintf(bp, "%-4s ", pe->p_name);
else else
printf("%4u ", id->proto); bprintf(bp, "%4u ", id->proto);
ina.s_addr = htonl(id->src_ip); ina.s_addr = htonl(id->src_ip);
printf("%15s/%-5d ", bprintf(bp, "%15s/%-5d ",
inet_ntoa(ina), id->src_port); inet_ntoa(ina), id->src_port);
ina.s_addr = htonl(id->dst_ip); ina.s_addr = htonl(id->dst_ip);
printf("%15s/%-5d ", bprintf(bp, "%15s/%-5d ",
inet_ntoa(ina), id->dst_port); inet_ntoa(ina), id->dst_port);
} else { } else {
/* Print IPv6 flows */ /* Print IPv6 flows */
if (pe != NULL) if (pe != NULL)
printf("%9s ", pe->p_name); bprintf(bp, "%9s ", pe->p_name);
else else
printf("%9u ", id->proto); bprintf(bp, "%9u ", id->proto);
printf("%7d %39s/%-5d ", id->flow_id6, bprintf(bp, "%7d %39s/%-5d ", id->flow_id6,
inet_ntop(AF_INET6, &(id->src_ip6), buff, sizeof(buff)), inet_ntop(AF_INET6, &(id->src_ip6), buff, sizeof(buff)),
id->src_port); id->src_port);
printf(" %39s/%-5d ", bprintf(bp, " %39s/%-5d ",
inet_ntop(AF_INET6, &(id->dst_ip6), buff, sizeof(buff)), inet_ntop(AF_INET6, &(id->dst_ip6), buff, sizeof(buff)),
id->dst_port); id->dst_port);
} }
pr_u64(&ni->tot_pkts, 4); pr_u64(bp, &ni->tot_pkts, 4);
pr_u64(&ni->tot_bytes, 8); pr_u64(bp, &ni->tot_bytes, 8);
printf("%2u %4u %3u\n", bprintf(bp, "%2u %4u %3u",
ni->length, ni->len_bytes, ni->drops); ni->length, ni->len_bytes, ni->drops);
} }
@ -303,8 +299,10 @@ list_pipes(struct dn_id *oid, struct dn_id *end)
{ {
char buf[160]; /* pending buffer */ char buf[160]; /* pending buffer */
int toPrint = 1; /* print header */ int toPrint = 1; /* print header */
struct buf_pr bp;
buf[0] = '\0'; buf[0] = '\0';
bp_alloc(&bp, 4096);
for (; oid != end; oid = O_NEXT(oid, oid->len)) { for (; oid != end; oid = O_NEXT(oid, oid->len)) {
if (oid->len < sizeof(*oid)) if (oid->len < sizeof(*oid))
errx(1, "invalid oid len %d\n", oid->len); errx(1, "invalid oid len %d\n", oid->len);
@ -346,7 +344,12 @@ list_pipes(struct dn_id *oid, struct dn_id *end)
break; break;
case DN_FLOW: case DN_FLOW:
list_flow((struct dn_flow *)oid, &toPrint); if (toPrint != 0) {
print_header(&((struct dn_flow *)oid)->fid);
toPrint = 0;
}
list_flow(&bp, (struct dn_flow *)oid);
printf("%s\n", bp.buf);
break; break;
case DN_LINK: { case DN_LINK: {
@ -384,6 +387,8 @@ list_pipes(struct dn_id *oid, struct dn_id *end)
} }
flush_buf(buf); // XXX does it really go here ? flush_buf(buf); // XXX does it really go here ?
} }
bp_free(&bp);
} }
/* /*

File diff suppressed because it is too large Load Diff

View File

@ -213,7 +213,19 @@ enum tokens {
#define NEED(_p, msg) {if (!_p) errx(EX_USAGE, msg);} #define NEED(_p, msg) {if (!_p) errx(EX_USAGE, msg);}
#define NEED1(msg) {if (!(*av)) errx(EX_USAGE, msg);} #define NEED1(msg) {if (!(*av)) errx(EX_USAGE, msg);}
int pr_u64(uint64_t *pd, int width); struct buf_pr {
char *buf; /* allocated buffer */
char *ptr; /* current pointer */
size_t size; /* total buffer size */
size_t avail; /* available storage */
size_t needed; /* length needed */
};
int pr_u64(struct buf_pr *bp, uint64_t *pd, int width);
int bp_alloc(struct buf_pr *b, size_t size);
void bp_free(struct buf_pr *b);
int bprintf(struct buf_pr *b, char *format, ...);
/* memory allocation support */ /* memory allocation support */
void *safe_calloc(size_t number, size_t size); void *safe_calloc(size_t number, size_t size);
@ -274,7 +286,7 @@ void ipfw_list(int ac, char *av[], int show_counters);
/* altq.c */ /* altq.c */
void altq_set_enabled(int enabled); void altq_set_enabled(int enabled);
u_int32_t altq_name_to_qid(const char *name); u_int32_t altq_name_to_qid(const char *name);
void print_altq_cmd(struct _ipfw_insn_altq *altqptr); void print_altq_cmd(struct buf_pr *bp, struct _ipfw_insn_altq *altqptr);
#else #else
#define NO_ALTQ #define NO_ALTQ
#endif #endif
@ -298,3 +310,8 @@ void fill_flow6(struct _ipfw_insn_u32 *cmd, char *av, int cblen);
void fill_unreach6_code(u_short *codep, char *str); void fill_unreach6_code(u_short *codep, char *str);
void fill_icmp6types(struct _ipfw_insn_icmp6 *cmd, char *av, int cblen); void fill_icmp6types(struct _ipfw_insn_icmp6 *cmd, char *av, int cblen);
int fill_ext6hdr(struct _ipfw_insn *cmd, char *av); int fill_ext6hdr(struct _ipfw_insn *cmd, char *av);
/* tables.c */
struct _ipfw_obj_ctlv;
char *table_search_ctlv(struct _ipfw_obj_ctlv *ctlv, uint16_t idx);

View File

@ -305,7 +305,7 @@ static void
table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint16_t uidx) table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint16_t uidx)
{ {
ntlv->head.type = IPFW_TLV_NAME; ntlv->head.type = IPFW_TLV_TBL_NAME;
ntlv->head.length = sizeof(ipfw_obj_ntlv); ntlv->head.length = sizeof(ipfw_obj_ntlv);
ntlv->idx = uidx; ntlv->idx = uidx;
strlcpy(ntlv->name, name, sizeof(ntlv->name)); strlcpy(ntlv->name, name, sizeof(ntlv->name));
@ -593,3 +593,43 @@ table_show_list(ipfw_obj_header *oh, int need_header)
} }
} }
int
compare_ntlv(const void *k, const void *v)
{
ipfw_obj_ntlv *ntlv;
uint16_t key;
key = *((uint16_t *)k);
ntlv = (ipfw_obj_ntlv *)v;
if (key < ntlv->idx)
return (-1);
else if (key > ntlv->idx)
return (1);
return (0);
}
/*
* Finds table name in @ctlv by @idx.
* Uses the following facts:
* 1) All TLVs are the same size
* 2) Kernel implementation provides already sorted list.
*
* Returns table name or NULL.
*/
char *
table_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx)
{
ipfw_obj_ntlv *ntlv;
ntlv = bsearch(&idx, (ctlv + 1), ctlv->count, ctlv->objsize,
compare_ntlv);
if (ntlv != 0)
return (ntlv->name);
return (NULL);
}

View File

@ -87,6 +87,7 @@ typedef struct _ip_fw3_opheader {
#define IP_FW_TABLE_XFLUSH 94 /* flush table data */ #define IP_FW_TABLE_XFLUSH 94 /* flush table data */
#define IP_FW_TABLE_XCREATE 95 /* create new table */ #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 */
/* /*
* Usage guidelines: * Usage guidelines:
@ -681,18 +682,30 @@ typedef struct _ipfw_xtable {
typedef struct _ipfw_obj_tlv { typedef struct _ipfw_obj_tlv {
uint16_t type; /* TLV type */ uint16_t type; /* TLV type */
uint16_t length; /* Total length, aligned to u32 */ uint16_t flags; /* unused */
uint32_t length; /* Total length, aligned to u64 */
} ipfw_obj_tlv; } 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_NAME 1
/* Object name TLV */ /* Object name TLV */
typedef struct _ipfw_obj_ntlv { typedef struct _ipfw_obj_ntlv {
ipfw_obj_tlv head; /* TLV header */ ipfw_obj_tlv head; /* TLV header */
uint16_t idx; /* Name index */ uint16_t idx; /* Name index */
uint16_t spare; /* unused */ uint16_t spare0; /* unused */
char name[64]; /* Null-terminated name */ uint32_t spare1; /* unused */
char name[64]; /* Null-terminated name */
} ipfw_obj_ntlv; } ipfw_obj_ntlv;
/* Containter TLVs */
typedef struct _ipfw_obj_ctlv {
ipfw_obj_tlv head; /* TLV header */
uint32_t count; /* Number of sub-TLVs */
uint32_t objsize; /* Single object size */
} ipfw_obj_ctlv;
typedef struct _ipfw_xtable_info { typedef struct _ipfw_xtable_info {
uint8_t type; /* table type (cidr,iface,..) */ uint8_t type; /* table type (cidr,iface,..) */
uint8_t ftype; /* table value format type */ uint8_t ftype; /* table value format type */
@ -725,4 +738,15 @@ typedef struct _ipfw_obj_lheader {
uint32_t objsize; /* Size of one object */ uint32_t objsize; /* Size of one object */
} ipfw_obj_lheader; } ipfw_obj_lheader;
#define IPFW_CFG_GET_STATIC 1
#define IPFW_CFG_GET_STATES 2
typedef struct _ipfw_cfg_lheader {
ip_fw3_opheader opheader; /* IP_FW3 opcode */
uint32_t set_mask; /* disabled set mask */
uint32_t flags; /* Request flags */
uint32_t size; /* neded buffer size */
uint32_t start_rule;
uint32_t end_rule;
} ipfw_cfg_lheader;
#endif /* _IPFW2_H */ #endif /* _IPFW2_H */

View File

@ -1454,6 +1454,74 @@ ipfw_dyn_len(void)
(DYN_COUNT * sizeof(ipfw_dyn_rule)); (DYN_COUNT * sizeof(ipfw_dyn_rule));
} }
static void
export_dyn_rule(ipfw_dyn_rule *src, ipfw_dyn_rule *dst)
{
memcpy(dst, src, sizeof(*src));
memcpy(&(dst->rule), &(src->rule->rulenum), sizeof(src->rule->rulenum));
/*
* store set number into high word of
* dst->rule pointer.
*/
memcpy((char *)&dst->rule + sizeof(src->rule->rulenum),
&(src->rule->set), sizeof(src->rule->set));
/*
* store a non-null value in "next".
* The userland code will interpret a
* NULL here as a marker
* for the last dynamic rule.
*/
memcpy(&dst->next, &dst, sizeof(dst));
dst->expire =
TIME_LEQ(dst->expire, time_uptime) ? 0 : dst->expire - time_uptime;
}
/*
* Fills int buffer given by @sd with dynamic states.
*
* 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_obj_ctlv *ctlv;
int i;
if (V_ipfw_dyn_v == NULL)
return (0);
IPFW_UH_RLOCK_ASSERT(chain);
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);
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));
if (dst == NULL) {
IPFW_BUCK_UNLOCK(i);
return (ENOMEM);
}
export_dyn_rule(p, dst);
last = dst;
}
IPFW_BUCK_UNLOCK(i);
}
if (last != NULL) /* mark last dynamic rule */
bzero(&last->next, sizeof(last));
return (0);
}
/* /*
* Fill given buffer with dynamic states. * Fill given buffer with dynamic states.
* IPFW_UH_RLOCK has to be held while calling. * IPFW_UH_RLOCK has to be held while calling.
@ -1477,28 +1545,9 @@ ipfw_get_dynamic(struct ip_fw_chain *chain, char **pbp, const char *ep)
if (bp + sizeof *p <= ep) { if (bp + sizeof *p <= ep) {
ipfw_dyn_rule *dst = ipfw_dyn_rule *dst =
(ipfw_dyn_rule *)bp; (ipfw_dyn_rule *)bp;
bcopy(p, dst, sizeof *p);
bcopy(&(p->rule->rulenum), &(dst->rule), export_dyn_rule(p, dst);
sizeof(p->rule->rulenum));
/*
* store set number into high word of
* dst->rule pointer.
*/
bcopy(&(p->rule->set),
(char *)&dst->rule +
sizeof(p->rule->rulenum),
sizeof(p->rule->set));
/*
* store a non-null value in "next".
* The userland code will interpret a
* NULL here as a marker
* for the last dynamic rule.
*/
bcopy(&dst, &dst->next, sizeof(dst));
last = dst; last = dst;
dst->expire =
TIME_LEQ(dst->expire, time_uptime) ?
0 : dst->expire - time_uptime ;
bp += sizeof(ipfw_dyn_rule); bp += sizeof(ipfw_dyn_rule);
} }
} }

View File

@ -176,6 +176,7 @@ enum { /* result for matching dynamic rules */
* Eventually we may implement it with a callback on the function. * Eventually we may implement it with a callback on the function.
*/ */
struct ip_fw_chain; struct ip_fw_chain;
struct sockopt_data;
void ipfw_expire_dyn_rules(struct ip_fw_chain *, struct ip_fw *, int); void ipfw_expire_dyn_rules(struct ip_fw_chain *, struct ip_fw *, int);
void ipfw_dyn_unlock(ipfw_dyn_rule *q); void ipfw_dyn_unlock(ipfw_dyn_rule *q);
@ -188,6 +189,7 @@ ipfw_dyn_rule *ipfw_lookup_dyn_rule(struct ipfw_flow_id *pkt,
int *match_direction, struct tcphdr *tcp); int *match_direction, struct tcphdr *tcp);
void ipfw_remove_dyn_children(struct ip_fw *rule); void ipfw_remove_dyn_children(struct ip_fw *rule);
void ipfw_get_dynamic(struct ip_fw_chain *chain, char **bp, const char *ep); void ipfw_get_dynamic(struct ip_fw_chain *chain, char **bp, const char *ep);
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_init(struct ip_fw_chain *); /* per-vnet initialization */
void ipfw_dyn_uninit(int); /* per-vnet deinitialization */ void ipfw_dyn_uninit(int); /* per-vnet deinitialization */

View File

@ -989,6 +989,178 @@ ipfw_getrules(struct ip_fw_chain *chain, void *buf, size_t space)
} }
struct dump_args {
uint32_t b; /* start rule */
uint32_t e; /* end rule */
uint32_t rcount; /* number of rules */
uint32_t rsize; /* rules size */
uint32_t tcount; /* number of tables */
};
/*
* Dumps static rules with table TLVs in buffer @sd.
*
* Returns 0 on success.
*/
static int
dump_static_rules(struct ip_fw_chain *chain, struct dump_args *da,
uint32_t *bmask, struct sockopt_data *sd)
{
int error;
int i, l;
uint32_t tcount;
ipfw_obj_ctlv *ctlv;
struct ip_fw *dst, *rule;
time_t boot_seconds;
/* Dump table names first (if any) */
if (da->tcount > 0) {
/* Header first */
ctlv = (ipfw_obj_ctlv *)ipfw_get_sopt_space(sd, sizeof(*ctlv));
if (ctlv == NULL)
return (ENOMEM);
ctlv->head.type = IPFW_TLV_TBLNAME_LIST;
ctlv->head.length = da->tcount * sizeof(ipfw_obj_ntlv) +
sizeof(*ctlv);
ctlv->count = da->tcount;
ctlv->objsize = sizeof(ipfw_obj_ntlv);
}
i = 0;
tcount = da->tcount;
while (tcount > 0) {
if ((bmask[i / 32] & (1 << (i % 32))) == 0) {
i++;
continue;
}
if ((error = ipfw_export_table_ntlv(chain, i, sd)) != 0)
return (error);
i++;
tcount--;
}
/* Dump rules */
ctlv = (ipfw_obj_ctlv *)ipfw_get_sopt_space(sd, sizeof(*ctlv));
if (ctlv == NULL)
return (ENOMEM);
ctlv->head.type = IPFW_TLV_RULE_LIST;
ctlv->head.length = da->rsize + sizeof(*ctlv);
ctlv->count = da->rcount;
boot_seconds = boottime.tv_sec;
for (i = da->b; i < da->e; i++) {
rule = chain->map[i];
l = RULESIZE(rule);
/* XXX: align to u64 */
dst = (struct ip_fw *)ipfw_get_sopt_space(sd, l);
if (rule == NULL)
return (ENOMEM);
bcopy(rule, dst, l);
if (dst->timestamp != 0)
dst->timestamp += boot_seconds;
}
return (0);
}
/*
* Dumps requested objects data
* Data layout (version 0)(current):
* Request: [ ipfw_cfg_lheader ] + IPFW_CFG_GET_* flags
* size = ipfw_cfg_lheader.size
* Reply: [ ipfw_rules_lheader
* [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional)
* [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) ip_fw x N ] (optional)
* [ ipfw_obj_ctlv(IPFW_TLV_STATE_LIST) ipfw_dyn_rule x N ] (optional)
* ]
* * NOTE IPFW_TLV_STATE_LIST has the single valid field: objsize.
* The rest (size, count) are set to zero and needs to be ignored.
*
* Returns 0 on success.
*/
static int
dump_config(struct ip_fw_chain *chain, struct sockopt_data *sd)
{
ipfw_cfg_lheader *hdr;
struct ip_fw *rule;
uint32_t sz;
int error, i;
struct dump_args da;
uint32_t *bmask;
hdr = (ipfw_cfg_lheader *)ipfw_get_sopt_header(sd, sizeof(*hdr));
if (hdr == NULL)
return (EINVAL);
/* Allocate needed state */
error = 0;
bmask = NULL;
if (hdr->flags & IPFW_CFG_GET_STATIC)
bmask = malloc(IPFW_TABLES_MAX / 8, M_TEMP, M_WAITOK | M_ZERO);
IPFW_UH_RLOCK(chain);
/*
* STAGE 1: Determine size/count for objects in range.
* Prepare used tables bitmask.
*/
sz = 0;
memset(&da, 0, sizeof(da));
da.b = 0;
da.e = chain->n_rules;
if (hdr->flags & IPFW_CFG_GET_STATIC) {
for (i = da.b; i < da.e; i++) {
rule = chain->map[i];
da.rsize += RULESIZE(rule);
da.rcount++;
da.tcount += ipfw_mark_table_kidx(chain, rule, bmask);
}
if (da.tcount > 0)
sz += da.tcount * sizeof(ipfw_obj_ntlv) +
sizeof(ipfw_obj_ctlv);
sz += da.rsize + sizeof(ipfw_obj_ctlv);
}
if (hdr->flags & IPFW_CFG_GET_STATES) {
sz += ipfw_dyn_len();
}
/* Fill header anyway */
hdr->size = sz;
hdr->set_mask = V_set_disable;
if (sd->valsize < sz) {
IPFW_UH_RUNLOCK(chain);
return (ENOMEM);
}
/* STAGE2: Store actual data */
if (hdr->flags & IPFW_CFG_GET_STATIC) {
error = dump_static_rules(chain, &da, bmask, sd);
if (error != 0) {
IPFW_UH_RUNLOCK(chain);
return (error);
}
}
if (hdr->flags & IPFW_CFG_GET_STATES)
error = ipfw_dump_states(chain, sd);
IPFW_UH_RUNLOCK(chain);
if (bmask != NULL)
free(bmask, M_TEMP);
return (error);
}
#define IP_FW3_OPLENGTH(x) ((x)->sopt_valsize - sizeof(ip_fw3_opheader)) #define IP_FW3_OPLENGTH(x) ((x)->sopt_valsize - sizeof(ip_fw3_opheader))
#define IP_FW3_OPTBUF 4096 /* page-size */ #define IP_FW3_OPTBUF 4096 /* page-size */
/** /**
@ -1101,6 +1273,10 @@ ipfw_ctl(struct sockopt *sopt)
} }
break; break;
case IP_FW_XGET: /* IP_FW3 */
error = dump_config(chain, &sdata);
break;
case IP_FW_FLUSH: case IP_FW_FLUSH:
/* locking is done within del_entry() */ /* locking is done within del_entry() */
error = del_entry(chain, 0); /* special case, rule=0, cmd=0 means all */ error = del_entry(chain, 0); /* special case, rule=0, cmd=0 means all */

View File

@ -27,16 +27,15 @@
__FBSDID("$FreeBSD$"); __FBSDID("$FreeBSD$");
/* /*
* Lookup table support for ipfw * Lookup table support for ipfw.
* *
* Lookup tables are implemented (at the moment) using the radix * This file containg handlers for all generic tables operations:
* tree used for routing tables. Tables store key-value entries, where * add/del/flush entries, list/dump tables etc..
* keys are network prefixes (addr/masklen), and values are integers.
* As a degenerate case we can interpret keys as 32-bit integers
* (with a /32 mask).
* *
* The table is protected by the IPFW lock even for manipulation coming * Table data modification is protected by both UH and runtimg lock
* from userland, because operations are typically fast. * while reading configuration/data is protected by UH lock.
*
* Lookup algorithms for all table types are located in ip_fw_table_algo.c
*/ */
#include "opt_ipfw.h" #include "opt_ipfw.h"
@ -901,6 +900,31 @@ objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti)
ti->tlen = oh->ntlv.head.length; ti->tlen = oh->ntlv.head.length;
} }
int
ipfw_export_table_ntlv(struct ip_fw_chain *ch, uint16_t kidx,
struct sockopt_data *sd)
{
struct namedobj_instance *ni;
struct named_object *no;
ipfw_obj_ntlv *ntlv;
ni = CHAIN_TO_NI(ch);
no = ipfw_objhash_lookup_idx(ni, 0, kidx);
KASSERT(no != NULL, ("invalid table kidx passed"));
ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv));
if (ntlv == NULL)
return (ENOMEM);
ntlv->head.type = IPFW_TLV_TBL_NAME;
ntlv->head.length = sizeof(*ntlv);
ntlv->idx = no->kidx;
strlcpy(ntlv->name, no->name, sizeof(ntlv->name));
return (0);
}
static void static void
export_table_info(struct table_config *tc, ipfw_xtable_info *i) export_table_info(struct table_config *tc, ipfw_xtable_info *i)
{ {
@ -1253,7 +1277,7 @@ find_name_tlv(void *tlvs, int len, uint16_t uidx)
for (; pa < pe; pa += l) { for (; pa < pe; pa += l) {
ntlv = (ipfw_obj_ntlv *)pa; ntlv = (ipfw_obj_ntlv *)pa;
l = ntlv->head.length; l = ntlv->head.length;
if (ntlv->head.type != IPFW_TLV_NAME) if (ntlv->head.type != IPFW_TLV_TBL_NAME)
continue; continue;
if (ntlv->idx != uidx) if (ntlv->idx != uidx)
continue; continue;
@ -1515,6 +1539,40 @@ ipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule)
return (0); return (0);
} }
/*
* Sets every table kidx in @bmask which is used in rule @rule.
*
* Returns number of newly-referenced tables.
*/
int
ipfw_mark_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule,
uint32_t *bmask)
{
int cmdlen, l, count;
ipfw_insn *cmd;
uint16_t kidx;
uint8_t type;
l = rule->cmd_len;
cmd = rule->cmd;
cmdlen = 0;
count = 0;
for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
cmdlen = F_LEN(cmd);
if (classify_table_opcode(cmd, &kidx, &type) != 0)
continue;
if ((bmask[kidx / 32] & (1 << (kidx % 32))) == 0)
count++;
bmask[kidx / 32] |= 1 << (kidx % 32);
}
return (count);
}
/* /*
* Checks is opcode is referencing table of appropriate type. * Checks is opcode is referencing table of appropriate type.

View File

@ -111,6 +111,10 @@ int ipfw_del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
int ipfw_rewrite_table_uidx(struct ip_fw_chain *chain, int ipfw_rewrite_table_uidx(struct ip_fw_chain *chain,
struct rule_check_info *ci); struct rule_check_info *ci);
int ipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule); int ipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule);
int ipfw_mark_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule,
uint32_t *bmask);
int ipfw_export_table_ntlv(struct ip_fw_chain *ch, uint16_t kidx,
struct sockopt_data *sd);
void ipfw_unbind_table_rule(struct ip_fw_chain *chain, struct ip_fw *rule); void ipfw_unbind_table_rule(struct ip_fw_chain *chain, struct ip_fw *rule);
void ipfw_unbind_table_list(struct ip_fw_chain *chain, struct ip_fw *head); void ipfw_unbind_table_list(struct ip_fw_chain *chain, struct ip_fw *head);