The new ipfw code.

This code makes use of variable-size kernel representation of rules
(exactly the same concept of BPF instructions, as used in the BSDI's
firewall), which makes firewall operation a lot faster, and the
code more readable and easier to extend and debug.

The interface with the rest of the system is unchanged, as witnessed
by this commit. The only extra kernel files that I am touching
are if_fw.h and ip_dummynet.c, which is quite tied to ipfw. In
userland I only had to touch those programs which manipulate the
internal representation of firewall rules).

The code is almost entirely new (and I believe I have written the
vast majority of those sections which were taken from the former
ip_fw.c), so rather than modifying the old ip_fw.c I decided to
create a new file, sys/netinet/ip_fw2.c .  Same for the user
interface, which is in sbin/ipfw/ipfw2.c (it still compiles to
/sbin/ipfw).  The old files are still there, and will be removed
in due time.

I have not renamed the header file because it would have required
touching a one-line change to a number of kernel files.

In terms of user interface, the new "ipfw" is supposed to accepts
the old syntax for ipfw rules (and produce the same output with
"ipfw show". Only a couple of the old options (out of some 30 of
them) has not been implemented, but they will be soon.

On the other hand, the new code has some very powerful extensions.
First, you can put "or" connectives between match fields (and soon
also between options), and write things like

ipfw add allow ip from { 1.2.3.4/27 or 5.6.7.8/30 } 10-23,25,1024-3000 to any

This should make rulesets slightly more compact (and lines longer!),
by condensing 2 or more of the old rules into single ones.

Also, as an example of how easy the rules can be extended, I have
implemented an 'address set' match pattern, where you can specify
an IP address in a format like this:

        10.20.30.0/26{18,44,33,22,9}

which will match the set of hosts listed in braces belonging to the
subnet 10.20.30.0/26 . The match is done using a bitmap, so it is
essentially a constant time operation requiring a handful of CPU
instructions (and a very small amount of memmory -- for a full /24
subnet, the instruction only consumes 40 bytes).

Again, in this commit I have focused on functionality and tried
to minimize changes to the other parts of the system. Some performance
improvement can be achieved with minor changes to the interface of
ip_fw_chk_t. This will be done later when this code is settled.

The code is meant to compile unmodified on RELENG_4 (once the
PACKET_TAG_* changes have been merged), for this reason
you will see #ifdef __FreeBSD_version in a couple of places.
This should minimize errors when (hopefully soon) it will be time
to do the MFC.
This commit is contained in:
Luigi Rizzo 2002-06-27 23:02:18 +00:00
parent c126c9fe5a
commit 9758b77ff1
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=98943
8 changed files with 6247 additions and 274 deletions

View File

@ -2641,6 +2641,82 @@ PacketAliasCheckNewLink(void)
#include <string.h>
#include <err.h>
#define NEW_IPFW 1 /* use new ipfw code */
#ifdef NEW_IPFW
/*
* A function to fill simple commands of size 1.
* Existing flags are preserved.
*/
static void
fill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, u_int16_t arg)
{
cmd->opcode = opcode;
cmd->len = ((cmd->len | flags) & (F_NOT | F_OR)) | 1;
cmd->arg1 = arg;
}
/*
* 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.
*/
static ipfw_insn *
next_cmd(ipfw_insn *cmd)
{
cmd += F_LEN(cmd);
bzero(cmd, sizeof(*cmd));
return cmd;
}
static void
fill_ip(ipfw_insn_ip *cmd, enum ipfw_opcodes opcode, u_int32_t addr)
{
cmd->o.opcode = opcode;
cmd->o.len = F_INSN_SIZE(ipfw_insn_u32);
cmd->addr.s_addr = addr;
}
static void
fill_one_port(ipfw_insn_u16 *cmd, enum ipfw_opcodes opcode, u_int16_t port)
{
cmd->o.opcode = opcode;
cmd->o.len = F_INSN_SIZE(ipfw_insn_u16);
cmd->ports[0] = cmd->ports[1] = port;
}
static int
fill_rule(void *buf, int bufsize, int rulenum,
enum ipfw_opcodes action, int proto,
struct in_addr sa, u_int16_t sp, struct in_addr da, u_int32_t dp)
{
struct ip_fw *rule = (struct ip_fw *)buf;
ipfw_insn *cmd = (ipfw_insn *)rule->cmd;
bzero(buf, bufsize);
rule->rulenum = rulenum;
fill_cmd(cmd, O_PROTO, 0, proto);
cmd = next_cmd(cmd);
fill_ip((ipfw_insn_ip *)cmd, O_IP_SRC, sa.s_addr);
cmd = next_cmd(cmd);
fill_one_port((ipfw_insn_u16 *)cmd, O_IP_SRCPORT, sp);
cmd = next_cmd(cmd);
fill_ip((ipfw_insn_ip *)cmd, O_IP_DST, da.s_addr);
cmd = next_cmd(cmd);
fill_one_port((ipfw_insn_u16 *)cmd, O_IP_DSTPORT, dp);
cmd = next_cmd(cmd);
fill_cmd(cmd, O_ACCEPT, 0, 0);
cmd = next_cmd(cmd);
return ((void *)cmd - buf);
}
#endif /* NEW_IPFW */
static void ClearAllFWHoles(void);
static int fireWallBaseNum; /* The first firewall entry free for our use */
@ -2724,6 +2800,35 @@ PunchFWHole(struct alias_link *link) {
/* Start next search at next position */
fireWallActiveNum = fwhole+1;
#ifdef NEW_IPFW
if (GetOriginalPort(link) != 0 && GetDestPort(link) != 0) {
/*
* generate two rules of the form
*
* add fwhole accept tcp from OAddr OPort to DAddr DPort
* add fwhole accept tcp from DAddr DPort to OAddr OPort
*/
u_int32_t rulebuf[255];
int i;
i = fill_rule(rulebuf, sizeof(rulebuf), fwhole,
O_ACCEPT, IPPROTO_TCP,
GetOriginalAddress(link), ntohs(GetOriginalPort(link)),
GetDestAddress(link), ntohs(GetDestPort(link)) );
r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, rulebuf, i);
if (r)
err(1, "alias punch inbound(1) setsockopt(IP_FW_ADD)");
i = fill_rule(rulebuf, sizeof(rulebuf), fwhole,
O_ACCEPT, IPPROTO_TCP,
GetDestAddress(link), ntohs(GetDestPort(link)),
GetOriginalAddress(link), ntohs(GetOriginalPort(link)) );
r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, rulebuf, i);
if (r)
err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)");
}
#else /* !NEW_IPFW old code to generate ipfw rule */
/* Build generic part of the two rules */
rule.fw_number = fwhole;
IP_FW_SETNSRCP(&rule, 1); /* Number of source ports. */
@ -2759,6 +2864,7 @@ PunchFWHole(struct alias_link *link) {
err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)");
#endif
}
#endif /* !NEW_IPFW */
/* Indicate hole applied */
link->data.tcp->fwhole = fwhole;
fw_setfield(fireWallField, fwhole);
@ -2770,6 +2876,10 @@ static void
ClearFWHole(struct alias_link *link) {
if (link->link_type == LINK_TCP) {
int fwhole = link->data.tcp->fwhole; /* Where is the firewall hole? */
#ifdef NEW_IPFW
while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &fwhole, sizeof fwhole))
;
#else /* !NEW_IPFW */
struct ip_fw rule;
if (fwhole < 0)
@ -2779,7 +2889,9 @@ ClearFWHole(struct alias_link *link) {
rule.fw_number = fwhole;
while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule))
;
#endif /* !NEW_IPFW */
fw_clrfield(fireWallField, fwhole);
link->data.tcp->fwhole = -1;
}
}
@ -2795,9 +2907,15 @@ ClearAllFWHoles(void) {
memset(&rule, 0, sizeof rule);
for (i = fireWallBaseNum; i < fireWallBaseNum + fireWallNumNums; i++) {
#ifdef NEW_IPFW
int r = i;
while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &r, sizeof r))
;
#else /* !NEW_IPFW */
rule.fw_number = i;
while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule))
;
#endif /* NEW_IPFW */
}
memset(fireWallField, 0, fireWallNumNums);
}

View File

@ -1,7 +1,8 @@
# $FreeBSD$
PROG= ipfw
WARNS= 0
SRCS= ipfw2.c
#WARNS= 0
MAN= ipfw.8
.include <bsd.prog.mk>

3178
sbin/ipfw/ipfw2.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1164,7 +1164,7 @@ netinet/ip_ecn.c optional inet6
netinet/ip_encap.c optional inet
netinet/ip_encap.c optional inet6
netinet/ip_flow.c optional inet
netinet/ip_fw.c optional ipfirewall
netinet/ip_fw2.c optional ipfirewall
netinet/ip_icmp.c optional inet
netinet/ip_input.c optional inet
netinet/ip_mroute.c optional inet

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998-2001 Luigi Rizzo, Universita` di Pisa
* Copyright (c) 1998-2002 Luigi Rizzo, Universita` di Pisa
* Portions Copyright (c) 2000 Akamba Corp.
* All rights reserved
*
@ -61,7 +61,6 @@
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/queue.h> /* XXX */
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/proc.h>
@ -166,12 +165,6 @@ static void dn_rule_delete(void *);
int if_tx_rdy(struct ifnet *ifp);
/*
* ip_fw_chain_head is used when deleting a pipe, because ipfw rules can
* hold references to the pipe.
*/
extern LIST_HEAD (ip_fw_head, ip_fw) ip_fw_chain_head;
static void
rt_unref(struct rtentry *rt)
{
@ -1023,9 +1016,13 @@ static __inline
struct dn_flow_set *
locate_flowset(int pipe_nr, struct ip_fw *rule)
{
struct dn_flow_set *fs = NULL ;
ipfw_insn_pipe *cmd = (ipfw_insn_pipe *)(rule->cmd + rule->act_ofs);
struct dn_flow_set *fs = (struct dn_flow_set *)(cmd->pipe_ptr);
if ( (rule->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_QUEUE )
if (fs != NULL)
return fs;
if ( cmd->o.opcode == O_QUEUE )
for (fs=all_flow_sets; fs && fs->fs_nr != pipe_nr; fs=fs->next)
;
else {
@ -1035,8 +1032,7 @@ locate_flowset(int pipe_nr, struct ip_fw *rule)
if (p1 != NULL)
fs = &(p1->fs) ;
}
if (fs != NULL)
rule->pipe_ptr = fs ; /* record for the future */
(struct dn_flow_set *)(cmd->pipe_ptr) = fs; /* record for the future */
return fs ;
}
@ -1065,16 +1061,18 @@ dummynet_io(struct mbuf *m, int pipe_nr, int dir, struct ip_fw_args *fwa)
u_int64_t len = m->m_pkthdr.len ;
struct dn_flow_queue *q = NULL ;
int s ;
int action = fwa->rule->cmd[fwa->rule->act_ofs].opcode;
s = splimp();
pipe_nr &= 0xffff ;
if ( (fs = fwa->rule->pipe_ptr) == NULL ) {
fs = locate_flowset(pipe_nr, fwa->rule);
if (fs == NULL)
goto dropit ; /* this queue/pipe does not exist! */
}
/*
* this is a dummynet rule, so we expect a O_PIPE or O_QUEUE rule
*/
fs = locate_flowset(pipe_nr, fwa->rule);
if (fs == NULL)
goto dropit ; /* this queue/pipe does not exist! */
pipe = fs->pipe ;
if (pipe == NULL) { /* must be a queue, try find a matching pipe */
for (pipe = all_pipes; pipe && pipe->pipe_nr != fs->parent_nr;
@ -1152,7 +1150,7 @@ dummynet_io(struct mbuf *m, int pipe_nr, int dir, struct ip_fw_args *fwa)
* to schedule it. This involves different actions for fixed-rate or
* WF2Q queues.
*/
if ( (fwa->rule->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_PIPE ) {
if ( action == O_PIPE ) {
/*
* Fixed-rate queue: just insert into the ready_heap.
*/
@ -1302,15 +1300,13 @@ static void
dummynet_flush()
{
struct dn_pipe *curr_p, *p ;
struct ip_fw *rule ;
struct dn_flow_set *fs, *curr_fs;
int s ;
s = splimp() ;
/* remove all references to pipes ...*/
LIST_FOREACH(rule, &ip_fw_chain_head, next)
rule->pipe_ptr = NULL ;
flush_pipe_ptrs(NULL);
/* prevent future matches... */
p = all_pipes ;
all_pipes = NULL ;
@ -1375,8 +1371,8 @@ dn_rule_delete(void *r)
fs = &(p->fs) ;
dn_rule_delete_fs(fs, r);
for (pkt = p->head ; pkt ; pkt = DN_NEXT(pkt) )
if (pkt->rule == r)
pkt->rule = ip_fw_default_rule ;
if (pkt->hdr.mh_data == r)
pkt->hdr.mh_data = (void *)ip_fw_default_rule ;
}
}
@ -1663,7 +1659,6 @@ static int
delete_pipe(struct dn_pipe *p)
{
int s ;
struct ip_fw *rule ;
if (p->pipe_nr == 0 && p->fs.fs_nr == 0)
return EINVAL ;
@ -1687,9 +1682,7 @@ delete_pipe(struct dn_pipe *p)
else
a->next = b->next ;
/* remove references to this pipe from the ip_fw rules. */
LIST_FOREACH(rule, &ip_fw_chain_head, next)
if (rule->pipe_ptr == &(b->fs))
rule->pipe_ptr = NULL ;
flush_pipe_ptrs(&(b->fs));
/* remove all references to this pipe from flow_sets */
for (fs = all_flow_sets; fs; fs= fs->next )
@ -1721,9 +1714,7 @@ delete_pipe(struct dn_pipe *p)
else
a->next = b->next ;
/* remove references to this flow_set from the ip_fw rules. */
LIST_FOREACH(rule, &ip_fw_chain_head, next)
if (rule->pipe_ptr == b)
rule->pipe_ptr = NULL ;
flush_pipe_ptrs(b);
if (b->pipe != NULL) {
/* Update total weight on parent pipe and cleanup parent heaps */
@ -1847,9 +1838,14 @@ ip_dn_ctl(struct sockopt *sopt)
/* Disallow sets in really-really secure mode. */
if (sopt->sopt_dir == SOPT_SET) {
#if __FreeBSD_version >= 500034
error = securelevel_ge(sopt->sopt_td->td_ucred, 3);
if (error)
return (error);
#else
if (securelevel >= 3)
return (EPERM);
#endif
}
switch (sopt->sopt_name) {

View File

@ -1,183 +1,305 @@
/*
* Copyright (c) 1993 Daniel Boulet
* Copyright (c) 1994 Ugen J.S.Antsilevich
* Copyright (c) 2002 Luigi Rizzo, Universita` di Pisa
*
* Redistribution and use in source forms, with and without modification,
* are permitted provided that this entire comment appears intact.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Redistribution in binary form may occur without any restrictions.
* Obviously, it would be nice if you gave credit where credit is due
* but requiring it would be too onerous.
*
* This software is provided ``AS IS'' without any warranties of any kind.
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _IP_FW_H
#define _IP_FW_H
#include <sys/queue.h>
#ifndef _IPFW2_H
#define _IPFW2_H
/*
* This union structure identifies an interface, either explicitly
* by name or implicitly by IP address. The flags IP_FW_F_IIFNAME
* and IP_FW_F_OIFNAME say how to interpret this structure. An
* interface unit number of -1 matches any unit number, while an
* IP address of 0.0.0.0 indicates matches any interface.
* The kernel representation of ipfw rules is made of a list of
* 'instructions' (for all practical purposes equivalent to BPF
* instructions), which specify which fields of the packet
* (or its metatada) should be analysed.
*
* The receive and transmit interfaces are only compared against the
* the packet if the corresponding bit (IP_FW_F_IIFACE or IP_FW_F_OIFACE)
* is set. Note some packets lack a receive or transmit interface
* (in which case the missing "interface" never matches).
* Each instruction is stored in a structure which begins with
* "ipfw_insn", and can contain extra fields depending on the
* instruction type (listed below).
*
* "enum ipfw_opcodes" are the opcodes supported. We can have up
* to 256 different opcodes.
*/
union ip_fw_if {
struct in_addr fu_via_ip; /* Specified by IP address */
struct { /* Specified by interface name */
#define FW_IFNLEN 10 /* need room ! was IFNAMSIZ */
char name[FW_IFNLEN];
short unit; /* -1 means match any unit */
} fu_via_if;
enum ipfw_opcodes { /* arguments (4 byte each) */
O_NOP,
O_IP_SRC, /* u32 = IP */
O_IP_SRC_MASK, /* ip = IP/mask */
O_IP_SRC_ME, /* none */
O_IP_SRC_SET, /* u32=base, arg1=len, bitmap */
O_IP_DST, /* u32 = IP */
O_IP_DST_MASK, /* ip = IP/mask */
O_IP_DST_ME, /* none */
O_IP_DST_SET, /* u32=base, arg1=len, bitmap */
O_IP_SRCPORT, /* (n)port list:mask 4 byte ea */
O_IP_DSTPORT, /* (n)port list:mask 4 byte ea */
O_PROTO, /* arg1=protocol */
O_MACADDR2, /* 2 mac addr:mask */
O_MAC_TYPE, /* same as srcport */
O_LAYER2, /* none */
O_IN, /* none */
O_FRAG, /* none */
O_RECV, /* none */
O_XMIT, /* none */
O_VIA, /* none */
O_IPOPT, /* arg1 = 2*u8 bitmap */
O_IPLEN, /* arg1 = len */
O_IPID, /* arg1 = id */
O_IPPRE, /* arg1 = id */
O_IPTOS, /* arg1 = id */
O_IPTTL, /* arg1 = TTL */
O_IPVER, /* arg1 = version */
O_UID, /* u32 = id */
O_GID, /* u32 = id */
O_ESTAB, /* none (tcp established) */
O_TCPFLAGS, /* arg1 = 2*u8 bitmap */
O_TCPWIN, /* arg1 = desired win */
O_TCPSEQ, /* u32 = desired seq. */
O_TCPACK, /* u32 = desired seq. */
O_ICMPTYPE, /* u32 = icmp bitmap */
O_TCPOPTS, /* arg1 = 2*u8 bitmap */
O_IPOPTS, /* arg1 = 2*u8 bitmap */
O_PROBE_STATE, /* none */
O_KEEP_STATE, /* none */
O_LIMIT, /* ipfw_insn_limit */
O_LIMIT_PARENT, /* dyn_type, not an opcode. */
/*
* these are really 'actions', and must be last in the list.
*/
O_LOG, /* ipfw_insn_log */
O_PROB, /* u32 = match probability */
O_CHECK_STATE, /* none */
O_ACCEPT, /* none */
O_DENY, /* none */
O_REJECT, /* arg1=icmp arg (same as deny) */
O_COUNT, /* none */
O_SKIPTO, /* arg1=next rule number */
O_PIPE, /* arg1=pipe number */
O_QUEUE, /* arg1=queue number */
O_DIVERT, /* arg1=port number */
O_TEE, /* arg1=port number */
O_FORWARD_IP, /* fwd sockaddr */
O_FORWARD_MAC, /* fwd mac */
O_LAST_OPCODE /* not an opcode! */
};
/*
* Format of an IP firewall descriptor
* Template for instructions.
*
* ipfw_insn is used for all instructions which require no operands,
* a single 16-bit value (arg1), or a couple of 8-bit values.
*
* For other instructions which require different/larger arguments
* we have derived structures, ipfw_insn_*.
*
* The size of the instruction (in 32-bit words) is in the low
* 6 bits of "len". The 2 remaining bits are used to implement
* NOT and OR on individual instructions. Given a type, you can
* compute the length to be put in "len" using F_INSN_SIZE(t)
*
* F_NOT negates the match result of the instruction.
*
* F_OR is used to build or blocks. By default, instructions
* are evaluated as part of a logical AND. An "or" block
* { X or Y or Z } contains F_OR set in all but the last
* instruction of the block. A match will cause the code
* to skip past the last instruction of the block.
*
* NOTA BENE: in a couple of places we assume that
* sizeof(ipfw_insn) == sizeof(u_int32_t)
* this needs to be fixed.
*
* fw_src, fw_dst, fw_smsk, fw_dmsk are always stored in network byte order.
* fw_flg and fw_n*p are stored in host byte order (of course).
* Port numbers are stored in HOST byte order.
*/
typedef struct _ipfw_insn { /* template for instructions */
enum ipfw_opcodes opcode:8;
u_int8_t len; /* numer of 32-byte words */
#define F_NOT 0x80
#define F_OR 0x40
#define F_LEN_MASK 0x3f
#define F_LEN(cmd) ((cmd)->len & F_LEN_MASK)
u_int16_t arg1;
} ipfw_insn;
/*
* To match MAC headers:
* 12 bytes at fw_mac_hdr contain the dst-src MAC address after masking.
* 12 bytes at fw_mac_mask contain the mask to apply to dst-src
* 2 bytes at fw_mac_type contain the mac type after mask (in net format)
* 2 bytes at fw_mac_type_mask contain the mac type mask
* If IP_FW_F_SRNG, the two contain the low-high of a range of types.
* IP_FW_F_DRNG is used to indicare we want to match a vlan.
* The F_INSN_SIZE(type) computes the size, in 4-byte words, of
* a given type.
*/
#define fw_mac_hdr fw_src
#define fw_mac_mask fw_uar
#define fw_mac_type fw_iplen
#define fw_mac_mask_type fw_ipid
#define F_INSN_SIZE(t) ((sizeof (t))/sizeof(u_int32_t))
struct ip_fw {
LIST_ENTRY(ip_fw) next; /* bidirectional list of rules */
u_int fw_flg; /* Operational Flags word */
u_int64_t fw_pcnt; /* Packet counters */
u_int64_t fw_bcnt; /* Byte counters */
/*
* This is used to store an array of 16-bit entries (ports etc.)
*/
typedef struct _ipfw_insn_u16 {
ipfw_insn o;
u_int16_t ports[2]; /* there may be more */
} ipfw_insn_u16;
struct in_addr fw_src; /* Source IP address */
struct in_addr fw_dst; /* Destination IP address */
struct in_addr fw_smsk; /* Mask for source IP address */
struct in_addr fw_dmsk; /* Mask for destination address */
u_short fw_number; /* Rule number */
u_char fw_prot; /* IP protocol */
#if 1
u_char fw_nports; /* # of src/dst port in array */
#define IP_FW_GETNSRCP(rule) ((rule)->fw_nports & 0x0f)
#define IP_FW_SETNSRCP(rule, n) do { \
(rule)->fw_nports &= ~0x0f; \
(rule)->fw_nports |= (n); \
} while (0)
#define IP_FW_GETNDSTP(rule) ((rule)->fw_nports >> 4)
#define IP_FW_SETNDSTP(rule, n) do { \
(rule)->fw_nports &= ~0xf0; \
(rule)->fw_nports |= (n) << 4;\
} while (0)
#define IP_FW_HAVEPORTS(rule) ((rule)->fw_nports != 0)
#else
u_char __pad[1];
u_int _nsrcp;
u_int _ndstp;
#define IP_FW_GETNSRCP(rule) (rule)->_nsrcp
#define IP_FW_SETNSRCP(rule,n) (rule)->_nsrcp = n
#define IP_FW_GETNDSTP(rule) (rule)->_ndstp
#define IP_FW_SETNDSTP(rule,n) (rule)->_ndstp = n
#define IP_FW_HAVEPORTS(rule) ((rule)->_ndstp + (rule)->_nsrcp != 0)
#endif
#define IP_FW_MAX_PORTS 10 /* A reasonable maximum */
union {
u_short fw_pts[IP_FW_MAX_PORTS]; /* port numbers to match */
#define IP_FW_ICMPTYPES_MAX 128
#define IP_FW_ICMPTYPES_DIM (IP_FW_ICMPTYPES_MAX / (sizeof(unsigned) * 8))
unsigned fw_icmptypes[IP_FW_ICMPTYPES_DIM]; /*ICMP types bitmap*/
} fw_uar;
/*
* This is used to store an array of 32-bit entries
* (uid, single IPv4 addresses etc.)
*/
typedef struct _ipfw_insn_u32 {
ipfw_insn o;
u_int32_t d[1]; /* one or more */
} ipfw_insn_u32;
u_int fw_ipflg; /* IP flags word */
u_short fw_iplen; /* IP length */
u_short fw_ipid; /* Identification */
u_char fw_ipopt; /* IP options set */
u_char fw_ipnopt; /* IP options unset */
u_char fw_iptos; /* IP type of service set */
u_char fw_ipntos; /* IP type of service unset */
u_char fw_ipttl; /* IP time to live */
u_int fw_ipver:4; /* IP version */
u_char fw_tcpopt; /* TCP options set */
u_char fw_tcpnopt; /* TCP options unset */
u_char fw_tcpf; /* TCP flags set */
u_char fw_tcpnf; /* TCP flags unset */
u_short fw_tcpwin; /* TCP window size */
u_int32_t fw_tcpseq; /* TCP sequence */
u_int32_t fw_tcpack; /* TCP acknowledgement */
long timestamp; /* timestamp (tv_sec) of last match */
union ip_fw_if fw_in_if; /* Incoming interfaces */
union ip_fw_if fw_out_if; /* Outgoing interfaces */
union {
u_short fu_divert_port; /* Divert/tee port (options IPDIVERT) */
u_short fu_pipe_nr; /* queue number (option DUMMYNET) */
u_short fu_skipto_rule; /* SKIPTO command rule number */
u_short fu_reject_code; /* REJECT response code */
struct sockaddr_in fu_fwd_ip;
} fw_un;
void *pipe_ptr; /* flow_set ptr for dummynet pipe */
void *next_rule_ptr; /* next rule in case of match */
uid_t fw_uid; /* uid to match */
gid_t fw_gid; /* gid to match */
int fw_logamount; /* amount to log */
u_int64_t fw_loghighest; /* highest number packet to log */
/*
* This is used to store IP addr-mask pairs.
*/
typedef struct _ipfw_insn_ip {
ipfw_insn o;
struct in_addr addr;
struct in_addr mask;
} ipfw_insn_ip;
long dont_match_prob; /* 0x7fffffff means 1.0, always fail */
u_char dyn_type; /* type for dynamic rule */
/*
* This is used to forward to a given address (ip)
*/
typedef struct _ipfw_insn_sa {
ipfw_insn o;
struct sockaddr_in sa;
} ipfw_insn_sa;
#define DYN_KEEP_STATE 0 /* type for keep-state rules */
#define DYN_LIMIT 1 /* type for limit connection rules */
#define DYN_LIMIT_PARENT 2 /* parent entry for limit connection rules */
/*
* This is used for MAC addr-mask pairs.
*/
typedef struct _ipfw_insn_mac {
ipfw_insn o;
u_char addr[12]; /* dst[6] + src[6] */
u_char mask[12]; /* dst[6] + src[6] */
} ipfw_insn_mac;
/* following two fields are used to limit number of connections
* basing on either src, srcport, dst, dstport.
*/
u_char limit_mask; /* mask type for limit rule, can
* have many.
*/
/*
* This is used for interface match rules (recv xx, xmit xx)
*/
typedef struct _ipfw_insn_if {
ipfw_insn o;
union {
struct in_addr ip;
int unit;
} p;
char name[IFNAMSIZ];
} ipfw_insn_if;
/*
* This is used for pipe and queue actions, which need to store
* a single pointer (which can have different size on different
* architectures.
*/
typedef struct _ipfw_insn_pipe {
ipfw_insn o;
void *pipe_ptr;
} ipfw_insn_pipe;
/*
* This is used for limit rules.
*/
typedef struct _ipfw_insn_limit {
ipfw_insn o;
u_int8_t _pad;
u_int8_t limit_mask; /* combination of DYN_* below */
#define DYN_SRC_ADDR 0x1
#define DYN_SRC_PORT 0x2
#define DYN_DST_ADDR 0x4
#define DYN_DST_PORT 0x8
u_short conn_limit; /* # of connections for limit rule */
};
#define fw_divert_port fw_un.fu_divert_port
#define fw_skipto_rule fw_un.fu_skipto_rule
#define fw_reject_code fw_un.fu_reject_code
#define fw_pipe_nr fw_un.fu_pipe_nr
#define fw_fwd_ip fw_un.fu_fwd_ip
u_int16_t conn_limit;
} ipfw_insn_limit;
/*
* This is used for log instructions
*/
typedef struct _ipfw_insn_log {
ipfw_insn o;
u_int32_t max_log; /* how many do we log -- 0 = all */
u_int32_t log_left; /* how many left to log */
} ipfw_insn_log;
/*
* Here we have the structure representing an ipfw rule.
*
* rule_ptr -------------+
* V
* [ next.le_next ]---->[ next.le_next ]---- [ next.le_next ]--->
* [ next.le_prev ]<----[ next.le_prev ]<----[ next.le_prev ]<---
* [ <ip_fw> body ] [ <ip_fw> body ] [ <ip_fw> body ]
* It starts with a general area (with link fields and counters)
* followed by an array of one or more instructions, which the code
* accesses as an array of 32-bit values.
*
* Given a rule pointer r:
*
* r->cmd is the start of the first instruction.
* ACTION_PTR(r) is the start of the first action (things to do
* once a rule matched).
*
* When assembling instruction, remember the following:
*
* + if a rule has a "keep-state" (or "limit") option, then the
* first instruction (at r->cmd) MUST BE an O_PROBE_STATE
* + if a rule has a "log" option, then the first action
* (at ACTION_PTR(r)) MUST be O_LOG
*
* NOTE: we use a simple linked list of rules because we never need
* to delete a rule without scanning the list. We do not use
* queue(3) macros for portability and readability.
*/
struct ip_fw {
struct ip_fw *next; /* linked list of rules */
u_int16_t act_ofs; /* offset of action in 32-bit units */
u_int16_t cmd_len; /* # of 32-bit words in cmd */
u_int16_t rulenum; /* rule number */
u_int16_t _pad; /* padding */
/* These fields are present in all rules. */
u_int64_t pcnt; /* Packet counter */
u_int64_t bcnt; /* Byte counter */
u_int32_t timestamp; /* tv_sec of last match */
struct ip_fw *next_rule; /* ptr to next rule */
ipfw_insn cmd[1]; /* storage for commands */
};
#define ACTION_PTR(rule) \
(ipfw_insn *)( (u_int32_t *)((rule)->cmd) + ((rule)->act_ofs) )
#define RULESIZE(rule) (sizeof(struct ip_fw) + \
((struct ip_fw *)(rule))->cmd_len * 4 - 4)
/*
* Flow mask/flow id for each queue.
* This structure is used as a flow mask and a flow id for various
* parts of the code.
*/
struct ipfw_flow_id {
u_int32_t dst_ip;
@ -191,107 +313,24 @@ struct ipfw_flow_id {
/*
* dynamic ipfw rule
*/
struct ipfw_dyn_rule {
struct ipfw_dyn_rule *next;
struct ipfw_flow_id id; /* (masked) flow id */
struct ip_fw *rule; /* pointer to rule */
struct ipfw_dyn_rule *parent; /* pointer to parent rule */
u_int32_t expire; /* expire time */
u_int64_t pcnt; /* packet match counters */
u_int64_t bcnt; /* byte match counters */
u_int32_t bucket; /* which bucket in hash table */
typedef struct _ipfw_dyn_rule ipfw_dyn_rule;
struct _ipfw_dyn_rule {
ipfw_dyn_rule *next; /* linked list of rules. */
struct ipfw_flow_id id; /* (masked) flow id */
struct ip_fw *rule; /* pointer to rule */
ipfw_dyn_rule *parent; /* pointer to parent rule */
u_int32_t expire; /* expire time */
u_int64_t pcnt; /* packet match counter */
u_int64_t bcnt; /* byte match counter */
u_int32_t bucket; /* which bucket in hash table */
u_int32_t state; /* state of this rule (typically a
* combination of TCP flags)
*/
u_int16_t dyn_type; /* rule type */
u_int16_t count; /* refcount */
u_int16_t dyn_type; /* rule type */
u_int16_t count; /* refcount */
};
/*
* Values for "flags" field .
*/
#define IP_FW_F_COMMAND 0x000000ff /* Mask for type of chain entry: */
#define IP_FW_F_DENY 0x00000000 /* This is a deny rule */
#define IP_FW_F_REJECT 0x00000001 /* Deny and send a response packet */
#define IP_FW_F_ACCEPT 0x00000002 /* This is an accept rule */
#define IP_FW_F_COUNT 0x00000003 /* This is a count rule */
#define IP_FW_F_DIVERT 0x00000004 /* This is a divert rule */
#define IP_FW_F_TEE 0x00000005 /* This is a tee rule */
#define IP_FW_F_SKIPTO 0x00000006 /* This is a skipto rule */
#define IP_FW_F_FWD 0x00000007 /* This is a "change forwarding
* address" rule
*/
#define IP_FW_F_PIPE 0x00000008 /* This is a dummynet rule */
#define IP_FW_F_QUEUE 0x00000009 /* This is a dummynet queue */
#define IP_FW_F_IN 0x00000100 /* Check inbound packets */
#define IP_FW_F_OUT 0x00000200 /* Check outbound packets */
#define IP_FW_F_IIFACE 0x00000400 /* Apply inbound interface test */
#define IP_FW_F_OIFACE 0x00000800 /* Apply outbound interface test */
#define IP_FW_F_PRN 0x00001000 /* Print if this rule matches */
#define IP_FW_F_SRNG 0x00002000 /* The first two src ports are a min
* and max range (stored in host byte
* order).
*/
#define IP_FW_F_DRNG 0x00004000 /* The first two dst ports are a min
* and max range (stored in host byte
* order).
*/
#define IP_FW_F_FRAG 0x00008000 /* Fragment */
#define IP_FW_F_IIFNAME 0x00010000 /* In interface by name/unit (not IP) */
#define IP_FW_F_OIFNAME 0x00020000 /* Out interface by name/unit (not IP)*/
#define IP_FW_F_INVSRC 0x00040000 /* Invert sense of src check */
#define IP_FW_F_INVDST 0x00080000 /* Invert sense of dst check */
#define IP_FW_F_ICMPBIT 0x00100000 /* ICMP type bitmap is valid */
#define IP_FW_F_UID 0x00200000 /* filter by uid */
#define IP_FW_F_GID 0x00400000 /* filter by gid */
#define IP_FW_F_RND_MATCH 0x00800000 /* probabilistic rule match */
#define IP_FW_F_SMSK 0x01000000 /* src-port + mask */
#define IP_FW_F_DMSK 0x02000000 /* dst-port + mask */
#define IP_FW_BRIDGED 0x04000000 /* only match bridged packets */
#define IP_FW_F_KEEP_S 0x08000000 /* keep state */
#define IP_FW_F_CHECK_S 0x10000000 /* check state */
#define IP_FW_F_SME 0x20000000 /* source = me */
#define IP_FW_F_DME 0x40000000 /* destination = me */
#define IP_FW_F_MAC 0x80000000 /* match MAC header */
#define IP_FW_F_MASK 0xFFFFFFFF /* All possible flag bits mask */
/*
* Flags for the 'fw_ipflg' field, for comparing values
* of ip and its protocols.
*/
#define IP_FW_IF_TCPOPT 0x00000001 /* tcp options */
#define IP_FW_IF_TCPFLG 0x00000002 /* tcp flags */
#define IP_FW_IF_TCPSEQ 0x00000004 /* tcp sequence number */
#define IP_FW_IF_TCPACK 0x00000008 /* tcp acknowledgement number */
#define IP_FW_IF_TCPWIN 0x00000010 /* tcp window size */
#define IP_FW_IF_TCPEST 0x00000020 /* established TCP connection */
#define IP_FW_IF_TCPMSK 0x0000003f /* mask of all tcp values */
#define IP_FW_IF_IPOPT 0x00000100 /* ip options */
#define IP_FW_IF_IPLEN 0x00000200 /* ip length */
#define IP_FW_IF_IPID 0x00000400 /* ip identification */
#define IP_FW_IF_IPTOS 0x00000800 /* ip type of service */
#define IP_FW_IF_IPTTL 0x00001000 /* ip time to live */
#define IP_FW_IF_IPVER 0x00002000 /* ip version */
#define IP_FW_IF_IPPRE 0x00004000 /* ip precedence */
#define IP_FW_IF_IPMSK 0x00007f00 /* mask of all ip values */
#define IP_FW_IF_MSK 0x0000ffff /* All possible bits mask */
/*
* For backwards compatibility with rules specifying "via iface" but
* not restricted to only "in" or "out" packets, we define this combination
* of bits to represent this configuration.
*/
#define IF_FW_F_VIAHACK (IP_FW_F_IN|IP_FW_F_OUT|IP_FW_F_IIFACE|IP_FW_F_OIFACE)
/*
* Definitions for REJECT response codes.
* Values less than 256 correspond to ICMP unreachable codes.
*/
#define IP_FW_REJECT_RST 0x0100 /* TCP packets: send RST */
/*
* Definitions for IP option names.
*/
@ -309,6 +348,8 @@ struct ipfw_dyn_rule {
#define IP_FW_TCPOPT_TS 0x08
#define IP_FW_TCPOPT_CC 0x10
#define ICMP_REJECT_RST 0x100 /* fake ICMP code (send a TCP RST) */
/*
* Main firewall chains definitions and global var's definitions.
*/
@ -319,7 +360,7 @@ struct ipfw_dyn_rule {
#define IP_FW_PORT_DENY_FLAG 0x40000
/*
* arguments for calling ip_fw_chk() and dummynet_io(). We put them
* arguments for calling ipfw_chk() and dummynet_io(). We put them
* all into a structure because this way it is easier and more
* efficient to pass variables around and extend the interface.
*/
@ -342,11 +383,13 @@ struct ip_fw_args {
/*
* Function definitions.
*/
void ip_fw_init(void);
/* Firewall hooks */
struct ip;
struct sockopt;
struct dn_flow_set;
void flush_pipe_ptrs(struct dn_flow_set *match); /* used by dummynet */
typedef int ip_fw_chk_t (struct ip_fw_args *args);
typedef int ip_fw_ctl_t (struct sockopt *);
extern ip_fw_chk_t *ip_fw_chk_ptr;
@ -356,4 +399,4 @@ extern int fw_enable;
#define IPFW_LOADED (ip_fw_chk_ptr != NULL)
#endif /* _KERNEL */
#endif /* _IP_FW_H */
#endif /* _IPFW2_H */

2519
sys/netinet/ip_fw2.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2641,6 +2641,82 @@ PacketAliasCheckNewLink(void)
#include <string.h>
#include <err.h>
#define NEW_IPFW 1 /* use new ipfw code */
#ifdef NEW_IPFW
/*
* A function to fill simple commands of size 1.
* Existing flags are preserved.
*/
static void
fill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, u_int16_t arg)
{
cmd->opcode = opcode;
cmd->len = ((cmd->len | flags) & (F_NOT | F_OR)) | 1;
cmd->arg1 = arg;
}
/*
* 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.
*/
static ipfw_insn *
next_cmd(ipfw_insn *cmd)
{
cmd += F_LEN(cmd);
bzero(cmd, sizeof(*cmd));
return cmd;
}
static void
fill_ip(ipfw_insn_ip *cmd, enum ipfw_opcodes opcode, u_int32_t addr)
{
cmd->o.opcode = opcode;
cmd->o.len = F_INSN_SIZE(ipfw_insn_u32);
cmd->addr.s_addr = addr;
}
static void
fill_one_port(ipfw_insn_u16 *cmd, enum ipfw_opcodes opcode, u_int16_t port)
{
cmd->o.opcode = opcode;
cmd->o.len = F_INSN_SIZE(ipfw_insn_u16);
cmd->ports[0] = cmd->ports[1] = port;
}
static int
fill_rule(void *buf, int bufsize, int rulenum,
enum ipfw_opcodes action, int proto,
struct in_addr sa, u_int16_t sp, struct in_addr da, u_int32_t dp)
{
struct ip_fw *rule = (struct ip_fw *)buf;
ipfw_insn *cmd = (ipfw_insn *)rule->cmd;
bzero(buf, bufsize);
rule->rulenum = rulenum;
fill_cmd(cmd, O_PROTO, 0, proto);
cmd = next_cmd(cmd);
fill_ip((ipfw_insn_ip *)cmd, O_IP_SRC, sa.s_addr);
cmd = next_cmd(cmd);
fill_one_port((ipfw_insn_u16 *)cmd, O_IP_SRCPORT, sp);
cmd = next_cmd(cmd);
fill_ip((ipfw_insn_ip *)cmd, O_IP_DST, da.s_addr);
cmd = next_cmd(cmd);
fill_one_port((ipfw_insn_u16 *)cmd, O_IP_DSTPORT, dp);
cmd = next_cmd(cmd);
fill_cmd(cmd, O_ACCEPT, 0, 0);
cmd = next_cmd(cmd);
return ((void *)cmd - buf);
}
#endif /* NEW_IPFW */
static void ClearAllFWHoles(void);
static int fireWallBaseNum; /* The first firewall entry free for our use */
@ -2724,6 +2800,35 @@ PunchFWHole(struct alias_link *link) {
/* Start next search at next position */
fireWallActiveNum = fwhole+1;
#ifdef NEW_IPFW
if (GetOriginalPort(link) != 0 && GetDestPort(link) != 0) {
/*
* generate two rules of the form
*
* add fwhole accept tcp from OAddr OPort to DAddr DPort
* add fwhole accept tcp from DAddr DPort to OAddr OPort
*/
u_int32_t rulebuf[255];
int i;
i = fill_rule(rulebuf, sizeof(rulebuf), fwhole,
O_ACCEPT, IPPROTO_TCP,
GetOriginalAddress(link), ntohs(GetOriginalPort(link)),
GetDestAddress(link), ntohs(GetDestPort(link)) );
r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, rulebuf, i);
if (r)
err(1, "alias punch inbound(1) setsockopt(IP_FW_ADD)");
i = fill_rule(rulebuf, sizeof(rulebuf), fwhole,
O_ACCEPT, IPPROTO_TCP,
GetDestAddress(link), ntohs(GetDestPort(link)),
GetOriginalAddress(link), ntohs(GetOriginalPort(link)) );
r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, rulebuf, i);
if (r)
err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)");
}
#else /* !NEW_IPFW old code to generate ipfw rule */
/* Build generic part of the two rules */
rule.fw_number = fwhole;
IP_FW_SETNSRCP(&rule, 1); /* Number of source ports. */
@ -2759,6 +2864,7 @@ PunchFWHole(struct alias_link *link) {
err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)");
#endif
}
#endif /* !NEW_IPFW */
/* Indicate hole applied */
link->data.tcp->fwhole = fwhole;
fw_setfield(fireWallField, fwhole);
@ -2770,6 +2876,10 @@ static void
ClearFWHole(struct alias_link *link) {
if (link->link_type == LINK_TCP) {
int fwhole = link->data.tcp->fwhole; /* Where is the firewall hole? */
#ifdef NEW_IPFW
while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &fwhole, sizeof fwhole))
;
#else /* !NEW_IPFW */
struct ip_fw rule;
if (fwhole < 0)
@ -2779,7 +2889,9 @@ ClearFWHole(struct alias_link *link) {
rule.fw_number = fwhole;
while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule))
;
#endif /* !NEW_IPFW */
fw_clrfield(fireWallField, fwhole);
link->data.tcp->fwhole = -1;
}
}
@ -2795,9 +2907,15 @@ ClearAllFWHoles(void) {
memset(&rule, 0, sizeof rule);
for (i = fireWallBaseNum; i < fireWallBaseNum + fireWallNumNums; i++) {
#ifdef NEW_IPFW
int r = i;
while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &r, sizeof r))
;
#else /* !NEW_IPFW */
rule.fw_number = i;
while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule))
;
#endif /* NEW_IPFW */
}
memset(fireWallField, 0, fireWallNumNums);
}