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