#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
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"
#if FW2_DEBUG_VERBOSE
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 }
};
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}
};
#endif
#if 0
static void
ipfw_print_fw_flags(u_int flags)
{
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;
}
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
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 = ',';
}
}
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)
break;
for (n=i+1; n < len; n++)
if ( (p[n/8] & (1 << (7 - (n%8)))) != 0)
return -1;
return i;
}
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;
}
for (len = len / 2; len > 0; len--, a += 2) {
int mb =
(cmd->o.opcode == O_IP_SRC || cmd->o.opcode == O_IP_DST) ?
32 : contigmask((uint8_t *)&(a[1]), 32);
if (mb == 0) {
printf("any");
} else {
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(",");
}
}
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);
}
}
#endif
#if FW2_DEBUG_VERBOSE
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: %llu\n", vers2_rule->pcnt);
printf("bcnt: %llu\n", vers2_rule->bcnt);
printf("timestamp: %d\n", vers2_rule->timestamp);
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:
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");
}
for (l = vers2_rule->act_ofs, cmd = vers2_rule->cmd ;
l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd;
switch(cmd->opcode) {
case O_PROB:
break;
case O_PROBE_STATE:
break;
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:
{
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
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 %u", ntohl(cmd32->d[0]));
break;
case O_TCPSEQ:
printf(" tcpseq %u", 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);
}
}
}
}
}
#endif
static ipfw_insn *
next_cmd(ipfw_insn *cmd)
{
cmd += F_LEN(cmd);
bzero(cmd, sizeof(*cmd));
return cmd;
}
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;
}
static void
ipfw_map_from_cmds_32(struct ip_fw_32 *curr_rule, struct ip_fw_compat_32 *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)) {
ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd;
switch (cmd->opcode) {
case O_PROTO:
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:
{
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:
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:
{
ipfw_insn_u16 *ports = (ipfw_insn_u16 *)cmd;
uint16_t *p = ports->ports;
int i, j;
for (i = F_LEN(cmd) - 1, j = 0; i > 0; i--, j++, p += 2) {
if (p[0] != p[1]) {
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:
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:
{
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:
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:
{
ipfw_insn_u16 *ports = (ipfw_insn_u16 *)cmd;
uint16_t *p = ports->ports;
int i,
j = IP_FW_GETNSRCP_COMPAT(compat_rule);
for (i = F_LEN(cmd) - 1; i > 0; i--, j++, p += 2) {
if (p[0] != p[1]) {
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')) {
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')) {
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')) {
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:
compat_rule->fw_ipopt = (cmd->arg1 & 0xff);
compat_rule->fw_ipnopt = ((cmd->arg1 >> 8) & 0xff);
break;
case O_TCPFLAGS:
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:
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:
{
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;
}
}
}
static void
ipfw_map_from_cmds_64(struct ip_fw_64 *curr_rule, struct ip_fw_compat_64 *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)) {
ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd;
switch (cmd->opcode) {
case O_PROTO:
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:
{
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:
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:
{
ipfw_insn_u16 *ports = (ipfw_insn_u16 *)cmd;
uint16_t *p = ports->ports;
int i, j;
for (i = F_LEN(cmd) - 1, j = 0; i > 0; i--, j++, p += 2) {
if (p[0] != p[1]) {
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:
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:
{
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:
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:
{
ipfw_insn_u16 *ports = (ipfw_insn_u16 *)cmd;
uint16_t *p = ports->ports;
int i,
j = IP_FW_GETNSRCP_COMPAT(compat_rule);
for (i = F_LEN(cmd) - 1; i > 0; i--, j++, p += 2) {
if (p[0] != p[1]) {
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')) {
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')) {
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')) {
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:
compat_rule->fw_ipopt = (cmd->arg1 & 0xff);
compat_rule->fw_ipnopt = ((cmd->arg1 >> 8) & 0xff);
break;
case O_TCPFLAGS:
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:
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:
{
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;
}
}
}
static void
ipfw_map_from_actions_32(struct ip_fw_32 *curr_rule, struct ip_fw_compat_32 *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_map_from_actions_64(struct ip_fw_64 *curr_rule, struct ip_fw_compat_64 *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_32(struct ip_fw_32 *curr_rule, struct ip_fw_compat_32 *rule_vers1)
{
if (!rule_vers1)
return;
bzero(rule_vers1, sizeof(struct ip_fw_compat));
rule_vers1->version = IP_FW_VERSION_1;
rule_vers1->context = CAST_DOWN_EXPLICIT(user32_addr_t,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;
ipfw_map_from_actions_32(curr_rule, rule_vers1);
ipfw_map_from_cmds_32(curr_rule, rule_vers1);
#if FW2_DEBUG_VERBOSE
ipfw_print_vers1_struct_32(rule_vers1);
#endif
}
static void
ipfw_version_latest_to_one_64(struct ip_fw_64 *curr_rule, struct ip_fw_compat_64 *rule_vers1)
{
if (!rule_vers1)
return;
bzero(rule_vers1, sizeof(struct ip_fw_compat));
rule_vers1->version = IP_FW_VERSION_1;
rule_vers1->context = CAST_DOWN_EXPLICIT(__uint64_t, 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;
ipfw_map_from_actions_64(curr_rule, rule_vers1);
ipfw_map_from_cmds_64(curr_rule, rule_vers1);
#if FW2_DEBUG_VERBOSE
ipfw_print_vers1_struct_64(rule_vers1);
#endif
}
static void
ipfw_version_latest_to_zero(struct ip_fw *curr_rule, struct ip_old_fw *rule_vers0, int is64user)
{
if ( is64user ){
struct ip_fw_compat_64 rule_vers1;
ipfw_version_latest_to_one_64((struct ip_fw_64*)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 = CAST_DOWN_EXPLICIT(void*, rule_vers1.pipe_ptr);
rule_vers0->next_rule_ptr = CAST_DOWN_EXPLICIT(void*, rule_vers1.next_rule_ptr);
if (rule_vers1.fw_ipflg && IP_FW_IF_TCPEST_COMPAT) rule_vers0->fw_tcpf |= IP_OLD_FW_TCPF_ESTAB;
}
else {
struct ip_fw_compat_32 rule_vers1;
ipfw_version_latest_to_one_32( (struct ip_fw_32*)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 = CAST_DOWN_EXPLICIT(void*, rule_vers1.pipe_ptr);
rule_vers0->next_rule_ptr = CAST_DOWN_EXPLICIT(void*, 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, int is64user)
{
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, is64user);
break;
}
case IP_FW_VERSION_1:
{
if ( is64user )
ipfw_version_latest_to_one_64((struct ip_fw_64*)curr_rule, (struct ip_fw_compat_64 *)old_rule);
else
ipfw_version_latest_to_one_32((struct ip_fw_32*)curr_rule, (struct ip_fw_compat_32 *)old_rule);
break;
}
case IP_FW_CURRENT_API_VERSION:
break;
default:
break;
}
}
static int
ipfw_check_vers1_struct_32(struct ip_fw_compat_32 *frwl)
{
if ((frwl->fw_flg & ~IP_FW_F_MASK_COMPAT) != 0) {
return (EINVAL);
}
if (frwl->fw_flg == IP_FW_F_CHECK_S_COMPAT) {
return 0 ;
}
if (!(frwl->fw_flg & (IP_FW_F_IN_COMPAT | IP_FW_F_OUT_COMPAT))) {
return (EINVAL);
}
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)) {
return (EINVAL);
}
if ((frwl->fw_flg & IF_FW_F_VIAHACK_COMPAT) == IF_FW_F_VIAHACK_COMPAT) {
;
} else if ((frwl->fw_flg & IP_FW_F_IN_COMPAT)
&& (frwl->fw_flg & IP_FW_F_OIFACE_COMPAT)) {
return (EINVAL);
}
if ((frwl->fw_flg & IP_FW_F_SRNG_COMPAT) && IP_FW_GETNSRCP_COMPAT(frwl) < 2) {
return (EINVAL);
}
if ((frwl->fw_flg & IP_FW_F_DRNG_COMPAT) && IP_FW_GETNDSTP_COMPAT(frwl) < 2) {
return (EINVAL);
}
if (IP_FW_GETNSRCP_COMPAT(frwl) + IP_FW_GETNDSTP_COMPAT(frwl) > IP_FW_MAX_PORTS_COMPAT) {
return (EINVAL);
}
if ((frwl->fw_prot != IPPROTO_TCP) &&
(frwl->fw_prot != IPPROTO_UDP) &&
(IP_FW_GETNSRCP_COMPAT(frwl) || IP_FW_GETNDSTP_COMPAT(frwl))) {
return (EINVAL);
}
if ((frwl->fw_src.s_addr & (~frwl->fw_smsk.s_addr)) ||
(frwl->fw_dst.s_addr & (~frwl->fw_dmsk.s_addr))) {
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) {
return (EINVAL);
}
if (frwl->fw_prot == IPPROTO_TCP &&
frwl->fw_tcpf != frwl->fw_tcpnf) {
return (EINVAL);
}
}
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)) {
return (EINVAL);
}
break;
case IP_FW_F_DIVERT_COMPAT:
case IP_FW_F_TEE_COMPAT:
case IP_FW_F_PIPE_COMPAT:
case IP_FW_F_QUEUE_COMPAT:
if (frwl->fw_divert_port_compat == 0) {
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:
return (EINVAL);
}
return 0;
}
static int
ipfw_check_vers1_struct_64(struct ip_fw_compat_64 *frwl)
{
if ((frwl->fw_flg & ~IP_FW_F_MASK_COMPAT) != 0) {
return (EINVAL);
}
if (frwl->fw_flg == IP_FW_F_CHECK_S_COMPAT) {
return 0 ;
}
if (!(frwl->fw_flg & (IP_FW_F_IN_COMPAT | IP_FW_F_OUT_COMPAT))) {
return (EINVAL);
}
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)) {
return (EINVAL);
}
if ((frwl->fw_flg & IF_FW_F_VIAHACK_COMPAT) == IF_FW_F_VIAHACK_COMPAT) {
;
} else if ((frwl->fw_flg & IP_FW_F_IN_COMPAT)
&& (frwl->fw_flg & IP_FW_F_OIFACE_COMPAT)) {
return (EINVAL);
}
if ((frwl->fw_flg & IP_FW_F_SRNG_COMPAT) && IP_FW_GETNSRCP_COMPAT(frwl) < 2) {
return (EINVAL);
}
if ((frwl->fw_flg & IP_FW_F_DRNG_COMPAT) && IP_FW_GETNDSTP_COMPAT(frwl) < 2) {
return (EINVAL);
}
if (IP_FW_GETNSRCP_COMPAT(frwl) + IP_FW_GETNDSTP_COMPAT(frwl) > IP_FW_MAX_PORTS_COMPAT) {
return (EINVAL);
}
if ((frwl->fw_prot != IPPROTO_TCP) &&
(frwl->fw_prot != IPPROTO_UDP) &&
(IP_FW_GETNSRCP_COMPAT(frwl) || IP_FW_GETNDSTP_COMPAT(frwl))) {
return (EINVAL);
}
if ((frwl->fw_src.s_addr & (~frwl->fw_smsk.s_addr)) ||
(frwl->fw_dst.s_addr & (~frwl->fw_dmsk.s_addr))) {
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) {
return (EINVAL);
}
if (frwl->fw_prot == IPPROTO_TCP &&
frwl->fw_tcpf != frwl->fw_tcpnf) {
return (EINVAL);
}
}
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)) {
return (EINVAL);
}
break;
case IP_FW_F_DIVERT_COMPAT:
case IP_FW_F_TEE_COMPAT:
case IP_FW_F_PIPE_COMPAT:
case IP_FW_F_QUEUE_COMPAT:
if (frwl->fw_divert_port_compat == 0) {
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:
return (EINVAL);
}
return 0;
}
static void
ipfw_convert_to_cmds_32(struct ip_fw *curr_rule, struct ip_fw_compat_32 *compat_rule)
{
int k;
uint32_t actbuf[255], cmdbuf[255];
ipfw_insn *action, *cmd, *src, *dst;
ipfw_insn *have_state = NULL;
if (!compat_rule || !curr_rule || !(curr_rule->cmd)) {
return;
}
if (ipfw_check_vers1_struct_32(compat_rule)) {
return;
}
bzero(actbuf, sizeof(actbuf));
bzero(cmdbuf, sizeof(cmdbuf));
action = (ipfw_insn *)actbuf;
{
u_int flag = compat_rule->fw_flg;
action->len = 1;
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;
}
}
if (action->opcode == O_NOP) {
return;
}
action = next_cmd(action);
}
cmd = (ipfw_insn *)cmdbuf;
if (have_state) {
goto done;
}
{
ipfw_insn *prev = NULL;
u_int flag = compat_rule->fw_flg;
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);
}
if (compat_rule->fw_prot != 0) {
fill_cmd(cmd, O_PROTO, compat_rule->fw_prot);
prev = cmd;
cmd = next_cmd(cmd);
}
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;
}
prev = cmd;
cmd = next_cmd(cmd);
} else {
if (compat_rule->fw_smsk.s_addr != 0) {
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);
} else {
ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd;
if (compat_rule->fw_src.s_addr == 0) {
cmd32->o.len &= ~F_LEN_MASK;
} 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;
}
if (F_LEN(cmd) != 0) {
prev = cmd;
cmd = next_cmd(cmd);
}
}
{
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;
}
prev = cmd;
cmd = next_cmd(cmd);
}
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;
}
prev = cmd;
cmd = next_cmd(cmd);
} else {
if (compat_rule->fw_dmsk.s_addr != 0) {
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);
} else {
ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd;
if (compat_rule->fw_dst.s_addr == 0) {
cmd32->o.len &= ~F_LEN_MASK;
} 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;
}
if (F_LEN(cmd) != 0) {
prev = cmd;
cmd = next_cmd(cmd);
}
}
{
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) {
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;
}
prev = cmd;
cmd = next_cmd(cmd);
}
if (flag & IP_FW_F_UID_COMPAT) {
ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)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) {
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) {
ifcmd->name[0] = '\0';
ifcmd->o.len = 0;
}
else if (compat_rule->fw_flg & IP_FW_F_IIFNAME_COMPAT) {
strncpy(ifcmd->name, ifu.fu_via_if_compat.name, sizeof(ifcmd->name));
ifcmd->p.unit = ifu.fu_via_if_compat.unit;
} else {
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 (prev->opcode == O_IN) {
cmd = prev;
bzero(cmd, sizeof(*cmd));
} else {
cmd->len ^= F_NOT;
fill_cmd(cmd, O_IN, 0);
prev = cmd;
cmd = next_cmd(cmd);
}
}
if (flag & IP_FW_F_OIFACE_COMPAT) {
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) {
ifcmd->name[0] = '\0';
ifcmd->o.len = 0;
}
else if (flag & IP_FW_F_OIFNAME_COMPAT) {
strncpy(ifcmd->name, ifu.fu_via_if_compat.name, sizeof(ifcmd->name));
ifcmd->p.unit = ifu.fu_via_if_compat.unit;
} else {
ifcmd->p.ip = ifu.fu_via_ip;
}
prev = cmd;
cmd = next_cmd(cmd);
}
else if (flag & IP_FW_F_IIFACE_COMPAT) {
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) {
ifcmd->name[0] = '\0';
ifcmd->o.len = 0;
}
else if (flag & IP_FW_F_IIFNAME_COMPAT) {
strncpy(ifcmd->name, ifu.fu_via_if_compat.name, sizeof(ifcmd->name));
ifcmd->p.unit = ifu.fu_via_if_compat.unit;
} else {
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);
}
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);
}
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);
}
}
if (flag & IP_FW_F_ICMPBIT_COMPAT) {
int i;
ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)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);
}
}
done:
dst = curr_rule->cmd;
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;
}
if (have_state && have_state->opcode != O_CHECK_STATE) {
fill_cmd(dst, O_PROBE_STATE, 0);
dst = next_cmd(dst);
}
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;
}
}
if (have_state && have_state->opcode != O_CHECK_STATE) {
k = F_LEN(have_state);
bcopy(have_state, dst, k * sizeof(uint32_t));
dst += k;
}
curr_rule->act_ofs = dst - curr_rule->cmd;
src = (ipfw_insn *)cmdbuf;
if (src->opcode == O_LOG) {
k = F_LEN(src);
bcopy(src, dst, k * sizeof(uint32_t));
dst += k;
}
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 void
ipfw_convert_to_cmds_64(struct ip_fw *curr_rule, struct ip_fw_compat_64 *compat_rule)
{
int k;
uint32_t actbuf[255], cmdbuf[255];
ipfw_insn *action, *cmd, *src, *dst;
ipfw_insn *have_state = NULL;
if (!compat_rule || !curr_rule || !(curr_rule->cmd)) {
return;
}
if (ipfw_check_vers1_struct_64(compat_rule)) {
return;
}
bzero(actbuf, sizeof(actbuf));
bzero(cmdbuf, sizeof(cmdbuf));
action = (ipfw_insn *)actbuf;
{
u_int flag = compat_rule->fw_flg;
action->len = 1;
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;
}
}
if (action->opcode == O_NOP) {
return;
}
action = next_cmd(action);
}
cmd = (ipfw_insn *)cmdbuf;
if (have_state) {
goto done;
}
{
ipfw_insn *prev = NULL;
u_int flag = compat_rule->fw_flg;
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);
}
if (compat_rule->fw_prot != 0) {
fill_cmd(cmd, O_PROTO, compat_rule->fw_prot);
prev = cmd;
cmd = next_cmd(cmd);
}
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;
}
prev = cmd;
cmd = next_cmd(cmd);
} else {
if (compat_rule->fw_smsk.s_addr != 0) {
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);
} else {
ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd;
if (compat_rule->fw_src.s_addr == 0) {
cmd32->o.len &= ~F_LEN_MASK;
} 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;
}
if (F_LEN(cmd) != 0) {
prev = cmd;
cmd = next_cmd(cmd);
}
}
{
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;
}
prev = cmd;
cmd = next_cmd(cmd);
}
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;
}
prev = cmd;
cmd = next_cmd(cmd);
} else {
if (compat_rule->fw_dmsk.s_addr != 0) {
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);
} else {
ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd;
if (compat_rule->fw_dst.s_addr == 0) {
cmd32->o.len &= ~F_LEN_MASK;
} 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;
}
if (F_LEN(cmd) != 0) {
prev = cmd;
cmd = next_cmd(cmd);
}
}
{
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) {
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;
}
prev = cmd;
cmd = next_cmd(cmd);
}
if (flag & IP_FW_F_UID_COMPAT) {
ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)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) {
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) {
ifcmd->name[0] = '\0';
ifcmd->o.len = 0;
}
else if (compat_rule->fw_flg & IP_FW_F_IIFNAME_COMPAT) {
strncpy(ifcmd->name, ifu.fu_via_if_compat.name, sizeof(ifcmd->name));
ifcmd->p.unit = ifu.fu_via_if_compat.unit;
} else {
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 (prev->opcode == O_IN) {
cmd = prev;
bzero(cmd, sizeof(*cmd));
} else {
cmd->len ^= F_NOT;
fill_cmd(cmd, O_IN, 0);
prev = cmd;
cmd = next_cmd(cmd);
}
}
if (flag & IP_FW_F_OIFACE_COMPAT) {
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) {
ifcmd->name[0] = '\0';
ifcmd->o.len = 0;
}
else if (flag & IP_FW_F_OIFNAME_COMPAT) {
strncpy(ifcmd->name, ifu.fu_via_if_compat.name, sizeof(ifcmd->name));
ifcmd->p.unit = ifu.fu_via_if_compat.unit;
} else {
ifcmd->p.ip = ifu.fu_via_ip;
}
prev = cmd;
cmd = next_cmd(cmd);
}
else if (flag & IP_FW_F_IIFACE_COMPAT) {
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) {
ifcmd->name[0] = '\0';
ifcmd->o.len = 0;
}
else if (flag & IP_FW_F_IIFNAME_COMPAT) {
strncpy(ifcmd->name, ifu.fu_via_if_compat.name, sizeof(ifcmd->name));
ifcmd->p.unit = ifu.fu_via_if_compat.unit;
} else {
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);
}
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);
}
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);
}
}
if (flag & IP_FW_F_ICMPBIT_COMPAT) {
int i;
ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)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);
}
}
done:
dst = curr_rule->cmd;
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;
}
if (have_state && have_state->opcode != O_CHECK_STATE) {
fill_cmd(dst, O_PROBE_STATE, 0);
dst = next_cmd(dst);
}
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;
}
}
if (have_state && have_state->opcode != O_CHECK_STATE) {
k = F_LEN(have_state);
bcopy(have_state, dst, k * sizeof(uint32_t));
dst += k;
}
curr_rule->act_ofs = dst - curr_rule->cmd;
src = (ipfw_insn *)cmdbuf;
if (src->opcode == O_LOG) {
k = F_LEN(src);
bcopy(src, dst, k * sizeof(uint32_t));
dst += k;
}
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_32(struct sockopt *sopt, struct ip_fw *curr_rule,
struct ip_fw_compat_32 *rule_vers1)
{
int err = EINVAL;
struct ip_fw_compat_32 *rule_ptr;
struct ip_fw_compat_32 rule;
if (rule_vers1) {
rule_ptr = rule_vers1;
err = 0;
} else {
if (!sopt->sopt_val || sopt->sopt_valsize < sizeof(struct ip_fw_compat_32))
return err;
if ((err = sooptcopyin(sopt, &rule, sizeof(struct ip_fw_compat_32),
sizeof(struct ip_fw_compat_32)))) {
return err;
}
rule_ptr = &rule;
}
ipfw_convert_to_cmds_32(curr_rule, rule_ptr);
curr_rule->version = IP_FW_CURRENT_API_VERSION;
curr_rule->context = CAST_DOWN_EXPLICIT(void*, 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
return err;
}
static int
ipfw_version_one_to_version_two_64(struct sockopt *sopt, struct ip_fw *curr_rule,
struct ip_fw_compat_64 *rule_vers1)
{
int err = EINVAL;
struct ip_fw_compat_64 *rule_ptr;
struct ip_fw_compat_64 rule;
if (rule_vers1) {
rule_ptr = rule_vers1;
err = 0;
} else {
if (!sopt->sopt_val || sopt->sopt_valsize < sizeof(struct ip_fw_compat_64))
return err;
if ((err = sooptcopyin(sopt, &rule, sizeof(struct ip_fw_compat_64),
sizeof(struct ip_fw_compat_64)))) {
return err;
}
rule_ptr = &rule;
}
ipfw_convert_to_cmds_64(curr_rule, rule_ptr);
curr_rule->version = IP_FW_CURRENT_API_VERSION;
curr_rule->context = CAST_DOWN_EXPLICIT( void *, 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
return err;
}
static int
ipfw_version_one_to_latest_32(struct sockopt *sopt, struct ip_fw *curr_rule, struct ip_fw_compat_32 *rule_vers1)
{
int err;
err = ipfw_version_one_to_version_two_32(sopt, curr_rule, rule_vers1);
return err;
}
static int
ipfw_version_one_to_latest_64(struct sockopt *sopt, struct ip_fw *curr_rule, struct ip_fw_compat_64 *rule_vers1)
{
int err;
err = ipfw_version_one_to_version_two_64(sopt, curr_rule, rule_vers1);
return err;
}
#if 0
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;
}
#endif
int
ipfw_convert_to_latest(struct sockopt *sopt, struct ip_fw *curr_rule, int api_version, int is64user)
{
int err = 0;
switch (api_version) {
case IP_FW_VERSION_0:
err = EOPNOTSUPP;
break;
case IP_FW_VERSION_1:
if ( is64user )
err = ipfw_version_one_to_latest_64(sopt, curr_rule, NULL);
else
err = ipfw_version_one_to_latest_32(sopt, curr_rule, NULL);
break;
case IP_FW_CURRENT_API_VERSION:
break;
default:
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;
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) {
struct ip_fw_64 rule;
size_t copyinsize;
if (proc_is64bit(sopt->sopt_p))
copyinsize = sizeof(struct ip_fw_64);
else
copyinsize = sizeof(struct ip_fw_32);
if (!sopt->sopt_val || sopt->sopt_valsize < copyinsize)
return EINVAL;
if ((err = sooptcopyin(sopt, &rule, copyinsize, copyinsize))) {
return err;
}
vers = rule.version;
}
if (command) {
*command = cmd;
}
if (api_version) {
*api_version = vers;
}
return err;
}