source: rtems-libbsd/freebsd/sys/netpfil/ipfw/ip_fw_nat.c @ 3c967ca

55-freebsd-126-freebsd-12
Last change on this file since 3c967ca was 3c967ca, checked in by Sebastian Huber <sebastian.huber@…>, on 06/08/17 at 11:15:12

Use <sys/lock.h> provided by Newlib

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