1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2008 Paolo Pisati
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/eventhandler.h>
35 #include <sys/malloc.h>
36 #include <sys/mbuf.h>
37 #include <sys/kernel.h>
38 #include <sys/lock.h>
39 #include <sys/module.h>
40 #include <sys/rwlock.h>
41 #include <sys/rmlock.h>
42
43 #include <netinet/libalias/alias.h>
44 #include <netinet/libalias/alias_local.h>
45
46 #include <net/if.h>
47 #include <net/if_var.h>
48 #include <netinet/in.h>
49 #include <netinet/ip.h>
50 #include <netinet/ip_var.h>
51 #include <netinet/ip_fw.h>
52 #include <netinet/tcp.h>
53 #include <netinet/udp.h>
54
55 #include <netpfil/ipfw/ip_fw_private.h>
56
57 #include <machine/in_cksum.h> /* XXX for in_cksum */
58
59 struct cfg_spool {
60 LIST_ENTRY(cfg_spool) _next; /* chain of spool instances */
61 struct in_addr addr;
62 uint16_t port;
63 };
64
65 /* Nat redirect configuration. */
66 struct cfg_redir {
67 LIST_ENTRY(cfg_redir) _next; /* chain of redir instances */
68 uint16_t mode; /* type of redirect mode */
69 uint16_t proto; /* protocol: tcp/udp */
70 struct in_addr laddr; /* local ip address */
71 struct in_addr paddr; /* public ip address */
72 struct in_addr raddr; /* remote ip address */
73 uint16_t lport; /* local port */
74 uint16_t pport; /* public port */
75 uint16_t rport; /* remote port */
76 uint16_t pport_cnt; /* number of public ports */
77 uint16_t rport_cnt; /* number of remote ports */
78 struct alias_link **alink;
79 u_int16_t spool_cnt; /* num of entry in spool chain */
80 /* chain of spool instances */
81 LIST_HEAD(spool_chain, cfg_spool) spool_chain;
82 };
83
84 /* Nat configuration data struct. */
85 struct cfg_nat {
86 /* chain of nat instances */
87 LIST_ENTRY(cfg_nat) _next;
88 int id; /* nat id */
89 struct in_addr ip; /* nat ip address */
90 struct libalias *lib; /* libalias instance */
91 int mode; /* aliasing mode */
92 int redir_cnt; /* number of entry in spool chain */
93 /* chain of redir instances */
94 LIST_HEAD(redir_chain, cfg_redir) redir_chain;
95 char if_name[IF_NAMESIZE]; /* interface name */
96 u_short alias_port_lo; /* low range for port aliasing */
97 u_short alias_port_hi; /* high range for port aliasing */
98 };
99
100 static eventhandler_tag ifaddr_event_tag;
101
102 static void
103 ifaddr_change(void *arg __unused, struct ifnet *ifp)
104 {
105 struct cfg_nat *ptr;
106 struct ifaddr *ifa;
107 struct ip_fw_chain *chain;
108
109 KASSERT(curvnet == ifp->if_vnet,
110 ("curvnet(%p) differs from iface vnet(%p)", curvnet, ifp->if_vnet));
111
112 if (V_ipfw_vnet_ready == 0 || V_ipfw_nat_ready == 0)
113 return;
114
115 chain = &V_layer3_chain;
116 IPFW_UH_WLOCK(chain);
117 /* Check every nat entry... */
118 LIST_FOREACH(ptr, &chain->nat, _next) {
119 struct epoch_tracker et;
120
121 /* ...using nic 'ifp->if_xname' as dynamic alias address. */
122 if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0)
123 continue;
124 NET_EPOCH_ENTER(et);
125 CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
126 if (ifa->ifa_addr == NULL)
127 continue;
128 if (ifa->ifa_addr->sa_family != AF_INET)
129 continue;
130 IPFW_WLOCK(chain);
131 ptr->ip = ((struct sockaddr_in *)
132 (ifa->ifa_addr))->sin_addr;
133 LibAliasSetAddress(ptr->lib, ptr->ip);
134 IPFW_WUNLOCK(chain);
135 }
136 NET_EPOCH_EXIT(et);
137 }
138 IPFW_UH_WUNLOCK(chain);
139 }
140
141 /*
142 * delete the pointers for nat entry ix, or all of them if ix < 0
143 */
144 static void
145 flush_nat_ptrs(struct ip_fw_chain *chain, const int ix)
146 {
147 ipfw_insn_nat *cmd;
148 int i;
149
150 IPFW_WLOCK_ASSERT(chain);
151 for (i = 0; i < chain->n_rules; i++) {
152 cmd = (ipfw_insn_nat *)ipfw_get_action(chain->map[i]);
153 if (cmd->o.opcode == O_NAT && cmd->nat != NULL &&
154 (ix < 0 || cmd->nat->id == ix))
155 cmd->nat = NULL;
156 }
157 }
158
159 static void
160 del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head)
161 {
162 struct cfg_redir *r, *tmp_r;
163 struct cfg_spool *s, *tmp_s;
164 int i, num;
165
166 LIST_FOREACH_SAFE(r, head, _next, tmp_r) {
167 num = 1; /* Number of alias_link to delete. */
168 switch (r->mode) {
169 case NAT44_REDIR_PORT:
170 num = r->pport_cnt;
171 /* FALLTHROUGH */
172 case NAT44_REDIR_ADDR:
173 case NAT44_REDIR_PROTO:
174 /* Delete all libalias redirect entry. */
175 for (i = 0; i < num; i++)
176 LibAliasRedirectDelete(n->lib, r->alink[i]);
177 /* Del spool cfg if any. */
178 LIST_FOREACH_SAFE(s, &r->spool_chain, _next, tmp_s) {
179 LIST_REMOVE(s, _next);
180 free(s, M_IPFW);
181 }
182 free(r->alink, M_IPFW);
183 LIST_REMOVE(r, _next);
184 free(r, M_IPFW);
185 break;
186 default:
187 printf("unknown redirect mode: %u\n", r->mode);
188 /* XXX - panic?!?!? */
189 break;
190 }
191 }
192 }
193
194 static int
195 add_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
196 {
197 struct cfg_redir *r;
198 struct cfg_spool *s;
199 struct nat44_cfg_redir *ser_r;
200 struct nat44_cfg_spool *ser_s;
201
202 int cnt, off, i;
203
204 for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) {
205 ser_r = (struct nat44_cfg_redir *)&buf[off];
206 r = malloc(sizeof(*r), M_IPFW, M_WAITOK | M_ZERO);
207 r->mode = ser_r->mode;
208 r->laddr = ser_r->laddr;
209 r->paddr = ser_r->paddr;
210 r->raddr = ser_r->raddr;
211 r->lport = ser_r->lport;
212 r->pport = ser_r->pport;
213 r->rport = ser_r->rport;
214 r->pport_cnt = ser_r->pport_cnt;
215 r->rport_cnt = ser_r->rport_cnt;
216 r->proto = ser_r->proto;
217 r->spool_cnt = ser_r->spool_cnt;
218 //memcpy(r, ser_r, SOF_REDIR);
219 LIST_INIT(&r->spool_chain);
220 off += sizeof(struct nat44_cfg_redir);
221 r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt,
222 M_IPFW, M_WAITOK | M_ZERO);
223 switch (r->mode) {
224 case NAT44_REDIR_ADDR:
225 r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr,
226 r->paddr);
227 break;
228 case NAT44_REDIR_PORT:
229 for (i = 0 ; i < r->pport_cnt; i++) {
230 /* If remotePort is all ports, set it to 0. */
231 u_short remotePortCopy = r->rport + i;
232 if (r->rport_cnt == 1 && r->rport == 0)
233 remotePortCopy = 0;
234 r->alink[i] = LibAliasRedirectPort(ptr->lib,
235 r->laddr, htons(r->lport + i), r->raddr,
236 htons(remotePortCopy), r->paddr,
237 htons(r->pport + i), r->proto);
238 if (r->alink[i] == NULL) {
239 r->alink[0] = NULL;
240 break;
241 }
242 }
243 break;
244 case NAT44_REDIR_PROTO:
245 r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr,
246 r->raddr, r->paddr, r->proto);
247 break;
248 default:
249 printf("unknown redirect mode: %u\n", r->mode);
250 break;
251 }
252 if (r->alink[0] == NULL) {
253 printf("LibAliasRedirect* returned NULL\n");
254 free(r->alink, M_IPFW);
255 free(r, M_IPFW);
256 return (EINVAL);
257 }
258 /* LSNAT handling. */
259 for (i = 0; i < r->spool_cnt; i++) {
260 ser_s = (struct nat44_cfg_spool *)&buf[off];
261 s = malloc(sizeof(*s), M_IPFW, M_WAITOK | M_ZERO);
262 s->addr = ser_s->addr;
263 s->port = ser_s->port;
264 LibAliasAddServer(ptr->lib, r->alink[0],
265 s->addr, htons(s->port));
266 off += sizeof(struct nat44_cfg_spool);
267 /* Hook spool entry. */
268 LIST_INSERT_HEAD(&r->spool_chain, s, _next);
269 }
270 /* And finally hook this redir entry. */
271 LIST_INSERT_HEAD(&ptr->redir_chain, r, _next);
272 }
273
274 return (0);
275 }
276
277 static void
278 free_nat_instance(struct cfg_nat *ptr)
279 {
280
281 del_redir_spool_cfg(ptr, &ptr->redir_chain);
282 LibAliasUninit(ptr->lib);
283 free(ptr, M_IPFW);
284 }
285
286 /*
287 * ipfw_nat - perform mbuf header translation.
288 *
289 * Note V_layer3_chain has to be locked while calling ipfw_nat() in
290 * 'global' operation mode (t == NULL).
291 *
292 */
293 static int
294 ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m)
295 {
296 struct mbuf *mcl;
297 struct ip *ip;
298 /* XXX - libalias duct tape */
299 int ldt, retval, found;
300 struct ip_fw_chain *chain;
301 char *c;
302
303 ldt = 0;
304 retval = 0;
305 mcl = m_megapullup(m, m->m_pkthdr.len);
306 if (mcl == NULL) {
307 args->m = NULL;
308 return (IP_FW_DENY);
309 }
310 M_ASSERTMAPPED(mcl);
311 ip = mtod(mcl, struct ip *);
312
313 /*
314 * XXX - Libalias checksum offload 'duct tape':
315 *
316 * locally generated packets have only pseudo-header checksum
317 * calculated and libalias will break it[1], so mark them for
318 * later fix. Moreover there are cases when libalias modifies
319 * tcp packet data[2], mark them for later fix too.
320 *
321 * [1] libalias was never meant to run in kernel, so it does
322 * not have any knowledge about checksum offloading, and
323 * expects a packet with a full internet checksum.
324 * Unfortunately, packets generated locally will have just the
325 * pseudo header calculated, and when libalias tries to adjust
326 * the checksum it will actually compute a wrong value.
327 *
328 * [2] when libalias modifies tcp's data content, full TCP
329 * checksum has to be recomputed: the problem is that
330 * libalias does not have any idea about checksum offloading.
331 * To work around this, we do not do checksumming in LibAlias,
332 * but only mark the packets in th_x2 field. If we receive a
333 * marked packet, we calculate correct checksum for it
334 * aware of offloading. Why such a terrible hack instead of
335 * recalculating checksum for each packet?
336 * Because the previous checksum was not checked!
337 * Recalculating checksums for EVERY packet will hide ALL
338 * transmission errors. Yes, marked packets still suffer from
339 * this problem. But, sigh, natd(8) has this problem, too.
340 *
341 * TODO: -make libalias mbuf aware (so
342 * it can handle delayed checksum and tso)
343 */
344
345 if (mcl->m_pkthdr.rcvif == NULL &&
346 mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA)
347 ldt = 1;
348
349 c = mtod(mcl, char *);
350
351 /* Check if this is 'global' instance */
352 if (t == NULL) {
353 if (args->flags & IPFW_ARGS_IN) {
354 /* Wrong direction, skip processing */
355 args->m = mcl;
356 return (IP_FW_NAT);
357 }
358
359 found = 0;
360 chain = &V_layer3_chain;
361 IPFW_RLOCK_ASSERT(chain);
362 /* Check every nat entry... */
363 LIST_FOREACH(t, &chain->nat, _next) {
364 if ((t->mode & PKT_ALIAS_SKIP_GLOBAL) != 0)
365 continue;
366 retval = LibAliasOutTry(t->lib, c,
367 mcl->m_len + M_TRAILINGSPACE(mcl), 0);
368 if (retval == PKT_ALIAS_OK) {
369 /* Nat instance recognises state */
370 found = 1;
371 break;
372 }
373 }
374 if (found != 1) {
375 /* No instance found, return ignore */
376 args->m = mcl;
377 return (IP_FW_NAT);
378 }
379 } else {
380 if (args->flags & IPFW_ARGS_IN)
381 retval = LibAliasIn(t->lib, c,
382 mcl->m_len + M_TRAILINGSPACE(mcl));
383 else
384 retval = LibAliasOut(t->lib, c,
385 mcl->m_len + M_TRAILINGSPACE(mcl));
386 }
387
388 /*
389 * We drop packet when:
390 * 1. libalias returns PKT_ALIAS_ERROR;
391 * 2. For incoming packets:
392 * a) for unresolved fragments;
393 * b) libalias returns PKT_ALIAS_IGNORED and
394 * PKT_ALIAS_DENY_INCOMING flag is set.
395 */
396 if (retval == PKT_ALIAS_ERROR ||
397 ((args->flags & IPFW_ARGS_IN) &&
398 (retval == PKT_ALIAS_UNRESOLVED_FRAGMENT ||
399 (retval == PKT_ALIAS_IGNORED &&
400 (t->mode & PKT_ALIAS_DENY_INCOMING) != 0)))) {
401 /* XXX - should i add some logging? */
402 m_free(mcl);
403 args->m = NULL;
404 return (IP_FW_DENY);
405 }
406
407 if (retval == PKT_ALIAS_RESPOND)
408 mcl->m_flags |= M_SKIP_FIREWALL;
409 mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len);
410
411 /*
412 * XXX - libalias checksum offload
413 * 'duct tape' (see above)
414 */
415
416 if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
417 ip->ip_p == IPPROTO_TCP) {
418 struct tcphdr *th;
419
420 th = (struct tcphdr *)(ip + 1);
421 if (th->th_x2 & (TH_RES1 >> 8))
422 ldt = 1;
423 }
424
425 if (ldt) {
426 struct tcphdr *th;
427 struct udphdr *uh;
428 uint16_t ip_len, cksum;
429
430 ip_len = ntohs(ip->ip_len);
431 cksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
432 htons(ip->ip_p + ip_len - (ip->ip_hl << 2)));
433
434 switch (ip->ip_p) {
435 case IPPROTO_TCP:
436 th = (struct tcphdr *)(ip + 1);
437 /*
438 * Maybe it was set in
439 * libalias...
440 */
441 th->th_x2 &= ~(TH_RES1 >> 8);
442 th->th_sum = cksum;
443 mcl->m_pkthdr.csum_data =
444 offsetof(struct tcphdr, th_sum);
445 break;
446 case IPPROTO_UDP:
447 uh = (struct udphdr *)(ip + 1);
448 uh->uh_sum = cksum;
449 mcl->m_pkthdr.csum_data =
450 offsetof(struct udphdr, uh_sum);
451 break;
452 }
453 /* No hw checksum offloading: do it ourselves */
454 if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) {
455 in_delayed_cksum(mcl);
456 mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
457 }
458 }
459 args->m = mcl;
460 return (IP_FW_NAT);
461 }
462
463 static struct cfg_nat *
464 lookup_nat(struct nat_list *l, int nat_id)
465 {
466 struct cfg_nat *res;
467
468 LIST_FOREACH(res, l, _next) {
469 if (res->id == nat_id)
470 break;
471 }
472 return res;
473 }
474
475 static struct cfg_nat *
476 lookup_nat_name(struct nat_list *l, char *name)
477 {
478 struct cfg_nat *res;
479 int id;
480 char *errptr;
481
482 id = strtol(name, &errptr, 10);
483 if (id == 0 || *errptr != '\0')
484 return (NULL);
485
486 LIST_FOREACH(res, l, _next) {
487 if (res->id == id)
488 break;
489 }
490 return (res);
491 }
492
493 /* IP_FW3 configuration routines */
494
495 static void
496 nat44_config(struct ip_fw_chain *chain, struct nat44_cfg_nat *ucfg)
497 {
498 struct cfg_nat *ptr, *tcfg;
499 int gencnt;
500
501 /*
502 * Find/create nat rule.
503 */
504 IPFW_UH_WLOCK(chain);
505 gencnt = chain->gencnt;
506 ptr = lookup_nat_name(&chain->nat, ucfg->name);
507 if (ptr == NULL) {
508 IPFW_UH_WUNLOCK(chain);
509 /* New rule: allocate and init new instance. */
510 ptr = malloc(sizeof(struct cfg_nat), M_IPFW, M_WAITOK | M_ZERO);
511 ptr->lib = LibAliasInit(NULL);
512 LIST_INIT(&ptr->redir_chain);
513 } else {
514 /* Entry already present: temporarily unhook it. */
515 IPFW_WLOCK(chain);
516 LIST_REMOVE(ptr, _next);
517 flush_nat_ptrs(chain, ptr->id);
518 IPFW_WUNLOCK(chain);
519 IPFW_UH_WUNLOCK(chain);
520 }
521
522 /*
523 * Basic nat (re)configuration.
524 */
525 ptr->id = strtol(ucfg->name, NULL, 10);
526 /*
527 * XXX - what if this rule doesn't nat any ip and just
528 * redirect?
529 * do we set aliasaddress to 0.0.0.0?
530 */
531 ptr->ip = ucfg->ip;
532 ptr->redir_cnt = ucfg->redir_cnt;
533 ptr->mode = ucfg->mode;
534 ptr->alias_port_lo = ucfg->alias_port_lo;
535 ptr->alias_port_hi = ucfg->alias_port_hi;
536 strlcpy(ptr->if_name, ucfg->if_name, sizeof(ptr->if_name));
537 LibAliasSetMode(ptr->lib, ptr->mode, ~0);
538 LibAliasSetAddress(ptr->lib, ptr->ip);
539 LibAliasSetAliasPortRange(ptr->lib, ptr->alias_port_lo, ptr->alias_port_hi);
540
541 /*
542 * Redir and LSNAT configuration.
543 */
544 /* Delete old cfgs. */
545 del_redir_spool_cfg(ptr, &ptr->redir_chain);
546 /* Add new entries. */
547 add_redir_spool_cfg((char *)(ucfg + 1), ptr);
548 IPFW_UH_WLOCK(chain);
549
550 /* Extra check to avoid race with another ipfw_nat_cfg() */
551 tcfg = NULL;
552 if (gencnt != chain->gencnt)
553 tcfg = lookup_nat_name(&chain->nat, ucfg->name);
554 IPFW_WLOCK(chain);
555 if (tcfg != NULL)
556 LIST_REMOVE(tcfg, _next);
557 LIST_INSERT_HEAD(&chain->nat, ptr, _next);
558 IPFW_WUNLOCK(chain);
559 chain->gencnt++;
560
561 IPFW_UH_WUNLOCK(chain);
562
563 if (tcfg != NULL)
564 free_nat_instance(ptr);
565 }
566
567 /*
568 * Creates/configure nat44 instance
569 * Data layout (v0)(current):
570 * Request: [ ipfw_obj_header nat44_cfg_nat .. ]
571 *
572 * Returns 0 on success
573 */
574 static int
575 nat44_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
576 struct sockopt_data *sd)
577 {
578 ipfw_obj_header *oh;
579 struct nat44_cfg_nat *ucfg;
580 int id;
581 size_t read;
582 char *errptr;
583
584 /* Check minimum header size */
585 if (sd->valsize < (sizeof(*oh) + sizeof(*ucfg)))
586 return (EINVAL);
587
588 oh = (ipfw_obj_header *)sd->kbuf;
589
590 /* Basic length checks for TLVs */
591 if (oh->ntlv.head.length != sizeof(oh->ntlv))
592 return (EINVAL);
593
594 ucfg = (struct nat44_cfg_nat *)(oh + 1);
595
596 /* Check if name is properly terminated and looks like number */
597 if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
598 return (EINVAL);
599 id = strtol(ucfg->name, &errptr, 10);
600 if (id == 0 || *errptr != '\0')
601 return (EINVAL);
602
603 read = sizeof(*oh) + sizeof(*ucfg);
604 /* Check number of redirs */
605 if (sd->valsize < read + ucfg->redir_cnt*sizeof(struct nat44_cfg_redir))
606 return (EINVAL);
607
608 nat44_config(chain, ucfg);
609 return (0);
610 }
611
612 /*
613 * Destroys given nat instances.
614 * Data layout (v0)(current):
615 * Request: [ ipfw_obj_header ]
616 *
617 * Returns 0 on success
618 */
619 static int
620 nat44_destroy(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
621 struct sockopt_data *sd)
622 {
623 ipfw_obj_header *oh;
624 struct cfg_nat *ptr;
625 ipfw_obj_ntlv *ntlv;
626
627 /* Check minimum header size */
628 if (sd->valsize < sizeof(*oh))
629 return (EINVAL);
630
631 oh = (ipfw_obj_header *)sd->kbuf;
632
633 /* Basic length checks for TLVs */
634 if (oh->ntlv.head.length != sizeof(oh->ntlv))
635 return (EINVAL);
636
637 ntlv = &oh->ntlv;
638 /* Check if name is properly terminated */
639 if (strnlen(ntlv->name, sizeof(ntlv->name)) == sizeof(ntlv->name))
640 return (EINVAL);
641
642 IPFW_UH_WLOCK(chain);
643 ptr = lookup_nat_name(&chain->nat, ntlv->name);
644 if (ptr == NULL) {
645 IPFW_UH_WUNLOCK(chain);
646 return (ESRCH);
647 }
648 IPFW_WLOCK(chain);
649 LIST_REMOVE(ptr, _next);
650 flush_nat_ptrs(chain, ptr->id);
651 IPFW_WUNLOCK(chain);
652 IPFW_UH_WUNLOCK(chain);
653
654 free_nat_instance(ptr);
655
656 return (0);
657 }
658
659 static void
660 export_nat_cfg(struct cfg_nat *ptr, struct nat44_cfg_nat *ucfg)
661 {
662
663 snprintf(ucfg->name, sizeof(ucfg->name), "%d", ptr->id);
664 ucfg->ip = ptr->ip;
665 ucfg->redir_cnt = ptr->redir_cnt;
666 ucfg->mode = ptr->mode;
667 ucfg->alias_port_lo = ptr->alias_port_lo;
668 ucfg->alias_port_hi = ptr->alias_port_hi;
669 strlcpy(ucfg->if_name, ptr->if_name, sizeof(ucfg->if_name));
670 }
671
672 /*
673 * Gets config for given nat instance
674 * Data layout (v0)(current):
675 * Request: [ ipfw_obj_header nat44_cfg_nat .. ]
676 *
677 * Returns 0 on success
678 */
679 static int
680 nat44_get_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
681 struct sockopt_data *sd)
682 {
683 ipfw_obj_header *oh;
684 struct nat44_cfg_nat *ucfg;
685 struct cfg_nat *ptr;
686 struct cfg_redir *r;
687 struct cfg_spool *s;
688 struct nat44_cfg_redir *ser_r;
689 struct nat44_cfg_spool *ser_s;
690 size_t sz;
691
692 sz = sizeof(*oh) + sizeof(*ucfg);
693 /* Check minimum header size */
694 if (sd->valsize < sz)
695 return (EINVAL);
696
697 oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
698
699 /* Basic length checks for TLVs */
700 if (oh->ntlv.head.length != sizeof(oh->ntlv))
701 return (EINVAL);
702
703 ucfg = (struct nat44_cfg_nat *)(oh + 1);
704
705 /* Check if name is properly terminated */
706 if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
707 return (EINVAL);
708
709 IPFW_UH_RLOCK(chain);
710 ptr = lookup_nat_name(&chain->nat, ucfg->name);
711 if (ptr == NULL) {
712 IPFW_UH_RUNLOCK(chain);
713 return (ESRCH);
714 }
715
716 export_nat_cfg(ptr, ucfg);
717
718 /* Estimate memory amount */
719 sz = sizeof(ipfw_obj_header) + sizeof(struct nat44_cfg_nat);
720 LIST_FOREACH(r, &ptr->redir_chain, _next) {
721 sz += sizeof(struct nat44_cfg_redir);
722 LIST_FOREACH(s, &r->spool_chain, _next)
723 sz += sizeof(struct nat44_cfg_spool);
724 }
725
726 ucfg->size = sz;
727 if (sd->valsize < sz) {
728 /*
729 * Submitted buffer size is not enough.
730 * WE've already filled in @ucfg structure with
731 * relevant info including size, so we
732 * can return. Buffer will be flushed automatically.
733 */
734 IPFW_UH_RUNLOCK(chain);
735 return (ENOMEM);
736 }
737
738 /* Size OK, let's copy data */
739 LIST_FOREACH(r, &ptr->redir_chain, _next) {
740 ser_r = (struct nat44_cfg_redir *)ipfw_get_sopt_space(sd,
741 sizeof(*ser_r));
742 ser_r->mode = r->mode;
743 ser_r->laddr = r->laddr;
744 ser_r->paddr = r->paddr;
745 ser_r->raddr = r->raddr;
746 ser_r->lport = r->lport;
747 ser_r->pport = r->pport;
748 ser_r->rport = r->rport;
749 ser_r->pport_cnt = r->pport_cnt;
750 ser_r->rport_cnt = r->rport_cnt;
751 ser_r->proto = r->proto;
752 ser_r->spool_cnt = r->spool_cnt;
753
754 LIST_FOREACH(s, &r->spool_chain, _next) {
755 ser_s = (struct nat44_cfg_spool *)ipfw_get_sopt_space(
756 sd, sizeof(*ser_s));
757
758 ser_s->addr = s->addr;
759 ser_s->port = s->port;
760 }
761 }
762
763 IPFW_UH_RUNLOCK(chain);
764
765 return (0);
766 }
767
768 /*
769 * Lists all nat44 instances currently available in kernel.
770 * Data layout (v0)(current):
771 * Request: [ ipfw_obj_lheader ]
772 * Reply: [ ipfw_obj_lheader nat44_cfg_nat x N ]
773 *
774 * Returns 0 on success
775 */
776 static int
777 nat44_list_nat(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
778 struct sockopt_data *sd)
779 {
780 ipfw_obj_lheader *olh;
781 struct nat44_cfg_nat *ucfg;
782 struct cfg_nat *ptr;
783 int nat_count;
784
785 /* Check minimum header size */
786 if (sd->valsize < sizeof(ipfw_obj_lheader))
787 return (EINVAL);
788
789 olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh));
790 IPFW_UH_RLOCK(chain);
791 nat_count = 0;
792 LIST_FOREACH(ptr, &chain->nat, _next)
793 nat_count++;
794
795 olh->count = nat_count;
796 olh->objsize = sizeof(struct nat44_cfg_nat);
797 olh->size = sizeof(*olh) + olh->count * olh->objsize;
798
799 if (sd->valsize < olh->size) {
800 IPFW_UH_RUNLOCK(chain);
801 return (ENOMEM);
802 }
803
804 LIST_FOREACH(ptr, &chain->nat, _next) {
805 ucfg = (struct nat44_cfg_nat *)ipfw_get_sopt_space(sd,
806 sizeof(*ucfg));
807 export_nat_cfg(ptr, ucfg);
808 }
809
810 IPFW_UH_RUNLOCK(chain);
811
812 return (0);
813 }
814
815 /*
816 * Gets log for given nat instance
817 * Data layout (v0)(current):
818 * Request: [ ipfw_obj_header nat44_cfg_nat ]
819 * Reply: [ ipfw_obj_header nat44_cfg_nat LOGBUFFER ]
820 *
821 * Returns 0 on success
822 */
823 static int
824 nat44_get_log(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
825 struct sockopt_data *sd)
826 {
827 ipfw_obj_header *oh;
828 struct nat44_cfg_nat *ucfg;
829 struct cfg_nat *ptr;
830 void *pbuf;
831 size_t sz;
832
833 sz = sizeof(*oh) + sizeof(*ucfg);
834 /* Check minimum header size */
835 if (sd->valsize < sz)
836 return (EINVAL);
837
838 oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
839
840 /* Basic length checks for TLVs */
841 if (oh->ntlv.head.length != sizeof(oh->ntlv))
842 return (EINVAL);
843
844 ucfg = (struct nat44_cfg_nat *)(oh + 1);
845
846 /* Check if name is properly terminated */
847 if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
848 return (EINVAL);
849
850 IPFW_UH_RLOCK(chain);
851 ptr = lookup_nat_name(&chain->nat, ucfg->name);
852 if (ptr == NULL) {
853 IPFW_UH_RUNLOCK(chain);
854 return (ESRCH);
855 }
856
857 if (ptr->lib->logDesc == NULL) {
858 IPFW_UH_RUNLOCK(chain);
859 return (ENOENT);
860 }
861
862 export_nat_cfg(ptr, ucfg);
863
864 /* Estimate memory amount */
865 ucfg->size = sizeof(struct nat44_cfg_nat) + LIBALIAS_BUF_SIZE;
866 if (sd->valsize < sz + sizeof(*oh)) {
867 /*
868 * Submitted buffer size is not enough.
869 * WE've already filled in @ucfg structure with
870 * relevant info including size, so we
871 * can return. Buffer will be flushed automatically.
872 */
873 IPFW_UH_RUNLOCK(chain);
874 return (ENOMEM);
875 }
876
877 pbuf = (void *)ipfw_get_sopt_space(sd, LIBALIAS_BUF_SIZE);
878 memcpy(pbuf, ptr->lib->logDesc, LIBALIAS_BUF_SIZE);
879
880 IPFW_UH_RUNLOCK(chain);
881
882 return (0);
883 }
884
885 static struct ipfw_sopt_handler scodes[] = {
886 { IP_FW_NAT44_XCONFIG, 0, HDIR_SET, nat44_cfg },
887 { IP_FW_NAT44_DESTROY, 0, HDIR_SET, nat44_destroy },
888 { IP_FW_NAT44_XGETCONFIG, 0, HDIR_GET, nat44_get_cfg },
889 { IP_FW_NAT44_LIST_NAT, 0, HDIR_GET, nat44_list_nat },
890 { IP_FW_NAT44_XGETLOG, 0, HDIR_GET, nat44_get_log },
891 };
892
893 /*
894 * Legacy configuration routines
895 */
896
897 struct cfg_spool_legacy {
898 LIST_ENTRY(cfg_spool_legacy) _next;
899 struct in_addr addr;
900 u_short port;
901 };
902
903 struct cfg_redir_legacy {
904 LIST_ENTRY(cfg_redir) _next;
905 u_int16_t mode;
906 struct in_addr laddr;
907 struct in_addr paddr;
908 struct in_addr raddr;
909 u_short lport;
910 u_short pport;
911 u_short rport;
912 u_short pport_cnt;
913 u_short rport_cnt;
914 int proto;
915 struct alias_link **alink;
916 u_int16_t spool_cnt;
917 LIST_HEAD(, cfg_spool_legacy) spool_chain;
918 };
919
920 struct cfg_nat_legacy {
921 LIST_ENTRY(cfg_nat_legacy) _next;
922 int id;
923 struct in_addr ip;
924 char if_name[IF_NAMESIZE];
925 int mode;
926 struct libalias *lib;
927 int redir_cnt;
928 LIST_HEAD(, cfg_redir_legacy) redir_chain;
929 };
930
931 static int
932 ipfw_nat_cfg(struct sockopt *sopt)
933 {
934 struct cfg_nat_legacy *cfg;
935 struct nat44_cfg_nat *ucfg;
936 struct cfg_redir_legacy *rdir;
937 struct nat44_cfg_redir *urdir;
938 char *buf;
939 size_t len, len2;
940 int error, i;
941
942 len = sopt->sopt_valsize;
943 len2 = len + 128;
944
945 /*
946 * Allocate 2x buffer to store converted structures.
947 * new redir_cfg has shrunk, so we're sure that
948 * new buffer size is enough.
949 */
950 buf = malloc(roundup2(len, 8) + len2, M_TEMP, M_WAITOK | M_ZERO);
951 error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat_legacy));
952 if (error != 0)
953 goto out;
954
955 cfg = (struct cfg_nat_legacy *)buf;
956 if (cfg->id < 0) {
957 error = EINVAL;
958 goto out;
959 }
960
961 ucfg = (struct nat44_cfg_nat *)&buf[roundup2(len, 8)];
962 snprintf(ucfg->name, sizeof(ucfg->name), "%d", cfg->id);
963 strlcpy(ucfg->if_name, cfg->if_name, sizeof(ucfg->if_name));
964 ucfg->ip = cfg->ip;
965 ucfg->mode = cfg->mode;
966 ucfg->redir_cnt = cfg->redir_cnt;
967
968 if (len < sizeof(*cfg) + cfg->redir_cnt * sizeof(*rdir)) {
969 error = EINVAL;
970 goto out;
971 }
972
973 urdir = (struct nat44_cfg_redir *)(ucfg + 1);
974 rdir = (struct cfg_redir_legacy *)(cfg + 1);
975 for (i = 0; i < cfg->redir_cnt; i++) {
976 urdir->mode = rdir->mode;
977 urdir->laddr = rdir->laddr;
978 urdir->paddr = rdir->paddr;
979 urdir->raddr = rdir->raddr;
980 urdir->lport = rdir->lport;
981 urdir->pport = rdir->pport;
982 urdir->rport = rdir->rport;
983 urdir->pport_cnt = rdir->pport_cnt;
984 urdir->rport_cnt = rdir->rport_cnt;
985 urdir->proto = rdir->proto;
986 urdir->spool_cnt = rdir->spool_cnt;
987
988 urdir++;
989 rdir++;
990 }
991
992 nat44_config(&V_layer3_chain, ucfg);
993
994 out:
995 free(buf, M_TEMP);
996 return (error);
997 }
998
999 static int
1000 ipfw_nat_del(struct sockopt *sopt)
1001 {
1002 struct cfg_nat *ptr;
1003 struct ip_fw_chain *chain = &V_layer3_chain;
1004 int i;
1005
1006 sooptcopyin(sopt, &i, sizeof i, sizeof i);
1007 /* XXX validate i */
1008 IPFW_UH_WLOCK(chain);
1009 ptr = lookup_nat(&chain->nat, i);
1010 if (ptr == NULL) {
1011 IPFW_UH_WUNLOCK(chain);
1012 return (EINVAL);
1013 }
1014 IPFW_WLOCK(chain);
1015 LIST_REMOVE(ptr, _next);
1016 flush_nat_ptrs(chain, i);
1017 IPFW_WUNLOCK(chain);
1018 IPFW_UH_WUNLOCK(chain);
1019 free_nat_instance(ptr);
1020 return (0);
1021 }
1022
1023 static int
1024 ipfw_nat_get_cfg(struct sockopt *sopt)
1025 {
1026 struct ip_fw_chain *chain = &V_layer3_chain;
1027 struct cfg_nat *n;
1028 struct cfg_nat_legacy *ucfg;
1029 struct cfg_redir *r;
1030 struct cfg_spool *s;
1031 struct cfg_redir_legacy *ser_r;
1032 struct cfg_spool_legacy *ser_s;
1033 char *data;
1034 int gencnt, nat_cnt, len, error;
1035
1036 nat_cnt = 0;
1037 len = sizeof(nat_cnt);
1038
1039 IPFW_UH_RLOCK(chain);
1040 retry:
1041 gencnt = chain->gencnt;
1042 /* Estimate memory amount */
1043 LIST_FOREACH(n, &chain->nat, _next) {
1044 nat_cnt++;
1045 len += sizeof(struct cfg_nat_legacy);
1046 LIST_FOREACH(r, &n->redir_chain, _next) {
1047 len += sizeof(struct cfg_redir_legacy);
1048 LIST_FOREACH(s, &r->spool_chain, _next)
1049 len += sizeof(struct cfg_spool_legacy);
1050 }
1051 }
1052 IPFW_UH_RUNLOCK(chain);
1053
1054 data = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
1055 bcopy(&nat_cnt, data, sizeof(nat_cnt));
1056
1057 nat_cnt = 0;
1058 len = sizeof(nat_cnt);
1059
1060 IPFW_UH_RLOCK(chain);
1061 if (gencnt != chain->gencnt) {
1062 free(data, M_TEMP);
1063 goto retry;
1064 }
1065 /* Serialize all the data. */
1066 LIST_FOREACH(n, &chain->nat, _next) {
1067 ucfg = (struct cfg_nat_legacy *)&data[len];
1068 ucfg->id = n->id;
1069 ucfg->ip = n->ip;
1070 ucfg->redir_cnt = n->redir_cnt;
1071 ucfg->mode = n->mode;
1072 strlcpy(ucfg->if_name, n->if_name, sizeof(ucfg->if_name));
1073 len += sizeof(struct cfg_nat_legacy);
1074 LIST_FOREACH(r, &n->redir_chain, _next) {
1075 ser_r = (struct cfg_redir_legacy *)&data[len];
1076 ser_r->mode = r->mode;
1077 ser_r->laddr = r->laddr;
1078 ser_r->paddr = r->paddr;
1079 ser_r->raddr = r->raddr;
1080 ser_r->lport = r->lport;
1081 ser_r->pport = r->pport;
1082 ser_r->rport = r->rport;
1083 ser_r->pport_cnt = r->pport_cnt;
1084 ser_r->rport_cnt = r->rport_cnt;
1085 ser_r->proto = r->proto;
1086 ser_r->spool_cnt = r->spool_cnt;
1087 len += sizeof(struct cfg_redir_legacy);
1088 LIST_FOREACH(s, &r->spool_chain, _next) {
1089 ser_s = (struct cfg_spool_legacy *)&data[len];
1090 ser_s->addr = s->addr;
1091 ser_s->port = s->port;
1092 len += sizeof(struct cfg_spool_legacy);
1093 }
1094 }
1095 }
1096 IPFW_UH_RUNLOCK(chain);
1097
1098 error = sooptcopyout(sopt, data, len);
1099 free(data, M_TEMP);
1100
1101 return (error);
1102 }
1103
1104 static int
1105 ipfw_nat_get_log(struct sockopt *sopt)
1106 {
1107 uint8_t *data;
1108 struct cfg_nat *ptr;
1109 int i, size;
1110 struct ip_fw_chain *chain;
1111 IPFW_RLOCK_TRACKER;
1112
1113 chain = &V_layer3_chain;
1114
1115 IPFW_RLOCK(chain);
1116 /* one pass to count, one to copy the data */
1117 i = 0;
1118 LIST_FOREACH(ptr, &chain->nat, _next) {
1119 if (ptr->lib->logDesc == NULL)
1120 continue;
1121 i++;
1122 }
1123 size = i * (LIBALIAS_BUF_SIZE + sizeof(int));
1124 data = malloc(size, M_IPFW, M_NOWAIT | M_ZERO);
1125 if (data == NULL) {
1126 IPFW_RUNLOCK(chain);
1127 return (ENOSPC);
1128 }
1129 i = 0;
1130 LIST_FOREACH(ptr, &chain->nat, _next) {
1131 if (ptr->lib->logDesc == NULL)
1132 continue;
1133 bcopy(&ptr->id, &data[i], sizeof(int));
1134 i += sizeof(int);
1135 bcopy(ptr->lib->logDesc, &data[i], LIBALIAS_BUF_SIZE);
1136 i += LIBALIAS_BUF_SIZE;
1137 }
1138 IPFW_RUNLOCK(chain);
1139 sooptcopyout(sopt, data, size);
1140 free(data, M_IPFW);
1141 return(0);
1142 }
1143
1144 static int
1145 vnet_ipfw_nat_init(const void *arg __unused)
1146 {
1147
1148 V_ipfw_nat_ready = 1;
1149 return (0);
1150 }
1151
1152 static int
1153 vnet_ipfw_nat_uninit(const void *arg __unused)
1154 {
1155 struct cfg_nat *ptr, *ptr_temp;
1156 struct ip_fw_chain *chain;
1157
1158 chain = &V_layer3_chain;
1159 IPFW_WLOCK(chain);
1160 V_ipfw_nat_ready = 0;
1161 LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) {
1162 LIST_REMOVE(ptr, _next);
1163 free_nat_instance(ptr);
1164 }
1165 flush_nat_ptrs(chain, -1 /* flush all */);
1166 IPFW_WUNLOCK(chain);
1167 return (0);
1168 }
1169
1170 static void
1171 ipfw_nat_init(void)
1172 {
1173
1174 /* init ipfw hooks */
1175 ipfw_nat_ptr = ipfw_nat;
1176 lookup_nat_ptr = lookup_nat;
1177 ipfw_nat_cfg_ptr = ipfw_nat_cfg;
1178 ipfw_nat_del_ptr = ipfw_nat_del;
1179 ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg;
1180 ipfw_nat_get_log_ptr = ipfw_nat_get_log;
1181 IPFW_ADD_SOPT_HANDLER(1, scodes);
1182
1183 ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change,
1184 NULL, EVENTHANDLER_PRI_ANY);
1185 }
1186
1187 static void
1188 ipfw_nat_destroy(void)
1189 {
1190
1191 EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag);
1192 /* deregister ipfw_nat */
1193 IPFW_DEL_SOPT_HANDLER(1, scodes);
1194 ipfw_nat_ptr = NULL;
1195 lookup_nat_ptr = NULL;
1196 ipfw_nat_cfg_ptr = NULL;
1197 ipfw_nat_del_ptr = NULL;
1198 ipfw_nat_get_cfg_ptr = NULL;
1199 ipfw_nat_get_log_ptr = NULL;
1200 }
1201
1202 static int
1203 ipfw_nat_modevent(module_t mod, int type, void *unused)
1204 {
1205 int err = 0;
1206
1207 switch (type) {
1208 case MOD_LOAD:
1209 break;
1210
1211 case MOD_UNLOAD:
1212 break;
1213
1214 default:
1215 return EOPNOTSUPP;
1216 break;
1217 }
1218 return err;
1219 }
1220
1221 static moduledata_t ipfw_nat_mod = {
1222 "ipfw_nat",
1223 ipfw_nat_modevent,
1224 0
1225 };
1226
1227 /* Define startup order. */
1228 #define IPFW_NAT_SI_SUB_FIREWALL SI_SUB_PROTO_FIREWALL
1229 #define IPFW_NAT_MODEVENT_ORDER (SI_ORDER_ANY - 128) /* after ipfw */
1230 #define IPFW_NAT_MODULE_ORDER (IPFW_NAT_MODEVENT_ORDER + 1)
1231 #define IPFW_NAT_VNET_ORDER (IPFW_NAT_MODEVENT_ORDER + 2)
1232
1233 DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, IPFW_NAT_SI_SUB_FIREWALL, SI_ORDER_ANY);
1234 MODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1);
1235 MODULE_DEPEND(ipfw_nat, ipfw, 3, 3, 3);
1236 MODULE_VERSION(ipfw_nat, 1);
1237
1238 SYSINIT(ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER,
1239 ipfw_nat_init, NULL);
1240 VNET_SYSINIT(vnet_ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_VNET_ORDER,
1241 vnet_ipfw_nat_init, NULL);
1242
1243 SYSUNINIT(ipfw_nat_destroy, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER,
1244 ipfw_nat_destroy, NULL);
1245 VNET_SYSUNINIT(vnet_ipfw_nat_uninit, IPFW_NAT_SI_SUB_FIREWALL,
1246 IPFW_NAT_VNET_ORDER, vnet_ipfw_nat_uninit, NULL);
1247
1248 /* end of file */
Cache object: a9b39e2544f56d17a0b188a9301d9a8a
|