1 /*
2 * Copyright (c) 2004 Camiel Dobbelaar, <cd@sentia.nl>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <sys/ioctl.h>
18 #include <sys/types.h>
19 #include <sys/socket.h>
20
21 #include <net/if.h>
22 #include <net/pf/pfvar.h>
23 #include <netinet/in.h>
24 #include <netinet/tcp.h>
25 #include <arpa/inet.h>
26
27 #include <err.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34
35 #include "filter.h"
36
37 static struct pfioc_pooladdr pfp;
38 static struct pfioc_rule pfr;
39 static struct pfioc_trans pft;
40 static struct pfioc_trans_e pfte;
41 static int dev;
42
43 void
filter_init(char * qname,char * tagname)44 filter_init(char *qname, char *tagname)
45 {
46 struct pf_status status;
47
48 dev = open("/dev/pf", O_RDWR);
49 if (dev == -1)
50 err(1, "/dev/pf");
51 if (ioctl(dev, DIOCGETSTATUS, &status) == -1)
52 err(1, "DIOCGETSTATUS");
53 if (!status.running)
54 errx(1, "pf is disabled");
55
56 /*
57 * Initialize the structs for filter_allow.
58 */
59
60 memset(&pfp, 0, sizeof pfp);
61 memset(&pfr, 0, sizeof pfr);
62 memset(&pft, 0, sizeof pft);
63 memset(&pfte, 0, sizeof pfte);
64
65 pft.size = 1;
66 pft.esize = sizeof pfte;
67 pft.array = &pfte;
68 pfte.rs_num = PF_RULESET_FILTER;
69
70 /*
71 * pass [quick] log inet proto tcp \
72 * from $src/32 to $dst/32 port = $d_port flags S/SAFR keep state
73 * [tag tagname] [queue qname]
74 */
75 pfr.rule.action = PF_PASS;
76 if (tagname == NULL)
77 pfr.rule.quick = 1;
78 pfr.rule.log = 1;
79 pfr.rule.af = AF_INET;
80 pfr.rule.proto = IPPROTO_TCP;
81 pfr.rule.src.addr.type = PF_ADDR_ADDRMASK;
82 memset(&pfr.rule.src.addr.v.a.mask.v4, 255, 4);
83 pfr.rule.dst.addr.type = PF_ADDR_ADDRMASK;
84 memset(&pfr.rule.dst.addr.v.a.mask.v4, 255, 4);
85 pfr.rule.dst.port_op = PF_OP_EQ;
86 pfr.rule.keep_state = 1;
87 pfr.rule.flags = TH_SYN;
88 pfr.rule.flagset = (TH_SYN|TH_ACK|TH_FIN|TH_RST);
89 if (tagname != NULL)
90 strlcpy(pfr.rule.tagname, tagname, sizeof pfr.rule.tagname);
91 if (qname != NULL)
92 strlcpy(pfr.rule.qname, qname, sizeof pfr.rule.qname);
93 }
94
95 int
filter_allow(u_int32_t id,struct in_addr * src,struct in_addr * src2,struct in_addr * dst,u_int16_t d_port)96 filter_allow(u_int32_t id, struct in_addr *src, struct in_addr *src2,
97 struct in_addr *dst, u_int16_t d_port)
98 {
99 char an[PF_ANCHOR_NAME_SIZE];
100
101 /* The structs are initialized in filter_init. */
102
103 snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTPSESAME_ANCHOR,
104 getpid(), id);
105 strlcpy(pfp.anchor, an, PF_ANCHOR_NAME_SIZE);
106 strlcpy(pfr.anchor, an, PF_ANCHOR_NAME_SIZE);
107 strlcpy(pfte.anchor, an, PF_ANCHOR_NAME_SIZE);
108
109 if (ioctl(dev, DIOCXBEGIN, &pft) == -1)
110 return (0);
111 pfr.ticket = pfte.ticket;
112
113 if (ioctl(dev, DIOCBEGINADDRS, &pfp) == -1)
114 return (0);
115 pfr.pool_ticket = pfp.ticket;
116
117 if (src != NULL && dst != NULL && d_port != 0) {
118 memcpy(&pfr.rule.src.addr.v.a.addr.v4, src, 4);
119 memcpy(&pfr.rule.dst.addr.v.a.addr.v4, dst, 4);
120 pfr.rule.dst.port[0] = htons(d_port);
121 if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
122 return (0);
123
124 if (src2 != NULL) {
125 memcpy(&pfr.rule.src.addr.v.a.addr.v4, src2, 4);
126 if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
127 return (0);
128 }
129 }
130
131 if (ioctl(dev, DIOCXCOMMIT, &pft) == -1)
132 return (0);
133
134 return (1);
135 }
136
137 int
filter_remove(u_int32_t id)138 filter_remove(u_int32_t id)
139 {
140 return (filter_allow(id, NULL, NULL, NULL, 0));
141 }
142
143 int
filter_lookup(struct in_addr * src,struct in_addr * dst,u_int16_t s_port,u_int16_t d_port,struct in_addr * src_real)144 filter_lookup(struct in_addr *src, struct in_addr *dst, u_int16_t s_port,
145 u_int16_t d_port, struct in_addr *src_real)
146 {
147 struct pfioc_natlook pnl;
148 int r;
149
150 memset(&pnl, 0, sizeof pnl);
151 pnl.af = AF_INET;
152 memcpy(&pnl.saddr.v4, src, sizeof pnl.saddr.v4);
153 memcpy(&pnl.daddr.v4, dst, sizeof pnl.daddr.v4);
154 pnl.proto = IPPROTO_TCP;
155 pnl.sport = s_port;
156 pnl.dport = d_port;
157 pnl.direction = PF_IN;
158
159 /*
160 * DIOCNATLOOK does not handle PF_INOUT, so we have to check
161 * both directions ourselves.
162 */
163 r = ioctl(dev, DIOCNATLOOK, &pnl);
164 if (r == -1 && errno == ENOENT) {
165 pnl.direction = PF_OUT;
166 r = ioctl(dev, DIOCNATLOOK, &pnl);
167 }
168 if (r == -1)
169 return (0);
170
171 /* Copy the real source address if there is NAT involved. */
172 if (memcmp(&pnl.saddr.v4, &pnl.rsaddr.v4, sizeof pnl.saddr.v4) != 0)
173 memcpy(src_real, &pnl.rsaddr.v4, sizeof src_real);
174
175 return (1);
176 }
177