public
Description: A rigorous set of firewall scripts for BSD ipfw, and Linux iptables
Homepage:
Clone URL: git://github.com/jwiegley/jw.firewall.git
jw.firewall / rc.firewall
100755 1136 lines (958 sloc) 38.659 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
#!/bin/bash
 
##############################################################################
#
# rc.firewall
#
# version 1.68 (2008/09/09) by John Wiegley <johnw@newartisans.com>
#
# This script takes a series of arguments describing the current network
# interfaces and the networks behind them. Basic usage is:
#
# rc.firewall [OPTIONS] [INTERFACES...]
#
# The list of INTERFACES specifies which interfaces and networks you know
# about, and their respective level of trust.
#
# NOTE: Interfaces/networks which are not mentioned are completely shut out
# and *not logged*. This means that if no interfaces are given, the effect is
# to shut out networking entirely, except for DHCP and certain types of ICMP.
# This is not a bad idea as a default after startup, until you know what your
# networking environment looks like.
#
# An INTERFACE may be just an interface name, in which case traffic is not
# filtered by a netmask. This is important for interfaces that will access
# addresses outside of their own range, such as those wishing to reach the
# Internet. If a network base address and mask is given after a colon, it
# specifies legal address ranges for that interface: this is used to check for
# spoofing and illegal addresses. If two colons are used, the network is
# considered "trusted" and additional traffic, such as Rendezvous, is allowed.
#
# INTERFACE EXAMPLES
#
# en1{0,0} en1 (Airport) accesses the Internet
# and uses priority packet queueing,
# but without rate limiting
# en0:192.168.0.0/24 Local network on en0: 192.168.0.x
# en0::192.168.0.0/16 Trusted network on en0: 192.168.x.x
# en0::192.168.0.0:255.255.0.0 Same as previous
# en0+mac::192.168.0.0/16 Trusted Mac network on en0
# en0+win::192.168.0.0/16 Trusted Windows network on en0
# en0+mac+win::192.168.0.0/16 Trusted mixed network on en0
#
# The same interface can appear multiple times, if you have several networks
# connected to it (such as having your router's local network, and the
# Internet, both visible over en1). NOTE: *In that case, always list the most
# specific interfaces/networks first*. This means that if you access both
# 192.168.0.0/24 and the Internet over en1, use this:
#
# rc.firewall en1::192.168.0.0/24 en1\{0,0\}
#
# The string {0,0} (escaped for the shell) specifies inbound and outbound
# bandwidth limits for public interfaces. A pair of zeros means bandwith
# isn't limited, but it *is* shaped -- under the assumption that resources
# probably *are* limited, you just don't want to specify an artifical number
# right now. If the "{IN,OUT}" isn't specified for an interface, it means
# neither limiting nor shaping is performed.
#
# NOTE: A deficiency of this script is that I limit/shape based on the
# interface, not the network; to compensate, traffic bound for internal
# networks is never limited or shaped.
 
# RECIPES
#
# 1. You're going to be gaming on a mixed local LAN tonight over Ethernet, but
# your Internet access will be via Wireless:
#
# rc.firewall --gaming en0\{0,0\}+mac+win::192.168.x.0/24 en1
#
# 2. You're at a local coffee shop, using wireless, and you'd like to be a
# good citizen (i.e., not saturate their network with your downloads), so
# you limit voluntarily rate-limit yourself to 512 Kbits down and 128 Kbits
# up:
#
# rc.firewall --stealth --blackhole en1\{512Kbits/s,128Kbits/s\}
#
# 3. You're at home, where you connect by wireless and have a Mac network,
# plus you leave your SSH port open:
#
# rc.firewall --trusted-tcp 22 en1+mac::192.168.x.0/24 en1\{0,0\}
#
# 4. You're running your secure little Mac at the cafe, same as #2, but you
# also have a Windows box running in VMware that you'd like to trust, and
# you have a VPN connection to your Windows-based office. Lastly, you
# sometimes route *all* traffic through the VPN, though not always:
#
# rc.firewall --stealth --blackhole \
#     en1\{512Kbits/s,128Kbits/s\} \
#     vmnet8+win::192.168.36.0/24 \
#     tap0\{500Kbits/s,118Kbits/s\}+win::10.0.0.0/8 \
#     tap0\{500Kbits/s,118Kbits/s\}
 
# OPTIONS
#
# --debug
# Show what commands would have been executed to setup the firewall. No
# system changes are made.
#
# --verbose
# Log suspicious packets. Note that on some network, this can result in
# rather large log files.
#
# --log-all
# Log every rejected packet, not just the suspicious ones. Note: If you're
# restarting the firewall, you'll initially reject a lot of established
# packets from connections made before the restart.
#
# --stealth
# Try to be as stealthy as possible. Ordinarily, this script responds to
# failed connection attempts in the following manner:
#
# port 113 TCP RESET
# broadcasts ICMP host-prohib
# connections ICMP filter-prohib
# old established connections TCP RESET
# NetBIOS/Rendezvous traffic silently drop packet
#
# Also, while all "normal" outbound traffic is silently allowed, anything
# out of the ordinary is logged (if --verbose is used).
#
# When --stealth mode is on, this script drops all inbound packets silently,
# and also all suspicious outbound packets. It tries to make your machine
# appear as far under the radar as possible on an open network.
#
# However, do realize that being entirely stealthy is not possible. Anyone
# on the same local network as you will be able to see your ARP packets
# flying around, and will know you are there and what your MAC address is.
# Also, by attempting to scan you, they will know you're trying to be
# stealthy. In fact, --stealth mode is really only effective against the
# most casual of observers.
#
# --blackhole
# If used, a tcp/udp blackhole is configured to help block stealth scanning.
# Note however that this has caused known slowdowns in services like Samba
# (smbfs).
#
# --gaming
# Indicates that you intend to use the machine primarily for gaming, where
# latency is more important than throughput. Kernel variables are tuned
# accordingly. You should also use priority queuing (suffix {0,0} to the
# interface) so that small ACK packets get out ASAP.
#
# --router INTF1,INTF2:NET
# This machine acts as a router between INTF1 and INTF2 for network NET.
# INTF1 specifies the target interface, so if you wanted to route traffic
# from en1 (local clients connected to your Airport network) over en0 (cable
# modem connection to the Internet), you would use:
#
# rc.firewall --router en0,en1:192.168.10.0/24
#
# This might read as "route traffic over Ethernet (en0) coming from the
# Wireless (en1) subnetwork (192.168.10.0/24)".
#
# --tcp PORT[,PORT...]
# --udp PORT[,PORT...]
# Make the given inbound PORTs accessible on any configured interface.
#
# --local-tcp PORT[,PORT...]
# --local-udp PORT[,PORT...]
# Make the given inbound PORTs accessible to local networks (i.e., not the
# Internet).
#
# --trusted-tcp PORT[,PORT...]
# --trusted-udp PORT[,PORT...]
# Make the given inbound PORTs accessible to trusted networks only.
 
# NOTES
#
# A word about a few of the things this script cannot do, owing to
# deficiencies in ipfw on Mac OS X 10.4 and 10.5:
#
# * You cannot filter incoming packets based on the MAC address of the source.
# This is because the necessary support is not compiled into the OS X
# kernel, not because ipfw doesn't support it.
#
# * You cannot take action based on counters, like shutting off ECHO REQUEST
# packets from a certain host once they exceed a certain number within a
# given set of time. Tools like IPNetSentryX can do this.
#
# * Although you can shape traffic by directing it along specific pipes or
# queues, you cannot manipulate traffic as it passes down the chain. Doing
# this requires divert'ing the traffic to a user-space daemon, which
# modifies the packets and passes them on (google for 'throttled' as an
# excellent example). iptables on Linux can do this sort of thing very
# easily, it's a shame ipfw cannot.
#
# * You can't match a packet based on its IP fragment offset, only on the
# existence of fragmentation or not. Also, ipfw always drops TCP packets
# with a fragment offset of 1.
#
##############################################################################
 
 
##############################################################################
#
# Process command-line options
#
##############################################################################
 
IPFW="/sbin/ipfw -q"
 
debug=false
gaming=false
blackhole=false
router=false
this="me"
logall=""
 
stealth=false
verbose=false
tcp_reset="reset"
unreach_host="unreach host"  # aka, reject
unreach_host_prohib="unreach host-prohib"
unreach_filter_prohib="unreach filter-prohib"
 
trusted_tcp_ports=""
trusted_udp_ports=""
local_tcp_ports=""
local_udp_ports=""
public_tcp_ports=""
public_udp_ports=""
 
while [[ -n "$1" ]] && (echo $1 | grep -q -e ^--); do
case "$1" in
    --debug)
  shift 1
  debug=true
  IPFW="echo ipfw" ;;
 
    --gaming)
  shift 1
  gaming=true ;;
 
    --verbose)
  shift 1
  verbose=true ;;
 
    --log-all)
  shift 1
  echo Logging all denied packets
        logall="log" ;;
 
    --stealth)
  shift 1
  echo Enabling stealth mode to avoid detection and leakage
  tcp_reset="drop"
  unreach_host="drop"  # aka, reject
  unreach_host_prohib="drop"
  unreach_filter_prohib="drop"
        stealth="true" ;;
 
    --blackhole)
  shift 1
  echo Enabling blackhole to avoid stealth port scans
        blackhole="true" ;;
 
    --router)
  shift 1
  router=true
  external_intf=$(echo $1 | sed 's/,.*//')
  client_intf=$(echo $1 | sed 's/.*,//')
  client_net=$(echo $client_intf | sed 's/.*://')
  client_intf=$(echo $client_intf | sed 's/:.*//')
  echo Enabling routing $client_intf \($client_net\) -\> $external_intf
  shift 1;;
 
    --trusted-tcp)
  shift 1
  if [[ -n "$trusted_tcp_ports" ]]; then
   trusted_tcp_ports="$trusted_tcp_ports,$1"
  else
   trusted_tcp_ports="$1"
  fi
  echo Opening trusted TCP ports: $trusted_tcp_ports
  shift 1 ;;
 
    --local-tcp)
  shift 1
  if [[ -n "$local_tcp_ports" ]]; then
   local_tcp_ports="$local_tcp_ports,$1"
  else
   local_tcp_ports="$1"
  fi
  echo Opening local TCP ports: $local_tcp_ports
  shift 1 ;;
 
    --tcp)
  shift 1
  if [[ -n "$public_tcp_ports" ]]; then
   public_tcp_ports="$public_tcp_ports,$1"
  else
   public_tcp_ports="$1"
  fi
  echo Opening public TCP ports: $public_tcp_ports
  shift 1 ;;
 
    --trusted-udp)
  shift 1
  if [[ -n "$trusted_udp_ports" ]]; then
   trusted_udp_ports="$trusted_udp_ports,$1"
  else
   trusted_udp_ports="$1"
  fi
  echo Opening trusted UDP ports: $trusted_udp_ports
  shift 1 ;;
 
    --local-udp)
  shift 1
  if [[ -n "$local_udp_ports" ]]; then
   local_udp_ports="$local_udp_ports,$1"
  else
   local_udp_ports="$1"
  fi
  echo Opening local UDP ports: $local_udp_ports
  shift 1 ;;
 
    --udp)
  shift 1
  if [[ -n "$public_udp_ports" ]]; then
   public_udp_ports="$public_udp_ports,$1"
  else
   public_udp_ports="$1"
  fi
  echo Opening public UDP ports: $public_udp_ports
  shift 1 ;;
 
    *)
  echo Unrecognized option $1
  exit 1
    esac
done
 
 
##############################################################################
#
# If interfaces were given, setup the global variables used by all the
# rules.
#
##############################################################################
 
args="$@"
 
declare -a interfaces
declare -a networks
declare -a trust
declare -a nettype
declare -a inbw
declare -a outbw
declare -a all_intf
 
intf_count=0
 
function add_interface()
{
    intf=$1
    netw=$2
    trusted=$3
    ntype=$4
    intf_inbw=$5
    intf_outbw=$6
 
    interfaces[$intf_count]=$intf
    networks[$intf_count]=$netw
    trust[$intf_count]=$trusted
    nettype[$intf_count]=$ntype
    inbw[$intf_count]=$intf_inbw
    outbw[$intf_count]=$intf_outbw
 
    echo Configuring interface $intf_count: $intf $netw \
  trusted? $trusted type $ntype \
  \(in $intf_inbw out $intf_outbw\)
 
    # Configure bandwith shaping in/out pipes for each interface that
    # has a bandwith defined
    if [[ -n "$intf_inbw" ]]; then
  $IPFW pipe $((100 + intf_count)) config bw 0
  $IPFW pipe $((200 + intf_count)) config bw 0
    fi
 
    # If the interface is not yet in the `all_intf' array, add it
    if echo ${all_intf[@]} | grep -qv "\\<$intf\\>"; then
  all_intf=(${all_intf[@]} $intf)
    fi
 
intf_count=$((intf_count + 1))
}
 
while [[ -n "$1" ]]; do
intf=""
    netw=any
    trusted=false
ntype=unknown
 
    intf=$(echo "$1" | sed 's/[+:].*//')
    netw=$(echo "$1" | sed 's/^[^:]*:*//')
 
    if [[ -z "$netw" ]]; then
  netw=any
    fi
 
if echo "$1" | grep -q "::"; then
  trusted=true
fi
 
if echo "$1" | grep -q "\\+mac"; then
  ntype=mac
    fi
 
if echo "$1" | grep -q "\\+win"; then
  if [[ $ntype == mac ]]; then
   ntype=both
  else
   ntype=win
  fi
fi
 
if echo "$intf" | grep -q "{"; then
  intf_inbw=$(echo "$intf" | sed 's/.*{//' | sed 's/,.*//' | sed 's/}//')
  intf_outbw=$(echo "$intf" | sed 's/.*{//' | sed 's/.*,//' | sed 's/}//')
  intf=$(echo "$intf" | sed 's/{.*//')
    else
  intf_inbw=""
  intf_outbw=""
    fi
 
add_interface $intf $netw $trusted $ntype $intf_inbw $intf_outbw
 
    shift 1
done
 
via_all=""
 
if [[ $intf_count > 0 ]]; then
for intf in ${all_intf[@]}; do
  if [[ -z "$via_all" ]]; then
   via_all="{"
  else
   via_all="$via_all or"
  fi
  via_all="$via_all via $intf"
    done
 
if [[ -n "$via_all" ]]; then
  via_all="$via_all }"
    fi
fi
 
 
##############################################################################
#
# Function for rate limiting/traffic shaping an interface
#
##############################################################################
 
declare -a limited
 
function limit_intf()
{
    intf=$1
    intf_index=""
 
    for (( i=0; i < intf_count; i++ )); do
  if [[ ${interfaces[$i]} == $intf && \
   -z "${limited[$i]}" && -n "${inbw[$i]}" ]]; then
   limited[$i]=true
 
    # Put a pipe in place for all remaining inbound/outbound traffic,
    # which can be throttled using the option --setrate or the
    # separate script "setrate". This makes it easy to be kind to
    # other people's networks, no matter what kind of traffic it is.
    inpipe="pipe $((100 + i))"
    outpipe="pipe $((200 + i))"
 
    $IPFW $inpipe config bw ${inbw[$i]}
    $IPFW $outpipe config bw ${outbw[$i]}
 
    private="192.168.0.0/16,172.16.0.0/12,10.0.0.0/8"
 
    $IPFW add 450 set 0 skipto 900 all from any to $private out via $intf
    $IPFW add 460 set 0 skipto 900 all from $private to any in via $intf
 
    $IPFW add 500 set 0 $inpipe tcp from any to any in via $intf tcpflags !syn
    $IPFW add 510 set 0 $inpipe udp from any to any in via $intf
 
    # Shape the outbound pipe so that some protocols get priority going
    # out
    $IPFW queue $((100 + i)) config $outpipe weight 7 # high-priority
    $IPFW queue $((200 + i)) config $outpipe weight 5 # medium-priority
    $IPFW queue $((300 + i)) config $outpipe weight 1 # low-priority
 
    # Assign outgoing empty/small ACK packets to the high-priority queue
    $IPFW add 600 set 0 queue $((100 + i)) \
        tcp from any to any out via $intf tcpflags ack iplen 0-80
 
    # Assign outgoing UDP (DNS) and SSH traffic to the medium-priority queue
    $IPFW add 700 set 0 queue $((200 + i)) tcp from any to any 22,80,443,5900 \
  out via $intf \{ tcpflags \!ack or iplen 81-65535 \}
    $IPFW add 710 set 0 queue $((200 + i)) udp from any to any 53,1194 out via $intf
 
    # Assign all other outgoing traffic to the low-priority queue:
    $IPFW add 800 set 0 queue $((300 + i)) tcp from any to any not 22,80,443,5900 \
  out via $intf \{ tcpflags \!ack or iplen 81-65535 \}
    $IPFW add 810 set 0 queue $((300 + i)) udp from any to any not 53,1194 out via $intf
 
  fi
done
}
 
 
##############################################################################
#
# Initialize and tune the firewalling environment
#
##############################################################################
 
# Remove any rules previously defined and flush the dynamic tables
$IPFW -f flush
$IPFW -f pipe flush
 
# Log all rejected packets, which are always "suspect"
if [[ $debug == false ]]; then
if [[ $verbose == true ]]; then
  sysctl -w net.inet.ip.fw.verbose=1
    else
  sysctl -w net.inet.ip.fw.verbose=0
    fi
 
    # Make sure packets get reinjected
    sysctl -w net.inet.ip.fw.one_pass=0
    # Check that packets are appropriate to their interface
    sysctl -w net.inet.ip.check_interface=1
 
    # Turn on RFC1323 TCP high speed optimization.
    # NOTE: This can be a security risk (DoS attack)
    sysctl -w net.inet.tcp.rfc1323=1
    # ICMP limit
    sysctl -w net.inet.icmp.icmplim=1024
    # Stop redirects
    sysctl -w net.inet.icmp.drop_redirect=1
    sysctl -w net.inet.icmp.log_redirect=1
    sysctl -w net.inet.ip.redirect=0
    # Stop source routing
    sysctl -w net.inet.ip.sourceroute=0
    sysctl -w net.inet.ip.accept_sourceroute=0
    # Stop broadcast ECHO response
    sysctl -w net.inet.icmp.bmcastecho=0
    # Stop other broadcast probes
    sysctl -w net.inet.icmp.maskrepl=0
    # Turn on strong TCP sequencing
    sysctl -w net.inet.tcp.strict_rfc1948=1
    # Socket queue defense against SYN attacks
    sysctl -w kern.ipc.somaxconn=1024
    # IPC max buffering
    sysctl -w kern.ipc.maxsockbuf=523288
    # ARP cleanup
    sysctl -w net.link.ether.inet.max_age=1200
 
    # Increase buffers for faster downloads when you have very
    # high-speed, latency-free bandwidth (ie, _not_ DSL which dies badly
    # as it saturates)
    if [[ $gaming == true ]]; then
  sysctl -w net.inet.tcp.sendspace=16383
  sysctl -w net.inet.tcp.recvspace=16383
  sysctl -w net.inet.udp.recvspace=42080
  sysctl -w net.inet.raw.recvspace=8192
  sysctl -w net.local.dgram.maxdgram=4196
 
  # Other network buffering
  sysctl -w net.local.stream.recvspace=16383
  sysctl -w net.local.stream.sendspace=16383
  sysctl -w net.local.dgram.recvspace=8192
 
  # TCP delayed ack off, better latency
  sysctl -w net.inet.tcp.delayed_ack=0
    else
  sysctl -w net.inet.tcp.sendspace=32767
  sysctl -w net.inet.tcp.recvspace=32767
  sysctl -w net.inet.udp.recvspace=65535
  sysctl -w net.inet.raw.recvspace=16384
  sysctl -w net.local.dgram.maxdgram=8192
 
  # Other network buffering
  sysctl -w net.local.stream.recvspace=32767
  sysctl -w net.local.stream.sendspace=32767
  sysctl -w net.local.dgram.recvspace=16384
 
  # TCP delayed ack on, better throughput
  sysctl -w net.inet.tcp.delayed_ack=1
    fi
 
    # Create a blackhole to avoid stealth port scans
    if [[ $blackhole == true ]]; then
  sysctl -w net.inet.tcp.blackhole=2
  sysctl -w net.inet.udp.blackhole=1
    else
  sysctl -w net.inet.tcp.blackhole=0
  sysctl -w net.inet.udp.blackhole=0
    fi
 
    # If we're acting as a router, enable packet forwarding
    if [[ $router == true ]]; then
  sysctl -w net.inet.ip.forwarding=1
    else
  sysctl -w net.inet.ip.forwarding=0
    fi
fi
 
 
##############################################################################
#
# Set 0: Allow loopback, divert NAT packets, shape ICMP/TCP-SYN
#
##############################################################################
 
# Allow all loopback traffic, don't bother with anything else
$IPFW add 100 set 0 allow all from any to any via 'lo*'
 
if [[ $router == true ]]; then
    # Divert traffic from the external interface to the nat daemon
    # (note: using "in" here, as some firewalls do, causes OpenVPN to
    # stop working)
    $IPFW add 200 set 0 divert natd all from any to any via $external_intf
fi
 
# Rate limit TCP traffic used to establish connections, to protect
# against SYN flooding
$IPFW pipe 300 config bw 64Kbit/s queue 5
 
$IPFW add 300 set 0 pipe 300 tcp from any to any in setup
 
# Delay TCP RESET packets. From the IPNetSentryX docs: "Some
# firewalls can send TCP RESET segments when denying access. If the
# interface running such a firewall is set to promiscuous mode, the
# firewall may send TCP RESET segments in response to connection
# requests that were not originally addressed to that host. The
# symptom is frequent “Connection refused” responses when trying to
# access remote servers. By delaying such TCP RESET segments
# (approximately 0.5 seconds), we allow the actual target of the
# connection request (if any) to respond first completing the
# connection process. When the RESET arrives, it will be safely
# ignored as out of order if the target host has already responded."
$IPFW pipe 350 config delay 500
 
$IPFW add 350 set 0 pipe 350 tcp from any to any in tcpflags rst
 
# Rate limit ICMP traffic to avoid line clogging by Smurf attacks
$IPFW pipe 400 config bw 16Kbit/s queue 1
$IPFW pipe 410 config bw 16Kbit/s queue 5
 
$IPFW add 410 set 0 pipe 400 icmp from any to any in
$IPFW add 415 set 0 pipe 410 icmp from any to any out
 
# Rules 500-899 are defined by limit_intf() above
 
$IPFW add 901 set 0 deny log all from any to any ipoptions rr in $via_all
$IPFW add 911 set 0 deny log all from any to any ipoptions ts in $via_all
$IPFW add 921 set 0 deny log all from any to any ipoptions lsrr in $via_all
$IPFW add 931 set 0 deny log all from any to any ipoptions ssrr in $via_all
 
$IPFW add 941 set 0 deny log tcp from any to any tcpflags syn,fin
$IPFW add 951 set 0 deny log tcp from any to any tcpflags syn,rst
$IPFW add 961 set 0 deny log tcp from any 0 to any
$IPFW add 971 set 0 deny log tcp from any to any 0
$IPFW add 981 set 0 deny log udp from any 0 to any
$IPFW add 991 set 0 deny log udp from any to any 0
 
 
##############################################################################
#
# Set 1: Allow routed traffic
#
# If this machine is acting as a router, allow traffic to pass through
# between the two connected interfaces.
#
##############################################################################
 
if [[ $router == true ]]; then
    # Allow traffic coming in from the "client" network, and traffic
    # coming in from the Internet bound for the client network
    $IPFW add 1000 set 1 allow all from $client_net to any in recv $client_intf
    $IPFW add 1010 set 1 allow all from any to $client_net in recv $external_intf
 
    # And allow this traffic out through the respective target interface
    $IPFW add 1100 set 1 allow all \
  from $client_net to any out recv $client_intf xmit $external_intf
    $IPFW add 1110 set 1 allow all \
  from any to $client_net out recv $external_intf xmit $client_intf
 
    # Also allow the client network to see the router
    $IPFW add 1200 set 1 allow all from $client_net to me out recv $client_intf
    $IPFW add 1210 set 1 allow all from me to $client_net out xmit $client_intf
fi
 
 
##############################################################################
#
# Set 2: Allow traffic relating to ongoing conversations
#
# But deny fragments and established connections not so related.
#
##############################################################################
 
# Allow traffic if it matches the dynamic table
$IPFW add 2000 set 2 check-state
 
# Drop fragments and reset established connections not matched by the
# dynamic table
$IPFW add 2100 set 2 deny $logall all from any to any frag
$IPFW add 2110 set 2 $tcp_reset $logall tcp from any to any established
 
 
##############################################################################
#
# Set 3: Allow Rendezvous/Bonjour and AFP for Apple networks
#
##############################################################################
 
# TCP 548 Apple AFP
# 5009 Apple Airport Express Admin
# 5900 Apple VNC (screen sharing)
 
tcp_ports=548,5009,5900
 
# UDP 192 Apple UPnP (network discovery)
 
udp_ports=192
 
allowed=false
 
for (( index=0; index < intf_count; index++ )); do
if [[ ${nettype[$index]} == mac || ${nettype[$index]} == both ]]; then
  netw=${networks[$index]}
  intf=${interfaces[$index]}
 
  # Allow outbound and inbound TCP connections to tcp_ports
  $IPFW add $((3100 + index)) set 3 allow $logall \
   tcp from me to $netw $tcp_ports out via $intf setup keep-state
  $IPFW add $((3110 + index)) set 3 allow $logall \
   tcp from $netw to me $tcp_ports in via $intf setup keep-state
 
  # Allow outbound and inbound UDP connections to udp_ports
  $IPFW add $((3200 + index)) set 3 allow $logall \
   udp from me to $netw $udp_ports out via $intf keep-state
  $IPFW add $((3210 + index)) set 3 allow $logall \
   udp from $netw to me $udp_ports in via $intf keep-state
 
  # Allow Rendezvous/Bonjour (Zeroconf) traffic
  $IPFW add $((3300 + index)) set 3 allow $logall \
   udp from $netw to any 5353 via $intf keep-state
 
  # Allow three types of broadcast traffic typically found in
  # Windows and Apple enviroments
  $IPFW add $((3400 + index)) set 3 allow $logall \
   ip from $netw to 224.0.0.0/3 via $intf keep-state
  $IPFW add $((3410 + index)) set 3 allow $logall \
   udp from $netw to 239.255.255.253 via $intf keep-state
  $IPFW add $((3420 + index)) set 3 allow $logall \
   udp from $netw to 255.255.255.255 via $intf keep-state
 
  allowed=true
fi
done
 
# Load or unload the Apple Multicast daemon based on whether we're
# using Rendezvous at all; no need to broadcast if we're not, since
# the packets won't even be let out
if [[ $debug == false ]]; then
launchctl unload /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist 2> /dev/null
    if [[ $allowed == true ]]; then
  launchctl load -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist
    fi
fi
 
##############################################################################
#
# Set 4: Allow NetBIOS and SMB for Windows networks
#
##############################################################################
 
# TCP 135-139 Windows File sharing (http://support.microsoft.com/kb/298804)
# 445 Windows Direct-hosted SMB traffic (same URL)
# 5000 Windows UPnP
 
tcp_ports=135-139,445,5000
 
# UDP 135-139 Windows File sharing (see above)
# 427 Windows SLP (Service Location Protocol)
# 445 Windows Direct-hosted SMB traffic
# 1900 Windows UPnP
 
udp_ports=135-139,427,445,1900
 
for (( index=0; index < intf_count; index++ )); do
if [[ ${nettype[$index]} == win || ${nettype[$index]} == both ]]; then
  netw=${networks[$index]}
  intf=${interfaces[$index]}
 
  # Allow outbound and inbound TCP connections to tcp_ports
  $IPFW add $((4100 + index)) set 4 allow $logall \
   tcp from me to $netw $tcp_ports out via $intf setup keep-state
  $IPFW add $((4110 + index)) set 4 allow $logall \
   tcp from $netw to me $tcp_ports in via $intf setup keep-state
 
  # Allow outbound and inbound UDP connections to udp_ports
  $IPFW add $((4200 + index)) set 4 allow $logall \
   udp from me to $netw $udp_ports out via $intf keep-state
  $IPFW add $((4210 + index)) set 4 allow $logall \
   udp from $netw to me $udp_ports in via $intf keep-state
 
  # Allow three types of broadcast traffic typically found in
  # Windows and Apple enviroments
  $IPFW add $((4300 + index)) set 4 allow $logall \
   ip from $netw to 224.0.0.0/3 via $intf keep-state
  $IPFW add $((4320 + index)) set 4 allow $logall \
   udp from $netw to 239.255.255.253 via $intf keep-state
  $IPFW add $((4340 + index)) set 4 allow $logall \
   udp from $netw to 255.255.255.255 via $intf keep-state
    fi
done
 
 
##############################################################################
#
# Set 5: Allow certain kinds of traffic for trusted networks
#
##############################################################################
 
for (( index=0; index < intf_count; index++ )); do
if [[ ${trust[$index]} == true ]]; then
  netw=${networks[$index]}
  intf=${interfaces[$index]}
 
  # Allow all ICMP traffic within trusted networks (aka ping)
  $IPFW add $((5100 + index)) set 5 allow $logall \
   icmp from me to $netw out via $intf
  $IPFW add $((5110 + index)) set 5 allow $logall \
   icmp from $netw to me in via $intf
    fi
done
 
# Reject broadcast traffic (possibly related to Rendezvous/NetBIOS)
$IPFW add 5200 set 5 $unreach_host_prohib $logall ip from any to 224.0.0.0/3 not 67-68
$IPFW add 5210 set 5 $unreach_host_prohib $logall ip from any to 239.255.255.253 not 67-68
$IPFW add 5220 set 5 $unreach_host_prohib $logall ip from any to 255.255.255.255 not 67-68
 
# Deny Windows and Apple network related UDP traffic reaching this
# point; reject TCP with host-prohib
$IPFW add 5300 set 5 deny $logall udp from any to any $udp_ports
$IPFW add 5310 set 5 $unreach_host_prohib $logall tcp from any to any $tcp_ports
 
 
##############################################################################
#
# Set 6: Allow in/out packets related to DHCP and some ICMP
#
# This rule set allows our network interfaces to be configured. It can
# be skipped if DHCP is not being used; it could also be disabled once
# an address is assigned, although only for as long as the lease will
# last.
#
##############################################################################
 
# Allow DHCP packets in and out, including broadcast. Since we don't have
# an address yet, we can't use "me" as a target here.
$IPFW add 6000 set 6 allow $logall udp \
    from any 67-68 to any 67-68 keep-state
 
# Allow certain types of ICMP packets on known interfaces, which might be
# necessary for proper operation
$IPFW add 6100 set 6 allow $logall icmp from any to any \
    icmptypes 0,3,4,11,12,13,14 keep-state
 
##############################################################################
#
# Set 7: Filter inbound traffic
#
# Stateful firewalling is used to allow in packets related to
# established outbound connections. Filtering is used to remove
# possible spoof packets. The remainder might be consider "legitimate
# inbound requests", and are passed to the subsequent rule sets.
#
##############################################################################
 
# Verify reverse path to help avoid spoofed packets. This means any
# packet coming from a particular interface must have an address
# matching the netmask for that interface. The `via_all' variable is
# used to avoid logging packets on interfaces we're not interested in.
$IPFW add 7000 set 7 deny $logall all from any to any not verrevpath in $via_all
 
# Reject anything received that was not directly intended for this
# matchine with a "host prohibited" response. This mostly means
# broadcast. We omit DHCP from this because until a DHCP lease is
# given, we have no idea what our address is/will be.
$IPFW add 7100 set 7 $unreach_host_prohib log all from any to not me not 67-68 in $via_all
$IPFW add 7110 set 7 $unreach_host_prohib log all from not me to any out $via_all
 
# Filter packets inbound on known interfaces
via_check=""
 
for (( index=0; index < intf_count; index++ )); do
netw=${networks[$index]}
    intf=${interfaces[$index]}
 
    if [[ $netw == any ]]; then
  if [[ -z "$via_check" ]]; then
   via_check="{"
  else
   via_check="$via_check or"
  fi
  via_check="$via_check via $intf"
    fi
done
 
if [[ -n "$via_check" ]]; then
via_check="$via_check }"
fi
 
for (( index=0; index < intf_count; index++ )); do
netw=${networks[$index]}
    intf=${interfaces[$index]}
 
    if [[ $netw != any ]]; then
  $IPFW add $((7200 + index)) set 7 skipto 7500 \
   all from $netw to me in via $intf
    fi
done
 
if [[ -n "$via_check" ]]; then
    # Deny all inbound traffic from RFC1918 address spaces (spoof!)
    $IPFW add $((7300 + index)) set 7 deny log \
  all from 192.168.0.0/16 to any in $via_check
    $IPFW add $((7320 + index)) set 7 deny log \
  all from 172.16.0.0/12 to any in $via_check
    $IPFW add $((7340 + index)) set 7 deny log \
  all from 10.0.0.0/8 to any in $via_check
 
    # Deny all inbound traffic from a loopback address (spoof!)
    $IPFW add $((7400 + index)) set 7 deny log \
  all from 127.0.0.0/8 to any in $via_check
fi
 
# Reject broadcasts not related to DHCP
$IPFW add 7500 set 7 $unreach_host_prohib $logall all from 0.0.0.0/8 to any not 67-68 in
 
# Reject DHCP auto-config, and public class D & E multicast
$IPFW add 7600 set 7 $unreach_host_prohib $logall all from 169.254.0.0/16 to any in
$IPFW add 7610 set 7 $unreach_host_prohib $logall all from 224.0.0.0/3 to any in
 
# Send TCP RST on attempted auth connections; IRC and SMTP might try
# to connect back to us legitimately
$IPFW add 7700 set 7 $tcp_reset $logall tcp from any to me 113 in setup
 
 
##############################################################################
#
# Set 10: Shape outbound traffic
#
# Outbound traffic is shaped for best response while concurrent
# transfers might be occurring in both directions. The actual shaping
# is done at a much earlier step, even though we set it up at this
# point in the script.
#
##############################################################################
 
$IPFW set disable 10
 
for (( index=0; index < intf_count; index++ )); do
limit_intf ${interfaces[$index]} ${inbw[$index]} ${outbw[$index]}
done
 
if [[ $intf_count > 0 ]]; then
    $IPFW set enable 10
fi
 
 
##############################################################################
#
# Set 11: Allow all outbound traffic
#
# If this ruleset is enabled, all other outbound traffic will be allowed
# here. I use the application Little Snitch to allow/deny outbound
# packets on a per-Application basis.
#
# Note that all "known" outbound traffic is silently allowed, whereas
# unknown traffic is logged. If the --stealth option is used, unknown
# traffic is logged and denied.
#
##############################################################################
 
$IPFW set disable 11
 
tcp_out_ports="80,443"     # Web
tcp_out_ports="$tcp_out_ports,21"     # FTP
tcp_out_ports="$tcp_out_ports,22"     # OpenSSH
tcp_out_ports="$tcp_out_ports,5900"     # VNC
tcp_out_ports="$tcp_out_ports,5222,5190,5050,1863" # IM
tcp_out_ports="$tcp_out_ports,6667"     # IRC
tcp_out_ports="$tcp_out_ports,25,26"     # SMTP
tcp_out_ports="$tcp_out_ports,110"     # POP3
tcp_out_ports="$tcp_out_ports,995"     # POP3S
tcp_out_ports="$tcp_out_ports,143"     # IMAP4
tcp_out_ports="$tcp_out_ports,993"     # IMAP4S
 
udp_out_ports="1194"     # OpenVPN
udp_out_ports="$udp_out_ports,53"     # DNS
udp_out_ports="$udp_out_ports,123"     # NTP
 
for (( index=0; index < intf_count; index++ )); do
netw=${networks[$index]}
    intf=${interfaces[$index]}
 
    $IPFW add 11000 set 11 allow $logall \
  tcp from me to $netw $tcp_out_ports out via $intf setup keep-state
 
    $IPFW add 11100 set 11 allow $logall udp \
  from me to $netw $udp_out_ports out via $intf keep-state
 
    $IPFW add 11200 set 11 allow $logall tcp \
  from me to $netw out via $intf setup keep-state
    $IPFW add 11210 set 11 allow $logall ip \
  from me to $netw out via $intf keep-state
 
    if [[ $intf_count > 0 ]]; then
  $IPFW set enable 11
    fi
done
 
 
##############################################################################
#
# Set 20: Allowing specified inbound traffic
#
# Ports can be opened for inbound traffic using the following options
# when invoking this script:
#
# --trusted-tcp PORT[,PORT...]
# --local-tcp PORT[,PORT...]
# --tcp PORT[,PORT...]
#
# --trusted-udp PORT[,PORT...]
# --local-udp PORT[,PORT...]
# --udp PORT[,PORT...]
#
# These options can specified a group of ports separated by commas, such
# as "--tcp 80,443", or you can give the same option multiple times,
# such as "--tcp 80 --tcp 443".
#
# The default, if no options are given, is to allow nothing.
#
##############################################################################
 
$IPFW set disable 20
 
for (( index=0; index < intf_count; index++ )); do
netw=${networks[$index]}
    intf=${interfaces[$index]}
 
    if [[ ${trust[$index]} != false ]]; then
  if [[ -n "$trusted_tcp_ports" ]]; then
   $IPFW add $((20000 + index)) set 20 allow tcp $logall \
    from $netw to me $trusted_tcp_ports \
    in via $intf setup keep-state
  fi
  if [[ -n "$trusted_udp_ports" ]]; then
   $IPFW add $((20020 + index)) set 20 allow udp $logall \
    from $netw to me $trusted_udp_ports \
    in via $intf keep-state
  fi
fi
 
if [[ $netw != any || ${trust[$index]} != false ]]; then
  if [[ -n "$local_tcp_ports" ]]; then
   $IPFW add $((20100 + index)) set 20 allow tcp $logall \
    from $netw to me $local_tcp_ports \
    in via $intf setup keep-state
  fi
  if [[ -n "$local_udp_ports" ]]; then
   $IPFW add $((20120 + index)) set 20 allow udp $logall \
    from $netw to me $local_udp_ports \
    in via $intf keep-state
  fi
fi
 
if [[ -n "$public_tcp_ports" ]]; then
  $IPFW add $((20200 + index)) set 20 allow tcp $logall \
   from $netw to me $public_tcp_ports \
   in via $intf setup keep-state
    fi
if [[ -n "$public_udp_ports" ]]; then
  $IPFW add $((20220 + index)) set 20 allow udp $logall \
   from $netw to me $public_udp_ports \
   in via $intf keep-state
    fi
done
 
if [[ $intf_count > 0 ]]; then
    $IPFW set enable 20
fi
 
 
##############################################################################
#
# Set 30: Reject ALL remaining packets, in or out
#
# Disabling this rule set will cause the default behavior to be to allow
# all packets not rejected by the above rules.
#
##############################################################################
 
# Reject certain packets that seem to fly around with no purpose
$IPFW add 30000 set 30 $unreach_host_prohib $logall udp from any 53 to any in $via_all
$IPFW add 30010 set 30 $unreach_host_prohib $logall udp from any to any 53 in $via_all
 
# Deny all traffic in or out, but divide the log between packets
# inbound on known interfaces and all other packets
$IPFW add 30100 set 30 $unreach_filter_prohib $logall udp from any to any in $via_all
$IPFW add 30110 set 30 $unreach_filter_prohib log tcp from any to any in $via_all
$IPFW add 30120 set 30 $unreach_filter_prohib $logall all from any to any in $via_all
 
# Deny all outbound traffic reaching this point, and log it so we know
# what the machine was trying to do
$IPFW add 30200 set 30 $unreach_filter_prohib log all from any to any out $via_all
 
# Lastly, we simply drop it all.
$IPFW add 30300 set 30 $unreach_filter_prohib $logall all from any to any
 
 
##############################################################################
#
# END: Inform the system that the firewall is now installed
#
##############################################################################
 
logger -i -p daemon.notice -t firewall "firewall installed: $args"
 
# rc.firewall ends here