]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netgraph/ng_nat.c
THIS BRANCH IS OBSOLETE, PLEASE READ:
[FreeBSD/FreeBSD.git] / sys / netgraph / ng_nat.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright 2005, Gleb Smirnoff <glebius@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $FreeBSD$
29  */
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/mbuf.h>
35 #include <sys/malloc.h>
36 #include <sys/ctype.h>
37 #include <sys/errno.h>
38 #include <sys/syslog.h>
39
40 #include <netinet/in_systm.h>
41 #include <netinet/in.h>
42 #include <netinet/ip.h>
43 #include <netinet/ip_var.h>
44 #include <netinet/tcp.h>
45 #include <machine/in_cksum.h>
46
47 #include <net/dlt.h>
48 #include <net/ethernet.h>
49
50 #include <netinet/libalias/alias.h>
51 #include <netinet/libalias/alias_local.h>
52
53 #include <netgraph/ng_message.h>
54 #include <netgraph/ng_parse.h>
55 #include <netgraph/ng_nat.h>
56 #include <netgraph/netgraph.h>
57
58 static ng_constructor_t ng_nat_constructor;
59 static ng_rcvmsg_t      ng_nat_rcvmsg;
60 static ng_shutdown_t    ng_nat_shutdown;
61 static ng_newhook_t     ng_nat_newhook;
62 static ng_rcvdata_t     ng_nat_rcvdata;
63 static ng_disconnect_t  ng_nat_disconnect;
64
65 static unsigned int     ng_nat_translate_flags(unsigned int x);
66
67 /* Parse type for struct ng_nat_mode. */
68 static const struct ng_parse_struct_field ng_nat_mode_fields[]
69         = NG_NAT_MODE_INFO;
70 static const struct ng_parse_type ng_nat_mode_type = {
71         &ng_parse_struct_type,
72         &ng_nat_mode_fields
73 };
74
75 /* Parse type for 'description' field in structs. */
76 static const struct ng_parse_fixedstring_info ng_nat_description_info
77         = { NG_NAT_DESC_LENGTH };
78 static const struct ng_parse_type ng_nat_description_type = {
79         &ng_parse_fixedstring_type,
80         &ng_nat_description_info
81 };
82
83 /* Parse type for struct ng_nat_redirect_port. */
84 static const struct ng_parse_struct_field ng_nat_redirect_port_fields[]
85         = NG_NAT_REDIRECT_PORT_TYPE_INFO(&ng_nat_description_type);
86 static const struct ng_parse_type ng_nat_redirect_port_type = {
87         &ng_parse_struct_type,
88         &ng_nat_redirect_port_fields
89 };
90
91 /* Parse type for struct ng_nat_redirect_addr. */
92 static const struct ng_parse_struct_field ng_nat_redirect_addr_fields[]
93         = NG_NAT_REDIRECT_ADDR_TYPE_INFO(&ng_nat_description_type);
94 static const struct ng_parse_type ng_nat_redirect_addr_type = {
95         &ng_parse_struct_type,
96         &ng_nat_redirect_addr_fields
97 };
98
99 /* Parse type for struct ng_nat_redirect_proto. */
100 static const struct ng_parse_struct_field ng_nat_redirect_proto_fields[]
101         = NG_NAT_REDIRECT_PROTO_TYPE_INFO(&ng_nat_description_type);
102 static const struct ng_parse_type ng_nat_redirect_proto_type = {
103         &ng_parse_struct_type,
104         &ng_nat_redirect_proto_fields
105 };
106
107 /* Parse type for struct ng_nat_add_server. */
108 static const struct ng_parse_struct_field ng_nat_add_server_fields[]
109         = NG_NAT_ADD_SERVER_TYPE_INFO;
110 static const struct ng_parse_type ng_nat_add_server_type = {
111         &ng_parse_struct_type,
112         &ng_nat_add_server_fields
113 };
114
115 /* Parse type for one struct ng_nat_listrdrs_entry. */
116 static const struct ng_parse_struct_field ng_nat_listrdrs_entry_fields[]
117         = NG_NAT_LISTRDRS_ENTRY_TYPE_INFO(&ng_nat_description_type);
118 static const struct ng_parse_type ng_nat_listrdrs_entry_type = {
119         &ng_parse_struct_type,
120         &ng_nat_listrdrs_entry_fields
121 };
122
123 /* Parse type for 'redirects' array in struct ng_nat_list_redirects. */
124 static int
125 ng_nat_listrdrs_ary_getLength(const struct ng_parse_type *type,
126         const u_char *start, const u_char *buf)
127 {
128         const struct ng_nat_list_redirects *lr;
129
130         lr = (const struct ng_nat_list_redirects *)
131             (buf - offsetof(struct ng_nat_list_redirects, redirects));
132         return lr->total_count;
133 }
134
135 static const struct ng_parse_array_info ng_nat_listrdrs_ary_info = {
136         &ng_nat_listrdrs_entry_type,
137         &ng_nat_listrdrs_ary_getLength,
138         NULL
139 };
140 static const struct ng_parse_type ng_nat_listrdrs_ary_type = {
141         &ng_parse_array_type,
142         &ng_nat_listrdrs_ary_info
143 };
144
145 /* Parse type for struct ng_nat_list_redirects. */
146 static const struct ng_parse_struct_field ng_nat_list_redirects_fields[]
147         = NG_NAT_LIST_REDIRECTS_TYPE_INFO(&ng_nat_listrdrs_ary_type);
148 static const struct ng_parse_type ng_nat_list_redirects_type = {
149         &ng_parse_struct_type,
150         &ng_nat_list_redirects_fields
151 };
152
153 /* Parse type for struct ng_nat_libalias_info. */
154 static const struct ng_parse_struct_field ng_nat_libalias_info_fields[]
155         = NG_NAT_LIBALIAS_INFO;
156 static const struct ng_parse_type ng_nat_libalias_info_type = {
157         &ng_parse_struct_type,
158         &ng_nat_libalias_info_fields
159 };
160
161 /* List of commands and how to convert arguments to/from ASCII. */
162 static const struct ng_cmdlist ng_nat_cmdlist[] = {
163         {
164           NGM_NAT_COOKIE,
165           NGM_NAT_SET_IPADDR,
166           "setaliasaddr",
167           &ng_parse_ipaddr_type,
168           NULL
169         },
170         {
171           NGM_NAT_COOKIE,
172           NGM_NAT_SET_MODE,
173           "setmode",
174           &ng_nat_mode_type,
175           NULL
176         },
177         {
178           NGM_NAT_COOKIE,
179           NGM_NAT_SET_TARGET,
180           "settarget",
181           &ng_parse_ipaddr_type,
182           NULL
183         },
184         {
185           NGM_NAT_COOKIE,
186           NGM_NAT_REDIRECT_PORT,
187           "redirectport",
188           &ng_nat_redirect_port_type,
189           &ng_parse_uint32_type
190         },
191         {
192           NGM_NAT_COOKIE,
193           NGM_NAT_REDIRECT_ADDR,
194           "redirectaddr",
195           &ng_nat_redirect_addr_type,
196           &ng_parse_uint32_type
197         },
198         {
199           NGM_NAT_COOKIE,
200           NGM_NAT_REDIRECT_PROTO,
201           "redirectproto",
202           &ng_nat_redirect_proto_type,
203           &ng_parse_uint32_type
204         },
205         {
206           NGM_NAT_COOKIE,
207           NGM_NAT_REDIRECT_DYNAMIC,
208           "redirectdynamic",
209           &ng_parse_uint32_type,
210           NULL
211         },
212         {
213           NGM_NAT_COOKIE,
214           NGM_NAT_REDIRECT_DELETE,
215           "redirectdelete",
216           &ng_parse_uint32_type,
217           NULL
218         },
219         {
220           NGM_NAT_COOKIE,
221           NGM_NAT_ADD_SERVER,
222           "addserver",
223           &ng_nat_add_server_type,
224           NULL
225         },
226         {
227           NGM_NAT_COOKIE,
228           NGM_NAT_LIST_REDIRECTS,
229           "listredirects",
230           NULL,
231           &ng_nat_list_redirects_type
232         },
233         {
234           NGM_NAT_COOKIE,
235           NGM_NAT_PROXY_RULE,
236           "proxyrule",
237           &ng_parse_string_type,
238           NULL
239         },
240         {
241           NGM_NAT_COOKIE,
242           NGM_NAT_LIBALIAS_INFO,
243           "libaliasinfo",
244           NULL,
245           &ng_nat_libalias_info_type
246         },
247         {
248           NGM_NAT_COOKIE,
249           NGM_NAT_SET_DLT,
250           "setdlt",
251           &ng_parse_uint8_type,
252           NULL
253         },
254         {
255           NGM_NAT_COOKIE,
256           NGM_NAT_GET_DLT,
257           "getdlt",
258           NULL,
259           &ng_parse_uint8_type
260         },
261         { 0 }
262 };
263
264 /* Netgraph node type descriptor. */
265 static struct ng_type typestruct = {
266         .version =      NG_ABI_VERSION,
267         .name =         NG_NAT_NODE_TYPE,
268         .constructor =  ng_nat_constructor,
269         .rcvmsg =       ng_nat_rcvmsg,
270         .shutdown =     ng_nat_shutdown,
271         .newhook =      ng_nat_newhook,
272         .rcvdata =      ng_nat_rcvdata,
273         .disconnect =   ng_nat_disconnect,
274         .cmdlist =      ng_nat_cmdlist,
275 };
276 NETGRAPH_INIT(nat, &typestruct);
277 MODULE_DEPEND(ng_nat, libalias, 1, 1, 1);
278
279 /* Element for list of redirects. */
280 struct ng_nat_rdr_lst {
281         STAILQ_ENTRY(ng_nat_rdr_lst) entries;
282         struct alias_link       *lnk;
283         struct ng_nat_listrdrs_entry rdr;
284 };
285 STAILQ_HEAD(rdrhead, ng_nat_rdr_lst);
286
287 /* Information we store for each node. */
288 struct ng_nat_priv {
289         node_p          node;           /* back pointer to node */
290         hook_p          in;             /* hook for demasquerading */
291         hook_p          out;            /* hook for masquerading */
292         struct libalias *lib;           /* libalias handler */
293         uint32_t        flags;          /* status flags */
294         uint32_t        rdrcount;       /* number or redirects in list */
295         uint32_t        nextid;         /* for next in turn in list */
296         struct rdrhead  redirhead;      /* redirect list header */
297         uint8_t         dlt;            /* DLT_XXX from bpf.h */
298 };
299 typedef struct ng_nat_priv *priv_p;
300
301 /* Values of flags */
302 #define NGNAT_CONNECTED         0x1     /* We have both hooks connected */
303 #define NGNAT_ADDR_DEFINED      0x2     /* NGM_NAT_SET_IPADDR happened */
304
305 static int
306 ng_nat_constructor(node_p node)
307 {
308         priv_p priv;
309
310         /* Initialize private descriptor. */
311         priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO);
312
313         /* Init aliasing engine. */
314         priv->lib = LibAliasInit(NULL);
315
316         /* Set same ports on. */
317         (void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS,
318             PKT_ALIAS_SAME_PORTS);
319
320         /* Init redirects housekeeping. */
321         priv->rdrcount = 0;
322         priv->nextid = 1;
323         priv->dlt = DLT_RAW;
324         STAILQ_INIT(&priv->redirhead);
325
326         /* Link structs together. */
327         NG_NODE_SET_PRIVATE(node, priv);
328         priv->node = node;
329
330         /*
331          * libalias is not thread safe, so our node
332          * must be single threaded.
333          */
334         NG_NODE_FORCE_WRITER(node);
335
336         return (0);
337 }
338
339 static int
340 ng_nat_newhook(node_p node, hook_p hook, const char *name)
341 {
342         const priv_p priv = NG_NODE_PRIVATE(node);
343
344         if (strcmp(name, NG_NAT_HOOK_IN) == 0) {
345                 priv->in = hook;
346         } else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) {
347                 priv->out = hook;
348         } else
349                 return (EINVAL);
350
351         if (priv->out != NULL &&
352             priv->in != NULL)
353                 priv->flags |= NGNAT_CONNECTED;
354
355         return(0);
356 }
357
358 static int
359 ng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook)
360 {
361         const priv_p priv = NG_NODE_PRIVATE(node);
362         struct ng_mesg *resp = NULL;
363         struct ng_mesg *msg;
364         int error = 0;
365
366         NGI_GET_MSG(item, msg);
367
368         switch (msg->header.typecookie) {
369         case NGM_NAT_COOKIE:
370                 switch (msg->header.cmd) {
371                 case NGM_NAT_SET_IPADDR:
372                     {
373                         struct in_addr *const ia = (struct in_addr *)msg->data;
374
375                         if (msg->header.arglen < sizeof(*ia)) {
376                                 error = EINVAL;
377                                 break;
378                         }
379
380                         LibAliasSetAddress(priv->lib, *ia);
381
382                         priv->flags |= NGNAT_ADDR_DEFINED;
383                     }
384                         break;
385                 case NGM_NAT_SET_MODE:
386                     {
387                         struct ng_nat_mode *const mode = 
388                             (struct ng_nat_mode *)msg->data;
389
390                         if (msg->header.arglen < sizeof(*mode)) {
391                                 error = EINVAL;
392                                 break;
393                         }
394                         
395                         if (LibAliasSetMode(priv->lib, 
396                             ng_nat_translate_flags(mode->flags),
397                             ng_nat_translate_flags(mode->mask)) < 0) {
398                                 error = ENOMEM;
399                                 break;
400                         }
401                     }
402                         break;
403                 case NGM_NAT_SET_TARGET:
404                     {
405                         struct in_addr *const ia = (struct in_addr *)msg->data;
406
407                         if (msg->header.arglen < sizeof(*ia)) {
408                                 error = EINVAL;
409                                 break;
410                         }
411
412                         LibAliasSetTarget(priv->lib, *ia);
413                     }
414                         break;
415                 case NGM_NAT_REDIRECT_PORT:
416                     {
417                         struct ng_nat_rdr_lst *entry;
418                         struct ng_nat_redirect_port *const rp =
419                             (struct ng_nat_redirect_port *)msg->data;
420
421                         if (msg->header.arglen < sizeof(*rp)) {
422                                 error = EINVAL;
423                                 break;
424                         }
425
426                         if ((entry = malloc(sizeof(struct ng_nat_rdr_lst),
427                             M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) {
428                                 error = ENOMEM;
429                                 break;
430                         }
431
432                         /* Try actual redirect. */
433                         entry->lnk = LibAliasRedirectPort(priv->lib,
434                                 rp->local_addr, htons(rp->local_port),
435                                 rp->remote_addr, htons(rp->remote_port),
436                                 rp->alias_addr, htons(rp->alias_port),
437                                 rp->proto);
438
439                         if (entry->lnk == NULL) {
440                                 error = ENOMEM;
441                                 free(entry, M_NETGRAPH);
442                                 break;
443                         }
444
445                         /* Successful, save info in our internal list. */
446                         entry->rdr.local_addr = rp->local_addr;
447                         entry->rdr.alias_addr = rp->alias_addr;
448                         entry->rdr.remote_addr = rp->remote_addr;
449                         entry->rdr.local_port = rp->local_port;
450                         entry->rdr.alias_port = rp->alias_port;
451                         entry->rdr.remote_port = rp->remote_port;
452                         entry->rdr.proto = rp->proto;
453                         bcopy(rp->description, entry->rdr.description,
454                             NG_NAT_DESC_LENGTH);
455
456                         /* Safety precaution. */
457                         entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0';
458
459                         entry->rdr.id = priv->nextid++;
460                         priv->rdrcount++;
461
462                         /* Link to list of redirects. */
463                         STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries);
464
465                         /* Response with id of newly added entry. */
466                         NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT);
467                         if (resp == NULL) {
468                                 error = ENOMEM;
469                                 break;
470                         }
471                         bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id));
472                     }
473                         break;
474                 case NGM_NAT_REDIRECT_ADDR:
475                     {
476                         struct ng_nat_rdr_lst *entry;
477                         struct ng_nat_redirect_addr *const ra =
478                             (struct ng_nat_redirect_addr *)msg->data;
479
480                         if (msg->header.arglen < sizeof(*ra)) {
481                                 error = EINVAL;
482                                 break;
483                         }
484
485                         if ((entry = malloc(sizeof(struct ng_nat_rdr_lst),
486                             M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) {
487                                 error = ENOMEM;
488                                 break;
489                         }
490
491                         /* Try actual redirect. */
492                         entry->lnk = LibAliasRedirectAddr(priv->lib,
493                                 ra->local_addr, ra->alias_addr);
494
495                         if (entry->lnk == NULL) {
496                                 error = ENOMEM;
497                                 free(entry, M_NETGRAPH);
498                                 break;
499                         }
500
501                         /* Successful, save info in our internal list. */
502                         entry->rdr.local_addr = ra->local_addr;
503                         entry->rdr.alias_addr = ra->alias_addr;
504                         entry->rdr.proto = NG_NAT_REDIRPROTO_ADDR;
505                         bcopy(ra->description, entry->rdr.description,
506                             NG_NAT_DESC_LENGTH);
507
508                         /* Safety precaution. */
509                         entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0';
510
511                         entry->rdr.id = priv->nextid++;
512                         priv->rdrcount++;
513
514                         /* Link to list of redirects. */
515                         STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries);
516
517                         /* Response with id of newly added entry. */
518                         NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT);
519                         if (resp == NULL) {
520                                 error = ENOMEM;
521                                 break;
522                         }
523                         bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id));
524                     }
525                         break;
526                 case NGM_NAT_REDIRECT_PROTO:
527                     {
528                         struct ng_nat_rdr_lst *entry;
529                         struct ng_nat_redirect_proto *const rp =
530                             (struct ng_nat_redirect_proto *)msg->data;
531
532                         if (msg->header.arglen < sizeof(*rp)) {
533                                 error = EINVAL;
534                                 break;
535                         }
536
537                         if ((entry = malloc(sizeof(struct ng_nat_rdr_lst),
538                             M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) {
539                                 error = ENOMEM;
540                                 break;
541                         }
542
543                         /* Try actual redirect. */
544                         entry->lnk = LibAliasRedirectProto(priv->lib,
545                                 rp->local_addr, rp->remote_addr,
546                                 rp->alias_addr, rp->proto);
547
548                         if (entry->lnk == NULL) {
549                                 error = ENOMEM;
550                                 free(entry, M_NETGRAPH);
551                                 break;
552                         }
553
554                         /* Successful, save info in our internal list. */
555                         entry->rdr.local_addr = rp->local_addr;
556                         entry->rdr.alias_addr = rp->alias_addr;
557                         entry->rdr.remote_addr = rp->remote_addr;
558                         entry->rdr.proto = rp->proto;
559                         bcopy(rp->description, entry->rdr.description,
560                             NG_NAT_DESC_LENGTH);
561
562                         /* Safety precaution. */
563                         entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0';
564
565                         entry->rdr.id = priv->nextid++;
566                         priv->rdrcount++;
567
568                         /* Link to list of redirects. */
569                         STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries);
570
571                         /* Response with id of newly added entry. */
572                         NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT);
573                         if (resp == NULL) {
574                                 error = ENOMEM;
575                                 break;
576                         }
577                         bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id));
578                     }
579                         break;
580                 case NGM_NAT_REDIRECT_DYNAMIC:
581                 case NGM_NAT_REDIRECT_DELETE:
582                     {
583                         struct ng_nat_rdr_lst *entry;
584                         uint32_t *const id = (uint32_t *)msg->data;
585
586                         if (msg->header.arglen < sizeof(*id)) {
587                                 error = EINVAL;
588                                 break;
589                         }
590
591                         /* Find entry with supplied id. */
592                         STAILQ_FOREACH(entry, &priv->redirhead, entries) {
593                                 if (entry->rdr.id == *id)
594                                         break;
595                         }
596
597                         /* Not found. */
598                         if (entry == NULL) {
599                                 error = ENOENT;
600                                 break;
601                         }
602
603                         if (msg->header.cmd == NGM_NAT_REDIRECT_DYNAMIC) {
604                                 if (LibAliasRedirectDynamic(priv->lib,
605                                     entry->lnk) == -1) {
606                                         error = ENOTTY; /* XXX Something better? */
607                                         break;
608                                 }
609                         } else {        /* NGM_NAT_REDIRECT_DELETE */
610                                 LibAliasRedirectDelete(priv->lib, entry->lnk);
611                         }
612
613                         /* Delete entry from our internal list. */
614                         priv->rdrcount--;
615                         STAILQ_REMOVE(&priv->redirhead, entry, ng_nat_rdr_lst, entries);
616                         free(entry, M_NETGRAPH);
617                     }
618                         break;
619                 case NGM_NAT_ADD_SERVER:
620                     {
621                         struct ng_nat_rdr_lst *entry;
622                         struct ng_nat_add_server *const as =
623                             (struct ng_nat_add_server *)msg->data;
624
625                         if (msg->header.arglen < sizeof(*as)) {
626                                 error = EINVAL;
627                                 break;
628                         }
629
630                         /* Find entry with supplied id. */
631                         STAILQ_FOREACH(entry, &priv->redirhead, entries) {
632                                 if (entry->rdr.id == as->id)
633                                         break;
634                         }
635
636                         /* Not found. */
637                         if (entry == NULL) {
638                                 error = ENOENT;
639                                 break;
640                         }
641
642                         if (LibAliasAddServer(priv->lib, entry->lnk,
643                             as->addr, htons(as->port)) == -1) {
644                                 error = ENOMEM;
645                                 break;
646                         }
647
648                         entry->rdr.lsnat++;
649                     }
650                         break;
651                 case NGM_NAT_LIST_REDIRECTS:
652                     {
653                         struct ng_nat_rdr_lst *entry;
654                         struct ng_nat_list_redirects *ary; 
655                         int i = 0;
656
657                         NG_MKRESPONSE(resp, msg, sizeof(*ary) +
658                             (priv->rdrcount) * sizeof(*entry), M_NOWAIT);
659                         if (resp == NULL) {
660                                 error = ENOMEM;
661                                 break;
662                         }
663
664                         ary = (struct ng_nat_list_redirects *)resp->data;
665                         ary->total_count = priv->rdrcount;
666
667                         STAILQ_FOREACH(entry, &priv->redirhead, entries) {
668                                 bcopy(&entry->rdr, &ary->redirects[i++],
669                                     sizeof(struct ng_nat_listrdrs_entry));
670                         }
671                     }
672                         break;
673                 case NGM_NAT_PROXY_RULE:
674                     {
675                         char *cmd = (char *)msg->data;
676
677                         if (msg->header.arglen < 6) {
678                                 error = EINVAL;
679                                 break;
680                         }
681
682                         if (LibAliasProxyRule(priv->lib, cmd) != 0)
683                                 error = ENOMEM;
684                     }
685                         break;
686                 case NGM_NAT_LIBALIAS_INFO:
687                     {
688                         struct ng_nat_libalias_info *i;
689
690                         NG_MKRESPONSE(resp, msg,
691                             sizeof(struct ng_nat_libalias_info), M_NOWAIT);
692                         if (resp == NULL) {
693                                 error = ENOMEM;
694                                 break;
695                         }
696                         i = (struct ng_nat_libalias_info *)resp->data;
697 #define COPY(F) do {                                            \
698         if (priv->lib->F >= 0 && priv->lib->F < UINT32_MAX)     \
699                 i->F = priv->lib->F;                            \
700         else                                                    \
701                 i->F = UINT32_MAX;                              \
702 } while (0)
703                 
704                         COPY(icmpLinkCount);
705                         COPY(udpLinkCount);
706                         COPY(tcpLinkCount);
707                         COPY(pptpLinkCount);
708                         COPY(sctpLinkCount);
709                         COPY(protoLinkCount);
710                         COPY(fragmentIdLinkCount);
711                         COPY(fragmentPtrLinkCount);
712                         COPY(sockCount);
713 #undef COPY
714                     }
715                         break;
716                 case NGM_NAT_SET_DLT:
717                         if (msg->header.arglen != sizeof(uint8_t)) {
718                                 error = EINVAL;
719                                 break;
720                         }
721                         switch (*(uint8_t *) msg->data) {
722                         case DLT_EN10MB:
723                         case DLT_RAW:
724                                 priv->dlt = *(uint8_t *) msg->data;
725                                 break;
726                         default:
727                                 error = EINVAL;
728                                 break;
729                         }
730                         break;
731                 default:
732                         error = EINVAL;         /* unknown command */
733                         break;
734                 }
735                 break;
736                 case NGM_NAT_GET_DLT:
737                         NG_MKRESPONSE(resp, msg, sizeof(uint8_t), M_WAITOK);
738                         if (resp == NULL) {
739                                 error = ENOMEM;
740                                 break;
741                         }
742                         *((uint8_t *) resp->data) = priv->dlt;
743                         break;
744         default:
745                 error = EINVAL;                 /* unknown cookie type */
746                 break;
747         }
748
749         NG_RESPOND_MSG(error, node, item, resp);
750         NG_FREE_MSG(msg);
751         return (error);
752 }
753
754 static int
755 ng_nat_rcvdata(hook_p hook, item_p item )
756 {
757         const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
758         struct mbuf     *m;
759         struct ip       *ip;
760         int rval, ipofs, error = 0;
761         char *c;
762
763         /* We have no required hooks. */
764         if (!(priv->flags & NGNAT_CONNECTED)) {
765                 NG_FREE_ITEM(item);
766                 return (ENXIO);
767         }
768
769         /* We have no alias address yet to do anything. */
770         if (!(priv->flags & NGNAT_ADDR_DEFINED))
771                 goto send;
772
773         m = NGI_M(item);
774
775         if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) {
776                 NGI_M(item) = NULL;     /* avoid double free */
777                 NG_FREE_ITEM(item);
778                 return (ENOBUFS);
779         }
780
781         NGI_M(item) = m;
782
783         switch (priv->dlt) {
784         case DLT_RAW:
785                 ipofs = 0;
786                 break;
787         case DLT_EN10MB:
788             {
789                 struct ether_header *eh;
790
791                 if (m->m_pkthdr.len < sizeof(struct ether_header)) {
792                         NG_FREE_ITEM(item);
793                         return (ENXIO);
794                 }
795                 eh = mtod(m, struct ether_header *);
796                 switch (ntohs(eh->ether_type)) {
797                 case ETHERTYPE_IP:
798                         ipofs = sizeof(struct ether_header);
799                         break;
800                 default:
801                         goto send;
802                 }
803                 break;
804             }
805         default:
806                 panic("Corrupted priv->dlt: %u", priv->dlt);
807         }
808
809         if (m->m_pkthdr.len < ipofs + sizeof(struct ip))
810                 goto send;              /* packet too short to hold IP */
811
812         c = (char *)mtodo(m, ipofs);
813         ip = (struct ip *)mtodo(m, ipofs);
814
815         if (ip->ip_v != IPVERSION)
816                 goto send;              /* other IP version, let it pass */
817         if (m->m_pkthdr.len < ipofs + ntohs(ip->ip_len))
818                 goto send;              /* packet too short (i.e. fragmented or broken) */
819
820         /*
821          * We drop packet when:
822          * 1. libalias returns PKT_ALIAS_ERROR;
823          * 2. For incoming packets:
824          *      a) for unresolved fragments;
825          *      b) libalias returns PKT_ALIAS_IGNORED and
826          *              PKT_ALIAS_DENY_INCOMING flag is set.
827          */
828         if (hook == priv->in) {
829                 rval = LibAliasIn(priv->lib, c, m->m_len - ipofs +
830                     M_TRAILINGSPACE(m));
831                 if (rval == PKT_ALIAS_ERROR ||
832                     rval == PKT_ALIAS_UNRESOLVED_FRAGMENT ||
833                     (rval == PKT_ALIAS_IGNORED &&
834                      (priv->lib->packetAliasMode &
835                       PKT_ALIAS_DENY_INCOMING) != 0)) {
836                         NG_FREE_ITEM(item);
837                         return (EINVAL);
838                 }
839         } else if (hook == priv->out) {
840                 rval = LibAliasOut(priv->lib, c, m->m_len - ipofs +
841                     M_TRAILINGSPACE(m));
842                 if (rval == PKT_ALIAS_ERROR) {
843                         NG_FREE_ITEM(item);
844                         return (EINVAL);
845                 }
846         } else
847                 panic("ng_nat: unknown hook!\n");
848
849         if (rval == PKT_ALIAS_RESPOND)
850                 m->m_flags |= M_SKIP_FIREWALL;
851         m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len) + ipofs;
852
853         if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
854             ip->ip_p == IPPROTO_TCP) {
855                 struct tcphdr *th = (struct tcphdr *)((caddr_t)ip +
856                     (ip->ip_hl << 2));
857
858                 /*
859                  * Here is our terrible HACK.
860                  *
861                  * Sometimes LibAlias edits contents of TCP packet.
862                  * In this case it needs to recompute full TCP
863                  * checksum. However, the problem is that LibAlias
864                  * doesn't have any idea about checksum offloading
865                  * in kernel. To workaround this, we do not do
866                  * checksumming in LibAlias, but only mark the
867                  * packets in th_x2 field. If we receive a marked
868                  * packet, we calculate correct checksum for it
869                  * aware of offloading.
870                  *
871                  * Why do I do such a terrible hack instead of
872                  * recalculating checksum for each packet?
873                  * Because the previous checksum was not checked!
874                  * Recalculating checksums for EVERY packet will
875                  * hide ALL transmission errors. Yes, marked packets
876                  * still suffer from this problem. But, sigh, natd(8)
877                  * has this problem, too.
878                  */
879
880                 if (th->th_x2) {
881                         uint16_t ip_len = ntohs(ip->ip_len);
882
883                         th->th_x2 = 0;
884                         th->th_sum = in_pseudo(ip->ip_src.s_addr,
885                             ip->ip_dst.s_addr, htons(IPPROTO_TCP +
886                             ip_len - (ip->ip_hl << 2)));
887
888                         if ((m->m_pkthdr.csum_flags & CSUM_TCP) == 0) {
889                                 m->m_pkthdr.csum_data = offsetof(struct tcphdr,
890                                     th_sum);
891                                 in_delayed_cksum(m);
892                         }
893                 }
894         }
895
896 send:
897         if (hook == priv->in)
898                 NG_FWD_ITEM_HOOK(error, item, priv->out);
899         else
900                 NG_FWD_ITEM_HOOK(error, item, priv->in);
901
902         return (error);
903 }
904
905 static int
906 ng_nat_shutdown(node_p node)
907 {
908         const priv_p priv = NG_NODE_PRIVATE(node);
909
910         NG_NODE_SET_PRIVATE(node, NULL);
911         NG_NODE_UNREF(node);
912
913         /* Free redirects list. */
914         while (!STAILQ_EMPTY(&priv->redirhead)) {
915                 struct ng_nat_rdr_lst *entry = STAILQ_FIRST(&priv->redirhead);
916                 STAILQ_REMOVE_HEAD(&priv->redirhead, entries);
917                 free(entry, M_NETGRAPH);
918         }
919
920         /* Final free. */
921         LibAliasUninit(priv->lib);
922         free(priv, M_NETGRAPH);
923
924         return (0);
925 }
926
927 static int
928 ng_nat_disconnect(hook_p hook)
929 {
930         const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
931
932         priv->flags &= ~NGNAT_CONNECTED;
933
934         if (hook == priv->out)
935                 priv->out = NULL;
936         if (hook == priv->in)
937                 priv->in = NULL;
938
939         if (priv->out == NULL && priv->in == NULL)
940                 ng_rmnode_self(NG_HOOK_NODE(hook));
941
942         return (0);
943 }
944
945 static unsigned int
946 ng_nat_translate_flags(unsigned int x)
947 {
948         unsigned int    res = 0;
949
950         if (x & NG_NAT_LOG)
951                 res |= PKT_ALIAS_LOG;
952         if (x & NG_NAT_DENY_INCOMING)
953                 res |= PKT_ALIAS_DENY_INCOMING;
954         if (x & NG_NAT_SAME_PORTS)
955                 res |= PKT_ALIAS_SAME_PORTS;
956         if (x & NG_NAT_UNREGISTERED_ONLY)
957                 res |= PKT_ALIAS_UNREGISTERED_ONLY;
958         if (x & NG_NAT_RESET_ON_ADDR_CHANGE)
959                 res |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
960         if (x & NG_NAT_PROXY_ONLY)
961                 res |= PKT_ALIAS_PROXY_ONLY;
962         if (x & NG_NAT_REVERSE)
963                 res |= PKT_ALIAS_REVERSE;
964
965         return (res);
966 }