2 /* $OpenBSD: filter.c,v 1.6 2007/08/01 09:31:41 henning Exp $ */
5 * Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <sys/ioctl.h>
21 #include <sys/types.h>
22 #include <sys/socket.h>
25 #include <net/pfvar.h>
26 #include <netinet/in.h>
27 #include <netinet/tcp.h>
28 #include <arpa/inet.h>
39 #if defined(__NetBSD__) && defined(WITH_IPF)
41 #endif /* __NetBSD__ && WITH_IPF */
43 /* From netinet/in.h, but only _KERNEL_ gets them. */
44 #define satosin(sa) ((struct sockaddr_in *)(sa))
45 #define satosin6(sa) ((struct sockaddr_in6 *)(sa))
47 enum { TRANS_FILTER
= 0, TRANS_NAT
, TRANS_RDR
, TRANS_SIZE
};
49 int prepare_rule(u_int32_t
, int, struct sockaddr
*, struct sockaddr
*,
51 int server_lookup4(struct sockaddr_in
*, struct sockaddr_in
*,
52 struct sockaddr_in
*);
53 int server_lookup6(struct sockaddr_in6
*, struct sockaddr_in6
*,
54 struct sockaddr_in6
*);
56 static struct pfioc_pooladdr pfp
;
57 static struct pfioc_rule pfr
;
58 static struct pfioc_trans pft
;
59 static struct pfioc_trans_e pfte
[TRANS_SIZE
];
60 static int dev
, rule_log
;
61 static char *qname
, *tagname
;
64 add_filter(u_int32_t id
, u_int8_t dir
, struct sockaddr
*src
,
65 struct sockaddr
*dst
, u_int16_t d_port
)
67 #if defined(__NetBSD__) && defined(WITH_IPF)
69 return ipf_add_filter(id
, dir
, src
, dst
, d_port
);
70 #endif /* __NetBSD__ && WITH_IPF */
72 if (!src
|| !dst
|| !d_port
) {
77 if (prepare_rule(id
, PF_RULESET_FILTER
, src
, dst
, d_port
) == -1)
80 pfr
.rule
.direction
= dir
;
81 if (ioctl(dev
, DIOCADDRULE
, &pfr
) == -1)
88 add_nat(u_int32_t id
, struct sockaddr
*src
, struct sockaddr
*dst
,
89 u_int16_t d_port
, struct sockaddr
*nat
, u_int16_t nat_range_low
,
90 u_int16_t nat_range_high
)
92 #if defined(__NetBSD__) && defined(WITH_IPF)
94 return ipf_add_nat(id
, src
, dst
, d_port
, nat
, nat_range_low
,
96 #endif /* __NetBSD__ && WITH_IPF */
98 if (!src
|| !dst
|| !d_port
|| !nat
|| !nat_range_low
||
99 (src
->sa_family
!= nat
->sa_family
)) {
104 if (prepare_rule(id
, PF_RULESET_NAT
, src
, dst
, d_port
) == -1)
107 if (nat
->sa_family
== AF_INET
) {
108 memcpy(&pfp
.addr
.addr
.v
.a
.addr
.v4
,
109 &satosin(nat
)->sin_addr
.s_addr
, 4);
110 memset(&pfp
.addr
.addr
.v
.a
.mask
.addr8
, 255, 4);
112 memcpy(&pfp
.addr
.addr
.v
.a
.addr
.v6
,
113 &satosin6(nat
)->sin6_addr
.s6_addr
, 16);
114 memset(&pfp
.addr
.addr
.v
.a
.mask
.addr8
, 255, 16);
116 if (ioctl(dev
, DIOCADDADDR
, &pfp
) == -1)
119 pfr
.rule
.rpool
.proxy_port
[0] = nat_range_low
;
120 pfr
.rule
.rpool
.proxy_port
[1] = nat_range_high
;
121 if (ioctl(dev
, DIOCADDRULE
, &pfr
) == -1)
128 add_rdr(u_int32_t id
, struct sockaddr
*src
, struct sockaddr
*dst
,
129 u_int16_t d_port
, struct sockaddr
*rdr
, u_int16_t rdr_port
)
131 #if defined(__NetBSD__) && defined(WITH_IPF)
133 return ipf_add_rdr(id
, src
, dst
, d_port
, rdr
, rdr_port
);
134 #endif /* __NetBSD__ && WITH_IPF */
136 if (!src
|| !dst
|| !d_port
|| !rdr
|| !rdr_port
||
137 (src
->sa_family
!= rdr
->sa_family
)) {
142 if (prepare_rule(id
, PF_RULESET_RDR
, src
, dst
, d_port
) == -1)
145 if (rdr
->sa_family
== AF_INET
) {
146 memcpy(&pfp
.addr
.addr
.v
.a
.addr
.v4
,
147 &satosin(rdr
)->sin_addr
.s_addr
, 4);
148 memset(&pfp
.addr
.addr
.v
.a
.mask
.addr8
, 255, 4);
150 memcpy(&pfp
.addr
.addr
.v
.a
.addr
.v6
,
151 &satosin6(rdr
)->sin6_addr
.s6_addr
, 16);
152 memset(&pfp
.addr
.addr
.v
.a
.mask
.addr8
, 255, 16);
154 if (ioctl(dev
, DIOCADDADDR
, &pfp
) == -1)
157 pfr
.rule
.rpool
.proxy_port
[0] = rdr_port
;
158 if (ioctl(dev
, DIOCADDRULE
, &pfr
) == -1)
167 #if defined(__NetBSD__) && defined(WITH_IPF)
169 return ipf_do_commit();
170 #endif /* __NetBSD__ && WITH_IPF */
172 if (ioctl(dev
, DIOCXCOMMIT
, &pft
) == -1)
181 #if defined(__NetBSD__) && defined(WITH_IPF)
183 return ipf_do_rollback();
184 #endif /* __NetBSD__ && WITH_IPF */
186 if (ioctl(dev
, DIOCXROLLBACK
, &pft
) == -1)
193 init_filter(char *opt_qname
, char *opt_tagname
, int opt_verbose
)
195 struct pf_status status
;
197 #if defined(__NetBSD__) && defined(WITH_IPF)
199 ipf_init_filter(opt_qname
, opt_tagname
, opt_verbose
);
202 #endif /* __NetBSD__ && WITH_IPF */
205 tagname
= opt_tagname
;
207 if (opt_verbose
== 1)
209 else if (opt_verbose
== 2)
210 rule_log
= PF_LOG_ALL
;
212 dev
= open("/dev/pf", O_RDWR
);
215 if (ioctl(dev
, DIOCGETSTATUS
, &status
) == -1)
216 err(1, "DIOCGETSTATUS");
218 errx(1, "pf is disabled");
222 prepare_commit(u_int32_t id
)
224 char an
[PF_ANCHOR_NAME_SIZE
];
227 #if defined(__NetBSD__) && defined(WITH_IPF)
229 return ipf_prepare_commit(id
);
230 #endif /* __NetBSD__ && WITH_IPF */
232 memset(&pft
, 0, sizeof pft
);
233 pft
.size
= TRANS_SIZE
;
234 pft
.esize
= sizeof pfte
[0];
237 snprintf(an
, PF_ANCHOR_NAME_SIZE
, "%s/%d.%d", FTP_PROXY_ANCHOR
,
239 for (i
= 0; i
< TRANS_SIZE
; i
++) {
240 memset(&pfte
[i
], 0, sizeof pfte
[0]);
241 strlcpy(pfte
[i
].anchor
, an
, PF_ANCHOR_NAME_SIZE
);
244 pfte
[i
].rs_num
= PF_RULESET_FILTER
;
247 pfte
[i
].rs_num
= PF_RULESET_NAT
;
250 pfte
[i
].rs_num
= PF_RULESET_RDR
;
258 if (ioctl(dev
, DIOCXBEGIN
, &pft
) == -1)
265 prepare_rule(u_int32_t id
, int rs_num
, struct sockaddr
*src
,
266 struct sockaddr
*dst
, u_int16_t d_port
)
268 char an
[PF_ANCHOR_NAME_SIZE
];
270 if ((src
->sa_family
!= AF_INET
&& src
->sa_family
!= AF_INET6
) ||
271 (src
->sa_family
!= dst
->sa_family
)) {
272 errno
= EPROTONOSUPPORT
;
276 memset(&pfp
, 0, sizeof pfp
);
277 memset(&pfr
, 0, sizeof pfr
);
278 snprintf(an
, PF_ANCHOR_NAME_SIZE
, "%s/%d.%d", FTP_PROXY_ANCHOR
,
280 strlcpy(pfp
.anchor
, an
, PF_ANCHOR_NAME_SIZE
);
281 strlcpy(pfr
.anchor
, an
, PF_ANCHOR_NAME_SIZE
);
284 case PF_RULESET_FILTER
:
285 pfr
.ticket
= pfte
[TRANS_FILTER
].ticket
;
288 pfr
.ticket
= pfte
[TRANS_NAT
].ticket
;
291 pfr
.ticket
= pfte
[TRANS_RDR
].ticket
;
297 if (ioctl(dev
, DIOCBEGINADDRS
, &pfp
) == -1)
299 pfr
.pool_ticket
= pfp
.ticket
;
301 /* Generic for all rule types. */
302 pfr
.rule
.af
= src
->sa_family
;
303 pfr
.rule
.proto
= IPPROTO_TCP
;
304 pfr
.rule
.src
.addr
.type
= PF_ADDR_ADDRMASK
;
305 pfr
.rule
.dst
.addr
.type
= PF_ADDR_ADDRMASK
;
306 if (src
->sa_family
== AF_INET
) {
307 memcpy(&pfr
.rule
.src
.addr
.v
.a
.addr
.v4
,
308 &satosin(src
)->sin_addr
.s_addr
, 4);
309 memset(&pfr
.rule
.src
.addr
.v
.a
.mask
.addr8
, 255, 4);
310 memcpy(&pfr
.rule
.dst
.addr
.v
.a
.addr
.v4
,
311 &satosin(dst
)->sin_addr
.s_addr
, 4);
312 memset(&pfr
.rule
.dst
.addr
.v
.a
.mask
.addr8
, 255, 4);
314 memcpy(&pfr
.rule
.src
.addr
.v
.a
.addr
.v6
,
315 &satosin6(src
)->sin6_addr
.s6_addr
, 16);
316 memset(&pfr
.rule
.src
.addr
.v
.a
.mask
.addr8
, 255, 16);
317 memcpy(&pfr
.rule
.dst
.addr
.v
.a
.addr
.v6
,
318 &satosin6(dst
)->sin6_addr
.s6_addr
, 16);
319 memset(&pfr
.rule
.dst
.addr
.v
.a
.mask
.addr8
, 255, 16);
321 pfr
.rule
.dst
.port_op
= PF_OP_EQ
;
322 pfr
.rule
.dst
.port
[0] = htons(d_port
);
324 strlcpy(pfr
.rule
.tagname
, tagname
, sizeof pfr
.rule
.tagname
);
327 case PF_RULESET_FILTER
:
329 * pass quick [log] inet[6] proto tcp \
330 * from $src to $dst port = $d_port flags S/SA keep state
331 * (max 1) [queue qname]
333 pfr
.rule
.action
= PF_PASS
;
335 pfr
.rule
.log
= rule_log
;
336 pfr
.rule
.keep_state
= 1;
337 pfr
.rule
.flags
= TH_SYN
;
338 pfr
.rule
.flagset
= (TH_SYN
|TH_ACK
);
339 pfr
.rule
.max_states
= 1;
341 strlcpy(pfr
.rule
.qname
, qname
, sizeof pfr
.rule
.qname
);
345 * nat inet[6] proto tcp from $src to $dst port $d_port -> $nat
347 pfr
.rule
.action
= PF_NAT
;
351 * rdr inet[6] proto tcp from $src to $dst port $d_port -> $rdr
353 pfr
.rule
.action
= PF_RDR
;
364 server_lookup(struct sockaddr
*client
, struct sockaddr
*proxy
,
365 struct sockaddr
*server
)
367 #if defined(__NetBSD__) && defined(WITH_IPF)
369 return ipf_server_lookup(client
, proxy
, server
);
370 #endif /* __NetBSD__ && WITH_IPF */
372 if (client
->sa_family
== AF_INET
)
373 return (server_lookup4(satosin(client
), satosin(proxy
),
376 if (client
->sa_family
== AF_INET6
)
377 return (server_lookup6(satosin6(client
), satosin6(proxy
),
380 errno
= EPROTONOSUPPORT
;
385 server_lookup4(struct sockaddr_in
*client
, struct sockaddr_in
*proxy
,
386 struct sockaddr_in
*server
)
388 struct pfioc_natlook pnl
;
390 memset(&pnl
, 0, sizeof pnl
);
391 pnl
.direction
= PF_OUT
;
393 pnl
.proto
= IPPROTO_TCP
;
394 memcpy(&pnl
.saddr
.v4
, &client
->sin_addr
.s_addr
, sizeof pnl
.saddr
.v4
);
395 memcpy(&pnl
.daddr
.v4
, &proxy
->sin_addr
.s_addr
, sizeof pnl
.daddr
.v4
);
396 pnl
.sport
= client
->sin_port
;
397 pnl
.dport
= proxy
->sin_port
;
399 if (ioctl(dev
, DIOCNATLOOK
, &pnl
) == -1)
402 memset(server
, 0, sizeof(struct sockaddr_in
));
403 server
->sin_len
= sizeof(struct sockaddr_in
);
404 server
->sin_family
= AF_INET
;
405 memcpy(&server
->sin_addr
.s_addr
, &pnl
.rdaddr
.v4
,
406 sizeof server
->sin_addr
.s_addr
);
407 server
->sin_port
= pnl
.rdport
;
413 server_lookup6(struct sockaddr_in6
*client
, struct sockaddr_in6
*proxy
,
414 struct sockaddr_in6
*server
)
416 struct pfioc_natlook pnl
;
418 memset(&pnl
, 0, sizeof pnl
);
419 pnl
.direction
= PF_OUT
;
421 pnl
.proto
= IPPROTO_TCP
;
422 memcpy(&pnl
.saddr
.v6
, &client
->sin6_addr
.s6_addr
, sizeof pnl
.saddr
.v6
);
423 memcpy(&pnl
.daddr
.v6
, &proxy
->sin6_addr
.s6_addr
, sizeof pnl
.daddr
.v6
);
424 pnl
.sport
= client
->sin6_port
;
425 pnl
.dport
= proxy
->sin6_port
;
427 if (ioctl(dev
, DIOCNATLOOK
, &pnl
) == -1)
430 memset(server
, 0, sizeof(struct sockaddr_in6
));
431 server
->sin6_len
= sizeof(struct sockaddr_in6
);
432 server
->sin6_family
= AF_INET6
;
433 memcpy(&server
->sin6_addr
.s6_addr
, &pnl
.rdaddr
.v6
,
434 sizeof server
->sin6_addr
);
435 server
->sin6_port
= pnl
.rdport
;