Openvpn server port and proto in one option
[re6stnet.git] / vifibnet.py
1 #!/usr/bin/env python
2 import argparse, errno, os, select, subprocess, time
3 from argparse import ArgumentParser
4 import db, plib, upnpigd, utils, tunnel
5
6
7 class ArgParser(ArgumentParser):
8
9 def convert_arg_line_to_args(self, arg_line):
10 arg_line = arg_line.split('#')[0].rstrip()
11 if arg_line:
12 for arg in ('--' + arg_line.lstrip('--')).split():
13 if arg.strip():
14 yield arg
15
16
17 def ovpnArgs(optional_args, ca_path, cert_path):
18 # Treat openvpn arguments
19 if optional_args[0] == "--":
20 del optional_args[0]
21 optional_args.append('--ca')
22 optional_args.append(ca_path)
23 optional_args.append('--cert')
24 optional_args.append(cert_path)
25 return optional_args
26
27
28 def getConfig():
29 parser = ArgParser(fromfile_prefix_chars='@',
30 description='Resilient virtual private network application')
31 _ = parser.add_argument
32
33 # General Configuration options
34 _('--ip', default=None, dest='address', action='append', nargs=3,
35 help='Ip address, port and protocol advertised to other vpn nodes')
36 _('--peers-db-refresh', default=3600, type=int,
37 help='the time (seconds) to wait before refreshing the peers db')
38 _('-l', '--log', default='/var/log',
39 help='Path to vifibnet logs directory')
40 _('-s', '--state', default='/var/lib/vifibnet',
41 help='Path to VPN state directory')
42 _('--verbose', '-v', default=0, type=int,
43 help='Defines the verbose level')
44 #_('--babel-state', default='/var/lib/vifibnet/babel_state',
45 # help='Path to babeld state-file')
46 #_('--db', default='/var/lib/vifibnet/peers.db',
47 # help='Path to peers database')
48 _('--server', required=True,
49 help="VPN address of the discovery peer server")
50 _('--server-port', required=True, type=int,
51 help="VPN port of the discovery peer server")
52
53 # Routing algorithm options
54 _('--hello', type=int, default=30,
55 help='Hello interval for babel, in seconds')
56 _('-w', '--wireless', action='store_true',
57 help='''Set all interfaces to be treated as wireless interfaces
58 for the routing protocol''')
59
60 # Tunnel options
61 _('--pp', nargs=2, action='append',
62 help='Port and protocol to be used by other peers to connect')
63 _('--tunnel-refresh', default=300, type=int,
64 help='the time (seconds) to wait before changing the connections')
65 _('--dh', required=True,
66 help='Path to dh file')
67 _('--ca', required=True,
68 help='Path to the certificate authority file')
69 _('--cert', required=True,
70 help='Path to the certificate file')
71 # args to be removed ?
72 _('--connection-count', default=20, type=int,
73 help='Number of tunnels')
74 _('--refresh-rate', default=0.05, type=float,
75 help='''The ratio of connections to drop when refreshing the
76 connections''')
77 # Openvpn options
78 _('openvpn_args', nargs=argparse.REMAINDER,
79 help="Common OpenVPN options (e.g. certificates)")
80 return parser.parse_args()
81
82
83 def main():
84 # Get arguments
85 config = getConfig()
86 if not config.pp:
87 config.pp = [['1194', 'udp']]
88 manual = bool(config.address)
89 network = utils.networkFromCa(config.ca)
90 internal_ip, prefix = utils.ipFromCert(network, config.cert)
91 openvpn_args = ovpnArgs(config.openvpn_args, config.ca, config.cert)
92
93 # Set global variables
94 tunnel.log = config.log
95 utils.verbose = plib.verbose = config.verbose
96
97 # Create and open read_only pipe to get server events
98 utils.log('Creating pipe for server events', 3)
99 r_pipe, write_pipe = os.pipe()
100 read_pipe = os.fdopen(r_pipe)
101
102 # Init db and tunnels
103 if manual:
104 utils.log('Manual external configuration', 3)
105 forward = None
106 else:
107 utils.log('Attempting automatic configuration via UPnP', 4)
108 try:
109 forward = list([upnpigd.UpnpForward(int(port), proto), proto]
110 for port, proto in config.pp)
111 config.address = list([ext.external_ip, str(ext.external_port),
112 proto] for ext, proto in forward)
113 except Exception:
114 forward = None
115 utils.log('An atempt to forward a port via UPnP failed', 4)
116
117 peer_db = db.PeerManager(config.state, config.server, config.server_port,
118 config.peers_db_refresh, config.address, internal_ip, prefix,
119 manual, config.pp, 200)
120 tunnel_manager = tunnel.TunnelManager(write_pipe, peer_db, openvpn_args,
121 config.hello, config.tunnel_refresh, config.connection_count,
122 config.refresh_rate)
123
124 # Launch routing protocol. WARNING : you have to be root to start babeld
125 interface_list = ['vifibnet'] + list(tunnel_manager.free_interface_set)
126 router = plib.router(network, internal_ip, interface_list, config.wireless,
127 config.hello, os.path.join(config.state, 'vifibnet.babeld.state'),
128 stdout=os.open(os.path.join(config.log, 'vifibnet.babeld.log'),
129 os.O_WRONLY | os.O_CREAT | os.O_TRUNC), stderr=subprocess.STDOUT)
130
131 # Establish connections
132 server_process = list(plib.server(internal_ip, len(network) + len(prefix),
133 config.connection_count, config.dh, write_pipe, port,
134 proto, config.hello, '--dev', 'vifibnet', *openvpn_args,
135 stdout=os.open(os.path.join(config.log,
136 'vifibnet.server.%s.log' % (proto,)),
137 os.O_WRONLY | os.O_CREAT | os.O_TRUNC))
138 for port, proto in config.pp)
139 tunnel_manager.refresh()
140
141 # main loop
142 try:
143 while True:
144 nextUpdate = min(tunnel_manager.next_refresh, peer_db.next_refresh)
145 if forward != None:
146 nextUpdate = min(nextUpdate, forward.next_refresh)
147 nextUpdate = max(0, nextUpdate - time.time())
148
149 ready, tmp1, tmp2 = select.select([read_pipe], [], [], nextUpdate)
150 if ready:
151 peer_db.handle_message(read_pipe.readline())
152 if time.time() >= peer_db.next_refresh:
153 peer_db.refresh()
154 if time.time() >= tunnel_manager.next_refresh:
155 tunnel_manager.refresh()
156 except KeyboardInterrupt:
157 return 0
158
159 if __name__ == "__main__":
160 main()