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