FreeBSD/Linux Kernel Cross Reference
sys/netgraph/ng_nat.c
1 /*-
2 * Copyright 2005, Gleb Smirnoff <glebius@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
13 *
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
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: releng/6.1/sys/netgraph/ng_nat.c 147625 2005-06-27 07:39:13Z glebius $
27 */
28
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/mbuf.h>
33 #include <sys/malloc.h>
34 #include <sys/ctype.h>
35 #include <sys/errno.h>
36 #include <sys/syslog.h>
37
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>
44
45 #include <netinet/libalias/alias.h>
46
47 #include <netgraph/ng_message.h>
48 #include <netgraph/ng_parse.h>
49 #include <netgraph/ng_nat.h>
50 #include <netgraph/netgraph.h>
51
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;
58
59 static struct mbuf * m_megapullup(struct mbuf *, int);
60
61 /* List of commands and how to convert arguments to/from ASCII. */
62 static const struct ng_cmdlist ng_nat_cmdlist[] = {
63 {
64 NGM_NAT_COOKIE,
65 NGM_NAT_SET_IPADDR,
66 "setaliasaddr",
67 &ng_parse_ipaddr_type,
68 NULL
69 },
70 { 0 }
71 };
72
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,
84 };
85 NETGRAPH_INIT(nat, &typestruct);
86 MODULE_DEPEND(ng_nat, libalias, 1, 1, 1);
87
88 /* Information we store for each node. */
89 struct ng_priv_priv {
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 */
95 };
96 typedef struct ng_priv_priv *priv_p;
97
98 /* Values of flags */
99 #define NGNAT_READY 0x1 /* We have everything to work */
100 #define NGNAT_ADDR_DEFINED 0x2 /* NGM_NAT_SET_IPADDR happened */
101
102 static int
103 ng_nat_constructor(node_p node)
104 {
105 priv_p priv;
106
107 /* Initialize private descriptor. */
108 MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH,
109 M_NOWAIT | M_ZERO);
110 if (priv == NULL)
111 return (ENOMEM);
112
113 /* Init aliasing engine. */
114 priv->lib = LibAliasInit(NULL);
115 if (priv->lib == NULL) {
116 FREE(priv, M_NETGRAPH);
117 return (ENOMEM);
118 }
119
120 /* Set same ports on. */
121 (void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS,
122 PKT_ALIAS_SAME_PORTS);
123
124 /* Link structs together. */
125 NG_NODE_SET_PRIVATE(node, priv);
126 priv->node = node;
127
128 /*
129 * libalias is not thread safe, so our node
130 * must be single threaded.
131 */
132 NG_NODE_FORCE_WRITER(node);
133
134 return (0);
135 }
136
137 static int
138 ng_nat_newhook(node_p node, hook_p hook, const char *name)
139 {
140 const priv_p priv = NG_NODE_PRIVATE(node);
141
142 if (strcmp(name, NG_NAT_HOOK_IN) == 0) {
143 priv->in = hook;
144 } else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) {
145 priv->out = hook;
146 } else
147 return (EINVAL);
148
149 if (priv->out != NULL &&
150 priv->in != NULL &&
151 priv->flags & NGNAT_ADDR_DEFINED)
152 priv->flags |= NGNAT_READY;
153
154 return(0);
155 }
156
157 static int
158 ng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook)
159 {
160 const priv_p priv = NG_NODE_PRIVATE(node);
161 struct ng_mesg *resp = NULL;
162 struct ng_mesg *msg;
163 int error = 0;
164
165 NGI_GET_MSG(item, msg);
166
167 switch (msg->header.typecookie) {
168 case NGM_NAT_COOKIE:
169 switch (msg->header.cmd) {
170 case NGM_NAT_SET_IPADDR:
171 {
172 struct in_addr *const ia = (struct in_addr *)msg->data;
173
174 if (msg->header.arglen < sizeof(*ia)) {
175 error = EINVAL;
176 break;
177 }
178
179 LibAliasSetAddress(priv->lib, *ia);
180
181 priv->flags |= NGNAT_ADDR_DEFINED;
182 if (priv->out != NULL &&
183 priv->in != NULL)
184 priv->flags |= NGNAT_READY;
185 }
186 break;
187 default:
188 error = EINVAL; /* unknown command */
189 break;
190 }
191 break;
192 default:
193 error = EINVAL; /* unknown cookie type */
194 break;
195 }
196
197 NG_RESPOND_MSG(error, node, item, resp);
198 NG_FREE_MSG(msg);
199 return (error);
200 }
201
202 static int
203 ng_nat_rcvdata(hook_p hook, item_p item )
204 {
205 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
206 struct mbuf *m;
207 struct ip *ip;
208 int rval, error = 0;
209 char *c;
210
211 if (!(priv->flags & NGNAT_READY)) {
212 NG_FREE_ITEM(item);
213 return (ENXIO);
214 }
215
216 m = NGI_M(item);
217
218 if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) {
219 NGI_M(item) = NULL; /* avoid double free */
220 NG_FREE_ITEM(item);
221 return (ENOBUFS);
222 }
223
224 NGI_M(item) = m;
225
226 c = mtod(m, char *);
227 ip = mtod(m, struct ip *);
228
229 KASSERT(m->m_pkthdr.len == ntohs(ip->ip_len),
230 ("ng_nat: ip_len != m_pkthdr.len"));
231
232 if (hook == priv->in) {
233 rval = LibAliasIn(priv->lib, c, MCLBYTES);
234 if (rval != PKT_ALIAS_OK) {
235 NG_FREE_ITEM(item);
236 return (EINVAL);
237 }
238 } else if (hook == priv->out) {
239 rval = LibAliasOut(priv->lib, c, MCLBYTES);
240 if (rval != PKT_ALIAS_OK) {
241 NG_FREE_ITEM(item);
242 return (EINVAL);
243 }
244 } else
245 panic("ng_nat: unknown hook!\n");
246
247 m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len);
248
249 if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
250 ip->ip_p == IPPROTO_TCP) {
251 struct tcphdr *th = (struct tcphdr *)(ip + 1);
252
253 /*
254 * Here is our terrible HACK.
255 *
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.
265 *
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.
273 */
274
275 if (th->th_x2) {
276 th->th_x2 = 0;
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)));
281
282 if ((m->m_pkthdr.csum_flags & CSUM_TCP) == 0) {
283 m->m_pkthdr.csum_data = offsetof(struct tcphdr,
284 th_sum);
285 in_delayed_cksum(m);
286 }
287 ip->ip_len = htons(ip->ip_len);
288 }
289 }
290
291 if (hook == priv->in)
292 NG_FWD_ITEM_HOOK(error, item, priv->out);
293 else
294 NG_FWD_ITEM_HOOK(error, item, priv->in);
295
296 return (error);
297 }
298
299 static int
300 ng_nat_shutdown(node_p node)
301 {
302 const priv_p priv = NG_NODE_PRIVATE(node);
303
304 NG_NODE_SET_PRIVATE(node, NULL);
305 NG_NODE_UNREF(node);
306 LibAliasUninit(priv->lib);
307 FREE(priv, M_NETGRAPH);
308
309 return (0);
310 }
311
312 static int
313 ng_nat_disconnect(hook_p hook)
314 {
315 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
316
317 priv->flags &= ~NGNAT_READY;
318
319 if (hook == priv->out)
320 priv->out = NULL;
321 if (hook == priv->in)
322 priv->in = NULL;
323
324 if (priv->out == NULL && priv->in == NULL)
325 ng_rmnode_self(NG_HOOK_NODE(hook));
326
327 return (0);
328 }
329
330 /*
331 * m_megapullup() function is a big hack.
332 *
333 * It allocates an mbuf with cluster and copies the whole
334 * chain into cluster, so that it is all contigous and the
335 * whole packet can be accessed via char pointer.
336 *
337 * This is required, because libalias doesn't have idea
338 * about mbufs.
339 */
340 static struct mbuf *
341 m_megapullup(struct mbuf *m, int len)
342 {
343 struct mbuf *mcl;
344 caddr_t cp;
345
346 if (len > MCLBYTES)
347 goto bad;
348
349 if ((mcl = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR)) == NULL)
350 goto bad;
351
352 cp = mtod(mcl, caddr_t);
353 m_copydata(m, 0, len, cp);
354 m_move_pkthdr(mcl, m);
355 mcl->m_len = mcl->m_pkthdr.len;
356 m_freem(m);
357
358 return (mcl);
359 bad:
360 m_freem(m);
361 return (NULL);
362 }
Cache object: 2ae6f0a64da56e30a7bd51ff4904639a
|