kernel - Fix a vty switch/history bug
[dragonfly.git] / sys / netgraph7 / ng_nat.c
blobf09ece14fb42371679e585507dd8b9222134fb8b
1 /*-
2 * Copyright 2005, Gleb Smirnoff <glebius@FreeBSD.org>
3 * All rights reserved.
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.
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.
26 * $FreeBSD: src/sys/netgraph/ng_nat.c,v 1.12 2008/06/01 15:13:32 mav Exp $
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>
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>
45 #include <netinet/libalias/alias.h>
47 #include "ng_message.h"
48 #include "ng_parse.h"
49 #include "ng_nat.h"
50 #include "netgraph.h"
52 static ng_constructor_t ng_nat_constructor;
53 static ng_rcvmsg_t ng_nat_rcvmsg;
54 static ng_shutdown_t ng_nat_shutdown;
55 static ng_newhook_t ng_nat_newhook;
56 static ng_rcvdata_t ng_nat_rcvdata;
57 static ng_disconnect_t ng_nat_disconnect;
59 static unsigned int ng_nat_translate_flags(unsigned int x);
61 /* Parse type for struct ng_nat_mode. */
62 static const struct ng_parse_struct_field ng_nat_mode_fields[]
63 = NG_NAT_MODE_INFO;
64 static const struct ng_parse_type ng_nat_mode_type = {
65 &ng_parse_struct_type,
66 &ng_nat_mode_fields
69 /* Parse type for 'description' field in structs. */
70 static const struct ng_parse_fixedstring_info ng_nat_description_info
71 = { NG_NAT_DESC_LENGTH };
72 static const struct ng_parse_type ng_nat_description_type = {
73 &ng_parse_fixedstring_type,
74 &ng_nat_description_info
77 /* Parse type for struct ng_nat_redirect_port. */
78 static const struct ng_parse_struct_field ng_nat_redirect_port_fields[]
79 = NG_NAT_REDIRECT_PORT_TYPE_INFO(&ng_nat_description_type);
80 static const struct ng_parse_type ng_nat_redirect_port_type = {
81 &ng_parse_struct_type,
82 &ng_nat_redirect_port_fields
85 /* Parse type for struct ng_nat_redirect_addr. */
86 static const struct ng_parse_struct_field ng_nat_redirect_addr_fields[]
87 = NG_NAT_REDIRECT_ADDR_TYPE_INFO(&ng_nat_description_type);
88 static const struct ng_parse_type ng_nat_redirect_addr_type = {
89 &ng_parse_struct_type,
90 &ng_nat_redirect_addr_fields
93 /* Parse type for struct ng_nat_redirect_proto. */
94 static const struct ng_parse_struct_field ng_nat_redirect_proto_fields[]
95 = NG_NAT_REDIRECT_PROTO_TYPE_INFO(&ng_nat_description_type);
96 static const struct ng_parse_type ng_nat_redirect_proto_type = {
97 &ng_parse_struct_type,
98 &ng_nat_redirect_proto_fields
101 /* Parse type for struct ng_nat_add_server. */
102 static const struct ng_parse_struct_field ng_nat_add_server_fields[]
103 = NG_NAT_ADD_SERVER_TYPE_INFO;
104 static const struct ng_parse_type ng_nat_add_server_type = {
105 &ng_parse_struct_type,
106 &ng_nat_add_server_fields
109 /* Parse type for one struct ng_nat_listrdrs_entry. */
110 static const struct ng_parse_struct_field ng_nat_listrdrs_entry_fields[]
111 = NG_NAT_LISTRDRS_ENTRY_TYPE_INFO(&ng_nat_description_type);
112 static const struct ng_parse_type ng_nat_listrdrs_entry_type = {
113 &ng_parse_struct_type,
114 &ng_nat_listrdrs_entry_fields
117 /* Parse type for 'redirects' array in struct ng_nat_list_redirects. */
118 static int
119 ng_nat_listrdrs_ary_getLength(const struct ng_parse_type *type,
120 const u_char *start, const u_char *buf)
122 const struct ng_nat_list_redirects *lr;
124 lr = (const struct ng_nat_list_redirects *)
125 (buf - offsetof(struct ng_nat_list_redirects, redirects));
126 return lr->total_count;
129 static const struct ng_parse_array_info ng_nat_listrdrs_ary_info = {
130 &ng_nat_listrdrs_entry_type,
131 &ng_nat_listrdrs_ary_getLength,
132 NULL
134 static const struct ng_parse_type ng_nat_listrdrs_ary_type = {
135 &ng_parse_array_type,
136 &ng_nat_listrdrs_ary_info
139 /* Parse type for struct ng_nat_list_redirects. */
140 static const struct ng_parse_struct_field ng_nat_list_redirects_fields[]
141 = NG_NAT_LIST_REDIRECTS_TYPE_INFO(&ng_nat_listrdrs_ary_type);
142 static const struct ng_parse_type ng_nat_list_redirects_type = {
143 &ng_parse_struct_type,
144 &ng_nat_list_redirects_fields
147 /* List of commands and how to convert arguments to/from ASCII. */
148 static const struct ng_cmdlist ng_nat_cmdlist[] = {
150 NGM_NAT_COOKIE,
151 NGM_NAT_SET_IPADDR,
152 "setaliasaddr",
153 &ng_parse_ipaddr_type,
154 NULL
157 NGM_NAT_COOKIE,
158 NGM_NAT_SET_MODE,
159 "setmode",
160 &ng_nat_mode_type,
161 NULL
164 NGM_NAT_COOKIE,
165 NGM_NAT_SET_TARGET,
166 "settarget",
167 &ng_parse_ipaddr_type,
168 NULL
171 NGM_NAT_COOKIE,
172 NGM_NAT_REDIRECT_PORT,
173 "redirectport",
174 &ng_nat_redirect_port_type,
175 &ng_parse_uint32_type
178 NGM_NAT_COOKIE,
179 NGM_NAT_REDIRECT_ADDR,
180 "redirectaddr",
181 &ng_nat_redirect_addr_type,
182 &ng_parse_uint32_type
185 NGM_NAT_COOKIE,
186 NGM_NAT_REDIRECT_PROTO,
187 "redirectproto",
188 &ng_nat_redirect_proto_type,
189 &ng_parse_uint32_type
192 NGM_NAT_COOKIE,
193 NGM_NAT_REDIRECT_DYNAMIC,
194 "redirectdynamic",
195 &ng_parse_uint32_type,
196 NULL
199 NGM_NAT_COOKIE,
200 NGM_NAT_REDIRECT_DELETE,
201 "redirectdelete",
202 &ng_parse_uint32_type,
203 NULL
206 NGM_NAT_COOKIE,
207 NGM_NAT_ADD_SERVER,
208 "addserver",
209 &ng_nat_add_server_type,
210 NULL
213 NGM_NAT_COOKIE,
214 NGM_NAT_LIST_REDIRECTS,
215 "listredirects",
216 NULL,
217 &ng_nat_list_redirects_type
220 NGM_NAT_COOKIE,
221 NGM_NAT_PROXY_RULE,
222 "proxyrule",
223 &ng_parse_string_type,
224 NULL
226 { 0 }
229 /* Netgraph node type descriptor. */
230 static struct ng_type typestruct = {
231 .version = NG_ABI_VERSION,
232 .name = NG_NAT_NODE_TYPE,
233 .constructor = ng_nat_constructor,
234 .rcvmsg = ng_nat_rcvmsg,
235 .shutdown = ng_nat_shutdown,
236 .newhook = ng_nat_newhook,
237 .rcvdata = ng_nat_rcvdata,
238 .disconnect = ng_nat_disconnect,
239 .cmdlist = ng_nat_cmdlist,
241 NETGRAPH_INIT(nat, &typestruct);
242 MODULE_DEPEND(ng_nat, libalias, 1, 1, 1);
244 /* Element for list of redirects. */
245 struct ng_nat_rdr_lst {
246 STAILQ_ENTRY(ng_nat_rdr_lst) entries;
247 struct alias_link *lnk;
248 struct ng_nat_listrdrs_entry rdr;
250 STAILQ_HEAD(rdrhead, ng_nat_rdr_lst);
252 /* Information we store for each node. */
253 struct ng_nat_priv {
254 node_p node; /* back pointer to node */
255 hook_p in; /* hook for demasquerading */
256 hook_p out; /* hook for masquerading */
257 struct libalias *lib; /* libalias handler */
258 uint32_t flags; /* status flags */
259 uint32_t rdrcount; /* number or redirects in list */
260 uint32_t nextid; /* for next in turn in list */
261 struct rdrhead redirhead; /* redirect list header */
263 typedef struct ng_nat_priv *priv_p;
265 /* Values of flags */
266 #define NGNAT_CONNECTED 0x1 /* We have both hooks connected */
267 #define NGNAT_ADDR_DEFINED 0x2 /* NGM_NAT_SET_IPADDR happened */
269 static int
270 ng_nat_constructor(node_p node)
272 priv_p priv;
274 /* Initialize private descriptor. */
275 priv = kmalloc(sizeof(*priv), M_NETGRAPH,
276 M_WAITOK | M_NULLOK | M_ZERO);
277 if (priv == NULL)
278 return (ENOMEM);
280 /* Init aliasing engine. */
281 priv->lib = LibAliasInit(NULL);
282 if (priv->lib == NULL) {
283 kfree(priv, M_NETGRAPH);
284 return (ENOMEM);
287 /* Set same ports on. */
288 (void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS,
289 PKT_ALIAS_SAME_PORTS);
291 /* Init redirects housekeeping. */
292 priv->rdrcount = 0;
293 priv->nextid = 1;
294 STAILQ_INIT(&priv->redirhead);
296 /* Link structs together. */
297 NG_NODE_SET_PRIVATE(node, priv);
298 priv->node = node;
301 * libalias is not thread safe, so our node
302 * must be single threaded.
304 NG_NODE_FORCE_WRITER(node);
306 return (0);
309 static int
310 ng_nat_newhook(node_p node, hook_p hook, const char *name)
312 const priv_p priv = NG_NODE_PRIVATE(node);
314 if (strcmp(name, NG_NAT_HOOK_IN) == 0) {
315 priv->in = hook;
316 } else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) {
317 priv->out = hook;
318 } else
319 return (EINVAL);
321 if (priv->out != NULL &&
322 priv->in != NULL)
323 priv->flags |= NGNAT_CONNECTED;
325 return(0);
328 static int
329 ng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook)
331 const priv_p priv = NG_NODE_PRIVATE(node);
332 struct ng_mesg *resp = NULL;
333 struct ng_mesg *msg;
334 int error = 0;
336 NGI_GET_MSG(item, msg);
338 switch (msg->header.typecookie) {
339 case NGM_NAT_COOKIE:
340 switch (msg->header.cmd) {
341 case NGM_NAT_SET_IPADDR:
343 struct in_addr *const ia = (struct in_addr *)msg->data;
345 if (msg->header.arglen < sizeof(*ia)) {
346 error = EINVAL;
347 break;
350 LibAliasSetAddress(priv->lib, *ia);
352 priv->flags |= NGNAT_ADDR_DEFINED;
354 break;
355 case NGM_NAT_SET_MODE:
357 struct ng_nat_mode *const mode =
358 (struct ng_nat_mode *)msg->data;
360 if (msg->header.arglen < sizeof(*mode)) {
361 error = EINVAL;
362 break;
365 if (LibAliasSetMode(priv->lib,
366 ng_nat_translate_flags(mode->flags),
367 ng_nat_translate_flags(mode->mask)) < 0) {
368 error = ENOMEM;
369 break;
372 break;
373 case NGM_NAT_SET_TARGET:
375 struct in_addr *const ia = (struct in_addr *)msg->data;
377 if (msg->header.arglen < sizeof(*ia)) {
378 error = EINVAL;
379 break;
382 LibAliasSetTarget(priv->lib, *ia);
384 break;
385 case NGM_NAT_REDIRECT_PORT:
387 struct ng_nat_rdr_lst *entry;
388 struct ng_nat_redirect_port *const rp =
389 (struct ng_nat_redirect_port *)msg->data;
391 if (msg->header.arglen < sizeof(*rp)) {
392 error = EINVAL;
393 break;
396 if ((entry = kmalloc(sizeof(struct ng_nat_rdr_lst),
397 M_NETGRAPH, M_WAITOK | M_NULLOK | M_ZERO)) == NULL) {
398 error = ENOMEM;
399 break;
402 /* Try actual redirect. */
403 entry->lnk = LibAliasRedirectPort(priv->lib,
404 rp->local_addr, htons(rp->local_port),
405 rp->remote_addr, htons(rp->remote_port),
406 rp->alias_addr, htons(rp->alias_port),
407 rp->proto);
409 if (entry->lnk == NULL) {
410 error = ENOMEM;
411 kfree(entry, M_NETGRAPH);
412 break;
415 /* Successful, save info in our internal list. */
416 entry->rdr.local_addr = rp->local_addr;
417 entry->rdr.alias_addr = rp->alias_addr;
418 entry->rdr.remote_addr = rp->remote_addr;
419 entry->rdr.local_port = rp->local_port;
420 entry->rdr.alias_port = rp->alias_port;
421 entry->rdr.remote_port = rp->remote_port;
422 entry->rdr.proto = rp->proto;
423 bcopy(rp->description, entry->rdr.description,
424 NG_NAT_DESC_LENGTH);
426 /* Safety precaution. */
427 entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0';
429 entry->rdr.id = priv->nextid++;
430 priv->rdrcount++;
432 /* Link to list of redirects. */
433 STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries);
435 /* Response with id of newly added entry. */
436 NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_WAITOK | M_NULLOK);
437 if (resp == NULL) {
438 error = ENOMEM;
439 break;
441 bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id));
443 break;
444 case NGM_NAT_REDIRECT_ADDR:
446 struct ng_nat_rdr_lst *entry;
447 struct ng_nat_redirect_addr *const ra =
448 (struct ng_nat_redirect_addr *)msg->data;
450 if (msg->header.arglen < sizeof(*ra)) {
451 error = EINVAL;
452 break;
455 if ((entry = kmalloc(sizeof(struct ng_nat_rdr_lst),
456 M_NETGRAPH, M_WAITOK | M_NULLOK | M_ZERO)) == NULL) {
457 error = ENOMEM;
458 break;
461 /* Try actual redirect. */
462 entry->lnk = LibAliasRedirectAddr(priv->lib,
463 ra->local_addr, ra->alias_addr);
465 if (entry->lnk == NULL) {
466 error = ENOMEM;
467 kfree(entry, M_NETGRAPH);
468 break;
471 /* Successful, save info in our internal list. */
472 entry->rdr.local_addr = ra->local_addr;
473 entry->rdr.alias_addr = ra->alias_addr;
474 entry->rdr.proto = NG_NAT_REDIRPROTO_ADDR;
475 bcopy(ra->description, entry->rdr.description,
476 NG_NAT_DESC_LENGTH);
478 /* Safety precaution. */
479 entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0';
481 entry->rdr.id = priv->nextid++;
482 priv->rdrcount++;
484 /* Link to list of redirects. */
485 STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries);
487 /* Response with id of newly added entry. */
488 NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_WAITOK | M_NULLOK);
489 if (resp == NULL) {
490 error = ENOMEM;
491 break;
493 bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id));
495 break;
496 case NGM_NAT_REDIRECT_PROTO:
498 struct ng_nat_rdr_lst *entry;
499 struct ng_nat_redirect_proto *const rp =
500 (struct ng_nat_redirect_proto *)msg->data;
502 if (msg->header.arglen < sizeof(*rp)) {
503 error = EINVAL;
504 break;
507 if ((entry = kmalloc(sizeof(struct ng_nat_rdr_lst),
508 M_NETGRAPH, M_WAITOK | M_NULLOK | M_ZERO)) == NULL) {
509 error = ENOMEM;
510 break;
513 /* Try actual redirect. */
514 entry->lnk = LibAliasRedirectProto(priv->lib,
515 rp->local_addr, rp->remote_addr,
516 rp->alias_addr, rp->proto);
518 if (entry->lnk == NULL) {
519 error = ENOMEM;
520 kfree(entry, M_NETGRAPH);
521 break;
524 /* Successful, save info in our internal list. */
525 entry->rdr.local_addr = rp->local_addr;
526 entry->rdr.alias_addr = rp->alias_addr;
527 entry->rdr.remote_addr = rp->remote_addr;
528 entry->rdr.proto = rp->proto;
529 bcopy(rp->description, entry->rdr.description,
530 NG_NAT_DESC_LENGTH);
532 /* Safety precaution. */
533 entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0';
535 entry->rdr.id = priv->nextid++;
536 priv->rdrcount++;
538 /* Link to list of redirects. */
539 STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries);
541 /* Response with id of newly added entry. */
542 NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_WAITOK | M_NULLOK);
543 if (resp == NULL) {
544 error = ENOMEM;
545 break;
547 bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id));
549 break;
550 case NGM_NAT_REDIRECT_DYNAMIC:
551 case NGM_NAT_REDIRECT_DELETE:
553 struct ng_nat_rdr_lst *entry;
554 uint32_t *const id = (uint32_t *)msg->data;
556 if (msg->header.arglen < sizeof(*id)) {
557 error = EINVAL;
558 break;
561 /* Find entry with supplied id. */
562 STAILQ_FOREACH(entry, &priv->redirhead, entries) {
563 if (entry->rdr.id == *id)
564 break;
567 /* Not found. */
568 if (entry == NULL) {
569 error = ENOENT;
570 break;
573 if (msg->header.cmd == NGM_NAT_REDIRECT_DYNAMIC) {
574 if (LibAliasRedirectDynamic(priv->lib,
575 entry->lnk) == -1) {
576 error = ENOTTY; /* XXX Something better? */
577 break;
579 } else { /* NGM_NAT_REDIRECT_DELETE */
580 LibAliasRedirectDelete(priv->lib, entry->lnk);
583 /* Delete entry from our internal list. */
584 priv->rdrcount--;
585 STAILQ_REMOVE(&priv->redirhead, entry, ng_nat_rdr_lst, entries);
586 kfree(entry, M_NETGRAPH);
588 break;
589 case NGM_NAT_ADD_SERVER:
591 struct ng_nat_rdr_lst *entry;
592 struct ng_nat_add_server *const as =
593 (struct ng_nat_add_server *)msg->data;
595 if (msg->header.arglen < sizeof(*as)) {
596 error = EINVAL;
597 break;
600 /* Find entry with supplied id. */
601 STAILQ_FOREACH(entry, &priv->redirhead, entries) {
602 if (entry->rdr.id == as->id)
603 break;
606 /* Not found. */
607 if (entry == NULL) {
608 error = ENOENT;
609 break;
612 if (LibAliasAddServer(priv->lib, entry->lnk,
613 as->addr, htons(as->port)) == -1) {
614 error = ENOMEM;
615 break;
618 entry->rdr.lsnat++;
620 break;
621 case NGM_NAT_LIST_REDIRECTS:
623 struct ng_nat_rdr_lst *entry;
624 struct ng_nat_list_redirects *ary;
625 int i = 0;
627 NG_MKRESPONSE(resp, msg, sizeof(*ary) +
628 (priv->rdrcount) * sizeof(*entry), M_WAITOK | M_NULLOK);
629 if (resp == NULL) {
630 error = ENOMEM;
631 break;
634 ary = (struct ng_nat_list_redirects *)resp->data;
635 ary->total_count = priv->rdrcount;
637 STAILQ_FOREACH(entry, &priv->redirhead, entries) {
638 bcopy(&entry->rdr, &ary->redirects[i++],
639 sizeof(struct ng_nat_listrdrs_entry));
642 break;
643 case NGM_NAT_PROXY_RULE:
645 char *cmd = (char *)msg->data;
647 if (msg->header.arglen < 6) {
648 error = EINVAL;
649 break;
652 if (LibAliasProxyRule(priv->lib, cmd) != 0)
653 error = ENOMEM;
655 break;
656 default:
657 error = EINVAL; /* unknown command */
658 break;
660 break;
661 default:
662 error = EINVAL; /* unknown cookie type */
663 break;
666 NG_RESPOND_MSG(error, node, item, resp);
667 NG_FREE_MSG(msg);
668 return (error);
671 static int
672 ng_nat_rcvdata(hook_p hook, item_p item )
674 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
675 struct mbuf *m;
676 struct ip *ip;
677 int rval, error = 0;
678 char *c;
680 /* We have no required hooks. */
681 if (!(priv->flags & NGNAT_CONNECTED)) {
682 NG_FREE_ITEM(item);
683 return (ENXIO);
686 /* We have no alias address yet to do anything. */
687 if (!(priv->flags & NGNAT_ADDR_DEFINED))
688 goto send;
690 m = NGI_M(item);
692 if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) {
693 NGI_M(item) = NULL; /* avoid double free */
694 NG_FREE_ITEM(item);
695 return (ENOBUFS);
698 NGI_M(item) = m;
700 c = mtod(m, char *);
701 ip = mtod(m, struct ip *);
703 KASSERT(m->m_pkthdr.len == ntohs(ip->ip_len),
704 ("ng_nat: ip_len != m_pkthdr.len"));
706 if (hook == priv->in) {
707 rval = LibAliasIn(priv->lib, c, m->m_len + M_TRAILINGSPACE(m));
708 if (rval != PKT_ALIAS_OK &&
709 rval != PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
710 NG_FREE_ITEM(item);
711 return (EINVAL);
713 } else if (hook == priv->out) {
714 rval = LibAliasOut(priv->lib, c, m->m_len + M_TRAILINGSPACE(m));
715 if (rval != PKT_ALIAS_OK) {
716 NG_FREE_ITEM(item);
717 return (EINVAL);
719 } else
720 panic("ng_nat: unknown hook!");
722 m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len);
724 if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
725 ip->ip_p == IPPROTO_TCP) {
726 struct tcphdr *th = (struct tcphdr *)((caddr_t)ip +
727 (ip->ip_hl << 2));
730 * Here is our terrible HACK.
732 * Sometimes LibAlias edits contents of TCP packet.
733 * In this case it needs to recompute full TCP
734 * checksum. However, the problem is that LibAlias
735 * doesn't have any idea about checksum offloading
736 * in kernel. To workaround this, we do not do
737 * checksumming in LibAlias, but only mark the
738 * packets in th_x2 field. If we receive a marked
739 * packet, we calculate correct checksum for it
740 * aware of offloading.
742 * Why do I do such a terrible hack instead of
743 * recalculating checksum for each packet?
744 * Because the previous checksum was not checked!
745 * Recalculating checksums for EVERY packet will
746 * hide ALL transmission errors. Yes, marked packets
747 * still suffer from this problem. But, sigh, natd(8)
748 * has this problem, too.
751 if (th->th_x2) {
752 th->th_x2 = 0;
753 ip->ip_len = ntohs(ip->ip_len);
754 th->th_sum = in_pseudo(ip->ip_src.s_addr,
755 ip->ip_dst.s_addr, htons(IPPROTO_TCP +
756 ip->ip_len - (ip->ip_hl << 2)));
758 if ((m->m_pkthdr.csum_flags & CSUM_TCP) == 0) {
759 m->m_pkthdr.csum_data = offsetof(struct tcphdr,
760 th_sum);
761 in_delayed_cksum(m);
763 ip->ip_len = htons(ip->ip_len);
767 send:
768 if (hook == priv->in)
769 NG_FWD_ITEM_HOOK(error, item, priv->out);
770 else
771 NG_FWD_ITEM_HOOK(error, item, priv->in);
773 return (error);
776 static int
777 ng_nat_shutdown(node_p node)
779 const priv_p priv = NG_NODE_PRIVATE(node);
781 NG_NODE_SET_PRIVATE(node, NULL);
782 NG_NODE_UNREF(node);
784 /* Free redirects list. */
785 while (!STAILQ_EMPTY(&priv->redirhead)) {
786 struct ng_nat_rdr_lst *entry = STAILQ_FIRST(&priv->redirhead);
787 STAILQ_REMOVE_HEAD(&priv->redirhead, entries);
788 kfree(entry, M_NETGRAPH);
791 /* Final free. */
792 LibAliasUninit(priv->lib);
793 kfree(priv, M_NETGRAPH);
795 return (0);
798 static int
799 ng_nat_disconnect(hook_p hook)
801 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
803 priv->flags &= ~NGNAT_CONNECTED;
805 if (hook == priv->out)
806 priv->out = NULL;
807 if (hook == priv->in)
808 priv->in = NULL;
810 if (priv->out == NULL && priv->in == NULL)
811 ng_rmnode_self(NG_HOOK_NODE(hook));
813 return (0);
816 static unsigned int
817 ng_nat_translate_flags(unsigned int x)
819 unsigned int res = 0;
821 if (x & NG_NAT_LOG)
822 res |= PKT_ALIAS_LOG;
823 if (x & NG_NAT_DENY_INCOMING)
824 res |= PKT_ALIAS_DENY_INCOMING;
825 if (x & NG_NAT_SAME_PORTS)
826 res |= PKT_ALIAS_SAME_PORTS;
827 if (x & NG_NAT_UNREGISTERED_ONLY)
828 res |= PKT_ALIAS_UNREGISTERED_ONLY;
829 if (x & NG_NAT_RESET_ON_ADDR_CHANGE)
830 res |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
831 if (x & NG_NAT_PROXY_ONLY)
832 res |= PKT_ALIAS_PROXY_ONLY;
833 if (x & NG_NAT_REVERSE)
834 res |= PKT_ALIAS_REVERSE;
836 return (res);