4 * Copyright (c) 2004, 2008 The NetBSD Foundation, Inc.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
29 #include <sys/param.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <sys/ioctl.h>
37 #include <netinet/in.h>
38 #include <netinet/in_systm.h>
39 #include <netinet/ip_compat.h>
40 #include <netinet/ipl.h>
41 #include <netinet/ip_fil.h>
42 #include <netinet/ip_nat.h>
44 #include <arpa/inet.h>
55 /* From netinet/in.h, but only _KERNEL_ gets them. */
56 #define satosin(sa) ((struct sockaddr_in *)(sa))
57 #define satosin6(sa) ((struct sockaddr_in6 *)(sa))
62 struct ftp_proxy_nat
{
64 LIST_ENTRY(ftp_proxy_nat
) link
;
67 struct ftp_proxy_entry
{
69 char proxy_tag
[IPFTAG_LEN
];
71 LIST_HEAD(, ftp_proxy_nat
) nat_entries
;
72 LIST_ENTRY(ftp_proxy_entry
) link
;
75 LIST_HEAD(, ftp_proxy_entry
) ftp_proxy_entries
=
76 LIST_HEAD_INITIALIZER(ftp_proxy_entries
);
78 static struct ftp_proxy_entry
*
79 ftp_proxy_entry_create(u_int32_t id
)
81 struct ftp_proxy_entry
*fpe
;
84 fpe
= malloc(sizeof(*fpe
));
91 rv
= snprintf(fpe
->proxy_tag
, sizeof(fpe
->proxy_tag
), "ftp_%d", id
);
92 if (rv
== -1 || rv
>= sizeof(fpe
->proxy_tag
)) {
97 LIST_INIT(&fpe
->nat_entries
);
98 LIST_INSERT_HEAD(&ftp_proxy_entries
, fpe
, link
);
104 ftp_proxy_entry_remove(struct ftp_proxy_entry
*fpe
)
106 struct ftp_proxy_nat
*fpn
;
108 while ((fpn
= LIST_FIRST(&fpe
->nat_entries
)) != NULL
) {
109 LIST_REMOVE(fpn
, link
);
113 LIST_REMOVE(fpe
, link
);
117 static struct ftp_proxy_entry
*
118 ftp_proxy_entry_find(u_int32_t id
)
120 struct ftp_proxy_entry
*fpe
;
122 LIST_FOREACH(fpe
, &ftp_proxy_entries
, link
) {
131 ftp_proxy_entry_add_nat(struct ftp_proxy_entry
*fpe
, ipnat_t ipn
)
133 struct ftp_proxy_nat
*fpn
;
135 fpn
= malloc(sizeof(*fpn
));
139 memcpy(&fpn
->ipn
, &ipn
, sizeof(fpn
->ipn
));
140 LIST_INSERT_HEAD(&fpe
->nat_entries
, fpn
, link
);
146 ipfilter_add_nat(ipnat_t ipn
)
150 memset(&obj
, 0, sizeof(obj
));
151 obj
.ipfo_rev
= IPFILTER_VERSION
;
152 obj
.ipfo_size
= sizeof(ipn
);
153 obj
.ipfo_type
= IPFOBJ_IPNAT
;
156 return ioctl(natfd
, SIOCADNAT
, &obj
);
160 ipfilter_remove_nat(ipnat_t ipn
)
164 memset(&obj
, 0, sizeof(obj
));
165 obj
.ipfo_rev
= IPFILTER_VERSION
;
166 obj
.ipfo_size
= sizeof(ipn
);
167 obj
.ipfo_type
= IPFOBJ_IPNAT
;
170 return ioctl(natfd
, SIOCRMNAT
, &obj
);
174 ipf_add_filter(u_int32_t id
, u_int8_t dir
, struct sockaddr
*src
,
175 struct sockaddr
*dst
, u_int16_t d_port
)
178 if (!src
|| !dst
|| !d_port
) {
189 ipf_add_nat(u_int32_t id
, struct sockaddr
*src
, struct sockaddr
*dst
,
190 u_int16_t d_port
, struct sockaddr
*snat
, u_int16_t nat_range_low
,
191 u_int16_t nat_range_high
)
200 ipf_add_rdr(u_int32_t id
, struct sockaddr
*src
, struct sockaddr
*dst
,
201 u_int16_t d_port
, struct sockaddr
*rdr
, u_int16_t rdr_port
)
203 struct ftp_proxy_entry
*fpe
= ftp_proxy_entry_find(id
);
211 if (!src
|| !dst
|| !d_port
|| !rdr
|| !rdr_port
||
212 (src
->sa_family
!= rdr
->sa_family
)) {
217 memset(&ipn
, 0, sizeof(ipn
));
218 ipn
.in_redir
= NAT_REDIRECT
;
220 ipn
.in_outip
= satosin(dst
)->sin_addr
.s_addr
;
221 ipn
.in_outmsk
= 0xffffffff;
222 strlcpy(ipn
.in_ifnames
[0], netif
, sizeof(ipn
.in_ifnames
[0]));
223 strlcpy(ipn
.in_ifnames
[1], netif
, sizeof(ipn
.in_ifnames
[1]));
224 ipn
.in_pmin
= htons(d_port
);
225 ipn
.in_pmax
= htons(d_port
);
226 ipn
.in_inip
= satosin(rdr
)->sin_addr
.s_addr
;
227 ipn
.in_inmsk
= 0xffffffff;
228 ipn
.in_pnext
= htons(rdr_port
);
229 ipn
.in_flags
= IPN_FIXEDDPORT
| IPN_TCP
;
230 strlcpy(ipn
.in_tag
.ipt_tag
, fpe
->proxy_tag
, sizeof(ipn
.in_tag
.ipt_tag
));
232 if (ipfilter_add_nat(ipn
) == -1)
235 if (ftp_proxy_entry_add_nat(fpe
, ipn
) == -1)
245 ipf_add_rdr(u_int32_t id
, struct sockaddr
*src
, struct sockaddr
*dst
,
246 u_int16_t d_port
, struct sockaddr
*rdr
, u_int16_t rdr_port
)
248 u_32_t sum1
, sum2
, sumd
;
254 if (!src
|| !dst
|| !d_port
|| !rdr
|| !rdr_port
||
255 (src
->sa_family
!= rdr
->sa_family
)) {
260 memset(&ns
, 0, sizeof(ns
));
263 nat
->nat_p
= IPPROTO_TCP
;
264 nat
->nat_dir
= NAT_OUTBOUND
;
265 nat
->nat_redir
= NAT_REDIRECT
;
266 strlcpy(nat
->nat_ifnames
[0], netif
, sizeof(nat
->nat_ifnames
[0]));
267 strlcpy(nat
->nat_ifnames
[1], netif
, sizeof(nat
->nat_ifnames
[1]));
269 nat
->nat_inip
= satosin(rdr
)->sin_addr
;
270 nat
->nat_outip
= satosin(dst
)->sin_addr
;
271 nat
->nat_oip
= satosin(src
)->sin_addr
;
273 sum1
= LONG_SUM(ntohl(nat
->nat_inip
.s_addr
)) + rdr_port
;
274 sum2
= LONG_SUM(ntohl(nat
->nat_outip
.s_addr
)) + d_port
;
275 CALC_SUMD(sum1
, sum2
, sumd
);
276 nat
->nat_sumd
[0] = (sumd
& 0xffff) + (sumd
>> 16);
277 nat
->nat_sumd
[1] = nat
->nat_sumd
[0];
279 sum1
= LONG_SUM(ntohl(nat
->nat_inip
.s_addr
));
280 sum2
= LONG_SUM(ntohl(nat
->nat_outip
.s_addr
));
281 CALC_SUMD(sum1
, sum2
, sumd
);
282 nat
->nat_ipsumd
= (sumd
& 0xffff) + (sumd
>> 16);
284 nat
->nat_inport
= htons(rdr_port
);
285 nat
->nat_outport
= htons(d_port
);
286 nat
->nat_oport
= satosin(src
)->sin_port
;
288 nat
->nat_flags
= IPN_TCPUDP
;
290 memset(&obj
, 0, sizeof(obj
));
291 obj
.ipfo_rev
= IPFILTER_VERSION
;
292 obj
.ipfo_size
= sizeof(ns
);
294 obj
.ipfo_type
= IPFOBJ_NATSAVE
;
298 if (ioctl(natfd
, SIOCSTLCK
, &onoff
) == -1)
300 if (ioctl(natfd
, SIOCSTPUT
, &obj
) == -1)
303 if (ioctl(natfd
, SIOCSTLCK
, &onoff
) == -1)
313 struct ftp_proxy_entry
*fpe
, *n
;
314 struct ftp_proxy_nat
*fpn
;
316 for (fpe
= LIST_FIRST(&ftp_proxy_entries
); fpe
!= NULL
; fpe
= n
) {
317 n
= LIST_NEXT(fpe
, link
);
320 * If status is nul, then the session is going to be ended.
321 * Remove all nat mappings that were added.
323 if (fpe
->status
== 0) {
324 while ((fpn
= LIST_FIRST(&fpe
->nat_entries
)) != NULL
) {
325 if (ipfilter_remove_nat(fpn
->ipn
) == -1)
328 LIST_REMOVE(fpn
, link
);
332 ftp_proxy_entry_remove(fpe
);
340 ipf_do_rollback(void)
349 ipf_init_filter(char *opt_qname
, char *opt_tagname
, int opt_verbose
)
351 natfd
= open(IPNAT_NAME
, O_RDWR
);
353 err(EXIT_FAILURE
, "cannot open " IPNAT_NAME
);
357 ipf_prepare_commit(u_int32_t id
)
359 struct ftp_proxy_entry
*fpe
;
361 fpe
= ftp_proxy_entry_find(id
);
363 fpe
= ftp_proxy_entry_create(id
);
373 ipf_server_lookup(struct sockaddr
*client
, struct sockaddr
*proxy
,
374 struct sockaddr
*server
)
379 /* IPv4-only for now. */
380 if (client
->sa_family
!= AF_INET
) {
381 errno
= EPROTONOSUPPORT
;
386 * Build up the ipf object description structure.
388 memset((void *)&obj
, 0, sizeof(obj
));
389 obj
.ipfo_rev
= IPFILTER_VERSION
;
390 obj
.ipfo_size
= sizeof(natlook
);
391 obj
.ipfo_ptr
= &natlook
;
392 obj
.ipfo_type
= IPFOBJ_NATLOOKUP
;
394 * Build up the ipf natlook structure.
396 memset((void *)&natlook
, 0, sizeof(natlook
));
397 natlook
.nl_flags
= IPN_TCPUDP
;
398 natlook
.nl_outip
= satosin(client
)->sin_addr
;
399 natlook
.nl_inip
= satosin(proxy
)->sin_addr
;
400 natlook
.nl_outport
= satosin(client
)->sin_port
;
401 natlook
.nl_inport
= satosin(proxy
)->sin_port
;
403 if (ioctl(natfd
, SIOCGNATL
, &obj
) == -1)
407 * Return the real destination address and port number in the sockaddr
410 memset((void *)server
, 0, sizeof(struct sockaddr_in
));
411 satosin(server
)->sin_len
= sizeof(struct sockaddr_in
);
412 satosin(server
)->sin_family
= AF_INET
;
413 satosin(server
)->sin_addr
= natlook
.nl_realip
;
414 satosin(server
)->sin_port
= natlook
.nl_realport
;