Page MenuHomeFreeBSD

D56660.id176671.diff
No OneTemporary

D56660.id176671.diff

diff --git a/sys/modules/Makefile b/sys/modules/Makefile
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -182,6 +182,7 @@
ipdivert \
${_ipfilter} \
${_ipfw} \
+ ${_ipfw_compat} \
ipfw_nat \
${_ipfw_nat64} \
${_ipfw_nptv6} \
@@ -506,6 +507,7 @@
.if (${KERN_OPTS:MINET} && ${MK_INET_SUPPORT} != "no") || defined(ALL_MODULES)
_if_me= if_me
_ipfw= ipfw
+_ipfw_compat= ipfw_compat
.if ${MK_INET6_SUPPORT} != "no" || defined(ALL_MODULES)
_ipfw_nat64= ipfw_nat64
.endif
diff --git a/sys/modules/ipfw_compat/Makefile b/sys/modules/ipfw_compat/Makefile
new file mode 100644
--- /dev/null
+++ b/sys/modules/ipfw_compat/Makefile
@@ -0,0 +1,6 @@
+.PATH: ${SRCTOP}/sys/netpfil/ipfw
+
+KMOD= ipfw_compat
+SRCS= ip_fw_compat.c opt_inet.h opt_inet6.h opt_ipfw.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/netpfil/ipfw/ip_fw_compat.c b/sys/netpfil/ipfw/ip_fw_compat.c
--- a/sys/netpfil/ipfw/ip_fw_compat.c
+++ b/sys/netpfil/ipfw/ip_fw_compat.c
@@ -155,6 +155,9 @@
clear_rules_v0, move_rules_v0, manage_sets_v0, dump_soptcodes_v0,
dump_srvobjects_v0;
+static sopt_handler_f manage_table_ent_v1_compat, find_table_entry_compat,
+ dump_table_v1_compat;
+
static struct ipfw_sopt_handler scodes[] = {
{ IP_FW_XGET, IP_FW3_OPVER_0, HDIR_GET, dump_config_v0 },
{ IP_FW_XADD, IP_FW3_OPVER_0, HDIR_BOTH, add_rules_v0 },
@@ -167,13 +170,482 @@
{ IP_FW_SET_ENABLE, IP_FW3_OPVER_0, HDIR_SET, manage_sets_v0 },
{ IP_FW_DUMP_SOPTCODES, IP_FW3_OPVER_0, HDIR_GET, dump_soptcodes_v0 },
{ IP_FW_DUMP_SRVOBJECTS, IP_FW3_OPVER_0, HDIR_GET, dump_srvobjects_v0 },
+ /*
+ * IP_FW_TABLE_* sockopts.
+ *
+ * The on-wire structures used by these handlers (ipfw_obj_header,
+ * ipfw_obj_ntlv, ipfw_obj_tentry, ipfw_xtable_info) preserved their
+ * total size in the v0 -> v1 transition; only the layout of a small
+ * idx/spare pair changed inside ipfw_obj_header and ipfw_obj_ntlv,
+ * and the analogous idx/spare1 pair in ipfw_obj_tentry.
+ *
+ * Most table operations (XCREATE, XINFO, XDESTROY, XFLUSH, XMODIFY,
+ * XSWAP, XLIST tables) only care about ntlv.name, which is at the
+ * same offset in both layouts, so the v1 handlers can be reused
+ * verbatim. Operations that look the table up via tent->idx or that
+ * cross-reference oh->ntlv.idx with tent->idx (XADD, XDEL, XFIND,
+ * XLIST entries) need an explicit conversion wrapper - see the
+ * v1_overrides[] array below.
+ */
+ { IP_FW_TABLE_XCREATE, IP_FW3_OPVER_0, HDIR_SET, create_table },
+ { IP_FW_TABLE_XDESTROY, IP_FW3_OPVER_0, HDIR_SET, flush_table_v0 },
+ { IP_FW_TABLE_XFLUSH, IP_FW3_OPVER_0, HDIR_SET, flush_table_v0 },
+ { IP_FW_TABLE_XMODIFY, IP_FW3_OPVER_0, HDIR_BOTH, modify_table },
+ { IP_FW_TABLE_XINFO, IP_FW3_OPVER_0, HDIR_GET, describe_table },
+ { IP_FW_TABLES_XLIST, IP_FW3_OPVER_0, HDIR_GET, list_tables },
+ { IP_FW_TABLE_XSWAP, IP_FW3_OPVER_0, HDIR_SET, swap_table },
+ /*
+ * The 14.x ipfw(8) sends version=0 only for XFIND; XADD/XDEL/XLIST
+ * are sent with version=1 (the original wire version of those
+ * handlers) and therefore reach the kernel through v1_overrides[]
+ * below. We still register the wrapper under version=0 here so that
+ * builds of 14.x ipfw(8) which omit the version=1 override land in
+ * the same conversion path.
+ */
+ { IP_FW_TABLE_XADD, IP_FW3_OPVER_0, HDIR_BOTH,
+ manage_table_ent_v1_compat },
+ { IP_FW_TABLE_XDEL, IP_FW3_OPVER_0, HDIR_BOTH,
+ manage_table_ent_v1_compat },
+ { IP_FW_TABLE_XFIND, IP_FW3_OPVER_0, HDIR_GET,
+ find_table_entry_compat },
+ { IP_FW_TABLE_XLIST, IP_FW3_OPVER_0, HDIR_GET,
+ dump_table_v1_compat },
+};
+
+/*
+ * Originals taken over from ip_fw_table.c at module load. We DELete the
+ * original (opcode, version=1, original-handler) registration and ADD our
+ * compat wrapper in its place; on unload we reverse the operation.
+ */
+static struct ipfw_sopt_handler v1_originals[] = {
+ { IP_FW_TABLE_XADD, IP_FW3_OPVER, HDIR_BOTH, manage_table_ent_v1 },
+ { IP_FW_TABLE_XDEL, IP_FW3_OPVER, HDIR_BOTH, manage_table_ent_v1 },
+ { IP_FW_TABLE_XLIST, IP_FW3_OPVER, HDIR_GET, dump_table_v1 },
+ { IP_FW_TABLE_XFIND, IP_FW3_OPVER, HDIR_GET, find_table_entry },
};
+static struct ipfw_sopt_handler v1_overrides[] = {
+ { IP_FW_TABLE_XADD, IP_FW3_OPVER, HDIR_BOTH,
+ manage_table_ent_v1_compat },
+ { IP_FW_TABLE_XDEL, IP_FW3_OPVER, HDIR_BOTH,
+ manage_table_ent_v1_compat },
+ { IP_FW_TABLE_XLIST, IP_FW3_OPVER, HDIR_GET,
+ dump_table_v1_compat },
+ { IP_FW_TABLE_XFIND, IP_FW3_OPVER, HDIR_GET,
+ find_table_entry_compat },
+};
+
+/*
+ * Calculate v0 cmd length for a v1 opcode (inverse of adjust_size_v0()).
+ * Returns v0 length in u32 words for @cmd encoded in v1.
+ */
+static int
+v0_cmdlen_for_v1(ipfw_insn *cmd)
+{
+ int cmdlen;
+
+ cmdlen = F_LEN(cmd);
+ switch (cmd->opcode) {
+ case O_CHECK_STATE:
+ case O_KEEP_STATE:
+ case O_PROBE_STATE:
+ case O_EXTERNAL_ACTION:
+ case O_EXTERNAL_INSTANCE:
+ return (F_INSN_SIZE(ipfw_insn));
+ case O_LIMIT:
+ return (F_INSN_SIZE(ipfw_insn_limit_v0));
+ case O_IP_SRC_LOOKUP:
+ case O_IP_DST_LOOKUP:
+ case O_IP_FLOW_LOOKUP:
+ case O_MAC_SRC_LOOKUP:
+ case O_MAC_DST_LOOKUP:
+ if (cmdlen == F_INSN_SIZE(ipfw_insn_kidx))
+ return (F_INSN_SIZE(ipfw_insn));
+ if (cmdlen == F_INSN_SIZE(ipfw_insn_table))
+ return (F_INSN_SIZE(ipfw_insn_u32));
+ return (cmdlen);
+ case O_SKIPTO:
+ case O_CALLRETURN:
+ return (F_INSN_SIZE(ipfw_insn));
+ default:
+ return (cmdlen);
+ }
+}
+
+/*
+ * Inverse of convert_v0_to_v1(): copy @src v1 opcode stream into @dst v0.
+ * Returns number of u32 words written to @dst.
+ */
+static void
+convert_v1_to_v0(ipfw_insn *src, ipfw_insn *dst, int cmd_len_v1,
+ uint16_t *act_ofs_v0, uint16_t act_ofs_v1)
+{
+ ipfw_insn *start = dst;
+ int l, cmdlen, newlen;
+
+ *act_ofs_v0 = 0;
+ for (l = cmd_len_v1; l > 0;
+ l -= cmdlen, src += cmdlen, dst += newlen) {
+ cmdlen = F_LEN(src);
+ if (cmd_len_v1 - l == act_ofs_v1)
+ *act_ofs_v0 = dst - start;
+ switch (src->opcode) {
+ case O_CHECK_STATE:
+ case O_KEEP_STATE:
+ case O_PROBE_STATE:
+ case O_EXTERNAL_ACTION:
+ case O_EXTERNAL_INSTANCE:
+ newlen = F_INSN_SIZE(ipfw_insn);
+ dst->opcode = src->opcode;
+ dst->len = (src->len & (F_NOT | F_OR)) | newlen;
+ dst->arg1 = (uint16_t)insntoc(src, kidx)->kidx;
+ break;
+ case O_LIMIT: {
+ ipfw_insn_limit_v0 *d0;
+ const ipfw_insn_limit *s1;
+
+ newlen = F_INSN_SIZE(ipfw_insn_limit_v0);
+ s1 = insntoc(src, limit);
+ d0 = (ipfw_insn_limit_v0 *)dst;
+ d0->o.opcode = src->opcode;
+ d0->o.len = (src->len & (F_NOT | F_OR)) | newlen;
+ d0->o.arg1 = (uint16_t)s1->kidx;
+ d0->_pad = 0;
+ d0->limit_mask = s1->limit_mask;
+ d0->conn_limit = s1->conn_limit;
+ break;
+ }
+ case O_IP_SRC_LOOKUP:
+ case O_IP_DST_LOOKUP:
+ case O_IP_FLOW_LOOKUP:
+ case O_MAC_SRC_LOOKUP:
+ case O_MAC_DST_LOOKUP:
+ if (cmdlen == F_INSN_SIZE(ipfw_insn_kidx)) {
+ newlen = F_INSN_SIZE(ipfw_insn);
+ dst->opcode = src->opcode;
+ dst->len = (src->len & (F_NOT | F_OR)) | newlen;
+ dst->arg1 =
+ (uint16_t)insntoc(src, kidx)->kidx;
+ } else if (cmdlen == F_INSN_SIZE(ipfw_insn_table)) {
+ const ipfw_insn_table *st;
+ ipfw_insn_u32 *d32;
+
+ newlen = F_INSN_SIZE(ipfw_insn_u32);
+ st = insntoc(src, table);
+ d32 = (ipfw_insn_u32 *)dst;
+ d32->o.opcode = src->opcode;
+ d32->o.len =
+ (src->len & (F_NOT | F_OR)) | newlen;
+ d32->o.arg1 = (uint16_t)st->kidx;
+ d32->d[0] = st->value;
+ } else {
+ newlen = cmdlen;
+ memcpy(dst, src, sizeof(uint32_t) * newlen);
+ }
+ break;
+ case O_SKIPTO:
+ case O_CALLRETURN:
+ newlen = F_INSN_SIZE(ipfw_insn);
+ dst->opcode = src->opcode;
+ dst->len = (src->len & (F_NOT | F_OR)) | newlen;
+ dst->arg1 =
+ (uint16_t)insntoc(src, u32)->d[0];
+ break;
+ default:
+ newlen = cmdlen;
+ memcpy(dst, src, sizeof(uint32_t) * newlen);
+ break;
+ }
+ }
+ if (cmd_len_v1 == act_ofs_v1)
+ *act_ofs_v0 = dst - start;
+}
+
+/*
+ * Compute v0 cmd_len (in u32 words) for @krule's cmd stream.
+ */
+static uint16_t
+v0_cmd_len(struct ip_fw *krule)
+{
+ ipfw_insn *cmd;
+ int l, cmdlen;
+ uint16_t total;
+
+ total = 0;
+ cmd = krule->cmd;
+ for (l = krule->cmd_len; l > 0; l -= cmdlen, cmd += cmdlen) {
+ cmdlen = F_LEN(cmd);
+ total += v0_cmdlen_for_v1(cmd);
+ }
+ return (total);
+}
+
+/*
+ * Total size (in bytes) of a single exported v0 rule, including TLV header.
+ * Mirrors RULEUSIZE1() but for the (possibly shorter) v0 encoding.
+ */
+static size_t
+ruleusize1_v0(struct ip_fw *krule)
+{
+ uint16_t cmd_len;
+
+ cmd_len = v0_cmd_len(krule);
+ return (roundup2(sizeof(struct ip_fw_rule) + cmd_len * 4 - 4, 8));
+}
+
+/*
+ * Export @krule into v0 userland buffer @data.
+ * Layout:
+ * [ ipfw_obj_tlv(IPFW_TLV_RULE_ENT) [ ip_fw_bcounter (optional) ip_fw_rule ] ]
+ * Assumes @data is zeroed.
+ */
+static void
+export_rule_v0(struct ip_fw *krule, caddr_t data, int len, int rcntrs)
+{
+ struct ip_fw_bcounter *cntr;
+ struct ip_fw_rule *urule;
+ ipfw_obj_tlv *tlv;
+ uint16_t act_ofs;
+
+ tlv = (ipfw_obj_tlv *)data;
+ tlv->type = IPFW_TLV_RULE_ENT;
+ tlv->length = len;
+
+ if (rcntrs != 0) {
+ cntr = (struct ip_fw_bcounter *)(tlv + 1);
+ urule = (struct ip_fw_rule *)(cntr + 1);
+ export_cntr1_base(krule, cntr);
+ } else
+ urule = (struct ip_fw_rule *)(tlv + 1);
+
+ convert_v1_to_v0(krule->cmd, urule->cmd, krule->cmd_len, &act_ofs,
+ krule->act_ofs);
+
+ urule->act_ofs = act_ofs;
+ urule->cmd_len = v0_cmd_len(krule);
+ urule->rulenum = krule->rulenum;
+ urule->set = krule->set;
+ urule->flags = krule->flags;
+ urule->id = krule->id;
+}
+
+/*
+ * Dump static rules (with v0 cmd stream) in @sd. Mirrors dump_static_rules()
+ * but writes v0-encoded rules.
+ */
+static int
+dump_static_rules_v0(struct ip_fw_chain *chain, struct rule_dump_args *da,
+ struct sockopt_data *sd)
+{
+ ipfw_obj_ctlv *ctlv;
+ struct ip_fw *krule;
+ caddr_t dst;
+ uint32_t i;
+ int l;
+
+ ctlv = (ipfw_obj_ctlv *)ipfw_get_sopt_space(sd, sizeof(*ctlv));
+ if (ctlv == NULL)
+ return (ENOMEM);
+ ctlv->head.type = IPFW_TLV_RULE_LIST;
+ ctlv->head.length = da->rsize + sizeof(*ctlv);
+ ctlv->count = da->rcount;
+
+ for (i = da->b; i < da->e; i++) {
+ krule = chain->map[i];
+
+ /* Skip rules with rulenum that doesn't fit v0 userland */
+ if (krule->rulenum > IPFW_DEFAULT_RULE)
+ continue;
+
+ l = ruleusize1_v0(krule) + sizeof(ipfw_obj_tlv);
+ if (da->rcounters != 0)
+ l += sizeof(struct ip_fw_bcounter);
+ dst = (caddr_t)ipfw_get_sopt_space(sd, l);
+ if (dst == NULL)
+ return (ENOMEM);
+
+ export_rule_v0(krule, dst, l, da->rcounters);
+ }
+
+ return (0);
+}
+
+/*
+ * Export one named object as ipfw_obj_ntlv_v0 (16-bit idx).
+ * Returns 0 on success or ENOMEM.
+ */
+static int
+export_objhash_ntlv_v0(struct namedobj_instance *ni, uint32_t kidx,
+ struct sockopt_data *sd)
+{
+ struct named_object *no;
+ ipfw_obj_ntlv_v0 *ntlv;
+
+ no = ipfw_objhash_lookup_kidx(ni, kidx);
+ KASSERT(no != NULL, ("invalid object kernel index passed"));
+
+ ntlv = (ipfw_obj_ntlv_v0 *)ipfw_get_sopt_space(sd, sizeof(*ntlv));
+ if (ntlv == NULL)
+ return (ENOMEM);
+
+ ntlv->head.type = no->etlv;
+ ntlv->head.length = sizeof(*ntlv);
+ /* v0 idx is 16-bit; drop high bits if present. */
+ ntlv->idx = (uint16_t)no->kidx;
+ strlcpy(ntlv->name, no->name, sizeof(ntlv->name));
+ return (0);
+}
+
+static int
+export_named_objects_v0(struct namedobj_instance *ni,
+ struct rule_dump_args *da, struct sockopt_data *sd)
+{
+ uint32_t i;
+ int error;
+
+ for (i = 0; i < IPFW_TABLES_MAX && da->tcount > 0; i++) {
+ if ((da->bmask[i / 32] & (1 << (i % 32))) == 0)
+ continue;
+ if ((error = export_objhash_ntlv_v0(ni, i, sd)) != 0)
+ return (error);
+ da->tcount--;
+ }
+ return (0);
+}
+
+static int
+dump_named_objects_v0(struct ip_fw_chain *ch, struct rule_dump_args *da,
+ struct sockopt_data *sd)
+{
+ ipfw_obj_ctlv *ctlv;
+ int error;
+
+ MPASS(da->tcount > 0);
+ ctlv = (ipfw_obj_ctlv *)ipfw_get_sopt_space(sd, sizeof(*ctlv));
+ if (ctlv == NULL)
+ return (ENOMEM);
+ ctlv->head.type = IPFW_TLV_TBLNAME_LIST;
+ ctlv->head.length = da->tcount * sizeof(ipfw_obj_ntlv_v0) +
+ sizeof(*ctlv);
+ ctlv->count = da->tcount;
+ ctlv->objsize = sizeof(ipfw_obj_ntlv_v0);
+
+ error = export_named_objects_v0(ipfw_get_table_objhash(ch), da, sd);
+ if (error != 0)
+ return (error);
+ da->bmask += IPFW_TABLES_MAX / 32;
+ return (export_named_objects_v0(CHAIN_TO_SRV(ch), da, sd));
+}
+
+/*
+ * Dumps requested objects data (v0 layout). Mirrors dump_config() in
+ * ip_fw_sockopt.c but emits v0-sized ipfw_obj_ntlv and converts the per-rule
+ * cmd stream back to v0 encoding. Dynamic states are not exported in v0 yet.
+ *
+ * Data layout (v0):
+ * Request: [ ipfw_cfg_lheader ] + IPFW_CFG_GET_* flags
+ * Reply: [ ipfw_cfg_lheader
+ * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv_v0 x N ] (optional)
+ * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST)
+ * ipfw_obj_tlv(IPFW_TLV_RULE_ENT) [ ip_fw_bcounter? ip_fw_rule ]
+ * ] (optional)
+ * ]
+ */
static int
dump_config_v0(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
- return (EOPNOTSUPP);
+ struct rule_dump_args da;
+ ipfw_cfg_lheader *hdr;
+ struct ip_fw *rule;
+ size_t sz, rnum;
+ uint32_t hdr_flags, *bmask;
+ int error, i;
+
+ hdr = (ipfw_cfg_lheader *)ipfw_get_sopt_header(sd, sizeof(*hdr));
+ if (hdr == NULL)
+ return (EINVAL);
+
+ error = 0;
+ bmask = NULL;
+ memset(&da, 0, sizeof(da));
+ if (hdr->flags & (IPFW_CFG_GET_STATIC | IPFW_CFG_GET_STATES))
+ da.bmask = bmask = malloc(
+ sizeof(uint32_t) * IPFW_TABLES_MAX * 2 / 32, M_TEMP,
+ M_WAITOK | M_ZERO);
+ IPFW_UH_RLOCK(chain);
+
+ /*
+ * STAGE 1: Determine size/count for objects in range.
+ */
+ sz = sizeof(ipfw_cfg_lheader);
+ da.e = chain->n_rules;
+
+ if (hdr->end_rule != 0) {
+ if ((rnum = hdr->start_rule) > IPFW_DEFAULT_RULE)
+ rnum = IPFW_DEFAULT_RULE;
+ da.b = ipfw_find_rule(chain, rnum, 0);
+ rnum = (hdr->end_rule < IPFW_DEFAULT_RULE) ?
+ hdr->end_rule + 1: IPFW_DEFAULT_RULE;
+ da.e = ipfw_find_rule(chain, rnum, UINT32_MAX) + 1;
+ }
+
+ if (hdr->flags & IPFW_CFG_GET_STATIC) {
+ for (i = da.b; i < da.e; i++) {
+ rule = chain->map[i];
+ /* Hide rules with rulenum > 16-bit from v0. */
+ if (rule->rulenum > IPFW_DEFAULT_RULE)
+ continue;
+ da.rsize += ruleusize1_v0(rule) + sizeof(ipfw_obj_tlv);
+ da.rcount++;
+ mark_rule_objects(chain, rule, &da);
+ }
+ if (hdr->flags & IPFW_CFG_GET_COUNTERS) {
+ da.rsize += sizeof(struct ip_fw_bcounter) * da.rcount;
+ da.rcounters = 1;
+ }
+ sz += da.rsize + sizeof(ipfw_obj_ctlv);
+ }
+
+ if (da.tcount > 0)
+ sz += da.tcount * sizeof(ipfw_obj_ntlv_v0) +
+ sizeof(ipfw_obj_ctlv);
+
+ hdr->size = sz;
+ hdr->set_mask = ~V_set_disable;
+ hdr_flags = hdr->flags;
+ hdr = NULL;
+
+ if (sd->valsize < sz) {
+ error = ENOMEM;
+ goto cleanup;
+ }
+
+ /* STAGE 2: Store actual data */
+ if (da.tcount > 0) {
+ error = dump_named_objects_v0(chain, &da, sd);
+ if (error != 0)
+ goto cleanup;
+ }
+
+ if (hdr_flags & IPFW_CFG_GET_STATIC) {
+ error = dump_static_rules_v0(chain, &da, sd);
+ if (error != 0)
+ goto cleanup;
+ }
+
+ /*
+ * Dynamic states export in v0 layout is not implemented;
+ * userland will just see an empty state list.
+ */
+
+cleanup:
+ IPFW_UH_RUNLOCK(chain);
+
+ if (bmask != NULL)
+ free(bmask, M_TEMP);
+
+ return (error);
}
/*
@@ -545,25 +1017,122 @@
return (0);
}
+/*
+ * Clear rule accounting data matching specified parameters.
+ * Data layout (v0):
+ * Request: [ ip_fw3_opheader ipfw_range_tlv_v0 ]
+ * Reply: [ ip_fw3_opheader ipfw_range_tlv_v0 ] (new_set = num cleared)
+ */
static int
clear_rules_v0(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
- return (EOPNOTSUPP);
+ ipfw_range_tlv rv;
+ ipfw_range_header_v0 *rh;
+ int log_only, num;
+ char *msg;
+
+ if (sd->valsize != sizeof(*rh))
+ return (EINVAL);
+
+ rh = (ipfw_range_header_v0 *)ipfw_get_sopt_space(sd, sd->valsize);
+ if (check_range_tlv_v0(&rh->range, &rv) != 0)
+ return (EINVAL);
+
+ log_only = (op3->opcode == IP_FW_XRESETLOG);
+ num = clear_range(chain, &rv, log_only);
+
+ if (rv.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);
}
+/*
+ * Move rules matching specified parameters to a new set.
+ * Data layout (v0):
+ * Request: [ ip_fw3_opheader ipfw_range_tlv_v0 ]
+ */
static int
move_rules_v0(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
- return (EOPNOTSUPP);
+ ipfw_range_tlv rv;
+ ipfw_range_header_v0 *rh;
+
+ if (sd->valsize != sizeof(*rh))
+ return (EINVAL);
+
+ rh = (ipfw_range_header_v0 *)ipfw_get_sopt_space(sd, sd->valsize);
+ if (check_range_tlv_v0(&rh->range, &rv) != 0)
+ return (EINVAL);
+
+ return (move_range(chain, &rv));
}
+/*
+ * Swap/move/enable sets.
+ * Data layout (v0):
+ * Request: [ ip_fw3_opheader ipfw_range_tlv_v0 ]
+ *
+ * Note: for IP_FW_SET_ENABLE the .set / .new_set fields are bitmasks of sets
+ * rather than set indices, so the IPFW_MAX_SETS check in check_range_tlv_v0()
+ * is not applicable.
+ */
static int
manage_sets_v0(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
- return (EOPNOTSUPP);
+ ipfw_range_tlv rv;
+ ipfw_range_header_v0 *rh;
+ int ret;
+
+ if (sd->valsize != sizeof(*rh))
+ return (EINVAL);
+
+ rh = (ipfw_range_header_v0 *)ipfw_get_sopt_space(sd, sd->valsize);
+
+ if (rh->range.head.length != sizeof(rh->range))
+ return (EINVAL);
+ if (op3->opcode != IP_FW_SET_ENABLE &&
+ (rh->range.set >= IPFW_MAX_SETS ||
+ rh->range.new_set >= IPFW_MAX_SETS))
+ return (EINVAL);
+
+ memset(&rv, 0, sizeof(rv));
+ rv.head = rh->range.head;
+ rv.head.length = sizeof(rv);
+ rv.flags = rh->range.flags;
+ rv.start_rule = rh->range.start_rule;
+ rv.end_rule = rh->range.end_rule;
+ rv.set = rh->range.set;
+ rv.new_set = rh->range.new_set;
+
+ ret = 0;
+ IPFW_UH_WLOCK(chain);
+ switch (op3->opcode) {
+ case IP_FW_SET_SWAP:
+ case IP_FW_SET_MOVE:
+ ret = ipfw_swap_sets(chain, &rv,
+ op3->opcode == IP_FW_SET_MOVE);
+ break;
+ case IP_FW_SET_ENABLE:
+ ipfw_enable_sets(chain, &rv);
+ break;
+ }
+ IPFW_UH_WUNLOCK(chain);
+
+ return (ret);
}
static int
@@ -679,16 +1248,189 @@
return (SUCCESS);
}
+/*
+ * 14.x -> 15.x compatibility for IP_FW_TABLE_X{ADD,DEL,LIST,FIND}
+ * =================================================================
+ *
+ * Commit 4a77657cbc01 widened a number of `idx` fields from 16 to 32 bits
+ * and rearranged the surrounding spare bytes in the on-wire structures
+ * used by IP_FW3 socket options:
+ *
+ * ipfw_obj_header (16-byte tail after the ip_fw3_opheader):
+ * v0: spare(u32) idx(u16) objtype(u8) objsubtype(u8)
+ * v1: idx(u32) spare(u16) objtype(u8) objsubtype(u8)
+ *
+ * ipfw_obj_ntlv (16-byte tail after ipfw_obj_tlv head):
+ * v0: idx(u16) set(u8) type(u8) spare(u32) name[64]
+ * v1: idx(u32) set(u8) type(u8) spare(u16) name[64]
+ *
+ * ipfw_obj_tentry (the idx slot at offset 12):
+ * v0: idx(u16) spare1(u16)
+ * v1: idx(u32)
+ *
+ * The total wire size of every structure is preserved, and the table
+ * name (the only field most XINFO/XCREATE/XLIST handlers consult) lives
+ * at the same offset in both layouts. That is why the table operations
+ * registered under IP_FW3_OPVER_0 above keep working with the unmodified
+ * v1 handlers.
+ *
+ * However, XADD, XDEL, XFIND and XLIST(entries) reach into the idx/set
+ * fields. They locate the table by `tent->idx` (used as ti.uidx) and
+ * then look the corresponding ntlv up by that uidx. With a 14.x client
+ * the v1 reinterpretation produces e.g. ntlv.idx = 0x04000001 (table
+ * type byte spilling into the high bits of idx) and tent.idx = 1, so
+ * the search never matches and add_table_entry() returns ESRCH ("table
+ * not found").
+ *
+ * The 14.x ipfw(8) always sets oh->idx = 1 (a legacy index marker), so
+ * after v1 reinterpret the high half of that area - which v1 calls
+ * `oh->spare` - becomes 1. 15.x clients leave both fields zero. We use
+ * that as the heuristic to decide whether to rewrite the request to v1
+ * layout in place before invoking the underlying v1 handler.
+ */
+static bool
+ipfw_compat_obj_header_is_v0(const ipfw_obj_header *oh)
+{
+
+ return (oh->spare != 0);
+}
+
+static void
+ipfw_compat_obj_ntlv_v0_to_v1(ipfw_obj_ntlv *ntlv)
+{
+ uint8_t *raw = (uint8_t *)ntlv;
+ uint8_t set, type;
+
+ /* Pull set/type from their v0 byte offsets before overwriting. */
+ set = raw[10]; /* v0 ntlv.set */
+ type = raw[11]; /* v0 ntlv.type */
+
+ ntlv->idx = 0;
+ ntlv->set = set;
+ ntlv->type = type;
+ ntlv->spare = 0;
+}
+
+static void
+ipfw_compat_obj_header_v0_to_v1(ipfw_obj_header *oh)
+{
+ /*
+ * The objtype/objsubtype bytes already line up between v0 and v1
+ * (both are the trailing two bytes of the 8-byte tail after the
+ * opheader). All we have to fix is the idx/spare pair.
+ */
+ oh->idx = 0;
+ oh->spare = 0;
+ ipfw_compat_obj_ntlv_v0_to_v1(&oh->ntlv);
+}
+
+static void
+ipfw_compat_obj_tentry_v0_to_v1(ipfw_obj_tentry *tent)
+{
+ /*
+ * In v0 the slot at offset 12 was idx(u16) + spare1(u16); 14.x
+ * always writes idx == oh->idx == 1 (the legacy marker) and
+ * leaves spare1 == 0. Replace the resulting v1 idx (== 1) with
+ * 0 so the v1 handler falls back to looking the table up by
+ * name via oh->ntlv (which we have already rewritten).
+ *
+ * The ipfw_table_value union that follows differs in field
+ * layout between v0 and v1 but its wire size is unchanged. We
+ * deliberately do not translate value contents here: that only
+ * matters for callers that pass non-zero values (e.g.
+ * `ipfw table N add KEY VALUE`), which 14.x ipfw(8) was already
+ * unable to express portably.
+ */
+ tent->idx = 0;
+}
+
+static int
+manage_table_ent_v1_compat(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
+ struct sockopt_data *sd)
+{
+ ipfw_obj_header *oh;
+ ipfw_obj_ctlv *ctlv;
+ ipfw_obj_tentry *tent;
+ uint32_t i;
+
+ if (sd->valsize >= sizeof(*oh) + sizeof(*ctlv)) {
+ oh = (ipfw_obj_header *)sd->kbuf;
+ if (ipfw_compat_obj_header_is_v0(oh)) {
+ uint32_t cnt, max_cnt;
+
+ ipfw_compat_obj_header_v0_to_v1(oh);
+ ctlv = (ipfw_obj_ctlv *)(oh + 1);
+ tent = (ipfw_obj_tentry *)(ctlv + 1);
+ /*
+ * Defensively cap the count by the available buffer
+ * before the underlying v1 handler has had a chance
+ * to validate it.
+ */
+ max_cnt = (sd->valsize - sizeof(*oh) -
+ sizeof(*ctlv)) / sizeof(*tent);
+ cnt = ctlv->count;
+ if (cnt > max_cnt)
+ cnt = max_cnt;
+ for (i = 0; i < cnt; i++)
+ ipfw_compat_obj_tentry_v0_to_v1(&tent[i]);
+ }
+ }
+ return (manage_table_ent_v1(ch, op3, sd));
+}
+
+static int
+find_table_entry_compat(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
+ struct sockopt_data *sd)
+{
+ ipfw_obj_header *oh;
+ ipfw_obj_tentry *tent;
+
+ if (sd->valsize >= sizeof(*oh) + sizeof(*tent)) {
+ oh = (ipfw_obj_header *)sd->kbuf;
+ if (ipfw_compat_obj_header_is_v0(oh)) {
+ ipfw_compat_obj_header_v0_to_v1(oh);
+ tent = (ipfw_obj_tentry *)(oh + 1);
+ ipfw_compat_obj_tentry_v0_to_v1(tent);
+ }
+ }
+ return (find_table_entry(ch, op3, sd));
+}
+
+static int
+dump_table_v1_compat(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
+ struct sockopt_data *sd)
+{
+ ipfw_obj_header *oh;
+
+ if (sd->valsize >= sizeof(*oh)) {
+ oh = (ipfw_obj_header *)sd->kbuf;
+ if (ipfw_compat_obj_header_is_v0(oh))
+ ipfw_compat_obj_header_v0_to_v1(oh);
+ }
+ return (dump_table_v1(ch, op3, sd));
+}
+
static int
ipfw_compat_modevent(module_t mod, int type, void *unused)
{
switch (type) {
case MOD_LOAD:
IPFW_ADD_SOPT_HANDLER(1, scodes);
+ /*
+ * Replace the v1 table sockopt handlers with our wrappers
+ * so that 14.x clients (which set opheader.version = 1 for
+ * XADD/XDEL/XLIST entries and rely on legacy v0 layout for
+ * the body) get the request rewritten to v1 layout before
+ * the underlying handler runs.
+ */
+ IPFW_DEL_SOPT_HANDLER(1, v1_originals);
+ IPFW_ADD_SOPT_HANDLER(1, v1_overrides);
ipfw_register_compat(check_opcode_compat);
break;
case MOD_UNLOAD:
ipfw_unregister_compat();
+ IPFW_DEL_SOPT_HANDLER(1, v1_overrides);
+ IPFW_ADD_SOPT_HANDLER(1, v1_originals);
IPFW_DEL_SOPT_HANDLER(1, scodes);
break;
default:
diff --git a/sys/netpfil/ipfw/ip_fw_private.h b/sys/netpfil/ipfw/ip_fw_private.h
--- a/sys/netpfil/ipfw/ip_fw_private.h
+++ b/sys/netpfil/ipfw/ip_fw_private.h
@@ -660,6 +660,57 @@
int ipfw_commit_rules(struct ip_fw_chain *chain, struct rule_check_info *rci,
int count);
int delete_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int *ndel);
+int move_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt);
+int clear_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int log_only);
+int ipfw_swap_sets(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int mv);
+void ipfw_enable_sets(struct ip_fw_chain *chain, ipfw_range_tlv *rt);
+
+/*
+ * Arguments for dump_config()/dump_static_rules() and their compat
+ * variants. Consumed by ip_fw_sockopt.c and ip_fw_compat.c. The
+ * "rule_" prefix avoids clashing with an unrelated, private
+ * struct dump_args in ip_fw_table.c.
+ */
+struct rule_dump_args {
+ uint32_t b; /* start rule */
+ uint32_t e; /* end rule */
+ uint32_t rcount; /* number of rules */
+ uint32_t rsize; /* rules size */
+ uint32_t tcount; /* number of tables */
+ int rcounters; /* counters */
+ uint32_t *bmask; /* index bitmask of used named objects */
+};
+
+void export_cntr1_base(struct ip_fw *krule, struct ip_fw_bcounter *cntr);
+void export_rule1(struct ip_fw *krule, caddr_t data, int len, int rcntrs);
+void mark_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule,
+ struct rule_dump_args *da);
+
+/*
+ * IP_FW_TABLE_* sockopt handlers (defined in ip_fw_table.c). Exposed so
+ * that ip_fw_compat.c can re-register them under IP_FW3_OPVER_0; the
+ * on-wire layouts of ipfw_obj_header / ipfw_obj_ntlv / ipfw_obj_tentry
+ * preserve their size and field offsets used by typical 14.x callers
+ * (idx/set are typically 0; the table name lives at a fixed offset).
+ */
+int create_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
+ struct sockopt_data *sd);
+int flush_table_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
+ struct sockopt_data *sd);
+int modify_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
+ struct sockopt_data *sd);
+int describe_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
+ struct sockopt_data *sd);
+int list_tables(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
+ struct sockopt_data *sd);
+int dump_table_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
+ struct sockopt_data *sd);
+int manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
+ struct sockopt_data *sd);
+int find_table_entry(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
+ struct sockopt_data *sd);
+int swap_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
+ struct sockopt_data *sd);
struct ip_fw *ipfw_alloc_rule(struct ip_fw_chain *chain, size_t rulesize);
void ipfw_free_rule(struct ip_fw *rule);
int ipfw_match_range(struct ip_fw *rule, ipfw_range_tlv *rt);
diff --git a/sys/netpfil/ipfw/ip_fw_sockopt.c b/sys/netpfil/ipfw/ip_fw_sockopt.c
--- a/sys/netpfil/ipfw/ip_fw_sockopt.c
+++ b/sys/netpfil/ipfw/ip_fw_sockopt.c
@@ -354,7 +354,7 @@
return old_map;
}
-static void
+void
export_cntr1_base(struct ip_fw *krule, struct ip_fw_bcounter *cntr)
{
struct timeval boottime;
@@ -381,7 +381,7 @@
* ]
* Assume @data is zeroed.
*/
-static void
+void
export_rule1(struct ip_fw *krule, caddr_t data, int len, int rcntrs)
{
struct ip_fw_bcounter *cntr;
@@ -901,7 +901,7 @@
*
* Returns 0 on success.
*/
-static int
+int
move_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt)
{
struct ip_fw *rule;
@@ -984,7 +984,7 @@
*
* Returns number of items cleared.
*/
-static int
+int
clear_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int log_only)
{
struct ip_fw *rule;
@@ -1128,8 +1128,8 @@
return (0);
}
-static void
-enable_sets(struct ip_fw_chain *chain, ipfw_range_tlv *rt)
+void
+ipfw_enable_sets(struct ip_fw_chain *chain, ipfw_range_tlv *rt)
{
uint32_t v_set;
@@ -1143,8 +1143,8 @@
IPFW_WUNLOCK(chain);
}
-static int
-swap_sets(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int mv)
+int
+ipfw_swap_sets(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int mv)
{
struct opcode_obj_rewrite *rw;
struct ip_fw *rule;
@@ -1219,11 +1219,11 @@
switch (op3->opcode) {
case IP_FW_SET_SWAP:
case IP_FW_SET_MOVE:
- ret = swap_sets(chain, &rh->range,
+ ret = ipfw_swap_sets(chain, &rh->range,
op3->opcode == IP_FW_SET_MOVE);
break;
case IP_FW_SET_ENABLE:
- enable_sets(chain, &rh->range);
+ ipfw_enable_sets(chain, &rh->range);
break;
}
IPFW_UH_WUNLOCK(chain);
@@ -1697,16 +1697,6 @@
return (0);
}
-struct dump_args {
- uint32_t b; /* start rule */
- uint32_t e; /* end rule */
- uint32_t rcount; /* number of rules */
- uint32_t rsize; /* rules size */
- uint32_t tcount; /* number of tables */
- int rcounters; /* counters */
- uint32_t *bmask; /* index bitmask of used named objects */
-};
-
void
ipfw_export_obj_ntlv(struct named_object *no, ipfw_obj_ntlv *ntlv)
{
@@ -1742,7 +1732,7 @@
}
static int
-export_named_objects(struct namedobj_instance *ni, struct dump_args *da,
+export_named_objects(struct namedobj_instance *ni, struct rule_dump_args *da,
struct sockopt_data *sd)
{
uint32_t i;
@@ -1759,7 +1749,7 @@
}
static int
-dump_named_objects(struct ip_fw_chain *ch, struct dump_args *da,
+dump_named_objects(struct ip_fw_chain *ch, struct rule_dump_args *da,
struct sockopt_data *sd)
{
ipfw_obj_ctlv *ctlv;
@@ -1791,7 +1781,7 @@
* Returns 0 on success.
*/
static int
-dump_static_rules(struct ip_fw_chain *chain, struct dump_args *da,
+dump_static_rules(struct ip_fw_chain *chain, struct rule_dump_args *da,
struct sockopt_data *sd)
{
ipfw_obj_ctlv *ctlv;
@@ -1845,9 +1835,9 @@
* Used to generate bitmask of referenced tables/objects for given ruleset
* or its part.
*/
-static void
+void
mark_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule,
- struct dump_args *da)
+ struct rule_dump_args *da)
{
struct opcode_obj_rewrite *rw;
ipfw_insn *cmd;
@@ -1891,7 +1881,7 @@
dump_config(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
- struct dump_args da;
+ struct rule_dump_args da;
ipfw_cfg_lheader *hdr;
struct ip_fw *rule;
size_t sz, rnum;
diff --git a/sys/netpfil/ipfw/ip_fw_table.c b/sys/netpfil/ipfw/ip_fw_table.c
--- a/sys/netpfil/ipfw/ip_fw_table.c
+++ b/sys/netpfil/ipfw/ip_fw_table.c
@@ -709,7 +709,7 @@
*
* Returns 0 on success
*/
-static int
+int
manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
@@ -831,7 +831,7 @@
*
* Returns 0 on success
*/
-static int
+int
find_table_entry(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
@@ -897,7 +897,7 @@
*
* Returns 0 on success
*/
-static int
+int
flush_table_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
@@ -1024,7 +1024,7 @@
*
* Returns 0 on success
*/
-static int
+int
swap_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
@@ -1368,7 +1368,7 @@
*
* Returns 0 on success
*/
-static int
+int
list_tables(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
@@ -1396,7 +1396,7 @@
*
* Returns 0 on success.
*/
-static int
+int
describe_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
@@ -1431,7 +1431,7 @@
*
* Returns 0 on success
*/
-static int
+int
modify_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
@@ -1488,7 +1488,7 @@
*
* Returns 0 on success
*/
-static int
+int
create_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
@@ -1833,7 +1833,7 @@
*
* Returns 0 on success
*/
-static int
+int
dump_table_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{

File Metadata

Mime Type
text/plain
Expires
Fri, Jul 24, 6:24 AM (12 h, 54 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
35433700
Default Alt Text
D56660.id176671.diff (31 KB)

Event Timeline