Summer of Code 2005: improve libalias - part 2 of 2

With the second (and last) part of my previous Summer of Code work, we get:

-ipfw's in kernel nat

-redirect_* and LSNAT support

General information about nat syntax and some examples are available
in the ipfw (8) man page. The redirect and LSNAT syntax are identical
to natd, so please refer to natd (8) man page.

To enable in kernel nat in rc.conf, two options were added:

o firewall_nat_enable: equivalent to natd_enable

o firewall_nat_interface: equivalent to natd_interface

Remember to set net.inet.ip.fw.one_pass to 0, if you want the packet
to continue being checked by the firewall ruleset after being
(de)aliased.

NOTA BENE: due to some problems with libalias architecture, in kernel
nat won't work with TSO enabled nic, thus you have to disable TSO via
ifconfig (ifconfig foo0 -tso).

Approved by: glebius (mentor)
This commit is contained in:
Paolo Pisati 2006-12-29 21:59:17 +00:00
parent 139bc87fda
commit ff2f6fe80f
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=165648
8 changed files with 1651 additions and 11 deletions

View File

@ -129,6 +129,14 @@ case ${firewall_type} in
fi
;;
esac
case ${firewall_nat_enable} in
[Yy][Ee][Ss])
if [ -n "${firewall_nat_interface}" ]; then
${fwcmd} nat 123 config if ${firewall_nat_interface} log
${fwcmd} add 50 nat 123 ip4 from any to any via ${firewall_nat_interface}
fi
;;
esac
esac
############

View File

@ -2035,6 +2035,59 @@ diverted to that port.
If no socket is bound to the destination port, or if the divert module is
not loaded, or if the kernel was not compiled with divert socket support,
the packets are dropped.
.Sh NETWORK ADDRESS TRANSLATION (NAT)
The nat configuration command is the following:
.Bd -ragged -offset indent
.Bk -words
.Cm nat
.Ar nat_number
.Cm config
.Ar nat-configuration
.Ek
.Ed
.Pp
.
The following parameters can be configured:
.Bl -tag -width indent
.It Cm ip Ar ip_address
Define an ip address to use for aliasing.
.It Cm if Ar nic
Use ip addres of NIC for aliasing, dynamically changing
it if NIC's ip address change.
.It Cm log
Enable logging on this nat instance.
.It Cm deny_in
Deny any incoming connection from outside world.
.It Cm same_ports
Try to leave the alias port numbers unchanged from
the actual local port numbers.
.It Cm unreg_only
Traffic on the local network not originating from an
unregistered address spaces will be ignored.
.It Cm reset
Reset table of the packet aliasing engine on address change.
.It Cm reverse
Reverse the way libalias handles aliasing.
.It Cm proxy_only
Obey transparent proxy rules only, packet aliasing is not performed.
.El
.Pp
To let the packet continue after being (de)aliased, set the sysctl variable
.Em net.inet.ip.fw.one_pass
to 0.
For more information about aliasing modes, refer to
.Xr libalias 3
.
See Section
.Sx EXAMPLES
for some examples about nat usage.
.Sh REDIRECT AND LSNAT SUPPORT IN IPFW
Redirect and LSNAT support follow closely the syntax used in
.Xr natd 8
.
See Section
.Sx EXAMPLES
for some examples on how to do redirect and lsnat.
.Sh SYSCTL VARIABLES
A set of
.Xr sysctl 8
@ -2423,6 +2476,55 @@ terminates, and your ruleset will be left active.
Otherwise, e.g.\& if
you cannot access your box, the ruleset will be disabled after
the sleep terminates thus restoring the previous situation.
.Ss NAT, REDIRECT AND LSNAT
First redirect all the traffic to nat instance 123:
.Pp
.Dl "ipfw add nat 123 all from any to any"
.Pp
Then to configure nat instance 123 to alias all the outgoing traffic with ip
192.168.0.123, blocking all incoming connections, trying to keep
same ports on both sides, clearing aliasing table on address change
and keeping a log of traffic/link statistics:
.Pp
.Dl "ipfw nat 123 config ip 192.168.0.123 log deny_in reset same_ports"
.Pp
Or to change address of instance 123, aliasing table will be cleared (see
reset option):
.Pp
.Dl "ipfw nat 123 config ip 10.0.0.1"
.Pp
To see configuration of nat instance 123:
.Pp
.Dl "ipfw nat 123 show config"
.Pp
To show logs of all the instances in range 111-999:
.Pp
.Dl "ipfw nat 111-999 show"
.Pp
To see configurations of all instances:
.Pp
.Dl "ipfw nat show config"
.Pp
Or a redirect rule with mixed modes could looks like:
.Pp
.Dl "ipfw nat 123 config redirect_addr 10.0.0.1 10.0.0.66"
.Dl " redirect_port tcp 192.168.0.1:80 500"
.Dl " redirect_proto udp 192.168.1.43 192.168.1.1"
.Dl " redirect_addr 192.168.0.10,192.168.0.11"
.Dl " 10.0.0.100 # LSNAT"
.Dl " redirect_port tcp 192.168.0.1:80,192.168.0.10:22"
.Dl " 500 # LSNAT"
.Pp
or it could be splitted in:
.Pp
.Dl "ipfw nat 1 config redirect_addr 10.0.0.1 10.0.0.66"
.Dl "ipfw nat 2 config redirect_port tcp 192.168.0.1:80 500"
.Dl "ipfw nat 3 config redirect_proto udp 192.168.1.43 192.168.1.1"
.Dl "ipfw nat 4 config redirect_addr 192.168.0.10,192.168.0.11,192.168.0.12"
.Dl " 10.0.0.100"
.Dl "ipfw nat 5 config redirect_port tcp"
.Dl " 192.168.0.1:80,192.168.0.10:22,192.168.0.20:25 500"
.Pp
.Sh SEE ALSO
.Xr cpp 1 ,
.Xr m4 1 ,
@ -2464,6 +2566,11 @@ API based upon code written by
.An Daniel Boulet
for BSDI.
.Pp
.An -nosplit
In-kernel NAT support written by
.An Paolo Pisati Aq piso@FreeBSD.org
as part of a Summer of Code 2005 project.
.Pp
Work on
.Xr dummynet 4
traffic shaper supported by Akamba Corp.
@ -2520,3 +2627,10 @@ violations in its implementation.
Rule syntax is subject to the command line environment and some patterns
may need to be escaped with the backslash character
or quoted appropriately.
.Pp
Due to the architecture of
.Xr libalias 3 ,
ipfw nat is not compatible with the tcp segmentation offloading
(TSO). Thus, to reliably nat your network traffic, please disable TSO
on your NICs using
.Xr ifconfig 8 .

File diff suppressed because it is too large Load Diff

View File

@ -410,6 +410,11 @@ __END_DECLS
#define IP_FW_GET 54 /* get entire firewall rule chain */
#define IP_FW_RESETLOG 55 /* reset logging counters */
#define IP_FW_NAT_CFG 56 /* add/config a nat rule */
#define IP_FW_NAT_DEL 57 /* delete a nat rule */
#define IP_FW_NAT_GET_CONFIG 58 /* get configuration of a nat rule */
#define IP_FW_NAT_GET_LOG 59 /* get log of a nat rule */
#define IP_DUMMYNET_CONFIGURE 60 /* add/configure a dummynet pipe */
#define IP_DUMMYNET_DEL 61 /* delete a dummynet pipe from chain */
#define IP_DUMMYNET_FLUSH 62 /* flush dummynet */

View File

@ -124,6 +124,7 @@ enum ipfw_opcodes { /* arguments (4 byte each) */
O_TEE, /* arg1=port number */
O_FORWARD_IP, /* fwd sockaddr */
O_FORWARD_MAC, /* fwd mac */
O_NAT, /* nope */
/*
* More opcodes.
@ -307,6 +308,64 @@ typedef struct _ipfw_insn_log {
u_int32_t log_left; /* how many left to log */
} ipfw_insn_log;
/* Server pool support (LSNAT). */
struct cfg_spool {
LIST_ENTRY(cfg_spool) _next; /* chain of spool instances */
struct in_addr addr;
u_short port;
};
/* Redirect modes id. */
#define REDIR_ADDR 0x01
#define REDIR_PORT 0x02
#define REDIR_PROTO 0x04
/* Nat redirect configuration. */
struct cfg_redir {
LIST_ENTRY(cfg_redir) _next; /* chain of redir instances */
u_int16_t mode; /* type of redirect mode */
struct in_addr laddr; /* local ip address */
struct in_addr paddr; /* public ip address */
struct in_addr raddr; /* remote ip address */
u_short lport; /* local port */
u_short pport; /* public port */
u_short rport; /* remote port */
u_short pport_cnt; /* number of public ports */
u_short rport_cnt; /* number of remote ports */
int proto; /* protocol: tcp/udp */
struct alias_link **alink;
/* num of entry in spool chain */
u_int16_t spool_cnt;
/* chain of spool instances */
LIST_HEAD(spool_chain, cfg_spool) spool_chain;
};
#define NAT_BUF_LEN 1024
/* Nat configuration data struct. */
struct cfg_nat {
/* chain of nat instances */
LIST_ENTRY(cfg_nat) _next;
int id; /* nat id */
struct in_addr ip; /* nat ip address */
char if_name[IF_NAMESIZE]; /* interface name */
int mode; /* aliasing mode */
struct libalias *lib; /* libalias instance */
/* number of entry in spool chain */
int redir_cnt;
/* chain of redir instances */
LIST_HEAD(redir_chain, cfg_redir) redir_chain;
};
#define SOF_NAT sizeof(struct cfg_nat)
#define SOF_REDIR sizeof(struct cfg_redir)
#define SOF_SPOOL sizeof(struct cfg_spool)
/* Nat command. */
typedef struct _ipfw_insn_nat {
ipfw_insn o;
struct cfg_nat *nat;
} ipfw_insn_nat;
/* Apply ipv6 mask on ipv6 addr */
#define APPLY_MASK(addr,mask) \
(addr)->__u6_addr.__u6_addr32[0] &= (mask)->__u6_addr.__u6_addr32[0]; \
@ -483,6 +542,7 @@ enum {
IP_FW_DUMMYNET,
IP_FW_NETGRAPH,
IP_FW_NGTEE,
IP_FW_NAT,
};
/* flags for divert mtag */

View File

@ -47,6 +47,7 @@
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/condvar.h>
#include <sys/eventhandler.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/kernel.h>
@ -84,6 +85,8 @@
#include <netinet/udp_var.h>
#include <netinet/sctp.h>
#include <netinet/libalias/alias.h>
#include <netinet/libalias/alias_local.h>
#include <netgraph/ng_ipfw.h>
#include <altq/if_altq.h>
@ -138,6 +141,7 @@ struct ip_fw_ugid {
struct ip_fw_chain {
struct ip_fw *rules; /* list of rules */
struct ip_fw *reap; /* list of rules to reap */
LIST_HEAD(, cfg_nat) nat; /* list of nat entries */
struct radix_node_head *tables[IPFW_TABLES_MAX];
struct rwlock rwmtx;
};
@ -303,6 +307,7 @@ static struct sysctl_oid *ip6_fw_sysctl_tree;
#endif /* INET6 */
#endif /* SYSCTL_NODE */
MODULE_DEPEND(ipfw, libalias, 1, 1, 1);
static int fw_deny_unknown_exthdrs = 1;
@ -861,6 +866,9 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args,
snprintf(SNPARGS(action2, 0), "Ngtee %d",
cmd->arg1);
break;
case O_NAT:
action = "Nat";
break;
default:
action = "UNKNOWN";
break;
@ -2028,6 +2036,178 @@ check_uidgid(ipfw_insn_u32 *insn,
return match;
}
static eventhandler_tag ifaddr_event_tag;
static void
ifaddr_change(void *arg __unused, struct ifnet *ifp) {
struct cfg_nat *ptr;
struct ifaddr *ifa;
IPFW_WLOCK(&layer3_chain);
/* Check every nat entry... */
LIST_FOREACH(ptr, &layer3_chain.nat, _next) {
/* ...using nic 'ifp->if_xname' as dynamic alias address. */
if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) == 0) {
mtx_lock(&ifp->if_addr_mtx);
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr == NULL)
continue;
if (ifa->ifa_addr->sa_family != AF_INET)
continue;
ptr->ip = ((struct sockaddr_in *)
(ifa->ifa_addr))->sin_addr;
LibAliasSetAddress(ptr->lib, ptr->ip);
}
mtx_unlock(&ifp->if_addr_mtx);
}
}
IPFW_WUNLOCK(&layer3_chain);
}
static void
flush_nat_ptrs(const int i) {
struct ip_fw *rule;
IPFW_WLOCK_ASSERT(&layer3_chain);
for (rule = layer3_chain.rules; rule; rule = rule->next) {
ipfw_insn_nat *cmd = (ipfw_insn_nat *)ACTION_PTR(rule);
if (cmd->o.opcode != O_NAT)
continue;
if (cmd->nat != NULL && cmd->nat->id == i)
cmd->nat = NULL;
}
}
static struct cfg_nat *
lookup_nat(const int i) {
struct cfg_nat *ptr;
LIST_FOREACH(ptr, &layer3_chain.nat, _next)
if (ptr->id == i)
return(ptr);
return (NULL);
}
#define HOOK_NAT(b, p) do { \
IPFW_WLOCK_ASSERT(&layer3_chain); \
LIST_INSERT_HEAD(b, p, _next); \
} while (0)
#define UNHOOK_NAT(p) do { \
IPFW_WLOCK_ASSERT(&layer3_chain); \
LIST_REMOVE(p, _next); \
} while (0)
#define HOOK_REDIR(b, p) do { \
LIST_INSERT_HEAD(b, p, _next); \
} while (0)
#define HOOK_SPOOL(b, p) do { \
LIST_INSERT_HEAD(b, p, _next); \
} while (0)
static void
del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head) {
struct cfg_redir *r, *tmp_r;
struct cfg_spool *s, *tmp_s;
int i, num;
LIST_FOREACH_SAFE(r, head, _next, tmp_r) {
num = 1; /* Number of alias_link to delete. */
switch (r->mode) {
case REDIR_PORT:
num = r->pport_cnt;
/* FALLTHROUGH */
case REDIR_ADDR:
case REDIR_PROTO:
/* Delete all libalias redirect entry. */
for (i = 0; i < num; i++)
LibAliasRedirectDelete(n->lib, r->alink[i]);
/* Del spool cfg if any. */
LIST_FOREACH_SAFE(s, &r->spool_chain, _next, tmp_s) {
LIST_REMOVE(s, _next);
free(s, M_IPFW);
}
free(r->alink, M_IPFW);
LIST_REMOVE(r, _next);
free(r, M_IPFW);
break;
default:
printf("unknown redirect mode: %u\n", r->mode);
/* XXX - panic?!?!? */
break;
}
}
}
static int
add_redir_spool_cfg(char *buf, struct cfg_nat *ptr) {
struct cfg_redir *r, *ser_r;
struct cfg_spool *s, *ser_s;
int cnt, off, i;
char *panic_err;
for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) {
ser_r = (struct cfg_redir *)&buf[off];
r = malloc(SOF_REDIR, M_IPFW, M_WAITOK | M_ZERO);
memcpy(r, ser_r, SOF_REDIR);
LIST_INIT(&r->spool_chain);
off += SOF_REDIR;
r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt,
M_IPFW, M_WAITOK | M_ZERO);
switch (r->mode) {
case REDIR_ADDR:
r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr,
r->paddr);
break;
case REDIR_PORT:
for (i = 0 ; i < r->pport_cnt; i++) {
/* If remotePort is all ports, set it to 0. */
u_short remotePortCopy = r->rport + i;
if (r->rport_cnt == 1 && r->rport == 0)
remotePortCopy = 0;
r->alink[i] = LibAliasRedirectPort(ptr->lib,
r->laddr, htons(r->lport + i), r->raddr,
htons(remotePortCopy), r->paddr,
htons(r->pport + i), r->proto);
if (r->alink[i] == NULL) {
r->alink[0] = NULL;
break;
}
}
break;
case REDIR_PROTO:
r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr,
r->raddr, r->paddr, r->proto);
break;
default:
printf("unknown redirect mode: %u\n", r->mode);
break;
}
if (r->alink[0] == NULL) {
panic_err = "LibAliasRedirect* returned NULL";
goto bad;
} else /* LSNAT handling. */
for (i = 0; i < r->spool_cnt; i++) {
ser_s = (struct cfg_spool *)&buf[off];
s = malloc(SOF_REDIR, M_IPFW,
M_WAITOK | M_ZERO);
memcpy(s, ser_s, SOF_SPOOL);
LibAliasAddServer(ptr->lib, r->alink[0],
s->addr, htons(s->port));
off += SOF_SPOOL;
/* Hook spool entry. */
HOOK_SPOOL(&r->spool_chain, s);
}
/* And finally hook this redir entry. */
HOOK_REDIR(&ptr->redir_chain, r);
}
return (1);
bad:
/* something really bad happened: panic! */
panic("%s\n", panic_err);
}
/*
* The main check routine for the firewall.
*
@ -3257,6 +3437,177 @@ do { \
IP_FW_NETGRAPH : IP_FW_NGTEE;
goto done;
case O_NAT: {
struct cfg_nat *t;
struct mbuf *mcl;
/* XXX - libalias duct tape */
int ldt;
char *c;
ldt = 0;
args->rule = f; /* Report matching rule. */
retval = 0;
t = ((ipfw_insn_nat *)cmd)->nat;
if (t == NULL) {
t = lookup_nat(cmd->arg1);
if (t == NULL) {
retval = IP_FW_DENY;
goto done;
} else
((ipfw_insn_nat *)cmd)->nat =
t;
}
if ((mcl = m_megapullup(m, m->m_pkthdr.len)) ==
NULL)
goto badnat;
ip = mtod(mcl, struct ip *);
if (args->eh == NULL) {
ip->ip_len = htons(ip->ip_len);
ip->ip_off = htons(ip->ip_off);
}
/*
* XXX - Libalias checksum offload 'duct tape':
*
* locally generated packets have only
* pseudo-header checksum calculated
* and libalias will screw it[1], so
* mark them for later fix. Moreover
* there are cases when libalias
* modify tcp packet data[2], mark it
* for later fix too.
*
* [1] libalias was never meant to run
* in kernel, so it doesn't have any
* knowledge about checksum
* offloading, and it expects a packet
* with a full internet
* checksum. Unfortunately, packets
* generated locally will have just the
* pseudo header calculated, and when
* libalias tries to adjust the
* checksum it will actually screw it.
*
* [2] when libalias modify tcp's data
* content, full TCP checksum has to
* be recomputed: the problem is that
* libalias doesn't have any idea
* about checksum offloading To
* workaround this, we do not do
* checksumming in LibAlias, but only
* mark the packets in th_x2 field. If
* we receive a marked packet, we
* calculate correct checksum for it
* aware of offloading. Why such a
* terrible hack instead of
* recalculating checksum for each
* packet? Because the previous
* checksum was not checked!
* Recalculating checksums for EVERY
* packet will hide ALL transmission
* errors. Yes, marked packets still
* suffer from this problem. But,
* sigh, natd(8) has this problem,
* too.
*
* TODO: -make libalias mbuf aware (so
* it can handle delayed checksum and tso)
*/
if (mcl->m_pkthdr.rcvif == NULL &&
mcl->m_pkthdr.csum_flags &
CSUM_DELAY_DATA)
ldt = 1;
c = mtod(mcl, char *);
if (oif == NULL)
retval = LibAliasIn(t->lib, c,
MCLBYTES);
else
retval = LibAliasOut(t->lib, c,
MCLBYTES);
if (retval != PKT_ALIAS_OK) {
/* XXX - should i add some logging? */
m_free(mcl);
badnat:
args->m = NULL;
retval = IP_FW_DENY;
goto done;
}
mcl->m_pkthdr.len = mcl->m_len =
ntohs(ip->ip_len);
/*
* XXX - libalias checksum offload
* 'duct tape' (see above)
*/
if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
ip->ip_p == IPPROTO_TCP) {
struct tcphdr *th;
th = (struct tcphdr *)(ip + 1);
if (th->th_x2)
ldt = 1;
}
if (ldt) {
struct tcphdr *th;
struct udphdr *uh;
u_short cksum;
ip->ip_len = ntohs(ip->ip_len);
cksum = in_pseudo(
ip->ip_src.s_addr,
ip->ip_dst.s_addr,
htons(ip->ip_p + ip->ip_len -
(ip->ip_hl << 2))
);
switch (ip->ip_p) {
case IPPROTO_TCP:
th = (struct tcphdr *)(ip + 1);
/*
* Maybe it was set in
* libalias...
*/
th->th_x2 = 0;
th->th_sum = cksum;
mcl->m_pkthdr.csum_data =
offsetof(struct tcphdr,
th_sum);
break;
case IPPROTO_UDP:
uh = (struct udphdr *)(ip + 1);
uh->uh_sum = cksum;
mcl->m_pkthdr.csum_data =
offsetof(struct udphdr,
uh_sum);
break;
}
/*
* No hw checksum offloading: do it
* by ourself.
*/
if ((mcl->m_pkthdr.csum_flags &
CSUM_DELAY_DATA) == 0) {
in_delayed_cksum(mcl);
mcl->m_pkthdr.csum_flags &=
~CSUM_DELAY_DATA;
}
ip->ip_len = htons(ip->ip_len);
}
if (args->eh == NULL) {
ip->ip_len = ntohs(ip->ip_len);
ip->ip_off = ntohs(ip->ip_off);
}
args->m = mcl;
retval = IP_FW_NAT;
goto done;
}
default:
panic("-- unknown opcode %d\n", cmd->opcode);
} /* end of switch() on opcodes */
@ -3826,6 +4177,10 @@ check_ipfw_struct(struct ip_fw *rule, int size)
return EINVAL;
else
goto check_size;
case O_NAT:
if (cmdlen != F_INSN_SIZE(ipfw_insn_nat))
goto bad_size;
goto check_action;
case O_FORWARD_MAC: /* XXX not implemented yet */
case O_CHECK_STATE:
case O_COUNT:
@ -4201,6 +4556,185 @@ ipfw_ctl(struct sockopt *sopt)
}
break;
case IP_FW_NAT_CFG:
{
struct cfg_nat *ptr, *ser_n;
char *buf;
buf = malloc(NAT_BUF_LEN, M_IPFW, M_WAITOK | M_ZERO);
error = sooptcopyin(sopt, buf, NAT_BUF_LEN,
sizeof(struct cfg_nat));
ser_n = (struct cfg_nat *)buf;
/*
* Find/create nat rule.
*/
IPFW_WLOCK(&layer3_chain);
ptr = lookup_nat(ser_n->id);
if (ptr == NULL) {
/* New rule: allocate and init new instance. */
ptr = malloc(sizeof(struct cfg_nat),
M_IPFW, M_NOWAIT | M_ZERO);
if (ptr == NULL) {
IPFW_WUNLOCK(&layer3_chain);
free(buf, M_IPFW);
return (ENOSPC);
}
ptr->lib = LibAliasInit(NULL);
if (ptr->lib == NULL) {
IPFW_WUNLOCK(&layer3_chain);
free(ptr, M_IPFW);
free(buf, M_IPFW);
return (EINVAL);
}
LIST_INIT(&ptr->redir_chain);
} else {
/* Entry already present: temporarly unhook it. */
UNHOOK_NAT(ptr);
flush_nat_ptrs(ser_n->id);
}
IPFW_WUNLOCK(&layer3_chain);
/*
* Basic nat configuration.
*/
ptr->id = ser_n->id;
/*
* XXX - what if this rule doesn't nat any ip and just
* redirect?
* do we set aliasaddress to 0.0.0.0?
*/
ptr->ip = ser_n->ip;
ptr->redir_cnt = ser_n->redir_cnt;
ptr->mode = ser_n->mode;
LibAliasSetMode(ptr->lib, ser_n->mode, ser_n->mode);
LibAliasSetAddress(ptr->lib, ptr->ip);
memcpy(ptr->if_name, ser_n->if_name, IF_NAMESIZE);
/*
* Redir and LSNAT configuration.
*/
/* Delete old cfgs. */
del_redir_spool_cfg(ptr, &ptr->redir_chain);
/* Add new entries. */
add_redir_spool_cfg(&buf[(sizeof(struct cfg_nat))], ptr);
free(buf, M_IPFW);
IPFW_WLOCK(&layer3_chain);
HOOK_NAT(&layer3_chain.nat, ptr);
IPFW_WUNLOCK(&layer3_chain);
}
break;
case IP_FW_NAT_DEL:
{
struct cfg_nat *ptr;
int i;
error = sooptcopyin(sopt, &i, sizeof i, sizeof i);
IPFW_WLOCK(&layer3_chain);
ptr = lookup_nat(i);
if (ptr == NULL) {
error = EINVAL;
IPFW_WUNLOCK(&layer3_chain);
break;
}
UNHOOK_NAT(ptr);
flush_nat_ptrs(i);
IPFW_WUNLOCK(&layer3_chain);
del_redir_spool_cfg(ptr, &ptr->redir_chain);
LibAliasUninit(ptr->lib);
free(ptr, M_IPFW);
}
break;
case IP_FW_NAT_GET_CONFIG:
{
uint8_t *data;
struct cfg_nat *n;
struct cfg_redir *r;
struct cfg_spool *s;
int nat_cnt, off;
nat_cnt = 0;
off = sizeof(nat_cnt);
data = malloc(NAT_BUF_LEN, M_IPFW, M_WAITOK | M_ZERO);
IPFW_RLOCK(&layer3_chain);
/* Serialize all the data. */
LIST_FOREACH(n, &layer3_chain.nat, _next) {
nat_cnt++;
if (off + SOF_NAT < NAT_BUF_LEN) {
bcopy(n, &data[off], SOF_NAT);
off += SOF_NAT;
LIST_FOREACH(r, &n->redir_chain, _next) {
if (off + SOF_REDIR < NAT_BUF_LEN) {
bcopy(r, &data[off],
SOF_REDIR);
off += SOF_REDIR;
LIST_FOREACH(s, &r->spool_chain,
_next) {
if (off + SOF_SPOOL <
NAT_BUF_LEN) {
bcopy(s,
&data[off],
SOF_SPOOL);
off +=
SOF_SPOOL;
} else
goto nospace;
}
} else
goto nospace;
}
} else
goto nospace;
}
bcopy(&nat_cnt, data, sizeof(nat_cnt));
IPFW_RUNLOCK(&layer3_chain);
error = sooptcopyout(sopt, data, NAT_BUF_LEN);
free(data, M_IPFW);
break;
nospace:
IPFW_RUNLOCK(&layer3_chain);
printf("serialized data buffer not big enough:"
"please increase NAT_BUF_LEN\n");
free(data, M_IPFW);
}
break;
case IP_FW_NAT_GET_LOG:
{
uint8_t *data;
struct cfg_nat *ptr;
int i, size, cnt, sof;
data = NULL;
sof = LIBALIAS_BUF_SIZE;
cnt = 0;
IPFW_RLOCK(&layer3_chain);
size = i = 0;
LIST_FOREACH(ptr, &layer3_chain.nat, _next) {
if (ptr->lib->logDesc == NULL)
continue;
cnt++;
size = cnt * (sof + sizeof(int));
data = realloc(data, size, M_IPFW, M_NOWAIT | M_ZERO);
if (data == NULL) {
IPFW_RUNLOCK(&layer3_chain);
return (ENOSPC);
}
bcopy(&ptr->id, &data[i], sizeof(int));
i += sizeof(int);
bcopy(ptr->lib->logDesc, &data[i], sof);
i += sof;
}
IPFW_RUNLOCK(&layer3_chain);
error = sooptcopyout(sopt, data, size);
free(data, M_IPFW);
}
break;
default:
printf("ipfw: ipfw_ctl invalid option %d\n", sopt->sopt_name);
error = EINVAL;
@ -4372,8 +4906,10 @@ ipfw_init(void)
}
ip_fw_ctl_ptr = ipfw_ctl;
ip_fw_chk_ptr = ipfw_chk;
callout_reset(&ipfw_timeout, hz, ipfw_tick, NULL);
callout_reset(&ipfw_timeout, hz, ipfw_tick, NULL);
LIST_INIT(&layer3_chain.nat);
ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change,
NULL, EVENTHANDLER_PRI_ANY);
return (0);
}
@ -4381,12 +4917,20 @@ void
ipfw_destroy(void)
{
struct ip_fw *reap;
struct cfg_nat *ptr, *ptr_temp;
ip_fw_chk_ptr = NULL;
ip_fw_ctl_ptr = NULL;
callout_drain(&ipfw_timeout);
IPFW_WLOCK(&layer3_chain);
flush_tables(&layer3_chain);
LIST_FOREACH_SAFE(ptr, &layer3_chain.nat, _next, ptr_temp) {
LIST_REMOVE(ptr, _next);
del_redir_spool_cfg(ptr, &ptr->redir_chain);
LibAliasUninit(ptr->lib);
free(ptr, M_IPFW);
}
EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag);
layer3_chain.reap = NULL;
free_chain(&layer3_chain, 1 /* kill default rule */);
reap = layer3_chain.reap, layer3_chain.reap = NULL;

View File

@ -189,6 +189,9 @@ ipfw_check_in(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
if (!NG_IPFW_LOADED)
goto drop;
return ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 0);
case IP_FW_NAT:
goto again; /* continue with packet */
default:
KASSERT(0, ("%s: unknown retval", __func__));
@ -315,6 +318,9 @@ ipfw_check_out(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
goto drop;
return ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 0);
case IP_FW_NAT:
goto again; /* continue with packet */
default:
KASSERT(0, ("%s: unknown retval", __func__));
}

View File

@ -389,6 +389,8 @@ rip_ctloutput(struct socket *so, struct sockopt *sopt)
case IP_FW_GET:
case IP_FW_TABLE_GETSIZE:
case IP_FW_TABLE_LIST:
case IP_FW_NAT_GET_CONFIG:
case IP_FW_NAT_GET_LOG:
/*
* XXXRW: Isn't this checked one layer down? Yes, it
* is.
@ -458,6 +460,8 @@ rip_ctloutput(struct socket *so, struct sockopt *sopt)
case IP_FW_TABLE_ADD:
case IP_FW_TABLE_DEL:
case IP_FW_TABLE_FLUSH:
case IP_FW_NAT_CFG:
case IP_FW_NAT_DEL:
/*
* XXXRW: Isn't this checked one layer down?
*/