]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/pf/tftp-proxy/filter.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / pf / tftp-proxy / filter.c
1 /*      $OpenBSD: filter.c,v 1.1 2005/12/28 19:07:07 jcs Exp $ */
2 /*      $FreeBSD$ */
3
4 /*
5  * Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
6  *
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.
10  *
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.
18  */
19
20 #include <syslog.h>
21
22 #include <sys/ioctl.h>
23 #include <sys/types.h>
24 #include <sys/socket.h>
25
26 #include <net/if.h>
27 #include <net/pfvar.h>
28 #include <netinet/in.h>
29 #include <netinet/tcp.h>
30 #include <arpa/inet.h>
31
32 #include <err.h>
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39
40 #include "filter.h"
41
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))
45
46 enum { TRANS_FILTER = 0, TRANS_NAT, TRANS_RDR, TRANS_SIZE };
47
48 int prepare_rule(u_int32_t, int, struct sockaddr *, struct sockaddr *,
49     u_int16_t, u_int8_t);
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);
54
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;
60 static char *qname;
61
62 int
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)
65 {
66         if (!src || !dst || !d_port || !proto) {
67                 errno = EINVAL;
68                 return (-1);
69         }
70
71         if (prepare_rule(id, PF_RULESET_FILTER, src, dst, d_port, proto) == -1)
72                 return (-1);
73
74         pfr.rule.direction = dir;
75         if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
76                 return (-1);
77
78         return (0);
79 }
80
81 int
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)
85 {
86         if (!src || !dst || !d_port || !nat || !nat_range_low || !proto ||
87             (src->sa_family != nat->sa_family)) {
88                 errno = EINVAL;
89                 return (-1);
90         }
91
92         if (prepare_rule(id, PF_RULESET_NAT, src, dst, d_port, proto) == -1)
93                 return (-1);
94
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);
99         } else {
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);
103         }
104         if (ioctl(dev, DIOCADDADDR, &pfp) == -1)
105                 return (-1);
106
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)
110                 return (-1);
111
112         return (0);
113 }
114
115 int
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)
118 {
119         if (!src || !dst || !d_port || !rdr || !rdr_port || !proto ||
120             (src->sa_family != rdr->sa_family)) {
121                 errno = EINVAL;
122                 return (-1);
123         }
124
125         if (prepare_rule(id, PF_RULESET_RDR, src, dst, d_port, proto) == -1)
126                 return (-1);
127
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);
132         } else {
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);
136         }
137         if (ioctl(dev, DIOCADDADDR, &pfp) == -1)
138                 return (-1);
139
140         pfr.rule.rpool.proxy_port[0] = rdr_port;
141         if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
142                 return (-1);
143
144         return (0);
145 }
146
147 int
148 do_commit(void)
149 {
150         if (ioctl(dev, DIOCXCOMMIT, &pft) == -1)
151                 return (-1);
152
153         return (0);
154 }
155
156 int
157 do_rollback(void)
158 {
159         if (ioctl(dev, DIOCXROLLBACK, &pft) == -1)
160                 return (-1);
161         
162         return (0);
163 }
164
165 void
166 init_filter(char *opt_qname, int opt_verbose)
167 {
168         struct pf_status status;
169
170         qname = opt_qname;
171
172         if (opt_verbose == 1)
173                 rule_log = PF_LOG;
174         else if (opt_verbose == 2)
175                 rule_log = PF_LOG_ALL;
176
177         dev = open("/dev/pf", O_RDWR);  
178         if (dev == -1) {
179                 syslog(LOG_ERR, "can't open /dev/pf");
180                 exit(1);
181         }
182         if (ioctl(dev, DIOCGETSTATUS, &status) == -1) {
183                 syslog(LOG_ERR, "DIOCGETSTATUS");
184                 exit(1);
185         }
186         if (!status.running) {
187                 syslog(LOG_ERR, "pf is disabled");
188                 exit(1);
189         }
190 }
191
192 int
193 prepare_commit(u_int32_t id)
194 {
195         char an[PF_ANCHOR_NAME_SIZE];
196         int i;
197
198         memset(&pft, 0, sizeof pft);
199         pft.size = TRANS_SIZE;
200         pft.esize = sizeof pfte[0];
201         pft.array = pfte;
202
203         snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR,
204             getpid(), id);
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);
208                 switch (i) {
209                 case TRANS_FILTER:
210                         pfte[i].rs_num = PF_RULESET_FILTER;
211                         break;
212                 case TRANS_NAT:
213                         pfte[i].rs_num = PF_RULESET_NAT;
214                         break;
215                 case TRANS_RDR:
216                         pfte[i].rs_num = PF_RULESET_RDR;
217                         break;
218                 default:
219                         errno = EINVAL;
220                         return (-1);
221                 }
222         }
223
224         if (ioctl(dev, DIOCXBEGIN, &pft) == -1)
225                 return (-1);
226
227         return (0);
228 }
229         
230 int
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)
233 {
234         char an[PF_ANCHOR_NAME_SIZE];
235
236         if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) ||
237             (src->sa_family != dst->sa_family)) {
238                 errno = EPROTONOSUPPORT;
239                 return (-1);
240         }
241
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,
245             getpid(), id);
246         strlcpy(pfp.anchor, an, PF_ANCHOR_NAME_SIZE);
247         strlcpy(pfr.anchor, an, PF_ANCHOR_NAME_SIZE);
248
249         switch (rs_num) {
250         case PF_RULESET_FILTER:
251                 pfr.ticket = pfte[TRANS_FILTER].ticket;
252                 break;
253         case PF_RULESET_NAT:
254                 pfr.ticket = pfte[TRANS_NAT].ticket;
255                 break;
256         case PF_RULESET_RDR:
257                 pfr.ticket = pfte[TRANS_RDR].ticket;
258                 break;
259         default:
260                 errno = EINVAL;
261                 return (-1);
262         }
263         if (ioctl(dev, DIOCBEGINADDRS, &pfp) == -1)
264                 return (-1);
265         pfr.pool_ticket = pfp.ticket;
266
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);
279         } else {
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);
286         }
287         pfr.rule.dst.port_op = PF_OP_EQ;
288         pfr.rule.dst.port[0] = htons(d_port);
289
290         switch (rs_num) {
291         case PF_RULESET_FILTER:
292                 /*
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]
296                  */
297                 pfr.rule.action = PF_PASS;
298                 pfr.rule.quick = 1;
299                 pfr.rule.log = rule_log;
300                 pfr.rule.keep_state = 1;
301 #ifdef __FreeBSD__
302                 pfr.rule.flags = (proto == IPPROTO_TCP ? TH_SYN : 0);
303                 pfr.rule.flagset = (proto == IPPROTO_TCP ?
304                     (TH_SYN|TH_ACK|TH_FIN|TH_RST) : 0);
305 #else
306                 pfr.rule.flags = (proto == IPPROTO_TCP ? TH_SYN : NULL);
307                 pfr.rule.flagset = (proto == IPPROTO_TCP ?
308                     (TH_SYN|TH_ACK|TH_FIN|TH_RST) : NULL);
309 #endif
310                 pfr.rule.max_states = 1;
311                 if (qname != NULL)
312                         strlcpy(pfr.rule.qname, qname, sizeof pfr.rule.qname);
313                 break;
314         case PF_RULESET_NAT:
315                 /*
316                  * nat inet[6] proto tcp from $src to $dst port $d_port -> $nat
317                  */
318                 pfr.rule.action = PF_NAT;
319                 break;
320         case PF_RULESET_RDR:
321                 /*
322                  * rdr inet[6] proto tcp from $src to $dst port $d_port -> $rdr
323                  */
324                 pfr.rule.action = PF_RDR;
325                 break;
326         default:
327                 errno = EINVAL;
328                 return (-1);
329         }
330
331         return (0);
332 }
333
334 int
335 server_lookup(struct sockaddr *client, struct sockaddr *proxy,
336     struct sockaddr *server, u_int8_t proto)
337 {
338         if (client->sa_family == AF_INET)
339                 return (server_lookup4(satosin(client), satosin(proxy),
340                     satosin(server), proto));
341
342         if (client->sa_family == AF_INET6)
343                 return (server_lookup6(satosin6(client), satosin6(proxy),
344                     satosin6(server), proto));
345
346         errno = EPROTONOSUPPORT;
347         return (-1);
348 }
349
350 int
351 server_lookup4(struct sockaddr_in *client, struct sockaddr_in *proxy,
352     struct sockaddr_in *server, u_int8_t proto)
353 {
354         struct pfioc_natlook pnl;
355
356         memset(&pnl, 0, sizeof pnl);
357         pnl.direction = PF_OUT;
358         pnl.af = AF_INET;
359         pnl.proto = proto;
360         memcpy(&pnl.saddr.v4, &client->sin_addr.s_addr, sizeof pnl.saddr.v4);
361         memcpy(&pnl.daddr.v4, &proxy->sin_addr.s_addr, sizeof pnl.daddr.v4);
362         pnl.sport = client->sin_port;
363         pnl.dport = proxy->sin_port;
364
365         if (ioctl(dev, DIOCNATLOOK, &pnl) == -1)
366                 return (-1);
367
368         memset(server, 0, sizeof(struct sockaddr_in));
369         server->sin_len = sizeof(struct sockaddr_in);
370         server->sin_family = AF_INET;
371         memcpy(&server->sin_addr.s_addr, &pnl.rdaddr.v4,
372             sizeof server->sin_addr.s_addr);
373         server->sin_port = pnl.rdport;
374
375         return (0);
376 }
377
378 int
379 server_lookup6(struct sockaddr_in6 *client, struct sockaddr_in6 *proxy,
380     struct sockaddr_in6 *server, u_int8_t proto)
381 {
382         struct pfioc_natlook pnl;
383
384         memset(&pnl, 0, sizeof pnl);
385         pnl.direction = PF_OUT;
386         pnl.af = AF_INET6;
387         pnl.proto = proto;
388         memcpy(&pnl.saddr.v6, &client->sin6_addr.s6_addr, sizeof pnl.saddr.v6);
389         memcpy(&pnl.daddr.v6, &proxy->sin6_addr.s6_addr, sizeof pnl.daddr.v6);
390         pnl.sport = client->sin6_port;
391         pnl.dport = proxy->sin6_port;
392         
393         if (ioctl(dev, DIOCNATLOOK, &pnl) == -1)
394                 return (-1);
395
396         memset(server, 0, sizeof(struct sockaddr_in6));
397         server->sin6_len = sizeof(struct sockaddr_in6);
398         server->sin6_family = AF_INET6;
399         memcpy(&server->sin6_addr.s6_addr, &pnl.rdaddr.v6,
400             sizeof server->sin6_addr);
401         server->sin6_port = pnl.rdport;
402
403         return (0);
404 }