line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Net::ISP::Balance; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
1071
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
32
|
|
4
|
1
|
|
|
1
|
|
6
|
use Fcntl ':flock'; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
157
|
|
5
|
1
|
|
|
1
|
|
8
|
use Carp 'croak','carp'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
52
|
|
6
|
1
|
|
|
1
|
|
685
|
use Data::Dumper; |
|
1
|
|
|
|
|
7043
|
|
|
1
|
|
|
|
|
61
|
|
7
|
1
|
|
|
1
|
|
8
|
no warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
64
|
|
8
|
|
|
|
|
|
|
|
9
|
1
|
|
|
1
|
|
678
|
eval 'use Net::Netmask'; |
|
1
|
|
|
|
|
203340
|
|
|
1
|
|
|
|
|
109
|
|
10
|
1
|
|
|
1
|
|
807
|
eval 'use Net::ISP::Balance::ConfigData'; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
23
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
our $VERSION = '1.31'; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=head1 NAME |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
Net::ISP::Balance - Support load balancing across multiple internet service providers |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=head1 SYNOPSIS |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
use Net::ISP::Balance; |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
# initialize the module with its configuration file |
23
|
|
|
|
|
|
|
my $bal = Net::ISP::Balance->new('/etc/network/balance.conf'); |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
$bal->verbose(1); # verbosely print routing and firewall |
26
|
|
|
|
|
|
|
# commands to STDERR before running them. |
27
|
|
|
|
|
|
|
$bal->echo_only(1); # echo commands to STDOUT; don't execute them. |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
# mark the balanced services that are up |
30
|
|
|
|
|
|
|
$bal->up('CABLE','DSL','SATELLITE'); |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
# write out routing and firewall commands |
33
|
|
|
|
|
|
|
$bal->set_routes_and_firewall(); |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
# write out a forwarding rule |
36
|
|
|
|
|
|
|
$bal->forward(80 => '192.168.10.35'); # forward web requests to this host |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
# write out an arbitrary routing rule |
39
|
|
|
|
|
|
|
$bal->ip_route('add 192.168.100.1 dev eth0 src 198.162.1.14'); |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
# write out an arbitrary iptables rule |
42
|
|
|
|
|
|
|
$bal->iptables('-A INCOMING -p tcp --dport 6000 -j REJECT'); |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
# get information about all services |
45
|
|
|
|
|
|
|
my @s = $bal->service_names; |
46
|
|
|
|
|
|
|
for my $s (@s) { |
47
|
|
|
|
|
|
|
print $bal->dev($s); |
48
|
|
|
|
|
|
|
print $bal->vdev($s); |
49
|
|
|
|
|
|
|
print $bal->ip($s); |
50
|
|
|
|
|
|
|
print $bal->gw($s); |
51
|
|
|
|
|
|
|
print $bal->net($s); |
52
|
|
|
|
|
|
|
print $bal->fwmark($s); |
53
|
|
|
|
|
|
|
print $bal->table($s); |
54
|
|
|
|
|
|
|
print $bal->running($s); |
55
|
|
|
|
|
|
|
print $bal->weight($s); |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=cut |
59
|
|
|
|
|
|
|
|
60
|
1
|
|
|
1
|
|
7
|
use Carp; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
10189
|
|
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
=head1 USAGE |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
This library supports load_balance.pl, a script to load-balance a home |
65
|
|
|
|
|
|
|
network across two or more Internet Service Providers (ISP). The |
66
|
|
|
|
|
|
|
load_balance.pl script can be found in the bin subdirectory of this |
67
|
|
|
|
|
|
|
distribution. Installation and configuration instructions can be found |
68
|
|
|
|
|
|
|
at http://lstein.github.io/Net-ISP-Balance/. |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
=head1 CONFIGURATION FILE |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
This module reads a configuration file with the following format: |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
#service device role ping-ip weight gateway |
75
|
|
|
|
|
|
|
CABLE eth0 isp 173.194.43.95 1 173.193.43.1 |
76
|
|
|
|
|
|
|
DSL ppp0 isp 173.194.43.95 1 |
77
|
|
|
|
|
|
|
LAN1 eth1 lan |
78
|
|
|
|
|
|
|
LAN2 eth2 lan |
79
|
|
|
|
|
|
|
LAN3 eth3 lan |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
The first column is a service name that is used to bring up or down |
83
|
|
|
|
|
|
|
the needed routes and firewall rules. |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
The second column is the name of the network interface device that |
86
|
|
|
|
|
|
|
connects to that service. |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
The third column is either "isp" or "lan". There may be any number of |
89
|
|
|
|
|
|
|
these. The script will firewall traffic passing through any of the |
90
|
|
|
|
|
|
|
ISPs, and will load balance traffic among them. Traffic can flow |
91
|
|
|
|
|
|
|
freely among any of the interfaces marked as belonging to a LAN. |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
The fourth column (optional) is the IP address of a host that can be |
94
|
|
|
|
|
|
|
periodically pinged to test the integrity of each ISP connection. If |
95
|
|
|
|
|
|
|
too many pings failed, the service will be brought down and all |
96
|
|
|
|
|
|
|
traffic routed through the remaining ISP(s). The service will continue |
97
|
|
|
|
|
|
|
to be monitored and will be brought up when it is once again |
98
|
|
|
|
|
|
|
working. Choose a host that is not likely to go offline for reasons |
99
|
|
|
|
|
|
|
unrelated to your network connectivity, such as google.com, or the |
100
|
|
|
|
|
|
|
ISP's web site. If this column is absent or marked "default", then the |
101
|
|
|
|
|
|
|
host will default to www.google.ca. |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
The fifth column (optional) is a weight to assign to the service, and |
104
|
|
|
|
|
|
|
is only valid for ISP rows. If weights are equal, traffic will be |
105
|
|
|
|
|
|
|
apportioned evenly between the two routes. Increase a weight to favor |
106
|
|
|
|
|
|
|
one ISP over the others. For example, if "CABLE" has a weight of 2 and |
107
|
|
|
|
|
|
|
"DSL" has a weight of 1, then twice as much traffic will flow through |
108
|
|
|
|
|
|
|
the CABLE service. If this column is omitted or marked "default", then |
109
|
|
|
|
|
|
|
equal weights are assumed. |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
The sixth column (optional) is the gateway for this service using |
112
|
|
|
|
|
|
|
dotted IP notation. If absent or named "default", the system will |
113
|
|
|
|
|
|
|
attempt to determine the proper gateway automatically. Note the |
114
|
|
|
|
|
|
|
algorithm relies on the fact that the gateway is almost always the |
115
|
|
|
|
|
|
|
first address in the IP range for the subnetwork. If this is not the |
116
|
|
|
|
|
|
|
case, then routing through the interface won't work properly. Add the |
117
|
|
|
|
|
|
|
correct gateway IP address manually to correct this. |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
If this package is running on a single Internet-connected host, not a |
120
|
|
|
|
|
|
|
router, then do not include a "lan" line. |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
In addition to the main table, there are several configuration options |
123
|
|
|
|
|
|
|
that follow the format "configuration_name=value": |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=over 4 |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
=item forwarding_group= |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
The forwarding_group configuration option defines a set of services |
130
|
|
|
|
|
|
|
that the router is allowed to forward packets among. Provide a |
131
|
|
|
|
|
|
|
space-delimited set of service names or one or more of the |
132
|
|
|
|
|
|
|
abbreviations ":isp" and ":lan". ":isp" is an abbreviation for all |
133
|
|
|
|
|
|
|
ISP services, while ":lan" is an abbreviation for all LAN services. So |
134
|
|
|
|
|
|
|
for example, the two configuration lines below will allow forwarding |
135
|
|
|
|
|
|
|
of packets between LAN1, LAN2, LAN3 and both ISPs. LAN4 will be |
136
|
|
|
|
|
|
|
granted access to both ISPs but won't be able to exchange packets with |
137
|
|
|
|
|
|
|
LANs 1 through 3: |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
forwarding_group=LAN1 LAN2 LAN3 :isp |
140
|
|
|
|
|
|
|
forwarding_group=LAN4 :isp |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
If no forwarding_group options are defined, then the router will |
143
|
|
|
|
|
|
|
forward packets among all LANs and ISP interfaces. It is equivalent to |
144
|
|
|
|
|
|
|
this: |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
forwarding_group=:lan :isp |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
=item warn_email= |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
Warn_email provides an email address to send notification messages to |
151
|
|
|
|
|
|
|
if the status of a link changes (goes down, or comes back up). You |
152
|
|
|
|
|
|
|
must have the "mail" program installed and configured for this to |
153
|
|
|
|
|
|
|
work. |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=item interval_ms= |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
Indicates how often to check the ping host for each ISP. |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
=item min_packet_loss= |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=item max_packet_loss= |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
These define the minimum and maximum packet losses required to declare |
164
|
|
|
|
|
|
|
a link up or down. |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=item min_successive_pkts_rcvd= |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=item max_successive_pkts_recvd= |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
These define the minimum and maximum numbers of |
171
|
|
|
|
|
|
|
successively-transmitted pings that must be returned in order to |
172
|
|
|
|
|
|
|
declare a link up or down. |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
=item long_down_time= |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
This is a value in seconds after a service that has gone down is |
177
|
|
|
|
|
|
|
considered to have been down for a long time. You may optionally run a |
178
|
|
|
|
|
|
|
series of shell scripts when this has occurred (see below). |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=back |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
=head1 FREQUENTLY-USED METHODS |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
Here are the class methods for this module that can be called on the |
185
|
|
|
|
|
|
|
class name. |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
=head2 $bal = Net::ISP::Balance->new('/path/to/config_file.conf'); |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
Creates a new balancer object. |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
The first optional argument is the balancer configuration file, which |
192
|
|
|
|
|
|
|
defaults to /etc/network/balance.conf on Ubuntu/Debian-derived |
193
|
|
|
|
|
|
|
systems, and /etc/sysconfig/network-scripts/balance.conf on |
194
|
|
|
|
|
|
|
RedHat/CentOS-derived systems. From hereon, we'll refer to the base of |
195
|
|
|
|
|
|
|
the various configuration files as $ETC_NETWORK. |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
=cut |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
sub new { |
200
|
2
|
|
|
2
|
1
|
1319
|
my $class = shift; |
201
|
2
|
|
|
|
|
10
|
my ($conf,%options) = @_; |
202
|
2
|
|
33
|
|
|
21
|
$conf ||= $class->default_conf_file; |
203
|
2
|
50
|
33
|
|
|
58
|
$conf && -r $conf || croak 'Must provide a readable configuration file path'; |
204
|
|
|
|
|
|
|
my $self = bless { |
205
|
|
|
|
|
|
|
verbose => 0, |
206
|
|
|
|
|
|
|
echo_only => 0, |
207
|
|
|
|
|
|
|
services => {}, |
208
|
|
|
|
|
|
|
rules_directory => $class->default_rules_directory, |
209
|
|
|
|
|
|
|
lsm_conf_file => $class->default_lsm_conf_file, |
210
|
|
|
|
|
|
|
lsm_scripts_dir => $class->default_lsm_scripts_dir, |
211
|
|
|
|
|
|
|
bal_conf_file => $conf, |
212
|
|
|
|
|
|
|
keep_custom_chains => 1, |
213
|
|
|
|
|
|
|
dummy_data => $options{dummy_test_data}, |
214
|
|
|
|
|
|
|
dev_lookup_retries => $options{dev_lookup_retries}, |
215
|
|
|
|
|
|
|
dev_lookup_retry_delay => $options{dev_lookup_retry_delay}, |
216
|
2
|
|
33
|
|
|
15
|
},ref $class || $class; |
217
|
|
|
|
|
|
|
|
218
|
2
|
|
|
|
|
11
|
$self->_parse_configuration_file($conf); |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
# Instead of potentially timing out on new(), we collect information on all |
221
|
|
|
|
|
|
|
# interfaces that are currently up. We do this again with the timeout before |
222
|
|
|
|
|
|
|
# actually changing the routing table, when it is critical that all interfaces |
223
|
|
|
|
|
|
|
# be configured. |
224
|
|
|
|
|
|
|
# $self->_collect_interfaces_retry(); # try to collect interfaces over 10 seconds |
225
|
2
|
|
|
|
|
4
|
my %ifs; |
226
|
2
|
|
|
|
|
11
|
$self->_collect_interfaces(\%ifs); |
227
|
2
|
|
|
|
|
6
|
$self->{services} = \%ifs; |
228
|
|
|
|
|
|
|
|
229
|
2
|
|
|
|
|
10
|
return $self; |
230
|
|
|
|
|
|
|
} |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
=head2 $bal->set_routes_and_firewall |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
Once the Balance objecty is created, call set_routes_and_firewall() to |
235
|
|
|
|
|
|
|
configure the routing tables and firewall for load balancing. These |
236
|
|
|
|
|
|
|
rules will either be executed on the system, or printed to standard |
237
|
|
|
|
|
|
|
output as a series of shell script commands if echo_only() is set to |
238
|
|
|
|
|
|
|
true. |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
The routing tables and firewall rules are based on the configuration |
241
|
|
|
|
|
|
|
described in $ETC_NETWORK/balance.conf. You may add custom routes and |
242
|
|
|
|
|
|
|
rules by creating files in $ETC_NETWORK/balance/routes and |
243
|
|
|
|
|
|
|
$ETC_NETWORK/balance/firewall. The former contains a series of files |
244
|
|
|
|
|
|
|
or perl scripts that define additional routing rules. The latter |
245
|
|
|
|
|
|
|
contains files or perl scripts that define additional firewall rules. |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
Files located in $ETC_NETWORK/balance/pre-run will be executed AFTER |
248
|
|
|
|
|
|
|
load_balance.pl has cleared the routing table and firewall, but before |
249
|
|
|
|
|
|
|
it has emitted any any route/firewall commands. Files located in |
250
|
|
|
|
|
|
|
in $ETC_NETWORK/balance/post-run will be run after load_balance.pl is |
251
|
|
|
|
|
|
|
finished. |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
Any files you put into these directories will be read in alphabetic |
254
|
|
|
|
|
|
|
order and added to the routes and/or firewall rules emitted by the |
255
|
|
|
|
|
|
|
load balancing script.Contained in this directory are subdirectories named "routes" and |
256
|
|
|
|
|
|
|
"firewall". The former contains a series of files or perl scripts that |
257
|
|
|
|
|
|
|
define additional routing rules. The latter contains files or perl |
258
|
|
|
|
|
|
|
scripts that define additional firewall rules. |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
Note that files ending in ~ or starting with # are treated as autosave files |
261
|
|
|
|
|
|
|
and ignored. |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
A typical routing rules file will look like the example shown |
264
|
|
|
|
|
|
|
below. |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
# file: /etc/network/balance/01.my_routes |
267
|
|
|
|
|
|
|
ip route add 192.168.100.1 dev eth0 src 198.162.1.14 |
268
|
|
|
|
|
|
|
ip route add 192.168.1.0/24 dev eth2 src 10.0.0.4 |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
Each line will be sent to the shell, and it is intended (but not |
271
|
|
|
|
|
|
|
required) that these be calls to the "ip" command. General shell |
272
|
|
|
|
|
|
|
scripting constructs are not allowed here. |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
A typical firewall rules file will look like the example shown here: |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
# file: /etc/network/firewall/01.my_firewall_rules |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
# accept incoming telnet connections to the router |
279
|
|
|
|
|
|
|
iptable -A INPUT -p tcp --syn --dport telnet -j ACCEPT |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
# masquerade connections to the DSL modem's control interface |
282
|
|
|
|
|
|
|
iptables -t nat -A POSTROUTING -o eth2 -j MASQUERADE |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
You may also insert routing and firewall rules via fragments of Perl |
285
|
|
|
|
|
|
|
code, which is convenient because you don't have to hard-code any |
286
|
|
|
|
|
|
|
network addresses and can make use of a variety of shortcuts. To do |
287
|
|
|
|
|
|
|
this, simply end the file's name with .pl and make it executable. |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
Here's an example that defines a series of port forwarding rules for |
290
|
|
|
|
|
|
|
incoming connections: |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
# file: /etc/network/firewall/02.forwardings.pl |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
$B->forward(80 => '192.168.10.35'); # forward port 80 to internal web server |
295
|
|
|
|
|
|
|
$B->forward(443=> '192.168.10.35'); # forward port 443 to |
296
|
|
|
|
|
|
|
$B->forward(23 => '192.168.10.35:22'); # forward port 23 to ssh on web sever |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
The main thing to know is that on entry to the script the global |
299
|
|
|
|
|
|
|
variable $B will contain an initialized instance of a |
300
|
|
|
|
|
|
|
Net::ISP::Balance object. You may then make method calls on this |
301
|
|
|
|
|
|
|
object to emit firewall and routing rules. |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
A typical routing rules file will look like the example shown |
304
|
|
|
|
|
|
|
below. |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
# file: /etc/network/balance/01.my_routes |
307
|
|
|
|
|
|
|
ip route add 192.168.100.1 dev eth0 src 198.162.1.14 |
308
|
|
|
|
|
|
|
ip route add 192.168.1.0/24 dev eth2 src 10.0.0.4 |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
Each line will be sent to the shell, and it is intended (but not |
311
|
|
|
|
|
|
|
required) that these be calls to the "ip" command. General shell |
312
|
|
|
|
|
|
|
scripting constructs are not allowed here. |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
A typical firewall rules file will look like the example shown here: |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
# file: /etc/network/firewall/01.my_firewall_rules |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
# accept incoming telnet connections to the router |
319
|
|
|
|
|
|
|
iptable -A INPUT -p tcp --syn --dport telnet -j ACCEPT |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
# masquerade connections to the DSL modem's control interface |
322
|
|
|
|
|
|
|
iptables -t nat -A POSTROUTING -o eth2 -j MASQUERADE |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
You may also insert routing and firewall rules via fragments of Perl |
325
|
|
|
|
|
|
|
code, which is convenient because you don't have to hard-code any |
326
|
|
|
|
|
|
|
network addresses and can make use of a variety of shortcuts. To do |
327
|
|
|
|
|
|
|
this, simply end the file's name with .pl and make it executable. |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
Here's an example that defines a series of port forwarding rules for |
330
|
|
|
|
|
|
|
incoming connections: |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
# file: /etc/network/firewall/02.forwardings.pl |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
$B->forward(80 => '192.168.10.35'); # forward port 80 to internal web server |
335
|
|
|
|
|
|
|
$B->forward(443=> '192.168.10.35'); # forward port 443 to |
336
|
|
|
|
|
|
|
$B->forward(23 => '192.168.10.35:22'); # forward port 23 to ssh on web sever |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
The main thing to know is that on entry to the script the global |
339
|
|
|
|
|
|
|
variable $B will contain an initialized instance of a |
340
|
|
|
|
|
|
|
Net::ISP::Balance object. You may then make method calls on this |
341
|
|
|
|
|
|
|
object to emit firewall and routing rules. |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
=cut |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
sub set_routes_and_firewall { |
346
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
347
|
|
|
|
|
|
|
|
348
|
0
|
|
|
|
|
0
|
$self->save_routing_and_firewall(); |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
# first disable forwarding |
351
|
0
|
|
|
|
|
0
|
$self->enable_forwarding(0); |
352
|
|
|
|
|
|
|
|
353
|
0
|
|
|
|
|
0
|
$self->_collect_interfaces_retry(); |
354
|
0
|
0
|
|
|
|
0
|
if ($self->isp_services) { |
355
|
0
|
|
|
|
|
0
|
$self->pre_run_rules(); |
356
|
0
|
|
|
|
|
0
|
$self->set_routes(); |
357
|
0
|
|
|
|
|
0
|
$self->set_firewall(); |
358
|
0
|
|
|
|
|
0
|
$self->enable_forwarding(1); |
359
|
0
|
|
|
|
|
0
|
$self->post_run_rules(); |
360
|
|
|
|
|
|
|
} else { |
361
|
0
|
|
|
|
|
0
|
warn "No ISP services seem to be up. Restoring routing tables and firewall.\n"; |
362
|
0
|
0
|
|
|
|
0
|
$self->restore_routing_and_firewall() unless $self->echo_only; |
363
|
0
|
|
|
|
|
0
|
return; |
364
|
|
|
|
|
|
|
} |
365
|
|
|
|
|
|
|
} |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
sub save_routing_and_firewall { |
368
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
369
|
|
|
|
|
|
|
|
370
|
0
|
|
|
|
|
0
|
$self->{stored_routes} = []; |
371
|
0
|
|
|
|
|
0
|
$self->{stored_rules} = ''; |
372
|
0
|
|
|
|
|
0
|
$self->{stored_firewall} = ''; |
373
|
|
|
|
|
|
|
|
374
|
0
|
0
|
|
|
|
0
|
open my $f,"ip route show table all|" or die $!; # binary |
375
|
0
|
|
|
|
|
0
|
while (<$f>) { |
376
|
0
|
|
|
|
|
0
|
chomp; |
377
|
0
|
0
|
|
|
|
0
|
next if /unreachable/; |
378
|
0
|
0
|
|
|
|
0
|
next if /proto none/; |
379
|
0
|
|
|
|
|
0
|
unshift @{$self->{stored_routes}},$_; |
|
0
|
|
|
|
|
0
|
|
380
|
|
|
|
|
|
|
} |
381
|
0
|
|
|
|
|
0
|
close $f; |
382
|
|
|
|
|
|
|
|
383
|
0
|
0
|
|
|
|
0
|
open $f,"ip rule show|" or die $!; # text |
384
|
0
|
|
|
|
|
0
|
while (<$f>) { |
385
|
0
|
|
|
|
|
0
|
$self->{stored_rules} .= $_; |
386
|
|
|
|
|
|
|
} |
387
|
0
|
|
|
|
|
0
|
close $f; |
388
|
|
|
|
|
|
|
|
389
|
0
|
0
|
|
|
|
0
|
open $f,"iptables-save|" or die $!; # text |
390
|
0
|
|
|
|
|
0
|
while (<$f>) { |
391
|
0
|
|
|
|
|
0
|
$self->{stored_firewall} .= $_; |
392
|
|
|
|
|
|
|
} |
393
|
0
|
|
|
|
|
0
|
close $f; |
394
|
|
|
|
|
|
|
} |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
sub restore_routing_and_firewall { |
397
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
398
|
|
|
|
|
|
|
|
399
|
0
|
|
|
|
|
0
|
$self->_initialize_routes(); |
400
|
0
|
0
|
|
|
|
0
|
if ($self->{stored_routes}) { |
401
|
0
|
|
|
|
|
0
|
for (@{$self->{stored_routes}}) { |
|
0
|
|
|
|
|
0
|
|
402
|
0
|
|
|
|
|
0
|
$self->ip_route("add $_"); |
403
|
|
|
|
|
|
|
} |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
|
406
|
0
|
0
|
|
|
|
0
|
if ($self->{stored_rules}) { |
407
|
0
|
|
|
|
|
0
|
my @rules = split "\n",$self->{stored_rules}; |
408
|
0
|
|
|
|
|
0
|
for my $r (@rules) { |
409
|
0
|
|
|
|
|
0
|
my ($priority,$rule) = $r =~ /^(\d+):\s*(.+)/; |
410
|
0
|
0
|
|
|
|
0
|
next if $priority == 32766; # these are created by _initialize! |
411
|
0
|
0
|
|
|
|
0
|
next if $priority == 32767; |
412
|
0
|
|
|
|
|
0
|
$self->ip_rule('add',$rule,"priority $priority"); |
413
|
|
|
|
|
|
|
} |
414
|
|
|
|
|
|
|
} |
415
|
|
|
|
|
|
|
|
416
|
0
|
0
|
|
|
|
0
|
if ($self->{stored_firewall}) { |
417
|
0
|
0
|
|
|
|
0
|
open my $f,"|iptables-restore" or die $!; |
418
|
0
|
|
|
|
|
0
|
print $f $self->{stored_firewall}; |
419
|
0
|
|
|
|
|
0
|
close $f; |
420
|
|
|
|
|
|
|
} |
421
|
|
|
|
|
|
|
} |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
=head2 $verbose = $bal->verbose([boolean]); |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
sub bal_conf_file { my $self = shift; my $d = $self->{bal_conf_file}; |
426
|
|
|
|
|
|
|
$self->{bal_conf_file} = shift if @_; $d; } Get/set verbosity of |
427
|
|
|
|
|
|
|
the module. If verbose is true, then firewall and routing rules |
428
|
|
|
|
|
|
|
will be echoed to STDERR before being executed on the system. |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
=cut |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
sub verbose { |
433
|
252
|
|
|
252
|
1
|
324
|
my $self = shift; |
434
|
252
|
|
|
|
|
343
|
my $d = $self->{verbose}; |
435
|
252
|
50
|
|
|
|
438
|
$self->{verbose} = shift if @_; |
436
|
252
|
|
|
|
|
466
|
$d; |
437
|
|
|
|
|
|
|
} |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
=head2 $echo = $bal->echo_only([boolean]); |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
Get/set the echo_only flag. If this is true (default false), then |
442
|
|
|
|
|
|
|
routing and firewall rules will be printed to STDOUT rathar than being |
443
|
|
|
|
|
|
|
executed. |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
=cut |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
sub echo_only { |
448
|
232
|
|
|
232
|
1
|
957
|
my $self = shift; |
449
|
232
|
|
|
|
|
306
|
my $d = $self->{echo_only}; |
450
|
232
|
100
|
|
|
|
393
|
$self->{echo_only} = shift if @_; |
451
|
232
|
|
|
|
|
425
|
$d; |
452
|
|
|
|
|
|
|
} |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
=head2 $mode = $bal->operating_mode([$mode]) |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
Set or interrogate the operating mode. Will return one of "balanced" |
457
|
|
|
|
|
|
|
(currently the default) or "failover". This corresponds to the "mode" |
458
|
|
|
|
|
|
|
option in the configuration file. If the option is neither "balanced" |
459
|
|
|
|
|
|
|
nor "failover", then "balanced" is chosen (be warned!) |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
In "balanced" mode, packets are distributed among WAN interfaces |
462
|
|
|
|
|
|
|
proportional to assigned weights. In "failover" mode, the interface |
463
|
|
|
|
|
|
|
with the heighest weight is chosen to route ALL packets. If it goes |
464
|
|
|
|
|
|
|
down, then the interface with the next heighest weight is used, and so |
465
|
|
|
|
|
|
|
forth. |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
=cut |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
sub operating_mode { |
470
|
10
|
|
|
10
|
1
|
363
|
my $self = shift; |
471
|
10
|
|
|
|
|
20
|
my $d = $self->{operating_mode}; |
472
|
10
|
100
|
|
|
|
28
|
$self->{operating_mode} = shift if @_; |
473
|
10
|
100
|
100
|
|
|
56
|
return 'failover' if $d && $d =~ /failover/i; |
474
|
6
|
|
|
|
|
19
|
return 'balanced'; |
475
|
|
|
|
|
|
|
} |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
=head2 $retries = $bal->dev_lookup_retries([$retries]) |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
Get/set the number of times the library will try to look up an interface |
480
|
|
|
|
|
|
|
that is not up or does not have an IP address. Default is 10 |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
=cut |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
sub dev_lookup_retries { |
485
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
486
|
0
|
|
0
|
|
|
0
|
my $d = $self->{dev_lookup_retries} || 10; |
487
|
0
|
0
|
|
|
|
0
|
$self->{dev_lookup_retries} = shift if @_; |
488
|
0
|
|
|
|
|
0
|
$d; |
489
|
|
|
|
|
|
|
} |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
=head2 $seconds = $bal->dev_lookup_retry_delay([$seconds]) |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
Get/set the number of seconds between retries when an interface is not up |
494
|
|
|
|
|
|
|
or is missing an IP address. Default is 1. |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
=cut |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
sub dev_lookup_retry_delay { |
499
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
500
|
0
|
|
0
|
|
|
0
|
my $d = $self->{dev_lookup_retry_delay} || 1; |
501
|
0
|
0
|
|
|
|
0
|
$self->{dev_lookup_retry_delay} = shift if @_; |
502
|
0
|
|
|
|
|
0
|
$d; |
503
|
|
|
|
|
|
|
} |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
=head2 $boolean = $bal->keep_custom_chains([boolean]); |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
Get/set the keep_custom_chains flag. If this is true (default), then |
508
|
|
|
|
|
|
|
any custom iptables chains, such as those created by miniunpnpd or |
509
|
|
|
|
|
|
|
fail2ban, will be restored after execution of the firewall rules. If |
510
|
|
|
|
|
|
|
false, then these rules will not be restored. |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
=cut |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
sub keep_custom_chains { |
515
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
516
|
0
|
|
|
|
|
0
|
my $d = $self->{keep_custom_chains}; |
517
|
0
|
0
|
|
|
|
0
|
$self->{keep_custom_chains} = shift if @_; |
518
|
0
|
|
|
|
|
0
|
$d; |
519
|
|
|
|
|
|
|
} |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
=head2 $result_code = $bal->sh(@args) |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
Pass @args to the shell for execution. If echo_only() is set to true, |
524
|
|
|
|
|
|
|
the command will not be executed, but instead be printed to standard |
525
|
|
|
|
|
|
|
output. |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
Example: |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
$bal->sh('ip rule flush'); |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
The result code is the same as CORE::system(). |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
=cut |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
sub sh { |
536
|
230
|
|
|
230
|
1
|
354
|
my $self = shift; |
537
|
230
|
|
|
|
|
431
|
my @args = @_; |
538
|
230
|
|
|
|
|
478
|
my $arg = join ' ',@args; |
539
|
230
|
|
|
|
|
313
|
chomp($arg); |
540
|
230
|
50
|
|
|
|
399
|
print STDERR "$arg\n" if $self->verbose; |
541
|
230
|
50
|
|
|
|
401
|
if ($self->echo_only) { |
542
|
230
|
|
|
|
|
306
|
$arg .= "\n"; |
543
|
230
|
|
|
|
|
725
|
print $arg; |
544
|
|
|
|
|
|
|
} else { |
545
|
0
|
|
|
|
|
0
|
system $arg; |
546
|
|
|
|
|
|
|
} |
547
|
|
|
|
|
|
|
} |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
=head2 $bal->iptables(@args) |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
Invoke sh() to call "iptables @args". |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
Example: |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
$bal->iptables('-A OUTPUT -o eth0 -j DROP'); |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
You may pass an array reference to iptables(), in which case iptables |
558
|
|
|
|
|
|
|
is called on each member of the array in turn. |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
Example: |
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
$bal->iptables(['-P OUTPUT DROP', |
563
|
|
|
|
|
|
|
'-P INPUT DROP', |
564
|
|
|
|
|
|
|
'-P FORWARD DROP']); |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
Note that the method keeps track of rules; if you try to enter the |
567
|
|
|
|
|
|
|
same iptables rule more than once the redundant ones will be ignored. |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
=cut |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
my %seen_rule; |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
sub iptables { |
574
|
164
|
|
|
164
|
1
|
230
|
my $self = shift; |
575
|
164
|
100
|
|
|
|
289
|
if (ref $_[0] eq 'ARRAY') { |
576
|
4
|
|
50
|
|
|
6
|
$seen_rule{$_}++ || $self->sh('iptables',$_) foreach @{$_[0]}; |
|
4
|
|
|
|
|
18
|
|
577
|
|
|
|
|
|
|
} else { |
578
|
160
|
100
|
|
|
|
608
|
$seen_rule{"@_"}++ || $self->sh('iptables',@_) |
579
|
|
|
|
|
|
|
} |
580
|
|
|
|
|
|
|
} |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
sub _iptables_add_rule { |
583
|
13
|
|
|
13
|
|
17
|
my $self = shift; |
584
|
13
|
|
|
|
|
37
|
my ($operation,$chain,$table,@args) = @_; |
585
|
13
|
50
|
|
|
|
23
|
croak "You must provide a chain name" unless $chain; |
586
|
13
|
50
|
|
|
|
42
|
my $op = $operation eq 'append' ? '-A' |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
587
|
|
|
|
|
|
|
:$operation eq 'delete' ? '-D' |
588
|
|
|
|
|
|
|
:$operation eq 'check ' ? '-C' |
589
|
|
|
|
|
|
|
:$operation eq 'insert' ? '-I' |
590
|
|
|
|
|
|
|
:'-A'; |
591
|
|
|
|
|
|
|
|
592
|
13
|
|
|
|
|
20
|
my $command = ''; |
593
|
13
|
100
|
|
|
|
25
|
$command .= "-t $table " if $table; |
594
|
13
|
|
|
|
|
24
|
$command .= "$op $chain "; |
595
|
13
|
|
|
|
|
25
|
$command .= $self->_process_iptable_options(@args); |
596
|
13
|
|
|
|
|
32
|
$self->iptables($command); |
597
|
|
|
|
|
|
|
} |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
sub iptables_append { |
600
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
601
|
0
|
|
|
|
|
0
|
my ($table,$chain,@args) = @_; |
602
|
0
|
|
|
|
|
0
|
$self->_iptables_add_rule('append',$table,$chain,@args); |
603
|
|
|
|
|
|
|
} |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
sub iptables_delete { |
606
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
607
|
0
|
|
|
|
|
0
|
my ($table,$chain,@args) = @_; |
608
|
0
|
|
|
|
|
0
|
$self->_iptables_add_rule('delete',$table,$chain,@args); |
609
|
|
|
|
|
|
|
} |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
sub iptables_check { |
612
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
613
|
0
|
|
|
|
|
0
|
my ($table,$chain,@args) = @_; |
614
|
0
|
|
|
|
|
0
|
$self->_iptables_add_rule('check',$table,$chain,@args); |
615
|
|
|
|
|
|
|
} |
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
sub iptables_insert { |
618
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
619
|
0
|
|
|
|
|
0
|
my ($table,$chain,@args) = @_; |
620
|
0
|
|
|
|
|
0
|
$self->_iptables_add_rule('insert',$table,$chain,@args); |
621
|
|
|
|
|
|
|
} |
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
=head2 $bal->firewall_rule($chain,$table,@args) |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
Issue an iptables firewall rule. |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
$chain -- The chain to apply the rule to, e.g. "INPUT". |
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
$table -- The table to apply the rule to, e.g. "nat". Undef defaults to |
630
|
|
|
|
|
|
|
the standard "filter" table. |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
@args -- The other arguments to pass to iptables. |
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
Here is a typical example of blocking incoming connections to port 25: |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
$bal->firewall_rule(INPUT=>undef,-p=>'tcp',-dport=>25,-j=>'REJECT'); |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
This will issue the following command: |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
iptables -A INPUT -p tcp --dport 25 -j REJECT |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
The default operation is to append the rule to the chain using |
643
|
|
|
|
|
|
|
-A. This can be changed by passing $bal->firewall_op() any of the |
644
|
|
|
|
|
|
|
strings "append", "delete", "insert" or "check". Subsequent calls to |
645
|
|
|
|
|
|
|
firewall_rule() will return commands for the indicated function: |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
$bal->firewall_op('delete'); |
648
|
|
|
|
|
|
|
$bal->firewall_rule(INPUT=>undef,-p=>'tcp',-dport=>25,-j=>'REJECT'); |
649
|
|
|
|
|
|
|
# gives iptables -A INPUT -p tcp --dport 25 -j REJECT |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
If you want to apply a series of deletes and then revert to the |
652
|
|
|
|
|
|
|
original append behavior, then it is easiest to localize the hash key |
653
|
|
|
|
|
|
|
"firewall_op": |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
{ |
656
|
|
|
|
|
|
|
local $bal->{firewall_op} = 'delete'; |
657
|
|
|
|
|
|
|
$bal->firewall_rule(INPUT=>undef,-dport=>25,-j=>'ACCEPT'); |
658
|
|
|
|
|
|
|
$bal->firewall_rule(INPUT->undef,-dport=>80,-j=>'ACCEPT'); |
659
|
|
|
|
|
|
|
} |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
$bal->firewall_rule(INPUT=>undef,-dport=>25,-j=>'DROP'); |
662
|
|
|
|
|
|
|
$bal->firewall_rule(INPUT=>undef,-dport=>80,-j=>'DROP'); |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
=cut |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
sub firewall_rule { |
667
|
13
|
|
|
13
|
1
|
20
|
my $self = shift; |
668
|
13
|
|
|
|
|
36
|
my ($chain,$table,@args) = @_; |
669
|
13
|
|
|
|
|
24
|
my $operation = $self->firewall_op(); |
670
|
13
|
|
|
|
|
29
|
$self->_iptables_add_rule($operation,$chain,$table,@args); |
671
|
|
|
|
|
|
|
} |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
sub firewall_op { |
674
|
13
|
|
|
13
|
0
|
19
|
my $self = shift; |
675
|
13
|
50
|
|
|
|
25
|
if (@_) { |
676
|
0
|
|
|
|
|
0
|
$self->{firewall_op} = shift; |
677
|
0
|
|
|
|
|
0
|
return; |
678
|
|
|
|
|
|
|
} |
679
|
13
|
|
100
|
|
|
32
|
my $d = $self->{firewall_op} || 'append'; |
680
|
13
|
|
|
|
|
23
|
return $d; |
681
|
|
|
|
|
|
|
} |
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
=head2 $bal->force_route($service_or_device,@selectors) |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
The force_route() method issues iptables commands that will force |
686
|
|
|
|
|
|
|
certain traffic to travel over a particular ISP service or network |
687
|
|
|
|
|
|
|
device. This is useful, for example, when one of your ISPs acts as |
688
|
|
|
|
|
|
|
your e-mail relay and only accepts connections from the IP address |
689
|
|
|
|
|
|
|
it assigns. |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
$service_or_device is the symbolic name of an ISP service |
692
|
|
|
|
|
|
|
(e.g. "CABLE") or a network device that a service is attached to |
693
|
|
|
|
|
|
|
(e.g. "eth0"). |
694
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
@selectors are a series of options that will be passed to |
696
|
|
|
|
|
|
|
iptables to select the routing of packets. For example, to forward all |
697
|
|
|
|
|
|
|
outgoing mail (destined to port 25) to the "CABLE" ISP, you would |
698
|
|
|
|
|
|
|
write: |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
$bal->force_route('CABLE','-p'=>'tcp','--syn','--dport'=>25); |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
@selectors is a series of optional arguments that will be passed to |
703
|
|
|
|
|
|
|
iptables on the command line. They will simply be space-separated, and |
704
|
|
|
|
|
|
|
so the following is equivalent to the previous example: |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
$bal->force_route('CABLE','-p tcp --syn --dport 25'); |
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
Bare arguments that begin with a leading hyphen and are followed by |
709
|
|
|
|
|
|
|
two or more alphanumeric characters are automatically converted into |
710
|
|
|
|
|
|
|
double-hyphen arguments. This allows you to simplify commands |
711
|
|
|
|
|
|
|
slightly. The following is equivalent to the previous examples: |
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
$bal->force_route('CABLE',-p=>'tcp',-syn,-dport=>25); |
714
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
You can delete force_route rules by setting firewall_op() to 'delete': |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
$bal->firewall_op('delete'); |
718
|
|
|
|
|
|
|
$bal->force_route('CABLE',-p=>'tcp',-syn,-dport=>25); |
719
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
=cut |
721
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
sub force_route { |
723
|
2
|
|
|
2
|
1
|
1472
|
my $self = shift; |
724
|
2
|
|
|
|
|
7
|
my ($service_or_device,@selectors) = @_; |
725
|
|
|
|
|
|
|
|
726
|
2
|
50
|
|
|
|
6
|
my $service = $self->_service_or_device($service_or_device) |
727
|
|
|
|
|
|
|
or croak "did not recognize $service_or_device as a service or a device"; |
728
|
|
|
|
|
|
|
|
729
|
2
|
|
|
|
|
7
|
my $dest = $self->mark_table($service); |
730
|
2
|
|
|
|
|
6
|
my $selectors = $self->_process_iptable_options(@selectors); |
731
|
2
|
|
|
|
|
8
|
$self->firewall_rule(PREROUTING=>'mangle',$selectors,-j=>$dest); |
732
|
|
|
|
|
|
|
} |
733
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
=head2 $bal->add_route($address => $device, [$masquerade]) |
735
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
This method is used to create routing and firewall rules for a network |
737
|
|
|
|
|
|
|
that isn't mentioned in balance.conf. This may be necessary to route |
738
|
|
|
|
|
|
|
to VPNs and/or to the control interfaces of attached modems. |
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
The first argument is the network address in CIDR format, |
741
|
|
|
|
|
|
|
e.g. '192.168.2.0/24'. The second is the network interface that the |
742
|
|
|
|
|
|
|
network can be accessed via. The third, optional, argument is a |
743
|
|
|
|
|
|
|
boolean. If true, then firewall rules will be set up to masquerade |
744
|
|
|
|
|
|
|
from the LAN into the attached network. |
745
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
Note that this is pretty limited. If you want to do anything more |
747
|
|
|
|
|
|
|
sophisticated you're better off setting the routes and firewall rules |
748
|
|
|
|
|
|
|
manually. |
749
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
=cut |
751
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
sub add_route { |
753
|
1
|
|
|
1
|
1
|
689
|
my $self = shift; |
754
|
1
|
|
|
|
|
3
|
my ($network,$device,$masquerade) = @_; |
755
|
1
|
50
|
33
|
|
|
7
|
$network && $device or croak "usage: add_network(\$network,\$device,[\$masquerade])"; |
756
|
|
|
|
|
|
|
# add the route to our main table |
757
|
1
|
|
|
|
|
7
|
$self->ip_route("add $network dev $device"); |
758
|
|
|
|
|
|
|
# add the route to each outgoing table |
759
|
1
|
|
|
|
|
5
|
$self->ip_route("add $network dev $device table $_") for map {$self->table($_)} $self->isp_services; |
|
3
|
|
|
|
|
6
|
|
760
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
# create appropriate firewall rules for the network |
762
|
|
|
|
|
|
|
{ |
763
|
1
|
|
|
|
|
3
|
local $self->{firewall_op} = 'insert'; |
|
1
|
|
|
|
|
3
|
|
764
|
1
|
|
|
|
|
5
|
$self->firewall_rule(OUTPUT => undef, |
765
|
|
|
|
|
|
|
-o => $device, |
766
|
|
|
|
|
|
|
-d => $network, |
767
|
|
|
|
|
|
|
-j => 'ACCEPT'); |
768
|
1
|
|
|
|
|
5
|
$self->firewall_rule(INPUT => undef, |
769
|
|
|
|
|
|
|
-i => $device, |
770
|
|
|
|
|
|
|
-s => $network, |
771
|
|
|
|
|
|
|
-j => 'ACCEPT'); |
772
|
|
|
|
|
|
|
$self->firewall_rule(FORWARD => undef, |
773
|
|
|
|
|
|
|
-i => $self->dev($_), |
774
|
|
|
|
|
|
|
-s => $self->net($_), |
775
|
|
|
|
|
|
|
-o => $device, |
776
|
|
|
|
|
|
|
-d => $network, |
777
|
1
|
|
|
|
|
4
|
-j => 'ACCEPT') for $self->lan_services; |
778
|
|
|
|
|
|
|
$self->firewall_rule(FORWARD => undef, |
779
|
|
|
|
|
|
|
-i => $device, |
780
|
|
|
|
|
|
|
-s => $network, |
781
|
|
|
|
|
|
|
-o => $self->dev($_), |
782
|
|
|
|
|
|
|
-d => $self->net($_), |
783
|
1
|
|
|
|
|
3
|
-j => 'ACCEPT') for $self->lan_services; |
784
|
|
|
|
|
|
|
} |
785
|
1
|
50
|
|
|
|
5
|
if ($masquerade) { |
786
|
1
|
|
|
|
|
4
|
$self->firewall_rule(POSTROUTING=>'nat', |
787
|
|
|
|
|
|
|
-d => $network, |
788
|
|
|
|
|
|
|
-o => $device, |
789
|
|
|
|
|
|
|
-j => 'MASQUERADE'); |
790
|
|
|
|
|
|
|
} |
791
|
|
|
|
|
|
|
} |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
sub _process_iptable_options { |
794
|
15
|
|
|
15
|
|
21
|
my $self = shift; |
795
|
15
|
|
|
|
|
37
|
my @opt = @_; |
796
|
15
|
|
|
|
|
26
|
foreach (@opt) { |
797
|
114
|
100
|
|
|
|
250
|
$_ = "-$_" if /^-\w{2,}/; # add an extra hyphen to -arguments |
798
|
114
|
|
|
|
|
786
|
$_ =~ quotemeta($_); |
799
|
|
|
|
|
|
|
} |
800
|
15
|
|
|
|
|
58
|
return join ' ',@opt; |
801
|
|
|
|
|
|
|
} |
802
|
|
|
|
|
|
|
|
803
|
|
|
|
|
|
|
sub _mark { |
804
|
30
|
|
|
30
|
|
39
|
my $self = shift; |
805
|
30
|
|
|
|
|
38
|
my $service = shift; |
806
|
30
|
|
|
|
|
64
|
return "MARK-${service}"; |
807
|
|
|
|
|
|
|
} |
808
|
|
|
|
|
|
|
|
809
|
|
|
|
|
|
|
=head2 $table_name = $bal->mark_table($service) |
810
|
|
|
|
|
|
|
|
811
|
|
|
|
|
|
|
This returns the iptables table name for connections marked for output |
812
|
|
|
|
|
|
|
on a particular ISP service. The name is simply the word "MARK-" |
813
|
|
|
|
|
|
|
appended to the service name. For example, for a service named "DSL", |
814
|
|
|
|
|
|
|
the corresponding firewall table will be named "MARK-DSL". |
815
|
|
|
|
|
|
|
|
816
|
|
|
|
|
|
|
=cut |
817
|
|
|
|
|
|
|
|
818
|
30
|
|
|
30
|
1
|
61
|
sub mark_table { shift->_mark(shift) } |
819
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
sub _service_or_device { |
821
|
2
|
|
|
2
|
|
3
|
my $self = shift; |
822
|
2
|
|
|
|
|
4
|
my $sod = shift; |
823
|
2
|
50
|
|
|
|
5
|
return $sod if $self->dev($sod); |
824
|
|
|
|
|
|
|
# otherwise try looking for devices |
825
|
0
|
|
|
|
|
0
|
my %dev2s = map {$self->dev($_) => $_} $self->service_names; |
|
0
|
|
|
|
|
0
|
|
826
|
0
|
|
|
|
|
0
|
return $dev2s{$sod}; |
827
|
|
|
|
|
|
|
} |
828
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
=head2 $bal->forward($incoming_port,$destination_host,@protocols) |
830
|
|
|
|
|
|
|
|
831
|
|
|
|
|
|
|
This method emits appropriate port/host forwarding rules using DNAT |
832
|
|
|
|
|
|
|
address translation. The destination host can be specified using |
833
|
|
|
|
|
|
|
either of these forms: |
834
|
|
|
|
|
|
|
|
835
|
|
|
|
|
|
|
192.168.100.1 # forward to same port as incoming |
836
|
|
|
|
|
|
|
192.168.100.1:8080 # forward to a different port on host |
837
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
Protocols are one or more of 'tcp','udp'. If omitted defaults to tcp. |
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
Examples: |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
$bal->forward(80 => '192.168.100.1'); |
843
|
|
|
|
|
|
|
$bal->forward(80 => '192.168.100.1:8080','tcp'); |
844
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
=cut |
846
|
|
|
|
|
|
|
|
847
|
|
|
|
|
|
|
sub forward { |
848
|
3
|
|
|
3
|
1
|
1074
|
my $self = shift; |
849
|
3
|
|
|
|
|
9
|
my ($port,$host,@protocols) = @_; |
850
|
3
|
100
|
|
|
|
10
|
@protocols = ('tcp') unless @protocols; |
851
|
|
|
|
|
|
|
|
852
|
3
|
|
|
|
|
13
|
my ($dhost,$dport) = split ':',$host; |
853
|
3
|
|
33
|
|
|
8
|
$dhost ||= $host; |
854
|
3
|
|
66
|
|
|
10
|
$dport ||= $port; |
855
|
|
|
|
|
|
|
|
856
|
3
|
|
|
|
|
8
|
my @dev = map {$self->vdev($_)} $self->isp_services; |
|
9
|
|
|
|
|
17
|
|
857
|
|
|
|
|
|
|
|
858
|
3
|
|
|
|
|
7
|
for my $dev (@dev) { |
859
|
9
|
|
|
|
|
17
|
for my $protocol (@protocols) { |
860
|
12
|
|
|
|
|
51
|
$self->iptables("-t nat -A PREROUTING -i $dev -p $protocol --dport $port -j DNAT --to-destination $host"); |
861
|
12
|
|
|
|
|
23
|
for my $lan ($self->lan_services) { |
862
|
48
|
|
|
|
|
80
|
my $landev = $self->vdev($lan); |
863
|
48
|
|
|
|
|
81
|
my $lannet = $self->net($lan); |
864
|
48
|
|
|
|
|
75
|
my $lanip = $self->ip($lan); |
865
|
48
|
100
|
|
|
|
91
|
my $syn = $protocol eq 'tcp' ? '--syn' : ''; |
866
|
48
|
|
|
|
|
123
|
$self->iptables("-A FORWARD -p $protocol -o $landev $syn -d $dhost --dport $dport -j ACCEPT"); |
867
|
|
|
|
|
|
|
} |
868
|
|
|
|
|
|
|
} |
869
|
|
|
|
|
|
|
} |
870
|
|
|
|
|
|
|
} |
871
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
=head2 $bal->forward_with_snat($incoming_port,$destination_host,@protocols) |
873
|
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
This method is the same as forward(), except that it also does source |
875
|
|
|
|
|
|
|
NATing from LAN-based requests to make the request appear to have come |
876
|
|
|
|
|
|
|
from the router. This is used when you expose a server, such as a web |
877
|
|
|
|
|
|
|
server, to the internet, but you also need to access the server from |
878
|
|
|
|
|
|
|
machines on the LAN. Use this if you find that the service is visible |
879
|
|
|
|
|
|
|
from outside the LAN but not inside the LAN. |
880
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
Examples: |
882
|
|
|
|
|
|
|
|
883
|
|
|
|
|
|
|
$bal->forward_with_snat(80 => '192.168.100.1'); |
884
|
|
|
|
|
|
|
$bal->forward_with_snat(80 => '192.168.100.1:8080','tcp'); |
885
|
|
|
|
|
|
|
|
886
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
=cut |
888
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
sub forward_with_snat { |
890
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
891
|
0
|
|
|
|
|
0
|
my ($port,$host,@protocols) = @_; |
892
|
|
|
|
|
|
|
|
893
|
0
|
0
|
|
|
|
0
|
@protocols = ('tcp') unless @protocols; |
894
|
|
|
|
|
|
|
|
895
|
0
|
|
|
|
|
0
|
my ($dhost,$dport) = split ':',$host; |
896
|
0
|
|
0
|
|
|
0
|
$dhost ||= $host; |
897
|
0
|
|
0
|
|
|
0
|
$dport ||= $port; |
898
|
|
|
|
|
|
|
|
899
|
0
|
|
|
|
|
0
|
for my $protocol (@protocols) { |
900
|
0
|
|
|
|
|
0
|
for my $svc ($self->isp_services) { |
901
|
0
|
|
|
|
|
0
|
my $external_ip = $self->ip($svc); |
902
|
0
|
|
|
|
|
0
|
$self->iptables("-t nat -A PREROUTING -d $external_ip -p $protocol --dport $port -j DNAT --to-destination $host"); |
903
|
|
|
|
|
|
|
} |
904
|
|
|
|
|
|
|
|
905
|
0
|
|
|
|
|
0
|
for my $lan ($self->lan_services) { |
906
|
0
|
|
|
|
|
0
|
my $lannet = $self->net($lan); |
907
|
0
|
|
|
|
|
0
|
$self->iptables("-t nat -A POSTROUTING -s $lannet -p $protocol --dport $port -d $host -j MASQUERADE"); |
908
|
|
|
|
|
|
|
} |
909
|
|
|
|
|
|
|
|
910
|
0
|
|
|
|
|
0
|
$self->iptables("-A FORWARD -p $protocol --dport $port -d $host -j ACCEPT"); |
911
|
|
|
|
|
|
|
|
912
|
|
|
|
|
|
|
} |
913
|
|
|
|
|
|
|
|
914
|
|
|
|
|
|
|
} |
915
|
|
|
|
|
|
|
|
916
|
|
|
|
|
|
|
=head2 $bal->ip_route(@args) |
917
|
|
|
|
|
|
|
|
918
|
|
|
|
|
|
|
Shortcut for $bal->sh('ip route',@args); |
919
|
|
|
|
|
|
|
|
920
|
|
|
|
|
|
|
=cut |
921
|
|
|
|
|
|
|
|
922
|
66
|
|
|
66
|
1
|
116
|
sub ip_route {shift->sh('ip','route',@_)} |
923
|
|
|
|
|
|
|
|
924
|
|
|
|
|
|
|
=head2 $bal->ip_rule(@args) |
925
|
|
|
|
|
|
|
|
926
|
|
|
|
|
|
|
Shortcut for $bal->sh('ip rule',@args); |
927
|
|
|
|
|
|
|
|
928
|
|
|
|
|
|
|
=cut |
929
|
|
|
|
|
|
|
|
930
|
15
|
|
|
15
|
1
|
26
|
sub ip_rule {shift->sh('ip','rule',@_)} |
931
|
|
|
|
|
|
|
|
932
|
|
|
|
|
|
|
=head2 $verbose = $bal->iptables_verbose([boolean]) |
933
|
|
|
|
|
|
|
|
934
|
|
|
|
|
|
|
Makes iptables send an incredible amount of debugging information to |
935
|
|
|
|
|
|
|
syslog. |
936
|
|
|
|
|
|
|
|
937
|
|
|
|
|
|
|
=cut |
938
|
|
|
|
|
|
|
|
939
|
|
|
|
|
|
|
sub iptables_verbose { |
940
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
941
|
0
|
|
|
|
|
0
|
my $d = $self->{iptables_verbose}; |
942
|
0
|
0
|
|
|
|
0
|
$self->{iptables_verbose} = shift if @_; |
943
|
0
|
|
|
|
|
0
|
$d; |
944
|
|
|
|
|
|
|
} |
945
|
|
|
|
|
|
|
|
946
|
|
|
|
|
|
|
=head1 QUERYING THE CONFIGURATION |
947
|
|
|
|
|
|
|
|
948
|
|
|
|
|
|
|
These methods allow you to get information about the Net::ISP::Balance |
949
|
|
|
|
|
|
|
object's configuration, including settings and other characteristics |
950
|
|
|
|
|
|
|
of the various network interfaces. |
951
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
=head2 @names = $bal->service_names |
953
|
|
|
|
|
|
|
|
954
|
|
|
|
|
|
|
Return the list of service names defined in balance.conf. |
955
|
|
|
|
|
|
|
|
956
|
|
|
|
|
|
|
=cut |
957
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
sub service_names { |
959
|
48
|
|
|
48
|
1
|
71
|
my $self = shift; |
960
|
48
|
|
|
|
|
86
|
my $s = $self->services; |
961
|
48
|
|
|
|
|
268
|
return sort keys %$s; |
962
|
|
|
|
|
|
|
} |
963
|
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
=head2 @names = $bal->isp_services |
965
|
|
|
|
|
|
|
|
966
|
|
|
|
|
|
|
Return list of service names that correspond to load-balanced ISPs. |
967
|
|
|
|
|
|
|
|
968
|
|
|
|
|
|
|
=cut |
969
|
|
|
|
|
|
|
|
970
|
|
|
|
|
|
|
sub isp_services { |
971
|
20
|
|
|
20
|
1
|
36
|
my $self = shift; |
972
|
20
|
|
|
|
|
39
|
my @n = $self->service_names; |
973
|
20
|
|
|
|
|
47
|
return grep {$self->role($_) eq 'isp'} @n; # kill uninit warning |
|
156
|
|
|
|
|
253
|
|
974
|
|
|
|
|
|
|
} |
975
|
|
|
|
|
|
|
|
976
|
|
|
|
|
|
|
=head2 @names = $bal->lan_services |
977
|
|
|
|
|
|
|
|
978
|
|
|
|
|
|
|
Return list of service names that correspond to lans. |
979
|
|
|
|
|
|
|
|
980
|
|
|
|
|
|
|
|
981
|
|
|
|
|
|
|
=cut |
982
|
|
|
|
|
|
|
|
983
|
|
|
|
|
|
|
sub lan_services { |
984
|
21
|
|
|
21
|
1
|
40
|
my $self = shift; |
985
|
21
|
|
|
|
|
36
|
my @n = $self->service_names; |
986
|
21
|
|
|
|
|
47
|
return grep {$self->role($_) eq 'lan'} @n; # kill uninit warning... |
|
168
|
|
|
|
|
280
|
|
987
|
|
|
|
|
|
|
} |
988
|
|
|
|
|
|
|
|
989
|
|
|
|
|
|
|
=head2 $state = $bal->event($service => $new_state) |
990
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
Record a transition between "up" and "down" for a named service. The |
992
|
|
|
|
|
|
|
first argument is the name of the ISP service that has changed, |
993
|
|
|
|
|
|
|
e.g. "CABLE". The second argument is either "up" or "down". |
994
|
|
|
|
|
|
|
|
995
|
|
|
|
|
|
|
The method returns a hashref in which the keys are the ISP service names |
996
|
|
|
|
|
|
|
and the values are one of 'up' or 'down'. |
997
|
|
|
|
|
|
|
|
998
|
|
|
|
|
|
|
The persistent state information is stored in /var/lib/lsm/ under a |
999
|
|
|
|
|
|
|
series of files named .state. |
1000
|
|
|
|
|
|
|
|
1001
|
|
|
|
|
|
|
=cut |
1002
|
|
|
|
|
|
|
|
1003
|
|
|
|
|
|
|
sub event { |
1004
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
1005
|
|
|
|
|
|
|
|
1006
|
0
|
0
|
|
|
|
0
|
if (@_) { |
1007
|
0
|
|
|
|
|
0
|
my ($svc,$new_state) = @_; |
1008
|
0
|
0
|
|
|
|
0
|
$new_state =~ /^(up|down)$/ or croak "state must be 'up' or down'"; |
1009
|
0
|
0
|
|
|
|
0
|
$self->vdev($svc) or croak "service '$svc' is unknown"; |
1010
|
0
|
|
|
|
|
0
|
my $file = "/var/lib/lsm/${svc}.state"; |
1011
|
0
|
0
|
|
|
|
0
|
my $mode = -e $file ? '+<' : '>'; |
1012
|
0
|
0
|
|
|
|
0
|
open my $fh,$mode,$file or croak "Couldn't open $file mode $mode: $!"; |
1013
|
0
|
|
|
|
|
0
|
flock $fh,LOCK_EX; |
1014
|
0
|
|
|
|
|
0
|
truncate $fh,0; |
1015
|
0
|
|
|
|
|
0
|
seek($fh,0,0); |
1016
|
0
|
|
|
|
|
0
|
print $fh $new_state; |
1017
|
0
|
|
|
|
|
0
|
close $fh; |
1018
|
|
|
|
|
|
|
} |
1019
|
|
|
|
|
|
|
|
1020
|
0
|
|
|
|
|
0
|
my %state; |
1021
|
0
|
|
|
|
|
0
|
for my $svc ($self->isp_services) { |
1022
|
0
|
|
|
|
|
0
|
my $file = "/var/lib/lsm/${svc}.state"; |
1023
|
0
|
0
|
|
|
|
0
|
if (open my $fh,'<',$file) { |
1024
|
0
|
|
|
|
|
0
|
flock $fh,LOCK_SH; |
1025
|
0
|
|
|
|
|
0
|
my $state = <$fh>; |
1026
|
0
|
|
|
|
|
0
|
close $fh; |
1027
|
0
|
|
|
|
|
0
|
$state{$svc}=$state; |
1028
|
|
|
|
|
|
|
} else { |
1029
|
0
|
|
|
|
|
0
|
$state{$svc}='unknown'; |
1030
|
|
|
|
|
|
|
} |
1031
|
|
|
|
|
|
|
} |
1032
|
0
|
|
|
|
|
0
|
my @up = grep {$state{$_} eq 'up'} keys %state; |
|
0
|
|
|
|
|
0
|
|
1033
|
0
|
|
|
|
|
0
|
$self->up(@up); |
1034
|
0
|
|
|
|
|
0
|
return \%state; |
1035
|
|
|
|
|
|
|
} |
1036
|
|
|
|
|
|
|
|
1037
|
|
|
|
|
|
|
=head2 $bal->run_eventd(@args) |
1038
|
|
|
|
|
|
|
|
1039
|
|
|
|
|
|
|
Runs scripts in response to lsm events. The scripts are stored in |
1040
|
|
|
|
|
|
|
directories named after the events, e.g.: |
1041
|
|
|
|
|
|
|
|
1042
|
|
|
|
|
|
|
/etc/network/lsm/up.d/* |
1043
|
|
|
|
|
|
|
/etc/network/lsm/down.d/* |
1044
|
|
|
|
|
|
|
/etc/network/lsm/long_down.d/* |
1045
|
|
|
|
|
|
|
|
1046
|
|
|
|
|
|
|
Scripts are called with the following arguments: |
1047
|
|
|
|
|
|
|
|
1048
|
|
|
|
|
|
|
0. STATE |
1049
|
|
|
|
|
|
|
1. SERVICE NAME |
1050
|
|
|
|
|
|
|
2. CHECKIP |
1051
|
|
|
|
|
|
|
3. DEVICE |
1052
|
|
|
|
|
|
|
4. WARN_EMAIL |
1053
|
|
|
|
|
|
|
5. REPLIED |
1054
|
|
|
|
|
|
|
6. WAITING |
1055
|
|
|
|
|
|
|
7. TIMEOUT |
1056
|
|
|
|
|
|
|
8. REPLY_LATE |
1057
|
|
|
|
|
|
|
9. CONS_RCVD |
1058
|
|
|
|
|
|
|
10. CONS_WAIT |
1059
|
|
|
|
|
|
|
11. CONS_MISS |
1060
|
|
|
|
|
|
|
12. AVG_RTT |
1061
|
|
|
|
|
|
|
13. SRCIP |
1062
|
|
|
|
|
|
|
14. PREVSTATE |
1063
|
|
|
|
|
|
|
15. TIMESTAMP |
1064
|
|
|
|
|
|
|
|
1065
|
|
|
|
|
|
|
=cut |
1066
|
|
|
|
|
|
|
|
1067
|
|
|
|
|
|
|
sub run_eventd { |
1068
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
1069
|
0
|
|
|
|
|
0
|
my @args = @_; |
1070
|
0
|
|
|
|
|
0
|
my $state = $args[0]; |
1071
|
0
|
|
|
|
|
0
|
my $dir = $self->lsm_scripts_dir(); |
1072
|
0
|
|
|
|
|
0
|
my $dird = "$dir/${state}.d"; |
1073
|
0
|
|
|
|
|
0
|
my @files = sort glob("$dird/*"); |
1074
|
0
|
|
|
|
|
0
|
for my $script (sort @files) { |
1075
|
0
|
0
|
|
|
|
0
|
next if $script =~ /^#/; |
1076
|
0
|
0
|
|
|
|
0
|
next if $script =~ /~$/; |
1077
|
0
|
0
|
0
|
|
|
0
|
next unless -f $script && -x _; |
1078
|
0
|
|
|
|
|
0
|
system $script,@args; |
1079
|
|
|
|
|
|
|
} |
1080
|
|
|
|
|
|
|
} |
1081
|
|
|
|
|
|
|
|
1082
|
|
|
|
|
|
|
=head2 @up = $bal->up(@up_services) |
1083
|
|
|
|
|
|
|
|
1084
|
|
|
|
|
|
|
Get or set the list of ISP interfaces that are currently active and |
1085
|
|
|
|
|
|
|
should be used for balancing. |
1086
|
|
|
|
|
|
|
|
1087
|
|
|
|
|
|
|
=cut |
1088
|
|
|
|
|
|
|
|
1089
|
|
|
|
|
|
|
sub up { |
1090
|
6
|
|
|
6
|
1
|
746
|
my $self = shift; |
1091
|
6
|
100
|
|
|
|
18
|
$self->{up} = \@_ if @_; |
1092
|
6
|
100
|
|
|
|
14
|
unless ($self->{up}) { # initialize with running services |
1093
|
2
|
|
|
|
|
6
|
my @svc = grep {$self->running($_)} $self->isp_services; |
|
5
|
|
|
|
|
10
|
|
1094
|
2
|
|
|
|
|
9
|
$self->{up} = \@svc; |
1095
|
|
|
|
|
|
|
} |
1096
|
6
|
|
|
|
|
9
|
my @up = @{$self->{up}}; |
|
6
|
|
|
|
|
13
|
|
1097
|
6
|
|
|
|
|
18
|
return @up; |
1098
|
|
|
|
|
|
|
} |
1099
|
|
|
|
|
|
|
|
1100
|
|
|
|
|
|
|
=head2 $services = $bal->services |
1101
|
|
|
|
|
|
|
|
1102
|
|
|
|
|
|
|
Return a hash containing the configuration information for each |
1103
|
|
|
|
|
|
|
service. The keys are the service names. Here's an example: |
1104
|
|
|
|
|
|
|
|
1105
|
|
|
|
|
|
|
{ |
1106
|
|
|
|
|
|
|
0 HASH(0x91201e8) |
1107
|
|
|
|
|
|
|
'CABLE' => HASH(0x9170500) |
1108
|
|
|
|
|
|
|
'dev' => 'eth0' |
1109
|
|
|
|
|
|
|
'fwmark' => 2 |
1110
|
|
|
|
|
|
|
'gw' => '191.3.88.1' |
1111
|
|
|
|
|
|
|
'ip' => '191.3.88.152' |
1112
|
|
|
|
|
|
|
'net' => '191.3.88.128/27' |
1113
|
|
|
|
|
|
|
'ping' => 'www.google.ca' |
1114
|
|
|
|
|
|
|
'role' => 'isp' |
1115
|
|
|
|
|
|
|
'running' => 1 |
1116
|
|
|
|
|
|
|
'table' => 2 |
1117
|
|
|
|
|
|
|
'DSL' => HASH(0x9113e00) |
1118
|
|
|
|
|
|
|
'dev' => 'ppp0' |
1119
|
|
|
|
|
|
|
'fwmark' => 1 |
1120
|
|
|
|
|
|
|
'gw' => '112.211.154.198' |
1121
|
|
|
|
|
|
|
'ip' => '11.120.199.108' |
1122
|
|
|
|
|
|
|
'net' => '112.211.154.198/32' |
1123
|
|
|
|
|
|
|
'ping' => 'www.google.ca' |
1124
|
|
|
|
|
|
|
'role' => 'isp' |
1125
|
|
|
|
|
|
|
'running' => 1 |
1126
|
|
|
|
|
|
|
'table' => 1 |
1127
|
|
|
|
|
|
|
'LAN' => HASH(0x913ce58) |
1128
|
|
|
|
|
|
|
'dev' => 'eth1' |
1129
|
|
|
|
|
|
|
'fwmark' => undef |
1130
|
|
|
|
|
|
|
'gw' => '192.168.10.1' |
1131
|
|
|
|
|
|
|
'ip' => '192.168.10.1' |
1132
|
|
|
|
|
|
|
'net' => '192.168.10.0/24' |
1133
|
|
|
|
|
|
|
'ping' => '' |
1134
|
|
|
|
|
|
|
'role' => 'lan' |
1135
|
|
|
|
|
|
|
'running' => 1 |
1136
|
|
|
|
|
|
|
} |
1137
|
|
|
|
|
|
|
|
1138
|
|
|
|
|
|
|
=cut |
1139
|
|
|
|
|
|
|
|
1140
|
49
|
|
|
49
|
1
|
476
|
sub services { return shift->{services} } |
1141
|
|
|
|
|
|
|
|
1142
|
|
|
|
|
|
|
=head2 $service = $bal->service('CABLE') |
1143
|
|
|
|
|
|
|
|
1144
|
|
|
|
|
|
|
Return the subhash describing the single named service (see services() |
1145
|
|
|
|
|
|
|
above). |
1146
|
|
|
|
|
|
|
|
1147
|
|
|
|
|
|
|
=cut |
1148
|
|
|
|
|
|
|
|
1149
|
|
|
|
|
|
|
sub service { |
1150
|
0
|
|
|
0
|
1
|
0
|
shift->{services}{shift()}; |
1151
|
|
|
|
|
|
|
} |
1152
|
|
|
|
|
|
|
|
1153
|
|
|
|
|
|
|
=head2 $dev = $bal->dev('CABLE') |
1154
|
|
|
|
|
|
|
|
1155
|
|
|
|
|
|
|
=head2 $ip = $bal->ip('CABLE') |
1156
|
|
|
|
|
|
|
|
1157
|
|
|
|
|
|
|
=head2 $gateway = $bal->gw('CABLE') |
1158
|
|
|
|
|
|
|
|
1159
|
|
|
|
|
|
|
=head2 $network = $bal->net('CABLE') |
1160
|
|
|
|
|
|
|
|
1161
|
|
|
|
|
|
|
=head2 $role = $bal->role('CABLE') |
1162
|
|
|
|
|
|
|
|
1163
|
|
|
|
|
|
|
=head2 $running = $bal->running('CABLE') |
1164
|
|
|
|
|
|
|
|
1165
|
|
|
|
|
|
|
=head2 $mark_number = $bal->fwmark('CABLE') |
1166
|
|
|
|
|
|
|
|
1167
|
|
|
|
|
|
|
=head2 $routing_table_number = $bal->table('CABLE') |
1168
|
|
|
|
|
|
|
|
1169
|
|
|
|
|
|
|
=head2 $ping_dest = $bal->ping('CABLE') |
1170
|
|
|
|
|
|
|
|
1171
|
|
|
|
|
|
|
These methods pull out the named information from the configuration |
1172
|
|
|
|
|
|
|
data. fwmark() returns a small integer that will be used for marking |
1173
|
|
|
|
|
|
|
connections for routing through one of the ISP connections when an |
1174
|
|
|
|
|
|
|
outgoing connection originates on the LAN and is routed through the |
1175
|
|
|
|
|
|
|
router. table() returns a small integer corresponding to a routing |
1176
|
|
|
|
|
|
|
table used to route connections originating on the router itself. |
1177
|
|
|
|
|
|
|
|
1178
|
|
|
|
|
|
|
=cut |
1179
|
|
|
|
|
|
|
|
1180
|
73
|
|
|
73
|
1
|
8846
|
sub dev { shift->_service_field(shift,'dev') } |
1181
|
129
|
|
|
129
|
0
|
212
|
sub vdev{ shift->_service_field(shift,'vdev') } |
1182
|
106
|
|
|
106
|
1
|
164
|
sub ip { shift->_service_field(shift,'ip') } |
1183
|
9
|
|
|
9
|
1
|
16
|
sub gw { shift->_service_field(shift,'gw') } |
1184
|
156
|
|
|
156
|
1
|
258
|
sub net { shift->_service_field(shift,'net') } |
1185
|
5
|
|
|
5
|
1
|
10
|
sub running { shift->_service_field(shift,'running') } |
1186
|
326
|
|
|
326
|
1
|
520
|
sub role { shift->_service_field(shift,'role') } |
1187
|
11
|
|
|
11
|
1
|
19
|
sub fwmark { shift->_service_field(shift,'fwmark') } |
1188
|
64
|
|
|
64
|
1
|
100
|
sub table { shift->_service_field(shift,'table') } |
1189
|
3
|
|
|
3
|
1
|
6
|
sub ping { shift->_service_field(shift,'ping') } |
1190
|
17
|
|
|
17
|
0
|
30
|
sub weight { shift->_service_field(shift,'weight') } |
1191
|
|
|
|
|
|
|
|
1192
|
|
|
|
|
|
|
sub _service_field { |
1193
|
899
|
|
|
899
|
|
1192
|
my $self = shift; |
1194
|
899
|
|
|
|
|
1362
|
my ($service,$field) = @_; |
1195
|
899
|
50
|
|
|
|
1642
|
my $s = $self->{services}{$service} or return; |
1196
|
899
|
|
|
|
|
2014
|
$s->{$field}; |
1197
|
|
|
|
|
|
|
} |
1198
|
|
|
|
|
|
|
|
1199
|
|
|
|
|
|
|
sub _save_custom_chains { |
1200
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1201
|
0
|
|
|
|
|
0
|
for my $table ('filter','nat','mangle') { |
1202
|
0
|
|
|
|
|
0
|
my @rules = split("\n",`sudo iptables -t $table -S`); |
1203
|
|
|
|
|
|
|
# find custom chains |
1204
|
0
|
|
|
|
|
0
|
my $mine = 'MARK-|REJECTPERM|DROPGEN|DROPINVAL|DROPPERM|DROPSPOOF|DROPFLOOD|DEBUG'; |
1205
|
0
|
0
|
|
|
|
0
|
my @chains = grep {!/^-N ($mine)/} grep {/^-N (\S+)/} @rules or next; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
1206
|
0
|
|
|
|
|
0
|
s/^-N // foreach @chains; |
1207
|
0
|
|
|
|
|
0
|
my $chains = join '|',map {quotemeta($_)} @chains; |
|
0
|
|
|
|
|
0
|
|
1208
|
0
|
|
|
|
|
0
|
my @targets = grep {/-(?:j|A|I) (?:$chains)/} @rules; |
|
0
|
|
|
|
|
0
|
|
1209
|
0
|
|
|
|
|
0
|
$self->{_custom_chains}{$table} = [(map {"-N $_"} @chains),@targets]; |
|
0
|
|
|
|
|
0
|
|
1210
|
|
|
|
|
|
|
} |
1211
|
|
|
|
|
|
|
} |
1212
|
|
|
|
|
|
|
|
1213
|
|
|
|
|
|
|
sub _restore_custom_chains { |
1214
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1215
|
0
|
0
|
|
|
|
0
|
my $custom_chains = $self->{_custom_chains} or return; |
1216
|
0
|
|
|
|
|
0
|
for my $table (keys %{$custom_chains}) { |
|
0
|
|
|
|
|
0
|
|
1217
|
0
|
0
|
|
|
|
0
|
my @rules = @{$custom_chains->{$table}} or next; |
|
0
|
|
|
|
|
0
|
|
1218
|
0
|
|
|
|
|
0
|
$self->iptables([map {"-t $table $_"} @rules]); |
|
0
|
|
|
|
|
0
|
|
1219
|
|
|
|
|
|
|
} |
1220
|
|
|
|
|
|
|
} |
1221
|
|
|
|
|
|
|
|
1222
|
|
|
|
|
|
|
=head1 FILES AND PATHS |
1223
|
|
|
|
|
|
|
|
1224
|
|
|
|
|
|
|
These are methods that determine where Net::ISP::Balance finds its |
1225
|
|
|
|
|
|
|
configuration files. |
1226
|
|
|
|
|
|
|
|
1227
|
|
|
|
|
|
|
=head2 $path = Net::ISP::Balance->install_etc |
1228
|
|
|
|
|
|
|
|
1229
|
|
|
|
|
|
|
Returns the path to where the network configuration files reside on |
1230
|
|
|
|
|
|
|
this system, e.g. /etc/network. Note that this only knows about |
1231
|
|
|
|
|
|
|
Ubuntu/Debian-style network configuration files in /etc/network, and |
1232
|
|
|
|
|
|
|
RedHat/CentOS network configuration files in |
1233
|
|
|
|
|
|
|
/etc/sysconfig/network-scripts. |
1234
|
|
|
|
|
|
|
|
1235
|
|
|
|
|
|
|
=cut |
1236
|
|
|
|
|
|
|
|
1237
|
|
|
|
|
|
|
sub install_etc { |
1238
|
7
|
|
|
7
|
1
|
12
|
my $self = shift; |
1239
|
7
|
50
|
|
|
|
164
|
return '/etc/network' if -d '/etc/network'; |
1240
|
0
|
0
|
|
|
|
0
|
return '/etc/sysconfig/network-scripts' if -d '/etc/sysconfig/network-scripts'; |
1241
|
0
|
|
|
|
|
0
|
return '/etc'; |
1242
|
|
|
|
|
|
|
} |
1243
|
|
|
|
|
|
|
|
1244
|
|
|
|
|
|
|
=head2 $file = Net::ISP::Balance->default_conf_file |
1245
|
|
|
|
|
|
|
|
1246
|
|
|
|
|
|
|
Returns the path to the default configuration file, |
1247
|
|
|
|
|
|
|
$ETC_NETWORK/balance.conf. |
1248
|
|
|
|
|
|
|
|
1249
|
|
|
|
|
|
|
=cut |
1250
|
|
|
|
|
|
|
|
1251
|
|
|
|
|
|
|
sub default_conf_file { |
1252
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
1253
|
0
|
|
|
|
|
0
|
return $self->install_etc.'/balance.conf'; |
1254
|
|
|
|
|
|
|
} |
1255
|
|
|
|
|
|
|
|
1256
|
|
|
|
|
|
|
=head2 $dir = Net::ISP::Balance->default_rules_directory |
1257
|
|
|
|
|
|
|
|
1258
|
|
|
|
|
|
|
Returns the path to the directory where the additional router and |
1259
|
|
|
|
|
|
|
firewall rules are stored. On Ubuntu-Debian-derived systems, this is |
1260
|
|
|
|
|
|
|
/etc/network/balance/. On RedHat/CentOS systems, this is |
1261
|
|
|
|
|
|
|
/etc/sysconfig/network-scripts/balance/. |
1262
|
|
|
|
|
|
|
|
1263
|
|
|
|
|
|
|
=cut |
1264
|
|
|
|
|
|
|
|
1265
|
|
|
|
|
|
|
sub default_rules_directory { |
1266
|
2
|
|
|
2
|
1
|
5
|
my $self = shift; |
1267
|
2
|
|
|
|
|
15
|
return $self->install_etc."/balance"; |
1268
|
|
|
|
|
|
|
} |
1269
|
|
|
|
|
|
|
|
1270
|
|
|
|
|
|
|
=head2 $file = Net::ISP::Balance->default_lsm_conf_file |
1271
|
|
|
|
|
|
|
|
1272
|
|
|
|
|
|
|
Returns the path to the place where we should store lsm.conf, the file |
1273
|
|
|
|
|
|
|
used to configure the lsm (link status monitor) application. |
1274
|
|
|
|
|
|
|
|
1275
|
|
|
|
|
|
|
On Ubuntu/Debian-derived systems, this will be the file |
1276
|
|
|
|
|
|
|
/etc/network/lsm.conf. On RedHad/CentOS-derived systems, this will be |
1277
|
|
|
|
|
|
|
/etc/sysconfig/network-scripts/lsm.conf. |
1278
|
|
|
|
|
|
|
|
1279
|
|
|
|
|
|
|
=cut |
1280
|
|
|
|
|
|
|
|
1281
|
|
|
|
|
|
|
sub default_lsm_conf_file { |
1282
|
2
|
|
|
2
|
1
|
7
|
my $self = shift; |
1283
|
2
|
|
|
|
|
4
|
return $self->install_etc."/balance/lsm.conf"; |
1284
|
|
|
|
|
|
|
} |
1285
|
|
|
|
|
|
|
|
1286
|
|
|
|
|
|
|
=head2 $dir = Net::ISP::Balance->default_lsm_scripts_dir |
1287
|
|
|
|
|
|
|
|
1288
|
|
|
|
|
|
|
Returns the path to the place where lsm stores its helper scripts. On |
1289
|
|
|
|
|
|
|
Ubuntu/Debian-derived systems, this will be the directory |
1290
|
|
|
|
|
|
|
/etc/network/lsm/. On RedHad/CentOS-derived systems, this will be |
1291
|
|
|
|
|
|
|
/etc/sysconfig/network-scripts/lsm/. |
1292
|
|
|
|
|
|
|
|
1293
|
|
|
|
|
|
|
=cut |
1294
|
|
|
|
|
|
|
|
1295
|
|
|
|
|
|
|
sub default_lsm_scripts_dir { |
1296
|
2
|
|
|
2
|
1
|
5
|
my $self = shift; |
1297
|
2
|
|
|
|
|
6
|
return $self->install_etc.'/balance/lsm'; |
1298
|
|
|
|
|
|
|
} |
1299
|
|
|
|
|
|
|
|
1300
|
|
|
|
|
|
|
=head2 $file = $bal->bal_conf_file([$new_file]) |
1301
|
|
|
|
|
|
|
|
1302
|
|
|
|
|
|
|
Get/set the main configuration file path, balance.conf. |
1303
|
|
|
|
|
|
|
|
1304
|
|
|
|
|
|
|
=cut |
1305
|
|
|
|
|
|
|
|
1306
|
|
|
|
|
|
|
sub bal_conf_file { |
1307
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
1308
|
0
|
|
|
|
|
0
|
my $d = $self->{bal_conf_file}; |
1309
|
0
|
0
|
|
|
|
0
|
$self->{bal_conf_file} = shift if @_; |
1310
|
0
|
|
|
|
|
0
|
$d; |
1311
|
|
|
|
|
|
|
} |
1312
|
|
|
|
|
|
|
|
1313
|
|
|
|
|
|
|
=head2 $dir = $bal->rules_directory([$new_rules_directory]) |
1314
|
|
|
|
|
|
|
|
1315
|
|
|
|
|
|
|
Get/set the route and firewall rules directory. |
1316
|
|
|
|
|
|
|
|
1317
|
|
|
|
|
|
|
=cut |
1318
|
|
|
|
|
|
|
|
1319
|
|
|
|
|
|
|
sub rules_directory { |
1320
|
3
|
|
|
3
|
1
|
9
|
my $self = shift; |
1321
|
3
|
|
|
|
|
7
|
my $d = $self->{rules_directory}; |
1322
|
3
|
100
|
|
|
|
9
|
$self->{rules_directory} = shift if @_; |
1323
|
3
|
|
|
|
|
7
|
$d; |
1324
|
|
|
|
|
|
|
} |
1325
|
|
|
|
|
|
|
|
1326
|
|
|
|
|
|
|
=head2 $file = $bal->lsm_conf_file([$new_conffile]) |
1327
|
|
|
|
|
|
|
|
1328
|
|
|
|
|
|
|
Get/set the path to the lsm configuration file. |
1329
|
|
|
|
|
|
|
|
1330
|
|
|
|
|
|
|
=cut |
1331
|
|
|
|
|
|
|
|
1332
|
|
|
|
|
|
|
sub lsm_conf_file { |
1333
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
1334
|
0
|
|
|
|
|
0
|
my $d = $self->{lsm_conf_file}; |
1335
|
0
|
0
|
|
|
|
0
|
$self->{lsm_conf_file} = shift if @_; |
1336
|
0
|
|
|
|
|
0
|
$d; |
1337
|
|
|
|
|
|
|
} |
1338
|
|
|
|
|
|
|
|
1339
|
|
|
|
|
|
|
=head2 $dir = $bal->lsm_scripts_dir([$new_dir]) |
1340
|
|
|
|
|
|
|
|
1341
|
|
|
|
|
|
|
Get/set the path to the lsm scripts directory. |
1342
|
|
|
|
|
|
|
|
1343
|
|
|
|
|
|
|
=cut |
1344
|
|
|
|
|
|
|
|
1345
|
|
|
|
|
|
|
sub lsm_scripts_dir { |
1346
|
1
|
|
|
1
|
1
|
3
|
my $self = shift; |
1347
|
1
|
|
|
|
|
3
|
my $d = $self->{lsm_scripts_dir}; |
1348
|
1
|
50
|
|
|
|
5
|
$self->{lsm_scripts_dir} = shift if @_; |
1349
|
1
|
|
|
|
|
3
|
$d; |
1350
|
|
|
|
|
|
|
} |
1351
|
|
|
|
|
|
|
|
1352
|
|
|
|
|
|
|
=head1 INFREQUENTLY-USED METHODS |
1353
|
|
|
|
|
|
|
|
1354
|
|
|
|
|
|
|
These are methods that are used internally, but may be useful to |
1355
|
|
|
|
|
|
|
applications developers. |
1356
|
|
|
|
|
|
|
|
1357
|
|
|
|
|
|
|
=head2 $lsm_config_text = $bal->lsm_config_file(-warn_email=>'root@localhost') |
1358
|
|
|
|
|
|
|
|
1359
|
|
|
|
|
|
|
This method creates the text used to create the lsm.conf configuration |
1360
|
|
|
|
|
|
|
file. Pass it a series of -name=>value pairs to incorporate into the |
1361
|
|
|
|
|
|
|
file. |
1362
|
|
|
|
|
|
|
|
1363
|
|
|
|
|
|
|
Possible switches and their defaults are: |
1364
|
|
|
|
|
|
|
|
1365
|
|
|
|
|
|
|
-checkip 127.0.0.1 |
1366
|
|
|
|
|
|
|
-eventscript /etc/network/load_balance.pl |
1367
|
|
|
|
|
|
|
-long_down_eventscript /etc/network/load_balance.pl |
1368
|
|
|
|
|
|
|
-notifyscript /etc/network/balance/lsm/default_script |
1369
|
|
|
|
|
|
|
-max_packet_loss 15 |
1370
|
|
|
|
|
|
|
-max_successive_pkts_lost 7 |
1371
|
|
|
|
|
|
|
-min_packet_loss 5 |
1372
|
|
|
|
|
|
|
-min_successive_pkts_rcvd 10 |
1373
|
|
|
|
|
|
|
-interval_ms 1000 |
1374
|
|
|
|
|
|
|
-timeout_ms 1000 |
1375
|
|
|
|
|
|
|
-warn_email root |
1376
|
|
|
|
|
|
|
-check_arp 0 |
1377
|
|
|
|
|
|
|
-sourceip |
1378
|
|
|
|
|
|
|
-device -eventscript => $balance_script, |
1379
|
|
|
|
|
|
|
-ttl 0 |
1380
|
|
|
|
|
|
|
-status 2 |
1381
|
|
|
|
|
|
|
-debug 8 |
1382
|
|
|
|
|
|
|
|
1383
|
|
|
|
|
|
|
=cut |
1384
|
|
|
|
|
|
|
|
1385
|
|
|
|
|
|
|
sub lsm_config_text { |
1386
|
1
|
|
|
1
|
0
|
3
|
my $self = shift; |
1387
|
1
|
|
|
|
|
4
|
my %args = @_; |
1388
|
1
|
|
|
|
|
4
|
my $scripts_dir = $self->lsm_scripts_dir; |
1389
|
1
|
|
|
|
|
3
|
my $balance_script = $self->install_etc."/load_balance.pl"; |
1390
|
1
|
|
|
|
|
21
|
my %defaults = ( |
1391
|
|
|
|
|
|
|
-checkip => '127.0.0.1', |
1392
|
|
|
|
|
|
|
-debug => 8, |
1393
|
|
|
|
|
|
|
-eventscript => $balance_script, |
1394
|
|
|
|
|
|
|
-long_down_eventscript => $balance_script, |
1395
|
|
|
|
|
|
|
-notifyscript => "$scripts_dir/default_script", |
1396
|
|
|
|
|
|
|
-max_packet_loss => 20, |
1397
|
|
|
|
|
|
|
-max_successive_pkts_lost => 7, |
1398
|
|
|
|
|
|
|
-min_packet_loss => 10, |
1399
|
|
|
|
|
|
|
-min_successive_pkts_rcvd => 5, |
1400
|
|
|
|
|
|
|
-interval_ms => 1000, |
1401
|
|
|
|
|
|
|
-timeout_ms => 500, |
1402
|
|
|
|
|
|
|
-long_down_time => 120, |
1403
|
|
|
|
|
|
|
-warn_email => 'root', |
1404
|
|
|
|
|
|
|
-check_arp => 0, |
1405
|
|
|
|
|
|
|
-sourceip => undef, |
1406
|
|
|
|
|
|
|
-device => undef, |
1407
|
|
|
|
|
|
|
-ttl => 0, |
1408
|
|
|
|
|
|
|
-status => 2 |
1409
|
|
|
|
|
|
|
); |
1410
|
1
|
|
|
|
|
4
|
%defaults = (%defaults,%{$self->{lsm_config}},%args); # %args supersedes what's in %defaults |
|
1
|
|
|
|
|
10
|
|
1411
|
|
|
|
|
|
|
|
1412
|
1
|
|
|
|
|
4
|
my $result = "# This file is autogenerated by load_balancer.pl when it first runs.\n"; |
1413
|
1
|
|
|
|
|
3
|
$result .= "# Do not edit directly. Instead edit /etc/network/balance.conf.\n\n"; |
1414
|
1
|
|
|
|
|
3
|
$result .= "debug=$defaults{-debug}\n\n"; |
1415
|
1
|
|
|
|
|
3
|
delete $defaults{-debug}; |
1416
|
|
|
|
|
|
|
|
1417
|
1
|
|
|
|
|
3
|
$result .= "defaults {\n"; |
1418
|
1
|
|
|
|
|
2
|
$result .= " name=defaults\n"; |
1419
|
1
|
|
|
|
|
11
|
for my $option (sort keys %defaults) { |
1420
|
17
|
|
|
|
|
46
|
(my $o = $option) =~ s/^-//; |
1421
|
17
|
100
|
|
|
|
36
|
$defaults{$option} = '' unless defined $defaults{$option}; # avoid uninit var warnings |
1422
|
17
|
|
|
|
|
39
|
$result .= " $o=$defaults{$option}\n"; |
1423
|
|
|
|
|
|
|
} |
1424
|
1
|
|
|
|
|
4
|
$result .= "}\n\n"; |
1425
|
|
|
|
|
|
|
|
1426
|
1
|
|
|
|
|
5
|
for my $svc ($self->isp_services) { |
1427
|
3
|
|
|
|
|
9
|
my $vdev = $self->vdev($svc); |
1428
|
3
|
|
|
|
|
6
|
my $device = $self->dev($svc); |
1429
|
3
|
|
|
|
|
13
|
my $src_ip = $self->ip($svc); |
1430
|
3
|
|
|
|
|
9
|
my $ping = $self->ping($svc); |
1431
|
3
|
|
|
|
|
6
|
$result .= "connection {\n"; |
1432
|
3
|
|
|
|
|
8
|
$result .= " name=$svc\n"; |
1433
|
3
|
|
|
|
|
6
|
$result .= " device=$vdev\n"; |
1434
|
3
|
50
|
|
|
|
7
|
$result .= " sourceip=$src_ip\n" if $vdev ne $device; |
1435
|
3
|
|
|
|
|
5
|
$result .= " checkip=$ping\n"; |
1436
|
3
|
|
|
|
|
5
|
$result .= "}\n\n"; |
1437
|
|
|
|
|
|
|
} |
1438
|
|
|
|
|
|
|
|
1439
|
1
|
|
|
|
|
7
|
return $result; |
1440
|
|
|
|
|
|
|
} |
1441
|
|
|
|
|
|
|
|
1442
|
|
|
|
|
|
|
sub _parse_configuration_file { |
1443
|
2
|
|
|
2
|
|
5
|
my $self = shift; |
1444
|
2
|
|
|
|
|
4
|
my $path = shift; |
1445
|
2
|
|
|
|
|
5
|
my (%services,%lsm_options,@forwarding_group); |
1446
|
2
|
50
|
|
|
|
86
|
open my $f,$path or die "Could not open $path: $!"; |
1447
|
|
|
|
|
|
|
|
1448
|
2
|
|
|
|
|
49
|
while (<$f>) { |
1449
|
32
|
|
|
|
|
49
|
chomp; |
1450
|
32
|
100
|
|
|
|
87
|
next if /^\s*#/; |
1451
|
26
|
50
|
|
|
|
44
|
if (/^forwarding_group\s*=\s*(.+)$/) { # routing group |
1452
|
0
|
0
|
|
|
|
0
|
my @group = split /\s+/,$1 or next; |
1453
|
0
|
|
|
|
|
0
|
push @forwarding_group,\@group; |
1454
|
0
|
|
|
|
|
0
|
next; |
1455
|
|
|
|
|
|
|
} |
1456
|
26
|
100
|
|
|
|
99
|
if (/^mode\s*=\s*(.+)$/) { # operating mode |
1457
|
1
|
|
|
|
|
20
|
$self->operating_mode($1); |
1458
|
1
|
|
|
|
|
4
|
next; |
1459
|
|
|
|
|
|
|
} |
1460
|
25
|
50
|
|
|
|
49
|
if (/^(\w+)\s*=\s*(.*)$/) { # lsm config |
1461
|
0
|
|
|
|
|
0
|
$lsm_options{"-${1}"} = $2; |
1462
|
0
|
|
|
|
|
0
|
next; |
1463
|
|
|
|
|
|
|
} |
1464
|
25
|
|
|
|
|
96
|
my ($service,$device,$role,$ping_dest,$weight,$gateway) = split /\s+/; |
1465
|
25
|
50
|
66
|
|
|
108
|
next unless $service && $device && $role; |
|
|
|
66
|
|
|
|
|
1466
|
15
|
50
|
|
|
|
41
|
croak "load_balance.conf line $.: A service can not be named 'up' or 'down'" |
1467
|
|
|
|
|
|
|
if $service=~/^(up|down)$/; |
1468
|
|
|
|
|
|
|
|
1469
|
15
|
|
|
|
|
28
|
foreach (\$ping_dest,\$weight,\$gateway) { |
1470
|
45
|
100
|
|
|
|
91
|
undef $$_ if $$_ eq 'default'; |
1471
|
|
|
|
|
|
|
} |
1472
|
|
|
|
|
|
|
|
1473
|
15
|
|
|
|
|
41
|
$services{$service}{dev} = $device; |
1474
|
15
|
|
|
|
|
31
|
$services{$service}{role} = $role; |
1475
|
15
|
|
100
|
|
|
37
|
$services{$service}{ping} = $ping_dest // 'www.google.ca'; |
1476
|
15
|
|
100
|
|
|
40
|
$services{$service}{weight} = $weight // 1; |
1477
|
15
|
|
|
|
|
41
|
$services{$service}{gateway}= $gateway; |
1478
|
|
|
|
|
|
|
} |
1479
|
2
|
|
|
|
|
23
|
close $f; |
1480
|
2
|
|
|
|
|
13
|
$self->{svc_config} = \%services; |
1481
|
2
|
|
|
|
|
7
|
$self->{lsm_config} = \%lsm_options; |
1482
|
2
|
|
|
|
|
13
|
$self->{forwarding_groups} = \@forwarding_group; |
1483
|
|
|
|
|
|
|
} |
1484
|
|
|
|
|
|
|
|
1485
|
|
|
|
|
|
|
sub _collect_interfaces_retry { |
1486
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1487
|
0
|
|
|
|
|
0
|
my $retries = $self->dev_lookup_retries; |
1488
|
0
|
|
|
|
|
0
|
my $wait = $self->dev_lookup_retry_delay; |
1489
|
0
|
|
|
|
|
0
|
my %ifs; |
1490
|
0
|
|
|
|
|
0
|
for (1..$retries) { |
1491
|
0
|
|
|
|
|
0
|
delete $self->{_interface_info_cache}; # don't want to cache partial results |
1492
|
0
|
0
|
|
|
|
0
|
last if $self->_collect_interfaces(\%ifs); |
1493
|
0
|
|
|
|
|
0
|
sleep $wait; |
1494
|
|
|
|
|
|
|
} |
1495
|
0
|
|
|
|
|
0
|
$self->{services} = \%ifs; |
1496
|
|
|
|
|
|
|
} |
1497
|
|
|
|
|
|
|
|
1498
|
|
|
|
|
|
|
sub _collect_interfaces { |
1499
|
2
|
|
|
2
|
|
5
|
my $self = shift; |
1500
|
2
|
|
|
|
|
3
|
my $interface_info = shift; |
1501
|
|
|
|
|
|
|
|
1502
|
2
|
50
|
|
|
|
23
|
my $s = $self->{svc_config} or return; |
1503
|
2
|
|
|
|
|
10
|
my $i = $self->interface_info; |
1504
|
|
|
|
|
|
|
|
1505
|
|
|
|
|
|
|
# print STDERR Dumper($i); |
1506
|
|
|
|
|
|
|
|
1507
|
|
|
|
|
|
|
# map devices to services |
1508
|
2
|
|
|
|
|
5
|
my %devs; |
1509
|
2
|
|
|
|
|
10
|
for my $svc (keys %$s) { |
1510
|
15
|
|
|
|
|
24
|
my $vdev = $s->{$svc}{dev}; |
1511
|
15
|
|
|
|
|
26
|
$devs{$vdev}=$svc; |
1512
|
|
|
|
|
|
|
} |
1513
|
|
|
|
|
|
|
|
1514
|
2
|
|
|
|
|
6
|
my $counter = 0; |
1515
|
2
|
|
|
|
|
4
|
my $configured_interfaces = 0; |
1516
|
|
|
|
|
|
|
|
1517
|
2
|
|
|
|
|
15
|
for my $vdev (sort keys %devs) { |
1518
|
15
|
100
|
|
|
|
36
|
my $info = $i->{$vdev} or next; |
1519
|
13
|
|
|
|
|
18
|
my $dev = $info->{dev}; |
1520
|
13
|
|
|
|
|
24
|
my $svc = $devs{$vdev}; |
1521
|
13
|
|
|
|
|
16
|
my $role = $s->{$svc}{role}; |
1522
|
13
|
|
|
|
|
17
|
$configured_interfaces++; |
1523
|
|
|
|
|
|
|
|
1524
|
|
|
|
|
|
|
# copy into hash passed to us |
1525
|
|
|
|
|
|
|
$interface_info->{$svc} = { |
1526
|
|
|
|
|
|
|
dev => $dev, # otherwise, iptables will croak!!! |
1527
|
|
|
|
|
|
|
vdev => $vdev, |
1528
|
|
|
|
|
|
|
running => $info->{running}, |
1529
|
|
|
|
|
|
|
gw => $s->{$svc}{gateway} || $info->{gw}, |
1530
|
|
|
|
|
|
|
net => $info->{net}, |
1531
|
|
|
|
|
|
|
ip => $info->{ip}, |
1532
|
|
|
|
|
|
|
fwmark => $role eq 'isp' ? ++$counter : undef, |
1533
|
|
|
|
|
|
|
table => $role eq 'isp' ? $counter : undef, |
1534
|
|
|
|
|
|
|
role => $role, |
1535
|
|
|
|
|
|
|
ping => $s->{$svc}{ping}, |
1536
|
|
|
|
|
|
|
weight => $s->{$svc}{weight}, |
1537
|
|
|
|
|
|
|
} |
1538
|
13
|
100
|
66
|
|
|
107
|
} |
|
|
100
|
|
|
|
|
|
1539
|
2
|
|
|
|
|
8
|
return $configured_interfaces >= keys %devs; |
1540
|
|
|
|
|
|
|
} |
1541
|
|
|
|
|
|
|
|
1542
|
|
|
|
|
|
|
=head2 $if_hash = $bal->interface_info |
1543
|
|
|
|
|
|
|
|
1544
|
|
|
|
|
|
|
=head2 $if_hash = Net::ISP::Balance->interface_info |
1545
|
|
|
|
|
|
|
|
1546
|
|
|
|
|
|
|
This method returns a hashref containing information about each of the |
1547
|
|
|
|
|
|
|
network interfaces found on the system (independent of those mentioned |
1548
|
|
|
|
|
|
|
in the configuration file). It may be called as a class method or an |
1549
|
|
|
|
|
|
|
instance method. |
1550
|
|
|
|
|
|
|
|
1551
|
|
|
|
|
|
|
Each key in the hash is the name of a (virtual) interface device. The |
1552
|
|
|
|
|
|
|
values are hashrefs with the following keys: |
1553
|
|
|
|
|
|
|
|
1554
|
|
|
|
|
|
|
key value |
1555
|
|
|
|
|
|
|
--- ----- |
1556
|
|
|
|
|
|
|
dev name of the underlying physical device (usually same as vdev) |
1557
|
|
|
|
|
|
|
running boolean, true if interface is running |
1558
|
|
|
|
|
|
|
gw gateway, if present |
1559
|
|
|
|
|
|
|
net subnet in xxx.xxx.xxx.xxx/xx |
1560
|
|
|
|
|
|
|
|
1561
|
|
|
|
|
|
|
=cut |
1562
|
|
|
|
|
|
|
|
1563
|
|
|
|
|
|
|
|
1564
|
|
|
|
|
|
|
sub interface_info { |
1565
|
2
|
|
|
2
|
1
|
5
|
my $self = shift; |
1566
|
|
|
|
|
|
|
return $self->{_interface_info_cache} |
1567
|
2
|
50
|
33
|
|
|
12
|
if ref $self && exists $self->{_interface_info_cache}; |
1568
|
|
|
|
|
|
|
|
1569
|
2
|
|
|
|
|
4
|
my %results; # keyed by interface device |
1570
|
|
|
|
|
|
|
|
1571
|
|
|
|
|
|
|
# LOGIC TO DEAL WITH VIRTUAL INTERFACES |
1572
|
|
|
|
|
|
|
# 1. From _ip_addr_show get all the inet XXX.XXX.XXX.XXX lines and calculate |
1573
|
|
|
|
|
|
|
# corresponding network and virtual interface. |
1574
|
|
|
|
|
|
|
# 2. Record mapping of network to virtual interface in a hash (%vif) |
1575
|
|
|
|
|
|
|
# 3. When going through the routes, replace $dev with virtual interface name |
1576
|
|
|
|
|
|
|
# 4. In (keys %devs) loop, create an inner loop for each inet found and replace |
1577
|
|
|
|
|
|
|
# device with correct virtual device. |
1578
|
|
|
|
|
|
|
|
1579
|
|
|
|
|
|
|
# get interfaces with assigned addresses |
1580
|
2
|
|
|
|
|
7
|
my $a = $self->_ip_addr_show; |
1581
|
2
|
|
|
|
|
39
|
my (undef,@ifs) = split /^\d+: /m,$a; |
1582
|
2
|
|
|
|
|
7
|
chomp(@ifs); |
1583
|
|
|
|
|
|
|
my %ifs = map { |
1584
|
2
|
|
|
|
|
5
|
my ($dev,$config) = split(/: /,$_,2); |
|
20
|
|
|
|
|
50
|
|
1585
|
20
|
|
|
|
|
33
|
$dev =~ s/\@.+$//; # get rid of bonding master information |
1586
|
20
|
|
|
|
|
48
|
($dev,$config); |
1587
|
|
|
|
|
|
|
} @ifs; |
1588
|
|
|
|
|
|
|
|
1589
|
|
|
|
|
|
|
# find virtual interfaces |
1590
|
2
|
|
|
|
|
7
|
my (%vnet,%vif); |
1591
|
2
|
|
|
|
|
9
|
for my $dev (keys %ifs) { |
1592
|
20
|
|
|
|
|
32
|
my $info = $ifs{$dev}; |
1593
|
20
|
|
|
|
|
215
|
while ($info =~ /inet (\d+\.\d+\.\d+\.\d+)(?:\/(\d+))?.+?(\S+)$/mg) { |
1594
|
18
|
|
|
|
|
62
|
my ($addr,$bits,$vdev) = ($1,$2,$3); |
1595
|
18
|
50
|
|
|
|
39
|
$addr or next; |
1596
|
18
|
|
100
|
|
|
55
|
$bits ||= 32; |
1597
|
18
|
|
|
|
|
50
|
my ($peer) = $info =~ /peer\s+(\d+\.\d+\.\d+\.\d+)/; |
1598
|
18
|
|
|
|
|
75
|
my $block = Net::Netmask->new2("$addr/$bits"); |
1599
|
18
|
|
|
|
|
1372
|
$vnet{$dev}{"$block"} = $vdev; |
1600
|
18
|
|
|
|
|
249
|
$vif{$dev}{$vdev}{block} = $block; |
1601
|
18
|
|
|
|
|
115
|
$vif{$dev}{$vdev}{addr} = $addr; |
1602
|
|
|
|
|
|
|
} |
1603
|
|
|
|
|
|
|
} |
1604
|
|
|
|
|
|
|
|
1605
|
|
|
|
|
|
|
# get existing routes |
1606
|
2
|
|
|
|
|
16
|
my (%gws,%nets); |
1607
|
2
|
|
|
|
|
9
|
my $r = $self->_ip_route_show; |
1608
|
2
|
|
|
|
|
21
|
my @routes = split /^(?!\s)/m,$r; |
1609
|
2
|
|
|
|
|
6
|
chomp(@routes); |
1610
|
2
|
|
|
|
|
6
|
foreach (@routes) { |
1611
|
16
|
|
|
|
|
55
|
while (/(\S+)\s+via\s+(\S+)\s+dev\s+(\S+)/g) { |
1612
|
6
|
|
|
|
|
23
|
my ($net,$gateway,$dev) = ($1,$2,$3); |
1613
|
6
|
100
|
|
|
|
22
|
($net) = /^(\S+)/ if $net eq 'nexthop'; |
1614
|
6
|
|
33
|
|
|
24
|
my $vdev = $vnet{$dev}{$net} || $dev; |
1615
|
6
|
100
|
|
|
|
19
|
$nets{$vdev} = $net unless $net eq 'default'; |
1616
|
6
|
|
|
|
|
24
|
$gws{$vdev} = $gateway; |
1617
|
|
|
|
|
|
|
} |
1618
|
|
|
|
|
|
|
} |
1619
|
|
|
|
|
|
|
|
1620
|
2
|
|
|
|
|
19
|
for my $dev (keys %ifs) { |
1621
|
20
|
|
|
|
|
39
|
my $info = $ifs{$dev}; |
1622
|
20
|
|
|
|
|
80
|
my $running = $info =~ /[<,]UP[,>]/; |
1623
|
20
|
|
|
|
|
51
|
my ($peer) = $info =~ /peer\s+(\d+\.\d+\.\d+\.\d+)/; |
1624
|
20
|
|
|
|
|
38
|
for my $vdev (keys %{$vif{$dev}}) { |
|
20
|
|
|
|
|
63
|
|
1625
|
18
|
|
|
|
|
33
|
my $addr = $vif{$dev}{$vdev}{addr}; |
1626
|
18
|
|
|
|
|
28
|
my $block = $vif{$dev}{$vdev}{block}; |
1627
|
18
|
|
66
|
|
|
96
|
my $net = $nets{$dev} || ($peer?"$peer/32":undef) || "$block"; |
1628
|
18
|
|
33
|
|
|
208
|
my $gw = $gws{$dev} || $peer |
1629
|
|
|
|
|
|
|
|| $self->_dhcp_gateway($dev) |
1630
|
|
|
|
|
|
|
|| $block->nth(1); # this guess is correct >95% of time |
1631
|
|
|
|
|
|
|
|
1632
|
|
|
|
|
|
|
# copy into hash passed to us |
1633
|
18
|
|
|
|
|
558
|
$results{$vdev} = { |
1634
|
|
|
|
|
|
|
dev => $dev, # otherwise, iptables will croak!!! |
1635
|
|
|
|
|
|
|
vdev => $vdev, |
1636
|
|
|
|
|
|
|
running => $running, |
1637
|
|
|
|
|
|
|
gw => $gw, |
1638
|
|
|
|
|
|
|
net => $net, |
1639
|
|
|
|
|
|
|
ip => $addr, |
1640
|
|
|
|
|
|
|
} |
1641
|
|
|
|
|
|
|
} |
1642
|
|
|
|
|
|
|
} |
1643
|
|
|
|
|
|
|
|
1644
|
2
|
50
|
|
|
|
14
|
$self->{_interface_info_cache} = \%results if ref $self; |
1645
|
2
|
|
|
|
|
52
|
return \%results; |
1646
|
|
|
|
|
|
|
} |
1647
|
|
|
|
|
|
|
|
1648
|
|
|
|
|
|
|
sub _ip_addr_show { |
1649
|
2
|
|
|
2
|
|
4
|
my $self = shift; |
1650
|
2
|
|
33
|
|
|
3
|
return eval{$self->{dummy_data}{"ip_addr_show"}} || `ip addr show`; |
1651
|
|
|
|
|
|
|
} |
1652
|
|
|
|
|
|
|
|
1653
|
|
|
|
|
|
|
sub _ip_route_show { |
1654
|
2
|
|
|
2
|
|
6
|
my $self = shift; |
1655
|
2
|
|
33
|
|
|
12
|
return eval{$self->{dummy_data}{"ip_route_show"}} || `ip route show all`; |
1656
|
|
|
|
|
|
|
} |
1657
|
|
|
|
|
|
|
|
1658
|
|
|
|
|
|
|
# This subroutine is called for dhcp-assigned IP addresses to try to |
1659
|
|
|
|
|
|
|
# get the gateway. It is used for those unusual cases in which the gateway |
1660
|
|
|
|
|
|
|
# is NOT the first IP address in the net block. |
1661
|
|
|
|
|
|
|
# In versions 1.05 and older, we tried to recover this information on static |
1662
|
|
|
|
|
|
|
# interfaces by reading /etc/network/interfaces as well, but the file location was too |
1663
|
|
|
|
|
|
|
# unpredictable across different Linux distros. |
1664
|
|
|
|
|
|
|
sub _dhcp_gateway { |
1665
|
12
|
|
|
12
|
|
20
|
my $self = shift; |
1666
|
12
|
|
|
|
|
17
|
my $dev = shift; |
1667
|
12
|
50
|
|
|
|
28
|
my $fh = $self->_open_dhclient_leases($dev) or return; |
1668
|
0
|
|
|
|
|
0
|
my ($gw); |
1669
|
0
|
|
|
|
|
0
|
while (<$fh>) { |
1670
|
0
|
|
|
|
|
0
|
chomp; |
1671
|
0
|
0
|
|
|
|
0
|
$gw = $1 if /option routers (\S+)[,;]/; |
1672
|
|
|
|
|
|
|
} |
1673
|
0
|
|
|
|
|
0
|
return $gw; |
1674
|
|
|
|
|
|
|
} |
1675
|
|
|
|
|
|
|
|
1676
|
|
|
|
|
|
|
sub _open_dhclient_leases { |
1677
|
12
|
|
|
12
|
|
17
|
my $self = shift; |
1678
|
12
|
|
|
|
|
17
|
my $device = shift; |
1679
|
12
|
50
|
|
|
|
17
|
if (my $dummy = eval{$self->{dummy_data}{"leases_$device"}}) { |
|
12
|
|
|
|
|
50
|
|
1680
|
0
|
0
|
|
|
|
0
|
open my $fh,'<',\$dummy or die $!; |
1681
|
0
|
|
|
|
|
0
|
return $fh; |
1682
|
|
|
|
|
|
|
} |
1683
|
12
|
50
|
|
|
|
29
|
my $leases = $self->_find_dhclient_leases($device) or return; |
1684
|
0
|
0
|
|
|
|
0
|
open my $fh,$leases or die "Can't open $leases: $!"; |
1685
|
0
|
|
|
|
|
0
|
return $fh; |
1686
|
|
|
|
|
|
|
} |
1687
|
|
|
|
|
|
|
|
1688
|
|
|
|
|
|
|
sub _find_dhclient_leases { |
1689
|
12
|
|
|
12
|
|
28
|
my $self = shift; |
1690
|
12
|
|
|
|
|
18
|
my $device = shift; |
1691
|
12
|
|
|
|
|
26
|
my @locations = ('/var/lib/NetworkManager','/var/lib/dhcp','/var/lib/dhclient'); |
1692
|
12
|
|
|
|
|
22
|
for my $l (@locations) { |
1693
|
36
|
|
|
|
|
1031
|
my @matches = glob("$l/dhclient*$device.lease*"); |
1694
|
36
|
50
|
|
|
|
152
|
next unless @matches; |
1695
|
0
|
|
|
|
|
0
|
return $matches[0]; |
1696
|
|
|
|
|
|
|
} |
1697
|
12
|
|
|
|
|
102
|
return; |
1698
|
|
|
|
|
|
|
} |
1699
|
|
|
|
|
|
|
|
1700
|
|
|
|
|
|
|
|
1701
|
|
|
|
|
|
|
|
1702
|
|
|
|
|
|
|
#################################### here are the routing rules ################### |
1703
|
|
|
|
|
|
|
|
1704
|
|
|
|
|
|
|
=head2 $bal->set_routes() |
1705
|
|
|
|
|
|
|
|
1706
|
|
|
|
|
|
|
This method is called by set_routes_and_firewall() to emit the rules |
1707
|
|
|
|
|
|
|
needed to create the load balancing routing tables. |
1708
|
|
|
|
|
|
|
|
1709
|
|
|
|
|
|
|
=cut |
1710
|
|
|
|
|
|
|
|
1711
|
|
|
|
|
|
|
sub set_routes { |
1712
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
1713
|
0
|
|
|
|
|
0
|
$self->_initialize_routes(); |
1714
|
0
|
|
|
|
|
0
|
$self->routing_rules(); |
1715
|
0
|
|
|
|
|
0
|
$self->local_routing_rules(); |
1716
|
|
|
|
|
|
|
} |
1717
|
|
|
|
|
|
|
|
1718
|
|
|
|
|
|
|
=head2 $bal->set_firewall |
1719
|
|
|
|
|
|
|
|
1720
|
|
|
|
|
|
|
This method is called by set_routes_and_firewall() to emit the rules |
1721
|
|
|
|
|
|
|
needed to create the balancing firewall. |
1722
|
|
|
|
|
|
|
|
1723
|
|
|
|
|
|
|
=cut |
1724
|
|
|
|
|
|
|
|
1725
|
|
|
|
|
|
|
sub set_firewall { |
1726
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
1727
|
0
|
0
|
|
|
|
0
|
$self->_save_custom_chains if $self->keep_custom_chains; |
1728
|
0
|
|
|
|
|
0
|
$self->_initialize_firewall(); |
1729
|
0
|
|
|
|
|
0
|
$self->base_fw_rules(); |
1730
|
0
|
0
|
|
|
|
0
|
$self->_restore_custom_chains if $self->keep_custom_chains; |
1731
|
0
|
|
|
|
|
0
|
$self->balancing_fw_rules(); # WARNING: This is a null-op in "failover" mode |
1732
|
0
|
|
|
|
|
0
|
$self->sanity_fw_rules(); |
1733
|
0
|
|
|
|
|
0
|
$self->nat_fw_rules(); |
1734
|
0
|
|
|
|
|
0
|
$self->local_fw_rules(); |
1735
|
|
|
|
|
|
|
} |
1736
|
|
|
|
|
|
|
|
1737
|
|
|
|
|
|
|
|
1738
|
|
|
|
|
|
|
=head2 $bal->enable_forwarding($boolean) |
1739
|
|
|
|
|
|
|
|
1740
|
|
|
|
|
|
|
=cut |
1741
|
|
|
|
|
|
|
|
1742
|
|
|
|
|
|
|
sub enable_forwarding { |
1743
|
1
|
|
|
1
|
1
|
1178
|
my $self = shift; |
1744
|
1
|
50
|
|
|
|
4
|
my $enable = $_[0] ? 1 : 0; |
1745
|
1
|
|
|
|
|
8
|
$self->sh("echo $enable > /proc/sys/net/ipv4/ip_forward"); |
1746
|
|
|
|
|
|
|
} |
1747
|
|
|
|
|
|
|
=head2 $bal->routing_rules() |
1748
|
|
|
|
|
|
|
|
1749
|
|
|
|
|
|
|
This method is called by set_routes() to emit the rules needed to |
1750
|
|
|
|
|
|
|
create the routing rules. |
1751
|
|
|
|
|
|
|
|
1752
|
|
|
|
|
|
|
=cut |
1753
|
|
|
|
|
|
|
|
1754
|
|
|
|
|
|
|
sub routing_rules { |
1755
|
2
|
|
|
2
|
0
|
9
|
my $self = shift; |
1756
|
|
|
|
|
|
|
# main table |
1757
|
2
|
|
|
|
|
8
|
$self->ip_route("add ",$self->net($_),'dev',$self->dev($_),'src',$self->ip($_)) foreach $self->service_names; |
1758
|
|
|
|
|
|
|
|
1759
|
|
|
|
|
|
|
# different handling of the default route depending on whether we are in |
1760
|
|
|
|
|
|
|
# "balanced" or "failover" mode. |
1761
|
2
|
|
|
|
|
8
|
my $mode = $self->operating_mode; |
1762
|
2
|
100
|
|
|
|
10
|
if ($mode eq 'balanced') { |
|
|
50
|
|
|
|
|
|
1763
|
1
|
|
|
|
|
4
|
$self->_create_default_multipath_route(); |
1764
|
|
|
|
|
|
|
} elsif ($mode eq 'failover') { |
1765
|
1
|
|
|
|
|
5
|
$self->_create_default_failover_route(); |
1766
|
|
|
|
|
|
|
} |
1767
|
|
|
|
|
|
|
|
1768
|
2
|
|
|
|
|
7
|
$self->_create_service_routing_tables(); |
1769
|
|
|
|
|
|
|
} |
1770
|
|
|
|
|
|
|
|
1771
|
|
|
|
|
|
|
sub _initialize_routes { |
1772
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1773
|
0
|
|
|
|
|
0
|
$self->sh(<
|
1774
|
|
|
|
|
|
|
ip route flush all |
1775
|
|
|
|
|
|
|
ip rule flush all |
1776
|
|
|
|
|
|
|
ip rule add from all lookup main pref 32766 |
1777
|
|
|
|
|
|
|
ip rule add from all lookup default pref 32767 |
1778
|
|
|
|
|
|
|
END |
1779
|
|
|
|
|
|
|
; |
1780
|
|
|
|
|
|
|
|
1781
|
0
|
|
|
|
|
0
|
$self->ip_route("flush table ",$self->table($_),'2>/dev/null') foreach $self->isp_services; |
1782
|
|
|
|
|
|
|
} |
1783
|
|
|
|
|
|
|
|
1784
|
|
|
|
|
|
|
sub _create_default_multipath_route { |
1785
|
1
|
|
|
1
|
|
3
|
my $self = shift; |
1786
|
|
|
|
|
|
|
|
1787
|
1
|
|
|
|
|
4
|
my @up = $self->up; |
1788
|
|
|
|
|
|
|
|
1789
|
|
|
|
|
|
|
# create multipath route |
1790
|
1
|
50
|
|
|
|
5
|
if (@up > 1) { # multipath |
1791
|
1
|
50
|
|
|
|
3
|
print STDERR "# setting multipath default gw\n" if $self->verbose; |
1792
|
|
|
|
|
|
|
# EG |
1793
|
|
|
|
|
|
|
# ip route add default scope global nexthop via 192.168.10.1 dev eth0 weight 1 \ |
1794
|
|
|
|
|
|
|
# nexthop via 192.168.11.1 dev eth1 weight 1 |
1795
|
1
|
|
|
|
|
3
|
my $hops = ''; |
1796
|
1
|
|
|
|
|
3
|
for my $svc (@up) { |
1797
|
3
|
50
|
|
|
|
7
|
my $gw = $self->gw($svc) or next; |
1798
|
3
|
50
|
|
|
|
19
|
my $dev = $self->vdev($svc) or next; |
1799
|
3
|
50
|
|
|
|
7
|
my $weight = $self->weight($svc) or next; |
1800
|
3
|
|
|
|
|
13
|
$hops .= "nexthop via $gw dev $dev weight $weight "; |
1801
|
|
|
|
|
|
|
} |
1802
|
1
|
50
|
|
|
|
4
|
die "no valid gateways!" unless $hops; |
1803
|
1
|
|
|
|
|
4
|
$self->ip_route("add default scope global $hops"); |
1804
|
|
|
|
|
|
|
} |
1805
|
|
|
|
|
|
|
|
1806
|
|
|
|
|
|
|
else { |
1807
|
0
|
0
|
|
|
|
0
|
print STDERR "# setting single default route via $up[0]n" if $self->verbose; |
1808
|
0
|
|
|
|
|
0
|
$self->ip_route("add default via",$self->gw($up[0]),'dev',$self->dev($up[0])); |
1809
|
|
|
|
|
|
|
} |
1810
|
|
|
|
|
|
|
|
1811
|
|
|
|
|
|
|
} |
1812
|
|
|
|
|
|
|
|
1813
|
|
|
|
|
|
|
sub _create_default_failover_route { |
1814
|
1
|
|
|
1
|
|
2
|
my $self = shift; |
1815
|
1
|
|
|
|
|
4
|
my $preferred = $self->preferred_service; |
1816
|
1
|
50
|
|
|
|
3
|
print STDERR "# setting single default route via $preferred\n" if $self->verbose; |
1817
|
1
|
|
|
|
|
4
|
$self->ip_route("add default via",$self->gw($preferred),'dev',$self->dev($preferred)); |
1818
|
|
|
|
|
|
|
} |
1819
|
|
|
|
|
|
|
|
1820
|
|
|
|
|
|
|
=head2 $service = $bal->preferred_service |
1821
|
|
|
|
|
|
|
|
1822
|
|
|
|
|
|
|
Returns the preferred service, which is the currently running service with the highest weight. Used for |
1823
|
|
|
|
|
|
|
failover mode. |
1824
|
|
|
|
|
|
|
|
1825
|
|
|
|
|
|
|
=cut |
1826
|
|
|
|
|
|
|
|
1827
|
|
|
|
|
|
|
sub preferred_service { |
1828
|
1
|
|
|
1
|
1
|
2
|
my $self = shift; |
1829
|
1
|
|
|
|
|
3
|
my @up = sort { $self->weight($b) <=> $self->weight($a) } $self->up; |
|
1
|
|
|
|
|
4
|
|
1830
|
1
|
|
|
|
|
3
|
return $up[0]; |
1831
|
|
|
|
|
|
|
} |
1832
|
|
|
|
|
|
|
|
1833
|
|
|
|
|
|
|
sub _create_service_routing_tables { |
1834
|
2
|
|
|
2
|
|
3
|
my $self = shift; |
1835
|
|
|
|
|
|
|
|
1836
|
2
|
|
|
|
|
5
|
for my $svc ($self->isp_services) { |
1837
|
5
|
50
|
|
|
|
13
|
print STDERR "# creating routing table for $svc\n" if $self->verbose; |
1838
|
5
|
|
|
|
|
13
|
$self->ip_route('flush table',$self->table($svc)); |
1839
|
5
|
|
|
|
|
14
|
$self->ip_route('add table',$self->table($svc),'default dev',$self->dev($svc),'via',$self->gw($svc)); |
1840
|
5
|
|
|
|
|
10
|
for my $s ($self->service_names) { |
1841
|
36
|
|
|
|
|
67
|
$self->ip_route('add table',$self->table($svc),$self->net($s),'dev',$self->dev($s),'src',$self->ip($s)); |
1842
|
|
|
|
|
|
|
} |
1843
|
5
|
|
|
|
|
13
|
$self->ip_rule('add from',$self->ip($svc),'table',$self->table($svc)); |
1844
|
5
|
|
|
|
|
12
|
$self->ip_rule('add oif',$self->vdev($svc),'table',$self->table($svc)); |
1845
|
5
|
|
|
|
|
12
|
$self->ip_rule('add fwmark',$self->fwmark($svc),'table',$self->table($svc)); |
1846
|
|
|
|
|
|
|
} |
1847
|
|
|
|
|
|
|
} |
1848
|
|
|
|
|
|
|
|
1849
|
|
|
|
|
|
|
=head2 $bal->local_routing_rules() |
1850
|
|
|
|
|
|
|
|
1851
|
|
|
|
|
|
|
This method is called by set_routes() to process the fules and emit |
1852
|
|
|
|
|
|
|
the commands contained in the customized route files located in |
1853
|
|
|
|
|
|
|
$ETC_DIR/balance/routes. |
1854
|
|
|
|
|
|
|
|
1855
|
|
|
|
|
|
|
=cut |
1856
|
|
|
|
|
|
|
|
1857
|
|
|
|
|
|
|
sub local_routing_rules { |
1858
|
1
|
|
|
1
|
1
|
6
|
my $self = shift; |
1859
|
1
|
|
|
|
|
4
|
my $dir = $self->rules_directory; |
1860
|
1
|
|
|
|
|
123
|
my @files = sort glob("$dir/routes/*"); |
1861
|
1
|
|
|
|
|
8
|
$self->_execute_rules_files(@files); |
1862
|
|
|
|
|
|
|
} |
1863
|
|
|
|
|
|
|
|
1864
|
|
|
|
|
|
|
=head2 $bal->local_fw_rules() |
1865
|
|
|
|
|
|
|
|
1866
|
|
|
|
|
|
|
This method is called by set_firewall() to process the fules and emit |
1867
|
|
|
|
|
|
|
the commands contained in the customized route files located in |
1868
|
|
|
|
|
|
|
$ETC_DIR/balance/firewall. |
1869
|
|
|
|
|
|
|
|
1870
|
|
|
|
|
|
|
=cut |
1871
|
|
|
|
|
|
|
|
1872
|
|
|
|
|
|
|
sub local_fw_rules { |
1873
|
1
|
|
|
1
|
1
|
713
|
my $self = shift; |
1874
|
1
|
|
|
|
|
4
|
my $dir = $self->rules_directory; |
1875
|
1
|
|
|
|
|
107
|
my @files = sort glob("$dir/firewall/*"); |
1876
|
1
|
|
|
|
|
7
|
$self->_execute_rules_files(@files); |
1877
|
|
|
|
|
|
|
} |
1878
|
|
|
|
|
|
|
|
1879
|
|
|
|
|
|
|
=head2 $bal->pre_run_rules() |
1880
|
|
|
|
|
|
|
|
1881
|
|
|
|
|
|
|
This method is called by set_routes_and_firewall() to process the fules and emit |
1882
|
|
|
|
|
|
|
the commands contained in the customized route files located in |
1883
|
|
|
|
|
|
|
$ETC_DIR/balance/pre-run. |
1884
|
|
|
|
|
|
|
|
1885
|
|
|
|
|
|
|
=cut |
1886
|
|
|
|
|
|
|
|
1887
|
|
|
|
|
|
|
sub pre_run_rules { |
1888
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
1889
|
0
|
|
|
|
|
0
|
my $dir = $self->rules_directory; |
1890
|
0
|
|
|
|
|
0
|
my @files = sort glob("$dir/pre-run/*"); |
1891
|
0
|
|
|
|
|
0
|
$self->_execute_rules_files(@files); |
1892
|
|
|
|
|
|
|
} |
1893
|
|
|
|
|
|
|
|
1894
|
|
|
|
|
|
|
=head2 $bal->post_run_rules() |
1895
|
|
|
|
|
|
|
|
1896
|
|
|
|
|
|
|
This method is called by set__routes_andfirewall() to process the |
1897
|
|
|
|
|
|
|
fules and emit the commands contained in the customized route files |
1898
|
|
|
|
|
|
|
located in $ETC_DIR/balance/post-run. |
1899
|
|
|
|
|
|
|
|
1900
|
|
|
|
|
|
|
=cut |
1901
|
|
|
|
|
|
|
|
1902
|
|
|
|
|
|
|
sub post_run_rules { |
1903
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
1904
|
0
|
|
|
|
|
0
|
my $dir = $self->rules_directory; |
1905
|
0
|
|
|
|
|
0
|
my @files = sort glob("$dir/post-run/*"); |
1906
|
0
|
|
|
|
|
0
|
$self->_execute_rules_files(@files); |
1907
|
|
|
|
|
|
|
} |
1908
|
|
|
|
|
|
|
|
1909
|
|
|
|
|
|
|
|
1910
|
|
|
|
|
|
|
sub _execute_rules_files { |
1911
|
2
|
|
|
2
|
|
5
|
my $self = shift; |
1912
|
2
|
|
|
|
|
12
|
my @files = @_; |
1913
|
|
|
|
|
|
|
|
1914
|
2
|
|
|
|
|
7
|
for my $f (@files) { |
1915
|
5
|
50
|
|
|
|
19
|
next if $f =~ /~$/; # ignore emacs backup files |
1916
|
5
|
50
|
|
|
|
12
|
next if $f =~ /^#/; # ignore autosave files |
1917
|
5
|
50
|
|
|
|
11
|
print STDERR "# executing contents of $f\n" if $self->verbose; |
1918
|
5
|
|
|
|
|
17
|
$self->sh("## Including rules from $f ##\n"); |
1919
|
5
|
50
|
33
|
|
|
48
|
next if $f =~ /(~|\.bak)$/ or $f=~/^#/; |
1920
|
|
|
|
|
|
|
|
1921
|
5
|
100
|
|
|
|
21
|
if ($f =~ /\.pl$/) { # perl script |
1922
|
3
|
|
|
|
|
7
|
our $B = $self; |
1923
|
3
|
|
|
|
|
1129
|
do $f; |
1924
|
3
|
50
|
|
|
|
22
|
warn $@ if $@; |
1925
|
|
|
|
|
|
|
} else { |
1926
|
2
|
50
|
|
|
|
88
|
open my $fh,$f or die "Couldn't open $f: $!"; |
1927
|
2
|
|
|
|
|
51
|
$self->sh($_) while <$fh>; |
1928
|
2
|
|
|
|
|
25
|
close $fh; |
1929
|
|
|
|
|
|
|
} |
1930
|
5
|
|
|
|
|
19
|
$self->sh("## Finished $f ##\n"); |
1931
|
|
|
|
|
|
|
} |
1932
|
|
|
|
|
|
|
} |
1933
|
|
|
|
|
|
|
|
1934
|
|
|
|
|
|
|
######################### |
1935
|
|
|
|
|
|
|
# firewall rules |
1936
|
|
|
|
|
|
|
######################### |
1937
|
|
|
|
|
|
|
|
1938
|
|
|
|
|
|
|
sub _initialize_firewall { |
1939
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1940
|
0
|
|
|
|
|
0
|
$self->sh(<
|
1941
|
|
|
|
|
|
|
iptables -F |
1942
|
|
|
|
|
|
|
iptables -X |
1943
|
|
|
|
|
|
|
iptables -t nat -F |
1944
|
|
|
|
|
|
|
iptables -t nat -X |
1945
|
|
|
|
|
|
|
iptables -t mangle -F |
1946
|
|
|
|
|
|
|
iptables -t mangle -X |
1947
|
|
|
|
|
|
|
END |
1948
|
|
|
|
|
|
|
} |
1949
|
|
|
|
|
|
|
|
1950
|
|
|
|
|
|
|
=head2 $bal->base_fw_rules() |
1951
|
|
|
|
|
|
|
|
1952
|
|
|
|
|
|
|
This method is called by set_firewall() to set up basic firewall |
1953
|
|
|
|
|
|
|
rules, including default rules and reporting. |
1954
|
|
|
|
|
|
|
|
1955
|
|
|
|
|
|
|
=cut |
1956
|
|
|
|
|
|
|
|
1957
|
|
|
|
|
|
|
sub base_fw_rules { |
1958
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
1959
|
0
|
|
|
|
|
0
|
$self->sh(<
|
1960
|
|
|
|
|
|
|
iptables -P INPUT DROP |
1961
|
|
|
|
|
|
|
iptables -P OUTPUT DROP |
1962
|
|
|
|
|
|
|
iptables -P FORWARD DROP |
1963
|
|
|
|
|
|
|
|
1964
|
|
|
|
|
|
|
iptables -N REJECTPERM |
1965
|
|
|
|
|
|
|
iptables -A REJECTPERM -j LOG -m limit --limit 1/minute --log-level 4 --log-prefix "REJECTED: " |
1966
|
|
|
|
|
|
|
iptables -A REJECTPERM -j REJECT --reject-with icmp-net-unreachable |
1967
|
|
|
|
|
|
|
|
1968
|
|
|
|
|
|
|
iptables -N DROPGEN |
1969
|
|
|
|
|
|
|
iptables -A DROPGEN -j LOG -m limit --limit 1/minute --log-level 4 --log-prefix "GENERAL: " |
1970
|
|
|
|
|
|
|
iptables -A DROPGEN -j DROP |
1971
|
|
|
|
|
|
|
|
1972
|
|
|
|
|
|
|
iptables -N DROPINVAL |
1973
|
|
|
|
|
|
|
iptables -A DROPINVAL -j LOG -m limit --limit 1/minute --log-level 4 --log-prefix "INVALID: " |
1974
|
|
|
|
|
|
|
iptables -A DROPINVAL -j DROP |
1975
|
|
|
|
|
|
|
|
1976
|
|
|
|
|
|
|
iptables -N DROPPERM |
1977
|
|
|
|
|
|
|
iptables -A DROPPERM -j LOG -m limit --limit 1/minute --log-level 4 --log-prefix "ACCESS-DENIED: " |
1978
|
|
|
|
|
|
|
iptables -A DROPPERM -j DROP |
1979
|
|
|
|
|
|
|
|
1980
|
|
|
|
|
|
|
iptables -N DROPSPOOF |
1981
|
|
|
|
|
|
|
iptables -A DROPSPOOF -j LOG -m limit --limit 1/minute --log-level 4 --log-prefix "DROP-SPOOF: " |
1982
|
|
|
|
|
|
|
iptables -A DROPSPOOF -j DROP |
1983
|
|
|
|
|
|
|
|
1984
|
|
|
|
|
|
|
iptables -N DROPFLOOD |
1985
|
|
|
|
|
|
|
iptables -A DROPFLOOD -m limit --limit 1/minute -j LOG --log-level 4 --log-prefix "DROP-FLOOD: " |
1986
|
|
|
|
|
|
|
iptables -A DROPFLOOD -j DROP |
1987
|
|
|
|
|
|
|
|
1988
|
|
|
|
|
|
|
iptables -N DEBUG |
1989
|
|
|
|
|
|
|
iptables -A DEBUG -j LOG --log-level 3 --log-prefix "DEBUG: " |
1990
|
|
|
|
|
|
|
END |
1991
|
|
|
|
|
|
|
; |
1992
|
0
|
0
|
|
|
|
0
|
if ($self->iptables_verbose) { |
1993
|
0
|
0
|
|
|
|
0
|
print STDERR " #Setting up debugging logging\n" if $self->verbose; |
1994
|
0
|
|
|
|
|
0
|
$self->sh(<
|
1995
|
|
|
|
|
|
|
iptables -A INPUT -j LOG --log-prefix "INPUT: " |
1996
|
|
|
|
|
|
|
iptables -A OUTPUT -j LOG --log-prefix "OUTPUT: " |
1997
|
|
|
|
|
|
|
iptables -A FORWARD -j LOG --log-prefix "FORWARD: " |
1998
|
|
|
|
|
|
|
iptables -t nat -A INPUT -j LOG --log-prefix "nat INPUT: " |
1999
|
|
|
|
|
|
|
iptables -t nat -A OUTPUT -j LOG --log-prefix "nat OUTPUT: " |
2000
|
|
|
|
|
|
|
iptables -t nat -A FORWARD -j LOG --log-prefix "nat FORWARD: " |
2001
|
|
|
|
|
|
|
iptables -t nat -A PREROUTING -j LOG --log-prefix "nat PREROUTING: " |
2002
|
|
|
|
|
|
|
iptables -t nat -A POSTROUTING -j LOG --log-prefix "nat POSTROUTING: " |
2003
|
|
|
|
|
|
|
iptables -t mangle -A INPUT -j LOG --log-prefix "mangle INPUT: " |
2004
|
|
|
|
|
|
|
iptables -t mangle -A OUTPUT -j LOG --log-prefix "mangle OUTPUT: " |
2005
|
|
|
|
|
|
|
iptables -t mangle -A FORWARD -j LOG --log-prefix "mangle FORWARD: " |
2006
|
|
|
|
|
|
|
iptables -t mangle -A PREROUTING -j LOG --log-prefix "mangle PRE: " |
2007
|
|
|
|
|
|
|
END |
2008
|
|
|
|
|
|
|
; |
2009
|
|
|
|
|
|
|
} |
2010
|
|
|
|
|
|
|
} |
2011
|
|
|
|
|
|
|
|
2012
|
|
|
|
|
|
|
=head2 $bal->balancing_fw_rules() |
2013
|
|
|
|
|
|
|
|
2014
|
|
|
|
|
|
|
This method is called by set_firewall() to set up the mangle/fwmark |
2015
|
|
|
|
|
|
|
rules for balancing outgoing connections. |
2016
|
|
|
|
|
|
|
|
2017
|
|
|
|
|
|
|
=cut |
2018
|
|
|
|
|
|
|
|
2019
|
|
|
|
|
|
|
sub balancing_fw_rules { |
2020
|
3
|
|
|
3
|
1
|
1654
|
my $self = shift; |
2021
|
|
|
|
|
|
|
|
2022
|
3
|
100
|
|
|
|
9
|
return unless $self->operating_mode eq 'balanced'; |
2023
|
|
|
|
|
|
|
|
2024
|
2
|
50
|
|
|
|
6
|
print STDERR "# balancing FW rules\n" if $self->verbose; |
2025
|
|
|
|
|
|
|
|
2026
|
2
|
|
|
|
|
6
|
for my $svc ($self->isp_services) { |
2027
|
6
|
|
|
|
|
13
|
my $table = $self->mark_table($svc); |
2028
|
6
|
|
|
|
|
12
|
my $mark = $self->fwmark($svc); |
2029
|
6
|
50
|
33
|
|
|
22
|
next unless defined $mark && defined $table; |
2030
|
6
|
|
|
|
|
21
|
$self->sh(<
|
2031
|
|
|
|
|
|
|
iptables -t mangle -N $table |
2032
|
|
|
|
|
|
|
iptables -t mangle -A $table -j MARK --set-mark $mark |
2033
|
|
|
|
|
|
|
iptables -t mangle -A $table -j CONNMARK --save-mark |
2034
|
|
|
|
|
|
|
END |
2035
|
|
|
|
|
|
|
} |
2036
|
|
|
|
|
|
|
|
2037
|
2
|
|
|
|
|
5
|
my @up = $self->up; |
2038
|
|
|
|
|
|
|
|
2039
|
|
|
|
|
|
|
# packets from LAN |
2040
|
2
|
|
|
|
|
6
|
for my $lan ($self->lan_services) { |
2041
|
8
|
|
|
|
|
17
|
my $landev = $self->vdev($lan); |
2042
|
8
|
|
|
|
|
15
|
my $src = $self->net($lan); |
2043
|
|
|
|
|
|
|
|
2044
|
8
|
100
|
|
|
|
16
|
if (@up > 1) { |
2045
|
4
|
50
|
|
|
|
10
|
print STDERR "# creating balanced mangling rules\n" if $self->verbose; |
2046
|
4
|
|
|
|
|
7
|
my $count = @up; |
2047
|
4
|
|
|
|
|
11
|
my $probabilities = $self->_weight_to_probability(\@up); |
2048
|
4
|
|
|
|
|
11
|
for my $svc (sort {$probabilities->{$b} <=> $probabilities->{$a}} @up) { |
|
8
|
|
|
|
|
16
|
|
2049
|
12
|
|
|
|
|
23
|
my $table = $self->mark_table($svc); |
2050
|
12
|
|
|
|
|
22
|
my $probability = $probabilities->{$svc}; |
2051
|
12
|
|
|
|
|
99
|
$self->iptables("-t mangle -A PREROUTING -i $landev -s $src -m conntrack --ctstate NEW -m statistic --mode random --probability $probability -j $table"); |
2052
|
|
|
|
|
|
|
} |
2053
|
|
|
|
|
|
|
} |
2054
|
|
|
|
|
|
|
|
2055
|
|
|
|
|
|
|
else { |
2056
|
4
|
|
|
|
|
6
|
my $svc = $up[0]; |
2057
|
4
|
50
|
|
|
|
8
|
print STDERR "# forcing all traffic through $svc\n" if $self->verbose; |
2058
|
4
|
|
|
|
|
7
|
my $table = $self->mark_table($svc); |
2059
|
4
|
|
|
|
|
13
|
$self->iptables("-t mangle -A PREROUTING -i $landev -s $src -m conntrack --ctstate NEW -j $table"); |
2060
|
|
|
|
|
|
|
} |
2061
|
|
|
|
|
|
|
|
2062
|
8
|
|
|
|
|
23
|
$self->iptables("-t mangle -A PREROUTING -i $landev -s $src -m conntrack --ctstate ESTABLISHED,RELATED -j CONNMARK --restore-mark"); |
2063
|
|
|
|
|
|
|
} |
2064
|
|
|
|
|
|
|
|
2065
|
|
|
|
|
|
|
# inbound packets from WAN |
2066
|
2
|
|
|
|
|
17
|
for my $wan ($self->isp_services) { |
2067
|
6
|
|
|
|
|
11
|
my $dev = $self->vdev($wan); |
2068
|
6
|
|
|
|
|
14
|
my $table = $self->mark_table($wan); |
2069
|
6
|
|
|
|
|
18
|
my $src = $self->net($wan); |
2070
|
6
|
|
|
|
|
18
|
$self->iptables("-t mangle -A PREROUTING -i $dev -m conntrack --ctstate NEW -j $table"); |
2071
|
6
|
|
|
|
|
18
|
$self->iptables("-t mangle -A PREROUTING -i $dev -m conntrack --ctstate ESTABLISHED,RELATED -j CONNMARK --restore-mark"); |
2072
|
|
|
|
|
|
|
} |
2073
|
|
|
|
|
|
|
|
2074
|
|
|
|
|
|
|
} |
2075
|
|
|
|
|
|
|
|
2076
|
|
|
|
|
|
|
sub _weight_to_probability { |
2077
|
4
|
|
|
4
|
|
5
|
my $self = shift; |
2078
|
4
|
|
|
|
|
7
|
my $svcs = shift; |
2079
|
|
|
|
|
|
|
|
2080
|
|
|
|
|
|
|
# first turn weights into proportions of the total |
2081
|
4
|
|
|
|
|
10
|
my %weights = map {$_ => $self->weight($_)} @$svcs; |
|
12
|
|
|
|
|
18
|
|
2082
|
4
|
|
|
|
|
9
|
my $total = 0; |
2083
|
4
|
|
|
|
|
16
|
$total += $_ foreach (values %weights); |
2084
|
4
|
|
|
|
|
9
|
my %proportions = map {$_ => $weights{$_}/$total} keys %weights; |
|
12
|
|
|
|
|
29
|
|
2085
|
|
|
|
|
|
|
|
2086
|
|
|
|
|
|
|
# now turn them into probabilities |
2087
|
4
|
|
|
|
|
6
|
my %probabilities; |
2088
|
|
|
|
|
|
|
|
2089
|
4
|
|
|
|
|
7
|
my $last = 0; |
2090
|
4
|
0
|
|
|
|
29
|
for (sort {$proportions{$a}<=>$proportions{$b} || $a cmp $b} keys %proportions) { |
|
11
|
|
|
|
|
31
|
|
2091
|
12
|
|
|
|
|
25
|
my $threshold = $proportions{$_}/(1-$last); |
2092
|
12
|
|
|
|
|
18
|
$last += $proportions{$_}; |
2093
|
12
|
|
|
|
|
20
|
$probabilities{$_} = $threshold; |
2094
|
|
|
|
|
|
|
} |
2095
|
4
|
|
|
|
|
13
|
return \%probabilities; |
2096
|
|
|
|
|
|
|
} |
2097
|
|
|
|
|
|
|
|
2098
|
|
|
|
|
|
|
=head2 $bal->sanity_fw_rules() |
2099
|
|
|
|
|
|
|
|
2100
|
|
|
|
|
|
|
This is called by set_firewall() to create a sensible series of |
2101
|
|
|
|
|
|
|
firewall rules that seeks to prevent spoofing, flooding, and other |
2102
|
|
|
|
|
|
|
antisocial behavior. It also enables UDP-based network time and domain |
2103
|
|
|
|
|
|
|
name service. |
2104
|
|
|
|
|
|
|
|
2105
|
|
|
|
|
|
|
=cut |
2106
|
|
|
|
|
|
|
|
2107
|
|
|
|
|
|
|
sub sanity_fw_rules { |
2108
|
1
|
|
|
1
|
1
|
26
|
my $self = shift; |
2109
|
|
|
|
|
|
|
|
2110
|
|
|
|
|
|
|
# if any of the devices are ppp, then we clamp the mss |
2111
|
|
|
|
|
|
|
# Dunno why we need to add this to both the FORWARD and POSTROUTING rules, but |
2112
|
|
|
|
|
|
|
# googling recommends it. |
2113
|
1
|
|
|
|
|
3
|
my @ppp_devices = grep {/ppp\d+/} map {$self->vdev($_)} $self->isp_services; |
|
3
|
|
|
|
|
12
|
|
|
3
|
|
|
|
|
8
|
|
2114
|
1
|
50
|
|
|
|
7
|
$self->iptables("-A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu") if @ppp_devices > 0; |
2115
|
1
|
|
|
|
|
4
|
foreach (@ppp_devices) { |
2116
|
1
|
|
|
|
|
4
|
$self->iptables("-t mangle -A POSTROUTING -o $_ -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu"); |
2117
|
|
|
|
|
|
|
} |
2118
|
|
|
|
|
|
|
|
2119
|
|
|
|
|
|
|
|
2120
|
|
|
|
|
|
|
# lo is ok |
2121
|
1
|
|
|
|
|
5
|
$self->iptables(['-A INPUT -i lo -j ACCEPT', |
2122
|
|
|
|
|
|
|
'-A OUTPUT -o lo -j ACCEPT', |
2123
|
|
|
|
|
|
|
'-A INPUT -d 127.0.0.0/8 -j DROPPERM']); |
2124
|
|
|
|
|
|
|
|
2125
|
|
|
|
|
|
|
# accept continuing foreign traffic |
2126
|
1
|
|
|
|
|
5
|
$self->iptables(['-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT', |
2127
|
|
|
|
|
|
|
'-A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT', |
2128
|
|
|
|
|
|
|
'-A INPUT -p tcp --tcp-flags SYN,ACK ACK -j ACCEPT', |
2129
|
|
|
|
|
|
|
'-A FORWARD -p tcp --tcp-flags SYN,ACK ACK -j ACCEPT', |
2130
|
|
|
|
|
|
|
'-A FORWARD -p tcp --tcp-flags SYN,ACK,FIN,RST RST -j ACCEPT' |
2131
|
|
|
|
|
|
|
]); |
2132
|
|
|
|
|
|
|
|
2133
|
|
|
|
|
|
|
# we allow ICMP echo, but establish flood limits |
2134
|
1
|
|
|
|
|
4
|
$self->iptables(['-A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s -j ACCEPT', |
2135
|
|
|
|
|
|
|
'-A INPUT -p icmp --icmp-type echo-request -j DROPFLOOD']); |
2136
|
|
|
|
|
|
|
|
2137
|
|
|
|
|
|
|
# allowable traffic patterns within the LAN services |
2138
|
1
|
|
|
|
|
3
|
for my $lan ($self->lan_services) { |
2139
|
4
|
|
|
|
|
8
|
my $dev = $self->vdev($lan); |
2140
|
4
|
|
|
|
|
21
|
my $net = $self->net($lan); |
2141
|
|
|
|
|
|
|
|
2142
|
|
|
|
|
|
|
# allow unlimited traffic from internal network using legit address |
2143
|
4
|
|
|
|
|
14
|
$self->iptables("-A INPUT -i $dev -s $net -j ACCEPT"); |
2144
|
|
|
|
|
|
|
|
2145
|
|
|
|
|
|
|
# allow locally-generated output to the LAN on the LANDEV |
2146
|
4
|
|
|
|
|
17
|
$self->iptables("-A OUTPUT -o $dev -d $net -j ACCEPT"); |
2147
|
|
|
|
|
|
|
|
2148
|
|
|
|
|
|
|
# and allow broadcasts to the lan |
2149
|
4
|
|
|
|
|
14
|
$self->iptables("-A OUTPUT -o $dev -d 255.255.255.255/32 -j ACCEPT"); |
2150
|
|
|
|
|
|
|
|
2151
|
|
|
|
|
|
|
# any outgoing udp packet is fine with me |
2152
|
4
|
|
|
|
|
10
|
$self->iptables("-A OUTPUT -p udp -s $net -j ACCEPT"); |
2153
|
|
|
|
|
|
|
} |
2154
|
|
|
|
|
|
|
|
2155
|
|
|
|
|
|
|
# allow appropriate outgoing traffic via the ISPs |
2156
|
|
|
|
|
|
|
# NOTE: we use svc_config here so that we allow outgoing traffic |
2157
|
|
|
|
|
|
|
# on interfaces that might be down |
2158
|
|
|
|
|
|
|
# for my $svc ($self->isp_services) { |
2159
|
|
|
|
|
|
|
# my $ispdev = $self->dev($svc); |
2160
|
|
|
|
|
|
|
# $self->iptables("-A OUTPUT -o $ispdev -j ACCEPT"); |
2161
|
|
|
|
|
|
|
# } |
2162
|
1
|
|
|
|
|
3
|
for my $svc (keys %{$self->{svc_config}}) { |
|
1
|
|
|
|
|
5
|
|
2163
|
8
|
100
|
|
|
|
22
|
next unless $self->{svc_config}{$svc}{role} eq 'isp'; |
2164
|
4
|
|
|
|
|
7
|
my $ispdev = $self->{svc_config}{$svc}{dev}; |
2165
|
4
|
|
|
|
|
12
|
$self->iptables("-A OUTPUT -o $ispdev -j ACCEPT"); |
2166
|
|
|
|
|
|
|
} |
2167
|
|
|
|
|
|
|
|
2168
|
|
|
|
|
|
|
# forwarding rules |
2169
|
1
|
|
|
|
|
6
|
$self->_lan_wan_forwarding_rules(); |
2170
|
1
|
|
|
|
|
5
|
$self->_lan_lan_forwarding_rules(); |
2171
|
|
|
|
|
|
|
|
2172
|
|
|
|
|
|
|
# anything else is bizarre and should be dropped |
2173
|
1
|
|
|
|
|
2
|
$self->iptables('-A OUTPUT -j DROPSPOOF'); |
2174
|
|
|
|
|
|
|
} |
2175
|
|
|
|
|
|
|
|
2176
|
|
|
|
|
|
|
# establish expected traffic patterns between lan(s) and isp interfaces |
2177
|
|
|
|
|
|
|
sub _lan_wan_forwarding_rules { |
2178
|
1
|
|
|
1
|
|
3
|
my $self = shift; |
2179
|
|
|
|
|
|
|
|
2180
|
1
|
|
|
|
|
4
|
for my $lan ($self->lan_services) { |
2181
|
4
|
|
|
|
|
18
|
my $dev = $self->vdev($lan); |
2182
|
4
|
|
|
|
|
8
|
my $net = $self->net($lan); |
2183
|
|
|
|
|
|
|
|
2184
|
|
|
|
|
|
|
# lan/wan forwarding |
2185
|
|
|
|
|
|
|
# allow lan/wan forwarding |
2186
|
4
|
|
|
|
|
9
|
for my $svc ($self->isp_services) { |
2187
|
12
|
|
|
|
|
20
|
my $ispdev = $self->vdev($svc); |
2188
|
12
|
50
|
|
|
|
27
|
my $target = $self->_allow_forwarding($lan,$svc) ? 'ACCEPT' : 'REJECTPERM'; |
2189
|
12
|
|
|
|
|
69
|
$self->iptables("-A FORWARD -i $dev -o $ispdev -s $net -j $target"); |
2190
|
|
|
|
|
|
|
} |
2191
|
|
|
|
|
|
|
} |
2192
|
|
|
|
|
|
|
} |
2193
|
|
|
|
|
|
|
|
2194
|
|
|
|
|
|
|
# Allow forwarding between lans |
2195
|
|
|
|
|
|
|
sub _lan_lan_forwarding_rules { |
2196
|
1
|
|
|
1
|
|
2
|
my $self = shift; |
2197
|
|
|
|
|
|
|
|
2198
|
|
|
|
|
|
|
# This generates a very long list of rules if you have multiple lan services, but I think |
2199
|
|
|
|
|
|
|
# it is the most general way to get this right. |
2200
|
1
|
|
|
|
|
3
|
my @lans = $self->lan_services; |
2201
|
1
|
|
|
|
|
5
|
for (my $i=0;$i<@lans;$i++) { |
2202
|
4
|
|
|
|
|
11
|
for (my $j=0;$j<@lans;$j++) { |
2203
|
16
|
100
|
|
|
|
74
|
next if $i == $j; |
2204
|
12
|
|
|
|
|
17
|
my $lan1 = $lans[$i]; |
2205
|
12
|
|
|
|
|
18
|
my $lan2 = $lans[$j]; |
2206
|
12
|
50
|
|
|
|
20
|
my $target = $self->_allow_forwarding($lan1,$lan2) ? 'ACCEPT' : 'REJECTPERM'; |
2207
|
12
|
|
|
|
|
23
|
$self->iptables('-A FORWARD','-i',$self->vdev($lan1),'-o',$self->vdev($lan2),'-s',$self->net($lan1),'-d',$self->net($lan2),"-j $target"); |
2208
|
|
|
|
|
|
|
} |
2209
|
|
|
|
|
|
|
} |
2210
|
|
|
|
|
|
|
} |
2211
|
|
|
|
|
|
|
|
2212
|
|
|
|
|
|
|
sub _allow_forwarding { |
2213
|
24
|
|
|
24
|
|
33
|
my $self = shift; |
2214
|
24
|
|
|
|
|
41
|
my ($net_a,$net_b) = @_; |
2215
|
24
|
|
|
|
|
37
|
my $forward = $self->_forwarding_groups(); |
2216
|
24
|
|
|
|
|
61
|
my $key = join ',',sort ($net_a,$net_b); |
2217
|
24
|
|
|
|
|
60
|
return $forward->{$key}; |
2218
|
|
|
|
|
|
|
} |
2219
|
|
|
|
|
|
|
|
2220
|
|
|
|
|
|
|
# this returns a hashref of service pairs that are allowed to forward packets. |
2221
|
|
|
|
|
|
|
# the keys are service name pairs, in alphabetic order, separated by a comma. |
2222
|
|
|
|
|
|
|
sub _forwarding_groups { |
2223
|
24
|
|
|
24
|
|
28
|
my $self = shift; |
2224
|
|
|
|
|
|
|
|
2225
|
|
|
|
|
|
|
# _forwarding_groups is the processed and normalized version of forwarding_groups |
2226
|
24
|
100
|
|
|
|
53
|
return $self->{_forwarding_groups} if exists $self->{_forwarding_groups}; |
2227
|
|
|
|
|
|
|
|
2228
|
1
|
|
|
|
|
1
|
my %allowed_pairs; |
2229
|
1
|
|
|
|
|
3
|
my $fgs = $self->{forwarding_groups}; |
2230
|
1
|
50
|
|
|
|
3
|
unless (@$fgs) { |
2231
|
1
|
|
|
|
|
4
|
$fgs = [[':isp',':lan']]; |
2232
|
|
|
|
|
|
|
} |
2233
|
|
|
|
|
|
|
|
2234
|
1
|
|
|
|
|
2
|
for my $fg (@$fgs) { |
2235
|
|
|
|
|
|
|
my @services = map { |
2236
|
1
|
50
|
|
|
|
3
|
/^:isp$/ ? $self->isp_services |
|
2
|
100
|
|
|
|
13
|
|
2237
|
|
|
|
|
|
|
: /^:lan$/ ? $self->lan_services |
2238
|
|
|
|
|
|
|
: $_ |
2239
|
|
|
|
|
|
|
} @$fg; |
2240
|
|
|
|
|
|
|
|
2241
|
1
|
|
|
|
|
5
|
for (my $i=0;$i<@services-1;$i++) { |
2242
|
6
|
|
|
|
|
13
|
for (my $j=$i;$j<@services;$j++) { |
2243
|
27
|
|
|
|
|
54
|
my $key = join ',',sort ($services[$i],$services[$j]); |
2244
|
27
|
|
|
|
|
82
|
$allowed_pairs{$key}++; |
2245
|
|
|
|
|
|
|
} |
2246
|
|
|
|
|
|
|
} |
2247
|
|
|
|
|
|
|
} |
2248
|
1
|
|
|
|
|
6
|
return $self->{_forwarding_groups} = \%allowed_pairs; |
2249
|
|
|
|
|
|
|
} |
2250
|
|
|
|
|
|
|
|
2251
|
|
|
|
|
|
|
=head2 $bal->nat_fw_rules() |
2252
|
|
|
|
|
|
|
|
2253
|
|
|
|
|
|
|
This is called by set_firewall() to set up basic NAT rules for lan traffic over ISP |
2254
|
|
|
|
|
|
|
|
2255
|
|
|
|
|
|
|
=cut |
2256
|
|
|
|
|
|
|
|
2257
|
|
|
|
|
|
|
sub nat_fw_rules { |
2258
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2259
|
0
|
0
|
|
|
|
|
return unless $self->lan_services; |
2260
|
|
|
|
|
|
|
$self->iptables('-t nat -A POSTROUTING -o',$self->vdev($_),'-j MASQUERADE') |
2261
|
0
|
|
|
|
|
|
foreach $self->isp_services; |
2262
|
|
|
|
|
|
|
} |
2263
|
|
|
|
|
|
|
|
2264
|
|
|
|
|
|
|
=head2 $bal->start_lsm() |
2265
|
|
|
|
|
|
|
|
2266
|
|
|
|
|
|
|
Start an lsm process. |
2267
|
|
|
|
|
|
|
|
2268
|
|
|
|
|
|
|
=cut |
2269
|
|
|
|
|
|
|
|
2270
|
|
|
|
|
|
|
sub start_lsm { |
2271
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2272
|
0
|
|
|
|
|
|
my $lsm = Net::ISP::Balance::ConfigData->config('lsm_path'); |
2273
|
0
|
|
|
|
|
|
my $lsm_conf = $self->lsm_conf_file; |
2274
|
0
|
|
|
|
|
|
my $pid_path = $self->lsm_pid_path; |
2275
|
0
|
|
|
|
|
|
system "$lsm -c $lsm_conf -p $pid_path"; |
2276
|
0
|
|
|
|
|
|
chmod 0644,$pid_path; |
2277
|
|
|
|
|
|
|
} |
2278
|
|
|
|
|
|
|
|
2279
|
|
|
|
|
|
|
=head2 $bal->lsm_pid_path |
2280
|
|
|
|
|
|
|
|
2281
|
|
|
|
|
|
|
Return the path to the LSM pid file "/var/run/lsm.pid" |
2282
|
|
|
|
|
|
|
|
2283
|
|
|
|
|
|
|
=cut |
2284
|
|
|
|
|
|
|
|
2285
|
0
|
|
|
0
|
1
|
|
sub lsm_pid_path { return '/var/run/lsm.pid' } |
2286
|
|
|
|
|
|
|
|
2287
|
|
|
|
|
|
|
=head2 $bal->signal_lsm($signal) |
2288
|
|
|
|
|
|
|
|
2289
|
|
|
|
|
|
|
Send a signal to a running LSM and return true if successfully |
2290
|
|
|
|
|
|
|
signalled. The signal can be numeric (e.g. 9) or a string ('TERM'). |
2291
|
|
|
|
|
|
|
|
2292
|
|
|
|
|
|
|
=cut |
2293
|
|
|
|
|
|
|
|
2294
|
|
|
|
|
|
|
sub signal_lsm { |
2295
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2296
|
0
|
|
|
|
|
|
my $signal = shift; |
2297
|
0
|
|
0
|
|
|
|
$signal ||= 0; |
2298
|
0
|
|
|
|
|
|
my $pid; |
2299
|
0
|
0
|
|
|
|
|
open my $f,'/var/run/lsm.pid' or return; |
2300
|
0
|
|
|
|
|
|
chomp($pid = <$f>); |
2301
|
0
|
|
|
|
|
|
close $f; |
2302
|
0
|
0
|
|
|
|
|
return unless $pid =~ /^\d+$/; |
2303
|
0
|
|
|
|
|
|
return kill($signal=>$pid); |
2304
|
|
|
|
|
|
|
} |
2305
|
|
|
|
|
|
|
|
2306
|
|
|
|
|
|
|
|
2307
|
|
|
|
|
|
|
1; |
2308
|
|
|
|
|
|
|
|
2309
|
|
|
|
|
|
|
=head1 BUGS |
2310
|
|
|
|
|
|
|
|
2311
|
|
|
|
|
|
|
Please report bugs to GitHub: https://github.com/lstein/Net-ISP-Balance. |
2312
|
|
|
|
|
|
|
|
2313
|
|
|
|
|
|
|
=head1 AUTHOR |
2314
|
|
|
|
|
|
|
|
2315
|
|
|
|
|
|
|
Copyright 2014, Lincoln D. Stein (lincoln.stein@gmail.com) |
2316
|
|
|
|
|
|
|
|
2317
|
|
|
|
|
|
|
Senior Principal Investigator, |
2318
|
|
|
|
|
|
|
Ontario Institute for Cancer Research |
2319
|
|
|
|
|
|
|
|
2320
|
|
|
|
|
|
|
=head1 LICENSE |
2321
|
|
|
|
|
|
|
|
2322
|
|
|
|
|
|
|
This package is distributed under the terms of the Perl Artistic |
2323
|
|
|
|
|
|
|
License 2.0. See http://www.perlfoundation.org/artistic_license_2_0. |
2324
|
|
|
|
|
|
|
|
2325
|
|
|
|
|
|
|
=cut |
2326
|
|
|
|
|
|
|
|
2327
|
|
|
|
|
|
|
__END__ |