Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F163443735
D56660.id176671.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
31 KB
Referenced Files
None
Subscribers
None
D56660.id176671.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D56660: ipfw: flesh out IP_FW3 OPVER_0 compat layer as ipfw_compat
Attached
Detach File
Event Timeline
Log In to Comment