1 |
|
|
/* $OpenBSD: pfe_filter.c,v 1.62 2017/05/28 10:39:15 benno Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> |
5 |
|
|
* |
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. |
9 |
|
|
* |
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. |
17 |
|
|
*/ |
18 |
|
|
|
19 |
|
|
#include <sys/types.h> |
20 |
|
|
#include <sys/queue.h> |
21 |
|
|
#include <sys/socket.h> |
22 |
|
|
#include <sys/time.h> |
23 |
|
|
#include <sys/ioctl.h> |
24 |
|
|
|
25 |
|
|
#include <net/if.h> |
26 |
|
|
#include <netinet/in.h> |
27 |
|
|
#include <netinet/tcp.h> |
28 |
|
|
#include <arpa/inet.h> |
29 |
|
|
#include <net/pfvar.h> |
30 |
|
|
|
31 |
|
|
#include <limits.h> |
32 |
|
|
#include <string.h> |
33 |
|
|
#include <stdio.h> |
34 |
|
|
#include <stdlib.h> |
35 |
|
|
#include <unistd.h> |
36 |
|
|
#include <errno.h> |
37 |
|
|
|
38 |
|
|
#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) |
39 |
|
|
|
40 |
|
|
#include "relayd.h" |
41 |
|
|
|
42 |
|
|
int transaction_init(struct relayd *, const char *); |
43 |
|
|
int transaction_commit(struct relayd *); |
44 |
|
|
void kill_tables(struct relayd *); |
45 |
|
|
int kill_srcnodes(struct relayd *, struct table *); |
46 |
|
|
|
47 |
|
|
void |
48 |
|
|
init_tables(struct relayd *env) |
49 |
|
|
{ |
50 |
|
|
int i; |
51 |
|
|
struct rdr *rdr; |
52 |
|
|
struct pfr_table *tables; |
53 |
|
|
struct pfioc_table io; |
54 |
|
|
|
55 |
|
|
if (!(env->sc_conf.flags & F_NEEDPF)) |
56 |
|
|
return; |
57 |
|
|
|
58 |
|
|
if ((tables = calloc(env->sc_rdrcount, sizeof(*tables))) == NULL) |
59 |
|
|
fatal("calloc"); |
60 |
|
|
i = 0; |
61 |
|
|
|
62 |
|
|
TAILQ_FOREACH(rdr, env->sc_rdrs, entry) { |
63 |
|
|
if (strlcpy(tables[i].pfrt_anchor, RELAYD_ANCHOR "/", |
64 |
|
|
sizeof(tables[i].pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE) |
65 |
|
|
goto toolong; |
66 |
|
|
if (strlcat(tables[i].pfrt_anchor, rdr->conf.name, |
67 |
|
|
sizeof(tables[i].pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE) |
68 |
|
|
goto toolong; |
69 |
|
|
if (strlcpy(tables[i].pfrt_name, rdr->conf.name, |
70 |
|
|
sizeof(tables[i].pfrt_name)) >= |
71 |
|
|
sizeof(tables[i].pfrt_name)) |
72 |
|
|
goto toolong; |
73 |
|
|
tables[i].pfrt_flags |= PFR_TFLAG_PERSIST; |
74 |
|
|
i++; |
75 |
|
|
} |
76 |
|
|
if (i != env->sc_rdrcount) |
77 |
|
|
fatalx("%s: table count modified", __func__); |
78 |
|
|
|
79 |
|
|
memset(&io, 0, sizeof(io)); |
80 |
|
|
io.pfrio_size = env->sc_rdrcount; |
81 |
|
|
io.pfrio_esize = sizeof(*tables); |
82 |
|
|
io.pfrio_buffer = tables; |
83 |
|
|
|
84 |
|
|
if (ioctl(env->sc_pf->dev, DIOCRADDTABLES, &io) == -1) |
85 |
|
|
fatal("%s: cannot create tables", __func__); |
86 |
|
|
log_debug("%s: created %d tables", __func__, io.pfrio_nadd); |
87 |
|
|
|
88 |
|
|
free(tables); |
89 |
|
|
|
90 |
|
|
if (io.pfrio_nadd == env->sc_rdrcount) |
91 |
|
|
return; |
92 |
|
|
|
93 |
|
|
/* |
94 |
|
|
* clear all tables, since some already existed |
95 |
|
|
*/ |
96 |
|
|
TAILQ_FOREACH(rdr, env->sc_rdrs, entry) |
97 |
|
|
flush_table(env, rdr); |
98 |
|
|
|
99 |
|
|
return; |
100 |
|
|
|
101 |
|
|
toolong: |
102 |
|
|
fatal("%s: name too long", __func__); |
103 |
|
|
} |
104 |
|
|
|
105 |
|
|
void |
106 |
|
|
kill_tables(struct relayd *env) |
107 |
|
|
{ |
108 |
|
|
struct pfioc_table io; |
109 |
|
|
struct rdr *rdr; |
110 |
|
|
int cnt = 0; |
111 |
|
|
|
112 |
|
|
if (!(env->sc_conf.flags & F_NEEDPF)) |
113 |
|
|
return; |
114 |
|
|
|
115 |
|
|
TAILQ_FOREACH(rdr, env->sc_rdrs, entry) { |
116 |
|
|
memset(&io, 0, sizeof(io)); |
117 |
|
|
if (strlcpy(io.pfrio_table.pfrt_anchor, RELAYD_ANCHOR "/", |
118 |
|
|
sizeof(io.pfrio_table.pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE) |
119 |
|
|
goto toolong; |
120 |
|
|
if (strlcat(io.pfrio_table.pfrt_anchor, rdr->conf.name, |
121 |
|
|
sizeof(io.pfrio_table.pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE) |
122 |
|
|
goto toolong; |
123 |
|
|
if (ioctl(env->sc_pf->dev, DIOCRCLRTABLES, &io) == -1) |
124 |
|
|
fatal("%s: ioctl failed", __func__); |
125 |
|
|
cnt += io.pfrio_ndel; |
126 |
|
|
} |
127 |
|
|
log_debug("%s: deleted %d tables", __func__, cnt); |
128 |
|
|
return; |
129 |
|
|
|
130 |
|
|
toolong: |
131 |
|
|
fatal("%s: name too long", __func__); |
132 |
|
|
} |
133 |
|
|
|
134 |
|
|
void |
135 |
|
|
sync_table(struct relayd *env, struct rdr *rdr, struct table *table) |
136 |
|
|
{ |
137 |
|
|
int i, cnt = 0; |
138 |
|
|
struct pfioc_table io; |
139 |
|
|
struct pfr_addr *addlist; |
140 |
|
|
struct sockaddr_in *sain; |
141 |
|
|
struct sockaddr_in6 *sain6; |
142 |
|
|
struct host *host; |
143 |
|
|
|
144 |
|
|
if (!(env->sc_conf.flags & F_NEEDPF)) |
145 |
|
|
return; |
146 |
|
|
|
147 |
|
|
if (table == NULL) |
148 |
|
|
return; |
149 |
|
|
|
150 |
|
|
if (table->up == 0) { |
151 |
|
|
flush_table(env, rdr); |
152 |
|
|
return; |
153 |
|
|
} |
154 |
|
|
|
155 |
|
|
if ((addlist = calloc(table->up, sizeof(*addlist))) == NULL) |
156 |
|
|
fatal("calloc"); |
157 |
|
|
|
158 |
|
|
memset(&io, 0, sizeof(io)); |
159 |
|
|
io.pfrio_esize = sizeof(struct pfr_addr); |
160 |
|
|
io.pfrio_size = table->up; |
161 |
|
|
io.pfrio_size2 = 0; |
162 |
|
|
io.pfrio_buffer = addlist; |
163 |
|
|
if (strlcpy(io.pfrio_table.pfrt_anchor, RELAYD_ANCHOR "/", |
164 |
|
|
sizeof(io.pfrio_table.pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE) |
165 |
|
|
goto toolong; |
166 |
|
|
if (strlcat(io.pfrio_table.pfrt_anchor, rdr->conf.name, |
167 |
|
|
sizeof(io.pfrio_table.pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE) |
168 |
|
|
goto toolong; |
169 |
|
|
if (strlcpy(io.pfrio_table.pfrt_name, rdr->conf.name, |
170 |
|
|
sizeof(io.pfrio_table.pfrt_name)) >= |
171 |
|
|
sizeof(io.pfrio_table.pfrt_name)) |
172 |
|
|
goto toolong; |
173 |
|
|
|
174 |
|
|
i = 0; |
175 |
|
|
TAILQ_FOREACH(host, &table->hosts, entry) { |
176 |
|
|
if (host->up != HOST_UP) |
177 |
|
|
continue; |
178 |
|
|
memset(&(addlist[i]), 0, sizeof(addlist[i])); |
179 |
|
|
switch (host->conf.ss.ss_family) { |
180 |
|
|
case AF_INET: |
181 |
|
|
sain = (struct sockaddr_in *)&host->conf.ss; |
182 |
|
|
addlist[i].pfra_af = AF_INET; |
183 |
|
|
memcpy(&(addlist[i].pfra_ip4addr), &sain->sin_addr, |
184 |
|
|
sizeof(sain->sin_addr)); |
185 |
|
|
addlist[i].pfra_net = 32; |
186 |
|
|
break; |
187 |
|
|
case AF_INET6: |
188 |
|
|
sain6 = (struct sockaddr_in6 *)&host->conf.ss; |
189 |
|
|
addlist[i].pfra_af = AF_INET6; |
190 |
|
|
memcpy(&(addlist[i].pfra_ip6addr), &sain6->sin6_addr, |
191 |
|
|
sizeof(sain6->sin6_addr)); |
192 |
|
|
addlist[i].pfra_net = 128; |
193 |
|
|
break; |
194 |
|
|
default: |
195 |
|
|
fatalx("%s: unknown address family", __func__); |
196 |
|
|
break; |
197 |
|
|
} |
198 |
|
|
i++; |
199 |
|
|
} |
200 |
|
|
if (i != table->up) |
201 |
|
|
fatalx("%s: desynchronized", __func__); |
202 |
|
|
|
203 |
|
|
if (ioctl(env->sc_pf->dev, DIOCRSETADDRS, &io) == -1) |
204 |
|
|
fatal("%s: cannot set address list", __func__); |
205 |
|
|
if (rdr->conf.flags & F_STICKY) |
206 |
|
|
cnt = kill_srcnodes(env, table); |
207 |
|
|
free(addlist); |
208 |
|
|
|
209 |
|
|
if (env->sc_conf.opts & RELAYD_OPT_LOGUPDATE) |
210 |
|
|
log_info("table %s: %d added, %d deleted, " |
211 |
|
|
"%d changed, %d killed", io.pfrio_table.pfrt_name, |
212 |
|
|
io.pfrio_nadd, io.pfrio_ndel, io.pfrio_nchange, cnt); |
213 |
|
|
return; |
214 |
|
|
|
215 |
|
|
toolong: |
216 |
|
|
fatal("%s: name too long", __func__); |
217 |
|
|
} |
218 |
|
|
|
219 |
|
|
int |
220 |
|
|
kill_srcnodes(struct relayd *env, struct table *table) |
221 |
|
|
{ |
222 |
|
|
struct host *host; |
223 |
|
|
struct pfioc_src_node_kill psnk; |
224 |
|
|
int cnt = 0; |
225 |
|
|
struct sockaddr_in *sain; |
226 |
|
|
struct sockaddr_in6 *sain6; |
227 |
|
|
|
228 |
|
|
bzero(&psnk, sizeof(psnk)); |
229 |
|
|
|
230 |
|
|
/* Only match the destination address, source mask will be zero */ |
231 |
|
|
memset(&psnk.psnk_dst.addr.v.a.mask, 0xff, |
232 |
|
|
sizeof(psnk.psnk_dst.addr.v.a.mask)); |
233 |
|
|
|
234 |
|
|
TAILQ_FOREACH(host, &table->hosts, entry) { |
235 |
|
|
if (host->up != HOST_DOWN) |
236 |
|
|
continue; |
237 |
|
|
|
238 |
|
|
switch (host->conf.ss.ss_family) { |
239 |
|
|
case AF_INET: |
240 |
|
|
sain = (struct sockaddr_in *)&host->conf.ss; |
241 |
|
|
bcopy(&sain->sin_addr, |
242 |
|
|
&psnk.psnk_dst.addr.v.a.addr.v4, |
243 |
|
|
sizeof(psnk.psnk_dst.addr.v.a.addr.v4)); |
244 |
|
|
break; |
245 |
|
|
case AF_INET6: |
246 |
|
|
sain6 = (struct sockaddr_in6 *)&host->conf.ss; |
247 |
|
|
bcopy(&sain6->sin6_addr, |
248 |
|
|
&psnk.psnk_dst.addr.v.a.addr.v6, |
249 |
|
|
sizeof(psnk.psnk_dst.addr.v.a.addr.v6)); |
250 |
|
|
break; |
251 |
|
|
default: |
252 |
|
|
fatalx("%s: unknown address family", __func__); |
253 |
|
|
break; |
254 |
|
|
} |
255 |
|
|
|
256 |
|
|
psnk.psnk_af = host->conf.ss.ss_family; |
257 |
|
|
psnk.psnk_killed = 0; |
258 |
|
|
|
259 |
|
|
if (ioctl(env->sc_pf->dev, |
260 |
|
|
DIOCKILLSRCNODES, &psnk) == -1) |
261 |
|
|
fatal("%s: cannot kill src nodes", __func__); |
262 |
|
|
cnt += psnk.psnk_killed; |
263 |
|
|
} |
264 |
|
|
|
265 |
|
|
return (cnt); |
266 |
|
|
} |
267 |
|
|
|
268 |
|
|
void |
269 |
|
|
flush_table(struct relayd *env, struct rdr *rdr) |
270 |
|
|
{ |
271 |
|
|
struct pfioc_table io; |
272 |
|
|
|
273 |
|
|
if (!(env->sc_conf.flags & F_NEEDPF)) |
274 |
|
|
return; |
275 |
|
|
|
276 |
|
|
memset(&io, 0, sizeof(io)); |
277 |
|
|
if (strlcpy(io.pfrio_table.pfrt_anchor, RELAYD_ANCHOR "/", |
278 |
|
|
sizeof(io.pfrio_table.pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE) |
279 |
|
|
goto toolong; |
280 |
|
|
if (strlcat(io.pfrio_table.pfrt_anchor, rdr->conf.name, |
281 |
|
|
sizeof(io.pfrio_table.pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE) |
282 |
|
|
goto toolong; |
283 |
|
|
if (strlcpy(io.pfrio_table.pfrt_name, rdr->conf.name, |
284 |
|
|
sizeof(io.pfrio_table.pfrt_name)) >= |
285 |
|
|
sizeof(io.pfrio_table.pfrt_name)) |
286 |
|
|
goto toolong; |
287 |
|
|
if (ioctl(env->sc_pf->dev, DIOCRCLRADDRS, &io) == -1) |
288 |
|
|
fatal("%s: cannot flush table addresses", __func__); |
289 |
|
|
|
290 |
|
|
io.pfrio_esize = sizeof(io.pfrio_table); |
291 |
|
|
io.pfrio_size = 1; |
292 |
|
|
io.pfrio_buffer = &io.pfrio_table; |
293 |
|
|
if (ioctl(env->sc_pf->dev, DIOCRCLRTSTATS, &io) == -1) |
294 |
|
|
fatal("%s: cannot flush table stats", __func__); |
295 |
|
|
|
296 |
|
|
log_debug("%s: flushed table %s", __func__, rdr->conf.name); |
297 |
|
|
return; |
298 |
|
|
|
299 |
|
|
toolong: |
300 |
|
|
fatal("%s: name too long", __func__); |
301 |
|
|
} |
302 |
|
|
|
303 |
|
|
int |
304 |
|
|
transaction_init(struct relayd *env, const char *anchor) |
305 |
|
|
{ |
306 |
|
|
env->sc_pf->pft.size = 1; |
307 |
|
|
env->sc_pf->pft.esize = sizeof(env->sc_pf->pfte); |
308 |
|
|
env->sc_pf->pft.array = &env->sc_pf->pfte; |
309 |
|
|
|
310 |
|
|
bzero(&env->sc_pf->pfte, sizeof(env->sc_pf->pfte)); |
311 |
|
|
(void)strlcpy(env->sc_pf->pfte.anchor, |
312 |
|
|
anchor, PF_ANCHOR_NAME_SIZE); |
313 |
|
|
env->sc_pf->pfte.type = PF_TRANS_RULESET; |
314 |
|
|
|
315 |
|
|
if (ioctl(env->sc_pf->dev, DIOCXBEGIN, |
316 |
|
|
&env->sc_pf->pft) == -1) |
317 |
|
|
return (-1); |
318 |
|
|
|
319 |
|
|
return (0); |
320 |
|
|
} |
321 |
|
|
|
322 |
|
|
int |
323 |
|
|
transaction_commit(struct relayd *env) |
324 |
|
|
{ |
325 |
|
|
if (ioctl(env->sc_pf->dev, DIOCXCOMMIT, |
326 |
|
|
&env->sc_pf->pft) == -1) |
327 |
|
|
return (-1); |
328 |
|
|
|
329 |
|
|
return (0); |
330 |
|
|
} |
331 |
|
|
|
332 |
|
|
void |
333 |
|
|
sync_ruleset(struct relayd *env, struct rdr *rdr, int enable) |
334 |
|
|
{ |
335 |
|
|
struct pfioc_rule rio; |
336 |
|
|
struct sockaddr_in *sain; |
337 |
|
|
struct sockaddr_in6 *sain6; |
338 |
|
|
struct address *address; |
339 |
|
|
char anchor[PF_ANCHOR_NAME_SIZE]; |
340 |
|
|
struct table *t = rdr->table; |
341 |
|
|
|
342 |
|
|
if ((env->sc_conf.flags & F_NEEDPF) == 0) |
343 |
|
|
return; |
344 |
|
|
|
345 |
|
|
bzero(anchor, sizeof(anchor)); |
346 |
|
|
if (strlcpy(anchor, RELAYD_ANCHOR "/", sizeof(anchor)) >= |
347 |
|
|
PF_ANCHOR_NAME_SIZE) |
348 |
|
|
goto toolong; |
349 |
|
|
if (strlcat(anchor, rdr->conf.name, sizeof(anchor)) >= |
350 |
|
|
PF_ANCHOR_NAME_SIZE) |
351 |
|
|
goto toolong; |
352 |
|
|
if (transaction_init(env, anchor) == -1) { |
353 |
|
|
log_warn("%s: transaction init failed", __func__); |
354 |
|
|
return; |
355 |
|
|
} |
356 |
|
|
|
357 |
|
|
if (!enable) { |
358 |
|
|
if (transaction_commit(env) == -1) |
359 |
|
|
log_warn("%s: remove rules transaction failed", |
360 |
|
|
__func__); |
361 |
|
|
else |
362 |
|
|
log_debug("%s: rules removed", __func__); |
363 |
|
|
return; |
364 |
|
|
} |
365 |
|
|
|
366 |
|
|
TAILQ_FOREACH(address, &rdr->virts, entry) { |
367 |
|
|
memset(&rio, 0, sizeof(rio)); |
368 |
|
|
(void)strlcpy(rio.anchor, anchor, sizeof(rio.anchor)); |
369 |
|
|
|
370 |
|
|
if (rdr->conf.flags & F_MATCH) { |
371 |
|
|
rio.rule.action = PF_MATCH; |
372 |
|
|
rio.rule.quick = 0; |
373 |
|
|
} else { |
374 |
|
|
rio.rule.action = PF_PASS; |
375 |
|
|
rio.rule.quick = 1; /* force first match */ |
376 |
|
|
} |
377 |
|
|
rio.rule.direction = PF_IN; |
378 |
|
|
rio.rule.keep_state = PF_STATE_NORMAL; |
379 |
|
|
|
380 |
|
|
switch (t->conf.fwdmode) { |
381 |
|
|
case FWD_NORMAL: |
382 |
|
|
/* traditional redirection */ |
383 |
|
|
if (address->ipproto == IPPROTO_TCP) { |
384 |
|
|
rio.rule.flags = TH_SYN; |
385 |
|
|
rio.rule.flagset = (TH_SYN|TH_ACK); |
386 |
|
|
} |
387 |
|
|
break; |
388 |
|
|
case FWD_ROUTE: |
389 |
|
|
/* re-route with pf for DSR (direct server return) */ |
390 |
|
|
rio.rule.rt = PF_ROUTETO; |
391 |
|
|
|
392 |
|
|
/* Use sloppy state handling for half connections */ |
393 |
|
|
rio.rule.rule_flag = PFRULE_STATESLOPPY; |
394 |
|
|
break; |
395 |
|
|
default: |
396 |
|
|
fatalx("%s: invalid forward mode", __func__); |
397 |
|
|
/* NOTREACHED */ |
398 |
|
|
} |
399 |
|
|
|
400 |
|
|
rio.ticket = env->sc_pf->pfte.ticket; |
401 |
|
|
|
402 |
|
|
rio.rule.af = address->ss.ss_family; |
403 |
|
|
rio.rule.proto = address->ipproto; |
404 |
|
|
rio.rule.src.addr.type = PF_ADDR_ADDRMASK; |
405 |
|
|
rio.rule.dst.addr.type = PF_ADDR_ADDRMASK; |
406 |
|
|
rio.rule.dst.port_op = address->port.op; |
407 |
|
|
rio.rule.dst.port[0] = address->port.val[0]; |
408 |
|
|
rio.rule.dst.port[1] = address->port.val[1]; |
409 |
|
|
rio.rule.rtableid = -1; /* stay in the main routing table */ |
410 |
|
|
rio.rule.onrdomain = env->sc_rtable; |
411 |
|
|
DPRINTF("%s rtable %d",__func__,env->sc_rtable); |
412 |
|
|
|
413 |
|
|
if (rio.rule.proto == IPPROTO_TCP) |
414 |
|
|
rio.rule.timeout[PFTM_TCP_ESTABLISHED] = |
415 |
|
|
(u_int32_t)MINIMUM(rdr->conf.timeout.tv_sec, |
416 |
|
|
INT_MAX); |
417 |
|
|
|
418 |
|
|
if (strlen(rdr->conf.tag)) |
419 |
|
|
(void)strlcpy(rio.rule.tagname, rdr->conf.tag, |
420 |
|
|
sizeof(rio.rule.tagname)); |
421 |
|
|
if (strlen(address->ifname)) |
422 |
|
|
(void)strlcpy(rio.rule.ifname, address->ifname, |
423 |
|
|
sizeof(rio.rule.ifname)); |
424 |
|
|
|
425 |
|
|
if (address->ss.ss_family == AF_INET) { |
426 |
|
|
sain = (struct sockaddr_in *)&address->ss; |
427 |
|
|
|
428 |
|
|
rio.rule.dst.addr.v.a.addr.addr32[0] = |
429 |
|
|
sain->sin_addr.s_addr; |
430 |
|
|
rio.rule.dst.addr.v.a.mask.addr32[0] = 0xffffffff; |
431 |
|
|
} else { |
432 |
|
|
sain6 = (struct sockaddr_in6 *)&address->ss; |
433 |
|
|
|
434 |
|
|
memcpy(&rio.rule.dst.addr.v.a.addr.v6, |
435 |
|
|
&sain6->sin6_addr.s6_addr, |
436 |
|
|
sizeof(sain6->sin6_addr.s6_addr)); |
437 |
|
|
memset(&rio.rule.dst.addr.v.a.mask.addr8, 0xff, 16); |
438 |
|
|
} |
439 |
|
|
|
440 |
|
|
rio.rule.nat.addr.type = PF_ADDR_NONE; |
441 |
|
|
rio.rule.rdr.addr.type = PF_ADDR_TABLE; |
442 |
|
|
if (strlen(t->conf.ifname)) |
443 |
|
|
(void)strlcpy(rio.rule.rdr.ifname, t->conf.ifname, |
444 |
|
|
sizeof(rio.rule.rdr.ifname)); |
445 |
|
|
if (strlcpy(rio.rule.rdr.addr.v.tblname, rdr->conf.name, |
446 |
|
|
sizeof(rio.rule.rdr.addr.v.tblname)) >= |
447 |
|
|
sizeof(rio.rule.rdr.addr.v.tblname)) |
448 |
|
|
fatal("%s: table name too long", __func__); |
449 |
|
|
|
450 |
|
|
if (address->port.op == PF_OP_EQ || |
451 |
|
|
rdr->table->conf.flags & F_PORT) { |
452 |
|
|
rio.rule.rdr.proxy_port[0] = |
453 |
|
|
ntohs(rdr->table->conf.port); |
454 |
|
|
rio.rule.rdr.port_op = PF_OP_EQ; |
455 |
|
|
} |
456 |
|
|
|
457 |
|
|
switch (rdr->conf.mode) { |
458 |
|
|
case RELAY_DSTMODE_RANDOM: |
459 |
|
|
rio.rule.rdr.opts = PF_POOL_RANDOM; |
460 |
|
|
break; |
461 |
|
|
case RELAY_DSTMODE_ROUNDROBIN: |
462 |
|
|
rio.rule.rdr.opts = PF_POOL_ROUNDROBIN; |
463 |
|
|
break; |
464 |
|
|
case RELAY_DSTMODE_SRCHASH: |
465 |
|
|
rio.rule.rdr.opts = PF_POOL_SRCHASH; |
466 |
|
|
break; |
467 |
|
|
case RELAY_DSTMODE_LEASTSTATES: |
468 |
|
|
rio.rule.rdr.opts = PF_POOL_LEASTSTATES; |
469 |
|
|
break; |
470 |
|
|
default: |
471 |
|
|
fatalx("%s: unsupported mode", __func__); |
472 |
|
|
/* NOTREACHED */ |
473 |
|
|
} |
474 |
|
|
if (rdr->conf.flags & F_STICKY) |
475 |
|
|
rio.rule.rdr.opts |= PF_POOL_STICKYADDR; |
476 |
|
|
if (rdr->conf.flags & F_HASHKEY) |
477 |
|
|
memcpy(rio.rule.rdr.key.key32, rdr->conf.key.data, |
478 |
|
|
sizeof(rio.rule.rdr.key.key32)); |
479 |
|
|
|
480 |
|
|
if (rio.rule.rt == PF_ROUTETO) { |
481 |
|
|
memcpy(&rio.rule.route, &rio.rule.rdr, |
482 |
|
|
sizeof(rio.rule.route)); |
483 |
|
|
rio.rule.rdr.addr.type = PF_ADDR_NONE; |
484 |
|
|
} |
485 |
|
|
|
486 |
|
|
if (ioctl(env->sc_pf->dev, DIOCADDRULE, &rio) == -1) |
487 |
|
|
fatal("cannot add rule"); |
488 |
|
|
log_debug("%s: rule added to anchor \"%s\"", __func__, anchor); |
489 |
|
|
} |
490 |
|
|
if (transaction_commit(env) == -1) |
491 |
|
|
log_warn("%s: add rules transaction failed", __func__); |
492 |
|
|
return; |
493 |
|
|
|
494 |
|
|
toolong: |
495 |
|
|
fatal("%s: name too long", __func__); |
496 |
|
|
} |
497 |
|
|
|
498 |
|
|
void |
499 |
|
|
flush_rulesets(struct relayd *env) |
500 |
|
|
{ |
501 |
|
|
struct rdr *rdr; |
502 |
|
|
char anchor[PF_ANCHOR_NAME_SIZE]; |
503 |
|
|
|
504 |
|
|
if (!(env->sc_conf.flags & F_NEEDPF)) |
505 |
|
|
return; |
506 |
|
|
|
507 |
|
|
kill_tables(env); |
508 |
|
|
TAILQ_FOREACH(rdr, env->sc_rdrs, entry) { |
509 |
|
|
if (strlcpy(anchor, RELAYD_ANCHOR "/", sizeof(anchor)) >= |
510 |
|
|
PF_ANCHOR_NAME_SIZE) |
511 |
|
|
goto toolong; |
512 |
|
|
if (strlcat(anchor, rdr->conf.name, sizeof(anchor)) >= |
513 |
|
|
PF_ANCHOR_NAME_SIZE) |
514 |
|
|
goto toolong; |
515 |
|
|
if (transaction_init(env, anchor) == -1 || |
516 |
|
|
transaction_commit(env) == -1) |
517 |
|
|
log_warn("%s: transaction for %s/ failed", __func__, |
518 |
|
|
RELAYD_ANCHOR); |
519 |
|
|
} |
520 |
|
|
if (strlcpy(anchor, RELAYD_ANCHOR, sizeof(anchor)) >= |
521 |
|
|
PF_ANCHOR_NAME_SIZE) |
522 |
|
|
goto toolong; |
523 |
|
|
if (transaction_init(env, anchor) == -1 || |
524 |
|
|
transaction_commit(env) == -1) |
525 |
|
|
log_warn("%s: transaction for %s failed", __func__, |
526 |
|
|
RELAYD_ANCHOR); |
527 |
|
|
log_debug("%s: flushed rules", __func__); |
528 |
|
|
return; |
529 |
|
|
|
530 |
|
|
toolong: |
531 |
|
|
fatal("%s: name too long", __func__); |
532 |
|
|
} |
533 |
|
|
|
534 |
|
|
int |
535 |
|
|
natlook(struct relayd *env, struct ctl_natlook *cnl) |
536 |
|
|
{ |
537 |
|
|
struct pfioc_natlook pnl; |
538 |
|
|
struct sockaddr_in *in, *out; |
539 |
|
|
struct sockaddr_in6 *in6, *out6; |
540 |
|
|
char ibuf[BUFSIZ], obuf[BUFSIZ]; |
541 |
|
|
|
542 |
|
|
if (!(env->sc_conf.flags & F_NEEDPF)) |
543 |
|
|
return (0); |
544 |
|
|
|
545 |
|
|
bzero(&pnl, sizeof(pnl)); |
546 |
|
|
|
547 |
|
|
if ((pnl.af = cnl->src.ss_family) != cnl->dst.ss_family) |
548 |
|
|
fatalx("%s: illegal address families", __func__); |
549 |
|
|
switch (pnl.af) { |
550 |
|
|
case AF_INET: |
551 |
|
|
in = (struct sockaddr_in *)&cnl->src; |
552 |
|
|
out = (struct sockaddr_in *)&cnl->dst; |
553 |
|
|
bcopy(&in->sin_addr, &pnl.saddr.v4, sizeof(pnl.saddr.v4)); |
554 |
|
|
pnl.sport = in->sin_port; |
555 |
|
|
bcopy(&out->sin_addr, &pnl.daddr.v4, sizeof(pnl.daddr.v4)); |
556 |
|
|
pnl.dport = out->sin_port; |
557 |
|
|
break; |
558 |
|
|
case AF_INET6: |
559 |
|
|
in6 = (struct sockaddr_in6 *)&cnl->src; |
560 |
|
|
out6 = (struct sockaddr_in6 *)&cnl->dst; |
561 |
|
|
bcopy(&in6->sin6_addr, &pnl.saddr.v6, sizeof(pnl.saddr.v6)); |
562 |
|
|
pnl.sport = in6->sin6_port; |
563 |
|
|
bcopy(&out6->sin6_addr, &pnl.daddr.v6, sizeof(pnl.daddr.v6)); |
564 |
|
|
pnl.dport = out6->sin6_port; |
565 |
|
|
} |
566 |
|
|
pnl.proto = cnl->proto; |
567 |
|
|
pnl.direction = PF_IN; |
568 |
|
|
cnl->in = 1; |
569 |
|
|
|
570 |
|
|
if (ioctl(env->sc_pf->dev, DIOCNATLOOK, &pnl) == -1) { |
571 |
|
|
pnl.direction = PF_OUT; |
572 |
|
|
cnl->in = 0; |
573 |
|
|
if (ioctl(env->sc_pf->dev, DIOCNATLOOK, &pnl) == -1) { |
574 |
|
|
log_debug("%s: ioctl: %s", __func__, strerror(errno)); |
575 |
|
|
return (-1); |
576 |
|
|
} |
577 |
|
|
} |
578 |
|
|
|
579 |
|
|
inet_ntop(pnl.af, &pnl.rsaddr, ibuf, sizeof(ibuf)); |
580 |
|
|
inet_ntop(pnl.af, &pnl.rdaddr, obuf, sizeof(obuf)); |
581 |
|
|
log_debug("%s: %s %s:%d -> %s:%d", __func__, |
582 |
|
|
pnl.direction == PF_IN ? "in" : "out", |
583 |
|
|
ibuf, ntohs(pnl.rsport), obuf, ntohs(pnl.rdport)); |
584 |
|
|
|
585 |
|
|
switch (pnl.af) { |
586 |
|
|
case AF_INET: |
587 |
|
|
in = (struct sockaddr_in *)&cnl->rsrc; |
588 |
|
|
out = (struct sockaddr_in *)&cnl->rdst; |
589 |
|
|
bcopy(&pnl.rsaddr.v4, &in->sin_addr, sizeof(in->sin_addr)); |
590 |
|
|
in->sin_port = pnl.rsport; |
591 |
|
|
bcopy(&pnl.rdaddr.v4, &out->sin_addr, sizeof(out->sin_addr)); |
592 |
|
|
out->sin_port = pnl.rdport; |
593 |
|
|
break; |
594 |
|
|
case AF_INET6: |
595 |
|
|
in6 = (struct sockaddr_in6 *)&cnl->rsrc; |
596 |
|
|
out6 = (struct sockaddr_in6 *)&cnl->rdst; |
597 |
|
|
bcopy(&pnl.rsaddr.v6, &in6->sin6_addr, sizeof(in6->sin6_addr)); |
598 |
|
|
bcopy(&pnl.rdaddr.v6, &out6->sin6_addr, |
599 |
|
|
sizeof(out6->sin6_addr)); |
600 |
|
|
break; |
601 |
|
|
} |
602 |
|
|
cnl->rsrc.ss_family = pnl.af; |
603 |
|
|
cnl->rdst.ss_family = pnl.af; |
604 |
|
|
cnl->rsport = pnl.rsport; |
605 |
|
|
cnl->rdport = pnl.rdport; |
606 |
|
|
|
607 |
|
|
return (0); |
608 |
|
|
} |
609 |
|
|
|
610 |
|
|
u_int64_t |
611 |
|
|
check_table(struct relayd *env, struct rdr *rdr, struct table *table) |
612 |
|
|
{ |
613 |
|
|
struct pfioc_table io; |
614 |
|
|
struct pfr_tstats tstats; |
615 |
|
|
|
616 |
|
|
if (table == NULL) |
617 |
|
|
return (0); |
618 |
|
|
|
619 |
|
|
bzero(&io, sizeof(io)); |
620 |
|
|
io.pfrio_esize = sizeof(struct pfr_tstats); |
621 |
|
|
io.pfrio_size = 1; |
622 |
|
|
io.pfrio_buffer = &tstats; |
623 |
|
|
if (strlcpy(io.pfrio_table.pfrt_anchor, RELAYD_ANCHOR "/", |
624 |
|
|
sizeof(io.pfrio_table.pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE) |
625 |
|
|
goto toolong; |
626 |
|
|
if (strlcat(io.pfrio_table.pfrt_anchor, rdr->conf.name, |
627 |
|
|
sizeof(io.pfrio_table.pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE) |
628 |
|
|
goto toolong; |
629 |
|
|
if (strlcpy(io.pfrio_table.pfrt_name, rdr->conf.name, |
630 |
|
|
sizeof(io.pfrio_table.pfrt_name)) >= |
631 |
|
|
sizeof(io.pfrio_table.pfrt_name)) |
632 |
|
|
goto toolong; |
633 |
|
|
|
634 |
|
|
if (ioctl(env->sc_pf->dev, DIOCRGETTSTATS, &io) == -1) |
635 |
|
|
fatal("%s: cannot get table stats", __func__); |
636 |
|
|
|
637 |
|
|
return (tstats.pfrts_match); |
638 |
|
|
|
639 |
|
|
toolong: |
640 |
|
|
fatal("%s: name too long", __func__); |
641 |
|
|
return (0); |
642 |
|
|
} |