--- /dev/null
+/* IPFW2 Backward Compatibility */
+
+/* Convert to and from IPFW2 structures. */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+
+#include <sys/types.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/ip_fw.h>
+#include <netinet/tcp.h>
+
+#include "ip_fw2_compat.h"
+
+#define FW2_DEBUG_VERBOSE 0
+
+/*
+ * _s_x is a structure that stores a string <-> token pairs, used in
+ * various places in the parser. Entries are stored in arrays,
+ * with an entry with s=NULL as terminator.
+ * The search routines are match_token() and match_value().
+ * Often, an element with x=0 contains an error string.
+ *
+ */
+struct _s_x {
+ char const *s;
+ int x;
+};
+
+#define NO_VERSION_STR "IP_FW_VERSION_NONE"
+#define VERSION_ZERO_STR "IP_FW_VERSION_0"
+#define VERSION_ONE_STR "IP_FW_VERSION_1"
+#define CURRENT_API_VERSION_STR "IP_FW_CURRENT_API_VERSION"
+
+static struct _s_x f_tcpflags[] = {
+ { "syn", TH_SYN },
+ { "fin", TH_FIN },
+ { "ack", TH_ACK },
+ { "psh", TH_PUSH },
+ { "rst", TH_RST },
+ { "urg", TH_URG },
+ { "tcp flag", 0 },
+ { NULL, 0 }
+};
+
+static struct _s_x f_tcpopts[] = {
+ { "mss", IP_FW_TCPOPT_MSS },
+ { "maxseg", IP_FW_TCPOPT_MSS },
+ { "window", IP_FW_TCPOPT_WINDOW },
+ { "sack", IP_FW_TCPOPT_SACK },
+ { "ts", IP_FW_TCPOPT_TS },
+ { "timestamp", IP_FW_TCPOPT_TS },
+ { "cc", IP_FW_TCPOPT_CC },
+ { "tcp option", 0 },
+ { NULL, 0 }
+};
+
+/*
+ * IP options span the range 0 to 255 so we need to remap them
+ * (though in fact only the low 5 bits are significant).
+ */
+static struct _s_x f_ipopts[] = {
+ { "ssrr", IP_FW_IPOPT_SSRR},
+ { "lsrr", IP_FW_IPOPT_LSRR},
+ { "rr", IP_FW_IPOPT_RR},
+ { "ts", IP_FW_IPOPT_TS},
+ { "ip option", 0 },
+ { NULL, 0 }
+};
+
+static struct _s_x f_iptos[] = {
+ { "lowdelay", IPTOS_LOWDELAY},
+ { "throughput", IPTOS_THROUGHPUT},
+ { "reliability", IPTOS_RELIABILITY},
+ { "mincost", IPTOS_MINCOST},
+ { "congestion", IPTOS_CE},
+ { "ecntransport", IPTOS_ECT},
+ { "ip tos option", 0},
+ { NULL, 0 }
+};
+
+static struct _s_x limit_masks[] = {
+ {"all", DYN_SRC_ADDR|DYN_SRC_PORT|DYN_DST_ADDR|DYN_DST_PORT},
+ {"src-addr", DYN_SRC_ADDR},
+ {"src-port", DYN_SRC_PORT},
+ {"dst-addr", DYN_DST_ADDR},
+ {"dst-port", DYN_DST_PORT},
+ {NULL, 0}
+};
+
+static void
+ipfw_print_fw_flags(u_int flags)
+{
+ /* print action */
+ switch (flags & IP_FW_F_COMMAND_COMPAT) {
+ case IP_FW_F_ACCEPT_COMPAT:
+ printf("IP_FW_F_ACCEPT_COMPAT\n");
+ break;
+ case IP_FW_F_COUNT_COMPAT:
+ printf("IP_FW_F_COUNT_COMPAT\n");
+ break;
+ case IP_FW_F_PIPE_COMPAT:
+ printf("IP_FW_F_PIPE_COMPAT\n");
+ break;
+ case IP_FW_F_QUEUE_COMPAT:
+ printf("IP_FW_F_QUEUE_COMPAT\n");
+ break;
+ case IP_FW_F_SKIPTO_COMPAT:
+ printf("IP_FW_F_SKIPTO_COMPAT\n");
+ break;
+ case IP_FW_F_DIVERT_COMPAT:
+ printf("IP_FW_F_DIVERT_COMPAT\n");
+ break;
+ case IP_FW_F_TEE_COMPAT:
+ printf("IP_FW_F_TEE_COMPAT\n");
+ break;
+ case IP_FW_F_FWD_COMPAT:
+ printf("IP_FW_F_FWD_COMPAT\n");
+ break;
+ case IP_FW_F_DENY_COMPAT:
+ printf("IP_FW_F_DENY_COMPAT\n");
+ break;
+ case IP_FW_F_REJECT_COMPAT:
+ printf("IP_FW_F_REJECT_COMPAT\n");
+ break;
+ case IP_FW_F_CHECK_S_COMPAT:
+ printf("IP_FW_F_CHECK_S_COMPAT\n");
+ break;
+ default:
+ printf("No action given\n");
+ break;
+ }
+
+ /* print commands */
+ if (flags & IP_FW_F_IN_COMPAT) {
+ printf("IP_FW_F_IN_COMPAT\n");
+ }
+ if (flags & IP_FW_F_OUT_COMPAT) {
+ printf("IP_FW_F_OUT_COMPAT\n");
+ }
+ if (flags & IP_FW_F_IIFACE_COMPAT) {
+ printf("IP_FW_F_IIFACE_COMPAT\n");
+ }
+ if (flags & IP_FW_F_OIFACE_COMPAT) {
+ printf("IP_FW_F_OIFACE_COMPAT\n");
+ }
+ if (flags & IP_FW_F_PRN_COMPAT) {
+ printf("IP_FW_F_PRN_COMPAT\n");
+ }
+ if (flags & IP_FW_F_SRNG_COMPAT) {
+ printf("IP_FW_F_SRNG_COMPAT\n");
+ }
+ if (flags & IP_FW_F_DRNG_COMPAT) {
+ printf("IP_FW_F_DRNG_COMPAT\n");
+ }
+ if (flags & IP_FW_F_FRAG_COMPAT) {
+ printf("IP_FW_F_FRAG_COMPAT\n");
+ }
+ if (flags & IP_FW_F_IIFNAME_COMPAT) {
+ printf("IP_FW_F_IIFNAME_COMPAT\n");
+ }
+ if (flags & IP_FW_F_OIFNAME_COMPAT) {
+ printf("IP_FW_F_OIFNAME_COMPAT\n");
+ }
+ if (flags & IP_FW_F_INVSRC_COMPAT) {
+ printf("IP_FW_F_INVSRC_COMPAT\n");
+ }
+ if (flags & IP_FW_F_INVDST_COMPAT) {
+ printf("IP_FW_F_INVDST_COMPAT\n");
+ }
+ if (flags & IP_FW_F_ICMPBIT_COMPAT) {
+ printf("IP_FW_F_ICMPBIT_COMPAT\n");
+ }
+ if (flags & IP_FW_F_UID_COMPAT) {
+ printf("IP_FW_F_UID_COMPAT\n");
+ }
+ if (flags & IP_FW_F_RND_MATCH_COMPAT) {
+ printf("IP_FW_F_RND_MATCH_COMPAT\n");
+ }
+ if (flags & IP_FW_F_SMSK_COMPAT) {
+ printf("IP_FW_F_SMSK_COMPAT\n");
+ }
+ if (flags & IP_FW_F_DMSK_COMPAT) {
+ printf("IP_FW_F_DMSK_COMPAT\n");
+ }
+ if (flags & IP_FW_BRIDGED_COMPAT) {
+ printf("IP_FW_BRIDGED_COMPAT\n");
+ }
+ if (flags & IP_FW_F_KEEP_S_COMPAT) {
+ printf("IP_FW_F_KEEP_S_COMPAT\n");
+ }
+ if (flags & IP_FW_F_CHECK_S_COMPAT) {
+ printf("IP_FW_F_CHECK_S_COMPAT\n");
+ }
+ if (flags & IP_FW_F_SME_COMPAT) {
+ printf("IP_FW_F_SME_COMPAT\n");
+ }
+ if (flags & IP_FW_F_DME_COMPAT) {
+ printf("IP_FW_F_DME_COMPAT\n");
+ }
+}
+
+static void
+print_fw_version(u_int32_t api_version)
+{
+ switch (api_version) {
+ case IP_FW_VERSION_0:
+ printf("Version: %s\n", VERSION_ZERO_STR);
+ break;
+ case IP_FW_VERSION_1:
+ printf("Version: %s\n", VERSION_ONE_STR);
+ break;
+ case IP_FW_CURRENT_API_VERSION:
+ printf("Version: %s\n", CURRENT_API_VERSION_STR);
+ break;
+ case IP_FW_VERSION_NONE:
+ printf("Version: %s\n", NO_VERSION_STR);
+ break;
+ default:
+ printf("Unrecognized version\n");
+ break;
+ }
+}
+
+static void
+ipfw_print_vers1_struct(struct ip_fw_compat *vers1_rule)
+{
+ char ipv4str[MAX_IPv4_STR_LEN];
+ print_fw_version(vers1_rule->version);
+ printf("Rule #%d\n", vers1_rule->fw_number);
+
+ ipfw_print_fw_flags(vers1_rule->fw_flg);
+
+ printf("fw_pcnt: %d\n", vers1_rule->fw_pcnt);
+ printf("fw_bcnt: %d\n", vers1_rule->fw_bcnt);
+ printf("fw_src: %s\n",
+ inet_ntop(AF_INET, &vers1_rule->fw_src, ipv4str, sizeof(ipv4str)));
+ printf("fw_dst: %s\n",
+ inet_ntop(AF_INET, &vers1_rule->fw_dst, ipv4str, sizeof(ipv4str)));
+ printf("fw_smsk: %s\n",
+ inet_ntop(AF_INET, &vers1_rule->fw_smsk, ipv4str, sizeof(ipv4str)));
+ printf("fw_dmsk: %s\n",
+ inet_ntop(AF_INET, &vers1_rule->fw_dmsk, ipv4str, sizeof(ipv4str)));
+
+ if (vers1_rule->fw_flg & IP_FW_F_ICMPBIT_COMPAT) {
+ int type_index;
+ int first = 1;
+
+ printf(" icmptype");
+
+ for (type_index = 0;
+ type_index < (IP_FW_ICMPTYPES_DIM_COMPAT * sizeof(unsigned) * 8);
+ ++type_index) {
+ if (vers1_rule->fw_uar_compat.fw_icmptypes[type_index / (sizeof(unsigned) * 8)] &
+ (1U << (type_index % (sizeof(unsigned) * 8)))) {
+ printf("%c%d", first == 1 ? ' ' : ',', type_index);
+ first = 0;
+ }
+ }
+ } else {
+ int i, nsp, ndp;
+
+ nsp = IP_FW_GETNSRCP_COMPAT(vers1_rule);
+ for (i = 0; i < nsp; i++) {
+ printf("source ports: fw_uar_compat.fw_pts: %04x", vers1_rule->fw_uar_compat.fw_pts[i]);
+ if (i == 0 && (vers1_rule->fw_flg & IP_FW_F_SRNG_COMPAT))
+ printf("-");
+ else if (i == 0 && (vers1_rule->fw_flg & IP_FW_F_SMSK_COMPAT))
+ printf(":");
+ else
+ printf(",");
+ }
+
+ printf("\n");
+
+ ndp = IP_FW_GETNDSTP_COMPAT(vers1_rule);
+ for (i = 0; i < ndp; i++) {
+ printf("source ports: fw_uar_compat.fw_pts: %04x", vers1_rule->fw_uar_compat.fw_pts[nsp+i]);
+ if (i == 0 && (vers1_rule->fw_flg & IP_FW_F_DRNG_COMPAT))
+ printf("-");
+ else if (i == 0 && (vers1_rule->fw_flg & IP_FW_F_DMSK_COMPAT))
+ printf(":");
+ else
+ printf(",");
+ }
+
+ printf("\n");
+ }
+
+ printf("fw_ipflg: %d\n", vers1_rule->fw_ipflg);
+ printf("fw_ipopt: %d\n", vers1_rule->fw_ipopt);
+ printf("fw_ipnopt: %d\n", vers1_rule->fw_ipnopt);
+ printf("fw_tcpopt: %d\n", vers1_rule->fw_tcpopt);
+ printf("fw_tcpnopt: %d\n", vers1_rule->fw_tcpnopt);
+ printf("fw_tcpf: %d\n", vers1_rule->fw_tcpf);
+ printf("fw_tcpnf: %d\n", vers1_rule->fw_tcpnf);
+ printf("timestamp: %d\n", vers1_rule->timestamp);
+
+ if ((vers1_rule->fw_flg & IF_FW_F_VIAHACK_COMPAT) == IF_FW_F_VIAHACK_COMPAT) {
+ printf("fw_in_if: ");
+ inet_ntop(AF_INET, &vers1_rule->fw_in_if.fu_via_ip, ipv4str,
+ sizeof(ipv4str));
+ printf("fu_via_ip: %s\n", ipv4str);
+ printf("fu_via_if_compat.name: %s\n", vers1_rule->fw_in_if.fu_via_if_compat.name);
+ printf("fu_via_if_compat.unit: %d\n", vers1_rule->fw_in_if.fu_via_if_compat.unit);
+ } else {
+ if (vers1_rule->fw_flg & IP_FW_F_IIFACE_COMPAT) {
+ printf("fw_in_if: ");
+ printf("fu_via_ip: %s\n",
+ inet_ntop(AF_INET, &vers1_rule->fw_in_if.fu_via_ip, ipv4str,
+ sizeof(ipv4str)));
+ printf("fu_via_if_compat.name: %s\n", vers1_rule->fw_in_if.fu_via_if_compat.name);
+ printf("fu_via_if_compat.unit: %d\n", vers1_rule->fw_in_if.fu_via_if_compat.unit);
+ }
+ if (vers1_rule->fw_flg & IP_FW_F_OIFACE_COMPAT) {
+ printf("fw_out_if: ");
+ printf("fu_via_ip: %s\n",
+ inet_ntop(AF_INET, &vers1_rule->fw_out_if.fu_via_ip,
+ ipv4str, sizeof(ipv4str)));
+ printf("fu_via_if_compat.name: %s\n", vers1_rule->fw_out_if.fu_via_if_compat.name);
+ printf("fu_via_if_compat.unit: %d\n", vers1_rule->fw_out_if.fu_via_if_compat.unit);
+ }
+ }
+
+ printf("fw_prot: %d\n", vers1_rule->fw_prot);
+ printf("fw_nports: %d\n", vers1_rule->fw_nports);
+ printf("pipe_ptr: %x\n", vers1_rule->pipe_ptr);
+ printf("next_rule_ptr: %x\n", vers1_rule->next_rule_ptr);
+ printf("fw_uid: %d\n", vers1_rule->fw_uid);
+ printf("fw_logamount: %d\n", vers1_rule->fw_logamount);
+ printf("fw_loghighest: %d\n", vers1_rule->fw_loghighest);
+}
+
+static void
+print_icmptypes(ipfw_insn_u32 *cmd)
+{
+ int i;
+ char sep= ' ';
+
+ printf(" icmptypes");
+ for (i = 0; i < 32; i++) {
+ if ( (cmd->d[0] & (1 << (i))) == 0)
+ continue;
+ printf("%c%d", sep, i);
+ sep = ',';
+ }
+}
+
+/*
+ * print flags set/clear in the two bitmasks passed as parameters.
+ * There is a specialized check for f_tcpflags.
+ */
+static void
+print_flags(char const *name, ipfw_insn *cmd, struct _s_x *list)
+{
+ char const *comma = "";
+ int i;
+ uint8_t set = cmd->arg1 & 0xff;
+ uint8_t clear = (cmd->arg1 >> 8) & 0xff;
+
+ if (list == f_tcpflags && set == TH_SYN && clear == TH_ACK) {
+ printf(" setup");
+ return;
+ }
+
+ printf(" %s ", name);
+ for (i=0; list[i].x != 0; i++) {
+ if (set & list[i].x) {
+ set &= ~list[i].x;
+ printf("%s%s", comma, list[i].s);
+ comma = ",";
+ }
+ if (clear & list[i].x) {
+ clear &= ~list[i].x;
+ printf("%s!%s", comma, list[i].s);
+ comma = ",";
+ }
+ }
+}
+
+static int
+contigmask(uint8_t *p, int len)
+{
+ int i, n;
+
+ for (i=0; i<len ; i++)
+ if ( (p[i/8] & (1 << (7 - (i%8)))) == 0) /* first bit unset */
+ break;
+ for (n=i+1; n < len; n++)
+ if ( (p[n/8] & (1 << (7 - (n%8)))) != 0)
+ return -1; /* mask not contiguous */
+ return i;
+}
+
+/*
+ * Print the ip address contained in a command.
+ */
+static void
+print_ip(ipfw_insn_ip *cmd)
+{
+ int len = F_LEN((ipfw_insn *)cmd);
+ uint32_t *a = ((ipfw_insn_u32 *)cmd)->d;
+ char ipv4str[MAX_IPv4_STR_LEN];
+
+ printf("%s ", cmd->o.len & F_NOT ? " not": "");
+
+ if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) {
+ printf("me");
+ return;
+ }
+
+ /*
+ * len == 2 indicates a single IP, whereas lists of 1 or more
+ * addr/mask pairs have len = (2n+1). We convert len to n so we
+ * use that to count the number of entries.
+ */
+ for (len = len / 2; len > 0; len--, a += 2) {
+ int mb = /* mask length */
+ (cmd->o.opcode == O_IP_SRC || cmd->o.opcode == O_IP_DST) ?
+ 32 : contigmask((uint8_t *)&(a[1]), 32);
+ if (mb == 0) { /* any */
+ printf("any");
+ } else { /* numeric IP followed by some kind of mask */
+ printf("%s", inet_ntop(AF_INET, &a[0], ipv4str, sizeof(ipv4str)));
+ if (mb < 0)
+ printf(":%s", inet_ntop(AF_INET, &a[1], ipv4str, sizeof(ipv4str)));
+ else if (mb < 32)
+ printf("/%d", mb);
+ }
+ if (len > 1)
+ printf(",");
+ }
+}
+
+/*
+ * prints a MAC address/mask pair
+ */
+static void
+print_mac(uint8_t *addr, uint8_t *mask)
+{
+ int l = contigmask(mask, 48);
+
+ if (l == 0)
+ printf(" any");
+ else {
+ printf(" %02x:%02x:%02x:%02x:%02x:%02x",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+ if (l == -1)
+ printf("&%02x:%02x:%02x:%02x:%02x:%02x",
+ mask[0], mask[1], mask[2],
+ mask[3], mask[4], mask[5]);
+ else if (l < 48)
+ printf("/%d", l);
+ }
+}
+
+static void
+ipfw_print_vers2_struct(struct ip_fw *vers2_rule)
+{
+ int l;
+ ipfw_insn *cmd;
+ ipfw_insn_log *logptr = NULL;
+ char ipv4str[MAX_IPv4_STR_LEN];
+
+ print_fw_version(vers2_rule->version);
+
+ printf("act_ofs: %d\n", vers2_rule->act_ofs);
+ printf("cmd_len: %d\n", vers2_rule->cmd_len);
+ printf("rulenum: %d\n", vers2_rule->rulenum);
+ printf("set: %d\n", vers2_rule->set);
+ printf("pcnt: %d\n", vers2_rule->pcnt);
+ printf("bcnt: %d\n", vers2_rule->bcnt);
+ printf("timestamp: %d\n", vers2_rule->timestamp);
+
+ /*
+ * first print actions
+ */
+ for (l = vers2_rule->cmd_len - vers2_rule->act_ofs, cmd = ACTION_PTR(vers2_rule);
+ l > 0 ; l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
+ switch(cmd->opcode) {
+ case O_CHECK_STATE:
+ printf("check-state");
+ break;
+
+ case O_ACCEPT:
+ printf("allow");
+ break;
+
+ case O_COUNT:
+ printf("count");
+ break;
+
+ case O_DENY:
+ printf("deny");
+ break;
+
+ case O_REJECT:
+ if (cmd->arg1 == ICMP_REJECT_RST)
+ printf("reset");
+ else if (cmd->arg1 == ICMP_UNREACH_HOST)
+ printf("reject");
+ else
+ printf("unreach %u", cmd->arg1);
+ break;
+
+ case O_SKIPTO:
+ printf("skipto %u", cmd->arg1);
+ break;
+
+ case O_PIPE:
+ printf("pipe %u", cmd->arg1);
+ break;
+
+ case O_QUEUE:
+ printf("queue %u", cmd->arg1);
+ break;
+
+ case O_DIVERT:
+ printf("divert %u", cmd->arg1);
+ break;
+
+ case O_TEE:
+ printf("tee %u", cmd->arg1);
+ break;
+
+ case O_FORWARD_IP:
+ {
+ ipfw_insn_sa *s = (ipfw_insn_sa *)cmd;
+
+ printf("fwd %s",
+ inet_ntop(AF_INET, &s->sa.sin_addr, ipv4str,
+ sizeof(ipv4str)));
+ if (s->sa.sin_port)
+ printf(",%d", s->sa.sin_port);
+ break;
+ }
+
+ case O_LOG: /* O_LOG is printed last */
+ logptr = (ipfw_insn_log *)cmd;
+ break;
+
+ default:
+ printf("** unrecognized action %d len %d",
+ cmd->opcode, cmd->len);
+ }
+ }
+ if (logptr) {
+ if (logptr->max_log > 0)
+ printf(" log logamount %d", logptr->max_log);
+ else
+ printf(" log");
+ }
+
+ /*
+ * then print the body.
+ */
+ for (l = vers2_rule->act_ofs, cmd = vers2_rule->cmd ;
+ l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
+ /* useful alias */
+ ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd;
+
+ switch(cmd->opcode) {
+ case O_PROB:
+ break; /* done already */
+
+ case O_PROBE_STATE:
+ break; /* no need to print anything here */
+
+ case O_MACADDR2:
+ {
+ ipfw_insn_mac *m = (ipfw_insn_mac *)cmd;
+
+ if (cmd->len & F_NOT)
+ printf(" not");
+ printf(" MAC");
+ print_mac(m->addr, m->mask);
+ print_mac(m->addr + 6, m->mask + 6);
+ printf("\n");
+ break;
+ }
+ case O_MAC_TYPE:
+ {
+ uint16_t *p = ((ipfw_insn_u16 *)cmd)->ports;
+ int i;
+
+ for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) {
+ printf("0x%04x", p[0]);
+ if (p[0] != p[1]) {
+ printf("-");
+ printf("0x%04x", p[1]);
+ }
+ printf(",");
+ }
+ break;
+ }
+ case O_IP_SRC:
+ case O_IP_SRC_MASK:
+ case O_IP_SRC_ME:
+ print_ip((ipfw_insn_ip *)cmd);
+ break;
+
+ case O_IP_DST:
+ case O_IP_DST_MASK:
+ case O_IP_DST_ME:
+ print_ip((ipfw_insn_ip *)cmd);
+ break;
+
+ case O_IP_DSTPORT:
+ case O_IP_SRCPORT:
+ {
+ uint16_t *p = ((ipfw_insn_u16 *)cmd)->ports;
+ int i;
+
+ for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) {
+ printf("0x%04x", p[0]);
+ if (p[0] != p[1]) {
+ printf("-");
+ printf("0x%04x", p[1]);
+ }
+ printf(",");
+ }
+ break;
+ }
+ case O_PROTO:
+ {
+ printf("O_PROTO");
+
+ if (cmd->len & F_NOT)
+ printf(" not");
+
+ printf(" %u", cmd->arg1);
+
+ break;
+ }
+
+ default: /*options ... */
+ {
+ if (cmd->len & F_NOT && cmd->opcode != O_IN)
+ printf(" not");
+ switch(cmd->opcode) {
+ case O_FRAG:
+ printf("O_FRAG");
+ break;
+
+ case O_IN:
+ printf(cmd->len & F_NOT ? " out" : " O_IN");
+ break;
+
+ case O_LAYER2:
+ printf(" O_LAYER2");
+ break;
+ case O_XMIT:
+ case O_RECV:
+ case O_VIA:
+ {
+ char const *s;
+ ipfw_insn_if *cmdif = (ipfw_insn_if *)cmd;
+
+ if (cmd->opcode == O_XMIT)
+ s = "O_XMIT";
+ else if (cmd->opcode == O_RECV)
+ s = "O_RECV";
+ else /* if (cmd->opcode == O_VIA) */
+ s = "O_VIA";
+ if (cmdif->name[0] == '\0') {
+ printf(" %s %s", s,
+ inet_ntop(AF_INET, &cmdif->p.ip, ipv4str,
+ sizeof(ipv4str)));
+ }
+ else if (cmdif->p.unit == -1)
+ printf(" %s %s*", s, cmdif->name);
+ else
+ printf(" %s %s%d", s, cmdif->name,
+ cmdif->p.unit);
+ }
+ break;
+
+ case O_IPID:
+ if (F_LEN(cmd) == 1)
+ printf(" ipid %u", cmd->arg1 );
+ else {
+ uint16_t *p = ((ipfw_insn_u16 *)cmd)->ports;
+ int i;
+
+ for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) {
+ printf("0x%04x", p[0]);
+ if (p[0] != p[1]) {
+ printf("-");
+ printf("0x%04x", p[1]);
+ }
+ printf(",");
+ }
+ }
+
+ break;
+
+ case O_IPTTL:
+ if (F_LEN(cmd) == 1)
+ printf(" ipttl %u", cmd->arg1 );
+ else {
+ uint16_t *p = ((ipfw_insn_u16 *)cmd)->ports;
+ int i;
+
+ for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) {
+ printf("0x%04x", p[0]);
+ if (p[0] != p[1]) {
+ printf("-");
+ printf("0x%04x", p[1]);
+ }
+ printf(",");
+ }
+ }
+
+ break;
+
+ case O_IPVER:
+ printf(" ipver %u", cmd->arg1 );
+ break;
+
+ case O_IPPRECEDENCE:
+ printf(" ipprecedence %u", (cmd->arg1) >> 5 );
+ break;
+
+ case O_IPLEN:
+ if (F_LEN(cmd) == 1)
+ printf(" iplen %u", cmd->arg1 );
+ else {
+ uint16_t *p = ((ipfw_insn_u16 *)cmd)->ports;
+ int i;
+
+ for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) {
+ printf("0x%04x", p[0]);
+ if (p[0] != p[1]) {
+ printf("-");
+ printf("0x%04x", p[1]);
+ }
+ printf(",");
+ }
+ }
+
+ break;
+
+ case O_IPOPT:
+ print_flags("ipoptions", cmd, f_ipopts);
+ break;
+
+ case O_IPTOS:
+ print_flags("iptos", cmd, f_iptos);
+ break;
+
+ case O_ICMPTYPE:
+ print_icmptypes((ipfw_insn_u32 *)cmd);
+ break;
+
+ case O_ESTAB:
+ printf(" established");
+ break;
+
+ case O_TCPFLAGS:
+ print_flags("tcpflags", cmd, f_tcpflags);
+ break;
+
+ case O_TCPOPTS:
+ print_flags("tcpoptions", cmd, f_tcpopts);
+ break;
+
+ case O_TCPWIN:
+ printf(" tcpwin %d", ntohs(cmd->arg1));
+ break;
+
+ case O_TCPACK:
+ printf(" tcpack %ld", ntohl(cmd32->d[0]));
+ break;
+
+ case O_TCPSEQ:
+ printf(" tcpseq %ld", ntohl(cmd32->d[0]));
+ break;
+
+ case O_UID:
+ printf(" uid %u", cmd32->d[0]);
+ break;
+
+ case O_GID:
+ printf(" gid %u", cmd32->d[0]);
+ break;
+
+ case O_VERREVPATH:
+ printf(" verrevpath");
+ break;
+
+ case O_IPSEC:
+ printf(" ipsec");
+ break;
+
+ case O_NOP:
+ break;
+
+ case O_KEEP_STATE:
+ printf(" keep-state");
+ break;
+
+ case O_LIMIT:
+ {
+ struct _s_x *p = limit_masks;
+ ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
+ uint8_t x = c->limit_mask;
+ char const *comma = " ";
+
+ printf(" limit");
+ for (; p->x != 0 ; p++)
+ if ((x & p->x) == p->x) {
+ x &= ~p->x;
+ printf("%s%s", comma, p->s);
+ comma = ",";
+ }
+ printf(" %d", c->conn_limit);
+
+ break;
+ }
+
+ default:
+ printf(" [opcode %d len %d]",
+ cmd->opcode, cmd->len);
+ } /* switch */
+ } /* default */
+ } /* switch */
+ } /* for */
+}
+
+/*
+ * helper function, updates the pointer to cmd with the length
+ * of the current command, and also cleans up the first word of
+ * the new command in case it has been clobbered before.
+ * from ipfw2.c
+ */
+static ipfw_insn *
+next_cmd(ipfw_insn *cmd)
+{
+ cmd += F_LEN(cmd);
+ bzero(cmd, sizeof(*cmd));
+ return cmd;
+}
+
+/*
+ * A function to fill simple commands of size 1.
+ * Existing flags are preserved.
+ * from ipfw2.c
+ */
+static void
+fill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, uint16_t arg)
+{
+ cmd->opcode = opcode;
+ cmd->len = ((cmd->len) & (F_NOT | F_OR)) | 1;
+ cmd->arg1 = arg;
+}
+
+
+static u_int32_t
+fill_compat_tcpflags(u_int32_t flags) {
+ u_int32_t flags_compat = 0;
+
+ if (flags & TH_FIN)
+ flags_compat |= IP_FW_TCPF_FIN_COMPAT;
+ if (flags & TH_SYN)
+ flags_compat |= IP_FW_TCPF_SYN_COMPAT;
+ if (flags & TH_RST)
+ flags_compat |= IP_FW_TCPF_RST_COMPAT;
+ if (flags & TH_PUSH)
+ flags_compat |= IP_FW_TCPF_PSH_COMPAT;
+ if (flags & TH_ACK)
+ flags_compat |= IP_FW_TCPF_ACK_COMPAT;
+ if (flags & TH_URG)
+ flags_compat |= IP_FW_TCPF_URG_COMPAT;
+
+ return flags_compat;
+}
+
+
+/* ********************************************
+ * *********** Convert from Latest ************
+ * ********************************************/
+
+/*
+ * Things we're actively ignoring:
+ * sets, sets of addresses, blocks (NOT, OR)
+ */
+static void
+ipfw_map_from_cmds(struct ip_fw *curr_rule, struct ip_fw_compat *compat_rule)
+{
+ int l;
+ ipfw_insn *cmd;
+
+ for (l = curr_rule->act_ofs, cmd = curr_rule->cmd ;
+ l > 0 ;
+ l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
+ /* useful alias */
+ ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd;
+
+ switch (cmd->opcode) {
+ case O_PROTO:
+ /* protocol */
+ compat_rule->fw_prot = cmd->arg1;
+ break;
+
+ case O_IP_SRC_ME:
+ compat_rule->fw_flg |= IP_FW_F_SME_COMPAT;
+ if (cmd->len & F_NOT) {
+ compat_rule->fw_flg |= IP_FW_F_INVSRC_COMPAT;
+ }
+ break;
+
+ case O_IP_SRC_MASK:
+ {
+ /* addr/mask */
+ ipfw_insn_ip *ip = (ipfw_insn_ip *)cmd;
+
+ compat_rule->fw_src = ip->addr;
+ compat_rule->fw_smsk = ip->mask;
+ if (cmd->len & F_NOT) {
+ compat_rule->fw_flg |= IP_FW_F_INVSRC_COMPAT;
+ }
+ break;
+ }
+
+ case O_IP_SRC:
+ /* one IP */
+ /* source -
+ * for now we only deal with one address
+ * per rule and ignore sets of addresses
+ */
+ compat_rule->fw_src.s_addr = cmd32->d[0];
+ if (cmd->len & F_NOT) {
+ compat_rule->fw_flg |= IP_FW_F_INVSRC_COMPAT;
+ }
+ break;
+
+ case O_IP_SRCPORT:
+ {
+ /* source ports */
+ ipfw_insn_u16 *ports = (ipfw_insn_u16 *)cmd;
+ uint16_t *p = ports->ports;
+ int i, j;
+
+ /* copy list of ports */
+ for (i = F_LEN(cmd) - 1, j = 0; i > 0; i--, j++, p += 2) {
+ if (p[0] != p[1]) {
+ /* this is a range */
+ compat_rule->fw_flg |= IP_FW_F_SRNG_COMPAT;
+ compat_rule->fw_uar_compat.fw_pts[j++] = p[0];
+ compat_rule->fw_uar_compat.fw_pts[j] = p[1];
+ } else {
+ compat_rule->fw_uar_compat.fw_pts[j] = p[0];
+ }
+ }
+ IP_FW_SETNSRCP_COMPAT(compat_rule, j);
+
+ break;
+ }
+
+ case O_IP_DST_ME:
+ /* destination */
+ compat_rule->fw_flg |= IP_FW_F_DME_COMPAT;
+ if (cmd->len & F_NOT) {
+ compat_rule->fw_flg |= IP_FW_F_INVDST_COMPAT;
+ }
+ break;
+
+ case O_IP_DST_MASK:
+ {
+ /* addr/mask */
+ ipfw_insn_ip *ip = (ipfw_insn_ip *)cmd;
+
+ compat_rule->fw_dst = ip->addr;
+ compat_rule->fw_dmsk = ip->mask;
+ if (cmd->len & F_NOT) {
+ compat_rule->fw_flg |= IP_FW_F_INVDST_COMPAT;
+ }
+ break;
+ }
+ case O_IP_DST:
+ /* one IP */
+ /* dest -
+ * for now we only deal with one address
+ * per rule, and ignore sets of addresses
+ */
+ compat_rule->fw_dst.s_addr = cmd32->d[0];
+ if (cmd->len & F_NOT) {
+ compat_rule->fw_flg |= IP_FW_F_INVDST_COMPAT;
+ }
+ break;
+
+ case O_IP_DSTPORT:
+ {
+ /* dest. ports */
+ ipfw_insn_u16 *ports = (ipfw_insn_u16 *)cmd;
+ uint16_t *p = ports->ports;
+ int i,
+ j = IP_FW_GETNSRCP_COMPAT(compat_rule);
+
+ /* copy list of ports */
+ for (i = F_LEN(cmd) - 1; i > 0; i--, j++, p += 2) {
+ if (p[0] != p[1]) {
+ /* this is a range */
+ compat_rule->fw_flg |= IP_FW_F_DRNG_COMPAT;
+ compat_rule->fw_uar_compat.fw_pts[j++] = p[0];
+ compat_rule->fw_uar_compat.fw_pts[j] = p[1];
+ } else {
+ compat_rule->fw_uar_compat.fw_pts[j] = p[0];
+ }
+ }
+ IP_FW_SETNDSTP_COMPAT(compat_rule, (j - IP_FW_GETNSRCP_COMPAT(compat_rule)));
+
+ break;
+ }
+
+ case O_LOG:
+ {
+ ipfw_insn_log *c = (ipfw_insn_log *)cmd;
+
+ compat_rule->fw_flg |= IP_FW_F_PRN_COMPAT;
+ compat_rule->fw_logamount = c->max_log;
+ break;
+ }
+ case O_UID:
+ compat_rule->fw_flg |= IP_FW_F_UID_COMPAT;
+ compat_rule->fw_uid = cmd32->d[0];
+ break;
+
+ case O_IN:
+ if (cmd->len & F_NOT) {
+ compat_rule->fw_flg |= IP_FW_F_OUT_COMPAT;
+ } else {
+ compat_rule->fw_flg |= IP_FW_F_IN_COMPAT;
+ }
+ break;
+
+ case O_KEEP_STATE:
+ compat_rule->fw_flg |= IP_FW_F_KEEP_S_COMPAT;
+ break;
+
+ case O_LAYER2:
+ compat_rule->fw_flg |= IP_FW_BRIDGED_COMPAT;
+ break;
+
+ case O_XMIT:
+ {
+ ipfw_insn_if *ifcmd = (ipfw_insn_if *)cmd;
+ union ip_fw_if_compat ifu;
+
+ if ((ifcmd->o.len == 0) && (ifcmd->name[0] == '\0')) {
+ /* any */
+ compat_rule->fw_flg |= IP_FW_F_OIFACE_COMPAT;
+ ifu.fu_via_ip.s_addr = 0;
+ }
+ else if (ifcmd->p.ip.s_addr != 0) {
+ compat_rule->fw_flg |= IP_FW_F_OIFACE_COMPAT;
+ ifu.fu_via_ip = ifcmd->p.ip;
+ } else {
+ compat_rule->fw_flg |= IP_FW_F_OIFNAME_COMPAT;
+ strncpy(ifu.fu_via_if_compat.name, ifcmd->name, sizeof(ifu.fu_via_if_compat.name));
+ ifu.fu_via_if_compat.unit = ifcmd->p.unit;
+ }
+ compat_rule->fw_out_if = ifu;
+
+ break;
+ }
+
+ case O_RECV:
+ {
+ ipfw_insn_if *ifcmd = (ipfw_insn_if *)cmd;
+ union ip_fw_if_compat ifu;
+
+ if ((ifcmd->o.len == 0) && (ifcmd->name[0] == '\0')) {
+ /* any */
+ compat_rule->fw_flg |= IP_FW_F_IIFACE_COMPAT;
+ ifu.fu_via_ip.s_addr = 0;
+ }
+ else if (ifcmd->p.ip.s_addr != 0) {
+ compat_rule->fw_flg |= IP_FW_F_IIFACE_COMPAT;
+ ifu.fu_via_ip = ifcmd->p.ip;
+ } else {
+ compat_rule->fw_flg |= IP_FW_F_IIFNAME_COMPAT;
+ strncpy(ifu.fu_via_if_compat.name, ifcmd->name, sizeof(ifu.fu_via_if_compat.name));
+ ifu.fu_via_if_compat.unit = ifcmd->p.unit;
+ }
+ compat_rule->fw_in_if = ifu;
+
+ break;
+ }
+
+ case O_VIA:
+ {
+ ipfw_insn_if *ifcmd = (ipfw_insn_if *)cmd;
+ union ip_fw_if_compat ifu;
+
+ if ((ifcmd->o.len == 0) && (ifcmd->name[0] == '\0')) {
+ /* any */
+ ifu.fu_via_ip.s_addr = 0;
+ }
+ else if (ifcmd->name[0] != '\0') {
+ compat_rule->fw_flg |= IP_FW_F_IIFNAME_COMPAT;
+ strncpy(ifu.fu_via_if_compat.name, ifcmd->name, sizeof(ifu.fu_via_if_compat.name));
+ ifu.fu_via_if_compat.unit = ifcmd->p.unit;
+ } else {
+ ifu.fu_via_ip = ifcmd->p.ip;
+ }
+ compat_rule->fw_flg |= IF_FW_F_VIAHACK_COMPAT;
+ compat_rule->fw_out_if = compat_rule->fw_in_if = ifu;
+
+ break;
+ }
+
+ case O_FRAG:
+ compat_rule->fw_flg |= IP_FW_F_FRAG_COMPAT;
+ break;
+
+ case O_IPOPT:
+ /* IP options */
+ compat_rule->fw_ipopt = (cmd->arg1 & 0xff);
+ compat_rule->fw_ipnopt = ((cmd->arg1 >> 8) & 0xff);
+ break;
+
+ case O_TCPFLAGS:
+ /* check for "setup" */
+ if ((cmd->arg1 & 0xff) == TH_SYN &&
+ ((cmd->arg1 >> 8) & 0xff) == TH_ACK) {
+ compat_rule->fw_tcpf = IP_FW_TCPF_SYN_COMPAT;
+ compat_rule->fw_tcpnf = IP_FW_TCPF_ACK_COMPAT;
+ }
+ else {
+ compat_rule->fw_tcpf = fill_compat_tcpflags(cmd->arg1 & 0xff);
+ compat_rule->fw_tcpnf = fill_compat_tcpflags((cmd->arg1 >> 8) & 0xff);
+ }
+ break;
+
+ case O_TCPOPTS:
+ /* TCP options */
+ compat_rule->fw_tcpopt = (cmd->arg1 & 0xff);
+ compat_rule->fw_tcpnopt = ((cmd->arg1 >> 8) & 0xff);
+ break;
+
+ case O_ESTAB:
+ compat_rule->fw_ipflg |= IP_FW_IF_TCPEST_COMPAT;
+ break;
+
+ case O_ICMPTYPE:
+ {
+ /* ICMP */
+ /* XXX: check this */
+ int i, type;
+
+ compat_rule->fw_flg |= IP_FW_F_ICMPBIT_COMPAT;
+ for (i = 0; i < sizeof(uint32_t) ; i++) {
+ type = cmd32->d[0] & i;
+
+ compat_rule->fw_uar_compat.fw_icmptypes[type / (sizeof(unsigned) * 8)] |=
+ 1 << (type % (sizeof(unsigned) * 8));
+ }
+ break;
+ }
+ default:
+ break;
+ } /* switch */
+ } /* for */
+}
+
+static void
+ipfw_map_from_actions(struct ip_fw *curr_rule, struct ip_fw_compat *compat_rule)
+{
+ int l;
+ ipfw_insn *cmd;
+
+ for (l = curr_rule->cmd_len - curr_rule->act_ofs, cmd = ACTION_PTR(curr_rule);
+ l > 0 ;
+ l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
+ switch (cmd->opcode) {
+ case O_ACCEPT:
+ compat_rule->fw_flg |= IP_FW_F_ACCEPT_COMPAT;
+ break;
+ case O_COUNT:
+ compat_rule->fw_flg |= IP_FW_F_COUNT_COMPAT;
+ break;
+ case O_PIPE:
+ compat_rule->fw_flg |= IP_FW_F_PIPE_COMPAT;
+ compat_rule->fw_divert_port_compat = cmd->arg1;
+ break;
+ case O_QUEUE:
+ compat_rule->fw_flg |= IP_FW_F_QUEUE_COMPAT;
+ compat_rule->fw_divert_port_compat = cmd->arg1;
+ break;
+ case O_SKIPTO:
+ compat_rule->fw_flg |= IP_FW_F_SKIPTO_COMPAT;
+ compat_rule->fw_skipto_rule_compat = cmd->arg1;
+ break;
+ case O_DIVERT:
+ compat_rule->fw_flg |= IP_FW_F_DIVERT_COMPAT;
+ compat_rule->fw_divert_port_compat = cmd->arg1;
+ break;
+ case O_TEE:
+ compat_rule->fw_flg |= IP_FW_F_TEE_COMPAT;
+ compat_rule->fw_divert_port_compat = cmd->arg1;
+ break;
+ case O_FORWARD_IP:
+ {
+ ipfw_insn_sa *p = (ipfw_insn_sa *)cmd;
+
+ compat_rule->fw_flg |= IP_FW_F_FWD_COMPAT;
+ compat_rule->fw_fwd_ip_compat.sin_len = p->sa.sin_len;
+ compat_rule->fw_fwd_ip_compat.sin_family = p->sa.sin_family;
+ compat_rule->fw_fwd_ip_compat.sin_port = p->sa.sin_port;
+ compat_rule->fw_fwd_ip_compat.sin_addr = p->sa.sin_addr;
+
+ break;
+ }
+ case O_DENY:
+ compat_rule->fw_flg |= IP_FW_F_DENY_COMPAT;
+ break;
+ case O_REJECT:
+ compat_rule->fw_flg |= IP_FW_F_REJECT_COMPAT;
+ compat_rule->fw_reject_code_compat = cmd->arg1;
+ break;
+ case O_CHECK_STATE:
+ compat_rule->fw_flg |= IP_FW_F_CHECK_S_COMPAT;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void
+ipfw_version_latest_to_one(struct ip_fw *curr_rule, struct ip_fw_compat *rule_vers1)
+{
+ if (!rule_vers1)
+ return;
+
+ bzero(rule_vers1, sizeof(struct ip_fw_compat));
+
+ rule_vers1->version = IP_FW_VERSION_1;
+ rule_vers1->context = curr_rule->context;
+ rule_vers1->fw_number = curr_rule->rulenum;
+ rule_vers1->fw_pcnt = curr_rule->pcnt;
+ rule_vers1->fw_bcnt = curr_rule->bcnt;
+ rule_vers1->timestamp = curr_rule->timestamp;
+
+ /* convert actions */
+ ipfw_map_from_actions(curr_rule, rule_vers1);
+
+ /* convert commands */
+ ipfw_map_from_cmds(curr_rule, rule_vers1);
+
+#if FW2_DEBUG_VERBOSE
+ ipfw_print_vers1_struct(rule_vers1);
+#endif
+}
+
+/* first convert to version one then to version zero */
+static void
+ipfw_version_latest_to_zero(struct ip_fw *curr_rule, struct ip_old_fw *rule_vers0)
+{
+ struct ip_fw_compat rule_vers1;
+
+ ipfw_version_latest_to_one(curr_rule, &rule_vers1);
+
+ bzero(rule_vers0, sizeof(struct ip_old_fw));
+ bcopy(&rule_vers1.fw_uar_compat, &rule_vers0->fw_uar, sizeof(rule_vers1.fw_uar_compat));
+ bcopy(&rule_vers1.fw_in_if, &rule_vers0->fw_in_if, sizeof(rule_vers1.fw_in_if));
+ bcopy(&rule_vers1.fw_out_if, &rule_vers0->fw_out_if, sizeof(rule_vers1.fw_out_if));
+ bcopy(&rule_vers1.fw_un_compat, &rule_vers0->fw_un, sizeof(rule_vers1.fw_un_compat));
+
+ rule_vers0->fw_pcnt = rule_vers1.fw_pcnt;
+ rule_vers0->fw_bcnt = rule_vers1.fw_bcnt;
+ rule_vers0->fw_src = rule_vers1.fw_src;
+ rule_vers0->fw_dst = rule_vers1.fw_dst;
+ rule_vers0->fw_smsk = rule_vers1.fw_smsk;
+ rule_vers0->fw_dmsk = rule_vers1.fw_dmsk;
+ rule_vers0->fw_number = rule_vers1.fw_number;
+ rule_vers0->fw_flg = rule_vers1.fw_flg;
+ rule_vers0->fw_ipopt = rule_vers1.fw_ipopt;
+ rule_vers0->fw_ipnopt = rule_vers1.fw_ipnopt;
+ rule_vers0->fw_tcpf = rule_vers1.fw_tcpf;
+ rule_vers0->fw_tcpnf = rule_vers1.fw_tcpnf;
+ rule_vers0->timestamp = rule_vers1.timestamp;
+ rule_vers0->fw_prot = rule_vers1.fw_prot;
+ rule_vers0->fw_nports = rule_vers1.fw_nports;
+ rule_vers0->pipe_ptr = rule_vers1.pipe_ptr;
+ rule_vers0->next_rule_ptr = rule_vers1.next_rule_ptr;
+
+ if (rule_vers1.fw_ipflg && IP_FW_IF_TCPEST_COMPAT) rule_vers0->fw_tcpf |= IP_OLD_FW_TCPF_ESTAB;
+}
+
+void
+ipfw_convert_from_latest(struct ip_fw *curr_rule, void *old_rule, u_int32_t api_version)
+{
+ switch (api_version) {
+ case IP_FW_VERSION_0:
+ {
+ struct ip_old_fw *rule_vers0 = old_rule;
+
+ ipfw_version_latest_to_zero(curr_rule, rule_vers0);
+ break;
+ }
+ case IP_FW_VERSION_1:
+ {
+ struct ip_fw_compat *rule_vers1 = old_rule;
+
+ ipfw_version_latest_to_one(curr_rule, rule_vers1);
+ break;
+ }
+ case IP_FW_CURRENT_API_VERSION:
+ /* ipfw2 for now, don't need to do anything */
+ break;
+
+ default:
+ /* unknown version */
+ break;
+ }
+}
+
+
+/* ********************************************
+ * *********** Convert to Latest **************
+ * ********************************************/
+
+/* from ip_fw.c */
+static int
+ipfw_check_vers1_struct(struct ip_fw_compat *frwl)
+{
+ /* Check for invalid flag bits */
+ if ((frwl->fw_flg & ~IP_FW_F_MASK_COMPAT) != 0) {
+ /*
+ printf(("%s undefined flag bits set (flags=%x)\n",
+ err_prefix, frwl->fw_flg));
+ */
+ return (EINVAL);
+ }
+ if (frwl->fw_flg == IP_FW_F_CHECK_S_COMPAT) {
+ /* check-state */
+ return 0 ;
+ }
+ /* Must apply to incoming or outgoing (or both) */
+ if (!(frwl->fw_flg & (IP_FW_F_IN_COMPAT | IP_FW_F_OUT_COMPAT))) {
+ /*
+ printf(("%s neither in nor out\n", err_prefix));
+ */
+ return (EINVAL);
+ }
+ /* Empty interface name is no good */
+ if (((frwl->fw_flg & IP_FW_F_IIFNAME_COMPAT)
+ && !*frwl->fw_in_if.fu_via_if_compat.name)
+ || ((frwl->fw_flg & IP_FW_F_OIFNAME_COMPAT)
+ && !*frwl->fw_out_if.fu_via_if_compat.name)) {
+ /*
+ printf(("%s empty interface name\n", err_prefix));
+ */
+ return (EINVAL);
+ }
+ /* Sanity check interface matching */
+ if ((frwl->fw_flg & IF_FW_F_VIAHACK_COMPAT) == IF_FW_F_VIAHACK_COMPAT) {
+ ; /* allow "via" backwards compatibility */
+ } else if ((frwl->fw_flg & IP_FW_F_IN_COMPAT)
+ && (frwl->fw_flg & IP_FW_F_OIFACE_COMPAT)) {
+ /*
+ printf(("%s outgoing interface check on incoming\n",
+ err_prefix));
+ */
+ return (EINVAL);
+ }
+ /* Sanity check port ranges */
+ if ((frwl->fw_flg & IP_FW_F_SRNG_COMPAT) && IP_FW_GETNSRCP_COMPAT(frwl) < 2) {
+ /*
+ printf(("%s src range set but n_src_p=%d\n",
+ err_prefix, IP_FW_GETNSRCP_COMPAT(frwl)));
+ */
+ return (EINVAL);
+ }
+ if ((frwl->fw_flg & IP_FW_F_DRNG_COMPAT) && IP_FW_GETNDSTP_COMPAT(frwl) < 2) {
+ /*
+ printf(("%s dst range set but n_dst_p=%d\n",
+ err_prefix, IP_FW_GETNDSTP_COMPAT(frwl)));
+ */
+ return (EINVAL);
+ }
+ if (IP_FW_GETNSRCP_COMPAT(frwl) + IP_FW_GETNDSTP_COMPAT(frwl) > IP_FW_MAX_PORTS_COMPAT) {
+ /*
+ printf(("%s too many ports (%d+%d)\n",
+ err_prefix, IP_FW_GETNSRCP_COMPAT(frwl), IP_FW_GETNDSTP_COMPAT(frwl)));
+ */
+ return (EINVAL);
+ }
+ /*
+ * Protocols other than TCP/UDP don't use port range
+ */
+ if ((frwl->fw_prot != IPPROTO_TCP) &&
+ (frwl->fw_prot != IPPROTO_UDP) &&
+ (IP_FW_GETNSRCP_COMPAT(frwl) || IP_FW_GETNDSTP_COMPAT(frwl))) {
+ /*
+ printf(("%s port(s) specified for non TCP/UDP rule\n",
+ err_prefix));
+ */
+ return (EINVAL);
+ }
+
+ /*
+ * Rather than modify the entry to make such entries work,
+ * we reject this rule and require user level utilities
+ * to enforce whatever policy they deem appropriate.
+ */
+ if ((frwl->fw_src.s_addr & (~frwl->fw_smsk.s_addr)) ||
+ (frwl->fw_dst.s_addr & (~frwl->fw_dmsk.s_addr))) {
+ /*
+ printf(("%s rule never matches\n", err_prefix));
+ */
+ return (EINVAL);
+ }
+
+ if ((frwl->fw_flg & IP_FW_F_FRAG_COMPAT) &&
+ (frwl->fw_prot == IPPROTO_UDP || frwl->fw_prot == IPPROTO_TCP)) {
+ if (frwl->fw_nports) {
+ /*
+ printf(("%s cannot mix 'frag' and ports\n", err_prefix));
+ */
+ return (EINVAL);
+ }
+ if (frwl->fw_prot == IPPROTO_TCP &&
+ frwl->fw_tcpf != frwl->fw_tcpnf) {
+ /*
+ printf(("%s cannot mix 'frag' and TCP flags\n", err_prefix));
+ */
+ return (EINVAL);
+ }
+ }
+
+ /* Check command specific stuff */
+ switch (frwl->fw_flg & IP_FW_F_COMMAND_COMPAT)
+ {
+ case IP_FW_F_REJECT_COMPAT:
+ if (frwl->fw_reject_code_compat >= 0x100
+ && !(frwl->fw_prot == IPPROTO_TCP
+ && frwl->fw_reject_code_compat == IP_FW_REJECT_RST_COMPAT)) {
+ /*
+ printf(("%s unknown reject code\n", err_prefix));
+ */
+ return (EINVAL);
+ }
+ break;
+ case IP_FW_F_DIVERT_COMPAT: /* Diverting to port zero is invalid */
+ case IP_FW_F_TEE_COMPAT:
+ case IP_FW_F_PIPE_COMPAT: /* piping through 0 is invalid */
+ case IP_FW_F_QUEUE_COMPAT: /* piping through 0 is invalid */
+ if (frwl->fw_divert_port_compat == 0) {
+ /*
+ printf(("%s can't divert to port 0\n", err_prefix));
+ */
+ return (EINVAL);
+ }
+ break;
+ case IP_FW_F_DENY_COMPAT:
+ case IP_FW_F_ACCEPT_COMPAT:
+ case IP_FW_F_COUNT_COMPAT:
+ case IP_FW_F_SKIPTO_COMPAT:
+ case IP_FW_F_FWD_COMPAT:
+ case IP_FW_F_UID_COMPAT:
+ break;
+ default:
+ /*
+ printf(("%s invalid command\n", err_prefix));
+ */
+ return (EINVAL);
+ }
+
+ return 0;
+}
+
+static void
+ipfw_convert_to_cmds(struct ip_fw *curr_rule, struct ip_fw_compat *compat_rule)
+{
+ int k;
+ uint32_t actbuf[255], cmdbuf[255];
+ ipfw_insn *action, *cmd, *src, *dst;
+ ipfw_insn *have_state = NULL, /* track check-state or keep-state */
+ *end_action = NULL,
+ *end_cmd = NULL;
+
+ if (!compat_rule || !curr_rule || !(curr_rule->cmd)) {
+ return;
+ }
+
+ /* preemptively check the old ip_fw rule to
+ * make sure it's valid before starting to copy stuff
+ */
+ if (ipfw_check_vers1_struct(compat_rule)) {
+ /* bad rule */
+ return;
+ }
+
+ bzero(actbuf, sizeof(actbuf)); /* actions go here */
+ bzero(cmdbuf, sizeof(cmdbuf));
+
+ /* fill in action */
+ action = (ipfw_insn *)actbuf;
+ {
+ u_int flag = compat_rule->fw_flg;
+
+ action->len = 1; /* default */
+
+ if (flag & IP_FW_F_CHECK_S_COMPAT) {
+ have_state = action;
+ action->opcode = O_CHECK_STATE;
+ }
+ else {
+ switch (flag & IP_FW_F_COMMAND_COMPAT) {
+ case IP_FW_F_ACCEPT_COMPAT:
+ action->opcode = O_ACCEPT;
+ break;
+ case IP_FW_F_COUNT_COMPAT:
+ action->opcode = O_COUNT;
+ break;
+ case IP_FW_F_PIPE_COMPAT:
+ action->opcode = O_PIPE;
+ action->len = F_INSN_SIZE(ipfw_insn_pipe);
+ action->arg1 = compat_rule->fw_divert_port_compat;
+ break;
+ case IP_FW_F_QUEUE_COMPAT:
+ action->opcode = O_QUEUE;
+ action->len = F_INSN_SIZE(ipfw_insn_pipe);
+ action->arg1 = compat_rule->fw_divert_port_compat;
+ break;
+ case IP_FW_F_SKIPTO_COMPAT:
+ action->opcode = O_SKIPTO;
+ action->arg1 = compat_rule->fw_skipto_rule_compat;
+ break;
+ case IP_FW_F_DIVERT_COMPAT:
+ action->opcode = O_DIVERT;
+ action->arg1 = compat_rule->fw_divert_port_compat;
+ break;
+ case IP_FW_F_TEE_COMPAT:
+ action->opcode = O_TEE;
+ action->arg1 = compat_rule->fw_divert_port_compat;
+ break;
+ case IP_FW_F_FWD_COMPAT:
+ {
+ ipfw_insn_sa *p = (ipfw_insn_sa *)action;
+
+ action->opcode = O_FORWARD_IP;
+ action->len = F_INSN_SIZE(ipfw_insn_sa);
+
+ p->sa.sin_len = compat_rule->fw_fwd_ip_compat.sin_len;
+ p->sa.sin_family = compat_rule->fw_fwd_ip_compat.sin_family;
+ p->sa.sin_port = compat_rule->fw_fwd_ip_compat.sin_port;
+ p->sa.sin_addr = compat_rule->fw_fwd_ip_compat.sin_addr;
+
+ break;
+ }
+ case IP_FW_F_DENY_COMPAT:
+ action->opcode = O_DENY;
+ action->arg1 = 0;
+ break;
+ case IP_FW_F_REJECT_COMPAT:
+ action->opcode = O_REJECT;
+ action->arg1 = compat_rule->fw_reject_code_compat;
+ break;
+ default:
+ action->opcode = O_NOP;
+ break;
+ }
+ }
+
+ /* action is mandatory */
+ if (action->opcode == O_NOP) {
+ return;
+ }
+
+ action = next_cmd(action);
+ } /* end actions */
+
+ cmd = (ipfw_insn *)cmdbuf;
+
+ /* this is O_CHECK_STATE, we're done */
+ if (have_state) {
+ goto done;
+ }
+
+ {
+ ipfw_insn *prev = NULL;
+ u_int flag = compat_rule->fw_flg;
+
+ /* logging */
+ if (flag & IP_FW_F_PRN_COMPAT) {
+ ipfw_insn_log *c = (ipfw_insn_log *)cmd;
+
+ cmd->opcode = O_LOG;
+ cmd->len |= F_INSN_SIZE(ipfw_insn_log);
+ c->max_log = compat_rule->fw_logamount;
+
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ }
+
+ /* protocol */
+ if (compat_rule->fw_prot != 0) {
+ fill_cmd(cmd, O_PROTO, compat_rule->fw_prot);
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ }
+
+ /* source */
+ if (flag & IP_FW_F_SME_COMPAT) {
+ cmd->opcode = O_IP_SRC_ME;
+ cmd->len |= F_INSN_SIZE(ipfw_insn);
+ if (flag & IP_FW_F_INVSRC_COMPAT) {
+ cmd->len ^= F_NOT; /* toggle F_NOT */
+ }
+
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ } else {
+ if (compat_rule->fw_smsk.s_addr != 0) {
+ /* addr/mask */
+ ipfw_insn_ip *ip = (ipfw_insn_ip *)cmd;
+
+ ip->addr = compat_rule->fw_src;
+ ip->mask = compat_rule->fw_smsk;
+ cmd->opcode = O_IP_SRC_MASK;
+ cmd->len |= F_INSN_SIZE(ipfw_insn_ip); /* double check this */
+ } else {
+ /* one IP */
+ ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd; /* alias for cmd */
+
+ if (compat_rule->fw_src.s_addr == 0) {
+ /* any */
+ cmd32->o.len &= ~F_LEN_MASK; /* zero len */
+ } else {
+ cmd32->d[0] = compat_rule->fw_src.s_addr;
+ cmd32->o.opcode = O_IP_SRC;
+ cmd32->o.len |= F_INSN_SIZE(ipfw_insn_u32);
+ }
+ }
+
+ if (flag & IP_FW_F_INVSRC_COMPAT) {
+ cmd->len ^= F_NOT; /* toggle F_NOT */
+ }
+
+ if (F_LEN(cmd) != 0) { /* !any */
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ }
+ }
+
+ /* source ports */
+ {
+ ipfw_insn_u16 *ports = (ipfw_insn_u16 *)cmd;
+ uint16_t *p = ports->ports;
+ int i, j = 0,
+ nports = IP_FW_GETNSRCP_COMPAT(compat_rule),
+ have_range = 0;
+
+ cmd->opcode = O_IP_SRCPORT;
+ for (i = 0; i < nports; i++) {
+ if (((flag & IP_FW_F_SRNG_COMPAT) ||
+ (flag & IP_FW_F_SMSK_COMPAT)) && !have_range) {
+ p[0] = compat_rule->fw_uar_compat.fw_pts[i++];
+ p[1] = compat_rule->fw_uar_compat.fw_pts[i];
+ have_range = 1;
+ } else {
+ p[0] = p[1] = compat_rule->fw_uar_compat.fw_pts[i];
+ }
+ p += 2;
+ j++;
+ }
+
+ if (j > 0) {
+ ports->o.len |= j+1; /* leave F_NOT and F_OR untouched */
+ }
+
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ }
+
+ /* destination */
+ if (flag & IP_FW_F_DME_COMPAT) {
+ cmd->opcode = O_IP_DST_ME;
+ cmd->len |= F_INSN_SIZE(ipfw_insn);
+ if (flag & IP_FW_F_INVDST_COMPAT) {
+ cmd->len ^= F_NOT; /* toggle F_NOT */
+ }
+
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ } else {
+ if (compat_rule->fw_dmsk.s_addr != 0) {
+ /* addr/mask */
+ ipfw_insn_ip *ip = (ipfw_insn_ip *)cmd;
+
+ ip->addr = compat_rule->fw_dst;
+ ip->mask = compat_rule->fw_dmsk;
+ cmd->opcode = O_IP_DST_MASK;
+ cmd->len |= F_INSN_SIZE(ipfw_insn_ip); /* double check this */
+ } else {
+ /* one IP */
+ ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd; /* alias for cmd */
+
+ if (compat_rule->fw_dst.s_addr == 0) {
+ /* any */
+ cmd32->o.len &= ~F_LEN_MASK; /* zero len */
+ } else {
+ cmd32->d[0] = compat_rule->fw_dst.s_addr;
+ cmd32->o.opcode = O_IP_DST;
+ cmd32->o.len |= F_INSN_SIZE(ipfw_insn_u32);
+ }
+ }
+
+ if (flag & IP_FW_F_INVDST_COMPAT) {
+ cmd->len ^= F_NOT; /* toggle F_NOT */
+ }
+
+ if (F_LEN(cmd) != 0) { /* !any */
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ }
+ }
+
+ /* dest. ports */
+ {
+ ipfw_insn_u16 *ports = (ipfw_insn_u16 *)cmd;
+ uint16_t *p = ports->ports;
+ int i = IP_FW_GETNSRCP_COMPAT(compat_rule),
+ j = 0,
+ nports = (IP_FW_GETNDSTP_COMPAT(compat_rule) + i),
+ have_range = 0;
+
+ cmd->opcode = O_IP_DSTPORT;
+ for (; i < nports; i++, p += 2) {
+ if (((flag & IP_FW_F_DRNG_COMPAT) ||
+ (flag & IP_FW_F_DMSK_COMPAT)) && !have_range) {
+ /* range */
+ p[0] = compat_rule->fw_uar_compat.fw_pts[i++];
+ p[1] = compat_rule->fw_uar_compat.fw_pts[i];
+ have_range = 1;
+ } else {
+ p[0] = p[1] = compat_rule->fw_uar_compat.fw_pts[i];
+ }
+ j++;
+ }
+
+ if (j > 0) {
+ ports->o.len |= j+1; /* leave F_NOT and F_OR untouched */
+ }
+
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ }
+
+ if (flag & IP_FW_F_UID_COMPAT) {
+ ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd; /* alias for cmd */
+
+ cmd32->o.opcode = O_UID;
+ cmd32->o.len |= F_INSN_SIZE(ipfw_insn_u32);
+ cmd32->d[0] = compat_rule->fw_uid;
+
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ }
+
+ if (flag & IP_FW_F_KEEP_S_COMPAT) {
+ have_state = cmd;
+ fill_cmd(cmd, O_KEEP_STATE, 0);
+
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ }
+ if (flag & IP_FW_BRIDGED_COMPAT) {
+ fill_cmd(cmd, O_LAYER2, 0);
+
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ }
+
+ if ((flag & IF_FW_F_VIAHACK_COMPAT) == IF_FW_F_VIAHACK_COMPAT) {
+ /* via */
+ ipfw_insn_if *ifcmd = (ipfw_insn_if *)cmd;
+ union ip_fw_if_compat ifu = compat_rule->fw_in_if;
+
+ cmd->opcode = O_VIA;
+ ifcmd->o.len |= F_INSN_SIZE(ipfw_insn_if);
+
+ if (ifu.fu_via_ip.s_addr == 0) {
+ /* "any" */
+ ifcmd->name[0] = '\0';
+ ifcmd->o.len = 0;
+ }
+ else if (compat_rule->fw_flg & IP_FW_F_IIFNAME_COMPAT) {
+ /* by name */
+ strncpy(ifcmd->name, ifu.fu_via_if_compat.name, sizeof(ifcmd->name));
+ ifcmd->p.unit = ifu.fu_via_if_compat.unit;
+ } else {
+ /* by addr */
+ ifcmd->p.ip = ifu.fu_via_ip;
+ }
+
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ } else {
+ if (flag & IP_FW_F_IN_COMPAT) {
+ fill_cmd(cmd, O_IN, 0);
+
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ }
+ if (flag & IP_FW_F_OUT_COMPAT) {
+ /* if the previous command was O_IN, and this
+ * is being set as well, it's equivalent to not
+ * having either command, so let's back up prev
+ * to the cmd before it and move cmd to prev.
+ */
+ if (prev->opcode == O_IN) {
+ cmd = prev;
+ bzero(cmd, sizeof(*cmd));
+ } else {
+ cmd->len ^= F_NOT; /* toggle F_NOT */
+ fill_cmd(cmd, O_IN, 0);
+
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ }
+ }
+ if (flag & IP_FW_F_OIFACE_COMPAT) {
+ /* xmit */
+ ipfw_insn_if *ifcmd = (ipfw_insn_if *)cmd;
+ union ip_fw_if_compat ifu = compat_rule->fw_out_if;
+
+ cmd->opcode = O_XMIT;
+ ifcmd->o.len |= F_INSN_SIZE(ipfw_insn_if);
+
+ if (ifu.fu_via_ip.s_addr == 0) {
+ /* "any" */
+ ifcmd->name[0] = '\0';
+ ifcmd->o.len = 0;
+ }
+ else if (flag & IP_FW_F_OIFNAME_COMPAT) {
+ /* by name */
+ strncpy(ifcmd->name, ifu.fu_via_if_compat.name, sizeof(ifcmd->name));
+ ifcmd->p.unit = ifu.fu_via_if_compat.unit;
+ } else {
+ /* by addr */
+ ifcmd->p.ip = ifu.fu_via_ip;
+ }
+
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ }
+ else if (flag & IP_FW_F_IIFACE_COMPAT) {
+ /* recv */
+ ipfw_insn_if *ifcmd = (ipfw_insn_if *)cmd;
+ union ip_fw_if_compat ifu = compat_rule->fw_in_if;
+
+ cmd->opcode = O_RECV;
+ ifcmd->o.len |= F_INSN_SIZE(ipfw_insn_if);
+
+ if (ifu.fu_via_ip.s_addr == 0) {
+ /* "any" */
+ ifcmd->name[0] = '\0';
+ ifcmd->o.len = 0;
+ }
+ else if (flag & IP_FW_F_IIFNAME_COMPAT) {
+ /* by name */
+ strncpy(ifcmd->name, ifu.fu_via_if_compat.name, sizeof(ifcmd->name));
+ ifcmd->p.unit = ifu.fu_via_if_compat.unit;
+ } else {
+ /* by addr */
+ ifcmd->p.ip = ifu.fu_via_ip;
+ }
+
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ }
+ }
+
+ if (flag & IP_FW_F_FRAG_COMPAT) {
+ fill_cmd(cmd, O_FRAG, 0);
+
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ }
+
+ /* IP options */
+ if (compat_rule->fw_ipopt != 0 || compat_rule->fw_ipnopt != 0) {
+ fill_cmd(cmd, O_IPOPT, (compat_rule->fw_ipopt & 0xff) |
+ (compat_rule->fw_ipnopt & 0xff) << 8);
+
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ }
+
+ if (compat_rule->fw_prot == IPPROTO_TCP) {
+ if (compat_rule->fw_ipflg & IP_FW_IF_TCPEST_COMPAT) {
+ fill_cmd(cmd, O_ESTAB, 0);
+
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ }
+
+ /* TCP options and flags */
+ if (compat_rule->fw_tcpf != 0 || compat_rule->fw_tcpnf != 0) {
+ if ((compat_rule->fw_tcpf & IP_FW_TCPF_SYN_COMPAT) &&
+ compat_rule->fw_tcpnf & IP_FW_TCPF_ACK_COMPAT) {
+ fill_cmd(cmd, O_TCPFLAGS, (TH_SYN) | ( (TH_ACK) & 0xff) <<8);
+
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ }
+ else {
+ fill_cmd(cmd, O_TCPFLAGS, (compat_rule->fw_tcpf & 0xff) |
+ (compat_rule->fw_tcpnf & 0xff) << 8);
+
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ }
+ }
+ if (compat_rule->fw_tcpopt != 0 || compat_rule->fw_tcpnopt != 0) {
+ fill_cmd(cmd, O_TCPOPTS, (compat_rule->fw_tcpopt & 0xff) |
+ (compat_rule->fw_tcpnopt & 0xff) << 8);
+
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ }
+ }
+
+ /* ICMP */
+ /* XXX: check this */
+ if (flag & IP_FW_F_ICMPBIT_COMPAT) {
+ int i;
+ ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd; /* alias for cmd */
+
+ cmd32->o.opcode = O_ICMPTYPE;
+ cmd32->o.len |= F_INSN_SIZE(ipfw_insn_u32);
+
+ for (i = 0; i < IP_FW_ICMPTYPES_DIM_COMPAT; i++) {
+ cmd32->d[0] |= compat_rule->fw_uar_compat.fw_icmptypes[i];
+ }
+
+ prev = cmd;
+ cmd = next_cmd(cmd);
+ }
+ } /* end commands */
+
+done:
+ /* finally, copy everything into the current
+ * rule buffer in the right order.
+ */
+ dst = curr_rule->cmd;
+
+ /* first, do match probability */
+ if (compat_rule->fw_flg & IP_FW_F_RND_MATCH_COMPAT) {
+ dst->opcode = O_PROB;
+ dst->len = 2;
+ *((int32_t *)(dst+1)) = compat_rule->pipe_ptr;
+ dst += dst->len;
+ }
+
+ /* generate O_PROBE_STATE if necessary */
+ if (have_state && have_state->opcode != O_CHECK_STATE) {
+ fill_cmd(dst, O_PROBE_STATE, 0);
+ dst = next_cmd(dst);
+ }
+
+ /*
+ * copy all commands but O_LOG, O_KEEP_STATE
+ */
+ for (src = (ipfw_insn *)cmdbuf; src != cmd; src += k) {
+ k = F_LEN(src);
+
+ switch (src->opcode) {
+ case O_LOG:
+ case O_KEEP_STATE:
+ break;
+ default:
+ bcopy(src, dst, k * sizeof(uint32_t));
+ dst += k;
+ }
+ }
+
+ /*
+ * put back the have_state command as last opcode
+ */
+ if (have_state && have_state->opcode != O_CHECK_STATE) {
+ k = F_LEN(have_state);
+ bcopy(have_state, dst, k * sizeof(uint32_t));
+ dst += k;
+ }
+
+ /*
+ * start action section
+ */
+ curr_rule->act_ofs = dst - curr_rule->cmd;
+
+ /*
+ * put back O_LOG if necessary
+ */
+ src = (ipfw_insn *)cmdbuf;
+ if (src->opcode == O_LOG) {
+ k = F_LEN(src);
+ bcopy(src, dst, k * sizeof(uint32_t));
+ dst += k;
+ }
+
+ /*
+ * copy all other actions
+ */
+ for (src = (ipfw_insn *)actbuf; src != action; src += k) {
+ k = F_LEN(src);
+ bcopy(src, dst, k * sizeof(uint32_t));
+ dst += k;
+ }
+
+ curr_rule->cmd_len = (uint32_t *)dst - (uint32_t *)(curr_rule->cmd);
+
+ return;
+}
+
+static int
+ipfw_version_one_to_version_two(struct sockopt *sopt, struct ip_fw *curr_rule,
+ struct ip_fw_compat *rule_vers1)
+{
+ int err = EINVAL;
+ struct ip_fw_compat *rule_ptr;
+ struct ip_fw_compat rule;
+
+ if (rule_vers1) {
+ rule_ptr = rule_vers1;
+ err = 0;
+ } else {
+ /* do some basic size checking here, more extensive checking later */
+ if (!sopt->sopt_val || sopt->sopt_valsize < sizeof(struct ip_fw_compat))
+ return err;
+
+ if ((err = sooptcopyin(sopt, &rule, sizeof(struct ip_fw_compat),
+ sizeof(struct ip_fw_compat)))) {
+ return err;
+ }
+
+ rule_ptr = &rule;
+ }
+
+ /* deal with commands */
+ ipfw_convert_to_cmds(curr_rule, rule_ptr);
+
+ curr_rule->version = IP_FW_CURRENT_API_VERSION;
+ curr_rule->context = rule_ptr->context;
+ curr_rule->rulenum = rule_ptr->fw_number;
+ curr_rule->pcnt = rule_ptr->fw_pcnt;
+ curr_rule->bcnt = rule_ptr->fw_bcnt;
+ curr_rule->timestamp = rule_ptr->timestamp;
+
+
+#if FW2_DEBUG_VERBOSE
+ ipfw_print_vers2_struct(curr_rule);
+#endif /* FW2_DEBUG_VERBOSE */
+
+ return err;
+}
+
+/* This converts to whatever the latest version is. Currently the
+ * latest version of the firewall is ipfw2.
+ */
+static int
+ipfw_version_one_to_latest(struct sockopt *sopt, struct ip_fw *curr_rule, struct ip_fw_compat *rule_vers1)
+{
+ int err;
+
+ /* if rule_vers1 is not null then this is coming from
+ * ipfw_version_zero_to_latest(), so pass that along;
+ * otherwise let ipfw_version_one_to_version_two()
+ * get the rule from sopt.
+ */
+ err = ipfw_version_one_to_version_two(sopt, curr_rule, rule_vers1);
+
+ return err;
+}
+
+static void
+ipfw_version_zero_to_one(struct ip_old_fw *rule_vers0, struct ip_fw_compat *rule_vers1)
+{
+ bzero(rule_vers1, sizeof(struct ip_fw_compat));
+ bcopy(&rule_vers0->fw_uar, &rule_vers1->fw_uar_compat, sizeof(rule_vers0->fw_uar));
+ bcopy(&rule_vers0->fw_in_if, &rule_vers1->fw_in_if, sizeof(rule_vers0->fw_in_if));
+ bcopy(&rule_vers0->fw_out_if, &rule_vers1->fw_out_if, sizeof(rule_vers0->fw_out_if));
+ bcopy(&rule_vers0->fw_un, &rule_vers1->fw_un_compat, sizeof(rule_vers0->fw_un));
+
+ rule_vers1->version = 10;
+ rule_vers1->fw_pcnt = rule_vers0->fw_pcnt;
+ rule_vers1->fw_bcnt = rule_vers0->fw_bcnt;
+ rule_vers1->fw_src = rule_vers0->fw_src;
+ rule_vers1->fw_dst = rule_vers0->fw_dst;
+ rule_vers1->fw_smsk = rule_vers0->fw_smsk;
+ rule_vers1->fw_dmsk = rule_vers0->fw_dmsk;
+ rule_vers1->fw_number = rule_vers0->fw_number;
+ rule_vers1->fw_flg = rule_vers0->fw_flg;
+ rule_vers1->fw_ipopt = rule_vers0->fw_ipopt;
+ rule_vers1->fw_ipnopt = rule_vers0->fw_ipnopt;
+ rule_vers1->fw_tcpf = rule_vers0->fw_tcpf & ~IP_OLD_FW_TCPF_ESTAB;
+ rule_vers1->fw_tcpnf = rule_vers0->fw_tcpnf;
+ rule_vers1->timestamp = rule_vers0->timestamp;
+ rule_vers1->fw_prot = rule_vers0->fw_prot;
+ rule_vers1->fw_nports = rule_vers0->fw_nports;
+ rule_vers1->pipe_ptr = rule_vers0->pipe_ptr;
+ rule_vers1->next_rule_ptr = rule_vers0->next_rule_ptr;
+ rule_vers1->fw_ipflg = (rule_vers0->fw_tcpf & IP_OLD_FW_TCPF_ESTAB) ? IP_FW_IF_TCPEST_COMPAT : 0;
+}
+
+/* first convert to version one, then to version two */
+static int
+ipfw_version_zero_to_latest(struct sockopt *sopt, struct ip_fw *curr_rule)
+{
+ int err;
+ struct ip_old_fw rule_vers0;
+ struct ip_fw_compat rule_vers1;
+
+ if (sopt->sopt_name == IP_OLD_FW_GET ||
+ sopt->sopt_name == IP_OLD_FW_FLUSH ||
+ sopt->sopt_val == NULL) {
+ /* In the old-style API, it was legal to not pass in a rule
+ * structure for certain firewall operations (e.g. flush,
+ * reset log). If that's the situation, we pretend we received
+ * a blank structure. */
+ bzero(curr_rule, sizeof(struct ip_fw));
+ curr_rule->version = 10;
+ }
+ else {
+ if (!sopt->sopt_val || sopt->sopt_valsize < sizeof(struct ip_old_fw)) {
+ return EINVAL;
+ }
+
+ err = sooptcopyin(sopt, &rule_vers0, sizeof(struct ip_old_fw),
+ sizeof(struct ip_old_fw));
+ if (err) {
+ return err;
+ }
+
+ ipfw_version_zero_to_one(&rule_vers0, &rule_vers1);
+ }
+
+ return (ipfw_version_one_to_latest(sopt, curr_rule, &rule_vers1));
+}
+
+/* rule is a u_int32_t buffer[255] into which the converted
+ * (if necessary) rules go.
+ */
+int
+ipfw_convert_to_latest(struct sockopt *sopt, struct ip_fw *curr_rule, int api_version)
+{
+ int err = 0;
+
+ /* the following functions copy the rules passed in and
+ * convert to latest structures based on version
+ */
+ switch (api_version) {
+ case IP_FW_VERSION_0:
+ /* this is the oldest version we support */
+ err = ipfw_version_zero_to_latest(sopt, curr_rule);
+ break;
+
+ case IP_FW_VERSION_1:
+ /* this is the version supported in Panther */
+ err = ipfw_version_one_to_latest(sopt, curr_rule, NULL);
+ break;
+
+ case IP_FW_CURRENT_API_VERSION:
+ /* IPFW2 for now */
+ /* do nothing here... */
+ break;
+
+ default:
+ /* unrecognized/unsupported version */
+ err = EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+int
+ipfw_get_command_and_version(struct sockopt *sopt, int *command, u_int32_t *api_version)
+{
+ int cmd;
+ int err = 0;
+ u_int32_t vers = IP_FW_VERSION_NONE;
+
+ /* first deal with the oldest version */
+ if (sopt->sopt_name == IP_OLD_FW_GET) {
+ vers = IP_FW_VERSION_0;
+ cmd = IP_FW_GET;
+ }
+ else if (sopt->sopt_name == IP_OLD_FW_FLUSH) {
+ vers = IP_FW_VERSION_0;
+ cmd = IP_FW_FLUSH;
+ }
+ else if (sopt->sopt_name == IP_OLD_FW_ZERO) {
+ vers = IP_FW_VERSION_0;
+ cmd = IP_FW_ZERO;
+ }
+ else if (sopt->sopt_name == IP_OLD_FW_ADD) {
+ vers = IP_FW_VERSION_0;
+ cmd = IP_FW_ADD;
+ }
+ else if (sopt->sopt_name == IP_OLD_FW_DEL) {
+ vers = IP_FW_VERSION_0;
+ cmd = IP_FW_DEL;
+ }
+ else if (sopt->sopt_name == IP_OLD_FW_RESETLOG) {
+ vers = IP_FW_VERSION_0;
+ cmd = IP_FW_RESETLOG;
+ }
+ else {
+ cmd = sopt->sopt_name;
+ }
+
+ if (vers == IP_FW_VERSION_NONE) {
+ /* working off the fact that the offset
+ * is the same in both structs.
+ */
+ struct ip_fw rule;
+
+ if (!sopt->sopt_val || sopt->sopt_valsize < sizeof(struct ip_fw))
+ return EINVAL;
+
+ if ((err = sooptcopyin(sopt, &rule, sizeof(struct ip_fw),
+ sizeof(struct ip_fw)))) {
+ return err;
+ }
+
+ vers = rule.version;
+ }
+
+ if (command) {
+ *command = cmd;
+ }
+
+ if (api_version) {
+ *api_version = vers;
+ }
+
+ return err;
+}
+