Date: Thu, 7 Aug 2014 21:37:32 +0000 (UTC) From: Alexander V. Chernikov <melifaro@FreeBSD.org> To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r269689 - in projects/ipfw: sbin/ipfw sys/netinet sys/netpfil/ipfw Message-ID: <53e3f19c.2e78.f884e9d@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
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 ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?53e3f19c.2e78.f884e9d>