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