]> git.llucax.com Git - software/pymin.git/blob - pymin/services/nat/__init__.py
a30f4c76dbabb2806781d10b7e67348cce5719f4
[software/pymin.git] / pymin / services / nat / __init__.py
1 # vim: set encoding=utf-8 et sw=4 sts=4 :
2
3 from os import path
4
5 from pymin.seqtools import Sequence
6 from pymin.dispatcher import Handler, handler, HandlerError
7 from pymin.services.util import Restorable, ConfigWriter, RestartHandler, \
8                                 ReloadHandler, TransactionalHandler, \
9                                 ServiceHandler, ListSubHandler, call
10
11 __ALL__ = ('NatHandler',)
12
13 class PortForward(Sequence):
14     r"""PortForward(dev, protocol, port, dst[, dst_port[, ...]]) -> PortForward.
15
16     dev - Netword device to use.
17     protocol - TCP or UDP.
18     port - Port to forward.
19     dst - Destination IP address.
20     dst_port - Destination port (at dst).
21     src_net - Source network to apply the forward (as IP/mask).
22     dst_net - Source network to apply the forward (as IP/mask).
23     """
24
25     def __init__(self, dev, protocol, port, dst, dst_port=None, src_net=None,
26                        dst_net=None):
27         r"Initialize object, see class documentation for details."
28         # TODO Validate
29         self.dev = dev
30         self.protocol = protocol
31         self.port = port
32         self.dst = dst
33         self.dst_port = dst_port
34         self.src_net = src_net
35         self.dst_net = dst_net
36
37     def update(self, dev=None, protocol=None, port=None, dst=None,
38                     dst_port=None, src_net=None, dst_net=None):
39         r"update([dev[, ...]]) -> Update the values of a port (see class doc)."
40         # TODO Validate
41         if dev is not None: self.dev = dev
42         if protocol is not None: self.protocol = protocol
43         if port is not None: self.port = port
44         if dst is not None: self.dst = dst
45         if dst_port is not None: self.dst_port = dst_port
46         if src_net is not None: self.src_net = src_net
47         if dst_net is not None: self.dst_net = dst_net
48
49     def as_tuple(self):
50         r"Return a tuple representing the port forward."
51         return (self.dev, self.protocol, self.port, self.dst, self.dst_port,
52                     self.src_net, self.dst_net)
53
54     def as_call_list(self, index=None):
55         if self.dst_port is not None:
56             self.dst = self.dst + ':' + self.dst_port
57         cmd = ['-t', 'nat', '-I', 'PREROUTING']
58         if index is not None:
59             cmd.append(str(index))
60         cmd.extend(('-i', self.dev, '-j', 'DNAT', '--to', self.dst,
61                 '-p', self.protocol, '--dport', self.port))
62         if self.src_net is not None:
63             cmd.extend(('-s', self.src_net))
64         if self.dst_net is not None:
65             cmd.extend(('-d', self.dst_net))
66         return cmd
67
68 class PortForwardHandler(ListSubHandler):
69     r"""PortForwardHandler(parent) -> PortForwardHandler instance.
70
71     This class is a helper for NatHandler to do all the work related to port
72     forwarding.
73
74     parent - The parent service handler.
75     """
76
77     handler_help = u"Manage NAT port forwarding."
78
79     _cont_subhandler_attr = 'ports'
80     _cont_subhandler_class = PortForward
81
82 class SNat(Sequence):
83     r"""SNat(dev, src[, src_net]) -> SNat instance.
84
85     dev - Netword device to use.
86     src - Source IP address.
87     src_net - Source network to apply the NAT (as IP/mask).
88     """
89
90     def __init__(self, dev, src, src_net=None):
91         r"Initialize object, see class documentation for details."
92         # TODO Validate
93         self.dev = dev
94         self.src = src
95         self.src_net = src_net
96
97     def update(self, dev=None, src=None, src_net=None):
98         r"update([dev[, ...]]) -> Update the values of a snat (see class doc)."
99         # TODO Validate
100         if dev is not None: self.dev = dev
101         if src is not None: self.src = src
102         if src_net is not None: self.src_net = src_net
103
104     def __cmp__(self, other):
105         r"Compares two SNat objects."
106         return cmp(self.as_tuple(), other.as_tuple())
107
108     def as_tuple(self):
109         r"Return a tuple representing the snat."
110         return (self.dev, self.src, self.src_net)
111
112     def as_call_list(self, index=None):
113         cmd = ['-t', 'nat', '-I', 'POSTROUTING']
114         if index is not None:
115             cmd.append(str(index))
116         cmd.extend(('-o', self.dev, '-j', 'SNAT', '--to', self.src))
117         if self.src_net is not None:
118             cmd.extend(('-s', self.src_net))
119         return cmd
120
121 class SNatHandler(ListSubHandler):
122     r"""SNatHandler(parent) -> SNatHandler instance.
123
124     This class is a helper for NatHandler to do all the work related to
125     Source NAT.
126
127     parent - The parent service handler.
128     """
129
130     handler_help = u"Manage source NAT."
131
132     _cont_subhandler_attr = 'snats'
133     _cont_subhandler_class = SNat
134
135 class Masq(Sequence):
136     r"""Masq(dev, src_net) -> Masq instance.
137
138     dev - Netword device to use.
139     src_net - Source network to apply the masquerade (as IP/mask).
140     """
141
142     def __init__(self, dev, src_net):
143         r"Initialize object, see class documentation for details."
144         # TODO Validate
145         self.dev = dev
146         self.src_net = src_net
147
148     def update(self, dev=None, src_net=None):
149         r"update([dev[, ...]]) -> Update the values of a masq (see class doc)."
150         # TODO Validate
151         if dev is not None: self.dev = dev
152         if src_net is not None: self.src_net = src_net
153
154     def __cmp__(self, other):
155         r"Compares two Masq objects."
156         return cmp(self.as_tuple(), other.as_tuple())
157
158     def as_tuple(self):
159         r"Return a tuple representing the masquerade."
160         return (self.dev, self.src_net)
161
162     def as_call_list(self, index=None):
163         cmd = ['-t', 'nat', '-I', 'POSTROUTING']
164         if index is not None:
165             cmd.append(str(index))
166         cmd.extend(('-o', self.dev, '-j', 'MASQUERADE', '-s', self.src_net))
167         return cmd
168
169 class MasqHandler(ListSubHandler):
170     r"""MasqHandler(parent) -> MasqHandler instance.
171
172     This class is a helper for NatHandler to do all the work related to
173     masquerading.
174
175     parent - The parent service handler.
176     """
177
178     handler_help = u"Manage NAT masquerading."
179
180     _cont_subhandler_attr = 'masqs'
181     _cont_subhandler_class = Masq
182
183 class NatHandler(Restorable, ConfigWriter, ReloadHandler, ServiceHandler,
184                         TransactionalHandler):
185     r"""NatHandler([pickle_dir[, config_dir]]) -> NatHandler instance.
186
187     Handles NAT commands using iptables.
188
189     pickle_dir - Directory where to write the persistent configuration data.
190
191     config_dir - Directory where to store de generated configuration files.
192
193     Both defaults to the current working directory.
194     """
195
196     handler_help = u"Manage NAT (Network Address Translation) service."
197
198     _persistent_attrs = ('ports', 'snats', 'masqs')
199
200     _restorable_defaults = dict(
201         ports=list(),
202         snats=list(),
203         masqs=list(),
204     )
205
206     def _service_start(self):
207         call(('iptables', '-t', 'nat', '-F'))
208         for (index, port) in enumerate(self.ports):
209             call(['iptables'] + port.as_call_list(index+1))
210         for (index, snat) in enumerate(self.snats):
211             call(['iptables'] + snat.as_call_list(index+1))
212         for (index, masq) in enumerate(self.masqs):
213             call(['iptables'] + masq.as_call_list(index+1))
214
215     def _service_stop(self):
216         call(('iptables', '-t', 'nat', '-F'))
217
218     _service_restart = _service_start
219
220     def __init__(self, pickle_dir='.'):
221         r"Initialize the object, see class documentation for details."
222         self._persistent_dir = pickle_dir
223         ServiceHandler.__init__(self)
224         self.forward = PortForwardHandler(self)
225         self.snat = SNatHandler(self)
226         self.masq = MasqHandler(self)
227
228
229 if __name__ == '__main__':
230
231     import os
232
233     handler = NatHandler()
234
235     def dump():
236         print '-' * 80
237         print 'Forwarded ports:'
238         print handler.forward.show()
239         print '-' * 10
240         print 'SNat:'
241         print handler.snat.show()
242         print '-' * 10
243         print 'Masq:'
244         print handler.masq.show()
245         print '-' * 80
246
247     dump()
248     handler.forward.add('eth0','tcp','80', '192.168.0.9', '8080')
249     handler.forward.update(0, dst_net='192.168.0.188/32')
250     handler.forward.add('eth0', 'udp', '53', '192.168.1.0')
251     handler.commit()
252     handler.stop()
253     dump()
254     handler.snat.add('eth0', '192.168.0.9')
255     handler.snat.update(0, src_net='192.168.0.188/32')
256     handler.snat.add('eth0', '192.168.1.0')
257     handler.commit()
258     dump()
259     dump()
260     handler.masq.add('eth0', '192.168.0.9/24')
261     handler.masq.update(0, src_net='192.168.0.188/30')
262     handler.masq.add('eth1', '192.168.1.0/24')
263     handler.commit()
264     dump()
265
266     os.system('rm -f *.pkl')
267