1 /*
2 * scamper_firewall.c
3 *
4 * $Id: scamper_firewall.c,v 1.54 2020/03/17 07:32:16 mjl Exp $
5 *
6 * Copyright (C) 2008-2011 The University of Waikato
7 * Copyright (C) 2016 Matthew Luckie
8 * Author: Matthew Luckie
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, version 2.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 #include "internal.h"
29
30 #ifdef HAVE_NETINET_IP_FW_H
31 #include <netinet/ip_fw.h>
32 #ifdef HAVE_NETINET6_IP_FW_H
33 #include <netinet6/ip6_fw.h>
34 #endif
35 #endif
36
37 #ifdef HAVE_NET_PFVAR_H
38 #include <net/pfvar.h>
39 #endif
40
41 #include "scamper_addr.h"
42 #include "scamper_debug.h"
43 #include "scamper_firewall.h"
44 #include "scamper_privsep.h"
45 #include "scamper_osinfo.h"
46 #include "mjl_heap.h"
47 #include "mjl_splaytree.h"
48 #include "utils.h"
49
50 #if defined(HAVE_IPFW) || defined(HAVE_PF)
51
52 struct scamper_firewall_entry
53 {
54 int slot;
55 int refcnt;
56 splaytree_node_t *node;
57 scamper_firewall_rule_t *rule;
58 };
59
60 static splaytree_t *entries = NULL;
61 static heap_t *freeslots = NULL;
62
firewall_rule_cmp(const scamper_firewall_rule_t * a,const scamper_firewall_rule_t * b)63 static int firewall_rule_cmp(const scamper_firewall_rule_t *a,
64 const scamper_firewall_rule_t *b)
65 {
66 int i;
67
68 assert(a->type == SCAMPER_FIREWALL_RULE_TYPE_5TUPLE);
69 assert(b->type == SCAMPER_FIREWALL_RULE_TYPE_5TUPLE);
70
71 if(a->type < b->type) return -1;
72 if(a->type > b->type) return 1;
73
74 if(a->type == SCAMPER_FIREWALL_RULE_TYPE_5TUPLE)
75 {
76 if(a->sfw_5tuple_proto < b->sfw_5tuple_proto) return -1;
77 if(a->sfw_5tuple_proto > b->sfw_5tuple_proto) return 1;
78
79 if(a->sfw_5tuple_sport < b->sfw_5tuple_sport) return -1;
80 if(a->sfw_5tuple_sport > b->sfw_5tuple_sport) return 1;
81
82 if(a->sfw_5tuple_dport < b->sfw_5tuple_dport) return -1;
83 if(a->sfw_5tuple_dport > b->sfw_5tuple_dport) return 1;
84
85 if((i = scamper_addr_cmp(a->sfw_5tuple_src, b->sfw_5tuple_src)) != 0)
86 return i;
87
88 if(a->sfw_5tuple_dst == NULL && b->sfw_5tuple_dst == NULL)
89 return 0;
90 if(a->sfw_5tuple_dst != NULL && b->sfw_5tuple_dst == NULL)
91 return -1;
92 if(a->sfw_5tuple_dst == NULL && b->sfw_5tuple_dst != NULL)
93 return 1;
94
95 return scamper_addr_cmp(a->sfw_5tuple_dst, b->sfw_5tuple_dst);
96 }
97
98 return 0;
99 }
100
firewall_rule_free(scamper_firewall_rule_t * sfw)101 static void firewall_rule_free(scamper_firewall_rule_t *sfw)
102 {
103 if(sfw == NULL)
104 return;
105
106 if(sfw->sfw_5tuple_src != NULL)
107 scamper_addr_free(sfw->sfw_5tuple_src);
108 if(sfw->sfw_5tuple_dst != NULL)
109 scamper_addr_free(sfw->sfw_5tuple_dst);
110 free(sfw);
111
112 return;
113 }
114
firewall_rule_dup(scamper_firewall_rule_t * sfw)115 static scamper_firewall_rule_t *firewall_rule_dup(scamper_firewall_rule_t *sfw)
116 {
117 scamper_firewall_rule_t *dup;
118
119 if((dup = memdup(sfw, sizeof(scamper_firewall_rule_t))) == NULL)
120 return NULL;
121
122 scamper_addr_use(dup->sfw_5tuple_src);
123 if(dup->sfw_5tuple_dst != NULL)
124 scamper_addr_use(dup->sfw_5tuple_dst);
125
126 return dup;
127 }
128
firewall_entry_cmp(const scamper_firewall_entry_t * a,const scamper_firewall_entry_t * b)129 static int firewall_entry_cmp(const scamper_firewall_entry_t *a,
130 const scamper_firewall_entry_t *b)
131 {
132 return firewall_rule_cmp(a->rule, b->rule);
133 }
134
135 /*
136 * firewall_entry_slot_cmp
137 *
138 * provide ordering for the freeslots heap by returning the earliest available
139 * slot number in a range
140 */
firewall_entry_slot_cmp(const scamper_firewall_entry_t * a,const scamper_firewall_entry_t * b)141 static int firewall_entry_slot_cmp(const scamper_firewall_entry_t *a,
142 const scamper_firewall_entry_t *b)
143 {
144 if(a->slot > b->slot) return -1;
145 if(a->slot < b->slot) return 1;
146 return 0;
147 }
148
149 /*
150 * firewall_entry_free
151 *
152 */
firewall_entry_free(scamper_firewall_entry_t * entry)153 static void firewall_entry_free(scamper_firewall_entry_t *entry)
154 {
155 if(entry->node != NULL)
156 splaytree_remove_node(entries, entry->node);
157 firewall_rule_free(entry->rule);
158 free(entry);
159 return;
160 }
161
firewall_freeslots_alloc(long start,long end)162 static int firewall_freeslots_alloc(long start, long end)
163 {
164 scamper_firewall_entry_t *entry;
165 long i;
166
167 if((freeslots = heap_alloc((heap_cmp_t)firewall_entry_slot_cmp)) == NULL)
168 {
169 printerror(__func__, "could not create freeslots heap");
170 return -1;
171 }
172
173 for(i=start; i<=end; i++)
174 {
175 if((entry = malloc_zero(sizeof(scamper_firewall_entry_t))) == NULL)
176 {
177 printerror(__func__, "could not alloc entry %d", i);
178 return -1;
179 }
180 entry->slot = i;
181 if(heap_insert(freeslots, entry) == NULL)
182 {
183 printerror(__func__, "could not add entry %d", i);
184 return -1;
185 }
186 }
187
188 return 0;
189 }
190
firewall_entries_alloc(void)191 static int firewall_entries_alloc(void)
192 {
193 if((entries = splaytree_alloc((splaytree_cmp_t)firewall_entry_cmp)) == NULL)
194 {
195 printerror(__func__, "could not create entries tree");
196 return -1;
197 }
198 return 0;
199 }
200 #endif /* HAVE_IPFW || HAVE_PF */
201
202 #ifdef HAVE_IPFW
203 /* variables to keep state with ipfw, which is rule-number based */
204 static int ipfw_use = 0;
205 static int ipfw_inited = 0;
206 static int ipfw_have_ipv6 = 0;
207 static int ipfw_have_ipv4 = 0;
208
ipfw_sysctl_check(void)209 static int ipfw_sysctl_check(void)
210 {
211 const scamper_osinfo_t *osinfo;
212 size_t len;
213 char *name;
214 int i;
215
216 len = sizeof(i);
217 name = "net.inet.ip.fw.enable";
218 if(sysctlbyname(name, &i, &len, NULL, 0) != 0)
219 {
220 printerror(__func__, "could not sysctl %s", name);
221 return -1;
222 }
223 else
224 {
225 if(i != 0)
226 ipfw_have_ipv4 = 1;
227 else
228 scamper_debug(__func__, "ipfw ipv4 not enabled");
229 }
230
231 len = sizeof(i);
232 name = "net.inet6.ip6.fw.enable";
233 if(sysctlbyname(name, &i, &len, NULL, 0) != 0)
234 {
235 printerror(__func__, "could not sysctl %s", name);
236 if(errno != ENOENT)
237 return -1;
238
239 /*
240 * check if the system is known to not have a separate sysctl for
241 * ipv6 ipfw.
242 */
243 i = 0;
244 osinfo = scamper_osinfo_get();
245 if((osinfo->os_id == SCAMPER_OSINFO_OS_FREEBSD &&
246 osinfo->os_rel_dots >= 2 &&
247 osinfo->os_rel[0] == 6 && osinfo->os_rel[1] < 3) ||
248 (osinfo->os_id == SCAMPER_OSINFO_OS_DARWIN &&
249 osinfo->os_rel_dots > 0 && osinfo->os_rel[0] == 8))
250 {
251 ipfw_have_ipv6 = ipfw_have_ipv4;
252 }
253 else i++;
254
255 if(i != 0)
256 return -1;
257 }
258 else
259 {
260 if(i != 0)
261 ipfw_have_ipv6 = 1;
262 else
263 scamper_debug(__func__, "ipfw ipv6 not enabled");
264 }
265
266 return 0;
267 }
268
269 #ifdef _IPFW2_H
270 static int fws = -1;
271
scamper_firewall_ipfw_init(void)272 int scamper_firewall_ipfw_init(void)
273 {
274 if(fws != -1 || ipfw_sysctl_check() != 0 || ipfw_inited != 0)
275 return -1;
276
277 if((fws = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
278 {
279 printerror(__func__, "could not open socket for ipfw");
280 return -1;
281 }
282
283 ipfw_inited = 1;
284 return 0;
285 }
286
scamper_firewall_ipfw_cleanup(void)287 void scamper_firewall_ipfw_cleanup(void)
288 {
289 if(fws != -1)
290 {
291 close(fws);
292 fws = -1;
293 }
294 return;
295 }
296
297 #if __FreeBSD_version >= 600000
ipfw_deny_ip6_ext6hdr_frag(int n,void * s,void * d)298 static int ipfw_deny_ip6_ext6hdr_frag(int n, void *s, void *d)
299 {
300 struct ip_fw *fw = NULL;
301 socklen_t sl;
302 uint16_t insnc;
303 ipfw_insn_ip6 *insn_ip6;
304 ipfw_insn *insn;
305
306 insnc = 1 + 5 + 1 + 1;
307
308 if(d != NULL)
309 insnc += 5;
310 else
311 insnc += 1;
312
313 sl = sizeof(struct ip_fw) + (insnc*4) - 4;
314
315 if((fw = malloc_zero(sl)) == NULL)
316 {
317 printerror(__func__, "could not malloc ip_fw");
318 goto err;
319 }
320
321 fw->rulenum = n;
322 fw->act_ofs = insnc-1;
323 fw->cmd_len = insnc;
324 insn = fw->cmd;
325
326 insn->opcode = O_IP6;
327 insn->len = 1;
328 insn += insn->len;
329
330 insn_ip6 = (ipfw_insn_ip6 *)insn;
331 insn->opcode = O_IP6_SRC;
332 insn->len = 5;
333 memcpy(&insn_ip6->addr6, s, 16);
334 insn += insn->len;
335
336 if(d != NULL)
337 {
338 insn_ip6 = (ipfw_insn_ip6 *)insn;
339 insn->opcode = O_IP6_DST;
340 insn->len = 5;
341 memcpy(&insn_ip6->addr6, d, 16);
342 }
343 else
344 {
345 insn->opcode = O_IP6_DST_ME;
346 insn->len = 1;
347 }
348 insn += insn->len;
349
350 insn->opcode = O_EXT_HDR;
351 insn->len = 1;
352 insn->arg1 = EXT_FRAGMENT;
353 insn += insn->len;
354
355 insn->opcode = O_DENY;
356 insn->len = 1;
357
358 if(getsockopt(fws, IPPROTO_IP, IP_FW_ADD, fw, &sl) != 0)
359 {
360 printerror(__func__, "could not add rule");
361 goto err;
362 }
363
364 free(fw);
365 return 0;
366
367 err:
368 if(fw != NULL) free(fw);
369 return -1;
370 }
371 #endif
372
scamper_firewall_ipfw_add(int n,int af,int p,void * s,void * d,int sp,int dp)373 int scamper_firewall_ipfw_add(int n,int af,int p,void *s,void *d,int sp,int dp)
374 {
375 ipfw_insn_u32 *insn_u32;
376 ipfw_insn_u16 *insn_u16;
377 struct ip_fw *fw = NULL;
378 ipfw_insn *insn;
379 socklen_t sl;
380 size_t len;
381
382 #if __FreeBSD_version >= 600000
383 ipfw_insn_ip6 *insn_ip6;
384 #endif
385
386 /*
387 * build ip_fw struct
388 *
389 * note that the ip_fw struct has one member reserved for
390 * the first instruction, so that is not counted here
391 */
392 len = 2 + 2 + 1; /* O_PROTO + O_IP_SRCPORT + O_IP_DSTPORT + O_DENY */
393
394 if(af == AF_INET)
395 len += 2; /* O_IP_SRC */
396 else if(af == AF_INET6)
397 len += 5; /* O_IP6_SRC */
398 else
399 goto err;
400
401 if(d == NULL)
402 len += 1; /* O_IP_DST_ME -or- O_IP6_DST_ME */
403 else if(af == AF_INET)
404 len += 2; /* O_IP_DST */
405 else if(af == AF_INET6)
406 len += 5; /* O_IP6_DST */
407 else
408 goto err;
409
410 if((fw = malloc_zero(sizeof(struct ip_fw) + (len * 4))) == NULL)
411 {
412 printerror(__func__, "could not malloc ip_fw");
413 goto err;
414 }
415 sl = sizeof(struct ip_fw) + (len * 4);
416
417 #if defined(__APPLE__)
418 fw->version = IP_FW_CURRENT_API_VERSION;
419 #endif
420
421 fw->rulenum = n;
422 fw->act_ofs = len;
423 fw->cmd_len = len+1;
424 insn = fw->cmd;
425
426 /* encode the O_PROTO parameter */
427 insn->opcode = O_PROTO;
428 insn->len = 1;
429 insn->arg1 = p;
430 insn += insn->len;
431
432 /* encode the O_IP_SRC parameter */
433 if(af == AF_INET)
434 {
435 insn_u32 = (ipfw_insn_u32 *)insn;
436 memcpy(insn_u32->d, s, 4);
437 insn->opcode = O_IP_SRC;
438 insn->len = 2;
439 }
440 #if __FreeBSD_version >= 600000
441 else if(af == AF_INET6)
442 {
443 insn_ip6 = (ipfw_insn_ip6 *)insn;
444 memcpy(&insn_ip6->addr6, s, 16);
445 insn->opcode = O_IP6_SRC;
446 insn->len = 5;
447 }
448 #endif
449 else
450 goto err;
451
452 insn += insn->len;
453
454 /* encode the O_IP_SRCPORT parameter */
455 insn_u16 = (ipfw_insn_u16 *)insn;
456 insn->opcode = O_IP_SRCPORT;
457 insn->len = 2;
458 insn_u16->ports[0] = sp;
459 insn_u16->ports[1] = sp;
460 insn += insn->len;
461
462 /* encode the O_IP_DST parameter */
463 if(d == NULL)
464 {
465 if(af == AF_INET)
466 insn->opcode = O_IP_DST_ME;
467 #if __FreeBSD_version >= 600000
468 else if(af == AF_INET6)
469 insn->opcode = O_IP6_DST_ME;
470 #endif
471 else
472 goto err;
473 insn->len = 1;
474 }
475 else if(af == AF_INET)
476 {
477 insn_u32 = (ipfw_insn_u32 *)insn;
478 memcpy(insn_u32->d, d, 4);
479 insn->opcode = O_IP_DST;
480 insn->len = 2;
481 }
482 #if __FreeBSD_version >= 600000
483 else if(af == AF_INET6)
484 {
485 insn_ip6 = (ipfw_insn_ip6 *)insn;
486 memcpy(&insn_ip6->addr6, d, 16);
487 insn->opcode = O_IP6_DST;
488 insn->len = 5;
489 }
490 #endif
491 else
492 goto err;
493 insn += insn->len;
494
495 /* encode the O_IP_DSTPORT parameter */
496 insn_u16 = (ipfw_insn_u16 *)insn;
497 insn->opcode = O_IP_DSTPORT;
498 insn->len = 2;
499 insn_u16->ports[0] = dp;
500 insn_u16->ports[1] = dp;
501 insn += insn->len;
502
503 /* encode the O_DENY action */
504 insn->opcode = O_DENY;
505 insn->len = 1;
506
507 if(getsockopt(fws, IPPROTO_IP, IP_FW_ADD, fw, &sl) != 0)
508 {
509 printerror(__func__, "could not add rule");
510 goto err;
511 }
512
513 free(fw);
514
515 #if __FreeBSD_version >= 600000
516 if(af == AF_INET6)
517 ipfw_deny_ip6_ext6hdr_frag(n, s, d);
518 #endif
519
520 return 0;
521
522 err:
523 if(fw != NULL) free(fw);
524 return -1;
525 }
526
scamper_firewall_ipfw_del(int n,int af)527 int scamper_firewall_ipfw_del(int n, int af)
528 {
529 uint32_t rule = n;
530
531 if(setsockopt(fws, IPPROTO_IP, IP_FW_DEL, &rule, sizeof(rule)) != 0)
532 {
533 printerror(__func__, "could not delete rule %d", n);
534 return -1;
535 }
536
537 return 0;
538 }
539 #endif /* _IPFW2_H */
540
541 #if defined(_IP_FW_H) || defined(__APPLE__)
542 static int fw4s = -1;
543 static int fw6s = -1;
544
scamper_firewall_ipfw_init(void)545 int scamper_firewall_ipfw_init(void)
546 {
547 if(fw4s != -1 || fw6s != -1 || ipfw_sysctl_check() != 0 || ipfw_inited != 0)
548 return -1;
549
550 if(ipfw_have_ipv4 != 0 && (fw4s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
551 {
552 printerror(__func__, "could not open socket for ipfw");
553 return -1;
554 }
555 if(ipfw_have_ipv6 != 0 && (fw6s = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW)) < 0)
556 {
557 printerror(__func__, "could not open socket for ip6fw");
558 return -1;
559 }
560
561 ipfw_inited = 1;
562 return 0;
563 }
564
scamper_firewall_ipfw_cleanup(void)565 void scamper_firewall_ipfw_cleanup(void)
566 {
567 if(fw4s != -1)
568 {
569 close(fw4s);
570 fw4s = -1;
571 }
572 if(fw6s != -1)
573 {
574 close(fw6s);
575 fw6s = -1;
576 }
577 return;
578 }
579
scamper_firewall_ipfw_add(int n,int af,int p,void * s,void * d,int sp,int dp)580 int scamper_firewall_ipfw_add(int n,int af,int p,void *s,void *d,int sp,int dp)
581 {
582 struct ip_fw fw;
583 struct ip6_fw fw6;
584 int level, optname;
585 void *optval;
586 socklen_t optlen;
587 int i, fd;
588
589 if(af == AF_INET)
590 {
591 memset(&fw, 0, sizeof(fw));
592 fw.fw_number = n;
593 fw.fw_flg = IP_FW_F_DENY | IP_FW_F_IN;
594 fw.fw_prot = p;
595 memcpy(&fw.fw_src, s, 4);
596 fw.fw_smsk.s_addr = ~0;
597 memcpy(&fw.fw_dst, d, 4);
598 fw.fw_dmsk.s_addr = ~0;
599 fw.fw_uar.fw_pts[0] = sp;
600 IP_FW_SETNSRCP(&fw, 1);
601 fw.fw_uar.fw_pts[1] = dp;
602 IP_FW_SETNDSTP(&fw, 1);
603
604 #ifdef __APPLE__
605 fw.version = IP_FW_CURRENT_API_VERSION;
606 #endif
607
608 level = IPPROTO_IP;
609 optname = IP_FW_ADD;
610 optval = &fw;
611 optlen = sizeof(fw);
612 fd = fw4s;
613 }
614 else if(af == AF_INET6)
615 {
616 memset(&fw6, 0, sizeof(fw6));
617 fw6.fw_number = n;
618 fw6.fw_flg = IPV6_FW_F_DENY | IPV6_FW_F_IN;
619 fw6.fw_prot = p;
620 memcpy(&fw6.fw_src, s, 16);
621 for(i=0; i<4; i++)
622 fw6.fw_smsk.s6_addr32[i] = ~0;
623 memcpy(&fw6.fw_dst, d, 16);
624 for(i=0; i<4; i++)
625 fw6.fw_dmsk.s6_addr32[i] = ~0;
626 fw6.fw_pts[0] = sp;
627 IPV6_FW_SETNSRCP(&fw6, 1);
628 fw6.fw_pts[1] = dp;
629 IPV6_FW_SETNDSTP(&fw6, 1);
630
631 #ifdef __APPLE__
632 fw6.version = IPV6_FW_CURRENT_API_VERSION;
633 #endif
634
635 level = IPPROTO_IPV6;
636 optname = IPV6_FW_ADD;
637 optval = &fw6;
638 optlen = sizeof(fw6);
639 fd = fw6s;
640 }
641 else return -1;
642
643 if(setsockopt(fd, level, optname, optval, optlen) != 0)
644 {
645 printerror(__func__, "could not add fw rule");
646 return -1;
647 }
648
649 return 0;
650 }
651
scamper_firewall_ipfw_del(int n,int af)652 int scamper_firewall_ipfw_del(int n, int af)
653 {
654 struct ip_fw fw;
655 struct ip6_fw fw6;
656 int level, optname;
657 void *optval;
658 socklen_t optlen;
659 int fd;
660
661 if(af == AF_INET)
662 {
663 memset(&fw, 0, sizeof(fw));
664 fw.fw_number = n;
665 #ifdef __APPLE__
666 fw.version = IP_FW_CURRENT_API_VERSION;
667 #endif
668
669 level = IPPROTO_IP;
670 optname = IP_FW_DEL;
671 optval = &fw;
672 optlen = sizeof(fw);
673 fd = fw4s;
674 }
675 else if(af == AF_INET6)
676 {
677 memset(&fw6, 0, sizeof(fw6));
678 fw6.fw_number = n;
679 #ifdef __APPLE__
680 fw6.version = IPV6_FW_CURRENT_API_VERSION;
681 #endif
682
683 level = IPPROTO_IPV6;
684 optname = IPV6_FW_DEL;
685 optval = &fw6;
686 optlen = sizeof(fw6);
687 fd = fw6s;
688 }
689 else
690 {
691 return -1;
692 }
693
694 if(setsockopt(fd, level, optname, optval, optlen) != 0)
695 {
696 printerror(__func__, "could not delete rule %d", n);
697 return -1;
698 }
699
700 return 0;
701 }
702 #endif /* _IPFW_H */
703
ipfw_init(char * opts)704 static int ipfw_init(char *opts)
705 {
706 long start, end;
707 char *ptr;
708
709 if(opts == NULL)
710 {
711 scamper_debug(__func__, "no IPFW configuration parameters supplied");
712 return -1;
713 }
714
715 string_nullterm_char(opts, '-', &ptr);
716 if(ptr == NULL || string_isnumber(opts) == 0 || string_isnumber(ptr) == 0)
717 {
718 scamper_debug(__func__, "invalid IFPW options");
719 return -1;
720 }
721 if(string_tolong(opts, &start) != 0 || start < 1 || start > 65534)
722 {
723 scamper_debug(__func__, "invalid start rule number for IPFW");
724 return -1;
725 }
726 if(string_tolong(ptr, &end) != 0 || end <= start || end > 65534)
727 {
728 scamper_debug(__func__, "invalid end rule number for IPFW");
729 return -1;
730 }
731
732 if(ipfw_sysctl_check() != 0)
733 return -1;
734
735 if(firewall_entries_alloc() != 0)
736 return -1;
737
738 if(firewall_freeslots_alloc(start, end) != 0)
739 return -1;
740
741 #ifdef WITHOUT_PRIVSEP
742 if(scamper_firewall_ipfw_init() != 0)
743 return -1;
744 #else
745 if(scamper_privsep_ipfw_init() != 0)
746 return -1;
747 #endif
748
749 ipfw_use = 1;
750 return 0;
751 }
752
ipfw_add(int ruleno,scamper_firewall_rule_t * sfw)753 static int ipfw_add(int ruleno, scamper_firewall_rule_t *sfw)
754 {
755 int af, p, sp, dp;
756 void *s, *d;
757
758 assert(ipfw_use != 0);
759
760 if(sfw->sfw_5tuple_src->type == SCAMPER_ADDR_TYPE_IPV4)
761 {
762 if(ipfw_have_ipv4 == 0)
763 {
764 scamper_debug(__func__, "IPv4 rule requested but no IPv4 firewall");
765 return -1;
766 }
767 af = AF_INET;
768 }
769 else if(sfw->sfw_5tuple_src->type == SCAMPER_ADDR_TYPE_IPV6)
770 {
771 if(ipfw_have_ipv6 == 0)
772 {
773 scamper_debug(__func__, "IPv6 rule requested but no IPv6 firewall");
774 return -1;
775 }
776 af = AF_INET6;
777 }
778 else
779 {
780 scamper_debug(__func__, "invalid src type");
781 return -1;
782 }
783
784 p = sfw->sfw_5tuple_proto;
785 dp = sfw->sfw_5tuple_dport;
786 sp = sfw->sfw_5tuple_sport;
787 s = sfw->sfw_5tuple_src->addr;
788 if(sfw->sfw_5tuple_dst == NULL)
789 d = NULL;
790 else
791 d = sfw->sfw_5tuple_dst->addr;
792
793 #ifdef WITHOUT_PRIVSEP
794 if(scamper_firewall_ipfw_add(ruleno, af, p, s, d, sp, dp) != 0)
795 return -1;
796 #else
797 if(scamper_privsep_ipfw_add(ruleno, af, p, s, d, sp, dp) != 0)
798 return -1;
799 #endif
800
801 return 0;
802 }
803
ipfw_del(const scamper_firewall_entry_t * entry)804 int ipfw_del(const scamper_firewall_entry_t *entry)
805 {
806 int af = scamper_addr_af(entry->rule->sfw_5tuple_src);
807 #ifdef WITHOUT_PRIVSEP
808 return scamper_firewall_ipfw_del(entry->slot, af);
809 #else
810 return scamper_privsep_ipfw_del(entry->slot, af);
811 #endif
812 }
813
814 #endif /* HAVE_IPFW */
815
816 #ifdef HAVE_PF
817 static int pf_use = 0;
818 static int pf_inited = 0;
819 static int pf_fd = -1;
820 static pid_t pf_pid = 0;
821 static char *pf_name = NULL;
822
823 /*
824 * scamper_firewall_pf_init
825 *
826 * code that can be called both inside scamper_firewall.c and
827 * scamper_privsep.c for initalising the PF firewall.
828 */
scamper_firewall_pf_init(const char * name)829 int scamper_firewall_pf_init(const char *name)
830 {
831 struct pf_status status;
832 size_t len;
833
834 if(pf_fd != -1 || pf_inited != 0)
835 return -1;
836 if((len = strlen(name)) == 0 || string_isprint(name, len) == 0)
837 return -1;
838
839 if((pf_name = strdup(name)) == NULL)
840 {
841 printerror(__func__, "could not dup name");
842 return -1;
843 }
844
845 if((pf_fd = open("/dev/pf", O_RDWR)) == -1)
846 {
847 printerror(__func__, "could not open socket");
848 return -1;
849 }
850
851 if(ioctl(pf_fd, DIOCGETSTATUS, &status) == -1)
852 {
853 printerror(__func__, "could not get status");
854 return -1;
855 }
856 if(status.running == 0)
857 {
858 scamper_debug(__func__, "pf not running");
859 return -1;
860 }
861
862 pf_pid = getpid();
863 pf_inited = 1;
864 return 0;
865 }
866
scamper_firewall_pf_cleanup(void)867 void scamper_firewall_pf_cleanup(void)
868 {
869 if(pf_fd != -1)
870 {
871 close(pf_fd);
872 pf_fd = -1;
873 }
874 if(pf_name != NULL)
875 {
876 free(pf_name);
877 pf_name = NULL;
878 }
879 return;
880 }
881
scamper_firewall_pf_add(int n,int af,int p,void * s,void * d,int sp,int dp)882 int scamper_firewall_pf_add(int n,int af,int p,void *s,void *d,int sp,int dp)
883 {
884 char anchor[PF_ANCHOR_NAME_SIZE];
885 struct pfioc_trans_e pfte;
886 struct pfioc_trans pft;
887 struct pfioc_rule pfr;
888 size_t off;
889
890 off = 0;
891 string_concat(anchor, sizeof(anchor), &off, "%s/%d.%d", pf_name, pf_pid, n);
892
893 memset(&pft, 0, sizeof(pft));
894 pft.size = 1;
895 pft.esize = sizeof(pfte);
896 pft.array = &pfte;
897 memset(&pfte, 0, sizeof(pfte));
898 strncpy(pfte.anchor, anchor, sizeof(pfte.anchor)-1);
899 pfte.anchor[sizeof(pfte.anchor)-1] = '\0';
900
901 #if defined(HAVE_STRUCT_PFIOC_TRANS_E_TYPE)
902 pfte.type = PF_TRANS_RULESET;
903 #endif
904 #if defined(HAVE_STRUCT_PFIOC_TRANS_E_RS_NUM)
905 pfte.rs_num = PF_RULESET_FILTER;
906 #endif
907
908 if(ioctl(pf_fd, DIOCXBEGIN, &pft) == -1)
909 {
910 printerror(__func__, "could not begin transaction");
911 return -1;
912 }
913
914 memset(&pfr, 0, sizeof(pfr));
915 strncpy(pfr.anchor, anchor, sizeof(pfr.anchor)-1);
916 pfte.anchor[sizeof(pfr.anchor)-1] = '\0';
917 pfr.ticket = pfte.ticket;
918 pfr.rule.af = af;
919 pfr.rule.proto = p;
920 pfr.rule.direction = PF_IN;
921 pfr.rule.src.addr.type = PF_ADDR_ADDRMASK;
922 pfr.rule.dst.addr.type = PF_ADDR_ADDRMASK;
923 #if defined(HAVE_STRUCT_PF_RULE_NAT)
924 pfr.rule.nat.addr.type = PF_ADDR_NONE;
925 #endif
926 #if defined(HAVE_STRUCT_PF_RULE_RDR)
927 pfr.rule.rdr.addr.type = PF_ADDR_NONE;
928 #endif
929
930 if(af == AF_INET)
931 {
932 memcpy(&pfr.rule.src.addr.v.a.addr.v4, s, 4);
933 memset(&pfr.rule.src.addr.v.a.mask.v4, 255, 4);
934 memcpy(&pfr.rule.dst.addr.v.a.addr.v4, d, 4);
935 memset(&pfr.rule.dst.addr.v.a.mask.v4, 255, 4);
936 }
937 else
938 {
939 memcpy(&pfr.rule.src.addr.v.a.addr.v6, s, 16);
940 memset(&pfr.rule.src.addr.v.a.mask.v6, 255, 16);
941 memcpy(&pfr.rule.src.addr.v.a.addr.v6, d, 16);
942 memset(&pfr.rule.dst.addr.v.a.mask.v6, 255, 16);
943 }
944
945 pfr.rule.src.port_op = PF_OP_EQ;
946 pfr.rule.src.port[0] = htons(sp);
947 pfr.rule.dst.port_op = PF_OP_EQ;
948 pfr.rule.dst.port[0] = htons(dp);
949 pfr.rule.action = PF_DROP;
950 pfr.rule.quick = 1;
951
952 if(ioctl(pf_fd, DIOCADDRULE, &pfr) == -1)
953 {
954 printerror(__func__, "could not add rule");
955 return -1;
956 }
957
958 if(ioctl(pf_fd, DIOCXCOMMIT, &pft) == -1)
959 {
960 printerror(__func__, "could not commit rule");
961 return -1;
962 }
963
964 return 0;
965 }
966
scamper_firewall_pf_del(int ruleno)967 int scamper_firewall_pf_del(int ruleno)
968 {
969 struct pfioc_trans_e pfte;
970 struct pfioc_trans pft;
971 size_t off;
972
973 memset(&pft, 0, sizeof(pft));
974 pft.size = 1;
975 pft.esize = sizeof(pfte);
976 pft.array = &pfte;
977 memset(&pfte, 0, sizeof(pfte));
978
979 off = 0;
980 string_concat(pfte.anchor, sizeof(pfte.anchor), &off,
981 "%s/%d.%d", pf_name,pf_pid,ruleno);
982
983 #if defined(HAVE_STRUCT_PFIOC_TRANS_E_TYPE)
984 pfte.type = PF_TRANS_RULESET;
985 #endif
986 #if defined(HAVE_STRUCT_PFIOC_TRANS_E_RS_NUM)
987 pfte.rs_num = PF_RULESET_FILTER;
988 #endif
989
990 if(ioctl(pf_fd, DIOCXBEGIN, &pft) == -1)
991 {
992 printerror(__func__, "could not begin transaction");
993 return -1;
994 }
995
996 if(ioctl(pf_fd, DIOCXCOMMIT, &pft) == -1)
997 {
998 printerror(__func__, "could not commit rule");
999 return -1;
1000 }
1001
1002 return 0;
1003 }
1004
pf_add(int n,scamper_firewall_rule_t * sfw)1005 static int pf_add(int n, scamper_firewall_rule_t *sfw)
1006 {
1007 int af, p, sp, dp;
1008 void *s, *d;
1009
1010 assert(pf_use != 0);
1011
1012 if(sfw->sfw_5tuple_src->type != SCAMPER_ADDR_TYPE_IPV4 &&
1013 sfw->sfw_5tuple_src->type != SCAMPER_ADDR_TYPE_IPV6)
1014 return -1;
1015
1016 af = scamper_addr_af(sfw->sfw_5tuple_src);
1017 p = sfw->sfw_5tuple_proto;
1018 dp = sfw->sfw_5tuple_dport;
1019 sp = sfw->sfw_5tuple_sport;
1020 s = sfw->sfw_5tuple_src->addr;
1021 d = sfw->sfw_5tuple_dst->addr;
1022
1023 #ifdef WITHOUT_PRIVSEP
1024 if(scamper_firewall_pf_add(n, af, p, s, d, sp, dp) != 0)
1025 return -1;
1026 #else
1027 if(scamper_privsep_pf_add(n, af, p, s, d, sp, dp) != 0)
1028 return -1;
1029 #endif
1030 return 0;
1031 }
1032
pf_del(int n)1033 static int pf_del(int n)
1034 {
1035 #ifdef WITHOUT_PRIVSEP
1036 return scamper_firewall_pf_del(n);
1037 #else
1038 return scamper_privsep_pf_del(n);
1039 #endif
1040 }
1041
pf_init(char * opts)1042 static int pf_init(char *opts)
1043 {
1044 char *name_str, *num_str;
1045 long num;
1046
1047 name_str = opts;
1048 string_nullterm_char(opts, ':', &num_str);
1049 if(strlen(name_str) < 1)
1050 {
1051 scamper_debug(__func__, "invalid name");
1052 return -1;
1053 }
1054 if(num_str == NULL)
1055 {
1056 scamper_debug(__func__, "missing number");
1057 return -1;
1058 }
1059
1060 if(string_isnumber(num_str) == 0 ||
1061 string_tolong(num_str, &num) != 0 || num < 1 || num > 65535)
1062 {
1063 scamper_debug(__func__, "%s is not a valid number", num_str);
1064 return -1;
1065 }
1066
1067 if(firewall_entries_alloc() != 0)
1068 return -1;
1069
1070 if(firewall_freeslots_alloc(1, num) != 0)
1071 return -1;
1072
1073 #ifdef WITHOUT_PRIVSEP
1074 if(scamper_firewall_pf_init(name_str) != 0)
1075 return -1;
1076 #else
1077 if(scamper_privsep_pf_init(name_str) != 0)
1078 return -1;
1079 #endif
1080
1081 pf_use = 1;
1082 return 0;
1083 }
1084 #endif /* HAVE_PF */
1085
scamper_firewall_entry_get(scamper_firewall_rule_t * sfw)1086 scamper_firewall_entry_t *scamper_firewall_entry_get(scamper_firewall_rule_t *sfw)
1087 {
1088 #if defined(HAVE_IPFW) || defined(HAVE_PF)
1089 scamper_firewall_entry_t findme, *entry = NULL;
1090
1091 /* sanity check the rule */
1092 if((sfw->sfw_5tuple_proto != IPPROTO_TCP &&
1093 sfw->sfw_5tuple_proto != IPPROTO_UDP) ||
1094 sfw->sfw_5tuple_sport == 0 ||
1095 sfw->sfw_5tuple_dport == 0 ||
1096 (sfw->sfw_5tuple_dst == NULL || sfw->sfw_5tuple_src == NULL ||
1097 sfw->sfw_5tuple_src->type != sfw->sfw_5tuple_dst->type))
1098 {
1099 scamper_debug(__func__, "invalid 5tuple rule");
1100 goto err;
1101 }
1102
1103 findme.rule = sfw;
1104 if((entry = splaytree_find(entries, &findme)) != NULL)
1105 {
1106 entry->refcnt++;
1107 return entry;
1108 }
1109
1110 if((entry = heap_remove(freeslots)) == NULL)
1111 goto err;
1112
1113 entry->refcnt = 1;
1114 if((entry->rule = firewall_rule_dup(sfw)) == NULL ||
1115 (entry->node = splaytree_insert(entries, entry)) == NULL)
1116 {
1117 goto err;
1118 }
1119
1120 #ifdef HAVE_IPFW
1121 if(ipfw_use != 0)
1122 {
1123 if(ipfw_add(entry->slot, sfw) != 0)
1124 goto err;
1125 return entry;
1126 }
1127 #endif
1128
1129 #ifdef HAVE_PF
1130 if(pf_use != 0)
1131 {
1132 if(pf_add(entry->slot, sfw) != 0)
1133 goto err;
1134 return entry;
1135 }
1136 #endif
1137
1138 err:
1139 if(entry != NULL)
1140 {
1141 if(entry->rule != NULL)
1142 firewall_rule_free(entry->rule);
1143 free(entry);
1144 }
1145
1146 #endif /* HAVE_IPFW || HAVE_PF */
1147
1148 return NULL;
1149 }
1150
1151 #if defined(HAVE_IPFW) || defined(HAVE_PF)
firewall_rule_delete(scamper_firewall_entry_t * entry)1152 static int firewall_rule_delete(scamper_firewall_entry_t *entry)
1153 {
1154 #if defined(HAVE_IPFW)
1155 if(ipfw_use != 0)
1156 {
1157 if(ipfw_del(entry) != 0)
1158 return -1;
1159 goto done;
1160 }
1161 #endif
1162
1163 #if defined(HAVE_PF)
1164 if(pf_use != 0)
1165 {
1166 if(pf_del(entry->slot) != 0)
1167 return -1;
1168 goto done;
1169 }
1170 #endif
1171
1172 done:
1173 /* put the rule back into the freeslots heap */
1174 if(heap_insert(freeslots, entry) == NULL)
1175 {
1176 printerror(__func__, "could not add entry %d", entry->slot);
1177 return -1;
1178 }
1179
1180 /* free up the firewall rule associated with the entry */
1181 firewall_rule_free(entry->rule);
1182 entry->rule = NULL;
1183
1184 return 0;
1185 }
1186 #endif
1187
scamper_firewall_entry_free(scamper_firewall_entry_t * entry)1188 void scamper_firewall_entry_free(scamper_firewall_entry_t *entry)
1189 {
1190 #if defined(HAVE_IPFW) || defined(HAVE_PF)
1191 entry->refcnt--;
1192 if(entry->refcnt > 0)
1193 return;
1194
1195 /* remove the entry from the tree */
1196 splaytree_remove_node(entries, entry->node);
1197 entry->node = NULL;
1198
1199 /*
1200 * if the entry is still loaded in the firewall, remove it now.
1201 * note that this code is to handle the case that scamper_firewall_cleanup
1202 * is called before this function is called.
1203 */
1204 if(entry->slot >= 0)
1205 firewall_rule_delete(entry);
1206 #endif
1207 return;
1208 }
1209
1210 #if defined(HAVE_IPFW) || defined(HAVE_PF)
firewall_entry_cleanup(void * param,scamper_firewall_entry_t * entry)1211 static int firewall_entry_cleanup(void *param, scamper_firewall_entry_t *entry)
1212 {
1213 firewall_rule_delete(entry);
1214 entry->slot = -1;
1215 return 0;
1216 }
1217 #endif
1218
scamper_firewall_cleanup(void)1219 void scamper_firewall_cleanup(void)
1220 {
1221 #if defined(HAVE_IPFW) || defined(HAVE_PF)
1222 scamper_firewall_entry_t *entry;
1223
1224 if(entries != NULL)
1225 splaytree_inorder(entries,(splaytree_inorder_t)firewall_entry_cleanup,NULL);
1226
1227 if(freeslots != NULL)
1228 {
1229 while((entry = heap_remove(freeslots)) != NULL)
1230 firewall_entry_free(entry);
1231 heap_free(freeslots, NULL);
1232 freeslots = NULL;
1233 }
1234
1235 if(entries != NULL)
1236 {
1237 splaytree_free(entries, NULL);
1238 entries = NULL;
1239 }
1240 #endif
1241
1242 #ifdef HAVE_IPFW
1243 if(ipfw_use != 0)
1244 {
1245 #ifdef WITHOUT_PRIVSEP
1246 scamper_firewall_ipfw_cleanup();
1247 #else
1248 if(ipfw_inited != 0)
1249 scamper_privsep_ipfw_cleanup();
1250 #endif
1251 }
1252 #endif
1253
1254 #ifdef HAVE_PF
1255 if(pf_use != 0)
1256 {
1257 #ifdef WITHOUT_PRIVSEP
1258 scamper_firewall_pf_cleanup();
1259 #else
1260 if(pf_inited != 0)
1261 scamper_privsep_pf_cleanup();
1262 #endif
1263 }
1264 #endif
1265
1266 return;
1267 }
1268
scamper_firewall_init(const char * opt)1269 int scamper_firewall_init(const char *opt)
1270 {
1271 #if defined(HAVE_IPFW) || defined(HAVE_PF)
1272 char *dup, *ptr;
1273 int rc = -1;
1274
1275 if((dup = strdup(opt)) == NULL)
1276 {
1277 printerror(__func__, "could not dup opt");
1278 return -1;
1279 }
1280 string_nullterm_char(dup, ':', &ptr);
1281
1282 #if defined(HAVE_IPFW)
1283 if(strcasecmp(dup, "ipfw") == 0)
1284 {
1285 rc = ipfw_init(ptr);
1286 goto done;
1287 }
1288 #endif
1289
1290 #if defined(HAVE_PF)
1291 if(strcasecmp(dup, "pf") == 0)
1292 {
1293 rc = pf_init(ptr);
1294 goto done;
1295 }
1296 #endif
1297
1298 done:
1299 free(dup);
1300 return rc;
1301
1302 #endif /* HAVE_IPFW || HAVE_PF */
1303 return -1;
1304 }
1305