tcp: Allow per-tcpcb keepintvl and keepcnt
[dragonfly.git] / sys / netgraph7 / ng_nat.c
1 /*-
2  * Copyright 2005, Gleb Smirnoff <glebius@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/sys/netgraph/ng_nat.c,v 1.12 2008/06/01 15:13:32 mav Exp $
27  * $DragonFly: src/sys/netgraph7/ng_nat.c,v 1.2 2008/06/26 23:05:35 dillon Exp $
28  */
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/mbuf.h>
34 #include <sys/malloc.h>
35 #include <sys/ctype.h>
36 #include <sys/errno.h>
37 #include <sys/syslog.h>
38
39 #include <netinet/in_systm.h>
40 #include <netinet/in.h>
41 #include <netinet/ip.h>
42 #include <netinet/ip_var.h>
43 #include <netinet/tcp.h>
44 #include <machine/in_cksum.h>
45
46 #include <netinet/libalias/alias.h>
47
48 #include "ng_message.h"
49 #include "ng_parse.h"
50 #include "ng_nat.h"
51 #include "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         MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH,
277                 M_WAITOK | M_NULLOK | M_ZERO);
278         if (priv == NULL)
279                 return (ENOMEM);
280
281         /* Init aliasing engine. */
282         priv->lib = LibAliasInit(NULL);
283         if (priv->lib == NULL) {
284                 FREE(priv, M_NETGRAPH);
285                 return (ENOMEM);
286         }
287
288         /* Set same ports on. */
289         (void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS,
290             PKT_ALIAS_SAME_PORTS);
291
292         /* Init redirects housekeeping. */
293         priv->rdrcount = 0;
294         priv->nextid = 1;
295         STAILQ_INIT(&priv->redirhead);
296
297         /* Link structs together. */
298         NG_NODE_SET_PRIVATE(node, priv);
299         priv->node = node;
300
301         /*
302          * libalias is not thread safe, so our node
303          * must be single threaded.
304          */
305         NG_NODE_FORCE_WRITER(node);
306
307         return (0);
308 }
309
310 static int
311 ng_nat_newhook(node_p node, hook_p hook, const char *name)
312 {
313         const priv_p priv = NG_NODE_PRIVATE(node);
314
315         if (strcmp(name, NG_NAT_HOOK_IN) == 0) {
316                 priv->in = hook;
317         } else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) {
318                 priv->out = hook;
319         } else
320                 return (EINVAL);
321
322         if (priv->out != NULL &&
323             priv->in != NULL)
324                 priv->flags |= NGNAT_CONNECTED;
325
326         return(0);
327 }
328
329 static int
330 ng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook)
331 {
332         const priv_p priv = NG_NODE_PRIVATE(node);
333         struct ng_mesg *resp = NULL;
334         struct ng_mesg *msg;
335         int error = 0;
336
337         NGI_GET_MSG(item, msg);
338
339         switch (msg->header.typecookie) {
340         case NGM_NAT_COOKIE:
341                 switch (msg->header.cmd) {
342                 case NGM_NAT_SET_IPADDR:
343                     {
344                         struct in_addr *const ia = (struct in_addr *)msg->data;
345
346                         if (msg->header.arglen < sizeof(*ia)) {
347                                 error = EINVAL;
348                                 break;
349                         }
350
351                         LibAliasSetAddress(priv->lib, *ia);
352
353                         priv->flags |= NGNAT_ADDR_DEFINED;
354                     }
355                         break;
356                 case NGM_NAT_SET_MODE:
357                     {
358                         struct ng_nat_mode *const mode = 
359                             (struct ng_nat_mode *)msg->data;
360
361                         if (msg->header.arglen < sizeof(*mode)) {
362                                 error = EINVAL;
363                                 break;
364                         }
365                         
366                         if (LibAliasSetMode(priv->lib, 
367                             ng_nat_translate_flags(mode->flags),
368                             ng_nat_translate_flags(mode->mask)) < 0) {
369                                 error = ENOMEM;
370                                 break;
371                         }
372                     }
373                         break;
374                 case NGM_NAT_SET_TARGET:
375                     {
376                         struct in_addr *const ia = (struct in_addr *)msg->data;
377
378                         if (msg->header.arglen < sizeof(*ia)) {
379                                 error = EINVAL;
380                                 break;
381                         }
382
383                         LibAliasSetTarget(priv->lib, *ia);
384                     }
385                         break;
386                 case NGM_NAT_REDIRECT_PORT:
387                     {
388                         struct ng_nat_rdr_lst *entry;
389                         struct ng_nat_redirect_port *const rp =
390                             (struct ng_nat_redirect_port *)msg->data;
391
392                         if (msg->header.arglen < sizeof(*rp)) {
393                                 error = EINVAL;
394                                 break;
395                         }
396
397                         if ((entry = kmalloc(sizeof(struct ng_nat_rdr_lst),
398                             M_NETGRAPH, M_WAITOK | M_NULLOK | M_ZERO)) == NULL) {
399                                 error = ENOMEM;
400                                 break;
401                         }
402
403                         /* Try actual redirect. */
404                         entry->lnk = LibAliasRedirectPort(priv->lib,
405                                 rp->local_addr, htons(rp->local_port),
406                                 rp->remote_addr, htons(rp->remote_port),
407                                 rp->alias_addr, htons(rp->alias_port),
408                                 rp->proto);
409
410                         if (entry->lnk == NULL) {
411                                 error = ENOMEM;
412                                 FREE(entry, M_NETGRAPH);
413                                 break;
414                         }
415
416                         /* Successful, save info in our internal list. */
417                         entry->rdr.local_addr = rp->local_addr;
418                         entry->rdr.alias_addr = rp->alias_addr;
419                         entry->rdr.remote_addr = rp->remote_addr;
420                         entry->rdr.local_port = rp->local_port;
421                         entry->rdr.alias_port = rp->alias_port;
422                         entry->rdr.remote_port = rp->remote_port;
423                         entry->rdr.proto = rp->proto;
424                         bcopy(rp->description, entry->rdr.description,
425                             NG_NAT_DESC_LENGTH);
426
427                         /* Safety precaution. */
428                         entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0';
429
430                         entry->rdr.id = priv->nextid++;
431                         priv->rdrcount++;
432
433                         /* Link to list of redirects. */
434                         STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries);
435
436                         /* Response with id of newly added entry. */
437                         NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_WAITOK | M_NULLOK);
438                         if (resp == NULL) {
439                                 error = ENOMEM;
440                                 break;
441                         }
442                         bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id));
443                     }
444                         break;
445                 case NGM_NAT_REDIRECT_ADDR:
446                     {
447                         struct ng_nat_rdr_lst *entry;
448                         struct ng_nat_redirect_addr *const ra =
449                             (struct ng_nat_redirect_addr *)msg->data;
450
451                         if (msg->header.arglen < sizeof(*ra)) {
452                                 error = EINVAL;
453                                 break;
454                         }
455
456                         if ((entry = kmalloc(sizeof(struct ng_nat_rdr_lst),
457                             M_NETGRAPH, M_WAITOK | M_NULLOK | M_ZERO)) == NULL) {
458                                 error = ENOMEM;
459                                 break;
460                         }
461
462                         /* Try actual redirect. */
463                         entry->lnk = LibAliasRedirectAddr(priv->lib,
464                                 ra->local_addr, ra->alias_addr);
465
466                         if (entry->lnk == NULL) {
467                                 error = ENOMEM;
468                                 FREE(entry, M_NETGRAPH);
469                                 break;
470                         }
471
472                         /* Successful, save info in our internal list. */
473                         entry->rdr.local_addr = ra->local_addr;
474                         entry->rdr.alias_addr = ra->alias_addr;
475                         entry->rdr.proto = NG_NAT_REDIRPROTO_ADDR;
476                         bcopy(ra->description, entry->rdr.description,
477                             NG_NAT_DESC_LENGTH);
478
479                         /* Safety precaution. */
480                         entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0';
481
482                         entry->rdr.id = priv->nextid++;
483                         priv->rdrcount++;
484
485                         /* Link to list of redirects. */
486                         STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries);
487
488                         /* Response with id of newly added entry. */
489                         NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_WAITOK | M_NULLOK);
490                         if (resp == NULL) {
491                                 error = ENOMEM;
492                                 break;
493                         }
494                         bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id));
495                     }
496                         break;
497                 case NGM_NAT_REDIRECT_PROTO:
498                     {
499                         struct ng_nat_rdr_lst *entry;
500                         struct ng_nat_redirect_proto *const rp =
501                             (struct ng_nat_redirect_proto *)msg->data;
502
503                         if (msg->header.arglen < sizeof(*rp)) {
504                                 error = EINVAL;
505                                 break;
506                         }
507
508                         if ((entry = kmalloc(sizeof(struct ng_nat_rdr_lst),
509                             M_NETGRAPH, M_WAITOK | M_NULLOK | M_ZERO)) == NULL) {
510                                 error = ENOMEM;
511                                 break;
512                         }
513
514                         /* Try actual redirect. */
515                         entry->lnk = LibAliasRedirectProto(priv->lib,
516                                 rp->local_addr, rp->remote_addr,
517                                 rp->alias_addr, rp->proto);
518
519                         if (entry->lnk == NULL) {
520                                 error = ENOMEM;
521                                 FREE(entry, M_NETGRAPH);
522                                 break;
523                         }
524
525                         /* Successful, save info in our internal list. */
526                         entry->rdr.local_addr = rp->local_addr;
527                         entry->rdr.alias_addr = rp->alias_addr;
528                         entry->rdr.remote_addr = rp->remote_addr;
529                         entry->rdr.proto = rp->proto;
530                         bcopy(rp->description, entry->rdr.description,
531                             NG_NAT_DESC_LENGTH);
532
533                         /* Safety precaution. */
534                         entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0';
535
536                         entry->rdr.id = priv->nextid++;
537                         priv->rdrcount++;
538
539                         /* Link to list of redirects. */
540                         STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries);
541
542                         /* Response with id of newly added entry. */
543                         NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_WAITOK | M_NULLOK);
544                         if (resp == NULL) {
545                                 error = ENOMEM;
546                                 break;
547                         }
548                         bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id));
549                     }
550                         break;
551                 case NGM_NAT_REDIRECT_DYNAMIC:
552                 case NGM_NAT_REDIRECT_DELETE:
553                     {
554                         struct ng_nat_rdr_lst *entry;
555                         uint32_t *const id = (uint32_t *)msg->data;
556
557                         if (msg->header.arglen < sizeof(*id)) {
558                                 error = EINVAL;
559                                 break;
560                         }
561
562                         /* Find entry with supplied id. */
563                         STAILQ_FOREACH(entry, &priv->redirhead, entries) {
564                                 if (entry->rdr.id == *id)
565                                         break;
566                         }
567
568                         /* Not found. */
569                         if (entry == NULL) {
570                                 error = ENOENT;
571                                 break;
572                         }
573
574                         if (msg->header.cmd == NGM_NAT_REDIRECT_DYNAMIC) {
575                                 if (LibAliasRedirectDynamic(priv->lib,
576                                     entry->lnk) == -1) {
577                                         error = ENOTTY; /* XXX Something better? */
578                                         break;
579                                 }
580                         } else {        /* NGM_NAT_REDIRECT_DELETE */
581                                 LibAliasRedirectDelete(priv->lib, entry->lnk);
582                         }
583
584                         /* Delete entry from our internal list. */
585                         priv->rdrcount--;
586                         STAILQ_REMOVE(&priv->redirhead, entry, ng_nat_rdr_lst, entries);
587                         FREE(entry, M_NETGRAPH);
588                     }
589                         break;
590                 case NGM_NAT_ADD_SERVER:
591                     {
592                         struct ng_nat_rdr_lst *entry;
593                         struct ng_nat_add_server *const as =
594                             (struct ng_nat_add_server *)msg->data;
595
596                         if (msg->header.arglen < sizeof(*as)) {
597                                 error = EINVAL;
598                                 break;
599                         }
600
601                         /* Find entry with supplied id. */
602                         STAILQ_FOREACH(entry, &priv->redirhead, entries) {
603                                 if (entry->rdr.id == as->id)
604                                         break;
605                         }
606
607                         /* Not found. */
608                         if (entry == NULL) {
609                                 error = ENOENT;
610                                 break;
611                         }
612
613                         if (LibAliasAddServer(priv->lib, entry->lnk,
614                             as->addr, htons(as->port)) == -1) {
615                                 error = ENOMEM;
616                                 break;
617                         }
618
619                         entry->rdr.lsnat++;
620                     }
621                         break;
622                 case NGM_NAT_LIST_REDIRECTS:
623                     {
624                         struct ng_nat_rdr_lst *entry;
625                         struct ng_nat_list_redirects *ary; 
626                         int i = 0;
627
628                         NG_MKRESPONSE(resp, msg, sizeof(*ary) +
629                             (priv->rdrcount) * sizeof(*entry), M_WAITOK | M_NULLOK);
630                         if (resp == NULL) {
631                                 error = ENOMEM;
632                                 break;
633                         }
634
635                         ary = (struct ng_nat_list_redirects *)resp->data;
636                         ary->total_count = priv->rdrcount;
637
638                         STAILQ_FOREACH(entry, &priv->redirhead, entries) {
639                                 bcopy(&entry->rdr, &ary->redirects[i++],
640                                     sizeof(struct ng_nat_listrdrs_entry));
641                         }
642                     }
643                         break;
644                 case NGM_NAT_PROXY_RULE:
645                     {
646                         char *cmd = (char *)msg->data;
647
648                         if (msg->header.arglen < 6) {
649                                 error = EINVAL;
650                                 break;
651                         }
652
653                         if (LibAliasProxyRule(priv->lib, cmd) != 0)
654                                 error = ENOMEM;
655                     }
656                         break;
657                 default:
658                         error = EINVAL;         /* unknown command */
659                         break;
660                 }
661                 break;
662         default:
663                 error = EINVAL;                 /* unknown cookie type */
664                 break;
665         }
666
667         NG_RESPOND_MSG(error, node, item, resp);
668         NG_FREE_MSG(msg);
669         return (error);
670 }
671
672 static int
673 ng_nat_rcvdata(hook_p hook, item_p item )
674 {
675         const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
676         struct mbuf     *m;
677         struct ip       *ip;
678         int rval, error = 0;
679         char *c;
680
681         /* We have no required hooks. */
682         if (!(priv->flags & NGNAT_CONNECTED)) {
683                 NG_FREE_ITEM(item);
684                 return (ENXIO);
685         }
686
687         /* We have no alias address yet to do anything. */
688         if (!(priv->flags & NGNAT_ADDR_DEFINED))
689                 goto send;
690
691         m = NGI_M(item);
692
693         if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) {
694                 NGI_M(item) = NULL;     /* avoid double free */
695                 NG_FREE_ITEM(item);
696                 return (ENOBUFS);
697         }
698
699         NGI_M(item) = m;
700
701         c = mtod(m, char *);
702         ip = mtod(m, struct ip *);
703
704         KASSERT(m->m_pkthdr.len == ntohs(ip->ip_len),
705             ("ng_nat: ip_len != m_pkthdr.len"));
706
707         if (hook == priv->in) {
708                 rval = LibAliasIn(priv->lib, c, m->m_len + M_TRAILINGSPACE(m));
709                 if (rval != PKT_ALIAS_OK &&
710                     rval != PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
711                         NG_FREE_ITEM(item);
712                         return (EINVAL);
713                 }
714         } else if (hook == priv->out) {
715                 rval = LibAliasOut(priv->lib, c, m->m_len + M_TRAILINGSPACE(m));
716                 if (rval != PKT_ALIAS_OK) {
717                         NG_FREE_ITEM(item);
718                         return (EINVAL);
719                 }
720         } else
721                 panic("ng_nat: unknown hook!\n");
722
723         m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len);
724
725         if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
726             ip->ip_p == IPPROTO_TCP) {
727                 struct tcphdr *th = (struct tcphdr *)((caddr_t)ip +
728                     (ip->ip_hl << 2));
729
730                 /*
731                  * Here is our terrible HACK.
732                  *
733                  * Sometimes LibAlias edits contents of TCP packet.
734                  * In this case it needs to recompute full TCP
735                  * checksum. However, the problem is that LibAlias
736                  * doesn't have any idea about checksum offloading
737                  * in kernel. To workaround this, we do not do
738                  * checksumming in LibAlias, but only mark the
739                  * packets in th_x2 field. If we receive a marked
740                  * packet, we calculate correct checksum for it
741                  * aware of offloading.
742                  *
743                  * Why do I do such a terrible hack instead of
744                  * recalculating checksum for each packet?
745                  * Because the previous checksum was not checked!
746                  * Recalculating checksums for EVERY packet will
747                  * hide ALL transmission errors. Yes, marked packets
748                  * still suffer from this problem. But, sigh, natd(8)
749                  * has this problem, too.
750                  */
751
752                 if (th->th_x2) {
753                         th->th_x2 = 0;
754                         ip->ip_len = ntohs(ip->ip_len);
755                         th->th_sum = in_pseudo(ip->ip_src.s_addr,
756                             ip->ip_dst.s_addr, htons(IPPROTO_TCP +
757                             ip->ip_len - (ip->ip_hl << 2)));
758         
759                         if ((m->m_pkthdr.csum_flags & CSUM_TCP) == 0) {
760                                 m->m_pkthdr.csum_data = offsetof(struct tcphdr,
761                                     th_sum);
762                                 in_delayed_cksum(m);
763                         }
764                         ip->ip_len = htons(ip->ip_len);
765                 }
766         }
767
768 send:
769         if (hook == priv->in)
770                 NG_FWD_ITEM_HOOK(error, item, priv->out);
771         else
772                 NG_FWD_ITEM_HOOK(error, item, priv->in);
773
774         return (error);
775 }
776
777 static int
778 ng_nat_shutdown(node_p node)
779 {
780         const priv_p priv = NG_NODE_PRIVATE(node);
781
782         NG_NODE_SET_PRIVATE(node, NULL);
783         NG_NODE_UNREF(node);
784
785         /* Free redirects list. */
786         while (!STAILQ_EMPTY(&priv->redirhead)) {
787                 struct ng_nat_rdr_lst *entry = STAILQ_FIRST(&priv->redirhead);
788                 STAILQ_REMOVE_HEAD(&priv->redirhead, entries);
789                 FREE(entry, M_NETGRAPH);
790         };
791
792         /* Final free. */
793         LibAliasUninit(priv->lib);
794         FREE(priv, M_NETGRAPH);
795
796         return (0);
797 }
798
799 static int
800 ng_nat_disconnect(hook_p hook)
801 {
802         const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
803
804         priv->flags &= ~NGNAT_CONNECTED;
805
806         if (hook == priv->out)
807                 priv->out = NULL;
808         if (hook == priv->in)
809                 priv->in = NULL;
810
811         if (priv->out == NULL && priv->in == NULL)
812                 ng_rmnode_self(NG_HOOK_NODE(hook));
813
814         return (0);
815 }
816
817 static unsigned int
818 ng_nat_translate_flags(unsigned int x)
819 {
820         unsigned int    res = 0;
821         
822         if (x & NG_NAT_LOG)
823                 res |= PKT_ALIAS_LOG;
824         if (x & NG_NAT_DENY_INCOMING)
825                 res |= PKT_ALIAS_DENY_INCOMING;
826         if (x & NG_NAT_SAME_PORTS)
827                 res |= PKT_ALIAS_SAME_PORTS;
828         if (x & NG_NAT_UNREGISTERED_ONLY)
829                 res |= PKT_ALIAS_UNREGISTERED_ONLY;
830         if (x & NG_NAT_RESET_ON_ADDR_CHANGE)
831                 res |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
832         if (x & NG_NAT_PROXY_ONLY)
833                 res |= PKT_ALIAS_PROXY_ONLY;
834         if (x & NG_NAT_REVERSE)
835                 res |= PKT_ALIAS_REVERSE;
836
837         return (res);
838 }