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.
This commit is contained in:
parent
46d5200874
commit
a73d728d31
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/projects/ipfw/; revision=269689
@ -2110,6 +2110,19 @@ show_dyn_state(struct cmdline_opts *co, struct format_opts *fo,
|
||||
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 optname /* 0 = IP_FW_ZERO, 1 = IP_FW_RESETLOG
|
||||
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 optname /* 0 = IP_FW_ZERO, 1 = IP_FW_RESETLOG
|
||||
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");
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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, default_rule, CTLFLAG_RD,
|
||||
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_ARGS)
|
||||
|
||||
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);
|
||||
|
@ -196,8 +196,7 @@ static int ipfw_dyn_count; /* number of objects */
|
||||
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 *chain, struct ip_fw *rule,
|
||||
/*
|
||||
* 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 *chain, struct ip_fw *rule,
|
||||
* 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 *chain, struct ip_fw *rule,
|
||||
* '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
|
||||
|
@ -177,7 +177,8 @@ enum { /* result for matching dynamic rules */
|
||||
*/
|
||||
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_fw_chain *ch, uint16_t tbl, uint16_t pl
|
||||
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 */
|
||||
|
@ -305,11 +305,12 @@ get_map(struct ip_fw_chain *chain, int extra, int locked)
|
||||
|
||||
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
|
||||
*
|
||||
*/
|
||||
static int
|
||||
keep_rule(struct ip_fw *rule, uint8_t cmd, uint8_t set, uint32_t n)
|
||||
int
|
||||
ipfw_match_range(struct ip_fw *rule, 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);
|
||||
|
||||
/* 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
|
||||
check_range_tlv(ipfw_range_tlv *rt)
|
||||
{
|
||||
|
||||
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 cmd, uint8_t set, uint32_t n)
|
||||
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, uint32_t arg)
|
||||
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))
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
/* A flush request (arg == 0 or cmd == 1) on empty
|
||||
* ruleset returns with no error. On the contrary,
|
||||
* if there is no match on a specific request,
|
||||
* we return EINVAL.
|
||||
*/
|
||||
if (arg != 0 && cmd != 1)
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* We have something to delete. Allocate the new map */
|
||||
map = get_map(chain, -n, 1 /* locked */);
|
||||
if (map == NULL) {
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* 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 = ofs = start; i < end; i++) {
|
||||
rule = chain->map[i];
|
||||
if (keep_rule(rule, cmd, new_set, num))
|
||||
map[ofs++] = rule;
|
||||
}
|
||||
/* 3. copy the final part of the map */
|
||||
bcopy(chain->map + end, map + ofs,
|
||||
(chain->n_rules - end) * sizeof(struct ip_fw *));
|
||||
/* 3.5. recalculate skipto cache */
|
||||
update_skipto_cache(chain, map);
|
||||
/* 4. swap the maps (under UH_WLOCK + WHLOCK) */
|
||||
map = swap_map(chain, map, chain->n_rules - n);
|
||||
/* 5. now remove the rules deleted from the old map */
|
||||
if (cmd == 1)
|
||||
ipfw_expire_dyn_rules(chain, NULL, new_set);
|
||||
for (i = start; i < end; i++) {
|
||||
rule = map[i];
|
||||
if (keep_rule(rule, cmd, new_set, num))
|
||||
continue;
|
||||
chain->static_len -= RULEUSIZE0(rule);
|
||||
if (cmd != 1)
|
||||
ipfw_expire_dyn_rules(chain, rule, RESVD_SET);
|
||||
rule->x_next = chain->reap;
|
||||
chain->reap = rule;
|
||||
}
|
||||
case 0: /* delete rules numbered "rulenum" */
|
||||
if (num == 0)
|
||||
rt.flags |= IPFW_RCFLAG_ALL;
|
||||
else
|
||||
rt.flags |= IPFW_RCFLAG_RANGE;
|
||||
do_del = 1;
|
||||
break;
|
||||
|
||||
/*
|
||||
* In the next 3 cases the loop stops at (n_rules - 1)
|
||||
* because the default rule is never eligible..
|
||||
*/
|
||||
|
||||
case 2: /* move rules with given RULE number to new set */
|
||||
for (i = 0; i < chain->n_rules - 1; i++) {
|
||||
rule = chain->map[i];
|
||||
if (rule->rulenum == num)
|
||||
rule->set = new_set;
|
||||
}
|
||||
case 1: /* delete rules in set "rulenum" */
|
||||
rt.flags |= IPFW_RCFLAG_SET;
|
||||
do_del = 1;
|
||||
break;
|
||||
|
||||
case 3: /* move rules with given SET number to new set */
|
||||
for (i = 0; i < chain->n_rules - 1; i++) {
|
||||
rule = chain->map[i];
|
||||
if (rule->set == num)
|
||||
rule->set = new_set;
|
||||
}
|
||||
case 5: /* delete rules "rulenum" and set "new_set" */
|
||||
rt.flags |= IPFW_RCFLAG_RANGE | IPFW_RCFLAG_SET;
|
||||
rt.set = new_set;
|
||||
rt.new_set = 0;
|
||||
do_del = 1;
|
||||
break;
|
||||
|
||||
case 4: /* swap two sets */
|
||||
for (i = 0; i < chain->n_rules - 1; i++) {
|
||||
rule = chain->map[i];
|
||||
if (rule->set == num)
|
||||
rule->set = new_set;
|
||||
else if (rule->set == new_set)
|
||||
rule->set = num;
|
||||
}
|
||||
case 2: /* move rules "rulenum" to set "new_set" */
|
||||
rt.flags |= IPFW_RCFLAG_RANGE;
|
||||
break;
|
||||
case 3: /* move rules from set "rulenum" to set "new_set" */
|
||||
IPFW_UH_WLOCK(chain);
|
||||
swap_sets(chain, &rt, 1);
|
||||
IPFW_UH_WUNLOCK(chain);
|
||||
return (0);
|
||||
case 4: /* swap sets "rulenum" and "new_set" */
|
||||
IPFW_UH_WLOCK(chain);
|
||||
swap_sets(chain, &rt, 0);
|
||||
IPFW_UH_WUNLOCK(chain);
|
||||
return (0);
|
||||
default:
|
||||
return (ENOTSUP);
|
||||
}
|
||||
|
||||
rule = chain->reap;
|
||||
chain->reap = NULL;
|
||||
ipfw_unbind_table_list(chain, rule);
|
||||
IPFW_UH_WUNLOCK(chain);
|
||||
ipfw_reap_rules(rule);
|
||||
if (map)
|
||||
free(map, M_IPFW);
|
||||
return error;
|
||||
}
|
||||
/*
|
||||
* 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 (do_del != 0) {
|
||||
if ((error = delete_range(chain, &rt, &ndel)) != 0)
|
||||
return (error);
|
||||
|
||||
if (log_only == 0)
|
||||
IPFW_ZERO_RULE_COUNTER(rule);
|
||||
if (l->o.opcode == O_LOG)
|
||||
l->log_left = l->max_log;
|
||||
if (ndel == 0 && (cmd != 1 && num != 0))
|
||||
return (EINVAL);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
return (move_range(chain, &rt));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1654,7 +1925,8 @@ check_object_name(ipfw_obj_ntlv *ntlv)
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
static int
|
||||
add_entry(struct ip_fw_chain *chain, struct sockopt_data *sd)
|
||||
add_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
|
||||
struct sockopt_data *sd)
|
||||
{
|
||||
ipfw_obj_ctlv *ctlv, *rtlv, *tstate;
|
||||
ipfw_obj_ntlv *ntlv;
|
||||
@ -1662,7 +1934,6 @@ add_entry(struct ip_fw_chain *chain, struct sockopt_data *sd)
|
||||
uint32_t count, read;
|
||||
struct ip_fw_rule *r;
|
||||
struct rule_check_info rci, *ci, *cbuf;
|
||||
ip_fw3_opheader *op3;
|
||||
int i, rsize;
|
||||
|
||||
if (sd->valsize > IP_FW3_READBUF)
|
||||
@ -1870,8 +2141,8 @@ ipfw_ctl3(struct sockopt *sopt)
|
||||
* Disallow modifications in really-really secure mode, but still allow
|
||||
* the logging counters to be reset.
|
||||
*/
|
||||
if (opt == IP_FW_ADD ||
|
||||
(sopt->sopt_dir == SOPT_SET && opt != IP_FW_RESETLOG)) {
|
||||
if (opt == IP_FW_XADD || opt == IP_FW_XDEL ||
|
||||
(sopt->sopt_dir == SOPT_SET && opt != IP_FW_XRESETLOG)) {
|
||||
error = securelevel_ge(sopt->sopt_td->td_ucred, 3);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
@ -1928,13 +2199,33 @@ ipfw_ctl3(struct sockopt *sopt)
|
||||
error = dump_config(chain, &sdata);
|
||||
break;
|
||||
|
||||
case IP_FW_XADD:
|
||||
error = add_rules(chain, op3, &sdata);
|
||||
break;
|
||||
|
||||
case IP_FW_XDEL:
|
||||
error = del_rules(chain, op3, &sdata);
|
||||
break;
|
||||
|
||||
case IP_FW_XZERO:
|
||||
case IP_FW_XRESETLOG:
|
||||
error = clear_rules(chain, op3, &sdata);
|
||||
break;
|
||||
|
||||
case IP_FW_XMOVE:
|
||||
error = move_rules(chain, op3, &sdata);
|
||||
break;
|
||||
|
||||
case IP_FW_SET_SWAP:
|
||||
case IP_FW_SET_MOVE:
|
||||
case IP_FW_SET_ENABLE:
|
||||
error = manage_sets(chain, op3, &sdata);
|
||||
break;
|
||||
|
||||
case IP_FW_XIFLIST:
|
||||
error = ipfw_list_ifaces(chain, &sdata);
|
||||
break;
|
||||
|
||||
case IP_FW_XADD:
|
||||
error = add_entry(chain, &sdata);
|
||||
break;
|
||||
/*--- TABLE opcodes ---*/
|
||||
case IP_FW_TABLE_XCREATE:
|
||||
error = ipfw_create_table(chain, op3, &sdata);
|
||||
|
@ -76,9 +76,11 @@ 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 ochanged; /* used by set swapping */
|
||||
uint32_t count; /* Number of records */
|
||||
uint32_t limit; /* Max number of records */
|
||||
uint32_t ocount; /* used by set swapping */
|
||||
uint64_t gencnt; /* generation count */
|
||||
char tablename[64]; /* table name */
|
||||
struct table_algo *ta; /* Callbacks for given algo */
|
||||
void *astate; /* algorithm state */
|
||||
@ -117,7 +119,7 @@ static int ipfw_manage_table_ent_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3
|
||||
struct sockopt_data *sd);
|
||||
static int ipfw_manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
||||
struct sockopt_data *sd);
|
||||
static int swap_table(struct ip_fw_chain *ch, struct tid_info *a,
|
||||
static int swap_tables(struct ip_fw_chain *ch, struct tid_info *a,
|
||||
struct tid_info *b);
|
||||
|
||||
static int check_table_space(struct ip_fw_chain *ch, struct table_config *tc,
|
||||
@ -129,6 +131,7 @@ static struct table_algo *find_table_algo(struct tables_config *tableconf,
|
||||
|
||||
static void objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti);
|
||||
static void ntlv_to_ti(struct _ipfw_obj_ntlv *ntlv, struct tid_info *ti);
|
||||
static int classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype);
|
||||
|
||||
#define CHAIN_TO_TCFG(chain) ((struct tables_config *)(chain)->tblcfg)
|
||||
#define CHAIN_TO_NI(chain) (CHAIN_TO_TCFG(chain)->namehash)
|
||||
@ -774,13 +777,13 @@ ipfw_swap_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
||||
ntlv_to_ti(&oh->ntlv, &ti_a);
|
||||
ntlv_to_ti((ipfw_obj_ntlv *)(oh + 1), &ti_b);
|
||||
|
||||
error = swap_table(ch, &ti_a, &ti_b);
|
||||
error = swap_tables(ch, &ti_a, &ti_b);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
swap_table(struct ip_fw_chain *ch, struct tid_info *a,
|
||||
swap_tables(struct ip_fw_chain *ch, struct tid_info *a,
|
||||
struct tid_info *b)
|
||||
{
|
||||
struct namedobj_instance *ni;
|
||||
@ -1025,6 +1028,61 @@ ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Switch between "set 0" and "rule set" table binding,
|
||||
* Check all ruleset bindings and permits changing
|
||||
* IFF each binding has both rule AND table in default set (set 0).
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int
|
||||
ipfw_switch_tables_namespace(struct ip_fw_chain *ch, unsigned int sets)
|
||||
{
|
||||
struct namedobj_instance *ni;
|
||||
struct named_object *no;
|
||||
struct ip_fw *rule;
|
||||
ipfw_insn *cmd;
|
||||
int cmdlen, i, l;
|
||||
uint16_t kidx;
|
||||
uint8_t type;
|
||||
|
||||
IPFW_UH_WLOCK(ch);
|
||||
|
||||
if (V_fw_tables_sets == sets) {
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
return (0);
|
||||
}
|
||||
|
||||
ni = CHAIN_TO_NI(ch);
|
||||
|
||||
for (i = 0; i < ch->n_rules; i++) {
|
||||
rule = ch->map[i];
|
||||
|
||||
l = rule->cmd_len;
|
||||
cmd = rule->cmd;
|
||||
cmdlen = 0;
|
||||
for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
|
||||
cmdlen = F_LEN(cmd);
|
||||
|
||||
if (classify_table_opcode(cmd, &kidx, &type) != 0)
|
||||
continue;
|
||||
|
||||
no = ipfw_objhash_lookup_kidx(ni, kidx);
|
||||
|
||||
if (no->set != 0 || rule->set != 0) {
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
return (EBUSY);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
V_fw_tables_sets = sets;
|
||||
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
|
||||
uint32_t *val)
|
||||
@ -2302,6 +2360,195 @@ bind_table_rule(struct ip_fw_chain *ch, struct ip_fw *rule,
|
||||
return (error);
|
||||
}
|
||||
|
||||
struct swap_table_args {
|
||||
int set;
|
||||
int new_set;
|
||||
int mv;
|
||||
};
|
||||
|
||||
/*
|
||||
* Change set for each matching table.
|
||||
*
|
||||
* Ensure we dispatch each table once by setting/checking ochange
|
||||
* fields.
|
||||
*/
|
||||
static void
|
||||
swap_table_set(struct namedobj_instance *ni, struct named_object *no,
|
||||
void *arg)
|
||||
{
|
||||
struct table_config *tc;
|
||||
struct swap_table_args *sta;
|
||||
|
||||
tc = (struct table_config *)no;
|
||||
sta = (struct swap_table_args *)arg;
|
||||
|
||||
if (no->set != sta->set && (no->set != sta->new_set || sta->mv != 0))
|
||||
return;
|
||||
|
||||
if (tc->ochanged != 0)
|
||||
return;
|
||||
|
||||
tc->ochanged = 1;
|
||||
ipfw_objhash_del(ni, no);
|
||||
if (no->set == sta->set)
|
||||
no->set = sta->new_set;
|
||||
else
|
||||
no->set = sta->set;
|
||||
ipfw_objhash_add(ni, no);
|
||||
}
|
||||
|
||||
/*
|
||||
* Cleans up ochange field for all tables.
|
||||
*/
|
||||
static void
|
||||
clean_table_set_data(struct namedobj_instance *ni, struct named_object *no,
|
||||
void *arg)
|
||||
{
|
||||
struct table_config *tc;
|
||||
struct swap_table_args *sta;
|
||||
|
||||
tc = (struct table_config *)no;
|
||||
sta = (struct swap_table_args *)arg;
|
||||
|
||||
tc->ochanged = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Swaps tables within two sets.
|
||||
*/
|
||||
void
|
||||
ipfw_swap_tables_sets(struct ip_fw_chain *ch, uint32_t set,
|
||||
uint32_t new_set, int mv)
|
||||
{
|
||||
struct swap_table_args sta;
|
||||
|
||||
IPFW_UH_WLOCK_ASSERT(ch);
|
||||
|
||||
sta.set = set;
|
||||
sta.new_set = new_set;
|
||||
sta.mv = mv;
|
||||
|
||||
ipfw_objhash_foreach(CHAIN_TO_NI(ch), swap_table_set, &sta);
|
||||
ipfw_objhash_foreach(CHAIN_TO_NI(ch), clean_table_set_data, &sta);
|
||||
}
|
||||
|
||||
/*
|
||||
* Move all tables which are reference by rules in @rr to set @new_set.
|
||||
* Makes sure that all relevant tables are referenced ONLLY by given rules.
|
||||
*
|
||||
* Retuns 0 on success,
|
||||
*/
|
||||
int
|
||||
ipfw_move_tables_sets(struct ip_fw_chain *ch, ipfw_range_tlv *rt,
|
||||
uint32_t new_set)
|
||||
{
|
||||
struct ip_fw *rule;
|
||||
struct table_config *tc;
|
||||
struct named_object *no;
|
||||
struct namedobj_instance *ni;
|
||||
int bad, i, l, cmdlen;
|
||||
uint16_t kidx;
|
||||
uint8_t type;
|
||||
ipfw_insn *cmd;
|
||||
|
||||
IPFW_UH_WLOCK_ASSERT(ch);
|
||||
|
||||
ni = CHAIN_TO_NI(ch);
|
||||
|
||||
/* Stage 1: count number of references by given rules */
|
||||
for (i = 0; i < ch->n_rules - 1; i++) {
|
||||
rule = ch->map[i];
|
||||
if (ipfw_match_range(rule, rt) == 0)
|
||||
continue;
|
||||
|
||||
l = rule->cmd_len;
|
||||
cmd = rule->cmd;
|
||||
cmdlen = 0;
|
||||
for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
|
||||
cmdlen = F_LEN(cmd);
|
||||
if (classify_table_opcode(cmd, &kidx, &type) != 0)
|
||||
continue;
|
||||
no = ipfw_objhash_lookup_kidx(ni, kidx);
|
||||
KASSERT(no != NULL,
|
||||
("objhash lookup failed on index %d", kidx));
|
||||
tc = (struct table_config *)no;
|
||||
tc->ocount++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Stage 2: verify "ownership" */
|
||||
bad = 0;
|
||||
for (i = 0; i < ch->n_rules - 1; i++) {
|
||||
rule = ch->map[i];
|
||||
if (ipfw_match_range(rule, rt) == 0)
|
||||
continue;
|
||||
|
||||
l = rule->cmd_len;
|
||||
cmd = rule->cmd;
|
||||
cmdlen = 0;
|
||||
for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
|
||||
cmdlen = F_LEN(cmd);
|
||||
if (classify_table_opcode(cmd, &kidx, &type) != 0)
|
||||
continue;
|
||||
no = ipfw_objhash_lookup_kidx(ni, kidx);
|
||||
KASSERT(no != NULL,
|
||||
("objhash lookup failed on index %d", kidx));
|
||||
tc = (struct table_config *)no;
|
||||
if (tc->no.refcnt != tc->ocount) {
|
||||
|
||||
/*
|
||||
* Number of references differ:
|
||||
* Other rule(s) are holding reference to given
|
||||
* table, so it is not possible to change its set.
|
||||
*
|
||||
* Note that refcnt may account
|
||||
* references to some going-to-be-added rules.
|
||||
* Since we don't know their numbers (and event
|
||||
* if they will be added) it is perfectly OK
|
||||
* to return error here.
|
||||
*/
|
||||
bad = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bad != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Stage 3: change set or cleanup */
|
||||
for (i = 0; i < ch->n_rules - 1; i++) {
|
||||
rule = ch->map[i];
|
||||
if (ipfw_match_range(rule, rt) == 0)
|
||||
continue;
|
||||
|
||||
l = rule->cmd_len;
|
||||
cmd = rule->cmd;
|
||||
cmdlen = 0;
|
||||
for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
|
||||
cmdlen = F_LEN(cmd);
|
||||
if (classify_table_opcode(cmd, &kidx, &type) != 0)
|
||||
continue;
|
||||
no = ipfw_objhash_lookup_kidx(ni, kidx);
|
||||
KASSERT(no != NULL,
|
||||
("objhash lookup failed on index %d", kidx));
|
||||
tc = (struct table_config *)no;
|
||||
|
||||
tc->ocount = 0;
|
||||
if (bad != 0)
|
||||
continue;
|
||||
|
||||
/* Actually change set. */
|
||||
ipfw_objhash_del(ni, no);
|
||||
no->set = new_set;
|
||||
ipfw_objhash_add(ni, no);
|
||||
}
|
||||
}
|
||||
|
||||
return (bad);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compatibility function for old ipfw(8) binaries.
|
||||
* Rewrites table kernel indices with userland ones.
|
||||
|
@ -174,6 +174,10 @@ void ipfw_unbind_table_list(struct ip_fw_chain *chain, struct ip_fw *head);
|
||||
|
||||
/* utility functions */
|
||||
int ipfw_check_table_name(char *name);
|
||||
int ipfw_move_tables_sets(struct ip_fw_chain *ch, ipfw_range_tlv *rt,
|
||||
uint32_t new_set);
|
||||
void ipfw_swap_tables_sets(struct ip_fw_chain *ch, uint32_t old_set,
|
||||
uint32_t new_set, int mv);
|
||||
|
||||
/* Legacy interfaces */
|
||||
int ipfw_count_table(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||
|
Loading…
Reference in New Issue
Block a user