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.3/sys/netgraph/ng_nat.c 171592 2007-07-25 19:10:39Z mav $
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 static unsigned int ng_nat_translate_flags(unsigned int x);
62
63 /* Parse type for struct ng_nat_mode. */
64 static const struct ng_parse_struct_field ng_nat_mode_fields[]
65 = NG_NAT_MODE_INFO;
66 static const struct ng_parse_type ng_nat_mode_type = {
67 &ng_parse_struct_type,
68 ng_nat_mode_fields
69 };
70
71 /* List of commands and how to convert arguments to/from ASCII. */
72 static const struct ng_cmdlist ng_nat_cmdlist[] = {
73 {
74 NGM_NAT_COOKIE,
75 NGM_NAT_SET_IPADDR,
76 "setaliasaddr",
77 &ng_parse_ipaddr_type,
78 NULL
79 },
80 {
81 NGM_NAT_COOKIE,
82 NGM_NAT_SET_MODE,
83 "setmode",
84 &ng_nat_mode_type,
85 NULL
86 },
87 {
88 NGM_NAT_COOKIE,
89 NGM_NAT_SET_TARGET,
90 "settarget",
91 &ng_parse_ipaddr_type,
92 NULL
93 },
94 { 0 }
95 };
96
97 /* Netgraph node type descriptor. */
98 static struct ng_type typestruct = {
99 .version = NG_ABI_VERSION,
100 .name = NG_NAT_NODE_TYPE,
101 .constructor = ng_nat_constructor,
102 .rcvmsg = ng_nat_rcvmsg,
103 .shutdown = ng_nat_shutdown,
104 .newhook = ng_nat_newhook,
105 .rcvdata = ng_nat_rcvdata,
106 .disconnect = ng_nat_disconnect,
107 .cmdlist = ng_nat_cmdlist,
108 };
109 NETGRAPH_INIT(nat, &typestruct);
110 MODULE_DEPEND(ng_nat, libalias, 1, 1, 1);
111
112 /* Information we store for each node. */
113 struct ng_nat_priv {
114 node_p node; /* back pointer to node */
115 hook_p in; /* hook for demasquerading */
116 hook_p out; /* hook for masquerading */
117 struct libalias *lib; /* libalias handler */
118 uint32_t flags; /* status flags */
119 };
120 typedef struct ng_nat_priv *priv_p;
121
122 /* Values of flags */
123 #define NGNAT_CONNECTED 0x1 /* We have both hooks connected */
124 #define NGNAT_ADDR_DEFINED 0x2 /* NGM_NAT_SET_IPADDR happened */
125
126 static int
127 ng_nat_constructor(node_p node)
128 {
129 priv_p priv;
130
131 /* Initialize private descriptor. */
132 MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH,
133 M_NOWAIT | M_ZERO);
134 if (priv == NULL)
135 return (ENOMEM);
136
137 /* Init aliasing engine. */
138 priv->lib = LibAliasInit(NULL);
139 if (priv->lib == NULL) {
140 FREE(priv, M_NETGRAPH);
141 return (ENOMEM);
142 }
143
144 /* Set same ports on. */
145 (void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS,
146 PKT_ALIAS_SAME_PORTS);
147
148 /* Link structs together. */
149 NG_NODE_SET_PRIVATE(node, priv);
150 priv->node = node;
151
152 /*
153 * libalias is not thread safe, so our node
154 * must be single threaded.
155 */
156 NG_NODE_FORCE_WRITER(node);
157
158 return (0);
159 }
160
161 static int
162 ng_nat_newhook(node_p node, hook_p hook, const char *name)
163 {
164 const priv_p priv = NG_NODE_PRIVATE(node);
165
166 if (strcmp(name, NG_NAT_HOOK_IN) == 0) {
167 priv->in = hook;
168 } else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) {
169 priv->out = hook;
170 } else
171 return (EINVAL);
172
173 if (priv->out != NULL &&
174 priv->in != NULL)
175 priv->flags |= NGNAT_CONNECTED;
176
177 return(0);
178 }
179
180 static int
181 ng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook)
182 {
183 const priv_p priv = NG_NODE_PRIVATE(node);
184 struct ng_mesg *resp = NULL;
185 struct ng_mesg *msg;
186 int error = 0;
187
188 NGI_GET_MSG(item, msg);
189
190 switch (msg->header.typecookie) {
191 case NGM_NAT_COOKIE:
192 switch (msg->header.cmd) {
193 case NGM_NAT_SET_IPADDR:
194 {
195 struct in_addr *const ia = (struct in_addr *)msg->data;
196
197 if (msg->header.arglen < sizeof(*ia)) {
198 error = EINVAL;
199 break;
200 }
201
202 LibAliasSetAddress(priv->lib, *ia);
203
204 priv->flags |= NGNAT_ADDR_DEFINED;
205 }
206 break;
207 case NGM_NAT_SET_MODE:
208 {
209 struct ng_nat_mode *const mode =
210 (struct ng_nat_mode *)msg->data;
211
212 if (msg->header.arglen < sizeof(*mode)) {
213 error = EINVAL;
214 break;
215 }
216
217 if (LibAliasSetMode(priv->lib,
218 ng_nat_translate_flags(mode->flags),
219 ng_nat_translate_flags(mode->mask)) < 0) {
220 error = ENOMEM;
221 break;
222 }
223 }
224 break;
225 case NGM_NAT_SET_TARGET:
226 {
227 struct in_addr *const ia = (struct in_addr *)msg->data;
228
229 if (msg->header.arglen < sizeof(*ia)) {
230 error = EINVAL;
231 break;
232 }
233
234 LibAliasSetTarget(priv->lib, *ia);
235 }
236 break;
237 default:
238 error = EINVAL; /* unknown command */
239 break;
240 }
241 break;
242 default:
243 error = EINVAL; /* unknown cookie type */
244 break;
245 }
246
247 NG_RESPOND_MSG(error, node, item, resp);
248 NG_FREE_MSG(msg);
249 return (error);
250 }
251
252 static int
253 ng_nat_rcvdata(hook_p hook, item_p item )
254 {
255 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
256 struct mbuf *m;
257 struct ip *ip;
258 int rval, error = 0;
259 char *c;
260
261 /* We have no required hooks. */
262 if (!(priv->flags & NGNAT_CONNECTED)) {
263 NG_FREE_ITEM(item);
264 return (ENXIO);
265 }
266
267 /* We have no alias address yet to do anything. */
268 if (!(priv->flags & NGNAT_ADDR_DEFINED))
269 goto send;
270
271 m = NGI_M(item);
272
273 if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) {
274 NGI_M(item) = NULL; /* avoid double free */
275 NG_FREE_ITEM(item);
276 return (ENOBUFS);
277 }
278
279 NGI_M(item) = m;
280
281 c = mtod(m, char *);
282 ip = mtod(m, struct ip *);
283
284 KASSERT(m->m_pkthdr.len == ntohs(ip->ip_len),
285 ("ng_nat: ip_len != m_pkthdr.len"));
286
287 if (hook == priv->in) {
288 rval = LibAliasIn(priv->lib, c, MCLBYTES);
289 if (rval != PKT_ALIAS_OK &&
290 rval != PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
291 NG_FREE_ITEM(item);
292 return (EINVAL);
293 }
294 } else if (hook == priv->out) {
295 rval = LibAliasOut(priv->lib, c, MCLBYTES);
296 if (rval != PKT_ALIAS_OK) {
297 NG_FREE_ITEM(item);
298 return (EINVAL);
299 }
300 } else
301 panic("ng_nat: unknown hook!\n");
302
303 m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len);
304
305 if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
306 ip->ip_p == IPPROTO_TCP) {
307 struct tcphdr *th = (struct tcphdr *)((caddr_t)ip +
308 (ip->ip_hl << 2));
309
310 /*
311 * Here is our terrible HACK.
312 *
313 * Sometimes LibAlias edits contents of TCP packet.
314 * In this case it needs to recompute full TCP
315 * checksum. However, the problem is that LibAlias
316 * doesn't have any idea about checksum offloading
317 * in kernel. To workaround this, we do not do
318 * checksumming in LibAlias, but only mark the
319 * packets in th_x2 field. If we receive a marked
320 * packet, we calculate correct checksum for it
321 * aware of offloading.
322 *
323 * Why do I do such a terrible hack instead of
324 * recalculating checksum for each packet?
325 * Because the previous checksum was not checked!
326 * Recalculating checksums for EVERY packet will
327 * hide ALL transmission errors. Yes, marked packets
328 * still suffer from this problem. But, sigh, natd(8)
329 * has this problem, too.
330 */
331
332 if (th->th_x2) {
333 th->th_x2 = 0;
334 ip->ip_len = ntohs(ip->ip_len);
335 th->th_sum = in_pseudo(ip->ip_src.s_addr,
336 ip->ip_dst.s_addr, htons(IPPROTO_TCP +
337 ip->ip_len - (ip->ip_hl << 2)));
338
339 if ((m->m_pkthdr.csum_flags & CSUM_TCP) == 0) {
340 m->m_pkthdr.csum_data = offsetof(struct tcphdr,
341 th_sum);
342 in_delayed_cksum(m);
343 }
344 ip->ip_len = htons(ip->ip_len);
345 }
346 }
347
348 send:
349 if (hook == priv->in)
350 NG_FWD_ITEM_HOOK(error, item, priv->out);
351 else
352 NG_FWD_ITEM_HOOK(error, item, priv->in);
353
354 return (error);
355 }
356
357 static int
358 ng_nat_shutdown(node_p node)
359 {
360 const priv_p priv = NG_NODE_PRIVATE(node);
361
362 NG_NODE_SET_PRIVATE(node, NULL);
363 NG_NODE_UNREF(node);
364 LibAliasUninit(priv->lib);
365 FREE(priv, M_NETGRAPH);
366
367 return (0);
368 }
369
370 static int
371 ng_nat_disconnect(hook_p hook)
372 {
373 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
374
375 priv->flags &= ~NGNAT_CONNECTED;
376
377 if (hook == priv->out)
378 priv->out = NULL;
379 if (hook == priv->in)
380 priv->in = NULL;
381
382 if (priv->out == NULL && priv->in == NULL)
383 ng_rmnode_self(NG_HOOK_NODE(hook));
384
385 return (0);
386 }
387
388 static unsigned int
389 ng_nat_translate_flags(unsigned int x)
390 {
391 unsigned int res = 0;
392
393 #ifdef PKT_ALIAS_LOG
394 if (x & NG_NAT_LOG)
395 res |= PKT_ALIAS_LOG;
396 #endif
397 if (x & NG_NAT_DENY_INCOMING)
398 res |= PKT_ALIAS_DENY_INCOMING;
399 if (x & NG_NAT_SAME_PORTS)
400 res |= PKT_ALIAS_SAME_PORTS;
401 if (x & NG_NAT_UNREGISTERED_ONLY)
402 res |= PKT_ALIAS_UNREGISTERED_ONLY;
403 if (x & NG_NAT_RESET_ON_ADDR_CHANGE)
404 res |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
405 if (x & NG_NAT_PROXY_ONLY)
406 res |= PKT_ALIAS_PROXY_ONLY;
407 if (x & NG_NAT_REVERSE)
408 res |= PKT_ALIAS_REVERSE;
409
410 return (res);
411 }
412
413 /*
414 * m_megapullup() function is a big hack.
415 *
416 * It allocates an mbuf with cluster and copies the whole
417 * chain into cluster, so that it is all contigous and the
418 * whole packet can be accessed via char pointer.
419 *
420 * This is required, because libalias doesn't have idea
421 * about mbufs.
422 */
423 static struct mbuf *
424 m_megapullup(struct mbuf *m, int len)
425 {
426 struct mbuf *mcl;
427 caddr_t cp;
428
429 if (len > MCLBYTES)
430 goto bad;
431
432 if ((mcl = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR)) == NULL)
433 goto bad;
434
435 cp = mtod(mcl, caddr_t);
436 m_copydata(m, 0, len, cp);
437 m_move_pkthdr(mcl, m);
438 mcl->m_len = mcl->m_pkthdr.len;
439 m_freem(m);
440
441 return (mcl);
442 bad:
443 m_freem(m);
444 return (NULL);
445 }
Cache object: 67ec5a0b7cd57630b2917ba9041cea0a
|