svn commit: r269689 - in projects/ipfw: sbin/ipfw sys/netinet sys/netpfil/ipfw
Alexander V. Chernikov
melifaro at FreeBSD.org
Thu Aug 7 21:37:33 UTC 2014
Author: melifaro
Date: Thu Aug 7 21:37:31 2014
New Revision: 269689
URL: http://svnweb.freebsd.org/changeset/base/269689
Log:
Kernel changes:
* Implement proper checks for switching between global and set-aware tables
* Split IP_FW_DEL mess into the following opcodes:
* IP_FW_XDEL (del rules matching pattern)
* IP_FW_XMOVE (move rules matching pattern to another set)
* IP_FW_SET_SWAP (swap between 2 sets)
* IP_FW_SET_MOVE (move one set to another one)
* IP_FW_SET_ENABLE (enable/disable sets)
* Add IP_FW_XZERO / IP_FW_XRESETLOG to finish IP_FW3 migration.
* Use unified ipfw_range_tlv as range description for all of the above.
* Check dynamic states IFF there was non-zero number of deleted dyn rules,
* Del relevant dynamic states with singe traversal instead of per-rule one.
Userland changes:
* Switch ipfw(8) to use new opcodes.
Modified:
projects/ipfw/sbin/ipfw/ipfw2.c
projects/ipfw/sys/netinet/ip_fw.h
projects/ipfw/sys/netpfil/ipfw/ip_fw2.c
projects/ipfw/sys/netpfil/ipfw/ip_fw_dynamic.c
projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h
projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c
projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c
projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h
Modified: projects/ipfw/sbin/ipfw/ipfw2.c
==============================================================================
--- projects/ipfw/sbin/ipfw/ipfw2.c Thu Aug 7 21:37:03 2014 (r269688)
+++ projects/ipfw/sbin/ipfw/ipfw2.c Thu Aug 7 21:37:31 2014 (r269689)
@@ -2110,6 +2110,19 @@ show_dyn_state(struct cmdline_opts *co,
bprintf(bp, " UNKNOWN <-> UNKNOWN\n");
}
+static int
+do_range_cmd(int cmd, ipfw_range_tlv *rt)
+{
+ ipfw_range_header rh;
+
+ memset(&rh, 0, sizeof(rh));
+ memcpy(&rh.range, rt, sizeof(*rt));
+ rh.range.head.length = sizeof(*rt);
+ rh.range.head.type = IPFW_TLV_RANGE;
+
+ return (do_set3(cmd, &rh.opheader, sizeof(rh)));
+}
+
/*
* This one handles all set-related commands
* ipfw set { show | enable | disable }
@@ -2122,12 +2135,13 @@ ipfw_sets_handler(char *av[])
{
uint32_t masks[2];
int i;
- uint16_t rulenum;
- uint8_t cmd, new_set;
+ uint8_t cmd, new_set, rulenum;
+ ipfw_range_tlv rt;
char *msg;
size_t size;
av++;
+ memset(&rt, 0, sizeof(rt));
if (av[0] == NULL)
errx(EX_USAGE, "set needs command");
@@ -2156,33 +2170,38 @@ ipfw_sets_handler(char *av[])
av++;
if ( av[0] == NULL || av[1] == NULL )
errx(EX_USAGE, "set swap needs 2 set numbers\n");
- rulenum = atoi(av[0]);
- new_set = atoi(av[1]);
- if (!isdigit(*(av[0])) || rulenum > RESVD_SET)
+ rt.set = atoi(av[0]);
+ rt.new_set = atoi(av[1]);
+ if (!isdigit(*(av[0])) || rt.set > RESVD_SET)
errx(EX_DATAERR, "invalid set number %s\n", av[0]);
- if (!isdigit(*(av[1])) || new_set > RESVD_SET)
+ if (!isdigit(*(av[1])) || rt.new_set > RESVD_SET)
errx(EX_DATAERR, "invalid set number %s\n", av[1]);
- masks[0] = (4 << 24) | (new_set << 16) | (rulenum);
- i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
+ i = do_range_cmd(IP_FW_SET_SWAP, &rt);
} else if (_substrcmp(*av, "move") == 0) {
av++;
if (av[0] && _substrcmp(*av, "rule") == 0) {
- cmd = 2;
+ rt.flags = IPFW_RCFLAG_RANGE; /* move rules to new set */
+ cmd = IP_FW_XMOVE;
av++;
} else
- cmd = 3;
+ cmd = IP_FW_SET_MOVE; /* Move set to new one */
if (av[0] == NULL || av[1] == NULL || av[2] == NULL ||
av[3] != NULL || _substrcmp(av[1], "to") != 0)
errx(EX_USAGE, "syntax: set move [rule] X to Y\n");
rulenum = atoi(av[0]);
- new_set = atoi(av[2]);
- if (!isdigit(*(av[0])) || (cmd == 3 && rulenum > RESVD_SET) ||
- (cmd == 2 && rulenum == IPFW_DEFAULT_RULE) )
+ rt.new_set = atoi(av[2]);
+ if (cmd == IP_FW_XMOVE) {
+ rt.start_rule = rulenum;
+ rt.end_rule = rulenum;
+ } else
+ rt.set = rulenum;
+ rt.new_set = atoi(av[2]);
+ if (!isdigit(*(av[0])) || (cmd == 3 && rt.set > RESVD_SET) ||
+ (cmd == 2 && rt.start_rule == IPFW_DEFAULT_RULE) )
errx(EX_DATAERR, "invalid source number %s\n", av[0]);
if (!isdigit(*(av[2])) || new_set > RESVD_SET)
errx(EX_DATAERR, "invalid dest. set %s\n", av[1]);
- masks[0] = (cmd << 24) | (new_set << 16) | (rulenum);
- i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
+ i = do_range_cmd(cmd, &rt);
} else if (_substrcmp(*av, "disable") == 0 ||
_substrcmp(*av, "enable") == 0 ) {
int which = _substrcmp(*av, "enable") == 0 ? 1 : 0;
@@ -2210,9 +2229,11 @@ ipfw_sets_handler(char *av[])
errx(EX_DATAERR,
"cannot enable and disable the same set\n");
- i = do_cmd(IP_FW_DEL, masks, sizeof(masks));
+ rt.set = masks[0];
+ rt.new_set = masks[1];
+ i = do_range_cmd(IP_FW_SET_ENABLE, &rt);
if (i)
- warn("set enable/disable: setsockopt(IP_FW_DEL)");
+ warn("set enable/disable: setsockopt(IP_FW_SET_ENABLE)");
} else
errx(EX_USAGE, "invalid set command %s\n", *av);
}
@@ -2984,9 +3005,11 @@ ipfw_delete(char *av[])
int i;
int exitval = EX_OK;
int do_set = 0;
+ ipfw_range_tlv rt;
av++;
NEED1("missing rule specification");
+ memset(&rt, 0, sizeof(rt));
if ( *av && _substrcmp(*av, "set") == 0) {
/* Do not allow using the following syntax:
* ipfw set N delete set M
@@ -3009,15 +3032,25 @@ ipfw_delete(char *av[])
} else if (co.do_pipe) {
exitval = ipfw_delete_pipe(co.do_pipe, i);
} else {
- if (co.use_set)
- rulenum = (i & 0xffff) | (5 << 24) |
- ((co.use_set - 1) << 16);
- else
- rulenum = (i & 0xffff) | (do_set << 24);
- i = do_cmd(IP_FW_DEL, &rulenum, sizeof rulenum);
- if (i) {
+ if (do_set != 0) {
+ rt.set = i & 31;
+ rt.flags = IPFW_RCFLAG_SET;
+ } else {
+ rt.start_rule = i & 0xffff;
+ rt.end_rule = i & 0xffff;
+ if (rt.start_rule == 0 && rt.end_rule == 0)
+ rt.flags |= IPFW_RCFLAG_ALL;
+ else
+ rt.flags |= IPFW_RCFLAG_RANGE;
+ if (co.use_set != 0) {
+ rt.set = co.use_set - 1;
+ rt.flags |= IPFW_RCFLAG_SET;
+ }
+ }
+ i = do_range_cmd(IP_FW_XDEL, &rt);
+ if (i != 0) {
exitval = EX_UNAVAILABLE;
- warn("rule %u: setsockopt(IP_FW_DEL)",
+ warn("rule %u: setsockopt(IP_FW_XDEL)",
rulenum);
}
}
@@ -4681,25 +4714,31 @@ ipfw_add(char *av[])
/*
* clear the counters or the log counters.
+ * optname has the following values:
+ * 0 (zero both counters and logging)
+ * 1 (zero logging only)
*/
void
-ipfw_zero(int ac, char *av[], int optname /* 0 = IP_FW_ZERO, 1 = IP_FW_RESETLOG */)
+ipfw_zero(int ac, char *av[], int optname)
{
- uint32_t arg, saved_arg;
+ ipfw_range_tlv rt;
+ uint32_t arg;
int failed = EX_OK;
char const *errstr;
char const *name = optname ? "RESETLOG" : "ZERO";
- optname = optname ? IP_FW_RESETLOG : IP_FW_ZERO;
+ optname = optname ? IP_FW_XRESETLOG : IP_FW_XZERO;
+ memset(&rt, 0, sizeof(rt));
av++; ac--;
- if (!ac) {
+ if (ac == 0) {
/* clear all entries */
- if (do_cmd(optname, NULL, 0) < 0)
- err(EX_UNAVAILABLE, "setsockopt(IP_FW_%s)", name);
+ rt.flags = IPFW_RCFLAG_ALL;
+ if (do_range_cmd(optname, &rt) < 0)
+ err(EX_UNAVAILABLE, "setsockopt(IP_FW_X%s)", name);
if (!co.do_quiet)
- printf("%s.\n", optname == IP_FW_ZERO ?
+ printf("%s.\n", optname == IP_FW_XZERO ?
"Accounting cleared":"Logging counts reset");
return;
@@ -4712,18 +4751,20 @@ ipfw_zero(int ac, char *av[], int optnam
if (errstr)
errx(EX_DATAERR,
"invalid rule number %s\n", *av);
- saved_arg = arg;
- if (co.use_set)
- arg |= (1 << 24) | ((co.use_set - 1) << 16);
- av++;
- ac--;
- if (do_cmd(optname, &arg, sizeof(arg))) {
- warn("rule %u: setsockopt(IP_FW_%s)",
- saved_arg, name);
+ rt.start_rule = arg;
+ rt.end_rule = arg;
+ rt.flags |= IPFW_RCFLAG_RANGE;
+ if (co.use_set != 0) {
+ rt.set = co.use_set - 1;
+ rt.flags |= IPFW_RCFLAG_SET;
+ }
+ if (do_range_cmd(optname, &rt) != 0) {
+ warn("rule %u: setsockopt(IP_FW_X%s)",
+ arg, name);
failed = EX_UNAVAILABLE;
} else if (!co.do_quiet)
- printf("Entry %d %s.\n", saved_arg,
- optname == IP_FW_ZERO ?
+ printf("Entry %d %s.\n", arg,
+ optname == IP_FW_XZERO ?
"cleared" : "logging count reset");
} else {
errx(EX_USAGE, "invalid rule number ``%s''", *av);
@@ -4736,7 +4777,7 @@ ipfw_zero(int ac, char *av[], int optnam
void
ipfw_flush(int force)
{
- int cmd = co.do_pipe ? IP_DUMMYNET_FLUSH : IP_FW_FLUSH;
+ ipfw_range_tlv rt;
if (!force && !co.do_quiet) { /* need to ask user */
int c;
@@ -4758,13 +4799,14 @@ ipfw_flush(int force)
return;
}
/* `ipfw set N flush` - is the same that `ipfw delete set N` */
- if (co.use_set) {
- uint32_t arg = ((co.use_set - 1) & 0xffff) | (1 << 24);
- if (do_cmd(IP_FW_DEL, &arg, sizeof(arg)) < 0)
- err(EX_UNAVAILABLE, "setsockopt(IP_FW_DEL)");
- } else if (do_cmd(cmd, NULL, 0) < 0)
- err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)",
- co.do_pipe ? "DUMMYNET" : "FW");
+ memset(&rt, 0, sizeof(rt));
+ if (co.use_set != 0) {
+ rt.set = co.use_set - 1;
+ rt.flags = IPFW_RCFLAG_SET;
+ } else
+ rt.flags = IPFW_RCFLAG_ALL;
+ if (do_range_cmd(IP_FW_XDEL, &rt) != 0)
+ err(EX_UNAVAILABLE, "setsockopt(IP_FW_XDEL)");
if (!co.do_quiet)
printf("Flushed all %s.\n", co.do_pipe ? "pipes" : "rules");
}
Modified: projects/ipfw/sys/netinet/ip_fw.h
==============================================================================
--- projects/ipfw/sys/netinet/ip_fw.h Thu Aug 7 21:37:03 2014 (r269688)
+++ projects/ipfw/sys/netinet/ip_fw.h Thu Aug 7 21:37:31 2014 (r269689)
@@ -86,11 +86,18 @@ typedef struct _ip_fw3_opheader {
#define IP_FW_TABLE_XCREATE 95 /* create new table */
//#define IP_FW_TABLE_XMODIFY 96 /* modify existing table */
#define IP_FW_XGET 97 /* Retrieve configuration */
-#define IP_FW_XADD 98 /* add entry */
-#define IP_FW_TABLE_XFIND 99 /* finds an entry */
-#define IP_FW_XIFLIST 100 /* list tracked interfaces */
-#define IP_FW_TABLES_ALIST 101 /* list table algorithms */
-#define IP_FW_TABLE_XSWAP 102 /* swap two tables */
+#define IP_FW_XADD 98 /* add rule */
+#define IP_FW_XDEL 99 /* del rule */
+#define IP_FW_XMOVE 100 /* move rules to different set */
+#define IP_FW_XZERO 101 /* clear accounting */
+#define IP_FW_XRESETLOG 102 /* zero rules logs */
+#define IP_FW_SET_SWAP 103 /* Swap between 2 sets */
+#define IP_FW_SET_MOVE 104 /* Move one set to another one */
+#define IP_FW_SET_ENABLE 105 /* Enable/disable sets */
+#define IP_FW_TABLE_XFIND 106 /* finds an entry */
+#define IP_FW_XIFLIST 107 /* list tracked interfaces */
+#define IP_FW_TABLES_ALIST 108 /* list table algorithms */
+#define IP_FW_TABLE_XSWAP 109 /* swap two tables */
/*
* Usage guidelines:
@@ -735,6 +742,7 @@ typedef struct _ipfw_obj_tlv {
#define IPFW_TLV_DYN_ENT 6
#define IPFW_TLV_RULE_ENT 7
#define IPFW_TLV_TBLENT_LIST 8
+#define IPFW_TLV_RANGE 9
/* Object name TLV */
typedef struct _ipfw_obj_ntlv {
@@ -799,6 +807,19 @@ typedef struct _ipfw_obj_ctlv {
uint8_t spare;
} ipfw_obj_ctlv;
+/* Range TLV */
+typedef struct _ipfw_range_tlv {
+ ipfw_obj_tlv head; /* TLV header */
+ uint32_t flags; /* Range flags */
+ uint16_t start_rule; /* Range start */
+ uint16_t end_rule; /* Range end */
+ uint32_t set; /* Range set to match */
+ uint32_t new_set; /* New set to move/swap to */
+} ipfw_range_tlv;
+#define IPFW_RCFLAG_RANGE 0x01 /* rule range is set */
+#define IPFW_RCFLAG_ALL 0x02 /* match ALL rules */
+#define IPFW_RCFLAG_SET 0x04 /* match rules in given set */
+
typedef struct _ipfw_ta_tinfo {
uint32_t flags; /* Format flags */
uint32_t spare;
@@ -893,4 +914,9 @@ typedef struct _ipfw_cfg_lheader {
uint32_t end_rule;
} ipfw_cfg_lheader;
+typedef struct _ipfw_range_header {
+ ip_fw3_opheader opheader; /* IP_FW3 opcode */
+ ipfw_range_tlv range;
+} ipfw_range_header;
+
#endif /* _IPFW2_H */
Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw2.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw2.c Thu Aug 7 21:37:03 2014 (r269688)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw2.c Thu Aug 7 21:37:31 2014 (r269689)
@@ -158,6 +158,7 @@ ipfw_nat_cfg_t *ipfw_nat_get_log_ptr;
#ifdef SYSCTL_NODE
uint32_t dummy_def = IPFW_DEFAULT_RULE;
static int sysctl_ipfw_table_num(SYSCTL_HANDLER_ARGS);
+static int sysctl_ipfw_tables_sets(SYSCTL_HANDLER_ARGS);
SYSBEGIN(f3)
@@ -180,8 +181,8 @@ SYSCTL_UINT(_net_inet_ip_fw, OID_AUTO, d
SYSCTL_VNET_PROC(_net_inet_ip_fw, OID_AUTO, tables_max,
CTLTYPE_UINT|CTLFLAG_RW, 0, 0, sysctl_ipfw_table_num, "IU",
"Maximum number of concurrently used tables");
-SYSCTL_VNET_INT(_net_inet_ip_fw, OID_AUTO, tables_sets,
- CTLFLAG_RW, &VNET_NAME(fw_tables_sets), 0,
+SYSCTL_VNET_PROC(_net_inet_ip_fw, OID_AUTO, tables_sets,
+ CTLTYPE_UINT|CTLFLAG_RW, 0, 0, sysctl_ipfw_tables_sets, "IU",
"Use per-set namespace for tables");
SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, default_to_accept, CTLFLAG_RDTUN,
&default_to_accept, 0,
@@ -2569,7 +2570,27 @@ sysctl_ipfw_table_num(SYSCTL_HANDLER_ARG
return (ipfw_resize_tables(&V_layer3_chain, ntables));
}
+
+/*
+ * Switches table namespace between global and per-set.
+ */
+static int
+sysctl_ipfw_tables_sets(SYSCTL_HANDLER_ARGS)
+{
+ int error;
+ unsigned int sets;
+
+ sets = V_fw_tables_sets;
+
+ error = sysctl_handle_int(oidp, &sets, 0, req);
+ /* Read operation or some error */
+ if ((error != 0) || (req->newptr == NULL))
+ return (error);
+
+ return (ipfw_switch_tables_namespace(&V_layer3_chain, sets));
+}
#endif
+
/*
* Module and VNET glue
*/
@@ -2752,8 +2773,7 @@ vnet_ipfw_uninit(const void *unused)
rule->x_next = reap;
reap = rule;
}
- if (chain->map)
- free(chain->map, M_IPFW);
+ free(chain->map, M_IPFW);
ipfw_destroy_skipto_cache(chain);
IPFW_WUNLOCK(chain);
IPFW_UH_WUNLOCK(chain);
Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_dynamic.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_dynamic.c Thu Aug 7 21:37:03 2014 (r269688)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_dynamic.c Thu Aug 7 21:37:31 2014 (r269689)
@@ -196,8 +196,7 @@ static int ipfw_dyn_count; /* number of
static int last_log; /* Log ratelimiting */
static void ipfw_dyn_tick(void *vnetx);
-static void check_dyn_rules(struct ip_fw_chain *, struct ip_fw *,
- int, int, int);
+static void check_dyn_rules(struct ip_fw_chain *, ipfw_range_tlv *, int, int);
#ifdef SYSCTL_NODE
static int sysctl_ipfw_dyn_count(SYSCTL_HANDLER_ARGS);
@@ -1008,7 +1007,7 @@ ipfw_dyn_tick(void * vnetx)
check_ka = 1;
}
- check_dyn_rules(chain, NULL, RESVD_SET, check_ka, 1);
+ check_dyn_rules(chain, NULL, check_ka, 1);
callout_reset_on(&V_ipfw_timeout, hz, ipfw_dyn_tick, vnetx, 0);
@@ -1040,8 +1039,8 @@ ipfw_dyn_tick(void * vnetx)
* are not freed by other instance (see stage 2, 3)
*/
static void
-check_dyn_rules(struct ip_fw_chain *chain, struct ip_fw *rule,
- int set, int check_ka, int timer)
+check_dyn_rules(struct ip_fw_chain *chain, ipfw_range_tlv *rt,
+ int check_ka, int timer)
{
struct mbuf *m0, *m, *mnext, **mtailp;
struct ip *h;
@@ -1105,12 +1104,10 @@ check_dyn_rules(struct ip_fw_chain *chai
/*
* Remove rules which are:
* 1) expired
- * 2) created by given rule
- * 3) created by any rule in given set
+ * 2) matches deletion range
*/
if ((TIME_LEQ(q->expire, time_uptime)) ||
- ((rule != NULL) && (q->rule == rule)) ||
- ((set != RESVD_SET) && (q->rule->set == set))) {
+ (rt != NULL && ipfw_match_range(q->rule, rt))) {
if (TIME_LE(time_uptime, q->expire) &&
q->dyn_type == O_KEEP_STATE &&
V_dyn_keep_states != 0) {
@@ -1324,8 +1321,7 @@ check_dyn_rules(struct ip_fw_chain *chai
* Deletes all dynamic rules originated by given rule or all rules in
* given set. Specify RESVD_SET to indicate set should not be used.
* @chain - pointer to current ipfw rules chain
- * @rule - delete all states originated by given rule if != NULL
- * @set - delete all states originated by any rule in set @set if != RESVD_SET
+ * @rr - delete all states originated by rules in matched range.
*
* Function has to be called with IPFW_UH_WLOCK held.
* Additionally, function assume that dynamic rule/set is
@@ -1333,10 +1329,39 @@ check_dyn_rules(struct ip_fw_chain *chai
* 'deleted' rules.
*/
void
-ipfw_expire_dyn_rules(struct ip_fw_chain *chain, struct ip_fw *rule, int set)
+ipfw_expire_dyn_rules(struct ip_fw_chain *chain, ipfw_range_tlv *rt)
{
- check_dyn_rules(chain, rule, set, 0, 0);
+ check_dyn_rules(chain, rt, 0, 0);
+}
+
+/*
+ * Check if rule contains at least one dynamic opcode.
+ *
+ * Returns 1 if such opcode is found, 0 otherwise.
+ */
+int
+ipfw_is_dyn_rule(struct ip_fw *rule)
+{
+ int cmdlen, l;
+ ipfw_insn *cmd;
+
+ l = rule->cmd_len;
+ cmd = rule->cmd;
+ cmdlen = 0;
+ for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
+ cmdlen = F_LEN(cmd);
+
+ switch (cmd->opcode) {
+ case O_LIMIT:
+ case O_KEEP_STATE:
+ case O_PROBE_STATE:
+ case O_CHECK_STATE:
+ return (1);
+ }
+ }
+
+ return (0);
}
void
Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h Thu Aug 7 21:37:03 2014 (r269688)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h Thu Aug 7 21:37:31 2014 (r269689)
@@ -177,7 +177,8 @@ enum { /* result for matching dynamic ru
*/
struct ip_fw_chain;
struct sockopt_data;
-void ipfw_expire_dyn_rules(struct ip_fw_chain *, struct ip_fw *, int);
+int ipfw_is_dyn_rule(struct ip_fw *rule);
+void ipfw_expire_dyn_rules(struct ip_fw_chain *, ipfw_range_tlv *);
void ipfw_dyn_unlock(ipfw_dyn_rule *q);
struct tcphdr;
@@ -272,7 +273,6 @@ struct ip_fw_chain {
#endif
int static_len; /* total len of static rules (v0) */
uint32_t gencnt; /* NAT generation count */
- struct ip_fw *reap; /* list of rules to reap */
struct ip_fw *default_rule;
struct tables_config *tblcfg; /* tables module data */
void *ifcfg; /* interface module data */
@@ -507,6 +507,7 @@ void ipfw_reap_rules(struct ip_fw *head)
void ipfw_init_counters(void);
void ipfw_destroy_counters(void);
struct ip_fw *ipfw_alloc_rule(struct ip_fw_chain *chain, size_t rulesize);
+int ipfw_match_range(struct ip_fw *rule, ipfw_range_tlv *rt);
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);
@@ -547,6 +548,7 @@ int ipfw_lookup_table_extended(struct ip
void *paddr, uint32_t *val);
int ipfw_init_tables(struct ip_fw_chain *ch);
int ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables);
+int ipfw_switch_tables_namespace(struct ip_fw_chain *ch, unsigned int nsets);
void ipfw_destroy_tables(struct ip_fw_chain *ch);
/* In ip_fw_nat.c -- XXX to be moved to ip_var.h */
Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c Thu Aug 7 21:37:03 2014 (r269688)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c Thu Aug 7 21:37:31 2014 (r269689)
@@ -305,11 +305,12 @@ get_map(struct ip_fw_chain *chain, int e
for (;;) {
struct ip_fw **map;
- int i;
+ int i, mflags;
+
+ mflags = M_ZERO | ((locked != 0) ? M_NOWAIT : M_WAITOK);
i = chain->n_rules + extra;
- map = malloc(i * sizeof(struct ip_fw *), M_IPFW,
- locked ? M_NOWAIT : M_WAITOK);
+ map = malloc(i * sizeof(struct ip_fw *), M_IPFW, mflags);
if (map == NULL) {
printf("%s: cannot allocate map\n", __FUNCTION__);
return NULL;
@@ -623,16 +624,6 @@ ipfw_reap_rules(struct ip_fw *head)
}
/*
- * Used by del_entry() to check if a rule should be kept.
- * Returns 1 if the rule must be kept, 0 otherwise.
- *
- * Called with cmd = {0,1,5}.
- * cmd == 0 matches on rule numbers, excludes rules in RESVD_SET if n == 0 ;
- * cmd == 1 matches on set numbers only, rule numbers are ignored;
- * cmd == 5 matches on rule and set numbers.
- *
- * n == 0 is a wildcard for rule numbers, there is no wildcard for sets.
- *
* Rules to keep are
* (default || reserved || !match_set || !match_number)
* where
@@ -649,14 +640,386 @@ ipfw_reap_rules(struct ip_fw *head)
* // number is ignored for cmd == 1 or n == 0
*
*/
+int
+ipfw_match_range(struct ip_fw *rule, ipfw_range_tlv *rt)
+{
+
+ /* Don't match default rule regardless of query */
+ if (rule->rulenum == IPFW_DEFAULT_RULE)
+ return (0);
+
+ /* Don't match rules in reserved set for flush requests */
+ if ((rt->flags & IPFW_RCFLAG_ALL) != 0 && rule->set == RESVD_SET)
+ return (0);
+
+ /* If we're filtering by set, don't match other sets */
+ if ((rt->flags & IPFW_RCFLAG_SET) != 0 && rule->set != rt->set)
+ return (0);
+
+ if ((rt->flags & IPFW_RCFLAG_RANGE) != 0 &&
+ (rule->rulenum < rt->start_rule || rule->rulenum > rt->end_rule))
+ return (0);
+
+ return (1);
+}
+
+/*
+ * Delete rules matching range @rt.
+ * Saves number of deleted rules in @ndel.
+ *
+ * Returns 0 on success.
+ */
+static int
+delete_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int *ndel)
+{
+ struct ip_fw *reap, *rule, **map;
+ int end, start;
+ int i, n, ndyn, ofs;
+
+ reap = NULL;
+ IPFW_UH_WLOCK(chain); /* arbitrate writers */
+
+ /*
+ * Stage 1: Determine range to inspect.
+ * Range is half-inclusive, e.g [start, end).
+ */
+ start = 0;
+ end = chain->n_rules - 1;
+
+ if ((rt->flags & IPFW_RCFLAG_RANGE) != 0) {
+ start = ipfw_find_rule(chain, rt->start_rule, 0);
+
+ end = ipfw_find_rule(chain, rt->end_rule, 0);
+ if (rt->end_rule != IPFW_DEFAULT_RULE)
+ while (chain->map[end]->rulenum == rt->end_rule)
+ end++;
+ }
+
+ /* Allocate new map of the same size */
+ map = get_map(chain, 0, 1 /* locked */);
+ if (map == NULL) {
+ IPFW_UH_WUNLOCK(chain);
+ return (ENOMEM);
+ }
+
+ n = 0;
+ ndyn = 0;
+ ofs = start;
+ /* 1. bcopy the initial part of the map */
+ if (start > 0)
+ bcopy(chain->map, map, start * sizeof(struct ip_fw *));
+ /* 2. copy active rules between start and end */
+ for (i = start; i < end; i++) {
+ rule = chain->map[i];
+ if (ipfw_match_range(rule, rt) == 0) {
+ map[ofs++] = rule;
+ continue;
+ }
+
+ n++;
+ if (ipfw_is_dyn_rule(rule) != 0)
+ ndyn++;
+ }
+ /* 3. copy the final part of the map */
+ bcopy(chain->map + end, map + ofs,
+ (chain->n_rules - end) * sizeof(struct ip_fw *));
+ /* 4. recalculate skipto cache */
+ update_skipto_cache(chain, map);
+ /* 5. swap the maps (under UH_WLOCK + WHLOCK) */
+ map = swap_map(chain, map, chain->n_rules - n);
+ /* 6. Remove all dynamic states originated by deleted rules */
+ if (ndyn > 0)
+ ipfw_expire_dyn_rules(chain, rt);
+ /* 7. now remove the rules deleted from the old map */
+ for (i = start; i < end; i++) {
+ rule = map[i];
+ if (ipfw_match_range(rule, rt) == 0)
+ continue;
+ chain->static_len -= RULEUSIZE0(rule);
+ rule->x_next = reap;
+ reap = rule;
+ }
+
+ ipfw_unbind_table_list(chain, reap);
+ IPFW_UH_WUNLOCK(chain);
+ ipfw_reap_rules(reap);
+ if (map != NULL)
+ free(map, M_IPFW);
+ *ndel = n;
+ return (0);
+}
+
+/*
+ * Changes set of given rule rannge @rt
+ * with each other.
+ *
+ * Returns 0 on success.
+ */
+static int
+move_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt)
+{
+ struct ip_fw *rule;
+ int i;
+
+ IPFW_UH_WLOCK(chain);
+
+ /*
+ * Move rules with matching paramenerts to a new set.
+ * This one is much more complex. We have to ensure
+ * that all referenced tables (if any) are referenced
+ * by given rule subset only. Otherwise, we can't move
+ * them to new set and have to return error.
+ */
+ if (V_fw_tables_sets != 0) {
+ if (ipfw_move_tables_sets(chain, rt, rt->new_set) != 0) {
+ IPFW_UH_WUNLOCK(chain);
+ return (EBUSY);
+ }
+ }
+
+ /* XXX: We have to do swap holding WLOCK */
+ for (i = 0; i < chain->n_rules - 1; i++) {
+ rule = chain->map[i];
+ if (ipfw_match_range(rule, rt) == 0)
+ continue;
+ rule->set = rt->new_set;
+ }
+
+ IPFW_UH_WUNLOCK(chain);
+
+ return (0);
+}
+
+/*
+ * Clear counters for a specific rule.
+ * Normally run under IPFW_UH_RLOCK, but these are idempotent ops
+ * so we only care that rules do not disappear.
+ */
+static void
+clear_counters(struct ip_fw *rule, int log_only)
+{
+ ipfw_insn_log *l = (ipfw_insn_log *)ACTION_PTR(rule);
+
+ if (log_only == 0)
+ IPFW_ZERO_RULE_COUNTER(rule);
+ if (l->o.opcode == O_LOG)
+ l->log_left = l->max_log;
+}
+
+/*
+ * Flushes rules counters and/or log values on matching range.
+ *
+ * Returns number of items cleared.
+ */
+static int
+clear_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int log_only)
+{
+ struct ip_fw *rule;
+ int num;
+ int i;
+
+ num = 0;
+
+ IPFW_UH_WLOCK(chain); /* arbitrate writers */
+ for (i = 0; i < chain->n_rules - 1; i++) {
+ rule = chain->map[i];
+ if (ipfw_match_range(rule, rt) == 0)
+ continue;
+ clear_counters(rule, log_only);
+ num++;
+ }
+ IPFW_UH_WUNLOCK(chain);
+
+ return (num);
+}
+
static int
-keep_rule(struct ip_fw *rule, uint8_t cmd, uint8_t set, uint32_t n)
+check_range_tlv(ipfw_range_tlv *rt)
{
- return
- (rule->rulenum == IPFW_DEFAULT_RULE) ||
- (cmd == 0 && n == 0 && rule->set == RESVD_SET) ||
- !(cmd == 0 || rule->set == set) ||
- !(cmd == 1 || n == 0 || n == rule->rulenum);
+
+ if (rt->head.length != sizeof(*rt))
+ return (1);
+ if (rt->start_rule > rt->end_rule)
+ return (1);
+ if (rt->set >= IPFW_MAX_SETS || rt->new_set >= IPFW_MAX_SETS)
+ return (1);
+
+ return (0);
+}
+
+/*
+ * Delete rules matching specified parameters
+ * Data layout (v0)(current):
+ * Request: [ ipfw_obj_header ipfw_range_tlv ]
+ * Reply: [ ipfw_obj_header ipfw_range_tlv ]
+ *
+ * Saves number of deleted rules in ipfw_range_tlv->new_set.
+ *
+ * Returns 0 on success.
+ */
+static int
+del_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
+ struct sockopt_data *sd)
+{
+ ipfw_range_header *rh;
+ int error, ndel;
+
+ if (sd->valsize != sizeof(*rh))
+ return (EINVAL);
+
+ rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
+
+ if (check_range_tlv(&rh->range) != 0)
+ return (EINVAL);
+
+ ndel = 0;
+ if ((error = delete_range(chain, &rh->range, &ndel)) != 0)
+ return (error);
+
+ /* Save number of rules deleted */
+ rh->range.new_set = ndel;
+ return (0);
+}
+
+/*
+ * Move rules/sets matching specified parameters
+ * Data layout (v0)(current):
+ * Request: [ ipfw_obj_header ipfw_range_tlv ]
+ *
+ * Returns 0 on success.
+ */
+static int
+move_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
+ struct sockopt_data *sd)
+{
+ ipfw_range_header *rh;
+
+ if (sd->valsize != sizeof(*rh))
+ return (EINVAL);
+
+ rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
+
+ if (check_range_tlv(&rh->range) != 0)
+ return (EINVAL);
+
+ return (move_range(chain, &rh->range));
+}
+
+/*
+ * Clear rule accounting data matching specified parameters
+ * Data layout (v0)(current):
+ * Request: [ ipfw_obj_header ipfw_range_tlv ]
+ * Reply: [ ipfw_obj_header ipfw_range_tlv ]
+ *
+ * Saves number of cleared rules in ipfw_range_tlv->new_set.
+ *
+ * Returns 0 on success.
+ */
+static int
+clear_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
+ struct sockopt_data *sd)
+{
+ ipfw_range_header *rh;
+ int log_only, num;
+ char *msg;
+
+ if (sd->valsize != sizeof(*rh))
+ return (EINVAL);
+
+ rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
+
+ if (check_range_tlv(&rh->range) != 0)
+ return (EINVAL);
+
+ log_only = (op3->opcode == IP_FW_XRESETLOG);
+
+ num = clear_range(chain, &rh->range, log_only);
+
+ if (rh->range.flags & IPFW_RCFLAG_ALL)
+ msg = log_only ? "All logging counts reset" :
+ "Accounting cleared";
+ else
+ msg = log_only ? "logging count reset" : "cleared";
+
+ if (V_fw_verbose) {
+ int lev = LOG_SECURITY | LOG_NOTICE;
+ log(lev, "ipfw: %s.\n", msg);
+ }
+
+ /* Save number of rules cleared */
+ rh->range.new_set = num;
+ return (0);
+}
+
+static void
+enable_sets(struct ip_fw_chain *chain, ipfw_range_tlv *rt)
+{
+ uint32_t v_set;
+
+ IPFW_UH_WLOCK_ASSERT(chain);
+
+ /* Change enabled/disabled sets mask */
+ v_set = (V_set_disable | rt->set) & ~rt->new_set;
+ v_set &= ~(1 << RESVD_SET); /* set RESVD_SET always enabled */
+ IPFW_WLOCK(chain);
+ V_set_disable = v_set;
+ IPFW_WUNLOCK(chain);
+}
+
+static void
+swap_sets(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int mv)
+{
+ struct ip_fw *rule;
+ int i;
+
+ IPFW_UH_WLOCK_ASSERT(chain);
+
+ /* Swap or move two sets */
+ for (i = 0; i < chain->n_rules - 1; i++) {
+ rule = chain->map[i];
+ if (rule->set == rt->set)
+ rule->set = rt->new_set;
+ else if (rule->set == rt->new_set && mv == 0)
+ rule->set = rt->set;
+ }
+ if (V_fw_tables_sets != 0)
+ ipfw_swap_tables_sets(chain, rt->set, rt->new_set, mv);
+}
+
+/*
+ * Swaps or moves set
+ * Data layout (v0)(current):
+ * Request: [ ipfw_obj_header ipfw_range_tlv ]
+ *
+ * Returns 0 on success.
+ */
+static int
+manage_sets(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
+ struct sockopt_data *sd)
+{
+ ipfw_range_header *rh;
+
+ if (sd->valsize != sizeof(*rh))
+ return (EINVAL);
+
+ rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
+
+ if (rh->range.head.length != sizeof(ipfw_range_tlv))
+ return (1);
+
+ IPFW_UH_WLOCK(chain);
+ switch (op3->opcode) {
+ case IP_FW_SET_SWAP:
+ case IP_FW_SET_MOVE:
+ swap_sets(chain, &rh->range, op3->opcode == IP_FW_SET_MOVE);
+ break;
+ case IP_FW_SET_ENABLE:
+ enable_sets(chain, &rh->range);
+ break;
+ }
+ IPFW_UH_WUNLOCK(chain);
+
+ return (0);
}
/**
@@ -676,12 +1039,11 @@ keep_rule(struct ip_fw *rule, uint8_t cm
static int
del_entry(struct ip_fw_chain *chain, uint32_t arg)
{
- struct ip_fw *rule;
uint32_t num; /* rule number or old_set */
uint8_t cmd, new_set;
- int start, end, i, ofs, n;
- struct ip_fw **map = NULL;
+ int do_del, ndel;
int error = 0;
+ ipfw_range_tlv rt;
num = arg & 0xffff;
cmd = (arg >> 24) & 0xff;
@@ -697,151 +1059,60 @@ del_entry(struct ip_fw_chain *chain, uin
return EINVAL;
}
- IPFW_UH_WLOCK(chain); /* arbitrate writers */
- chain->reap = NULL; /* prepare for deletions */
+ /* Convert old requests into new representation */
+ memset(&rt, 0, sizeof(rt));
+ rt.start_rule = num;
+ rt.end_rule = num;
+ rt.set = num;
+ rt.new_set = new_set;
+ do_del = 0;
switch (cmd) {
- case 0: /* delete rules "num" (num == 0 matches all) */
- case 1: /* delete all rules in set N */
- case 5: /* delete rules with number N and set "new_set". */
-
- /*
- * Locate first rule to delete (start), the rule after
- * the last one to delete (end), and count how many
- * rules to delete (n). Always use keep_rule() to
- * determine which rules to keep.
- */
- n = 0;
- if (cmd == 1) {
- /* look for a specific set including RESVD_SET.
- * Must scan the entire range, ignore num.
- */
- new_set = num;
- for (start = -1, end = i = 0; i < chain->n_rules; i++) {
- if (keep_rule(chain->map[i], cmd, new_set, 0))
- continue;
- if (start < 0)
- start = i;
- end = i;
- n++;
- }
- end++; /* first non-matching */
- } else {
- /* Optimized search on rule numbers */
- start = ipfw_find_rule(chain, num, 0);
- for (end = start; end < chain->n_rules; end++) {
- rule = chain->map[end];
- if (num > 0 && rule->rulenum != num)
- break;
- if (!keep_rule(rule, cmd, new_set, num))
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-projects
mailing list