]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netgraph/ng_nat.c
Add devctl_process_running() so that power management system driver
[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 static unsigned int     ng_nat_translate_flags(unsigned int x);
60
61 /* Parse type for struct ng_nat_mode. */
62 static const struct ng_parse_struct_field ng_nat_mode_fields[]
63         = NG_NAT_MODE_INFO;
64 static const struct ng_parse_type ng_nat_mode_type = {
65         &ng_parse_struct_type,
66         ng_nat_mode_fields
67 };
68
69 /* List of commands and how to convert arguments to/from ASCII. */
70 static const struct ng_cmdlist ng_nat_cmdlist[] = {
71         {
72           NGM_NAT_COOKIE,
73           NGM_NAT_SET_IPADDR,
74           "setaliasaddr",
75           &ng_parse_ipaddr_type,
76           NULL
77         },
78         {
79           NGM_NAT_COOKIE,
80           NGM_NAT_SET_MODE,
81           "setmode",
82           &ng_nat_mode_type,
83           NULL
84         },
85         {
86           NGM_NAT_COOKIE,
87           NGM_NAT_SET_TARGET,
88           "settarget",
89           &ng_parse_ipaddr_type,
90           NULL
91         },
92         { 0 }
93 };
94
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,
106 };
107 NETGRAPH_INIT(nat, &typestruct);
108 MODULE_DEPEND(ng_nat, libalias, 1, 1, 1);
109
110 /* Information we store for each node. */
111 struct ng_nat_priv {
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 */
117 };
118 typedef struct ng_nat_priv *priv_p;
119
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 */
123
124 static int
125 ng_nat_constructor(node_p node)
126 {
127         priv_p priv;
128
129         /* Initialize private descriptor. */
130         MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH,
131                 M_NOWAIT | M_ZERO);
132         if (priv == NULL)
133                 return (ENOMEM);
134
135         /* Init aliasing engine. */
136         priv->lib = LibAliasInit(NULL);
137         if (priv->lib == NULL) {
138                 FREE(priv, M_NETGRAPH);
139                 return (ENOMEM);
140         }
141
142         /* Set same ports on. */
143         (void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS,
144             PKT_ALIAS_SAME_PORTS);
145
146         /* Link structs together. */
147         NG_NODE_SET_PRIVATE(node, priv);
148         priv->node = node;
149
150         /*
151          * libalias is not thread safe, so our node
152          * must be single threaded.
153          */
154         NG_NODE_FORCE_WRITER(node);
155
156         return (0);
157 }
158
159 static int
160 ng_nat_newhook(node_p node, hook_p hook, const char *name)
161 {
162         const priv_p priv = NG_NODE_PRIVATE(node);
163
164         if (strcmp(name, NG_NAT_HOOK_IN) == 0) {
165                 priv->in = hook;
166         } else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) {
167                 priv->out = hook;
168         } else
169                 return (EINVAL);
170
171         if (priv->out != NULL &&
172             priv->in != NULL)
173                 priv->flags |= NGNAT_CONNECTED;
174
175         return(0);
176 }
177
178 static int
179 ng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook)
180 {
181         const priv_p priv = NG_NODE_PRIVATE(node);
182         struct ng_mesg *resp = NULL;
183         struct ng_mesg *msg;
184         int error = 0;
185
186         NGI_GET_MSG(item, msg);
187
188         switch (msg->header.typecookie) {
189         case NGM_NAT_COOKIE:
190                 switch (msg->header.cmd) {
191                 case NGM_NAT_SET_IPADDR:
192                     {
193                         struct in_addr *const ia = (struct in_addr *)msg->data;
194
195                         if (msg->header.arglen < sizeof(*ia)) {
196                                 error = EINVAL;
197                                 break;
198                         }
199
200                         LibAliasSetAddress(priv->lib, *ia);
201
202                         priv->flags |= NGNAT_ADDR_DEFINED;
203                     }
204                         break;
205                 case NGM_NAT_SET_MODE:
206                     {
207                         struct ng_nat_mode *const mode = 
208                             (struct ng_nat_mode *)msg->data;
209
210                         if (msg->header.arglen < sizeof(*mode)) {
211                                 error = EINVAL;
212                                 break;
213                         }
214                         
215                         if (LibAliasSetMode(priv->lib, 
216                             ng_nat_translate_flags(mode->flags),
217                             ng_nat_translate_flags(mode->mask)) < 0) {
218                                 error = ENOMEM;
219                                 break;
220                         }
221                     }
222                         break;
223                 case NGM_NAT_SET_TARGET:
224                     {
225                         struct in_addr *const ia = (struct in_addr *)msg->data;
226
227                         if (msg->header.arglen < sizeof(*ia)) {
228                                 error = EINVAL;
229                                 break;
230                         }
231
232                         LibAliasSetTarget(priv->lib, *ia);
233                     }
234                         break;
235                 default:
236                         error = EINVAL;         /* unknown command */
237                         break;
238                 }
239                 break;
240         default:
241                 error = EINVAL;                 /* unknown cookie type */
242                 break;
243         }
244
245         NG_RESPOND_MSG(error, node, item, resp);
246         NG_FREE_MSG(msg);
247         return (error);
248 }
249
250 static int
251 ng_nat_rcvdata(hook_p hook, item_p item )
252 {
253         const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
254         struct mbuf     *m;
255         struct ip       *ip;
256         int rval, error = 0;
257         char *c;
258
259         /* We have no required hooks. */
260         if (!(priv->flags & NGNAT_CONNECTED)) {
261                 NG_FREE_ITEM(item);
262                 return (ENXIO);
263         }
264
265         /* We have no alias address yet to do anything. */
266         if (!(priv->flags & NGNAT_ADDR_DEFINED))
267                 goto send;
268
269         m = NGI_M(item);
270
271         if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) {
272                 NGI_M(item) = NULL;     /* avoid double free */
273                 NG_FREE_ITEM(item);
274                 return (ENOBUFS);
275         }
276
277         NGI_M(item) = m;
278
279         c = mtod(m, char *);
280         ip = mtod(m, struct ip *);
281
282         KASSERT(m->m_pkthdr.len == ntohs(ip->ip_len),
283             ("ng_nat: ip_len != m_pkthdr.len"));
284
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) {
289                         NG_FREE_ITEM(item);
290                         return (EINVAL);
291                 }
292         } else if (hook == priv->out) {
293                 rval = LibAliasOut(priv->lib, c, MCLBYTES);
294                 if (rval != PKT_ALIAS_OK) {
295                         NG_FREE_ITEM(item);
296                         return (EINVAL);
297                 }
298         } else
299                 panic("ng_nat: unknown hook!\n");
300
301         m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len);
302
303         if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
304             ip->ip_p == IPPROTO_TCP) {
305                 struct tcphdr *th = (struct tcphdr *)((caddr_t)ip +
306                     (ip->ip_hl << 2));
307
308                 /*
309                  * Here is our terrible HACK.
310                  *
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.
320                  *
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.
328                  */
329
330                 if (th->th_x2) {
331                         th->th_x2 = 0;
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)));
336         
337                         if ((m->m_pkthdr.csum_flags & CSUM_TCP) == 0) {
338                                 m->m_pkthdr.csum_data = offsetof(struct tcphdr,
339                                     th_sum);
340                                 in_delayed_cksum(m);
341                         }
342                         ip->ip_len = htons(ip->ip_len);
343                 }
344         }
345
346 send:
347         if (hook == priv->in)
348                 NG_FWD_ITEM_HOOK(error, item, priv->out);
349         else
350                 NG_FWD_ITEM_HOOK(error, item, priv->in);
351
352         return (error);
353 }
354
355 static int
356 ng_nat_shutdown(node_p node)
357 {
358         const priv_p priv = NG_NODE_PRIVATE(node);
359
360         NG_NODE_SET_PRIVATE(node, NULL);
361         NG_NODE_UNREF(node);
362         LibAliasUninit(priv->lib);
363         FREE(priv, M_NETGRAPH);
364
365         return (0);
366 }
367
368 static int
369 ng_nat_disconnect(hook_p hook)
370 {
371         const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
372
373         priv->flags &= ~NGNAT_CONNECTED;
374
375         if (hook == priv->out)
376                 priv->out = NULL;
377         if (hook == priv->in)
378                 priv->in = NULL;
379
380         if (priv->out == NULL && priv->in == NULL)
381                 ng_rmnode_self(NG_HOOK_NODE(hook));
382
383         return (0);
384 }
385
386 static unsigned int
387 ng_nat_translate_flags(unsigned int x)
388 {
389         unsigned int    res = 0;
390         
391         if (x & NG_NAT_LOG)
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;
405
406         return (res);
407 }