Statistics
| Branch: | Tag: | Revision:

root / snf-router / router.py @ 2cad0830

History | View | Annotate | Download (7.6 kB)

1
# Copyright 2011-2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34

    
35
"""
36
Script is called when an interface changes. For the time being we only support
37
one subnet and one public IP.
38

39
Logic:
40
Whenever this script is called, one of our nics changed, so flush everything.
41
If the nic is public create the rules for the private net given in the config
42
file
43
Else do nothing
44
"""
45

    
46
import iptc
47
import netifaces
48
import sys
49
import IPy
50
import ConfigParser
51
import logging
52
import logging.handlers
53

    
54
CONFIG_FILE = 'file.conf'
55

    
56

    
57
def flush(table_name, chain_name):
58
    """
59
    Flushes the chain chain_name of table table_name
60
    iptables -F chain_name -t table_name
61
    """
62
    table = iptc.Table(getattr(iptc.Table, table_name))
63
    table.flush_entries(chain_name)
64

    
65

    
66
def create_dnat(cidr, ports, start_port, step, public_ip):
67
    """
68
    Create the dnat rules
69
    iptables -t nat -A PREROUTING -p tcp -d public_ip --dport sport \
70
        -j DNAT --to-destination cidr:ports
71
    """
72
    i = 0
73
    for ip in IPy.IP(cidr):
74
        j = 0
75
        for sport in ports:
76
            rule = iptc.Rule()
77
            rule.dst = public_ip
78
            rule.protocol = "tcp"
79
            match = iptc.Match(rule, "tcp")
80
            port = int(start_port) + j*int(step) + i
81
            match.dport = str(port)
82
            rule.add_match(match)
83
            target = iptc.Target(rule, "DNAT")
84
            rule.target = target
85
            rule.target.to_destination = "%s:%s" % (ip, sport)
86
            chain = iptc.Chain(iptc.Table(iptc.Table.NAT), "PREROUTING")
87
            chain.insert_rule(rule)
88
            j = j + 1
89

    
90
        i = i + 1
91

    
92

    
93
def create_snat(public_ip, private_net, public_iface):
94
    """
95
    Creates the snat rules
96
    iptables -t nat -A POSTROUTING -o public_iface -s private_net \
97
        ! -d private_net -j SNAT --to-source public_ip
98
    """
99
    chain = iptc.Chain(iptc.Table(iptc.Table.NAT), "POSTROUTING")
100
    rule = iptc.Rule()
101
    private_net = "%s" % IPy.IP(private_net).strNormal(2)
102
    rule.src = private_net
103
    rule.dst = "!%s" % private_net
104
    rule.out_interface = public_iface
105
    target = iptc.Target(rule, "SNAT")
106
    target.to_source = public_ip
107
    rule.target = target
108
    chain.insert_rule(rule)
109

    
110

    
111
def create_states(private_net, public_iface, private_iface):
112
    """
113
    Creates rules for the established connections
114
    iptables -A FORWARD -i public_iface -o private_iface ! -s private_net \
115
        -d private_net -m state --state ESTABLISHED,RELATED
116
    """
117
    chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), "FORWARD")
118
    rule = iptc.Rule()
119
    private_net = "%s" % IPy.IP(private_net).strNormal(2)
120
    rule.src = "!%s" % private_net
121
    rule.dst = private_net
122
    rule.out_interface = private_iface
123
    rule.in_interface = public_iface
124
    match = iptc.Match(rule, 'state')
125
    match.state = "ESTABLISHED,RELATED"
126
    rule.add_match(match)
127
    target = iptc.Target(rule, "ACCEPT")
128
    rule.target = target
129
    chain.insert_rule(rule)
130

    
131

    
132
def nic_info(interface):
133
    """
134
    Returns the IP and ip type (PUBLIC/PRIVATE) of interface
135
    """
136
    nic = dict()
137
    nic['ip'] = netifaces.ifaddresses(interface)[netifaces.AF_INET][0]['addr']
138
    nic['ip_type'] = IPy.IP(nic['ip']).iptype()
139
    return nic
140

    
141

    
142
def parse_config(filename):
143
    config = ConfigParser.ConfigParser()
144
    config.read(filename)
145
    return config
146

    
147

    
148
def create_remote_logger(host, url, method):
149
    logger = logging.getLogger('snf-router')
150
    logger.setLevel(logging.INFO)
151
    #http_handler = logging.handlers.HTTPHandler(host, url, method)
152
    http_handler = logging.handlers.WatchedFileHandler("test.log")
153
    formatter = logging.Formatter("%(asctime)s - %(message)s")
154
    http_handler.setFormatter(formatter)
155
    logger.addHandler(http_handler)
156
    return logger
157

    
158

    
159
def allow_ssh():
160
    chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), "INPUT")
161
    rule = iptc.Rule()
162
    rule.protocol = "tcp"
163
    match = iptc.Match(rule, "tcp")
164
    match.dport = "22"
165
    rule.add_match(match)
166
    target = iptc.Target(rule, "ACCEPT")
167
    rule.target = target
168
    chain.insert_rule(rule)
169

    
170
    chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), "OUTPUT")
171
    rule = iptc.Rule()
172
    rule.protocol = "tcp"
173
    match = iptc.Match(rule, "tcp")
174
    match.sport = "22"
175
    rule.add_match(match)
176
    target = iptc.Target(rule, "ACCEPT")
177
    rule.target = target
178
    chain.insert_rule(rule)
179

    
180

    
181
def set_policy(table, chain, policy):
182
    cur_table = iptc.Table(getattr(iptc.Table, table))
183
    cur_chain = iptc.Chain(cur_table, chain)
184
    cur_chain.set_policy(policy)
185

    
186

    
187
def get_public_iface():
188
    public_iface = ''
189
    for iface in netifaces.interfaces():
190
        nic = nic_info(iface)
191
        if nic['ip_type'] == 'PUBLIC':
192
            public_iface = iface
193
            break
194
    return public_iface
195

    
196

    
197
def main():
198
    #read config file
199
    config = parse_config(CONFIG_FILE)
200

    
201
    start_port = config.get("private-network", "start_port")
202
    step = config.get("private-network", "step")
203
    private_cidr = config.get("private-network", "cidr")
204
    private_iface = config.get("private-network", "iface")
205
    nic = nic_info(interface_name)
206

    
207
    interface_name = sys.argv[1]
208
    action = sys.argv[2]
209

    
210
    #create logger
211
    logger = create_remote_logger(config.get('log', 'host'),
212
                                  config.get('log', 'url'),
213
                                  config.get('log', 'method'))
214

    
215
    #logger.info(nic['ip_type'] + " " + sys.argv[1] + " " + sys.argv[2])
216
    logger.info("%s %s %s" % (nic['ip_type'], action, interface_name))
217

    
218
    if action == 'up' and nic['ip_type'] == "PUBLIC":
219
        #flush the old rules
220
        flush("NAT", "PREROUTING")
221
        flush("NAT", "POSTROUTING")
222
        flush("FILTER", "FORWARD")
223

    
224
        #set policy
225
        #set_policy("FILTER", "FORWARD", config.get('policy', 'FORWARD'))
226
        #set_policy("FILTER", "OUTPUT", config.get('policy', 'OUTPUT'))
227
        #set_policy("FILTER", "INPUT", config.get('policy', 'INPUT'))
228

    
229
        # create dnat
230
        create_dnat(private_cidr,
231
                    config.get("private-network", "ports").split(","),
232
                    start_port, step, nic["ip"])
233

    
234
        # create snat
235
        create_snat(nic['ip'], private_cidr, interface_name)
236
        create_states(private_cidr, interface_name, private_iface)
237

    
238
if __name__ == "__main__":
239
    main()