2 * Copyright 2005, Gleb Smirnoff <glebius@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
33 #include <sys/malloc.h>
34 #include <sys/ctype.h>
35 #include <sys/errno.h>
36 #include <sys/syslog.h>
38 #include <netinet/in_systm.h>
39 #include <netinet/in.h>
40 #include <netinet/ip.h>
41 #include <netinet/ip_var.h>
42 #include <netinet/tcp.h>
43 #include <machine/in_cksum.h>
45 #include <netinet/libalias/alias.h>
47 #include <netgraph/ng_message.h>
48 #include <netgraph/ng_parse.h>
49 #include <netgraph/ng_nat.h>
50 #include <netgraph/netgraph.h>
52 static ng_constructor_t ng_nat_constructor;
53 static ng_rcvmsg_t ng_nat_rcvmsg;
54 static ng_shutdown_t ng_nat_shutdown;
55 static ng_newhook_t ng_nat_newhook;
56 static ng_rcvdata_t ng_nat_rcvdata;
57 static ng_disconnect_t ng_nat_disconnect;
59 /* List of commands and how to convert arguments to/from ASCII. */
60 static const struct ng_cmdlist ng_nat_cmdlist[] = {
65 &ng_parse_ipaddr_type,
71 /* Netgraph node type descriptor. */
72 static struct ng_type typestruct = {
73 .version = NG_ABI_VERSION,
74 .name = NG_NAT_NODE_TYPE,
75 .constructor = ng_nat_constructor,
76 .rcvmsg = ng_nat_rcvmsg,
77 .shutdown = ng_nat_shutdown,
78 .newhook = ng_nat_newhook,
79 .rcvdata = ng_nat_rcvdata,
80 .disconnect = ng_nat_disconnect,
81 .cmdlist = ng_nat_cmdlist,
83 NETGRAPH_INIT(nat, &typestruct);
84 MODULE_DEPEND(ng_nat, libalias, 1, 1, 1);
86 /* Information we store for each node. */
88 node_p node; /* back pointer to node */
89 hook_p in; /* hook for demasquerading */
90 hook_p out; /* hook for masquerading */
91 struct libalias *lib; /* libalias handler */
92 uint32_t flags; /* status flags */
94 typedef struct ng_nat_priv *priv_p;
97 #define NGNAT_READY 0x1 /* We have everything to work */
98 #define NGNAT_ADDR_DEFINED 0x2 /* NGM_NAT_SET_IPADDR happened */
101 ng_nat_constructor(node_p node)
105 /* Initialize private descriptor. */
106 MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH,
111 /* Init aliasing engine. */
112 priv->lib = LibAliasInit(NULL);
113 if (priv->lib == NULL) {
114 FREE(priv, M_NETGRAPH);
118 /* Set same ports on. */
119 (void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS,
120 PKT_ALIAS_SAME_PORTS);
122 /* Link structs together. */
123 NG_NODE_SET_PRIVATE(node, priv);
127 * libalias is not thread safe, so our node
128 * must be single threaded.
130 NG_NODE_FORCE_WRITER(node);
136 ng_nat_newhook(node_p node, hook_p hook, const char *name)
138 const priv_p priv = NG_NODE_PRIVATE(node);
140 if (strcmp(name, NG_NAT_HOOK_IN) == 0) {
142 } else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) {
147 if (priv->out != NULL &&
149 priv->flags & NGNAT_ADDR_DEFINED)
150 priv->flags |= NGNAT_READY;
156 ng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook)
158 const priv_p priv = NG_NODE_PRIVATE(node);
159 struct ng_mesg *resp = NULL;
163 NGI_GET_MSG(item, msg);
165 switch (msg->header.typecookie) {
167 switch (msg->header.cmd) {
168 case NGM_NAT_SET_IPADDR:
170 struct in_addr *const ia = (struct in_addr *)msg->data;
172 if (msg->header.arglen < sizeof(*ia)) {
177 LibAliasSetAddress(priv->lib, *ia);
179 priv->flags |= NGNAT_ADDR_DEFINED;
180 if (priv->out != NULL &&
182 priv->flags |= NGNAT_READY;
186 error = EINVAL; /* unknown command */
191 error = EINVAL; /* unknown cookie type */
195 NG_RESPOND_MSG(error, node, item, resp);
201 ng_nat_rcvdata(hook_p hook, item_p item )
203 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
209 if (!(priv->flags & NGNAT_READY)) {
216 if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) {
217 NGI_M(item) = NULL; /* avoid double free */
225 ip = mtod(m, struct ip *);
227 KASSERT(m->m_pkthdr.len == ntohs(ip->ip_len),
228 ("ng_nat: ip_len != m_pkthdr.len"));
230 if (hook == priv->in) {
231 rval = LibAliasIn(priv->lib, c, MCLBYTES);
232 if (rval != PKT_ALIAS_OK &&
233 rval != PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
237 } else if (hook == priv->out) {
238 rval = LibAliasOut(priv->lib, c, MCLBYTES);
239 if (rval != PKT_ALIAS_OK) {
244 panic("ng_nat: unknown hook!\n");
246 m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len);
248 if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
249 ip->ip_p == IPPROTO_TCP) {
250 struct tcphdr *th = (struct tcphdr *)((caddr_t)ip +
254 * Here is our terrible HACK.
256 * Sometimes LibAlias edits contents of TCP packet.
257 * In this case it needs to recompute full TCP
258 * checksum. However, the problem is that LibAlias
259 * doesn't have any idea about checksum offloading
260 * in kernel. To workaround this, we do not do
261 * checksumming in LibAlias, but only mark the
262 * packets in th_x2 field. If we receive a marked
263 * packet, we calculate correct checksum for it
264 * aware of offloading.
266 * Why do I do such a terrible hack instead of
267 * recalculating checksum for each packet?
268 * Because the previous checksum was not checked!
269 * Recalculating checksums for EVERY packet will
270 * hide ALL transmission errors. Yes, marked packets
271 * still suffer from this problem. But, sigh, natd(8)
272 * has this problem, too.
277 ip->ip_len = ntohs(ip->ip_len);
278 th->th_sum = in_pseudo(ip->ip_src.s_addr,
279 ip->ip_dst.s_addr, htons(IPPROTO_TCP +
280 ip->ip_len - (ip->ip_hl << 2)));
282 if ((m->m_pkthdr.csum_flags & CSUM_TCP) == 0) {
283 m->m_pkthdr.csum_data = offsetof(struct tcphdr,
287 ip->ip_len = htons(ip->ip_len);
291 if (hook == priv->in)
292 NG_FWD_ITEM_HOOK(error, item, priv->out);
294 NG_FWD_ITEM_HOOK(error, item, priv->in);
300 ng_nat_shutdown(node_p node)
302 const priv_p priv = NG_NODE_PRIVATE(node);
304 NG_NODE_SET_PRIVATE(node, NULL);
306 LibAliasUninit(priv->lib);
307 FREE(priv, M_NETGRAPH);
313 ng_nat_disconnect(hook_p hook)
315 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
317 priv->flags &= ~NGNAT_READY;
319 if (hook == priv->out)
321 if (hook == priv->in)
324 if (priv->out == NULL && priv->in == NULL)
325 ng_rmnode_self(NG_HOOK_NODE(hook));