Statistics
| Branch: | Tag: | Revision:

root / snf-router / router.py @ 8c850fc2

History | View | Annotate | Download (7 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, pub_ip):
67
    i = 0
68
    for ip in IPy.IP(cidr):
69
        j = 1
70
        for sport in ports:
71
            rule = iptc.Rule()
72
            rule.dst = pub_ip
73
            rule.protocol = "tcp"
74
            match = iptc.Match(rule, "tcp")
75
            port = int(start_port) + j*int(step) + i
76
            match.dport = str(port)
77
            rule.add_match(match)
78
            target = iptc.Target(rule, "DNAT")
79
            rule.target = target
80
            rule.target.to_destination = "%s:%s" % (ip, sport)
81
            chain = iptc.Chain(iptc.Table(iptc.Table.NAT), "PREROUTING")
82
            chain.insert_rule(rule)
83
            j = j + 1
84

    
85
        i = i + 1
86

    
87

    
88
def create_snat(public_ip, private_net, public_iface):
89
    chain = iptc.Chain(iptc.Table(iptc.Table.NAT), "POSTROUTING")
90
    rule = iptc.Rule()
91
    private_net = "%s/%s" % (str(IPy.IP(private_net).net()), str(IPy.IP(private_net).netmask()))
92
    rule.src = private_net
93
    rule.dst = "!%s" % private_net
94
    rule.out_interface = public_iface
95
    target = iptc.Target(rule, "SNAT")
96
    target.to_source = public_ip
97
    rule.target = target
98
    chain.insert_rule(rule)
99

    
100

    
101
def create_states(private_net, public_iface, private_iface):
102
    chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), "FORWARD")
103
    rule = iptc.Rule()
104
    private_net = "%s/%s" % (str(IPy.IP(private_net).net()), str(IPy.IP(private_net).netmask()))
105
    rule.src = "!%s" % private_net
106
    rule.dst = private_net
107
    rule.out_interface = private_iface
108
    rule.in_interface = public_iface
109
    match = iptc.Match(rule, 'state')
110
    match.state = "ESTABLISHED,RELATED"
111
    rule.add_match(match)
112
    target = iptc.Target(rule, "ACCEPT")
113
    rule.target = target
114
    chain.insert_rule(rule)
115

    
116

    
117
def nic_info(interface):
118
    '''
119
    Returns the IP and ip type (PUBLIC/PRIVATE) of interface
120
    '''
121
    nic = dict()
122
    nic['ip'] = netifaces.ifaddresses(interface)[netifaces.AF_INET][0]['addr']
123
    nic['ip_type'] = IPy.IP(nic['ip']).iptype()
124
    return nic
125

    
126

    
127
def parse_config(filename):
128
    config = ConfigParser.ConfigParser()
129
    config.read(filename)
130
    return config
131

    
132

    
133
def create_remote_logger(host, url, method):
134
    logger = logging.getLogger('snf-router')
135
    logger.setLevel(logging.INFO)
136
    #http_handler = logging.handlers.HTTPHandler(host, url, method)
137
    http_handler = logging.handlers.WatchedFileHandler("test.log")
138
    formatter = logging.Formatter("%(asctime)s - %(message)s")
139
    http_handler.setFormatter(formatter)
140
    logger.addHandler(http_handler)
141
    return logger
142

    
143

    
144
def allow_ssh():
145
    chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), "INPUT")
146
    rule = iptc.Rule()
147
    rule.protocol = "tcp"
148
    match = iptc.Match(rule, "tcp")
149
    match.dport = "22"
150
    rule.add_match(match)
151
    target = iptc.Target(rule, "ACCEPT")
152
    rule.target = target
153
    chain.insert_rule(rule)
154
    
155
    chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), "OUTPUT")
156
    rule = iptc.Rule()
157
    rule.protocol = "tcp"
158
    match = iptc.Match(rule, "tcp")
159
    match.sport = "22"
160
    rule.add_match(match)
161
    target = iptc.Target(rule, "ACCEPT")
162
    rule.target = target
163
    chain.insert_rule(rule)
164

    
165
def set_policy(table, chain, policy):
166
    cur_table = iptc.Table(getattr(iptc.Table, table))
167
    cur_chain = iptc.Chain(cur_table, chain)
168
    cur_chain.set_policy(policy)
169

    
170

    
171
def get_public_iface():
172
    public_iface = ''
173
    for iface in netifaces.interfaces():
174
        nic = nic_info(iface)
175
        if nic['ip_type'] == 'PUBLIC':
176
            public_iface = iface
177
            break
178
    return public_iface
179

    
180

    
181
def main():
182
    #read config file
183
    config = parse_config(CONFIG_FILE)
184

    
185
    #create logger
186
    logger = create_remote_logger(config.get('log', 'host'),
187
                    config.get('log', 'url'),
188
                    config.get('log', 'method'))
189
    
190
    nic = nic_info(sys.argv[1])
191
    logger.info(nic['ip_type'] + " " + sys.argv[1] + " " + sys.argv[2])
192

    
193
    if sys.argv[2] == 'up' and nic['ip_type'] == "PUBLIC":
194
        #flush the old rules
195
        flush("NAT", "PREROUTING")
196
        flush("NAT", "POSTROUTING")
197
        flush("FILTER", "FORWARD")
198

    
199
        #set policy
200
        #set_policy("FILTER", "FORWARD", config.get('policy', 'FORWARD'))
201
        #set_policy("FILTER", "OUTPUT", config.get('policy', 'OUTPUT'))
202
        #set_policy("FILTER", "INPUT", config.get('policy', 'INPUT'))
203

    
204
        # create dnat
205
        create_dnat(config.get("private-network", "cidr"),
206
                    config.get("private-network", "ports").split(","),
207
                    config.get("private-network", "start_port"),
208
                    config.get("private-network", "step"),
209
                    nic["ip"])
210

    
211
        # create snat
212
        create_snat(nic['ip'], config.get("private-network","cidr"),
213
                sys.argv[1])
214

    
215
        create_states(config.get("private-network","cidr"), sys.argv[1],
216
             config.get("private-network","iface"))
217

    
218
if __name__ == "__main__":
219
    main()