summaryrefslogtreecommitdiff
path: root/scripts/firewall/vyatta-fw-global-state-policy.pl
blob: 859b810558f93ce415550865ae6e126abd072e31 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
#!/usr/bin/perl
#
# Module: vyatta-fw-global-state-policy.pl
#
# **** License ****
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# This code was originally developed by Vyatta, Inc.
# Portions created by Vyatta are Copyright (C) 2011 Vyatta, Inc.
# All Rights Reserved.
#
# Author: Mohit Mehta
# Date: November 2011
# Description: Script for setting/changing/removing global FW state-policy
#
# **** End License ****
#

use lib "/opt/vyatta/share/perl5";
use warnings;
use strict;
use Switch;
use Vyatta::Config;
use Vyatta::IpTables::Mgr;
use Getopt::Long;
use Sys::Syslog qw(:standard :macros);

# mapping from config node to iptables command
our %cmd_hash = ( 'name'      => '/sbin/iptables',
                  'ipv6-name' => '/sbin/ip6tables');

# mapping from config node to iptables/ip6tables table
our %table_hash = ( 'name'      => 'filter',
                    'ipv6-name' => 'filter');

# pre FW hooks in iptables' INPUT, OUTPUT and FORWARD chains
our %pre_fw_hooks_hash = ( 'INPUT'   => 'VYATTA_PRE_FW_IN_HOOK',
                           'FORWARD' => 'VYATTA_PRE_FW_FWD_HOOK',
                           'OUTPUT'  => 'VYATTA_PRE_FW_OUT_HOOK');

# post FW hooks in iptables' INPUT, OUTPUT and FORWARD chains
our %post_fw_hooks_hash = ( 'INPUT'   => 'VYATTA_POST_FW_IN_HOOK',
                            'FORWARD' => 'VYATTA_POST_FW_FWD_HOOK',
                            'OUTPUT'  => 'VYATTA_POST_FW_OUT_HOOK');

# state policy chains in iptables' INPUT, OUTPUT and FORWARD chains
our %state_policy_chains_hash = ( 'INPUT'   => 'VYATTA_STATE_POLICY_IN_HOOK',
                                  'FORWARD' => 'VYATTA_STATE_POLICY_FWD_HOOK',
                                  'OUTPUT'  => 'VYATTA_STATE_POLICY_OUT_HOOK');

# state actions
our %state_action_hash = ( 'drop'   => 'DROP',
                           'reject' => 'REJECT',
                           'accept' => 'JUMP_TO_INDIVIDUAL_POST_FW_HOOK',
			                     'log'    => 'LOG');

# state actions' log abbreviations
our %state_log_abbr_hash = ( 'drop'   => 'D',
                             'reject' => 'R',
                             'accept' => 'A');

# imp to maintain order of this array since this is the
# order we want to insert rules into state-policy chains
my @fw_states = ('invalid', 'established', 'related');

# log prefix - FW_STATE_POL-$STATE-$ACTION_ABBREVIATION
my $fw_log_prefix = 'FW-STATE_POL';

# this function performs the following functions:
# 1. sets up VYATTA_FW_*_STATE_POLICY chains i.e. for INPUT, OUTPUT, FORWARD hooks
# 2. adds rules in VYATTA_PRE_FW_*_HOOK hooks to jump to VYATTA_FW_*_STATE_POLICY
sub setup_state_policy {
  my ($cmd, $error);

  foreach my $tree (keys %cmd_hash) {
    foreach my $iptables_chain (keys %state_policy_chains_hash) {
      # create VYATTA_FW_*_STATE_POLICY chains
      $error = Vyatta::IpTables::Mgr::create_ipt_chain ($cmd_hash{$tree},
$table_hash{$tree}, $state_policy_chains_hash{$iptables_chain});
      return ($error, ) if $error;

      # append RETURN to VYATTA_FW_*_STATE_POLICY chains
      $error = Vyatta::IpTables::Mgr::append_ipt_rule ($cmd_hash{$tree},
$table_hash{$tree}, $state_policy_chains_hash{$iptables_chain}, 'RETURN');
      return ($error, ) if $error;

      # insert rule in VYATTA_PRE_FW_*_HOOK to jump to VYATTA_FW_*_STATE_POLICY
      $error = Vyatta::IpTables::Mgr::insert_ipt_rule ($cmd_hash{$tree},
$table_hash{$tree}, $pre_fw_hooks_hash{$iptables_chain}, 
$state_policy_chains_hash{$iptables_chain});
      return ($error, ) if $error;
    }
  }

  return;
}

# this function reverts the operations done in setup_state_policy():
# 1. removes rules from VYATTA_PRE_FW_*_HOOK hooks to jump to VYATTA_FW_*_STATE_POLICY
# 2. deletes VYATTA_FW_STATE_POLICY chains i.e. for IN, OUT, FWD hooks
sub teardown_state_policy {
  my ($cmd, $error);

  foreach my $tree (keys %cmd_hash) {
    foreach my $iptables_chain (keys %state_policy_chains_hash) {
      # remove rule in VYATTA_PRE_FW_*_HOOK to jump to VYATTA_FW_*_STATE_POLICY
      $error = Vyatta::IpTables::Mgr::delete_ipt_rule ($cmd_hash{$tree}, 
$table_hash{$tree}, $pre_fw_hooks_hash{$iptables_chain}, 
$state_policy_chains_hash{$iptables_chain});
      return ($error, ) if $error;

      # flush all rules from VYATTA_FW_*_STATE_POLICY chains
      $error = Vyatta::IpTables::Mgr::flush_ipt_chain($cmd_hash{$tree}, 
$table_hash{$tree}, $state_policy_chains_hash{$iptables_chain});
      return ($error, ) if $error;

      # delete VYATTA_FW_*_STATE_POLICY chains
      $error = Vyatta::IpTables::Mgr::delete_ipt_chain($cmd_hash{$tree},
$table_hash{$tree}, $state_policy_chains_hash{$iptables_chain});
      return ($error, ) if $error;
    }
  }

  return;
}

# set all state actions and their log rules
# Flush all previous rules and then set rules in the following order:
# INVALID - log rule followed by action rule
# ESTABLISHED - log rule followed by action rule
# RELATED - log rule followed by action rule
# Keep appending rules and then append RETURN rule at the end
sub set_state_actions {
  my ($cmd, $error);

  my $config = new Vyatta::Config;
  # skip steps below if state-policy deleted
  return if (!defined $config->exists("firewall state-policy"));

  # flush state_policy_chains
  foreach my $tree (keys %cmd_hash) {
    foreach my $iptables_chain (keys %state_policy_chains_hash) {
      # flush all rules from VYATTA_FW_*_STATE_POLICY chains
      $error = Vyatta::IpTables::Mgr::flush_ipt_chain($cmd_hash{$tree},
$table_hash{$tree}, $state_policy_chains_hash{$iptables_chain});
      return ($error, ) if $error;
    }
  }

  # check config for each states in this order: invalid, established, related
  # insert rules for log and action for each state
  foreach my $state (@fw_states) {
    $config->setLevel("firewall state-policy $state");
    my ($action, $log_enabled) = (undef, undef);
    $log_enabled = $config->exists("log enable");
    $action = $config->returnValue("action");
    my $uc_action = uc($action) if defined $action;
    my $uc_state = uc ($state) if defined $state;
    if (defined $log_enabled) {
      foreach my $tree (keys %cmd_hash) {
        foreach my $iptables_chain (keys %state_policy_chains_hash) {
          # insert rule in VYATTA_FW_*_STATE_POLICY
          my $jump_target = "LOG --log-prefix \"[$fw_log_prefix-$uc_state-$state_log_abbr_hash{$action}]\" ";
          $error = Vyatta::IpTables::Mgr::append_ipt_rule ($cmd_hash{$tree},
$table_hash{$tree}, $state_policy_chains_hash{$iptables_chain}, $jump_target, "-m state --state $uc_state");
          return ($error, ) if $error;
        }
      }
    }
    if (defined $action) {
      foreach my $tree (keys %cmd_hash) {
        foreach my $iptables_chain (keys %state_policy_chains_hash) {
          # if action is accept then jump target shold be post_fw_hooks post_fw_hooks_hash
          if ($action eq 'accept') {
            $error = Vyatta::IpTables::Mgr::append_ipt_rule ($cmd_hash{$tree},
$table_hash{$tree}, $state_policy_chains_hash{$iptables_chain}, 
$post_fw_hooks_hash{$iptables_chain}, "-m state --state $uc_state");
          } else {
            $error = Vyatta::IpTables::Mgr::append_ipt_rule ($cmd_hash{$tree},
$table_hash{$tree}, $state_policy_chains_hash{$iptables_chain}, 
$uc_action, "-m state --state $uc_state");
          }
          return ($error, ) if $error;
        }
      }
    }
  }

  # append rule with target RETURN at the end
  foreach my $tree (keys %cmd_hash) {
    foreach my $iptables_chain (keys %state_policy_chains_hash) {
      # append RETURN to VYATTA_FW_*_STATE_POLICY chains
      $error = Vyatta::IpTables::Mgr::append_ipt_rule ($cmd_hash{$tree},
$table_hash{$tree}, $state_policy_chains_hash{$iptables_chain}, 'RETURN');
      return ($error, ) if $error;
    }
  }

  return;
}

sub enable_disable_conntrack {
  my ($cmd, $error);

  my $conntrack_enabled = 'false';
  foreach my $state (@fw_states) {
    my $config = new Vyatta::Config;
    $config->setLevel("firewall state-policy $state");
    my ($action) = (undef);
    $action = $config->returnOrigValue("action");
    if (defined $action) {
      $conntrack_enabled = 'true';
      last;
    }
  }
  if ($conntrack_enabled eq 'true') {
    foreach my $tree (keys %cmd_hash) {
      Vyatta::IpTables::Mgr::ipt_disable_conntrack($cmd_hash{$tree}, 'FW_STATE_POLICY_CONNTRACK');
    }
  }

  my $enable_conntrack = 'false';
  foreach my $state (@fw_states) {
    my $config = new Vyatta::Config;
    $config->setLevel("firewall state-policy $state");
    my ($action) = (undef);
    $action = $config->returnValue("action");
    if (defined $action) {
      $enable_conntrack = 'true';
      last;
    }
  }
  if ($enable_conntrack eq 'true') {
    foreach my $tree (keys %cmd_hash) {
      Vyatta::IpTables::Mgr::ipt_enable_conntrack($cmd_hash{$tree}, 'FW_STATE_POLICY_CONNTRACK');
    }
  }

  return;
}

sub state_policy_validity_checks {
  my ($cmd, $error);

  foreach my $state (@fw_states) {
    my $config = new Vyatta::Config;
    $config->setLevel("firewall state-policy $state");
    my ($action, $log_enabled) = (undef, undef);
    $log_enabled = $config->exists("log enable");
    $action = $config->returnValue("action");
    if (defined $log_enabled && !defined $action) {
      $error = "log enabled but action not configured for state: $state\n" . 
"action is required to log packets\n";
      return $error;
    }
  }

  return;
}

#
# main
#

my ($action, $state, $state_action);

GetOptions("action=s"       => \$action,
	         "state=s"        => \$state,
           "state-action=s" => \$state_action,
);

die "undefined action" if ! defined $action;

my ($error, $warning);

($error, $warning) = setup_state_policy()                 if $action eq 'setup-state-policy';

($error, $warning) = teardown_state_policy()              if $action eq 'teardown-state-policy';

($error, $warning) = set_state_actions()                  if $action eq 'set-state-actions';

($error, $warning) = enable_disable_conntrack($state)     if $action eq 'enable-disable-conntrack';

($error, $warning) = state_policy_validity_checks($state) if $action eq 'state-policy-validity-checks';

if (defined $warning) {
    print "$warning\n";
}

if (defined $error) {
    print "$error\n";
    exit 1;
}

exit 0;

# end of file