2 /* $OpenBSD: filter.c,v 1.2 2007/06/23 15:51:21 jcs 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.
22 #include <sys/ioctl.h>
23 #include <sys/types.h>
24 #include <sys/socket.h>
27 #include <net/pfvar.h>
28 #include <netinet/in.h>
29 #include <netinet/tcp.h>
30 #include <arpa/inet.h>
42 /* From netinet/in.h, but only _KERNEL_ gets them. */
43 #define satosin(sa) ((struct sockaddr_in *)(sa))
44 #define satosin6(sa) ((struct sockaddr_in6 *)(sa))
46 enum { TRANS_FILTER
= 0, TRANS_NAT
, TRANS_RDR
, TRANS_SIZE
};
48 int prepare_rule(u_int32_t
, int, struct sockaddr
*, struct sockaddr
*,
50 int server_lookup4(struct sockaddr_in
*, struct sockaddr_in
*,
51 struct sockaddr_in
*, u_int8_t
);
52 int server_lookup6(struct sockaddr_in6
*, struct sockaddr_in6
*,
53 struct sockaddr_in6
*, u_int8_t
);
55 static struct pfioc_pooladdr pfp
;
56 static struct pfioc_rule pfr
;
57 static struct pfioc_trans pft
;
58 static struct pfioc_trans_e pfte
[TRANS_SIZE
];
59 static int dev
, rule_log
;
63 add_filter(u_int32_t id
, u_int8_t dir
, struct sockaddr
*src
,
64 struct sockaddr
*dst
, u_int16_t d_port
, u_int8_t proto
)
66 if (!src
|| !dst
|| !d_port
|| !proto
) {
71 if (prepare_rule(id
, PF_RULESET_FILTER
, src
, dst
, d_port
, proto
) == -1)
74 pfr
.rule
.direction
= dir
;
75 if (ioctl(dev
, DIOCADDRULE
, &pfr
) == -1)
82 add_nat(u_int32_t id
, struct sockaddr
*src
, struct sockaddr
*dst
,
83 u_int16_t d_port
, struct sockaddr
*nat
, u_int16_t nat_range_low
,
84 u_int16_t nat_range_high
, u_int8_t proto
)
86 if (!src
|| !dst
|| !d_port
|| !nat
|| !nat_range_low
|| !proto
||
87 (src
->sa_family
!= nat
->sa_family
)) {
92 if (prepare_rule(id
, PF_RULESET_NAT
, src
, dst
, d_port
, proto
) == -1)
95 if (nat
->sa_family
== AF_INET
) {
96 memcpy(&pfp
.addr
.addr
.v
.a
.addr
.v4
,
97 &satosin(nat
)->sin_addr
.s_addr
, 4);
98 memset(&pfp
.addr
.addr
.v
.a
.mask
.addr8
, 255, 4);
100 memcpy(&pfp
.addr
.addr
.v
.a
.addr
.v6
,
101 &satosin6(nat
)->sin6_addr
.s6_addr
, 16);
102 memset(&pfp
.addr
.addr
.v
.a
.mask
.addr8
, 255, 16);
104 if (ioctl(dev
, DIOCADDADDR
, &pfp
) == -1)
107 pfr
.rule
.rpool
.proxy_port
[0] = nat_range_low
;
108 pfr
.rule
.rpool
.proxy_port
[1] = nat_range_high
;
109 if (ioctl(dev
, DIOCADDRULE
, &pfr
) == -1)
116 add_rdr(u_int32_t id
, struct sockaddr
*src
, struct sockaddr
*dst
,
117 u_int16_t d_port
, struct sockaddr
*rdr
, u_int16_t rdr_port
, u_int8_t proto
)
119 if (!src
|| !dst
|| !d_port
|| !rdr
|| !rdr_port
|| !proto
||
120 (src
->sa_family
!= rdr
->sa_family
)) {
125 if (prepare_rule(id
, PF_RULESET_RDR
, src
, dst
, d_port
, proto
) == -1)
128 if (rdr
->sa_family
== AF_INET
) {
129 memcpy(&pfp
.addr
.addr
.v
.a
.addr
.v4
,
130 &satosin(rdr
)->sin_addr
.s_addr
, 4);
131 memset(&pfp
.addr
.addr
.v
.a
.mask
.addr8
, 255, 4);
133 memcpy(&pfp
.addr
.addr
.v
.a
.addr
.v6
,
134 &satosin6(rdr
)->sin6_addr
.s6_addr
, 16);
135 memset(&pfp
.addr
.addr
.v
.a
.mask
.addr8
, 255, 16);
137 if (ioctl(dev
, DIOCADDADDR
, &pfp
) == -1)
140 pfr
.rule
.rpool
.proxy_port
[0] = rdr_port
;
141 if (ioctl(dev
, DIOCADDRULE
, &pfr
) == -1)
150 if (ioctl(dev
, DIOCXCOMMIT
, &pft
) == -1)
159 if (ioctl(dev
, DIOCXROLLBACK
, &pft
) == -1)
166 init_filter(char *opt_qname
, int opt_verbose
)
168 struct pf_status status
;
172 if (opt_verbose
== 1)
174 else if (opt_verbose
== 2)
175 rule_log
= PF_LOG_ALL
;
177 dev
= open("/dev/pf", O_RDWR
);
179 syslog(LOG_ERR
, "can't open /dev/pf");
182 if (ioctl(dev
, DIOCGETSTATUS
, &status
) == -1) {
183 syslog(LOG_ERR
, "DIOCGETSTATUS");
186 if (!status
.running
) {
187 syslog(LOG_ERR
, "pf is disabled");
193 prepare_commit(u_int32_t id
)
195 char an
[PF_ANCHOR_NAME_SIZE
];
198 memset(&pft
, 0, sizeof pft
);
199 pft
.size
= TRANS_SIZE
;
200 pft
.esize
= sizeof pfte
[0];
203 snprintf(an
, PF_ANCHOR_NAME_SIZE
, "%s/%d.%d", FTP_PROXY_ANCHOR
,
205 for (i
= 0; i
< TRANS_SIZE
; i
++) {
206 memset(&pfte
[i
], 0, sizeof pfte
[0]);
207 strlcpy(pfte
[i
].anchor
, an
, PF_ANCHOR_NAME_SIZE
);
210 pfte
[i
].rs_num
= PF_RULESET_FILTER
;
213 pfte
[i
].rs_num
= PF_RULESET_NAT
;
216 pfte
[i
].rs_num
= PF_RULESET_RDR
;
224 if (ioctl(dev
, DIOCXBEGIN
, &pft
) == -1)
231 prepare_rule(u_int32_t id
, int rs_num
, struct sockaddr
*src
,
232 struct sockaddr
*dst
, u_int16_t d_port
, u_int8_t proto
)
234 char an
[PF_ANCHOR_NAME_SIZE
];
236 if ((src
->sa_family
!= AF_INET
&& src
->sa_family
!= AF_INET6
) ||
237 (src
->sa_family
!= dst
->sa_family
)) {
238 errno
= EPROTONOSUPPORT
;
242 memset(&pfp
, 0, sizeof pfp
);
243 memset(&pfr
, 0, sizeof pfr
);
244 snprintf(an
, PF_ANCHOR_NAME_SIZE
, "%s/%d.%d", FTP_PROXY_ANCHOR
,
246 strlcpy(pfp
.anchor
, an
, PF_ANCHOR_NAME_SIZE
);
247 strlcpy(pfr
.anchor
, an
, PF_ANCHOR_NAME_SIZE
);
250 case PF_RULESET_FILTER
:
251 pfr
.ticket
= pfte
[TRANS_FILTER
].ticket
;
254 pfr
.ticket
= pfte
[TRANS_NAT
].ticket
;
257 pfr
.ticket
= pfte
[TRANS_RDR
].ticket
;
263 if (ioctl(dev
, DIOCBEGINADDRS
, &pfp
) == -1)
265 pfr
.pool_ticket
= pfp
.ticket
;
267 /* Generic for all rule types. */
268 pfr
.rule
.af
= src
->sa_family
;
269 pfr
.rule
.proto
= proto
;
270 pfr
.rule
.src
.addr
.type
= PF_ADDR_ADDRMASK
;
271 pfr
.rule
.dst
.addr
.type
= PF_ADDR_ADDRMASK
;
272 if (src
->sa_family
== AF_INET
) {
273 memcpy(&pfr
.rule
.src
.addr
.v
.a
.addr
.v4
,
274 &satosin(src
)->sin_addr
.s_addr
, 4);
275 memset(&pfr
.rule
.src
.addr
.v
.a
.mask
.addr8
, 255, 4);
276 memcpy(&pfr
.rule
.dst
.addr
.v
.a
.addr
.v4
,
277 &satosin(dst
)->sin_addr
.s_addr
, 4);
278 memset(&pfr
.rule
.dst
.addr
.v
.a
.mask
.addr8
, 255, 4);
280 memcpy(&pfr
.rule
.src
.addr
.v
.a
.addr
.v6
,
281 &satosin6(src
)->sin6_addr
.s6_addr
, 16);
282 memset(&pfr
.rule
.src
.addr
.v
.a
.mask
.addr8
, 255, 16);
283 memcpy(&pfr
.rule
.dst
.addr
.v
.a
.addr
.v6
,
284 &satosin6(dst
)->sin6_addr
.s6_addr
, 16);
285 memset(&pfr
.rule
.dst
.addr
.v
.a
.mask
.addr8
, 255, 16);
287 pfr
.rule
.dst
.port_op
= PF_OP_EQ
;
288 pfr
.rule
.dst
.port
[0] = htons(d_port
);
291 case PF_RULESET_FILTER
:
293 * pass quick [log] inet[6] proto tcp \
294 * from $src to $dst port = $d_port flags S/SAFR keep state
295 * (max 1) [queue qname]
297 pfr
.rule
.action
= PF_PASS
;
299 pfr
.rule
.log
= rule_log
;
300 pfr
.rule
.keep_state
= 1;
301 pfr
.rule
.flags
= (proto
== IPPROTO_TCP
? TH_SYN
: 0);
302 pfr
.rule
.flagset
= (proto
== IPPROTO_TCP
?
303 (TH_SYN
|TH_ACK
|TH_FIN
|TH_RST
) : 0);
304 pfr
.rule
.max_states
= 1;
306 strlcpy(pfr
.rule
.qname
, qname
, sizeof pfr
.rule
.qname
);
310 * nat inet[6] proto tcp from $src to $dst port $d_port -> $nat
312 pfr
.rule
.action
= PF_NAT
;
316 * rdr inet[6] proto tcp from $src to $dst port $d_port -> $rdr
318 pfr
.rule
.action
= PF_RDR
;
329 server_lookup(struct sockaddr
*client
, struct sockaddr
*proxy
,
330 struct sockaddr
*server
, u_int8_t proto
)
332 if (client
->sa_family
== AF_INET
)
333 return (server_lookup4(satosin(client
), satosin(proxy
),
334 satosin(server
), proto
));
336 if (client
->sa_family
== AF_INET6
)
337 return (server_lookup6(satosin6(client
), satosin6(proxy
),
338 satosin6(server
), proto
));
340 errno
= EPROTONOSUPPORT
;
345 server_lookup4(struct sockaddr_in
*client
, struct sockaddr_in
*proxy
,
346 struct sockaddr_in
*server
, u_int8_t proto
)
348 struct pfioc_natlook pnl
;
350 memset(&pnl
, 0, sizeof pnl
);
351 pnl
.direction
= PF_OUT
;
354 memcpy(&pnl
.saddr
.v4
, &client
->sin_addr
.s_addr
, sizeof pnl
.saddr
.v4
);
355 memcpy(&pnl
.daddr
.v4
, &proxy
->sin_addr
.s_addr
, sizeof pnl
.daddr
.v4
);
356 pnl
.sport
= client
->sin_port
;
357 pnl
.dport
= proxy
->sin_port
;
359 if (ioctl(dev
, DIOCNATLOOK
, &pnl
) == -1)
362 memset(server
, 0, sizeof(struct sockaddr_in
));
363 server
->sin_len
= sizeof(struct sockaddr_in
);
364 server
->sin_family
= AF_INET
;
365 memcpy(&server
->sin_addr
.s_addr
, &pnl
.rdaddr
.v4
,
366 sizeof server
->sin_addr
.s_addr
);
367 server
->sin_port
= pnl
.rdport
;
373 server_lookup6(struct sockaddr_in6
*client
, struct sockaddr_in6
*proxy
,
374 struct sockaddr_in6
*server
, u_int8_t proto
)
376 struct pfioc_natlook pnl
;
378 memset(&pnl
, 0, sizeof pnl
);
379 pnl
.direction
= PF_OUT
;
382 memcpy(&pnl
.saddr
.v6
, &client
->sin6_addr
.s6_addr
, sizeof pnl
.saddr
.v6
);
383 memcpy(&pnl
.daddr
.v6
, &proxy
->sin6_addr
.s6_addr
, sizeof pnl
.daddr
.v6
);
384 pnl
.sport
= client
->sin6_port
;
385 pnl
.dport
= proxy
->sin6_port
;
387 if (ioctl(dev
, DIOCNATLOOK
, &pnl
) == -1)
390 memset(server
, 0, sizeof(struct sockaddr_in6
));
391 server
->sin6_len
= sizeof(struct sockaddr_in6
);
392 server
->sin6_family
= AF_INET6
;
393 memcpy(&server
->sin6_addr
.s6_addr
, &pnl
.rdaddr
.v6
,
394 sizeof server
->sin6_addr
);
395 server
->sin6_port
= pnl
.rdport
;