Implement the last 2-3 missing instructions for ipfw,

now it should support all the instructions of the old ipfw.

Fix some bugs in the user interface, /sbin/ipfw.

Please check this code against your rulesets, so i can fix the
remaining bugs (if any, i think they will be mostly in /sbin/ipfw).

Once we have done a bit of testing, this code is ready to be MFC'ed,
together with a bunch of other changes (glue to ipfw, and also the
removal of some global variables) which have been in -current for
a couple of weeks now.

MFC after: 7 days
This commit is contained in:
Luigi Rizzo 2002-07-05 22:43:06 +00:00
parent 17b9cc4941
commit 5e43aef891
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=99475
3 changed files with 219 additions and 160 deletions

View File

@ -116,7 +116,6 @@ static struct _s_x f_ipopts[] = {
{ NULL, 0 }
};
#if 0 /* XXX not used yet */
static struct _s_x f_iptos[] = {
{ "lowdelay", IPTOS_LOWDELAY},
{ "throughput", IPTOS_THROUGHPUT},
@ -127,7 +126,6 @@ static struct _s_x f_iptos[] = {
{ "ip tos option", 0},
{ NULL, 0 }
};
#endif
static struct _s_x limit_masks[] = {
{"all", DYN_SRC_ADDR|DYN_SRC_PORT|DYN_DST_ADDR|DYN_DST_PORT},
@ -254,6 +252,7 @@ struct _s_x dummynet_params[] = {
{ "bw", TOK_BW },
{ "bandwidth", TOK_BW },
{ "delay", TOK_DELAY },
{ "pipe", TOK_PIPE },
{ "queue", TOK_QUEUE },
{ "dummynet-params", TOK_NULL },
{ NULL, 0 }
@ -276,6 +275,7 @@ struct _s_x rule_actions[] = {
{ "drop", TOK_DENY },
{ "reject", TOK_REJECT },
{ "reset", TOK_RESET },
{ "unreach", TOK_UNREACH },
{ "check-state", TOK_CHECKSTATE },
{ NULL, TOK_NULL },
{ NULL, 0 }
@ -496,7 +496,6 @@ fill_newports(ipfw_insn_u16 *cmd, char *av, int proto)
return i;
}
#if 0 /* XXX not used yet */
static struct _s_x icmpcodes[] = {
{ "net", ICMP_UNREACH_NET },
{ "host", ICMP_UNREACH_HOST },
@ -533,16 +532,15 @@ fill_reject_code(u_short *codep, char *str)
}
static void
print_reject_code(u_int32_t code)
print_reject_code(u_int16_t code)
{
char *s = match_value(icmpcodes, code);
if (s != NULL)
printf("%s", s);
printf("unreach %s", s);
else
printf("%u", code);
printf("unreach %u", code);
}
#endif /* XXX not used yet */
/*
* Returns the number of bits set (from left) in a contiguous bitmask,
@ -678,6 +676,44 @@ print_mac(u_char *addr, u_char *mask)
}
}
static void
fill_icmptypes(ipfw_insn_u32 *cmd, char *av)
{
u_int8_t type;
cmd->d[0] = 0;
while (*av) {
if (*av == ',')
av++;
type = strtoul(av, &av, 0);
if (*av != ',' && *av != '\0')
errx(EX_DATAERR, "invalid ICMP type");
if (type > 31)
errx(EX_DATAERR, "ICMP type out of range");
cmd->d[0] |= 1 << type;
}
cmd->o.opcode = O_ICMPTYPE;
cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
}
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 = ',';
}
}
/*
* show_ipfw() prints the body of an ipfw rule.
@ -763,6 +799,15 @@ show_ipfw(struct ip_fw *rule)
printf("deny");
break;
case O_REJECT:
if (cmd->arg1 == ICMP_REJECT_RST)
printf("reset");
else if (cmd->arg1 == ICMP_UNREACH_HOST)
printf("reject");
else
print_reject_code(cmd->arg1);
break;
case O_SKIPTO:
printf("skipto %u", cmd->arg1);
break;
@ -813,7 +858,8 @@ show_ipfw(struct ip_fw *rule)
*/
for (l = rule->act_ofs, cmd = rule->cmd ;
l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd; /* useful alias */
/* useful alias */
ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd;
switch(cmd->opcode) {
case O_PROBE_STATE:
@ -918,11 +964,13 @@ show_ipfw(struct ip_fw *rule)
else if (cmd->opcode == O_VIA)
s = "via";
if (cmdif->name[0] == '\0')
printf(" %s %s", s, inet_ntoa(cmdif->p.ip));
printf(" %s %s", s,
inet_ntoa(cmdif->p.ip));
else if (cmdif->p.unit == -1)
printf(" %s %s*", s, cmdif->name);
else
printf(" %s %s%d", s, cmdif->name, cmdif->p.unit);
printf(" %s %s%d", s, cmdif->name,
cmdif->p.unit);
}
break;
@ -938,6 +986,10 @@ show_ipfw(struct ip_fw *rule)
printf(" ipver %u", cmd->arg1 );
break;
case O_IPPRECEDENCE:
printf(" ipprecedence %u", (cmd->arg1) >> 5 );
break;
case O_IPLEN:
printf(" iplen %u", cmd->arg1 );
break;
@ -946,6 +998,14 @@ show_ipfw(struct ip_fw *rule)
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;
@ -1029,45 +1089,6 @@ show_ipfw(struct ip_fw *rule)
}
show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP|HAVE_DSTIP);
#if 0 /* old stuff */
switch (chain->fw_flg & IP_FW_F_COMMAND) {
case IP_FW_F_REJECT:
if (chain->fw_reject_code == IP_FW_REJECT_RST)
printf("reset");
else {
printf("unreach ");
print_reject_code(chain->fw_reject_code);
}
break;
}
do_options:
if (chain->fw_ipflg & IP_FW_IF_IPOPT)
print_flags("ipopt", chain->fw_ipopt, chain->fw_ipnopt,
f_ipopts);
if (chain->fw_ipflg & IP_FW_IF_IPPRE)
printf(" ipprecedence %u", (chain->fw_iptos & 0xe0) >> 5);
if (chain->fw_ipflg & IP_FW_IF_IPTOS)
print_flags("iptos", chain->fw_iptos, chain->fw_ipntos,
f_iptos);
if (chain->fw_flg & IP_FW_F_ICMPBIT) {
int i, first = 1;
unsigned j;
printf(" icmptype");
for (i = 0; i < IP_FW_ICMPTYPES_DIM; ++i)
for (j = 0; j < sizeof(unsigned) * 8; ++j)
if (chain->fw_uar.fw_icmptypes[i] & (1 << j)) {
printf("%c%d", first ? ' ' : ',',
i * sizeof(unsigned) * 8 + j);
first = 0;
}
}
#endif /* XXX old stuff */
printf("\n");
}
@ -1620,31 +1641,6 @@ fill_flags(ipfw_insn *cmd, enum ipfw_opcodes opcode,
cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8);
}
#if 0 /* XXX todo */
static void
fill_icmptypes(unsigned *types, char **vp, u_int *fw_flg)
{
unsigned long icmptype;
char *c = *vp;
while (*c) {
if (*c == ',')
++c;
icmptype = strtoul(c, &c, 0);
if (*c != ',' && *c != '\0')
errx(EX_DATAERR, "invalid ICMP type");
if (icmptype >= IP_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8)
errx(EX_DATAERR, "ICMP type out of range");
types[icmptype / (sizeof(unsigned) * 8)] |=
1 << (icmptype % (sizeof(unsigned) * 8));
*fw_flg |= IP_FW_F_ICMPBIT;
}
}
#endif /* XXX todo */
static void
delete(int ac, char *av[])
@ -1745,7 +1741,7 @@ config_pipe(int ac, char **av)
else
pipe.fs.fs_nr = i;
}
while (ac > 1) {
while (ac > 0) {
double d;
int tok = match_token(dummynet_params, *av);
ac--; av++;
@ -2231,6 +2227,24 @@ add(int ac, char *av[])
case TOK_DENY:
action->opcode = O_DENY;
action->arg1 = 0;
break;
case TOK_REJECT:
action->opcode = O_REJECT;
action->arg1 = ICMP_UNREACH_HOST;
break;
case TOK_RESET:
action->opcode = O_REJECT;
action->arg1 = ICMP_REJECT_RST;
break;
case TOK_UNREACH:
action->opcode = O_REJECT;
NEED1("missing reject code");
fill_reject_code(&action->arg1, *av);
ac--; av++;
break;
case TOK_COUNT:
@ -2305,19 +2319,6 @@ add(int ac, char *av[])
}
action = next_cmd(action);
#if 0
} else if (!strncmp(*av, "reject", strlen(*av))) {
rule.fw_flg |= IP_FW_F_REJECT; av++; ac--;
rule.fw_reject_code = ICMP_UNREACH_HOST;
} else if (!strncmp(*av, "reset", strlen(*av))) {
rule.fw_flg |= IP_FW_F_REJECT; av++; ac--;
rule.fw_reject_code = ICMP_REJECT_RST; /* check TCP later */
} else if (!strncmp(*av, "unreach", strlen(*av))) {
rule.fw_flg |= IP_FW_F_REJECT; av++; ac--;
fill_reject_code(&rule.fw_reject_code, *av); av++; ac--;
}
#endif /* XXX other actions */
/*
* [log [logamount N]] -- log, optional
*
@ -2568,6 +2569,12 @@ add(int ac, char *av[])
cmd->opcode = O_VIA;
break;
case TOK_ICMPTYPES:
NEED1("icmptypes requires list of types");
fill_icmptypes((ipfw_insn_u32 *)cmd, *av);
av++; ac--;
break;
case TOK_IPTTL:
NEED1("ipttl requires TTL");
fill_cmd(cmd, O_IPTTL, 0, strtoul(*av, NULL, 0));
@ -2592,12 +2599,25 @@ add(int ac, char *av[])
ac--; av++;
break;
case TOK_IPPRECEDENCE:
NEED1("ipprecedence requires value");
fill_cmd(cmd, O_IPPRECEDENCE, 0,
(strtoul(*av, NULL, 0) & 7) << 5);
ac--; av++;
break;
case TOK_IPOPTS:
NEED1("missing argument for ipoptions");
fill_flags(cmd, O_IPOPTS, f_ipopts, *av);
ac--; av++;
break;
case TOK_IPTOS:
NEED1("missing argument for iptos");
fill_flags(cmd, O_IPOPTS, f_iptos, *av);
ac--; av++;
break;
case TOK_UID:
NEED1("uid requires argument");
{
@ -2714,43 +2734,7 @@ add(int ac, char *av[])
cmd = next_cmd(cmd);
}
}
#if 0 /* XXX todo */
do_options:
while (ac) {
} else if (!strncmp(*av, "ipprecedence", strlen(*av))) {
u_long ippre;
char *c;
av++; ac--;
NEED1("missing argument for ``ipprecedence''");
ippre = strtoul(*av, &c, 0);
if (*c != '\0')
errx(EX_DATAERR, "argument to ipprecedence"
" must be numeric");
if (ippre > 7)
errx(EX_DATAERR, "argument to ipprecedence"
" out of range");
rule.fw_ipflg |= IP_FW_IF_IPPRE;
rule.fw_iptos |= (u_short)(ippre << 5);
av++; ac--;
} else if (!strncmp(*av, "iptos", strlen(*av))) {
av++; ac--;
NEED1("missing argument for ``iptos''");
rule.fw_ipflg |= IP_FW_IF_IPTOS;
fill_flags(&rule.fw_iptos, &rule.fw_ipntos,
f_iptos, av);
av++; ac--;
} else if (rule.fw_prot == IPPROTO_ICMP) {
if (!strncmp(*av, "icmptypes", strlen(*av))) {
av++; ac--;
NEED1("missing argument for ``icmptypes''");
fill_icmptypes(rule.fw_uar.fw_icmptypes,
av, &rule.fw_flg);
av++; ac--;
}
}
}
#endif /* XXX todo */
done:
/*
* Now copy stuff into the rule.

View File

@ -76,6 +76,7 @@ enum ipfw_opcodes { /* arguments (4 byte each) */
O_IPPRE, /* arg1 = id */
O_IPTOS, /* arg1 = id */
O_IPPRECEDENCE, /* arg1 = precedence << 5 */
O_IPTTL, /* arg1 = TTL */
O_IPVER, /* arg1 = version */

View File

@ -73,6 +73,8 @@
#include <netinet/if_ether.h> /* XXX for ETHERTYPE_IP */
#include <machine/in_cksum.h> /* XXX for in_cksum */
static int fw_verbose = 0;
static int verbose_limit = 0;
@ -345,16 +347,13 @@ tcpopts_match(struct ip *ip, ipfw_insn *cmd)
return (flags_match(cmd, bits));
}
/*
* XXX done
*/
static int
iface_match(struct ifnet *ifp, ipfw_insn_if *cmd)
{
if (ifp == NULL) /* no iface with this packet, match fails */
return 0;
/* Check by name or by IP address */
if (cmd->name[0] != '\0') { /* XXX by name */
if (cmd->name[0] != '\0') { /* match by name */
/* Check unit number (-1 is wildcard) */
if (cmd->p.unit != -1 && cmd->p.unit != ifp->if_unit)
return(0);
@ -419,10 +418,17 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ether_header *eh,
case O_DENY:
action = "Deny";
break;
case O_REJECT:
action = (cmd->arg1==ICMP_REJECT_RST) ?
"Reset" : "Unreach";
if (cmd->arg1==ICMP_REJECT_RST)
action = "Reset";
else if (cmd->arg1==ICMP_UNREACH_HOST)
action = "Reject";
else
snprintf(SNPARGS(action2, 0), "Unreach %d",
cmd->arg1);
break;
case O_ACCEPT:
action = "Accept";
break;
@ -568,7 +574,7 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ether_header *eh,
/*
* IMPORTANT: the hash function for dynamic rules must be commutative
* in * source and destination (ip,port), because rules are bidirectional
* in source and destination (ip,port), because rules are bidirectional
* and we want to find both in the same bucket.
*/
static __inline int
@ -979,23 +985,94 @@ install_state(struct ip_fw *rule, ipfw_insn_limit *cmd,
return 0;
}
#if 0
static void
send_pkt(struct ip_fw_args *args, u_int32_t seq, u_int32_t ack, int flags)
{
struct mbuf *m;
struct ip *ip = mtod(args->m, struct ip *);
struct tcphdr *tcp;
struct route sro; /* fake route */
MGETHDR(m, M_DONTWAIT, MT_HEADER);
if (m == 0)
return;
m->m_pkthdr.rcvif = (struct ifnet *)0;
m->m_pkthdr.len = m->m_len = sizeof(struct ip) + sizeof(struct tcphdr);
m->m_data += max_linkhdr;
ip->ip_v = 4;
ip->ip_hl = 5;
ip->ip_tos = 0;
ip->ip_len = 0; /* set later */
#ifdef RANDOM_IP_ID
ip->ip_id = ip_randomid();
#else
ip->ip_id = htons(ip_id++);
#endif
ip->ip_off = 0;
ip->ip_ttl = 0; /* set later */
ip->ip_p = IPPROTO_TCP;
ip->ip_sum = 0;
ip->ip_src.s_addr = args->f_id.dst_ip;
ip->ip_dst.s_addr = args->f_id.src_ip;
tcp = L3HDR(struct tcphdr, ip);
tcp->th_sport = htons(args->f_id.dst_port); /* swap ports */
tcp->th_dport = htons(args->f_id.src_port);
tcp->th_off = 4;
tcp->th_x2 = 0;
tcp->th_win = 0;
tcp->th_sum = 0;
tcp->th_urp = 0;
if (flags & TH_ACK) {
tcp->th_seq = htonl(ack);
tcp->th_ack = htonl(0);
tcp->th_flags = TH_RST;
} else {
if (flags & TH_SYN)
seq++;
tcp->th_seq = htonl(0);
tcp->th_ack = htonl(seq);
tcp->th_flags = TH_RST | TH_ACK;
}
/* compute TCP checksum... */
tcp->th_sum = in_cksum(m, m->m_pkthdr.len);
ip->ip_ttl = ip_defttl;
ip->ip_len = m->m_pkthdr.len;
bzero (&sro, sizeof (sro));
ip_rtaddr(ip->ip_dst, &sro);
ip_output(m, NULL, &sro, 0, NULL);
if (sro.ro_rt)
RTFREE(sro.ro_rt);
}
#endif
/*
* sends a reject message, consuming the mbuf passed as an argument.
*/
static void
send_reject(struct mbuf *m, int code, int offset, int ip_len)
send_reject(struct ip_fw_args *args, int code, int offset, int ip_len)
{
if (code != ICMP_REJECT_RST) /* Send an ICMP unreach */
icmp_error(m, ICMP_UNREACH, code, 0L, 0);
else {
icmp_error(args->m, ICMP_UNREACH, code, 0L, 0);
else if (offset == 0 && args->f_id.proto == IPPROTO_TCP) {
#if 0
struct tcphdr *const tcp =
L3HDR(struct tcphdr, mtod(args->m, struct ip *));
if ( (tcp->th_flags & TH_RST) == 0)
send_pkt(args, tcp->th_seq, tcp->th_ack, tcp->th_flags);
m_freem(args->m);
#else
/* XXX warning, this code writes into the mbuf */
struct ip *ip = mtod(m, struct ip *);
struct ip *ip = mtod(args->m, struct ip *);
struct tcphdr *const tcp = L3HDR(struct tcphdr, ip);
struct tcpiphdr ti, *const tip = (struct tcpiphdr *) ip;
int hlen = ip->ip_hl << 2;
if (offset != 0 || (tcp->th_flags & TH_RST)) {
m_freem(m); /* free the mbuf */
if (tcp->th_flags & TH_RST) {
m_freem(args->m); /* free the mbuf */
args->m = NULL;
return;
}
ti.ti_i = *((struct ipovly *) ip);
@ -1005,15 +1082,18 @@ send_reject(struct mbuf *m, int code, int offset, int ip_len)
tip->ti_ack = ntohl(tip->ti_ack);
tip->ti_len = ip_len - hlen - (tip->ti_off << 2);
if (tcp->th_flags & TH_ACK) {
tcp_respond(NULL, (void *)ip, tcp, m,
tcp_respond(NULL, (void *)ip, tcp, args->m,
0, tcp->th_ack, TH_RST);
} else {
if (tcp->th_flags & TH_SYN)
tip->ti_len++;
tcp_respond(NULL, (void *)ip, tcp, m,
tcp_respond(NULL, (void *)ip, tcp, args->m,
tip->ti_seq + tip->ti_len, 0, TH_RST|TH_ACK);
}
}
#endif
} else
m_freem(args->m);
args->m = NULL;
}
/**
@ -1579,6 +1659,12 @@ ipfw_chk(struct ip_fw_args *args)
goto cmd_fail;
goto cmd_match;
case O_IPPRECEDENCE:
if (hlen == 0 ||
(cmd->arg1 != (ip->ip_tos & 0xe0)) )
goto cmd_fail;
goto cmd_match;
case O_IPTOS:
if (hlen == 0 ||
!flags_match(cmd, ip->ip_tos))
@ -1635,7 +1721,7 @@ ipfw_chk(struct ip_fw_args *args)
ipfw_log(f, hlen, args->eh, m, oif);
goto cmd_match;
case O_PROB: /* XXX check */
case O_PROB:
if (random() < ((ipfw_insn_u32 *)cmd)->d[0] )
goto cmd_match;
goto cmd_fail;
@ -1717,11 +1803,12 @@ ipfw_chk(struct ip_fw_args *args)
*/
if (hlen > 0 &&
(proto != IPPROTO_ICMP ||
is_icmp_query(ip)) &&
is_icmp_query(ip)) &&
!(m->m_flags & (M_BCAST|M_MCAST)) &&
!IN_MULTICAST(dst_ip.s_addr)) {
send_reject(m,cmd->arg1,offset,ip_len);
args->m = m = NULL;
send_reject(args, cmd->arg1,
offset,ip_len);
m = args->m;
}
goto deny;
@ -1796,20 +1883,6 @@ next_rule:; /* try next rule */
return(IP_FW_PORT_DENY_FLAG);
}
#if 0 /* XXX old instructions not implemented yet XXX */
bogusfrag:
if (fw_verbose) {
if (*m != NULL)
ipfw_report(NULL, ip, ip_off, ip_len, (*m)->m_pkthdr.rcvif, oif);
}
return(IP_FW_PORT_DENY_FLAG);
if (f->fw_ipflg & IP_FW_IF_IPPRE &&
(f->fw_iptos & 0xe0) != (ip->ip_tos & 0xe0))
continue;
#endif /* XXX old instructions not implemented yet */
/*
* When a rule is added/deleted, clear the next_rule pointers in all rules.
* These will be reconstructed on the fly as packets are matched.
@ -2119,6 +2192,7 @@ check_ipfw_struct(struct ip_fw *rule, int size)
case O_IPID:
case O_IPPRE:
case O_IPTOS:
case O_IPPRECEDENCE:
case O_IPTTL:
case O_IPVER:
case O_TCPWIN:
@ -2203,12 +2277,12 @@ check_ipfw_struct(struct ip_fw *rule, int size)
goto bad_size;
goto check_action;
case O_FORWARD_IP: /* XXX no! */
case O_FORWARD_IP:
if (cmdlen != F_INSN_SIZE(ipfw_insn_sa))
goto bad_size;
goto check_action;
case O_FORWARD_MAC: /* XXX no! */
case O_FORWARD_MAC: /* XXX not implemented yet */
case O_CHECK_STATE:
case O_COUNT:
case O_ACCEPT: