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 static unsigned int ng_nat_translate_flags(unsigned int x);
61 /* Parse type for struct ng_nat_mode. */
62 static const struct ng_parse_struct_field ng_nat_mode_fields[]
64 static const struct ng_parse_type ng_nat_mode_type = {
65 &ng_parse_struct_type,
69 /* List of commands and how to convert arguments to/from ASCII. */
70 static const struct ng_cmdlist ng_nat_cmdlist[] = {
75 &ng_parse_ipaddr_type,
89 &ng_parse_ipaddr_type,
95 /* Netgraph node type descriptor. */
96 static struct ng_type typestruct = {
97 .version = NG_ABI_VERSION,
98 .name = NG_NAT_NODE_TYPE,
99 .constructor = ng_nat_constructor,
100 .rcvmsg = ng_nat_rcvmsg,
101 .shutdown = ng_nat_shutdown,
102 .newhook = ng_nat_newhook,
103 .rcvdata = ng_nat_rcvdata,
104 .disconnect = ng_nat_disconnect,
105 .cmdlist = ng_nat_cmdlist,
107 NETGRAPH_INIT(nat, &typestruct);
108 MODULE_DEPEND(ng_nat, libalias, 1, 1, 1);
110 /* Information we store for each node. */
112 node_p node; /* back pointer to node */
113 hook_p in; /* hook for demasquerading */
114 hook_p out; /* hook for masquerading */
115 struct libalias *lib; /* libalias handler */
116 uint32_t flags; /* status flags */
118 typedef struct ng_nat_priv *priv_p;
120 /* Values of flags */
121 #define NGNAT_CONNECTED 0x1 /* We have both hooks connected */
122 #define NGNAT_ADDR_DEFINED 0x2 /* NGM_NAT_SET_IPADDR happened */
125 ng_nat_constructor(node_p node)
129 /* Initialize private descriptor. */
130 MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH,
135 /* Init aliasing engine. */
136 priv->lib = LibAliasInit(NULL);
137 if (priv->lib == NULL) {
138 FREE(priv, M_NETGRAPH);
142 /* Set same ports on. */
143 (void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS,
144 PKT_ALIAS_SAME_PORTS);
146 /* Link structs together. */
147 NG_NODE_SET_PRIVATE(node, priv);
151 * libalias is not thread safe, so our node
152 * must be single threaded.
154 NG_NODE_FORCE_WRITER(node);
160 ng_nat_newhook(node_p node, hook_p hook, const char *name)
162 const priv_p priv = NG_NODE_PRIVATE(node);
164 if (strcmp(name, NG_NAT_HOOK_IN) == 0) {
166 } else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) {
171 if (priv->out != NULL &&
173 priv->flags |= NGNAT_CONNECTED;
179 ng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook)
181 const priv_p priv = NG_NODE_PRIVATE(node);
182 struct ng_mesg *resp = NULL;
186 NGI_GET_MSG(item, msg);
188 switch (msg->header.typecookie) {
190 switch (msg->header.cmd) {
191 case NGM_NAT_SET_IPADDR:
193 struct in_addr *const ia = (struct in_addr *)msg->data;
195 if (msg->header.arglen < sizeof(*ia)) {
200 LibAliasSetAddress(priv->lib, *ia);
202 priv->flags |= NGNAT_ADDR_DEFINED;
205 case NGM_NAT_SET_MODE:
207 struct ng_nat_mode *const mode =
208 (struct ng_nat_mode *)msg->data;
210 if (msg->header.arglen < sizeof(*mode)) {
215 if (LibAliasSetMode(priv->lib,
216 ng_nat_translate_flags(mode->flags),
217 ng_nat_translate_flags(mode->mask)) < 0) {
223 case NGM_NAT_SET_TARGET:
225 struct in_addr *const ia = (struct in_addr *)msg->data;
227 if (msg->header.arglen < sizeof(*ia)) {
232 LibAliasSetTarget(priv->lib, *ia);
236 error = EINVAL; /* unknown command */
241 error = EINVAL; /* unknown cookie type */
245 NG_RESPOND_MSG(error, node, item, resp);
251 ng_nat_rcvdata(hook_p hook, item_p item )
253 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
259 /* We have no required hooks. */
260 if (!(priv->flags & NGNAT_CONNECTED)) {
265 /* We have no alias address yet to do anything. */
266 if (!(priv->flags & NGNAT_ADDR_DEFINED))
271 if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) {
272 NGI_M(item) = NULL; /* avoid double free */
280 ip = mtod(m, struct ip *);
282 KASSERT(m->m_pkthdr.len == ntohs(ip->ip_len),
283 ("ng_nat: ip_len != m_pkthdr.len"));
285 if (hook == priv->in) {
286 rval = LibAliasIn(priv->lib, c, MCLBYTES);
287 if (rval != PKT_ALIAS_OK &&
288 rval != PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
292 } else if (hook == priv->out) {
293 rval = LibAliasOut(priv->lib, c, MCLBYTES);
294 if (rval != PKT_ALIAS_OK) {
299 panic("ng_nat: unknown hook!\n");
301 m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len);
303 if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
304 ip->ip_p == IPPROTO_TCP) {
305 struct tcphdr *th = (struct tcphdr *)((caddr_t)ip +
309 * Here is our terrible HACK.
311 * Sometimes LibAlias edits contents of TCP packet.
312 * In this case it needs to recompute full TCP
313 * checksum. However, the problem is that LibAlias
314 * doesn't have any idea about checksum offloading
315 * in kernel. To workaround this, we do not do
316 * checksumming in LibAlias, but only mark the
317 * packets in th_x2 field. If we receive a marked
318 * packet, we calculate correct checksum for it
319 * aware of offloading.
321 * Why do I do such a terrible hack instead of
322 * recalculating checksum for each packet?
323 * Because the previous checksum was not checked!
324 * Recalculating checksums for EVERY packet will
325 * hide ALL transmission errors. Yes, marked packets
326 * still suffer from this problem. But, sigh, natd(8)
327 * has this problem, too.
332 ip->ip_len = ntohs(ip->ip_len);
333 th->th_sum = in_pseudo(ip->ip_src.s_addr,
334 ip->ip_dst.s_addr, htons(IPPROTO_TCP +
335 ip->ip_len - (ip->ip_hl << 2)));
337 if ((m->m_pkthdr.csum_flags & CSUM_TCP) == 0) {
338 m->m_pkthdr.csum_data = offsetof(struct tcphdr,
342 ip->ip_len = htons(ip->ip_len);
347 if (hook == priv->in)
348 NG_FWD_ITEM_HOOK(error, item, priv->out);
350 NG_FWD_ITEM_HOOK(error, item, priv->in);
356 ng_nat_shutdown(node_p node)
358 const priv_p priv = NG_NODE_PRIVATE(node);
360 NG_NODE_SET_PRIVATE(node, NULL);
362 LibAliasUninit(priv->lib);
363 FREE(priv, M_NETGRAPH);
369 ng_nat_disconnect(hook_p hook)
371 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
373 priv->flags &= ~NGNAT_CONNECTED;
375 if (hook == priv->out)
377 if (hook == priv->in)
380 if (priv->out == NULL && priv->in == NULL)
381 ng_rmnode_self(NG_HOOK_NODE(hook));
387 ng_nat_translate_flags(unsigned int x)
389 unsigned int res = 0;
392 res |= PKT_ALIAS_LOG;
393 if (x & NG_NAT_DENY_INCOMING)
394 res |= PKT_ALIAS_DENY_INCOMING;
395 if (x & NG_NAT_SAME_PORTS)
396 res |= PKT_ALIAS_SAME_PORTS;
397 if (x & NG_NAT_UNREGISTERED_ONLY)
398 res |= PKT_ALIAS_UNREGISTERED_ONLY;
399 if (x & NG_NAT_RESET_ON_ADDR_CHANGE)
400 res |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
401 if (x & NG_NAT_PROXY_ONLY)
402 res |= PKT_ALIAS_PROXY_ONLY;
403 if (x & NG_NAT_REVERSE)
404 res |= PKT_ALIAS_REVERSE;