e8beec032741d370a4d6acb2a1ca9a55abc6ce90
[dragonfly.git] / sys / netgraph7 / 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: src/sys/netgraph/ng_nat.c,v 1.12 2008/06/01 15:13:32 mav Exp $
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 "ng_message.h"
48 #include "ng_parse.h"
49 #include "ng_nat.h"
50 #include "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 /* Parse type for 'description' field in structs. */
70 static const struct ng_parse_fixedstring_info ng_nat_description_info
71         = { NG_NAT_DESC_LENGTH };
72 static const struct ng_parse_type ng_nat_description_type = {
73         &ng_parse_fixedstring_type,
74         &ng_nat_description_info
75 };
76
77 /* Parse type for struct ng_nat_redirect_port. */
78 static const struct ng_parse_struct_field ng_nat_redirect_port_fields[]
79         = NG_NAT_REDIRECT_PORT_TYPE_INFO(&ng_nat_description_type);
80 static const struct ng_parse_type ng_nat_redirect_port_type = {
81         &ng_parse_struct_type,
82         &ng_nat_redirect_port_fields
83 };
84
85 /* Parse type for struct ng_nat_redirect_addr. */
86 static const struct ng_parse_struct_field ng_nat_redirect_addr_fields[]
87         = NG_NAT_REDIRECT_ADDR_TYPE_INFO(&ng_nat_description_type);
88 static const struct ng_parse_type ng_nat_redirect_addr_type = {
89         &ng_parse_struct_type,
90         &ng_nat_redirect_addr_fields
91 };
92
93 /* Parse type for struct ng_nat_redirect_proto. */
94 static const struct ng_parse_struct_field ng_nat_redirect_proto_fields[]
95         = NG_NAT_REDIRECT_PROTO_TYPE_INFO(&ng_nat_description_type);
96 static const struct ng_parse_type ng_nat_redirect_proto_type = {
97         &ng_parse_struct_type,
98         &ng_nat_redirect_proto_fields
99 };
100
101 /* Parse type for struct ng_nat_add_server. */
102 static const struct ng_parse_struct_field ng_nat_add_server_fields[]
103         = NG_NAT_ADD_SERVER_TYPE_INFO;
104 static const struct ng_parse_type ng_nat_add_server_type = {
105         &ng_parse_struct_type,
106         &ng_nat_add_server_fields
107 };
108
109 /* Parse type for one struct ng_nat_listrdrs_entry. */
110 static const struct ng_parse_struct_field ng_nat_listrdrs_entry_fields[]
111         = NG_NAT_LISTRDRS_ENTRY_TYPE_INFO(&ng_nat_description_type);
112 static const struct ng_parse_type ng_nat_listrdrs_entry_type = {
113         &ng_parse_struct_type,
114         &ng_nat_listrdrs_entry_fields
115 };
116
117 /* Parse type for 'redirects' array in struct ng_nat_list_redirects. */
118 static int
119 ng_nat_listrdrs_ary_getLength(const struct ng_parse_type *type,
120         const u_char *start, const u_char *buf)
121 {
122         const struct ng_nat_list_redirects *lr;
123
124         lr = (const struct ng_nat_list_redirects *)
125             (buf - offsetof(struct ng_nat_list_redirects, redirects));
126         return lr->total_count;
127 }
128
129 static const struct ng_parse_array_info ng_nat_listrdrs_ary_info = {
130         &ng_nat_listrdrs_entry_type,
131         &ng_nat_listrdrs_ary_getLength,
132         NULL
133 };
134 static const struct ng_parse_type ng_nat_listrdrs_ary_type = {
135         &ng_parse_array_type,
136         &ng_nat_listrdrs_ary_info
137 };
138
139 /* Parse type for struct ng_nat_list_redirects. */
140 static const struct ng_parse_struct_field ng_nat_list_redirects_fields[]
141         = NG_NAT_LIST_REDIRECTS_TYPE_INFO(&ng_nat_listrdrs_ary_type);
142 static const struct ng_parse_type ng_nat_list_redirects_type = {
143         &ng_parse_struct_type,
144         &ng_nat_list_redirects_fields
145 };
146
147 /* List of commands and how to convert arguments to/from ASCII. */
148 static const struct ng_cmdlist ng_nat_cmdlist[] = {
149         {
150           NGM_NAT_COOKIE,
151           NGM_NAT_SET_IPADDR,
152           "setaliasaddr",
153           &ng_parse_ipaddr_type,
154           NULL
155         },
156         {
157           NGM_NAT_COOKIE,
158           NGM_NAT_SET_MODE,
159           "setmode",
160           &ng_nat_mode_type,
161           NULL
162         },
163         {
164           NGM_NAT_COOKIE,
165           NGM_NAT_SET_TARGET,
166           "settarget",
167           &ng_parse_ipaddr_type,
168           NULL
169         },
170         {
171           NGM_NAT_COOKIE,
172           NGM_NAT_REDIRECT_PORT,
173           "redirectport",
174           &ng_nat_redirect_port_type,
175           &ng_parse_uint32_type
176         },
177         {
178           NGM_NAT_COOKIE,
179           NGM_NAT_REDIRECT_ADDR,
180           "redirectaddr",
181           &ng_nat_redirect_addr_type,
182           &ng_parse_uint32_type
183         },
184         {
185           NGM_NAT_COOKIE,
186           NGM_NAT_REDIRECT_PROTO,
187           "redirectproto",
188           &ng_nat_redirect_proto_type,
189           &ng_parse_uint32_type
190         },
191         {
192           NGM_NAT_COOKIE,
193           NGM_NAT_REDIRECT_DYNAMIC,
194           "redirectdynamic",
195           &ng_parse_uint32_type,
196           NULL
197         },
198         {
199           NGM_NAT_COOKIE,
200           NGM_NAT_REDIRECT_DELETE,
201           "redirectdelete",
202           &ng_parse_uint32_type,
203           NULL
204         },
205         {
206           NGM_NAT_COOKIE,
207           NGM_NAT_ADD_SERVER,
208           "addserver",
209           &ng_nat_add_server_type,
210           NULL
211         },
212         {
213           NGM_NAT_COOKIE,
214           NGM_NAT_LIST_REDIRECTS,
215           "listredirects",
216           NULL,
217           &ng_nat_list_redirects_type
218         },
219         {
220           NGM_NAT_COOKIE,
221           NGM_NAT_PROXY_RULE,
222           "proxyrule",
223           &ng_parse_string_type,
224           NULL
225         },
226         { 0 }
227 };
228
229 /* Netgraph node type descriptor. */
230 static struct ng_type typestruct = {
231         .version =      NG_ABI_VERSION,
232         .name =         NG_NAT_NODE_TYPE,
233         .constructor =  ng_nat_constructor,
234         .rcvmsg =       ng_nat_rcvmsg,
235         .shutdown =     ng_nat_shutdown,
236         .newhook =      ng_nat_newhook,
237         .rcvdata =      ng_nat_rcvdata,
238         .disconnect =   ng_nat_disconnect,
239         .cmdlist =      ng_nat_cmdlist,
240 };
241 NETGRAPH_INIT(nat, &typestruct);
242 MODULE_DEPEND(ng_nat, libalias, 1, 1, 1);
243
244 /* Element for list of redirects. */
245 struct ng_nat_rdr_lst {
246         STAILQ_ENTRY(ng_nat_rdr_lst) entries;
247         struct alias_link       *lnk;
248         struct ng_nat_listrdrs_entry rdr;
249 };
250 STAILQ_HEAD(rdrhead, ng_nat_rdr_lst);
251
252 /* Information we store for each node. */
253 struct ng_nat_priv {
254         node_p          node;           /* back pointer to node */
255         hook_p          in;             /* hook for demasquerading */
256         hook_p          out;            /* hook for masquerading */
257         struct libalias *lib;           /* libalias handler */
258         uint32_t        flags;          /* status flags */
259         uint32_t        rdrcount;       /* number or redirects in list */
260         uint32_t        nextid;         /* for next in turn in list */
261         struct rdrhead  redirhead;      /* redirect list header */
262 };
263 typedef struct ng_nat_priv *priv_p;
264
265 /* Values of flags */
266 #define NGNAT_CONNECTED         0x1     /* We have both hooks connected */
267 #define NGNAT_ADDR_DEFINED      0x2     /* NGM_NAT_SET_IPADDR happened */
268
269 static int
270 ng_nat_constructor(node_p node)
271 {
272         priv_p priv;
273
274         /* Initialize private descriptor. */
275         priv = kmalloc(sizeof(*priv), M_NETGRAPH,
276                        M_WAITOK | M_NULLOK | M_ZERO);
277         if (priv == NULL)
278                 return (ENOMEM);
279
280         /* Init aliasing engine. */
281         priv->lib = LibAliasInit(NULL);
282         if (priv->lib == NULL) {
283                 kfree(priv, M_NETGRAPH);
284                 return (ENOMEM);
285         }
286
287         /* Set same ports on. */
288         (void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS,
289             PKT_ALIAS_SAME_PORTS);
290
291         /* Init redirects housekeeping. */
292         priv->rdrcount = 0;
293         priv->nextid = 1;
294         STAILQ_INIT(&priv->redirhead);
295
296         /* Link structs together. */
297         NG_NODE_SET_PRIVATE(node, priv);
298         priv->node = node;
299
300         /*
301          * libalias is not thread safe, so our node
302          * must be single threaded.
303          */
304         NG_NODE_FORCE_WRITER(node);
305
306         return (0);
307 }
308
309 static int
310 ng_nat_newhook(node_p node, hook_p hook, const char *name)
311 {
312         const priv_p priv = NG_NODE_PRIVATE(node);
313
314         if (strcmp(name, NG_NAT_HOOK_IN) == 0) {
315                 priv->in = hook;
316         } else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) {
317                 priv->out = hook;
318         } else
319                 return (EINVAL);
320
321         if (priv->out != NULL &&
322             priv->in != NULL)
323                 priv->flags |= NGNAT_CONNECTED;
324
325         return(0);
326 }
327
328 static int
329 ng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook)
330 {
331         const priv_p priv = NG_NODE_PRIVATE(node);
332         struct ng_mesg *resp = NULL;
333         struct ng_mesg *msg;
334         int error = 0;
335
336         NGI_GET_MSG(item, msg);
337
338         switch (msg->header.typecookie) {
339         case NGM_NAT_COOKIE:
340                 switch (msg->header.cmd) {
341                 case NGM_NAT_SET_IPADDR:
342                     {
343                         struct in_addr *const ia = (struct in_addr *)msg->data;
344
345                         if (msg->header.arglen < sizeof(*ia)) {
346                                 error = EINVAL;
347                                 break;
348                         }
349
350                         LibAliasSetAddress(priv->lib, *ia);
351
352                         priv->flags |= NGNAT_ADDR_DEFINED;
353                     }
354                         break;
355                 case NGM_NAT_SET_MODE:
356                     {
357                         struct ng_nat_mode *const mode = 
358                             (struct ng_nat_mode *)msg->data;
359
360                         if (msg->header.arglen < sizeof(*mode)) {
361                                 error = EINVAL;
362                                 break;
363                         }
364                         
365                         if (LibAliasSetMode(priv->lib, 
366                             ng_nat_translate_flags(mode->flags),
367                             ng_nat_translate_flags(mode->mask)) < 0) {
368                                 error = ENOMEM;
369                                 break;
370                         }
371                     }
372                         break;
373                 case NGM_NAT_SET_TARGET:
374                     {
375                         struct in_addr *const ia = (struct in_addr *)msg->data;
376
377                         if (msg->header.arglen < sizeof(*ia)) {
378                                 error = EINVAL;
379                                 break;
380                         }
381
382                         LibAliasSetTarget(priv->lib, *ia);
383                     }
384                         break;
385                 case NGM_NAT_REDIRECT_PORT:
386                     {
387                         struct ng_nat_rdr_lst *entry;
388                         struct ng_nat_redirect_port *const rp =
389                             (struct ng_nat_redirect_port *)msg->data;
390
391                         if (msg->header.arglen < sizeof(*rp)) {
392                                 error = EINVAL;
393                                 break;
394                         }
395
396                         if ((entry = kmalloc(sizeof(struct ng_nat_rdr_lst),
397                             M_NETGRAPH, M_WAITOK | M_NULLOK | M_ZERO)) == NULL) {
398                                 error = ENOMEM;
399                                 break;
400                         }
401
402                         /* Try actual redirect. */
403                         entry->lnk = LibAliasRedirectPort(priv->lib,
404                                 rp->local_addr, htons(rp->local_port),
405                                 rp->remote_addr, htons(rp->remote_port),
406                                 rp->alias_addr, htons(rp->alias_port),
407                                 rp->proto);
408
409                         if (entry->lnk == NULL) {
410                                 error = ENOMEM;
411                                 kfree(entry, M_NETGRAPH);
412                                 break;
413                         }
414
415                         /* Successful, save info in our internal list. */
416                         entry->rdr.local_addr = rp->local_addr;
417                         entry->rdr.alias_addr = rp->alias_addr;
418                         entry->rdr.remote_addr = rp->remote_addr;
419                         entry->rdr.local_port = rp->local_port;
420                         entry->rdr.alias_port = rp->alias_port;
421                         entry->rdr.remote_port = rp->remote_port;
422                         entry->rdr.proto = rp->proto;
423                         bcopy(rp->description, entry->rdr.description,
424                             NG_NAT_DESC_LENGTH);
425
426                         /* Safety precaution. */
427                         entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0';
428
429                         entry->rdr.id = priv->nextid++;
430                         priv->rdrcount++;
431
432                         /* Link to list of redirects. */
433                         STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries);
434
435                         /* Response with id of newly added entry. */
436                         NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_WAITOK | M_NULLOK);
437                         if (resp == NULL) {
438                                 error = ENOMEM;
439                                 break;
440                         }
441                         bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id));
442                     }
443                         break;
444                 case NGM_NAT_REDIRECT_ADDR:
445                     {
446                         struct ng_nat_rdr_lst *entry;
447                         struct ng_nat_redirect_addr *const ra =
448                             (struct ng_nat_redirect_addr *)msg->data;
449
450                         if (msg->header.arglen < sizeof(*ra)) {
451                                 error = EINVAL;
452                                 break;
453                         }
454
455                         if ((entry = kmalloc(sizeof(struct ng_nat_rdr_lst),
456                             M_NETGRAPH, M_WAITOK | M_NULLOK | M_ZERO)) == NULL) {
457                                 error = ENOMEM;
458                                 break;
459                         }
460
461                         /* Try actual redirect. */
462                         entry->lnk = LibAliasRedirectAddr(priv->lib,
463                                 ra->local_addr, ra->alias_addr);
464
465                         if (entry->lnk == NULL) {
466                                 error = ENOMEM;
467                                 kfree(entry, M_NETGRAPH);
468                                 break;
469                         }
470
471                         /* Successful, save info in our internal list. */
472                         entry->rdr.local_addr = ra->local_addr;
473                         entry->rdr.alias_addr = ra->alias_addr;
474                         entry->rdr.proto = NG_NAT_REDIRPROTO_ADDR;
475                         bcopy(ra->description, entry->rdr.description,
476                             NG_NAT_DESC_LENGTH);
477
478                         /* Safety precaution. */
479                         entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0';
480
481                         entry->rdr.id = priv->nextid++;
482                         priv->rdrcount++;
483
484                         /* Link to list of redirects. */
485                         STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries);
486
487                         /* Response with id of newly added entry. */
488                         NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_WAITOK | M_NULLOK);
489                         if (resp == NULL) {
490                                 error = ENOMEM;
491                                 break;
492                         }
493                         bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id));
494                     }
495                         break;
496                 case NGM_NAT_REDIRECT_PROTO:
497                     {
498                         struct ng_nat_rdr_lst *entry;
499                         struct ng_nat_redirect_proto *const rp =
500                             (struct ng_nat_redirect_proto *)msg->data;
501
502                         if (msg->header.arglen < sizeof(*rp)) {
503                                 error = EINVAL;
504                                 break;
505                         }
506
507                         if ((entry = kmalloc(sizeof(struct ng_nat_rdr_lst),
508                             M_NETGRAPH, M_WAITOK | M_NULLOK | M_ZERO)) == NULL) {
509                                 error = ENOMEM;
510                                 break;
511                         }
512
513                         /* Try actual redirect. */
514                         entry->lnk = LibAliasRedirectProto(priv->lib,
515                                 rp->local_addr, rp->remote_addr,
516                                 rp->alias_addr, rp->proto);
517
518                         if (entry->lnk == NULL) {
519                                 error = ENOMEM;
520                                 kfree(entry, M_NETGRAPH);
521                                 break;
522                         }
523
524                         /* Successful, save info in our internal list. */
525                         entry->rdr.local_addr = rp->local_addr;
526                         entry->rdr.alias_addr = rp->alias_addr;
527                         entry->rdr.remote_addr = rp->remote_addr;
528                         entry->rdr.proto = rp->proto;
529                         bcopy(rp->description, entry->rdr.description,
530                             NG_NAT_DESC_LENGTH);
531
532                         /* Safety precaution. */
533                         entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0';
534
535                         entry->rdr.id = priv->nextid++;
536                         priv->rdrcount++;
537
538                         /* Link to list of redirects. */
539                         STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries);
540
541                         /* Response with id of newly added entry. */
542                         NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_WAITOK | M_NULLOK);
543                         if (resp == NULL) {
544                                 error = ENOMEM;
545                                 break;
546                         }
547                         bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id));
548                     }
549                         break;
550                 case NGM_NAT_REDIRECT_DYNAMIC:
551                 case NGM_NAT_REDIRECT_DELETE:
552                     {
553                         struct ng_nat_rdr_lst *entry;
554                         uint32_t *const id = (uint32_t *)msg->data;
555
556                         if (msg->header.arglen < sizeof(*id)) {
557                                 error = EINVAL;
558                                 break;
559                         }
560
561                         /* Find entry with supplied id. */
562                         STAILQ_FOREACH(entry, &priv->redirhead, entries) {
563                                 if (entry->rdr.id == *id)
564                                         break;
565                         }
566
567                         /* Not found. */
568                         if (entry == NULL) {
569                                 error = ENOENT;
570                                 break;
571                         }
572
573                         if (msg->header.cmd == NGM_NAT_REDIRECT_DYNAMIC) {
574                                 if (LibAliasRedirectDynamic(priv->lib,
575                                     entry->lnk) == -1) {
576                                         error = ENOTTY; /* XXX Something better? */
577                                         break;
578                                 }
579                         } else {        /* NGM_NAT_REDIRECT_DELETE */
580                                 LibAliasRedirectDelete(priv->lib, entry->lnk);
581                         }
582
583                         /* Delete entry from our internal list. */
584                         priv->rdrcount--;
585                         STAILQ_REMOVE(&priv->redirhead, entry, ng_nat_rdr_lst, entries);
586                         kfree(entry, M_NETGRAPH);
587                     }
588                         break;
589                 case NGM_NAT_ADD_SERVER:
590                     {
591                         struct ng_nat_rdr_lst *entry;
592                         struct ng_nat_add_server *const as =
593                             (struct ng_nat_add_server *)msg->data;
594
595                         if (msg->header.arglen < sizeof(*as)) {
596                                 error = EINVAL;
597                                 break;
598                         }
599
600                         /* Find entry with supplied id. */
601                         STAILQ_FOREACH(entry, &priv->redirhead, entries) {
602                                 if (entry->rdr.id == as->id)
603                                         break;
604                         }
605
606                         /* Not found. */
607                         if (entry == NULL) {
608                                 error = ENOENT;
609                                 break;
610                         }
611
612                         if (LibAliasAddServer(priv->lib, entry->lnk,
613                             as->addr, htons(as->port)) == -1) {
614                                 error = ENOMEM;
615                                 break;
616                         }
617
618                         entry->rdr.lsnat++;
619                     }
620                         break;
621                 case NGM_NAT_LIST_REDIRECTS:
622                     {
623                         struct ng_nat_rdr_lst *entry;
624                         struct ng_nat_list_redirects *ary; 
625                         int i = 0;
626
627                         NG_MKRESPONSE(resp, msg, sizeof(*ary) +
628                             (priv->rdrcount) * sizeof(*entry), M_WAITOK | M_NULLOK);
629                         if (resp == NULL) {
630                                 error = ENOMEM;
631                                 break;
632                         }
633
634                         ary = (struct ng_nat_list_redirects *)resp->data;
635                         ary->total_count = priv->rdrcount;
636
637                         STAILQ_FOREACH(entry, &priv->redirhead, entries) {
638                                 bcopy(&entry->rdr, &ary->redirects[i++],
639                                     sizeof(struct ng_nat_listrdrs_entry));
640                         }
641                     }
642                         break;
643                 case NGM_NAT_PROXY_RULE:
644                     {
645                         char *cmd = (char *)msg->data;
646
647                         if (msg->header.arglen < 6) {
648                                 error = EINVAL;
649                                 break;
650                         }
651
652                         if (LibAliasProxyRule(priv->lib, cmd) != 0)
653                                 error = ENOMEM;
654                     }
655                         break;
656                 default:
657                         error = EINVAL;         /* unknown command */
658                         break;
659                 }
660                 break;
661         default:
662                 error = EINVAL;                 /* unknown cookie type */
663                 break;
664         }
665
666         NG_RESPOND_MSG(error, node, item, resp);
667         NG_FREE_MSG(msg);
668         return (error);
669 }
670
671 static int
672 ng_nat_rcvdata(hook_p hook, item_p item )
673 {
674         const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
675         struct mbuf     *m;
676         struct ip       *ip;
677         int rval, error = 0;
678         char *c;
679
680         /* We have no required hooks. */
681         if (!(priv->flags & NGNAT_CONNECTED)) {
682                 NG_FREE_ITEM(item);
683                 return (ENXIO);
684         }
685
686         /* We have no alias address yet to do anything. */
687         if (!(priv->flags & NGNAT_ADDR_DEFINED))
688                 goto send;
689
690         m = NGI_M(item);
691
692         if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) {
693                 NGI_M(item) = NULL;     /* avoid double free */
694                 NG_FREE_ITEM(item);
695                 return (ENOBUFS);
696         }
697
698         NGI_M(item) = m;
699
700         c = mtod(m, char *);
701         ip = mtod(m, struct ip *);
702
703         KASSERT(m->m_pkthdr.len == ntohs(ip->ip_len),
704             ("ng_nat: ip_len != m_pkthdr.len"));
705
706         if (hook == priv->in) {
707                 rval = LibAliasIn(priv->lib, c, m->m_len + M_TRAILINGSPACE(m));
708                 if (rval != PKT_ALIAS_OK &&
709                     rval != PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
710                         NG_FREE_ITEM(item);
711                         return (EINVAL);
712                 }
713         } else if (hook == priv->out) {
714                 rval = LibAliasOut(priv->lib, c, m->m_len + M_TRAILINGSPACE(m));
715                 if (rval != PKT_ALIAS_OK) {
716                         NG_FREE_ITEM(item);
717                         return (EINVAL);
718                 }
719         } else
720                 panic("ng_nat: unknown hook!");
721
722         m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len);
723
724         if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
725             ip->ip_p == IPPROTO_TCP) {
726                 struct tcphdr *th = (struct tcphdr *)((caddr_t)ip +
727                     (ip->ip_hl << 2));
728
729                 /*
730                  * Here is our terrible HACK.
731                  *
732                  * Sometimes LibAlias edits contents of TCP packet.
733                  * In this case it needs to recompute full TCP
734                  * checksum. However, the problem is that LibAlias
735                  * doesn't have any idea about checksum offloading
736                  * in kernel. To workaround this, we do not do
737                  * checksumming in LibAlias, but only mark the
738                  * packets in th_x2 field. If we receive a marked
739                  * packet, we calculate correct checksum for it
740                  * aware of offloading.
741                  *
742                  * Why do I do such a terrible hack instead of
743                  * recalculating checksum for each packet?
744                  * Because the previous checksum was not checked!
745                  * Recalculating checksums for EVERY packet will
746                  * hide ALL transmission errors. Yes, marked packets
747                  * still suffer from this problem. But, sigh, natd(8)
748                  * has this problem, too.
749                  */
750
751                 if (th->th_x2) {
752                         th->th_x2 = 0;
753                         ip->ip_len = ntohs(ip->ip_len);
754                         th->th_sum = in_pseudo(ip->ip_src.s_addr,
755                             ip->ip_dst.s_addr, htons(IPPROTO_TCP +
756                             ip->ip_len - (ip->ip_hl << 2)));
757         
758                         if ((m->m_pkthdr.csum_flags & CSUM_TCP) == 0) {
759                                 m->m_pkthdr.csum_data = offsetof(struct tcphdr,
760                                     th_sum);
761                                 in_delayed_cksum(m);
762                         }
763                         ip->ip_len = htons(ip->ip_len);
764                 }
765         }
766
767 send:
768         if (hook == priv->in)
769                 NG_FWD_ITEM_HOOK(error, item, priv->out);
770         else
771                 NG_FWD_ITEM_HOOK(error, item, priv->in);
772
773         return (error);
774 }
775
776 static int
777 ng_nat_shutdown(node_p node)
778 {
779         const priv_p priv = NG_NODE_PRIVATE(node);
780
781         NG_NODE_SET_PRIVATE(node, NULL);
782         NG_NODE_UNREF(node);
783
784         /* Free redirects list. */
785         while (!STAILQ_EMPTY(&priv->redirhead)) {
786                 struct ng_nat_rdr_lst *entry = STAILQ_FIRST(&priv->redirhead);
787                 STAILQ_REMOVE_HEAD(&priv->redirhead, entries);
788                 kfree(entry, M_NETGRAPH);
789         };
790
791         /* Final free. */
792         LibAliasUninit(priv->lib);
793         kfree(priv, M_NETGRAPH);
794
795         return (0);
796 }
797
798 static int
799 ng_nat_disconnect(hook_p hook)
800 {
801         const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
802
803         priv->flags &= ~NGNAT_CONNECTED;
804
805         if (hook == priv->out)
806                 priv->out = NULL;
807         if (hook == priv->in)
808                 priv->in = NULL;
809
810         if (priv->out == NULL && priv->in == NULL)
811                 ng_rmnode_self(NG_HOOK_NODE(hook));
812
813         return (0);
814 }
815
816 static unsigned int
817 ng_nat_translate_flags(unsigned int x)
818 {
819         unsigned int    res = 0;
820         
821         if (x & NG_NAT_LOG)
822                 res |= PKT_ALIAS_LOG;
823         if (x & NG_NAT_DENY_INCOMING)
824                 res |= PKT_ALIAS_DENY_INCOMING;
825         if (x & NG_NAT_SAME_PORTS)
826                 res |= PKT_ALIAS_SAME_PORTS;
827         if (x & NG_NAT_UNREGISTERED_ONLY)
828                 res |= PKT_ALIAS_UNREGISTERED_ONLY;
829         if (x & NG_NAT_RESET_ON_ADDR_CHANGE)
830                 res |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
831         if (x & NG_NAT_PROXY_ONLY)
832                 res |= PKT_ALIAS_PROXY_ONLY;
833         if (x & NG_NAT_REVERSE)
834                 res |= PKT_ALIAS_REVERSE;
835
836         return (res);
837 }