13b3a8eb9SGleb Smirnoff /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3fe267a55SPedro F. Giffuni *
43b3a8eb9SGleb Smirnoff * Copyright (c) 2008 Paolo Pisati
53b3a8eb9SGleb Smirnoff * All rights reserved.
63b3a8eb9SGleb Smirnoff *
73b3a8eb9SGleb Smirnoff * Redistribution and use in source and binary forms, with or without
83b3a8eb9SGleb Smirnoff * modification, are permitted provided that the following conditions
93b3a8eb9SGleb Smirnoff * are met:
103b3a8eb9SGleb Smirnoff * 1. Redistributions of source code must retain the above copyright
113b3a8eb9SGleb Smirnoff * notice, this list of conditions and the following disclaimer.
123b3a8eb9SGleb Smirnoff * 2. Redistributions in binary form must reproduce the above copyright
133b3a8eb9SGleb Smirnoff * notice, this list of conditions and the following disclaimer in the
143b3a8eb9SGleb Smirnoff * documentation and/or other materials provided with the distribution.
153b3a8eb9SGleb Smirnoff *
163b3a8eb9SGleb Smirnoff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
173b3a8eb9SGleb Smirnoff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
183b3a8eb9SGleb Smirnoff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
193b3a8eb9SGleb Smirnoff * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
203b3a8eb9SGleb Smirnoff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
213b3a8eb9SGleb Smirnoff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
223b3a8eb9SGleb Smirnoff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
233b3a8eb9SGleb Smirnoff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
243b3a8eb9SGleb Smirnoff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
253b3a8eb9SGleb Smirnoff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
263b3a8eb9SGleb Smirnoff * SUCH DAMAGE.
273b3a8eb9SGleb Smirnoff */
283b3a8eb9SGleb Smirnoff
293b3a8eb9SGleb Smirnoff #include <sys/param.h>
303b3a8eb9SGleb Smirnoff #include <sys/systm.h>
313b3a8eb9SGleb Smirnoff #include <sys/eventhandler.h>
323b3a8eb9SGleb Smirnoff #include <sys/malloc.h>
3376039bc8SGleb Smirnoff #include <sys/mbuf.h>
343b3a8eb9SGleb Smirnoff #include <sys/kernel.h>
353b3a8eb9SGleb Smirnoff #include <sys/lock.h>
363b3a8eb9SGleb Smirnoff #include <sys/module.h>
373b3a8eb9SGleb Smirnoff #include <sys/rwlock.h>
38ccba94b8SAlexander V. Chernikov #include <sys/rmlock.h>
393b3a8eb9SGleb Smirnoff
403b3a8eb9SGleb Smirnoff #include <netinet/libalias/alias.h>
413b3a8eb9SGleb Smirnoff #include <netinet/libalias/alias_local.h>
423b3a8eb9SGleb Smirnoff
433b3a8eb9SGleb Smirnoff #include <net/if.h>
4476039bc8SGleb Smirnoff #include <net/if_var.h>
453d0d5b21SJustin Hibbits #include <net/if_private.h>
463b3a8eb9SGleb Smirnoff #include <netinet/in.h>
473b3a8eb9SGleb Smirnoff #include <netinet/ip.h>
483b3a8eb9SGleb Smirnoff #include <netinet/ip_var.h>
493b3a8eb9SGleb Smirnoff #include <netinet/ip_fw.h>
503b3a8eb9SGleb Smirnoff #include <netinet/tcp.h>
513b3a8eb9SGleb Smirnoff #include <netinet/udp.h>
523b3a8eb9SGleb Smirnoff
533b3a8eb9SGleb Smirnoff #include <netpfil/ipfw/ip_fw_private.h>
543b3a8eb9SGleb Smirnoff
553b3a8eb9SGleb Smirnoff #include <machine/in_cksum.h> /* XXX for in_cksum */
563b3a8eb9SGleb Smirnoff
57d6164b77SAlexander V. Chernikov struct cfg_spool {
58d6164b77SAlexander V. Chernikov LIST_ENTRY(cfg_spool) _next; /* chain of spool instances */
59d6164b77SAlexander V. Chernikov struct in_addr addr;
60d6164b77SAlexander V. Chernikov uint16_t port;
61d6164b77SAlexander V. Chernikov };
62d6164b77SAlexander V. Chernikov
63d6164b77SAlexander V. Chernikov /* Nat redirect configuration. */
64d6164b77SAlexander V. Chernikov struct cfg_redir {
65d6164b77SAlexander V. Chernikov LIST_ENTRY(cfg_redir) _next; /* chain of redir instances */
66d6164b77SAlexander V. Chernikov uint16_t mode; /* type of redirect mode */
67d6164b77SAlexander V. Chernikov uint16_t proto; /* protocol: tcp/udp */
68d6164b77SAlexander V. Chernikov struct in_addr laddr; /* local ip address */
69d6164b77SAlexander V. Chernikov struct in_addr paddr; /* public ip address */
70d6164b77SAlexander V. Chernikov struct in_addr raddr; /* remote ip address */
71d6164b77SAlexander V. Chernikov uint16_t lport; /* local port */
72d6164b77SAlexander V. Chernikov uint16_t pport; /* public port */
73d6164b77SAlexander V. Chernikov uint16_t rport; /* remote port */
74d6164b77SAlexander V. Chernikov uint16_t pport_cnt; /* number of public ports */
75d6164b77SAlexander V. Chernikov uint16_t rport_cnt; /* number of remote ports */
76d6164b77SAlexander V. Chernikov struct alias_link **alink;
77d6164b77SAlexander V. Chernikov u_int16_t spool_cnt; /* num of entry in spool chain */
78d6164b77SAlexander V. Chernikov /* chain of spool instances */
79d6164b77SAlexander V. Chernikov LIST_HEAD(spool_chain, cfg_spool) spool_chain;
80d6164b77SAlexander V. Chernikov };
81d6164b77SAlexander V. Chernikov
82d6164b77SAlexander V. Chernikov /* Nat configuration data struct. */
83d6164b77SAlexander V. Chernikov struct cfg_nat {
84d6164b77SAlexander V. Chernikov /* chain of nat instances */
85d6164b77SAlexander V. Chernikov LIST_ENTRY(cfg_nat) _next;
86d6164b77SAlexander V. Chernikov int id; /* nat id */
87d6164b77SAlexander V. Chernikov struct in_addr ip; /* nat ip address */
88d6164b77SAlexander V. Chernikov struct libalias *lib; /* libalias instance */
89d6164b77SAlexander V. Chernikov int mode; /* aliasing mode */
90d6164b77SAlexander V. Chernikov int redir_cnt; /* number of entry in spool chain */
91d6164b77SAlexander V. Chernikov /* chain of redir instances */
92d6164b77SAlexander V. Chernikov LIST_HEAD(redir_chain, cfg_redir) redir_chain;
93d6164b77SAlexander V. Chernikov char if_name[IF_NAMESIZE]; /* interface name */
94a08cdb6cSNeel Chauhan u_short alias_port_lo; /* low range for port aliasing */
95a08cdb6cSNeel Chauhan u_short alias_port_hi; /* high range for port aliasing */
96d6164b77SAlexander V. Chernikov };
97d6164b77SAlexander V. Chernikov
988856400bSMikolaj Golub static eventhandler_tag ifaddr_event_tag;
993b3a8eb9SGleb Smirnoff
1003b3a8eb9SGleb Smirnoff static void
ifaddr_change(void * arg __unused,struct ifnet * ifp)1013b3a8eb9SGleb Smirnoff ifaddr_change(void *arg __unused, struct ifnet *ifp)
1023b3a8eb9SGleb Smirnoff {
1033b3a8eb9SGleb Smirnoff struct cfg_nat *ptr;
1043b3a8eb9SGleb Smirnoff struct ifaddr *ifa;
1053b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain;
1063b3a8eb9SGleb Smirnoff
1078856400bSMikolaj Golub KASSERT(curvnet == ifp->if_vnet,
1088856400bSMikolaj Golub ("curvnet(%p) differs from iface vnet(%p)", curvnet, ifp->if_vnet));
1099ac51e79SBjoern A. Zeeb
1109ac51e79SBjoern A. Zeeb if (V_ipfw_vnet_ready == 0 || V_ipfw_nat_ready == 0)
1119ac51e79SBjoern A. Zeeb return;
1129ac51e79SBjoern A. Zeeb
1133b3a8eb9SGleb Smirnoff chain = &V_layer3_chain;
1140b47e42bSAlexander V. Chernikov IPFW_UH_WLOCK(chain);
1153b3a8eb9SGleb Smirnoff /* Check every nat entry... */
1163b3a8eb9SGleb Smirnoff LIST_FOREACH(ptr, &chain->nat, _next) {
11716a72f53SGleb Smirnoff struct epoch_tracker et;
11816a72f53SGleb Smirnoff
1193b3a8eb9SGleb Smirnoff /* ...using nic 'ifp->if_xname' as dynamic alias address. */
120c254a209SAlexander V. Chernikov if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0)
1213b3a8eb9SGleb Smirnoff continue;
12216a72f53SGleb Smirnoff NET_EPOCH_ENTER(et);
123d7c5a620SMatt Macy CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
1243b3a8eb9SGleb Smirnoff if (ifa->ifa_addr == NULL)
1253b3a8eb9SGleb Smirnoff continue;
1263b3a8eb9SGleb Smirnoff if (ifa->ifa_addr->sa_family != AF_INET)
1273b3a8eb9SGleb Smirnoff continue;
1280b47e42bSAlexander V. Chernikov IPFW_WLOCK(chain);
1293b3a8eb9SGleb Smirnoff ptr->ip = ((struct sockaddr_in *)
1303b3a8eb9SGleb Smirnoff (ifa->ifa_addr))->sin_addr;
1313b3a8eb9SGleb Smirnoff LibAliasSetAddress(ptr->lib, ptr->ip);
1320b47e42bSAlexander V. Chernikov IPFW_WUNLOCK(chain);
1333b3a8eb9SGleb Smirnoff }
13416a72f53SGleb Smirnoff NET_EPOCH_EXIT(et);
1353b3a8eb9SGleb Smirnoff }
1360b47e42bSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
1373b3a8eb9SGleb Smirnoff }
1383b3a8eb9SGleb Smirnoff
1393b3a8eb9SGleb Smirnoff /*
1403b3a8eb9SGleb Smirnoff * delete the pointers for nat entry ix, or all of them if ix < 0
1413b3a8eb9SGleb Smirnoff */
1423b3a8eb9SGleb Smirnoff static void
flush_nat_ptrs(struct ip_fw_chain * chain,const int ix)1433b3a8eb9SGleb Smirnoff flush_nat_ptrs(struct ip_fw_chain *chain, const int ix)
1443b3a8eb9SGleb Smirnoff {
1453b3a8eb9SGleb Smirnoff ipfw_insn_nat *cmd;
146e758846cSAndrey V. Elsukov int i;
1473b3a8eb9SGleb Smirnoff
1483b3a8eb9SGleb Smirnoff IPFW_WLOCK_ASSERT(chain);
1493b3a8eb9SGleb Smirnoff for (i = 0; i < chain->n_rules; i++) {
150e758846cSAndrey V. Elsukov cmd = (ipfw_insn_nat *)ipfw_get_action(chain->map[i]);
1513b3a8eb9SGleb Smirnoff if (cmd->o.opcode == O_NAT && cmd->nat != NULL &&
1523b3a8eb9SGleb Smirnoff (ix < 0 || cmd->nat->id == ix))
1533b3a8eb9SGleb Smirnoff cmd->nat = NULL;
1543b3a8eb9SGleb Smirnoff }
1553b3a8eb9SGleb Smirnoff }
1563b3a8eb9SGleb Smirnoff
1573b3a8eb9SGleb Smirnoff static void
del_redir_spool_cfg(struct cfg_nat * n,struct redir_chain * head)1583b3a8eb9SGleb Smirnoff del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head)
1593b3a8eb9SGleb Smirnoff {
1603b3a8eb9SGleb Smirnoff struct cfg_redir *r, *tmp_r;
1613b3a8eb9SGleb Smirnoff struct cfg_spool *s, *tmp_s;
1623b3a8eb9SGleb Smirnoff int i, num;
1633b3a8eb9SGleb Smirnoff
1643b3a8eb9SGleb Smirnoff LIST_FOREACH_SAFE(r, head, _next, tmp_r) {
1653b3a8eb9SGleb Smirnoff num = 1; /* Number of alias_link to delete. */
1663b3a8eb9SGleb Smirnoff switch (r->mode) {
167d6164b77SAlexander V. Chernikov case NAT44_REDIR_PORT:
1683b3a8eb9SGleb Smirnoff num = r->pport_cnt;
1693b3a8eb9SGleb Smirnoff /* FALLTHROUGH */
170d6164b77SAlexander V. Chernikov case NAT44_REDIR_ADDR:
171d6164b77SAlexander V. Chernikov case NAT44_REDIR_PROTO:
1723b3a8eb9SGleb Smirnoff /* Delete all libalias redirect entry. */
1733b3a8eb9SGleb Smirnoff for (i = 0; i < num; i++)
1743b3a8eb9SGleb Smirnoff LibAliasRedirectDelete(n->lib, r->alink[i]);
1753b3a8eb9SGleb Smirnoff /* Del spool cfg if any. */
1763b3a8eb9SGleb Smirnoff LIST_FOREACH_SAFE(s, &r->spool_chain, _next, tmp_s) {
1773b3a8eb9SGleb Smirnoff LIST_REMOVE(s, _next);
1783b3a8eb9SGleb Smirnoff free(s, M_IPFW);
1793b3a8eb9SGleb Smirnoff }
1803b3a8eb9SGleb Smirnoff free(r->alink, M_IPFW);
1813b3a8eb9SGleb Smirnoff LIST_REMOVE(r, _next);
1823b3a8eb9SGleb Smirnoff free(r, M_IPFW);
1833b3a8eb9SGleb Smirnoff break;
1843b3a8eb9SGleb Smirnoff default:
1853b3a8eb9SGleb Smirnoff printf("unknown redirect mode: %u\n", r->mode);
1863b3a8eb9SGleb Smirnoff /* XXX - panic?!?!? */
1873b3a8eb9SGleb Smirnoff break;
1883b3a8eb9SGleb Smirnoff }
1893b3a8eb9SGleb Smirnoff }
1903b3a8eb9SGleb Smirnoff }
1913b3a8eb9SGleb Smirnoff
192d6164b77SAlexander V. Chernikov static int
add_redir_spool_cfg(char * buf,struct cfg_nat * ptr)1933b3a8eb9SGleb Smirnoff add_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
1943b3a8eb9SGleb Smirnoff {
195d6164b77SAlexander V. Chernikov struct cfg_redir *r;
196d6164b77SAlexander V. Chernikov struct cfg_spool *s;
197d6164b77SAlexander V. Chernikov struct nat44_cfg_redir *ser_r;
198d6164b77SAlexander V. Chernikov struct nat44_cfg_spool *ser_s;
199d6164b77SAlexander V. Chernikov
2003b3a8eb9SGleb Smirnoff int cnt, off, i;
2013b3a8eb9SGleb Smirnoff
2023b3a8eb9SGleb Smirnoff for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) {
203d6164b77SAlexander V. Chernikov ser_r = (struct nat44_cfg_redir *)&buf[off];
204d6164b77SAlexander V. Chernikov r = malloc(sizeof(*r), M_IPFW, M_WAITOK | M_ZERO);
205d6164b77SAlexander V. Chernikov r->mode = ser_r->mode;
206d6164b77SAlexander V. Chernikov r->laddr = ser_r->laddr;
207d6164b77SAlexander V. Chernikov r->paddr = ser_r->paddr;
208d6164b77SAlexander V. Chernikov r->raddr = ser_r->raddr;
209d6164b77SAlexander V. Chernikov r->lport = ser_r->lport;
210d6164b77SAlexander V. Chernikov r->pport = ser_r->pport;
211d6164b77SAlexander V. Chernikov r->rport = ser_r->rport;
212d6164b77SAlexander V. Chernikov r->pport_cnt = ser_r->pport_cnt;
213d6164b77SAlexander V. Chernikov r->rport_cnt = ser_r->rport_cnt;
214d6164b77SAlexander V. Chernikov r->proto = ser_r->proto;
215d6164b77SAlexander V. Chernikov r->spool_cnt = ser_r->spool_cnt;
216d6164b77SAlexander V. Chernikov //memcpy(r, ser_r, SOF_REDIR);
2173b3a8eb9SGleb Smirnoff LIST_INIT(&r->spool_chain);
218d6164b77SAlexander V. Chernikov off += sizeof(struct nat44_cfg_redir);
2193b3a8eb9SGleb Smirnoff r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt,
2203b3a8eb9SGleb Smirnoff M_IPFW, M_WAITOK | M_ZERO);
2213b3a8eb9SGleb Smirnoff switch (r->mode) {
222d6164b77SAlexander V. Chernikov case NAT44_REDIR_ADDR:
2233b3a8eb9SGleb Smirnoff r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr,
2243b3a8eb9SGleb Smirnoff r->paddr);
2253b3a8eb9SGleb Smirnoff break;
226d6164b77SAlexander V. Chernikov case NAT44_REDIR_PORT:
2273b3a8eb9SGleb Smirnoff for (i = 0 ; i < r->pport_cnt; i++) {
2283b3a8eb9SGleb Smirnoff /* If remotePort is all ports, set it to 0. */
2293b3a8eb9SGleb Smirnoff u_short remotePortCopy = r->rport + i;
2303b3a8eb9SGleb Smirnoff if (r->rport_cnt == 1 && r->rport == 0)
2313b3a8eb9SGleb Smirnoff remotePortCopy = 0;
2323b3a8eb9SGleb Smirnoff r->alink[i] = LibAliasRedirectPort(ptr->lib,
2333b3a8eb9SGleb Smirnoff r->laddr, htons(r->lport + i), r->raddr,
2343b3a8eb9SGleb Smirnoff htons(remotePortCopy), r->paddr,
2353b3a8eb9SGleb Smirnoff htons(r->pport + i), r->proto);
2363b3a8eb9SGleb Smirnoff if (r->alink[i] == NULL) {
2373b3a8eb9SGleb Smirnoff r->alink[0] = NULL;
2383b3a8eb9SGleb Smirnoff break;
2393b3a8eb9SGleb Smirnoff }
2403b3a8eb9SGleb Smirnoff }
2413b3a8eb9SGleb Smirnoff break;
242d6164b77SAlexander V. Chernikov case NAT44_REDIR_PROTO:
2433b3a8eb9SGleb Smirnoff r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr,
2443b3a8eb9SGleb Smirnoff r->raddr, r->paddr, r->proto);
2453b3a8eb9SGleb Smirnoff break;
2463b3a8eb9SGleb Smirnoff default:
2473b3a8eb9SGleb Smirnoff printf("unknown redirect mode: %u\n", r->mode);
2483b3a8eb9SGleb Smirnoff break;
2493b3a8eb9SGleb Smirnoff }
250d6164b77SAlexander V. Chernikov if (r->alink[0] == NULL) {
251d6164b77SAlexander V. Chernikov printf("LibAliasRedirect* returned NULL\n");
252fdf6290eSGleb Smirnoff free(r->alink, M_IPFW);
253fdf6290eSGleb Smirnoff free(r, M_IPFW);
254d6164b77SAlexander V. Chernikov return (EINVAL);
255d6164b77SAlexander V. Chernikov }
2563b3a8eb9SGleb Smirnoff /* LSNAT handling. */
2573b3a8eb9SGleb Smirnoff for (i = 0; i < r->spool_cnt; i++) {
258d6164b77SAlexander V. Chernikov ser_s = (struct nat44_cfg_spool *)&buf[off];
259d6164b77SAlexander V. Chernikov s = malloc(sizeof(*s), M_IPFW, M_WAITOK | M_ZERO);
260d6164b77SAlexander V. Chernikov s->addr = ser_s->addr;
261d6164b77SAlexander V. Chernikov s->port = ser_s->port;
2623b3a8eb9SGleb Smirnoff LibAliasAddServer(ptr->lib, r->alink[0],
2633b3a8eb9SGleb Smirnoff s->addr, htons(s->port));
264d6164b77SAlexander V. Chernikov off += sizeof(struct nat44_cfg_spool);
2653b3a8eb9SGleb Smirnoff /* Hook spool entry. */
2663b3a8eb9SGleb Smirnoff LIST_INSERT_HEAD(&r->spool_chain, s, _next);
2673b3a8eb9SGleb Smirnoff }
2683b3a8eb9SGleb Smirnoff /* And finally hook this redir entry. */
2693b3a8eb9SGleb Smirnoff LIST_INSERT_HEAD(&ptr->redir_chain, r, _next);
2703b3a8eb9SGleb Smirnoff }
271d6164b77SAlexander V. Chernikov
272d6164b77SAlexander V. Chernikov return (0);
2733b3a8eb9SGleb Smirnoff }
2743b3a8eb9SGleb Smirnoff
2751a458088SAlexander V. Chernikov static void
free_nat_instance(struct cfg_nat * ptr)2761a458088SAlexander V. Chernikov free_nat_instance(struct cfg_nat *ptr)
2771a458088SAlexander V. Chernikov {
2781a458088SAlexander V. Chernikov
2791a458088SAlexander V. Chernikov del_redir_spool_cfg(ptr, &ptr->redir_chain);
2801a458088SAlexander V. Chernikov LibAliasUninit(ptr->lib);
2811a458088SAlexander V. Chernikov free(ptr, M_IPFW);
2821a458088SAlexander V. Chernikov }
2831a458088SAlexander V. Chernikov
28410ab2de0SAlexander V. Chernikov /*
28510ab2de0SAlexander V. Chernikov * ipfw_nat - perform mbuf header translation.
28610ab2de0SAlexander V. Chernikov *
28710ab2de0SAlexander V. Chernikov * Note V_layer3_chain has to be locked while calling ipfw_nat() in
28810ab2de0SAlexander V. Chernikov * 'global' operation mode (t == NULL).
28910ab2de0SAlexander V. Chernikov *
29010ab2de0SAlexander V. Chernikov */
2913b3a8eb9SGleb Smirnoff static int
ipfw_nat(struct ip_fw_args * args,struct cfg_nat * t,struct mbuf * m)2923b3a8eb9SGleb Smirnoff ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m)
2933b3a8eb9SGleb Smirnoff {
2943b3a8eb9SGleb Smirnoff struct mbuf *mcl;
2953b3a8eb9SGleb Smirnoff struct ip *ip;
2963b3a8eb9SGleb Smirnoff /* XXX - libalias duct tape */
2973b3a8eb9SGleb Smirnoff int ldt, retval, found;
2983b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain;
2993b3a8eb9SGleb Smirnoff char *c;
3003b3a8eb9SGleb Smirnoff
3013b3a8eb9SGleb Smirnoff ldt = 0;
3023b3a8eb9SGleb Smirnoff retval = 0;
3033b3a8eb9SGleb Smirnoff mcl = m_megapullup(m, m->m_pkthdr.len);
3043b3a8eb9SGleb Smirnoff if (mcl == NULL) {
3053b3a8eb9SGleb Smirnoff args->m = NULL;
3063b3a8eb9SGleb Smirnoff return (IP_FW_DENY);
3073b3a8eb9SGleb Smirnoff }
30865290859SMark Johnston M_ASSERTMAPPED(mcl);
3093b3a8eb9SGleb Smirnoff ip = mtod(mcl, struct ip *);
3103b3a8eb9SGleb Smirnoff
3113b3a8eb9SGleb Smirnoff /*
3123b3a8eb9SGleb Smirnoff * XXX - Libalias checksum offload 'duct tape':
3133b3a8eb9SGleb Smirnoff *
3143b3a8eb9SGleb Smirnoff * locally generated packets have only pseudo-header checksum
3153b3a8eb9SGleb Smirnoff * calculated and libalias will break it[1], so mark them for
3163b3a8eb9SGleb Smirnoff * later fix. Moreover there are cases when libalias modifies
3173b3a8eb9SGleb Smirnoff * tcp packet data[2], mark them for later fix too.
3183b3a8eb9SGleb Smirnoff *
3193b3a8eb9SGleb Smirnoff * [1] libalias was never meant to run in kernel, so it does
3203b3a8eb9SGleb Smirnoff * not have any knowledge about checksum offloading, and
3213b3a8eb9SGleb Smirnoff * expects a packet with a full internet checksum.
3223b3a8eb9SGleb Smirnoff * Unfortunately, packets generated locally will have just the
3233b3a8eb9SGleb Smirnoff * pseudo header calculated, and when libalias tries to adjust
3243b3a8eb9SGleb Smirnoff * the checksum it will actually compute a wrong value.
3253b3a8eb9SGleb Smirnoff *
3263b3a8eb9SGleb Smirnoff * [2] when libalias modifies tcp's data content, full TCP
3273b3a8eb9SGleb Smirnoff * checksum has to be recomputed: the problem is that
3283b3a8eb9SGleb Smirnoff * libalias does not have any idea about checksum offloading.
3293b3a8eb9SGleb Smirnoff * To work around this, we do not do checksumming in LibAlias,
3303b3a8eb9SGleb Smirnoff * but only mark the packets in th_x2 field. If we receive a
3313b3a8eb9SGleb Smirnoff * marked packet, we calculate correct checksum for it
3323b3a8eb9SGleb Smirnoff * aware of offloading. Why such a terrible hack instead of
3333b3a8eb9SGleb Smirnoff * recalculating checksum for each packet?
3343b3a8eb9SGleb Smirnoff * Because the previous checksum was not checked!
3353b3a8eb9SGleb Smirnoff * Recalculating checksums for EVERY packet will hide ALL
3363b3a8eb9SGleb Smirnoff * transmission errors. Yes, marked packets still suffer from
3373b3a8eb9SGleb Smirnoff * this problem. But, sigh, natd(8) has this problem, too.
3383b3a8eb9SGleb Smirnoff *
3393b3a8eb9SGleb Smirnoff * TODO: -make libalias mbuf aware (so
3403b3a8eb9SGleb Smirnoff * it can handle delayed checksum and tso)
3413b3a8eb9SGleb Smirnoff */
3423b3a8eb9SGleb Smirnoff
3433b3a8eb9SGleb Smirnoff if (mcl->m_pkthdr.rcvif == NULL &&
3443b3a8eb9SGleb Smirnoff mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA)
3453b3a8eb9SGleb Smirnoff ldt = 1;
3463b3a8eb9SGleb Smirnoff
3473b3a8eb9SGleb Smirnoff c = mtod(mcl, char *);
3483b3a8eb9SGleb Smirnoff
3493b3a8eb9SGleb Smirnoff /* Check if this is 'global' instance */
3503b3a8eb9SGleb Smirnoff if (t == NULL) {
351b7795b67SGleb Smirnoff if (args->flags & IPFW_ARGS_IN) {
3523b3a8eb9SGleb Smirnoff /* Wrong direction, skip processing */
3533b3a8eb9SGleb Smirnoff args->m = mcl;
3543b3a8eb9SGleb Smirnoff return (IP_FW_NAT);
3553b3a8eb9SGleb Smirnoff }
3563b3a8eb9SGleb Smirnoff
3573b3a8eb9SGleb Smirnoff found = 0;
3583b3a8eb9SGleb Smirnoff chain = &V_layer3_chain;
3595d0cd926SAlexander V. Chernikov IPFW_RLOCK_ASSERT(chain);
3603b3a8eb9SGleb Smirnoff /* Check every nat entry... */
3613b3a8eb9SGleb Smirnoff LIST_FOREACH(t, &chain->nat, _next) {
3623b3a8eb9SGleb Smirnoff if ((t->mode & PKT_ALIAS_SKIP_GLOBAL) != 0)
3633b3a8eb9SGleb Smirnoff continue;
3643b3a8eb9SGleb Smirnoff retval = LibAliasOutTry(t->lib, c,
3653b3a8eb9SGleb Smirnoff mcl->m_len + M_TRAILINGSPACE(mcl), 0);
3663b3a8eb9SGleb Smirnoff if (retval == PKT_ALIAS_OK) {
3673b3a8eb9SGleb Smirnoff /* Nat instance recognises state */
3683b3a8eb9SGleb Smirnoff found = 1;
3693b3a8eb9SGleb Smirnoff break;
3703b3a8eb9SGleb Smirnoff }
3713b3a8eb9SGleb Smirnoff }
3723b3a8eb9SGleb Smirnoff if (found != 1) {
3733b3a8eb9SGleb Smirnoff /* No instance found, return ignore */
3743b3a8eb9SGleb Smirnoff args->m = mcl;
3753b3a8eb9SGleb Smirnoff return (IP_FW_NAT);
3763b3a8eb9SGleb Smirnoff }
3773b3a8eb9SGleb Smirnoff } else {
378b7795b67SGleb Smirnoff if (args->flags & IPFW_ARGS_IN)
3793b3a8eb9SGleb Smirnoff retval = LibAliasIn(t->lib, c,
3803b3a8eb9SGleb Smirnoff mcl->m_len + M_TRAILINGSPACE(mcl));
3813b3a8eb9SGleb Smirnoff else
3823b3a8eb9SGleb Smirnoff retval = LibAliasOut(t->lib, c,
3833b3a8eb9SGleb Smirnoff mcl->m_len + M_TRAILINGSPACE(mcl));
3843b3a8eb9SGleb Smirnoff }
3853b3a8eb9SGleb Smirnoff
3863b3a8eb9SGleb Smirnoff /*
3873b3a8eb9SGleb Smirnoff * We drop packet when:
3883b3a8eb9SGleb Smirnoff * 1. libalias returns PKT_ALIAS_ERROR;
3893b3a8eb9SGleb Smirnoff * 2. For incoming packets:
3903b3a8eb9SGleb Smirnoff * a) for unresolved fragments;
3913b3a8eb9SGleb Smirnoff * b) libalias returns PKT_ALIAS_IGNORED and
3923b3a8eb9SGleb Smirnoff * PKT_ALIAS_DENY_INCOMING flag is set.
3933b3a8eb9SGleb Smirnoff */
3943b3a8eb9SGleb Smirnoff if (retval == PKT_ALIAS_ERROR ||
395b7795b67SGleb Smirnoff ((args->flags & IPFW_ARGS_IN) &&
396b7795b67SGleb Smirnoff (retval == PKT_ALIAS_UNRESOLVED_FRAGMENT ||
3973b3a8eb9SGleb Smirnoff (retval == PKT_ALIAS_IGNORED &&
3983b3a8eb9SGleb Smirnoff (t->mode & PKT_ALIAS_DENY_INCOMING) != 0)))) {
3993b3a8eb9SGleb Smirnoff /* XXX - should i add some logging? */
4003b3a8eb9SGleb Smirnoff m_free(mcl);
4013b3a8eb9SGleb Smirnoff args->m = NULL;
4023b3a8eb9SGleb Smirnoff return (IP_FW_DENY);
4033b3a8eb9SGleb Smirnoff }
4043b3a8eb9SGleb Smirnoff
4053b3a8eb9SGleb Smirnoff if (retval == PKT_ALIAS_RESPOND)
4063b3a8eb9SGleb Smirnoff mcl->m_flags |= M_SKIP_FIREWALL;
4073b3a8eb9SGleb Smirnoff mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len);
4083b3a8eb9SGleb Smirnoff
4093b3a8eb9SGleb Smirnoff /*
4103b3a8eb9SGleb Smirnoff * XXX - libalias checksum offload
4113b3a8eb9SGleb Smirnoff * 'duct tape' (see above)
4123b3a8eb9SGleb Smirnoff */
4133b3a8eb9SGleb Smirnoff
4143b3a8eb9SGleb Smirnoff if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
4153b3a8eb9SGleb Smirnoff ip->ip_p == IPPROTO_TCP) {
4163b3a8eb9SGleb Smirnoff struct tcphdr *th;
4173b3a8eb9SGleb Smirnoff
4183b3a8eb9SGleb Smirnoff th = (struct tcphdr *)(ip + 1);
4190fc7bdc9SRichard Scheffenegger if (tcp_get_flags(th) & TH_RES1)
4203b3a8eb9SGleb Smirnoff ldt = 1;
4213b3a8eb9SGleb Smirnoff }
4223b3a8eb9SGleb Smirnoff
4233b3a8eb9SGleb Smirnoff if (ldt) {
4243b3a8eb9SGleb Smirnoff struct tcphdr *th;
4253b3a8eb9SGleb Smirnoff struct udphdr *uh;
42623e9c6dcSGleb Smirnoff uint16_t ip_len, cksum;
4273b3a8eb9SGleb Smirnoff
42823e9c6dcSGleb Smirnoff ip_len = ntohs(ip->ip_len);
4293b3a8eb9SGleb Smirnoff cksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
43023e9c6dcSGleb Smirnoff htons(ip->ip_p + ip_len - (ip->ip_hl << 2)));
4313b3a8eb9SGleb Smirnoff
4323b3a8eb9SGleb Smirnoff switch (ip->ip_p) {
4333b3a8eb9SGleb Smirnoff case IPPROTO_TCP:
4343b3a8eb9SGleb Smirnoff th = (struct tcphdr *)(ip + 1);
4353b3a8eb9SGleb Smirnoff /*
4363b3a8eb9SGleb Smirnoff * Maybe it was set in
4373b3a8eb9SGleb Smirnoff * libalias...
4383b3a8eb9SGleb Smirnoff */
4390fc7bdc9SRichard Scheffenegger tcp_set_flags(th, tcp_get_flags(th) & ~TH_RES1);
4403b3a8eb9SGleb Smirnoff th->th_sum = cksum;
4413b3a8eb9SGleb Smirnoff mcl->m_pkthdr.csum_data =
4423b3a8eb9SGleb Smirnoff offsetof(struct tcphdr, th_sum);
4433b3a8eb9SGleb Smirnoff break;
4443b3a8eb9SGleb Smirnoff case IPPROTO_UDP:
4453b3a8eb9SGleb Smirnoff uh = (struct udphdr *)(ip + 1);
4463b3a8eb9SGleb Smirnoff uh->uh_sum = cksum;
4473b3a8eb9SGleb Smirnoff mcl->m_pkthdr.csum_data =
4483b3a8eb9SGleb Smirnoff offsetof(struct udphdr, uh_sum);
4493b3a8eb9SGleb Smirnoff break;
4503b3a8eb9SGleb Smirnoff }
4513b3a8eb9SGleb Smirnoff /* No hw checksum offloading: do it ourselves */
4523b3a8eb9SGleb Smirnoff if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) {
4533b3a8eb9SGleb Smirnoff in_delayed_cksum(mcl);
4543b3a8eb9SGleb Smirnoff mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
4553b3a8eb9SGleb Smirnoff }
4563b3a8eb9SGleb Smirnoff }
4573b3a8eb9SGleb Smirnoff args->m = mcl;
4583b3a8eb9SGleb Smirnoff return (IP_FW_NAT);
4593b3a8eb9SGleb Smirnoff }
4603b3a8eb9SGleb Smirnoff
4613b3a8eb9SGleb Smirnoff static struct cfg_nat *
lookup_nat(struct nat_list * l,int nat_id)4623b3a8eb9SGleb Smirnoff lookup_nat(struct nat_list *l, int nat_id)
4633b3a8eb9SGleb Smirnoff {
4643b3a8eb9SGleb Smirnoff struct cfg_nat *res;
4653b3a8eb9SGleb Smirnoff
4663b3a8eb9SGleb Smirnoff LIST_FOREACH(res, l, _next) {
4673b3a8eb9SGleb Smirnoff if (res->id == nat_id)
4683b3a8eb9SGleb Smirnoff break;
4693b3a8eb9SGleb Smirnoff }
4703b3a8eb9SGleb Smirnoff return res;
4713b3a8eb9SGleb Smirnoff }
4723b3a8eb9SGleb Smirnoff
473d6164b77SAlexander V. Chernikov static struct cfg_nat *
lookup_nat_name(struct nat_list * l,char * name)474d6164b77SAlexander V. Chernikov lookup_nat_name(struct nat_list *l, char *name)
4753b3a8eb9SGleb Smirnoff {
476d6164b77SAlexander V. Chernikov struct cfg_nat *res;
477d6164b77SAlexander V. Chernikov int id;
478d6164b77SAlexander V. Chernikov char *errptr;
4793b3a8eb9SGleb Smirnoff
480d6164b77SAlexander V. Chernikov id = strtol(name, &errptr, 10);
481d6164b77SAlexander V. Chernikov if (id == 0 || *errptr != '\0')
482d6164b77SAlexander V. Chernikov return (NULL);
4833b3a8eb9SGleb Smirnoff
484d6164b77SAlexander V. Chernikov LIST_FOREACH(res, l, _next) {
485d6164b77SAlexander V. Chernikov if (res->id == id)
486d6164b77SAlexander V. Chernikov break;
4873b3a8eb9SGleb Smirnoff }
488d6164b77SAlexander V. Chernikov return (res);
489d6164b77SAlexander V. Chernikov }
490d6164b77SAlexander V. Chernikov
491d6164b77SAlexander V. Chernikov /* IP_FW3 configuration routines */
492d6164b77SAlexander V. Chernikov
493d6164b77SAlexander V. Chernikov static void
nat44_config(struct ip_fw_chain * chain,struct nat44_cfg_nat * ucfg)494d6164b77SAlexander V. Chernikov nat44_config(struct ip_fw_chain *chain, struct nat44_cfg_nat *ucfg)
495d6164b77SAlexander V. Chernikov {
496d6164b77SAlexander V. Chernikov struct cfg_nat *ptr, *tcfg;
497d6164b77SAlexander V. Chernikov int gencnt;
4983b3a8eb9SGleb Smirnoff
4993b3a8eb9SGleb Smirnoff /*
5003b3a8eb9SGleb Smirnoff * Find/create nat rule.
5013b3a8eb9SGleb Smirnoff */
502d6164b77SAlexander V. Chernikov IPFW_UH_WLOCK(chain);
5033b3a8eb9SGleb Smirnoff gencnt = chain->gencnt;
504d6164b77SAlexander V. Chernikov ptr = lookup_nat_name(&chain->nat, ucfg->name);
5053b3a8eb9SGleb Smirnoff if (ptr == NULL) {
506d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
5073b3a8eb9SGleb Smirnoff /* New rule: allocate and init new instance. */
5083b3a8eb9SGleb Smirnoff ptr = malloc(sizeof(struct cfg_nat), M_IPFW, M_WAITOK | M_ZERO);
5093b3a8eb9SGleb Smirnoff ptr->lib = LibAliasInit(NULL);
5103b3a8eb9SGleb Smirnoff LIST_INIT(&ptr->redir_chain);
5113b3a8eb9SGleb Smirnoff } else {
5123b3a8eb9SGleb Smirnoff /* Entry already present: temporarily unhook it. */
513d6164b77SAlexander V. Chernikov IPFW_WLOCK(chain);
5143b3a8eb9SGleb Smirnoff LIST_REMOVE(ptr, _next);
515d6164b77SAlexander V. Chernikov flush_nat_ptrs(chain, ptr->id);
5163b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(chain);
517d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
5183b3a8eb9SGleb Smirnoff }
5193b3a8eb9SGleb Smirnoff
5203b3a8eb9SGleb Smirnoff /*
521d6164b77SAlexander V. Chernikov * Basic nat (re)configuration.
5223b3a8eb9SGleb Smirnoff */
523d6164b77SAlexander V. Chernikov ptr->id = strtol(ucfg->name, NULL, 10);
5243b3a8eb9SGleb Smirnoff /*
5253b3a8eb9SGleb Smirnoff * XXX - what if this rule doesn't nat any ip and just
5263b3a8eb9SGleb Smirnoff * redirect?
5273b3a8eb9SGleb Smirnoff * do we set aliasaddress to 0.0.0.0?
5283b3a8eb9SGleb Smirnoff */
529d6164b77SAlexander V. Chernikov ptr->ip = ucfg->ip;
530d6164b77SAlexander V. Chernikov ptr->redir_cnt = ucfg->redir_cnt;
531d6164b77SAlexander V. Chernikov ptr->mode = ucfg->mode;
532a08cdb6cSNeel Chauhan ptr->alias_port_lo = ucfg->alias_port_lo;
533a08cdb6cSNeel Chauhan ptr->alias_port_hi = ucfg->alias_port_hi;
534d6164b77SAlexander V. Chernikov strlcpy(ptr->if_name, ucfg->if_name, sizeof(ptr->if_name));
535d6164b77SAlexander V. Chernikov LibAliasSetMode(ptr->lib, ptr->mode, ~0);
5363b3a8eb9SGleb Smirnoff LibAliasSetAddress(ptr->lib, ptr->ip);
537a08cdb6cSNeel Chauhan LibAliasSetAliasPortRange(ptr->lib, ptr->alias_port_lo, ptr->alias_port_hi);
5383b3a8eb9SGleb Smirnoff
5393b3a8eb9SGleb Smirnoff /*
5403b3a8eb9SGleb Smirnoff * Redir and LSNAT configuration.
5413b3a8eb9SGleb Smirnoff */
5423b3a8eb9SGleb Smirnoff /* Delete old cfgs. */
5433b3a8eb9SGleb Smirnoff del_redir_spool_cfg(ptr, &ptr->redir_chain);
5443b3a8eb9SGleb Smirnoff /* Add new entries. */
545d6164b77SAlexander V. Chernikov add_redir_spool_cfg((char *)(ucfg + 1), ptr);
546d6164b77SAlexander V. Chernikov IPFW_UH_WLOCK(chain);
5473b3a8eb9SGleb Smirnoff
5483b3a8eb9SGleb Smirnoff /* Extra check to avoid race with another ipfw_nat_cfg() */
549d6164b77SAlexander V. Chernikov tcfg = NULL;
550d6164b77SAlexander V. Chernikov if (gencnt != chain->gencnt)
551d6164b77SAlexander V. Chernikov tcfg = lookup_nat_name(&chain->nat, ucfg->name);
552d6164b77SAlexander V. Chernikov IPFW_WLOCK(chain);
553d6164b77SAlexander V. Chernikov if (tcfg != NULL)
554d6164b77SAlexander V. Chernikov LIST_REMOVE(tcfg, _next);
5553b3a8eb9SGleb Smirnoff LIST_INSERT_HEAD(&chain->nat, ptr, _next);
5563b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(chain);
557d6164b77SAlexander V. Chernikov chain->gencnt++;
558d6164b77SAlexander V. Chernikov
559d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
560d6164b77SAlexander V. Chernikov
561d6164b77SAlexander V. Chernikov if (tcfg != NULL)
5621a458088SAlexander V. Chernikov free_nat_instance(ptr);
563d6164b77SAlexander V. Chernikov }
564d6164b77SAlexander V. Chernikov
565d6164b77SAlexander V. Chernikov /*
566d6164b77SAlexander V. Chernikov * Creates/configure nat44 instance
567d6164b77SAlexander V. Chernikov * Data layout (v0)(current):
568d6164b77SAlexander V. Chernikov * Request: [ ipfw_obj_header nat44_cfg_nat .. ]
569d6164b77SAlexander V. Chernikov *
570d6164b77SAlexander V. Chernikov * Returns 0 on success
571d6164b77SAlexander V. Chernikov */
572d6164b77SAlexander V. Chernikov static int
nat44_cfg(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)573d6164b77SAlexander V. Chernikov nat44_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
574d6164b77SAlexander V. Chernikov struct sockopt_data *sd)
575d6164b77SAlexander V. Chernikov {
576d6164b77SAlexander V. Chernikov ipfw_obj_header *oh;
577d6164b77SAlexander V. Chernikov struct nat44_cfg_nat *ucfg;
578d6164b77SAlexander V. Chernikov int id;
579d6164b77SAlexander V. Chernikov size_t read;
580d6164b77SAlexander V. Chernikov char *errptr;
581d6164b77SAlexander V. Chernikov
582d6164b77SAlexander V. Chernikov /* Check minimum header size */
583d6164b77SAlexander V. Chernikov if (sd->valsize < (sizeof(*oh) + sizeof(*ucfg)))
584d6164b77SAlexander V. Chernikov return (EINVAL);
585d6164b77SAlexander V. Chernikov
586d6164b77SAlexander V. Chernikov oh = (ipfw_obj_header *)sd->kbuf;
587d6164b77SAlexander V. Chernikov
588d6164b77SAlexander V. Chernikov /* Basic length checks for TLVs */
589d6164b77SAlexander V. Chernikov if (oh->ntlv.head.length != sizeof(oh->ntlv))
590d6164b77SAlexander V. Chernikov return (EINVAL);
591d6164b77SAlexander V. Chernikov
592d6164b77SAlexander V. Chernikov ucfg = (struct nat44_cfg_nat *)(oh + 1);
593d6164b77SAlexander V. Chernikov
594d6164b77SAlexander V. Chernikov /* Check if name is properly terminated and looks like number */
595d6164b77SAlexander V. Chernikov if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
596d6164b77SAlexander V. Chernikov return (EINVAL);
597d6164b77SAlexander V. Chernikov id = strtol(ucfg->name, &errptr, 10);
598d6164b77SAlexander V. Chernikov if (id == 0 || *errptr != '\0')
599d6164b77SAlexander V. Chernikov return (EINVAL);
600d6164b77SAlexander V. Chernikov
601d6164b77SAlexander V. Chernikov read = sizeof(*oh) + sizeof(*ucfg);
602d6164b77SAlexander V. Chernikov /* Check number of redirs */
603d6164b77SAlexander V. Chernikov if (sd->valsize < read + ucfg->redir_cnt*sizeof(struct nat44_cfg_redir))
604d6164b77SAlexander V. Chernikov return (EINVAL);
605d6164b77SAlexander V. Chernikov
606d6164b77SAlexander V. Chernikov nat44_config(chain, ucfg);
607d6164b77SAlexander V. Chernikov return (0);
608d6164b77SAlexander V. Chernikov }
609d6164b77SAlexander V. Chernikov
610d6164b77SAlexander V. Chernikov /*
611d6164b77SAlexander V. Chernikov * Destroys given nat instances.
612d6164b77SAlexander V. Chernikov * Data layout (v0)(current):
613d6164b77SAlexander V. Chernikov * Request: [ ipfw_obj_header ]
614d6164b77SAlexander V. Chernikov *
615d6164b77SAlexander V. Chernikov * Returns 0 on success
616d6164b77SAlexander V. Chernikov */
617d6164b77SAlexander V. Chernikov static int
nat44_destroy(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)618d6164b77SAlexander V. Chernikov nat44_destroy(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
619d6164b77SAlexander V. Chernikov struct sockopt_data *sd)
620d6164b77SAlexander V. Chernikov {
621d6164b77SAlexander V. Chernikov ipfw_obj_header *oh;
622d6164b77SAlexander V. Chernikov struct cfg_nat *ptr;
623d6164b77SAlexander V. Chernikov ipfw_obj_ntlv *ntlv;
624d6164b77SAlexander V. Chernikov
625d6164b77SAlexander V. Chernikov /* Check minimum header size */
626d6164b77SAlexander V. Chernikov if (sd->valsize < sizeof(*oh))
627d6164b77SAlexander V. Chernikov return (EINVAL);
628d6164b77SAlexander V. Chernikov
629d6164b77SAlexander V. Chernikov oh = (ipfw_obj_header *)sd->kbuf;
630d6164b77SAlexander V. Chernikov
631d6164b77SAlexander V. Chernikov /* Basic length checks for TLVs */
632d6164b77SAlexander V. Chernikov if (oh->ntlv.head.length != sizeof(oh->ntlv))
633d6164b77SAlexander V. Chernikov return (EINVAL);
634d6164b77SAlexander V. Chernikov
635d6164b77SAlexander V. Chernikov ntlv = &oh->ntlv;
636d6164b77SAlexander V. Chernikov /* Check if name is properly terminated */
637d6164b77SAlexander V. Chernikov if (strnlen(ntlv->name, sizeof(ntlv->name)) == sizeof(ntlv->name))
638d6164b77SAlexander V. Chernikov return (EINVAL);
639d6164b77SAlexander V. Chernikov
640d6164b77SAlexander V. Chernikov IPFW_UH_WLOCK(chain);
641d6164b77SAlexander V. Chernikov ptr = lookup_nat_name(&chain->nat, ntlv->name);
642d6164b77SAlexander V. Chernikov if (ptr == NULL) {
643d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
644d6164b77SAlexander V. Chernikov return (ESRCH);
645d6164b77SAlexander V. Chernikov }
646d6164b77SAlexander V. Chernikov IPFW_WLOCK(chain);
647d6164b77SAlexander V. Chernikov LIST_REMOVE(ptr, _next);
648d6164b77SAlexander V. Chernikov flush_nat_ptrs(chain, ptr->id);
649d6164b77SAlexander V. Chernikov IPFW_WUNLOCK(chain);
650d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
651d6164b77SAlexander V. Chernikov
6521a458088SAlexander V. Chernikov free_nat_instance(ptr);
653d6164b77SAlexander V. Chernikov
654d6164b77SAlexander V. Chernikov return (0);
655d6164b77SAlexander V. Chernikov }
656d6164b77SAlexander V. Chernikov
657d6164b77SAlexander V. Chernikov static void
export_nat_cfg(struct cfg_nat * ptr,struct nat44_cfg_nat * ucfg)658d6164b77SAlexander V. Chernikov export_nat_cfg(struct cfg_nat *ptr, struct nat44_cfg_nat *ucfg)
659d6164b77SAlexander V. Chernikov {
660d6164b77SAlexander V. Chernikov
661d6164b77SAlexander V. Chernikov snprintf(ucfg->name, sizeof(ucfg->name), "%d", ptr->id);
662d6164b77SAlexander V. Chernikov ucfg->ip = ptr->ip;
663d6164b77SAlexander V. Chernikov ucfg->redir_cnt = ptr->redir_cnt;
664d6164b77SAlexander V. Chernikov ucfg->mode = ptr->mode;
665a08cdb6cSNeel Chauhan ucfg->alias_port_lo = ptr->alias_port_lo;
666a08cdb6cSNeel Chauhan ucfg->alias_port_hi = ptr->alias_port_hi;
667d6164b77SAlexander V. Chernikov strlcpy(ucfg->if_name, ptr->if_name, sizeof(ucfg->if_name));
668d6164b77SAlexander V. Chernikov }
669d6164b77SAlexander V. Chernikov
670d6164b77SAlexander V. Chernikov /*
671d6164b77SAlexander V. Chernikov * Gets config for given nat instance
672d6164b77SAlexander V. Chernikov * Data layout (v0)(current):
673d6164b77SAlexander V. Chernikov * Request: [ ipfw_obj_header nat44_cfg_nat .. ]
674d6164b77SAlexander V. Chernikov *
675d6164b77SAlexander V. Chernikov * Returns 0 on success
676d6164b77SAlexander V. Chernikov */
677d6164b77SAlexander V. Chernikov static int
nat44_get_cfg(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)678d6164b77SAlexander V. Chernikov nat44_get_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
679d6164b77SAlexander V. Chernikov struct sockopt_data *sd)
680d6164b77SAlexander V. Chernikov {
681d6164b77SAlexander V. Chernikov ipfw_obj_header *oh;
682d6164b77SAlexander V. Chernikov struct nat44_cfg_nat *ucfg;
683d6164b77SAlexander V. Chernikov struct cfg_nat *ptr;
684d6164b77SAlexander V. Chernikov struct cfg_redir *r;
685d6164b77SAlexander V. Chernikov struct cfg_spool *s;
686d6164b77SAlexander V. Chernikov struct nat44_cfg_redir *ser_r;
687d6164b77SAlexander V. Chernikov struct nat44_cfg_spool *ser_s;
688d6164b77SAlexander V. Chernikov size_t sz;
689d6164b77SAlexander V. Chernikov
690d6164b77SAlexander V. Chernikov sz = sizeof(*oh) + sizeof(*ucfg);
691d6164b77SAlexander V. Chernikov /* Check minimum header size */
692d6164b77SAlexander V. Chernikov if (sd->valsize < sz)
693d6164b77SAlexander V. Chernikov return (EINVAL);
694d6164b77SAlexander V. Chernikov
695d6164b77SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
696d6164b77SAlexander V. Chernikov
697d6164b77SAlexander V. Chernikov /* Basic length checks for TLVs */
698d6164b77SAlexander V. Chernikov if (oh->ntlv.head.length != sizeof(oh->ntlv))
699d6164b77SAlexander V. Chernikov return (EINVAL);
700d6164b77SAlexander V. Chernikov
701d6164b77SAlexander V. Chernikov ucfg = (struct nat44_cfg_nat *)(oh + 1);
702d6164b77SAlexander V. Chernikov
703d6164b77SAlexander V. Chernikov /* Check if name is properly terminated */
704d6164b77SAlexander V. Chernikov if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
705d6164b77SAlexander V. Chernikov return (EINVAL);
706d6164b77SAlexander V. Chernikov
707d6164b77SAlexander V. Chernikov IPFW_UH_RLOCK(chain);
708d6164b77SAlexander V. Chernikov ptr = lookup_nat_name(&chain->nat, ucfg->name);
709d6164b77SAlexander V. Chernikov if (ptr == NULL) {
710d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain);
711d6164b77SAlexander V. Chernikov return (ESRCH);
712d6164b77SAlexander V. Chernikov }
713d6164b77SAlexander V. Chernikov
714d6164b77SAlexander V. Chernikov export_nat_cfg(ptr, ucfg);
715d6164b77SAlexander V. Chernikov
716d6164b77SAlexander V. Chernikov /* Estimate memory amount */
7179f925e8aSAlexander V. Chernikov sz = sizeof(ipfw_obj_header) + sizeof(struct nat44_cfg_nat);
718d6164b77SAlexander V. Chernikov LIST_FOREACH(r, &ptr->redir_chain, _next) {
719d6164b77SAlexander V. Chernikov sz += sizeof(struct nat44_cfg_redir);
720d6164b77SAlexander V. Chernikov LIST_FOREACH(s, &r->spool_chain, _next)
721d6164b77SAlexander V. Chernikov sz += sizeof(struct nat44_cfg_spool);
722d6164b77SAlexander V. Chernikov }
723d6164b77SAlexander V. Chernikov
724d6164b77SAlexander V. Chernikov ucfg->size = sz;
7259f925e8aSAlexander V. Chernikov if (sd->valsize < sz) {
726d6164b77SAlexander V. Chernikov /*
727d6164b77SAlexander V. Chernikov * Submitted buffer size is not enough.
728d6164b77SAlexander V. Chernikov * WE've already filled in @ucfg structure with
729d6164b77SAlexander V. Chernikov * relevant info including size, so we
730d6164b77SAlexander V. Chernikov * can return. Buffer will be flushed automatically.
731d6164b77SAlexander V. Chernikov */
732d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain);
733d6164b77SAlexander V. Chernikov return (ENOMEM);
734d6164b77SAlexander V. Chernikov }
735d6164b77SAlexander V. Chernikov
736d6164b77SAlexander V. Chernikov /* Size OK, let's copy data */
737d6164b77SAlexander V. Chernikov LIST_FOREACH(r, &ptr->redir_chain, _next) {
738d6164b77SAlexander V. Chernikov ser_r = (struct nat44_cfg_redir *)ipfw_get_sopt_space(sd,
739d6164b77SAlexander V. Chernikov sizeof(*ser_r));
740d6164b77SAlexander V. Chernikov ser_r->mode = r->mode;
741d6164b77SAlexander V. Chernikov ser_r->laddr = r->laddr;
742d6164b77SAlexander V. Chernikov ser_r->paddr = r->paddr;
743d6164b77SAlexander V. Chernikov ser_r->raddr = r->raddr;
744d6164b77SAlexander V. Chernikov ser_r->lport = r->lport;
745d6164b77SAlexander V. Chernikov ser_r->pport = r->pport;
746d6164b77SAlexander V. Chernikov ser_r->rport = r->rport;
747d6164b77SAlexander V. Chernikov ser_r->pport_cnt = r->pport_cnt;
748d6164b77SAlexander V. Chernikov ser_r->rport_cnt = r->rport_cnt;
749d6164b77SAlexander V. Chernikov ser_r->proto = r->proto;
750d6164b77SAlexander V. Chernikov ser_r->spool_cnt = r->spool_cnt;
751d6164b77SAlexander V. Chernikov
752d6164b77SAlexander V. Chernikov LIST_FOREACH(s, &r->spool_chain, _next) {
753d6164b77SAlexander V. Chernikov ser_s = (struct nat44_cfg_spool *)ipfw_get_sopt_space(
754d6164b77SAlexander V. Chernikov sd, sizeof(*ser_s));
755d6164b77SAlexander V. Chernikov
756d6164b77SAlexander V. Chernikov ser_s->addr = s->addr;
757d6164b77SAlexander V. Chernikov ser_s->port = s->port;
758d6164b77SAlexander V. Chernikov }
759d6164b77SAlexander V. Chernikov }
760d6164b77SAlexander V. Chernikov
761d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain);
762d6164b77SAlexander V. Chernikov
763d6164b77SAlexander V. Chernikov return (0);
764d6164b77SAlexander V. Chernikov }
765d6164b77SAlexander V. Chernikov
766d6164b77SAlexander V. Chernikov /*
767d6164b77SAlexander V. Chernikov * Lists all nat44 instances currently available in kernel.
768d6164b77SAlexander V. Chernikov * Data layout (v0)(current):
769d6164b77SAlexander V. Chernikov * Request: [ ipfw_obj_lheader ]
770d6164b77SAlexander V. Chernikov * Reply: [ ipfw_obj_lheader nat44_cfg_nat x N ]
771d6164b77SAlexander V. Chernikov *
772d6164b77SAlexander V. Chernikov * Returns 0 on success
773d6164b77SAlexander V. Chernikov */
774d6164b77SAlexander V. Chernikov static int
nat44_list_nat(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)775d6164b77SAlexander V. Chernikov nat44_list_nat(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
776d6164b77SAlexander V. Chernikov struct sockopt_data *sd)
777d6164b77SAlexander V. Chernikov {
778d6164b77SAlexander V. Chernikov ipfw_obj_lheader *olh;
779d6164b77SAlexander V. Chernikov struct nat44_cfg_nat *ucfg;
780d6164b77SAlexander V. Chernikov struct cfg_nat *ptr;
781d6164b77SAlexander V. Chernikov int nat_count;
782d6164b77SAlexander V. Chernikov
783d6164b77SAlexander V. Chernikov /* Check minimum header size */
784d6164b77SAlexander V. Chernikov if (sd->valsize < sizeof(ipfw_obj_lheader))
785d6164b77SAlexander V. Chernikov return (EINVAL);
786d6164b77SAlexander V. Chernikov
787d6164b77SAlexander V. Chernikov olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh));
788d6164b77SAlexander V. Chernikov IPFW_UH_RLOCK(chain);
789d6164b77SAlexander V. Chernikov nat_count = 0;
790d6164b77SAlexander V. Chernikov LIST_FOREACH(ptr, &chain->nat, _next)
791d6164b77SAlexander V. Chernikov nat_count++;
792d6164b77SAlexander V. Chernikov
793d6164b77SAlexander V. Chernikov olh->count = nat_count;
794d6164b77SAlexander V. Chernikov olh->objsize = sizeof(struct nat44_cfg_nat);
795d6164b77SAlexander V. Chernikov olh->size = sizeof(*olh) + olh->count * olh->objsize;
796d6164b77SAlexander V. Chernikov
797d6164b77SAlexander V. Chernikov if (sd->valsize < olh->size) {
798d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain);
799d6164b77SAlexander V. Chernikov return (ENOMEM);
800d6164b77SAlexander V. Chernikov }
801d6164b77SAlexander V. Chernikov
802d6164b77SAlexander V. Chernikov LIST_FOREACH(ptr, &chain->nat, _next) {
803d6164b77SAlexander V. Chernikov ucfg = (struct nat44_cfg_nat *)ipfw_get_sopt_space(sd,
804d6164b77SAlexander V. Chernikov sizeof(*ucfg));
805d6164b77SAlexander V. Chernikov export_nat_cfg(ptr, ucfg);
806d6164b77SAlexander V. Chernikov }
807d6164b77SAlexander V. Chernikov
808d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain);
809d6164b77SAlexander V. Chernikov
810d6164b77SAlexander V. Chernikov return (0);
811d6164b77SAlexander V. Chernikov }
812d6164b77SAlexander V. Chernikov
813d6164b77SAlexander V. Chernikov /*
814d6164b77SAlexander V. Chernikov * Gets log for given nat instance
815d6164b77SAlexander V. Chernikov * Data layout (v0)(current):
816d6164b77SAlexander V. Chernikov * Request: [ ipfw_obj_header nat44_cfg_nat ]
817d6164b77SAlexander V. Chernikov * Reply: [ ipfw_obj_header nat44_cfg_nat LOGBUFFER ]
818d6164b77SAlexander V. Chernikov *
819d6164b77SAlexander V. Chernikov * Returns 0 on success
820d6164b77SAlexander V. Chernikov */
821d6164b77SAlexander V. Chernikov static int
nat44_get_log(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)822d6164b77SAlexander V. Chernikov nat44_get_log(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
823d6164b77SAlexander V. Chernikov struct sockopt_data *sd)
824d6164b77SAlexander V. Chernikov {
825d6164b77SAlexander V. Chernikov ipfw_obj_header *oh;
826d6164b77SAlexander V. Chernikov struct nat44_cfg_nat *ucfg;
827d6164b77SAlexander V. Chernikov struct cfg_nat *ptr;
828d6164b77SAlexander V. Chernikov void *pbuf;
829d6164b77SAlexander V. Chernikov size_t sz;
830d6164b77SAlexander V. Chernikov
831d6164b77SAlexander V. Chernikov sz = sizeof(*oh) + sizeof(*ucfg);
832d6164b77SAlexander V. Chernikov /* Check minimum header size */
833d6164b77SAlexander V. Chernikov if (sd->valsize < sz)
834d6164b77SAlexander V. Chernikov return (EINVAL);
835d6164b77SAlexander V. Chernikov
836d6164b77SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
837d6164b77SAlexander V. Chernikov
838d6164b77SAlexander V. Chernikov /* Basic length checks for TLVs */
839d6164b77SAlexander V. Chernikov if (oh->ntlv.head.length != sizeof(oh->ntlv))
840d6164b77SAlexander V. Chernikov return (EINVAL);
841d6164b77SAlexander V. Chernikov
842d6164b77SAlexander V. Chernikov ucfg = (struct nat44_cfg_nat *)(oh + 1);
843d6164b77SAlexander V. Chernikov
844d6164b77SAlexander V. Chernikov /* Check if name is properly terminated */
845d6164b77SAlexander V. Chernikov if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
846d6164b77SAlexander V. Chernikov return (EINVAL);
847d6164b77SAlexander V. Chernikov
848d6164b77SAlexander V. Chernikov IPFW_UH_RLOCK(chain);
849d6164b77SAlexander V. Chernikov ptr = lookup_nat_name(&chain->nat, ucfg->name);
850d6164b77SAlexander V. Chernikov if (ptr == NULL) {
851d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain);
852d6164b77SAlexander V. Chernikov return (ESRCH);
853d6164b77SAlexander V. Chernikov }
854d6164b77SAlexander V. Chernikov
855d6164b77SAlexander V. Chernikov if (ptr->lib->logDesc == NULL) {
856d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain);
857d6164b77SAlexander V. Chernikov return (ENOENT);
858d6164b77SAlexander V. Chernikov }
859d6164b77SAlexander V. Chernikov
860d6164b77SAlexander V. Chernikov export_nat_cfg(ptr, ucfg);
861d6164b77SAlexander V. Chernikov
862d6164b77SAlexander V. Chernikov /* Estimate memory amount */
863d6164b77SAlexander V. Chernikov ucfg->size = sizeof(struct nat44_cfg_nat) + LIBALIAS_BUF_SIZE;
864d6164b77SAlexander V. Chernikov if (sd->valsize < sz + sizeof(*oh)) {
865d6164b77SAlexander V. Chernikov /*
866d6164b77SAlexander V. Chernikov * Submitted buffer size is not enough.
867d6164b77SAlexander V. Chernikov * WE've already filled in @ucfg structure with
868d6164b77SAlexander V. Chernikov * relevant info including size, so we
869d6164b77SAlexander V. Chernikov * can return. Buffer will be flushed automatically.
870d6164b77SAlexander V. Chernikov */
871d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain);
872d6164b77SAlexander V. Chernikov return (ENOMEM);
873d6164b77SAlexander V. Chernikov }
874d6164b77SAlexander V. Chernikov
875d6164b77SAlexander V. Chernikov pbuf = (void *)ipfw_get_sopt_space(sd, LIBALIAS_BUF_SIZE);
876d6164b77SAlexander V. Chernikov memcpy(pbuf, ptr->lib->logDesc, LIBALIAS_BUF_SIZE);
877d6164b77SAlexander V. Chernikov
878d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain);
879d6164b77SAlexander V. Chernikov
880d6164b77SAlexander V. Chernikov return (0);
881d6164b77SAlexander V. Chernikov }
882d6164b77SAlexander V. Chernikov
883d6164b77SAlexander V. Chernikov static struct ipfw_sopt_handler scodes[] = {
884*4a77657cSAndrey V. Elsukov { IP_FW_NAT44_XCONFIG, IP_FW3_OPVER, HDIR_SET, nat44_cfg },
885*4a77657cSAndrey V. Elsukov { IP_FW_NAT44_DESTROY, IP_FW3_OPVER, HDIR_SET, nat44_destroy },
886*4a77657cSAndrey V. Elsukov { IP_FW_NAT44_XGETCONFIG, IP_FW3_OPVER, HDIR_GET, nat44_get_cfg },
887*4a77657cSAndrey V. Elsukov { IP_FW_NAT44_LIST_NAT, IP_FW3_OPVER, HDIR_GET, nat44_list_nat },
888*4a77657cSAndrey V. Elsukov { IP_FW_NAT44_XGETLOG, IP_FW3_OPVER, HDIR_GET, nat44_get_log },
889d6164b77SAlexander V. Chernikov };
890d6164b77SAlexander V. Chernikov
891d6164b77SAlexander V. Chernikov /*
892d6164b77SAlexander V. Chernikov * Legacy configuration routines
893d6164b77SAlexander V. Chernikov */
894d6164b77SAlexander V. Chernikov
895d6164b77SAlexander V. Chernikov struct cfg_spool_legacy {
896d6164b77SAlexander V. Chernikov LIST_ENTRY(cfg_spool_legacy) _next;
897d6164b77SAlexander V. Chernikov struct in_addr addr;
898d6164b77SAlexander V. Chernikov u_short port;
899d6164b77SAlexander V. Chernikov };
900d6164b77SAlexander V. Chernikov
901d6164b77SAlexander V. Chernikov struct cfg_redir_legacy {
902d6164b77SAlexander V. Chernikov LIST_ENTRY(cfg_redir) _next;
903d6164b77SAlexander V. Chernikov u_int16_t mode;
904d6164b77SAlexander V. Chernikov struct in_addr laddr;
905d6164b77SAlexander V. Chernikov struct in_addr paddr;
906d6164b77SAlexander V. Chernikov struct in_addr raddr;
907d6164b77SAlexander V. Chernikov u_short lport;
908d6164b77SAlexander V. Chernikov u_short pport;
909d6164b77SAlexander V. Chernikov u_short rport;
910d6164b77SAlexander V. Chernikov u_short pport_cnt;
911d6164b77SAlexander V. Chernikov u_short rport_cnt;
912d6164b77SAlexander V. Chernikov int proto;
913d6164b77SAlexander V. Chernikov struct alias_link **alink;
914d6164b77SAlexander V. Chernikov u_int16_t spool_cnt;
915d6164b77SAlexander V. Chernikov LIST_HEAD(, cfg_spool_legacy) spool_chain;
916d6164b77SAlexander V. Chernikov };
917d6164b77SAlexander V. Chernikov
918d6164b77SAlexander V. Chernikov struct cfg_nat_legacy {
919d6164b77SAlexander V. Chernikov LIST_ENTRY(cfg_nat_legacy) _next;
920d6164b77SAlexander V. Chernikov int id;
921d6164b77SAlexander V. Chernikov struct in_addr ip;
922d6164b77SAlexander V. Chernikov char if_name[IF_NAMESIZE];
923d6164b77SAlexander V. Chernikov int mode;
924d6164b77SAlexander V. Chernikov struct libalias *lib;
925d6164b77SAlexander V. Chernikov int redir_cnt;
926d6164b77SAlexander V. Chernikov LIST_HEAD(, cfg_redir_legacy) redir_chain;
927d6164b77SAlexander V. Chernikov };
928d6164b77SAlexander V. Chernikov
929d6164b77SAlexander V. Chernikov static int
ipfw_nat_cfg(struct sockopt * sopt)930d6164b77SAlexander V. Chernikov ipfw_nat_cfg(struct sockopt *sopt)
931d6164b77SAlexander V. Chernikov {
932d6164b77SAlexander V. Chernikov struct cfg_nat_legacy *cfg;
933d6164b77SAlexander V. Chernikov struct nat44_cfg_nat *ucfg;
934d6164b77SAlexander V. Chernikov struct cfg_redir_legacy *rdir;
935d6164b77SAlexander V. Chernikov struct nat44_cfg_redir *urdir;
936d6164b77SAlexander V. Chernikov char *buf;
937d6164b77SAlexander V. Chernikov size_t len, len2;
938d6164b77SAlexander V. Chernikov int error, i;
939d6164b77SAlexander V. Chernikov
940d6164b77SAlexander V. Chernikov len = sopt->sopt_valsize;
941d6164b77SAlexander V. Chernikov len2 = len + 128;
942d6164b77SAlexander V. Chernikov
943d6164b77SAlexander V. Chernikov /*
944d6164b77SAlexander V. Chernikov * Allocate 2x buffer to store converted structures.
945a4641f4eSPedro F. Giffuni * new redir_cfg has shrunk, so we're sure that
946d6164b77SAlexander V. Chernikov * new buffer size is enough.
947d6164b77SAlexander V. Chernikov */
948d6164b77SAlexander V. Chernikov buf = malloc(roundup2(len, 8) + len2, M_TEMP, M_WAITOK | M_ZERO);
949d6164b77SAlexander V. Chernikov error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat_legacy));
950d6164b77SAlexander V. Chernikov if (error != 0)
951d6164b77SAlexander V. Chernikov goto out;
952d6164b77SAlexander V. Chernikov
953d6164b77SAlexander V. Chernikov cfg = (struct cfg_nat_legacy *)buf;
954d6164b77SAlexander V. Chernikov if (cfg->id < 0) {
955d6164b77SAlexander V. Chernikov error = EINVAL;
956d6164b77SAlexander V. Chernikov goto out;
957d6164b77SAlexander V. Chernikov }
958d6164b77SAlexander V. Chernikov
959d6164b77SAlexander V. Chernikov ucfg = (struct nat44_cfg_nat *)&buf[roundup2(len, 8)];
960d6164b77SAlexander V. Chernikov snprintf(ucfg->name, sizeof(ucfg->name), "%d", cfg->id);
961d6164b77SAlexander V. Chernikov strlcpy(ucfg->if_name, cfg->if_name, sizeof(ucfg->if_name));
962d6164b77SAlexander V. Chernikov ucfg->ip = cfg->ip;
963d6164b77SAlexander V. Chernikov ucfg->mode = cfg->mode;
964d6164b77SAlexander V. Chernikov ucfg->redir_cnt = cfg->redir_cnt;
965d6164b77SAlexander V. Chernikov
966d6164b77SAlexander V. Chernikov if (len < sizeof(*cfg) + cfg->redir_cnt * sizeof(*rdir)) {
967d6164b77SAlexander V. Chernikov error = EINVAL;
968d6164b77SAlexander V. Chernikov goto out;
969d6164b77SAlexander V. Chernikov }
970d6164b77SAlexander V. Chernikov
971d6164b77SAlexander V. Chernikov urdir = (struct nat44_cfg_redir *)(ucfg + 1);
972d6164b77SAlexander V. Chernikov rdir = (struct cfg_redir_legacy *)(cfg + 1);
973d6164b77SAlexander V. Chernikov for (i = 0; i < cfg->redir_cnt; i++) {
974d6164b77SAlexander V. Chernikov urdir->mode = rdir->mode;
975d6164b77SAlexander V. Chernikov urdir->laddr = rdir->laddr;
976d6164b77SAlexander V. Chernikov urdir->paddr = rdir->paddr;
977d6164b77SAlexander V. Chernikov urdir->raddr = rdir->raddr;
978d6164b77SAlexander V. Chernikov urdir->lport = rdir->lport;
979d6164b77SAlexander V. Chernikov urdir->pport = rdir->pport;
980d6164b77SAlexander V. Chernikov urdir->rport = rdir->rport;
981d6164b77SAlexander V. Chernikov urdir->pport_cnt = rdir->pport_cnt;
982d6164b77SAlexander V. Chernikov urdir->rport_cnt = rdir->rport_cnt;
983d6164b77SAlexander V. Chernikov urdir->proto = rdir->proto;
984d6164b77SAlexander V. Chernikov urdir->spool_cnt = rdir->spool_cnt;
985d6164b77SAlexander V. Chernikov
986d6164b77SAlexander V. Chernikov urdir++;
987d6164b77SAlexander V. Chernikov rdir++;
988d6164b77SAlexander V. Chernikov }
989d6164b77SAlexander V. Chernikov
990d6164b77SAlexander V. Chernikov nat44_config(&V_layer3_chain, ucfg);
9913b3a8eb9SGleb Smirnoff
9923b3a8eb9SGleb Smirnoff out:
9933b3a8eb9SGleb Smirnoff free(buf, M_TEMP);
9943b3a8eb9SGleb Smirnoff return (error);
9953b3a8eb9SGleb Smirnoff }
9963b3a8eb9SGleb Smirnoff
9973b3a8eb9SGleb Smirnoff static int
ipfw_nat_del(struct sockopt * sopt)9983b3a8eb9SGleb Smirnoff ipfw_nat_del(struct sockopt *sopt)
9993b3a8eb9SGleb Smirnoff {
10003b3a8eb9SGleb Smirnoff struct cfg_nat *ptr;
10013b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain = &V_layer3_chain;
10023b3a8eb9SGleb Smirnoff int i;
10033b3a8eb9SGleb Smirnoff
10043b3a8eb9SGleb Smirnoff sooptcopyin(sopt, &i, sizeof i, sizeof i);
10053b3a8eb9SGleb Smirnoff /* XXX validate i */
1006d6164b77SAlexander V. Chernikov IPFW_UH_WLOCK(chain);
10073b3a8eb9SGleb Smirnoff ptr = lookup_nat(&chain->nat, i);
10083b3a8eb9SGleb Smirnoff if (ptr == NULL) {
1009d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
10103b3a8eb9SGleb Smirnoff return (EINVAL);
10113b3a8eb9SGleb Smirnoff }
1012d6164b77SAlexander V. Chernikov IPFW_WLOCK(chain);
10133b3a8eb9SGleb Smirnoff LIST_REMOVE(ptr, _next);
10143b3a8eb9SGleb Smirnoff flush_nat_ptrs(chain, i);
10153b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(chain);
1016d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
10171a458088SAlexander V. Chernikov free_nat_instance(ptr);
10183b3a8eb9SGleb Smirnoff return (0);
10193b3a8eb9SGleb Smirnoff }
10203b3a8eb9SGleb Smirnoff
10213b3a8eb9SGleb Smirnoff static int
ipfw_nat_get_cfg(struct sockopt * sopt)10223b3a8eb9SGleb Smirnoff ipfw_nat_get_cfg(struct sockopt *sopt)
10233b3a8eb9SGleb Smirnoff {
10243b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain = &V_layer3_chain;
10253b3a8eb9SGleb Smirnoff struct cfg_nat *n;
1026d6164b77SAlexander V. Chernikov struct cfg_nat_legacy *ucfg;
10273b3a8eb9SGleb Smirnoff struct cfg_redir *r;
10283b3a8eb9SGleb Smirnoff struct cfg_spool *s;
1029d6164b77SAlexander V. Chernikov struct cfg_redir_legacy *ser_r;
1030d6164b77SAlexander V. Chernikov struct cfg_spool_legacy *ser_s;
10313b3a8eb9SGleb Smirnoff char *data;
10323b3a8eb9SGleb Smirnoff int gencnt, nat_cnt, len, error;
10333b3a8eb9SGleb Smirnoff
10343b3a8eb9SGleb Smirnoff nat_cnt = 0;
10353b3a8eb9SGleb Smirnoff len = sizeof(nat_cnt);
10363b3a8eb9SGleb Smirnoff
1037d6164b77SAlexander V. Chernikov IPFW_UH_RLOCK(chain);
10383b3a8eb9SGleb Smirnoff retry:
10393b3a8eb9SGleb Smirnoff gencnt = chain->gencnt;
10403b3a8eb9SGleb Smirnoff /* Estimate memory amount */
10413b3a8eb9SGleb Smirnoff LIST_FOREACH(n, &chain->nat, _next) {
10423b3a8eb9SGleb Smirnoff nat_cnt++;
1043d6164b77SAlexander V. Chernikov len += sizeof(struct cfg_nat_legacy);
10443b3a8eb9SGleb Smirnoff LIST_FOREACH(r, &n->redir_chain, _next) {
1045d6164b77SAlexander V. Chernikov len += sizeof(struct cfg_redir_legacy);
10463b3a8eb9SGleb Smirnoff LIST_FOREACH(s, &r->spool_chain, _next)
1047d6164b77SAlexander V. Chernikov len += sizeof(struct cfg_spool_legacy);
10483b3a8eb9SGleb Smirnoff }
10493b3a8eb9SGleb Smirnoff }
1050d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain);
10513b3a8eb9SGleb Smirnoff
10523b3a8eb9SGleb Smirnoff data = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
10533b3a8eb9SGleb Smirnoff bcopy(&nat_cnt, data, sizeof(nat_cnt));
10543b3a8eb9SGleb Smirnoff
10553b3a8eb9SGleb Smirnoff nat_cnt = 0;
10563b3a8eb9SGleb Smirnoff len = sizeof(nat_cnt);
10573b3a8eb9SGleb Smirnoff
1058d6164b77SAlexander V. Chernikov IPFW_UH_RLOCK(chain);
10593b3a8eb9SGleb Smirnoff if (gencnt != chain->gencnt) {
10603b3a8eb9SGleb Smirnoff free(data, M_TEMP);
10613b3a8eb9SGleb Smirnoff goto retry;
10623b3a8eb9SGleb Smirnoff }
10633b3a8eb9SGleb Smirnoff /* Serialize all the data. */
10643b3a8eb9SGleb Smirnoff LIST_FOREACH(n, &chain->nat, _next) {
1065d6164b77SAlexander V. Chernikov ucfg = (struct cfg_nat_legacy *)&data[len];
1066d6164b77SAlexander V. Chernikov ucfg->id = n->id;
1067d6164b77SAlexander V. Chernikov ucfg->ip = n->ip;
1068d6164b77SAlexander V. Chernikov ucfg->redir_cnt = n->redir_cnt;
1069d6164b77SAlexander V. Chernikov ucfg->mode = n->mode;
1070d6164b77SAlexander V. Chernikov strlcpy(ucfg->if_name, n->if_name, sizeof(ucfg->if_name));
1071d6164b77SAlexander V. Chernikov len += sizeof(struct cfg_nat_legacy);
10723b3a8eb9SGleb Smirnoff LIST_FOREACH(r, &n->redir_chain, _next) {
1073d6164b77SAlexander V. Chernikov ser_r = (struct cfg_redir_legacy *)&data[len];
1074d6164b77SAlexander V. Chernikov ser_r->mode = r->mode;
1075d6164b77SAlexander V. Chernikov ser_r->laddr = r->laddr;
1076d6164b77SAlexander V. Chernikov ser_r->paddr = r->paddr;
1077d6164b77SAlexander V. Chernikov ser_r->raddr = r->raddr;
1078d6164b77SAlexander V. Chernikov ser_r->lport = r->lport;
1079d6164b77SAlexander V. Chernikov ser_r->pport = r->pport;
1080d6164b77SAlexander V. Chernikov ser_r->rport = r->rport;
1081d6164b77SAlexander V. Chernikov ser_r->pport_cnt = r->pport_cnt;
1082d6164b77SAlexander V. Chernikov ser_r->rport_cnt = r->rport_cnt;
1083d6164b77SAlexander V. Chernikov ser_r->proto = r->proto;
1084d6164b77SAlexander V. Chernikov ser_r->spool_cnt = r->spool_cnt;
1085d6164b77SAlexander V. Chernikov len += sizeof(struct cfg_redir_legacy);
10863b3a8eb9SGleb Smirnoff LIST_FOREACH(s, &r->spool_chain, _next) {
1087d6164b77SAlexander V. Chernikov ser_s = (struct cfg_spool_legacy *)&data[len];
1088d6164b77SAlexander V. Chernikov ser_s->addr = s->addr;
1089d6164b77SAlexander V. Chernikov ser_s->port = s->port;
1090d6164b77SAlexander V. Chernikov len += sizeof(struct cfg_spool_legacy);
10913b3a8eb9SGleb Smirnoff }
10923b3a8eb9SGleb Smirnoff }
10933b3a8eb9SGleb Smirnoff }
1094d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain);
10953b3a8eb9SGleb Smirnoff
10963b3a8eb9SGleb Smirnoff error = sooptcopyout(sopt, data, len);
10973b3a8eb9SGleb Smirnoff free(data, M_TEMP);
10983b3a8eb9SGleb Smirnoff
10993b3a8eb9SGleb Smirnoff return (error);
11003b3a8eb9SGleb Smirnoff }
11013b3a8eb9SGleb Smirnoff
11023b3a8eb9SGleb Smirnoff static int
ipfw_nat_get_log(struct sockopt * sopt)11033b3a8eb9SGleb Smirnoff ipfw_nat_get_log(struct sockopt *sopt)
11043b3a8eb9SGleb Smirnoff {
11053b3a8eb9SGleb Smirnoff uint8_t *data;
11063b3a8eb9SGleb Smirnoff struct cfg_nat *ptr;
11073b3a8eb9SGleb Smirnoff int i, size;
11083b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain;
1109ccba94b8SAlexander V. Chernikov IPFW_RLOCK_TRACKER;
11103b3a8eb9SGleb Smirnoff
11113b3a8eb9SGleb Smirnoff chain = &V_layer3_chain;
11123b3a8eb9SGleb Smirnoff
11133b3a8eb9SGleb Smirnoff IPFW_RLOCK(chain);
11143b3a8eb9SGleb Smirnoff /* one pass to count, one to copy the data */
11153b3a8eb9SGleb Smirnoff i = 0;
11163b3a8eb9SGleb Smirnoff LIST_FOREACH(ptr, &chain->nat, _next) {
11173b3a8eb9SGleb Smirnoff if (ptr->lib->logDesc == NULL)
11183b3a8eb9SGleb Smirnoff continue;
11193b3a8eb9SGleb Smirnoff i++;
11203b3a8eb9SGleb Smirnoff }
11213b3a8eb9SGleb Smirnoff size = i * (LIBALIAS_BUF_SIZE + sizeof(int));
11223b3a8eb9SGleb Smirnoff data = malloc(size, M_IPFW, M_NOWAIT | M_ZERO);
11233b3a8eb9SGleb Smirnoff if (data == NULL) {
11243b3a8eb9SGleb Smirnoff IPFW_RUNLOCK(chain);
11253b3a8eb9SGleb Smirnoff return (ENOSPC);
11263b3a8eb9SGleb Smirnoff }
11273b3a8eb9SGleb Smirnoff i = 0;
11283b3a8eb9SGleb Smirnoff LIST_FOREACH(ptr, &chain->nat, _next) {
11293b3a8eb9SGleb Smirnoff if (ptr->lib->logDesc == NULL)
11303b3a8eb9SGleb Smirnoff continue;
11313b3a8eb9SGleb Smirnoff bcopy(&ptr->id, &data[i], sizeof(int));
11323b3a8eb9SGleb Smirnoff i += sizeof(int);
11333b3a8eb9SGleb Smirnoff bcopy(ptr->lib->logDesc, &data[i], LIBALIAS_BUF_SIZE);
11343b3a8eb9SGleb Smirnoff i += LIBALIAS_BUF_SIZE;
11353b3a8eb9SGleb Smirnoff }
11363b3a8eb9SGleb Smirnoff IPFW_RUNLOCK(chain);
11373b3a8eb9SGleb Smirnoff sooptcopyout(sopt, data, size);
11383b3a8eb9SGleb Smirnoff free(data, M_IPFW);
11393b3a8eb9SGleb Smirnoff return(0);
11403b3a8eb9SGleb Smirnoff }
11413b3a8eb9SGleb Smirnoff
11428856400bSMikolaj Golub static int
vnet_ipfw_nat_init(const void * arg __unused)11438856400bSMikolaj Golub vnet_ipfw_nat_init(const void *arg __unused)
11443b3a8eb9SGleb Smirnoff {
11453b3a8eb9SGleb Smirnoff
11468856400bSMikolaj Golub V_ipfw_nat_ready = 1;
11478856400bSMikolaj Golub return (0);
11483b3a8eb9SGleb Smirnoff }
11493b3a8eb9SGleb Smirnoff
11508856400bSMikolaj Golub static int
vnet_ipfw_nat_uninit(const void * arg __unused)11518856400bSMikolaj Golub vnet_ipfw_nat_uninit(const void *arg __unused)
11523b3a8eb9SGleb Smirnoff {
11533b3a8eb9SGleb Smirnoff struct cfg_nat *ptr, *ptr_temp;
11543b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain;
11553b3a8eb9SGleb Smirnoff
11563b3a8eb9SGleb Smirnoff chain = &V_layer3_chain;
11573b3a8eb9SGleb Smirnoff IPFW_WLOCK(chain);
11589ac51e79SBjoern A. Zeeb V_ipfw_nat_ready = 0;
11593b3a8eb9SGleb Smirnoff LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) {
11603b3a8eb9SGleb Smirnoff LIST_REMOVE(ptr, _next);
11611a458088SAlexander V. Chernikov free_nat_instance(ptr);
11623b3a8eb9SGleb Smirnoff }
11633b3a8eb9SGleb Smirnoff flush_nat_ptrs(chain, -1 /* flush all */);
11648856400bSMikolaj Golub IPFW_WUNLOCK(chain);
11658856400bSMikolaj Golub return (0);
11668856400bSMikolaj Golub }
11678856400bSMikolaj Golub
11688856400bSMikolaj Golub static void
ipfw_nat_init(void)11698856400bSMikolaj Golub ipfw_nat_init(void)
11708856400bSMikolaj Golub {
11718856400bSMikolaj Golub
11728856400bSMikolaj Golub /* init ipfw hooks */
11738856400bSMikolaj Golub ipfw_nat_ptr = ipfw_nat;
11748856400bSMikolaj Golub lookup_nat_ptr = lookup_nat;
11758856400bSMikolaj Golub ipfw_nat_cfg_ptr = ipfw_nat_cfg;
11768856400bSMikolaj Golub ipfw_nat_del_ptr = ipfw_nat_del;
11778856400bSMikolaj Golub ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg;
11788856400bSMikolaj Golub ipfw_nat_get_log_ptr = ipfw_nat_get_log;
1179d6164b77SAlexander V. Chernikov IPFW_ADD_SOPT_HANDLER(1, scodes);
11808856400bSMikolaj Golub
11818856400bSMikolaj Golub ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change,
11828856400bSMikolaj Golub NULL, EVENTHANDLER_PRI_ANY);
11838856400bSMikolaj Golub }
11848856400bSMikolaj Golub
11858856400bSMikolaj Golub static void
ipfw_nat_destroy(void)11868856400bSMikolaj Golub ipfw_nat_destroy(void)
11878856400bSMikolaj Golub {
11888856400bSMikolaj Golub
11898856400bSMikolaj Golub EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag);
11903b3a8eb9SGleb Smirnoff /* deregister ipfw_nat */
1191d6164b77SAlexander V. Chernikov IPFW_DEL_SOPT_HANDLER(1, scodes);
11923b3a8eb9SGleb Smirnoff ipfw_nat_ptr = NULL;
11933b3a8eb9SGleb Smirnoff lookup_nat_ptr = NULL;
11943b3a8eb9SGleb Smirnoff ipfw_nat_cfg_ptr = NULL;
11953b3a8eb9SGleb Smirnoff ipfw_nat_del_ptr = NULL;
11963b3a8eb9SGleb Smirnoff ipfw_nat_get_cfg_ptr = NULL;
11973b3a8eb9SGleb Smirnoff ipfw_nat_get_log_ptr = NULL;
11983b3a8eb9SGleb Smirnoff }
11993b3a8eb9SGleb Smirnoff
12003b3a8eb9SGleb Smirnoff static int
ipfw_nat_modevent(module_t mod,int type,void * unused)12013b3a8eb9SGleb Smirnoff ipfw_nat_modevent(module_t mod, int type, void *unused)
12023b3a8eb9SGleb Smirnoff {
12033b3a8eb9SGleb Smirnoff int err = 0;
12043b3a8eb9SGleb Smirnoff
12053b3a8eb9SGleb Smirnoff switch (type) {
12063b3a8eb9SGleb Smirnoff case MOD_LOAD:
12073b3a8eb9SGleb Smirnoff break;
12083b3a8eb9SGleb Smirnoff
12093b3a8eb9SGleb Smirnoff case MOD_UNLOAD:
12103b3a8eb9SGleb Smirnoff break;
12113b3a8eb9SGleb Smirnoff
12123b3a8eb9SGleb Smirnoff default:
12133b3a8eb9SGleb Smirnoff return EOPNOTSUPP;
12143b3a8eb9SGleb Smirnoff break;
12153b3a8eb9SGleb Smirnoff }
12163b3a8eb9SGleb Smirnoff return err;
12173b3a8eb9SGleb Smirnoff }
12183b3a8eb9SGleb Smirnoff
12193b3a8eb9SGleb Smirnoff static moduledata_t ipfw_nat_mod = {
12203b3a8eb9SGleb Smirnoff "ipfw_nat",
12213b3a8eb9SGleb Smirnoff ipfw_nat_modevent,
12229823d527SKevin Lo 0
12233b3a8eb9SGleb Smirnoff };
12243b3a8eb9SGleb Smirnoff
12258856400bSMikolaj Golub /* Define startup order. */
122689856f7eSBjoern A. Zeeb #define IPFW_NAT_SI_SUB_FIREWALL SI_SUB_PROTO_FIREWALL
12278a477d48SMikolaj Golub #define IPFW_NAT_MODEVENT_ORDER (SI_ORDER_ANY - 128) /* after ipfw */
12288856400bSMikolaj Golub #define IPFW_NAT_MODULE_ORDER (IPFW_NAT_MODEVENT_ORDER + 1)
12298856400bSMikolaj Golub #define IPFW_NAT_VNET_ORDER (IPFW_NAT_MODEVENT_ORDER + 2)
12308856400bSMikolaj Golub
12318856400bSMikolaj Golub DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, IPFW_NAT_SI_SUB_FIREWALL, SI_ORDER_ANY);
12323b3a8eb9SGleb Smirnoff MODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1);
1233f9ab623bSAlexander V. Chernikov MODULE_DEPEND(ipfw_nat, ipfw, 3, 3, 3);
12343b3a8eb9SGleb Smirnoff MODULE_VERSION(ipfw_nat, 1);
12358856400bSMikolaj Golub
12368856400bSMikolaj Golub SYSINIT(ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER,
12378856400bSMikolaj Golub ipfw_nat_init, NULL);
12388856400bSMikolaj Golub VNET_SYSINIT(vnet_ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_VNET_ORDER,
12398856400bSMikolaj Golub vnet_ipfw_nat_init, NULL);
12408856400bSMikolaj Golub
12418856400bSMikolaj Golub SYSUNINIT(ipfw_nat_destroy, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER,
12428856400bSMikolaj Golub ipfw_nat_destroy, NULL);
12438856400bSMikolaj Golub VNET_SYSUNINIT(vnet_ipfw_nat_uninit, IPFW_NAT_SI_SUB_FIREWALL,
12448856400bSMikolaj Golub IPFW_NAT_VNET_ORDER, vnet_ipfw_nat_uninit, NULL);
12458856400bSMikolaj Golub
12463b3a8eb9SGleb Smirnoff /* end of file */
1247