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