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