]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netgraph/ng_nat.c
This commit was generated by cvs2svn to compensate for changes in r166124,
[FreeBSD/FreeBSD.git] / 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$
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 /* List of commands and how to convert arguments to/from ASCII. */
60 static const struct ng_cmdlist ng_nat_cmdlist[] = {
61         {
62           NGM_NAT_COOKIE,
63           NGM_NAT_SET_IPADDR,
64           "setaliasaddr",
65           &ng_parse_ipaddr_type,
66           NULL
67         },
68         { 0 }
69 };
70
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,
82 };
83 NETGRAPH_INIT(nat, &typestruct);
84 MODULE_DEPEND(ng_nat, libalias, 1, 1, 1);
85
86 /* Information we store for each node. */
87 struct ng_nat_priv {
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 */
93 };
94 typedef struct ng_nat_priv *priv_p;
95
96 /* Values of flags */
97 #define NGNAT_READY             0x1     /* We have everything to work */
98 #define NGNAT_ADDR_DEFINED      0x2     /* NGM_NAT_SET_IPADDR happened */
99
100 static int
101 ng_nat_constructor(node_p node)
102 {
103         priv_p priv;
104
105         /* Initialize private descriptor. */
106         MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH,
107                 M_NOWAIT | M_ZERO);
108         if (priv == NULL)
109                 return (ENOMEM);
110
111         /* Init aliasing engine. */
112         priv->lib = LibAliasInit(NULL);
113         if (priv->lib == NULL) {
114                 FREE(priv, M_NETGRAPH);
115                 return (ENOMEM);
116         }
117
118         /* Set same ports on. */
119         (void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS,
120             PKT_ALIAS_SAME_PORTS);
121
122         /* Link structs together. */
123         NG_NODE_SET_PRIVATE(node, priv);
124         priv->node = node;
125
126         /*
127          * libalias is not thread safe, so our node
128          * must be single threaded.
129          */
130         NG_NODE_FORCE_WRITER(node);
131
132         return (0);
133 }
134
135 static int
136 ng_nat_newhook(node_p node, hook_p hook, const char *name)
137 {
138         const priv_p priv = NG_NODE_PRIVATE(node);
139
140         if (strcmp(name, NG_NAT_HOOK_IN) == 0) {
141                 priv->in = hook;
142         } else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) {
143                 priv->out = hook;
144         } else
145                 return (EINVAL);
146
147         if (priv->out != NULL &&
148             priv->in != NULL &&
149             priv->flags & NGNAT_ADDR_DEFINED)
150                 priv->flags |= NGNAT_READY;
151
152         return(0);
153 }
154
155 static int
156 ng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook)
157 {
158         const priv_p priv = NG_NODE_PRIVATE(node);
159         struct ng_mesg *resp = NULL;
160         struct ng_mesg *msg;
161         int error = 0;
162
163         NGI_GET_MSG(item, msg);
164
165         switch (msg->header.typecookie) {
166         case NGM_NAT_COOKIE:
167                 switch (msg->header.cmd) {
168                 case NGM_NAT_SET_IPADDR:
169                     {
170                         struct in_addr *const ia = (struct in_addr *)msg->data;
171
172                         if (msg->header.arglen < sizeof(*ia)) {
173                                 error = EINVAL;
174                                 break;
175                         }
176
177                         LibAliasSetAddress(priv->lib, *ia);
178
179                         priv->flags |= NGNAT_ADDR_DEFINED;
180                         if (priv->out != NULL &&
181                             priv->in != NULL)
182                                 priv->flags |= NGNAT_READY;
183                     }
184                         break;
185                 default:
186                         error = EINVAL;         /* unknown command */
187                         break;
188                 }
189                 break;
190         default:
191                 error = EINVAL;                 /* unknown cookie type */
192                 break;
193         }
194
195         NG_RESPOND_MSG(error, node, item, resp);
196         NG_FREE_MSG(msg);
197         return (error);
198 }
199
200 static int
201 ng_nat_rcvdata(hook_p hook, item_p item )
202 {
203         const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
204         struct mbuf     *m;
205         struct ip       *ip;
206         int rval, error = 0;
207         char *c;
208
209         if (!(priv->flags & NGNAT_READY)) {
210                 NG_FREE_ITEM(item);
211                 return (ENXIO);
212         }
213
214         m = NGI_M(item);
215
216         if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) {
217                 NGI_M(item) = NULL;     /* avoid double free */
218                 NG_FREE_ITEM(item);
219                 return (ENOBUFS);
220         }
221
222         NGI_M(item) = m;
223
224         c = mtod(m, char *);
225         ip = mtod(m, struct ip *);
226
227         KASSERT(m->m_pkthdr.len == ntohs(ip->ip_len),
228             ("ng_nat: ip_len != m_pkthdr.len"));
229
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) {
234                         NG_FREE_ITEM(item);
235                         return (EINVAL);
236                 }
237         } else if (hook == priv->out) {
238                 rval = LibAliasOut(priv->lib, c, MCLBYTES);
239                 if (rval != PKT_ALIAS_OK) {
240                         NG_FREE_ITEM(item);
241                         return (EINVAL);
242                 }
243         } else
244                 panic("ng_nat: unknown hook!\n");
245
246         m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len);
247
248         if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
249             ip->ip_p == IPPROTO_TCP) {
250                 struct tcphdr *th = (struct tcphdr *)((caddr_t)ip +
251                     (ip->ip_hl << 2));
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