1 /* $OpenBSD: filter.c,v 1.6 2007/08/01 09:31:41 henning Exp $ */
4 * Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/ioctl.h>
20 #include <sys/types.h>
21 #include <sys/socket.h>
24 #include <net/pf/pfvar.h>
25 #include <netinet/in.h>
26 #include <netinet/tcp.h>
27 #include <arpa/inet.h>
38 /* From netinet/in.h, but only _KERNEL_ gets them. */
39 #define satosin(sa) ((struct sockaddr_in *)(sa))
40 #define satosin6(sa) ((struct sockaddr_in6 *)(sa))
42 enum { TRANS_FILTER
= 0, TRANS_NAT
, TRANS_RDR
, TRANS_SIZE
};
44 int prepare_rule(u_int32_t
, int, struct sockaddr
*, struct sockaddr
*,
46 int server_lookup4(struct sockaddr_in
*, struct sockaddr_in
*,
47 struct sockaddr_in
*);
48 int server_lookup6(struct sockaddr_in6
*, struct sockaddr_in6
*,
49 struct sockaddr_in6
*);
51 static struct pfioc_pooladdr pfp
;
52 static struct pfioc_rule pfr
;
53 static struct pfioc_trans pft
;
54 static struct pfioc_trans_e pfte
[TRANS_SIZE
];
55 static int dev
, rule_log
;
56 static char *qname
, *tagname
;
59 add_filter(u_int32_t id
, u_int8_t dir
, struct sockaddr
*src
,
60 struct sockaddr
*dst
, u_int16_t d_port
)
62 if (!src
|| !dst
|| !d_port
) {
67 if (prepare_rule(id
, PF_RULESET_FILTER
, src
, dst
, d_port
) == -1)
70 pfr
.rule
.direction
= dir
;
71 if (ioctl(dev
, DIOCADDRULE
, &pfr
) == -1)
78 add_nat(u_int32_t id
, struct sockaddr
*src
, struct sockaddr
*dst
,
79 u_int16_t d_port
, struct sockaddr
*nat
, u_int16_t nat_range_low
,
80 u_int16_t nat_range_high
)
82 if (!src
|| !dst
|| !d_port
|| !nat
|| !nat_range_low
||
83 (src
->sa_family
!= nat
->sa_family
)) {
88 if (prepare_rule(id
, PF_RULESET_NAT
, src
, dst
, d_port
) == -1)
91 if (nat
->sa_family
== AF_INET
) {
92 memcpy(&pfp
.addr
.addr
.v
.a
.addr
.v4
,
93 &satosin(nat
)->sin_addr
.s_addr
, 4);
94 memset(&pfp
.addr
.addr
.v
.a
.mask
.addr8
, 255, 4);
96 memcpy(&pfp
.addr
.addr
.v
.a
.addr
.v6
,
97 &satosin6(nat
)->sin6_addr
.s6_addr
, 16);
98 memset(&pfp
.addr
.addr
.v
.a
.mask
.addr8
, 255, 16);
100 if (ioctl(dev
, DIOCADDADDR
, &pfp
) == -1)
103 pfr
.rule
.rpool
.proxy_port
[0] = nat_range_low
;
104 pfr
.rule
.rpool
.proxy_port
[1] = nat_range_high
;
105 if (ioctl(dev
, DIOCADDRULE
, &pfr
) == -1)
112 add_rdr(u_int32_t id
, struct sockaddr
*src
, struct sockaddr
*dst
,
113 u_int16_t d_port
, struct sockaddr
*rdr
, u_int16_t rdr_port
)
115 if (!src
|| !dst
|| !d_port
|| !rdr
|| !rdr_port
||
116 (src
->sa_family
!= rdr
->sa_family
)) {
121 if (prepare_rule(id
, PF_RULESET_RDR
, src
, dst
, d_port
) == -1)
124 if (rdr
->sa_family
== AF_INET
) {
125 memcpy(&pfp
.addr
.addr
.v
.a
.addr
.v4
,
126 &satosin(rdr
)->sin_addr
.s_addr
, 4);
127 memset(&pfp
.addr
.addr
.v
.a
.mask
.addr8
, 255, 4);
129 memcpy(&pfp
.addr
.addr
.v
.a
.addr
.v6
,
130 &satosin6(rdr
)->sin6_addr
.s6_addr
, 16);
131 memset(&pfp
.addr
.addr
.v
.a
.mask
.addr8
, 255, 16);
133 if (ioctl(dev
, DIOCADDADDR
, &pfp
) == -1)
136 pfr
.rule
.rpool
.proxy_port
[0] = rdr_port
;
137 if (ioctl(dev
, DIOCADDRULE
, &pfr
) == -1)
146 if (ioctl(dev
, DIOCXCOMMIT
, &pft
) == -1)
155 if (ioctl(dev
, DIOCXROLLBACK
, &pft
) == -1)
162 init_filter(char *opt_qname
, char *opt_tagname
, int opt_verbose
)
164 struct pf_status status
;
167 tagname
= opt_tagname
;
169 if (opt_verbose
== 1)
171 else if (opt_verbose
== 2)
172 rule_log
= PF_LOG_ALL
;
174 dev
= open("/dev/pf", O_RDWR
);
177 if (ioctl(dev
, DIOCGETSTATUS
, &status
) == -1)
178 err(1, "DIOCGETSTATUS");
180 errx(1, "pf is disabled");
184 prepare_commit(u_int32_t id
)
186 char an
[PF_ANCHOR_NAME_SIZE
];
189 memset(&pft
, 0, sizeof pft
);
190 pft
.size
= TRANS_SIZE
;
191 pft
.esize
= sizeof pfte
[0];
194 snprintf(an
, PF_ANCHOR_NAME_SIZE
, "%s/%d.%d", FTP_PROXY_ANCHOR
,
196 for (i
= 0; i
< TRANS_SIZE
; i
++) {
197 memset(&pfte
[i
], 0, sizeof pfte
[0]);
198 strlcpy(pfte
[i
].anchor
, an
, PF_ANCHOR_NAME_SIZE
);
201 pfte
[i
].rs_num
= PF_RULESET_FILTER
;
204 pfte
[i
].rs_num
= PF_RULESET_NAT
;
207 pfte
[i
].rs_num
= PF_RULESET_RDR
;
215 if (ioctl(dev
, DIOCXBEGIN
, &pft
) == -1)
222 prepare_rule(u_int32_t id
, int rs_num
, struct sockaddr
*src
,
223 struct sockaddr
*dst
, u_int16_t d_port
)
225 char an
[PF_ANCHOR_NAME_SIZE
];
227 if ((src
->sa_family
!= AF_INET
&& src
->sa_family
!= AF_INET6
) ||
228 (src
->sa_family
!= dst
->sa_family
)) {
229 errno
= EPROTONOSUPPORT
;
233 memset(&pfp
, 0, sizeof pfp
);
234 memset(&pfr
, 0, sizeof pfr
);
235 snprintf(an
, PF_ANCHOR_NAME_SIZE
, "%s/%d.%d", FTP_PROXY_ANCHOR
,
237 strlcpy(pfp
.anchor
, an
, PF_ANCHOR_NAME_SIZE
);
238 strlcpy(pfr
.anchor
, an
, PF_ANCHOR_NAME_SIZE
);
241 case PF_RULESET_FILTER
:
242 pfr
.ticket
= pfte
[TRANS_FILTER
].ticket
;
245 pfr
.ticket
= pfte
[TRANS_NAT
].ticket
;
248 pfr
.ticket
= pfte
[TRANS_RDR
].ticket
;
254 if (ioctl(dev
, DIOCBEGINADDRS
, &pfp
) == -1)
256 pfr
.pool_ticket
= pfp
.ticket
;
258 /* Generic for all rule types. */
259 pfr
.rule
.af
= src
->sa_family
;
260 pfr
.rule
.proto
= IPPROTO_TCP
;
261 pfr
.rule
.src
.addr
.type
= PF_ADDR_ADDRMASK
;
262 pfr
.rule
.dst
.addr
.type
= PF_ADDR_ADDRMASK
;
263 if (src
->sa_family
== AF_INET
) {
264 memcpy(&pfr
.rule
.src
.addr
.v
.a
.addr
.v4
,
265 &satosin(src
)->sin_addr
.s_addr
, 4);
266 memset(&pfr
.rule
.src
.addr
.v
.a
.mask
.addr8
, 255, 4);
267 memcpy(&pfr
.rule
.dst
.addr
.v
.a
.addr
.v4
,
268 &satosin(dst
)->sin_addr
.s_addr
, 4);
269 memset(&pfr
.rule
.dst
.addr
.v
.a
.mask
.addr8
, 255, 4);
271 memcpy(&pfr
.rule
.src
.addr
.v
.a
.addr
.v6
,
272 &satosin6(src
)->sin6_addr
.s6_addr
, 16);
273 memset(&pfr
.rule
.src
.addr
.v
.a
.mask
.addr8
, 255, 16);
274 memcpy(&pfr
.rule
.dst
.addr
.v
.a
.addr
.v6
,
275 &satosin6(dst
)->sin6_addr
.s6_addr
, 16);
276 memset(&pfr
.rule
.dst
.addr
.v
.a
.mask
.addr8
, 255, 16);
278 pfr
.rule
.dst
.port_op
= PF_OP_EQ
;
279 pfr
.rule
.dst
.port
[0] = htons(d_port
);
281 strlcpy(pfr
.rule
.tagname
, tagname
, sizeof pfr
.rule
.tagname
);
284 case PF_RULESET_FILTER
:
286 * pass quick [log] inet[6] proto tcp \
287 * from $src to $dst port = $d_port flags S/SA keep state
288 * (max 1) [queue qname]
290 pfr
.rule
.action
= PF_PASS
;
292 pfr
.rule
.log
= rule_log
;
293 pfr
.rule
.keep_state
= 1;
294 pfr
.rule
.flags
= TH_SYN
;
295 pfr
.rule
.flagset
= (TH_SYN
|TH_ACK
);
296 pfr
.rule
.max_states
= 1;
298 strlcpy(pfr
.rule
.qname
, qname
, sizeof pfr
.rule
.qname
);
302 * nat inet[6] proto tcp from $src to $dst port $d_port -> $nat
304 pfr
.rule
.action
= PF_NAT
;
308 * rdr inet[6] proto tcp from $src to $dst port $d_port -> $rdr
310 pfr
.rule
.action
= PF_RDR
;
321 server_lookup(struct sockaddr
*client
, struct sockaddr
*proxy
,
322 struct sockaddr
*server
)
324 if (client
->sa_family
== AF_INET
)
325 return (server_lookup4(satosin(client
), satosin(proxy
),
328 if (client
->sa_family
== AF_INET6
)
329 return (server_lookup6(satosin6(client
), satosin6(proxy
),
332 errno
= EPROTONOSUPPORT
;
337 server_lookup4(struct sockaddr_in
*client
, struct sockaddr_in
*proxy
,
338 struct sockaddr_in
*server
)
340 struct pfioc_natlook pnl
;
342 memset(&pnl
, 0, sizeof pnl
);
343 pnl
.direction
= PF_OUT
;
345 pnl
.proto
= IPPROTO_TCP
;
346 memcpy(&pnl
.saddr
.v4
, &client
->sin_addr
.s_addr
, sizeof pnl
.saddr
.v4
);
347 memcpy(&pnl
.daddr
.v4
, &proxy
->sin_addr
.s_addr
, sizeof pnl
.daddr
.v4
);
348 pnl
.sport
= client
->sin_port
;
349 pnl
.dport
= proxy
->sin_port
;
351 if (ioctl(dev
, DIOCNATLOOK
, &pnl
) == -1)
354 memset(server
, 0, sizeof(struct sockaddr_in
));
355 server
->sin_len
= sizeof(struct sockaddr_in
);
356 server
->sin_family
= AF_INET
;
357 memcpy(&server
->sin_addr
.s_addr
, &pnl
.rdaddr
.v4
,
358 sizeof server
->sin_addr
.s_addr
);
359 server
->sin_port
= pnl
.rdport
;
365 server_lookup6(struct sockaddr_in6
*client
, struct sockaddr_in6
*proxy
,
366 struct sockaddr_in6
*server
)
368 struct pfioc_natlook pnl
;
370 memset(&pnl
, 0, sizeof pnl
);
371 pnl
.direction
= PF_OUT
;
373 pnl
.proto
= IPPROTO_TCP
;
374 memcpy(&pnl
.saddr
.v6
, &client
->sin6_addr
.s6_addr
, sizeof pnl
.saddr
.v6
);
375 memcpy(&pnl
.daddr
.v6
, &proxy
->sin6_addr
.s6_addr
, sizeof pnl
.daddr
.v6
);
376 pnl
.sport
= client
->sin6_port
;
377 pnl
.dport
= proxy
->sin6_port
;
379 if (ioctl(dev
, DIOCNATLOOK
, &pnl
) == -1)
382 memset(server
, 0, sizeof(struct sockaddr_in6
));
383 server
->sin6_len
= sizeof(struct sockaddr_in6
);
384 server
->sin6_family
= AF_INET6
;
385 memcpy(&server
->sin6_addr
.s6_addr
, &pnl
.rdaddr
.v6
,
386 sizeof server
->sin6_addr
);
387 server
->sin6_port
= pnl
.rdport
;