]>
Commit | Line | Data |
---|---|---|
b08d4dde SS |
1 | package Guardian::IPtables; |
2 | use strict; | |
3 | use warnings; | |
4 | ||
5 | use Exporter qw(import); | |
6 | ||
7 | our @EXPORT = qw(DoBlock DoUnblock DoFlush); | |
8 | ||
e9c558fe SS |
9 | # Array of supported block actions. |
10 | my @supported_actions = ("DROP", "REJECT"); | |
11 | ||
345e62e2 SS |
12 | # Path where the IPtables related binaries are stored. |
13 | my $bindir = "/usr/sbin/"; | |
14 | ||
15 | # Hash which contains the path to the IPtables binaries, | |
16 | # based on the used IP protocol version. IPtables is | |
17 | # designed to use different binaries for IPv4 and IPv6. | |
18 | my %binaries = ( | |
19 | "4" => "iptables", | |
20 | "6" => "ip6tables", | |
21 | ); | |
b08d4dde SS |
22 | |
23 | # The used firewall chain. | |
24 | my $chain = "INPUT"; | |
25 | ||
26 | # | |
27 | ## The DoBlock subroutine. | |
28 | # | |
29 | ## This subroutine is called, when a given address should be locked by | |
30 | ## using IPtables. | |
31 | # | |
32 | ## Guardian is using the "append" option from IPtables, which will add the new rule | |
33 | ## to the end of the chain to prevent from possible race-conditions when adding/deleting | |
34 | ## rules at the same time. | |
35 | # | |
36 | sub DoBlock (@) { | |
37 | my ($address, $action) = @_; | |
38 | ||
39 | # If no action has been given, default to "DROP". | |
40 | unless($action) { | |
41 | $action = "DROP"; | |
42 | } | |
43 | ||
e9c558fe | 44 | # Check if the given action is supported. |
345e62e2 SS |
45 | unless(&_check_action($action)) { |
46 | # Abort and return an error message. | |
47 | return "Unsupported action: $action"; | |
48 | } | |
49 | ||
50 | # Obtain which binary should be executed. | |
51 | my $iptables = &_omit_binary($address); | |
e9c558fe | 52 | |
345e62e2 SS |
53 | # Abort if no suitable binary could be obtained. |
54 | unless ($iptables) { | |
55 | return "No suitable binary found."; | |
e9c558fe SS |
56 | } |
57 | ||
345e62e2 | 58 | # Call IPtables binary to block the given address. |
b08d4dde SS |
59 | system("$iptables --wait -A $chain -s $address -j $action"); |
60 | } | |
61 | ||
62 | # | |
63 | ## The DoUnblock subroutine. | |
64 | # | |
65 | ## This subroutine can be used to delete all firewall rules (unblock) | |
66 | ## of a previously blocked address. | |
67 | # | |
68 | ## To do this a subroutine will be called, which is collecting all rule | |
69 | ## positions in the firewall chain for the given address, which returns | |
70 | ## them in reversed order. This list of rules will be deleted one-by-one | |
71 | ## so multiple entries (if present) for the address will be deleted. | |
72 | # | |
73 | ## This approach also eliminates the exact rule argument processing again | |
74 | ## for dropping it. So it is not neccessary to know the additional arguments | |
75 | ## like firewall action (DROP, REJECT) etc. | |
76 | # | |
77 | sub DoUnblock ($) { | |
78 | my ($address) = @_; | |
79 | ||
345e62e2 SS |
80 | # Obtain which binary should be executed. |
81 | my $iptables = &_omit_binary($address); | |
82 | ||
83 | # Abort if no suitable binary could be obtained. | |
84 | unless($iptables) { | |
85 | return "No suitable binary found."; | |
86 | } | |
87 | ||
b08d4dde | 88 | # Get rulepositions for the specified address. |
345e62e2 | 89 | my @rules = &_get_rules_positions_by_address($address, $iptables); |
b08d4dde SS |
90 | |
91 | # Loop through the rules array and drop the firewall rules. | |
92 | foreach my $rule (@rules) { | |
93 | # Call iptables to delete the rule. | |
94 | system("$iptables --wait -D $chain $rule"); | |
95 | } | |
96 | } | |
97 | ||
98 | # | |
99 | ## The DoFlush subroutine. | |
100 | # | |
345e62e2 SS |
101 | ## Call this subroutine to entirely flush the IPtables chains for each |
102 | ## supported protocol version. | |
b08d4dde SS |
103 | # |
104 | sub DoFlush () { | |
345e62e2 SS |
105 | # Loop through the binaries hash to flush |
106 | foreach my $key (keys %binaries) { | |
107 | # Grab binary from hash and generate the absolute path | |
108 | # to the binary. | |
109 | my $iptables = join ("/", $bindir,$binaries{$key}); | |
110 | ||
111 | # Execute the binary if avail- and executeable. | |
112 | if (-x $iptables) { | |
113 | system("$iptables --wait -F $chain"); | |
114 | } | |
115 | } | |
b08d4dde SS |
116 | } |
117 | ||
118 | # | |
119 | ## Get rules subroutine. | |
120 | # | |
121 | ## This subroutine is used to get the rule position of the active | |
122 | ## firewall rules for a given address. Those position will be collected | |
123 | ## and returned in reversed order. | |
124 | # | |
345e62e2 SS |
125 | sub _get_rules_positions_by_address ($$) { |
126 | my ($address, $iptables) = @_; | |
b08d4dde SS |
127 | |
128 | # Array to store the rule positions. | |
129 | my @rules; | |
130 | ||
131 | # Call iptables and list all firewall rules. | |
132 | open(RULES, "$iptables --wait -L $chain -n -v --line-numbers |"); | |
133 | ||
134 | # Read input line by line. | |
135 | foreach my $line (<RULES>) { | |
136 | # Skip descriptive line. | |
137 | next if ($line =~ /^Chain/); | |
138 | next if ($line =~ /^ pkts/); | |
139 | ||
140 | # Generate array, based on the line content | |
141 | # (seperator is a single or multiple space's) | |
142 | my @comps = split(/\s{1,}/, $line); | |
143 | my ($pos, $pkts, $bytes, $target, $prot, $opt, $in, $out, $source, $destination) = @comps; | |
144 | ||
145 | # Compare the current source address with the given one. | |
146 | # If they are equal, the rule position will be added to the | |
147 | # rules array. | |
148 | if ($address eq $source) { | |
149 | push(@rules, $pos); | |
150 | } | |
151 | } | |
152 | ||
153 | # Reverse the array. | |
154 | my @reversed_rules = reverse @rules; | |
155 | ||
156 | # Return the reversed array. | |
157 | return @reversed_rules; | |
158 | } | |
159 | ||
e9c558fe SS |
160 | # |
161 | ## The _check_action function. | |
162 | # | |
163 | ## This private function is used to check if the given action is supported by | |
164 | ## the firewall engine. | |
165 | # | |
166 | sub _check_action ($) { | |
345e62e2 | 167 | my $action = shift; |
e9c558fe SS |
168 | |
169 | # Check if the recieved action is part of the supported_actions array. | |
170 | foreach my $item (@supported_actions) { | |
171 | # Exit the loop and return "nothing" if we found a match. | |
172 | if($item eq $action) { | |
345e62e2 SS |
173 | # Return "1" (True). |
174 | return 1; | |
e9c558fe SS |
175 | } |
176 | } | |
177 | ||
178 | # If we got here, the given action is not part of the array of supported | |
345e62e2 SS |
179 | # actions. Return nothing (False). |
180 | return; | |
181 | } | |
182 | ||
183 | # | |
184 | ## The _omit_binary function. | |
185 | # | |
186 | ## This private function is responsible for selecting and returning the correct | |
187 | ## IPtables binary, based on which kind of IP address has been given. | |
188 | # | |
189 | sub _omit_binary ($) { | |
190 | my $address = shift; | |
191 | ||
192 | # Obtain used protocol version, based on the | |
193 | # given IP address. | |
194 | my $proto_version = &Guardian::Base::DetectIPProtocolVersion($address); | |
195 | ||
196 | # Abort if the protocol version could not proper be detected. | |
197 | unless ($proto_version) { | |
198 | # Return nothing (False). | |
199 | return; | |
200 | } | |
201 | ||
202 | # Obtain which binary is responsible, based on the detected protocol version and | |
203 | # generate the full path to it. | |
204 | my $binary = join("/",$bindir,$binaries{$proto_version}); | |
205 | ||
206 | # Abort if the obtained binary is not avail- or executeable. | |
207 | unless (-x $binary) { | |
208 | # Return nothing (False). | |
209 | return; | |
210 | } | |
211 | ||
212 | # Return the detected and validated binary. | |
213 | return $binary; | |
e9c558fe SS |
214 | } |
215 | ||
b08d4dde | 216 | 1; |