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_icmp.h>
42 #include <netinet/tcp.h>
43 #include <netinet/udp.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 struct mbuf * m_megapullup(struct mbuf *, int);
61 /* List of commands and how to convert arguments to/from ASCII. */
62 static const struct ng_cmdlist ng_nat_cmdlist[] = {
67 &ng_parse_ipaddr_type,
73 /* Netgraph node type descriptor. */
74 static struct ng_type typestruct = {
75 .version = NG_ABI_VERSION,
76 .name = NG_NAT_NODE_TYPE,
77 .constructor = ng_nat_constructor,
78 .rcvmsg = ng_nat_rcvmsg,
79 .shutdown = ng_nat_shutdown,
80 .newhook = ng_nat_newhook,
81 .rcvdata = ng_nat_rcvdata,
82 .disconnect = ng_nat_disconnect,
83 .cmdlist = ng_nat_cmdlist,
85 NETGRAPH_INIT(nat, &typestruct);
86 MODULE_DEPEND(ng_nat, libalias, 1, 1, 1);
88 /* Information we store for each node. */
90 node_p node; /* back pointer to node */
91 hook_p in; /* hook for demasquerading */
92 hook_p out; /* hook for masquerading */
93 struct libalias *lib; /* libalias handler */
94 uint32_t flags; /* status flags */
96 typedef struct ng_priv_priv *priv_p;
99 #define NGNAT_READY 0x1 /* We have everything to work */
100 #define NGNAT_ADDR_DEFINED 0x2 /* NGM_NAT_SET_IPADDR happened */
103 ng_nat_constructor(node_p node)
107 /* Initialize private descriptor. */
108 MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH,
113 /* Init aliasing engine. */
114 priv->lib = LibAliasInit(NULL);
115 if (priv->lib == NULL) {
116 FREE(priv, M_NETGRAPH);
120 /* Set same ports on. */
121 (void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS,
122 PKT_ALIAS_SAME_PORTS);
124 /* Link structs together. */
125 NG_NODE_SET_PRIVATE(node, priv);
129 * libalias is not thread safe, so our node
130 * must be single threaded.
132 NG_NODE_FORCE_WRITER(node);
138 ng_nat_newhook(node_p node, hook_p hook, const char *name)
140 const priv_p priv = NG_NODE_PRIVATE(node);
142 if (strcmp(name, NG_NAT_HOOK_IN) == 0) {
144 } else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) {
149 if (priv->out != NULL &&
151 priv->flags & NGNAT_ADDR_DEFINED)
152 priv->flags |= NGNAT_READY;
158 ng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook)
160 const priv_p priv = NG_NODE_PRIVATE(node);
161 struct ng_mesg *resp = NULL;
165 NGI_GET_MSG(item, msg);
167 switch (msg->header.typecookie) {
169 switch (msg->header.cmd) {
170 case NGM_NAT_SET_IPADDR:
172 struct in_addr *const ia = (struct in_addr *)msg->data;
174 if (msg->header.arglen < sizeof(*ia)) {
179 LibAliasSetAddress(priv->lib, *ia);
181 priv->flags |= NGNAT_ADDR_DEFINED;
182 if (priv->out != NULL &&
184 priv->flags |= NGNAT_READY;
188 error = EINVAL; /* unknown command */
193 error = EINVAL; /* unknown cookie type */
197 NG_RESPOND_MSG(error, node, item, resp);
203 ng_nat_rcvdata(hook_p hook, item_p item )
205 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
211 if (!(priv->flags & NGNAT_READY)) {
218 if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) {
219 NGI_M(item) = NULL; /* avoid double free */
227 ip = mtod(m, struct ip *);
229 KASSERT(m->m_pkthdr.len == ntohs(ip->ip_len),
230 ("ng_nat: ip_len != m_pkthdr.len"));
232 if (hook == priv->in) {
233 rval = LibAliasIn(priv->lib, c, MCLBYTES);
234 if (rval != PKT_ALIAS_OK) {
235 printf("in %u\n", rval);
239 m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len);
240 NG_FWD_ITEM_HOOK(error, item, priv->out);
241 } else if (hook == priv->out) {
242 rval = LibAliasOut(priv->lib, c, MCLBYTES);
243 if (rval != PKT_ALIAS_OK) {
244 printf("out %u\n", rval);
248 m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len);
249 NG_FWD_ITEM_HOOK(error, item, priv->in);
251 panic("ng_nat: unknown hook!\n");
257 ng_nat_shutdown(node_p node)
259 const priv_p priv = NG_NODE_PRIVATE(node);
261 NG_NODE_SET_PRIVATE(node, NULL);
263 LibAliasUninit(priv->lib);
264 FREE(priv, M_NETGRAPH);
270 ng_nat_disconnect(hook_p hook)
272 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
274 priv->flags &= ~NGNAT_READY;
276 if (hook == priv->out)
278 if (hook == priv->in)
281 if (priv->out == NULL && priv->in == NULL)
282 ng_rmnode_self(NG_HOOK_NODE(hook));
288 * m_megapullup() function is a big hack.
290 * It allocates an mbuf with cluster and copies the whole
291 * chain into cluster, so that it is all contigous and the
292 * whole packet can be accessed via char pointer.
294 * This is required, because libalias doesn't have idea
298 m_megapullup(struct mbuf *m, int len)
306 if ((mcl = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR)) == NULL)
309 cp = mtod(mcl, caddr_t);
310 m_copydata(m, 0, len, cp);
311 m_move_pkthdr(mcl, m);
312 mcl->m_len = mcl->m_pkthdr.len;