vifibnet.py 6.12 KB
Newer Older
Ulysse Beaugnon's avatar
Ulysse Beaugnon committed
1
#!/usr/bin/env python
2
import argparse, errno, os, select, subprocess, time
3
from argparse import ArgumentParser
Guillaume Bury's avatar
Guillaume Bury committed
4
import db, plib, upnpigd, utils, tunnel
5

6
class ArgParser(ArgumentParser):
7 8

    def convert_arg_line_to_args(self, arg_line):
9 10 11 12 13
        arg_line = arg_line.split('#')[0].rstrip()
        if arg_line:
            for arg in ('--' + arg_line.lstrip('--')).split():
                if arg.strip():
                    yield arg
14 15 16 17 18 19 20 21 22 23 24

def ovpnArgs(optional_args, ca_path, cert_path):
    # Treat openvpn arguments
    if optional_args[0] == "--":
        del optional_args[0]
    optional_args.append('--ca')
    optional_args.append(ca_path)
    optional_args.append('--cert')
    optional_args.append(cert_path)
    return optional_args

Guillaume Bury's avatar
Guillaume Bury committed
25
def getConfig():
26
    parser = ArgParser(fromfile_prefix_chars='@',
Guillaume Bury's avatar
Guillaume Bury committed
27 28
            description='Resilient virtual private network application')
    _ = parser.add_argument
29 30 31 32 33 34 35 36

    # General Configuration options
    _('--ip', default=None, dest='address', action='append', nargs=3,
            help='Ip address, port and protocol advertised to other vpn nodes')
    _('--internal-port', default=1194,
            help='Port on the machine to listen on for incomming connections')
    _('--peers-db-refresh', default=3600, type=int,
            help='the time (seconds) to wait before refreshing the peers db')
37
    _('-l', '--log', default='/var/log',
38 39 40 41 42 43 44 45 46
            help='Path to vifibnet logs directory')
    _('-s', '--state', default='/var/lib/vifibnet',
            help='Path to VPN state directory')
    _('--verbose', '-v', default=0, type=int,
            help='Defines the verbose level')
    #_('--babel-state', default='/var/lib/vifibnet/babel_state',
    #        help='Path to babeld state-file')
    #_('--db', default='/var/lib/vifibnet/peers.db',
    #        help='Path to peers database')
Guillaume Bury's avatar
Guillaume Bury committed
47
    _('--server', required=True,
48
            help="VPN address of the discovery peer server")
Guillaume Bury's avatar
Guillaume Bury committed
49
    _('--server-port', required=True, type=int,
50
            help="VPN port of the discovery peer server")
51 52 53 54 55 56 57 58 59 60 61

    # Routing algorithm options
    _('--hello', type=int, default=30,
            help='Hello interval for babel, in seconds')
    _('-w', '--wireless', action='store_true',
            help='''Set all interfaces to be treated as wireless interfaces
                    for the routing protocol''')

    # Tunnel options
    _('--proto', choices=['udp', 'tcp-server'], nargs='+', default=['udp'],
            help='Protocol(s) to be used by other peers to connect')
62
    _('--tunnel-refresh', default=300, type=int,
Guillaume Bury's avatar
Guillaume Bury committed
63 64 65 66 67 68 69
            help='the time (seconds) to wait before changing the connections')
    _('--dh', required=True,
            help='Path to dh file')
    _('--ca', required=True,
            help='Path to the certificate authority file')
    _('--cert', required=True,
            help='Path to the certificate file')
70
    # args to be removed ?
71 72
    _('--connection-count', default=20, type=int,
            help='Number of tunnels')
73
    _('--refresh-rate', default=0.05, type=float,
74 75
            help='''The ratio of connections to drop when refreshing the
                    connections''')
Guillaume Bury's avatar
Guillaume Bury committed
76 77 78 79
    # Openvpn options
    _('openvpn_args', nargs=argparse.REMAINDER,
            help="Common OpenVPN options (e.g. certificates)")
    return parser.parse_args()
Ulysse Beaugnon's avatar
Ulysse Beaugnon committed
80

81
def main():
82
    # Get arguments
Guillaume Bury's avatar
Guillaume Bury committed
83
    config = getConfig()
84
    manual = bool(config.address)
Guillaume Bury's avatar
Guillaume Bury committed
85
    network = utils.networkFromCa(config.ca)
86 87
    internal_ip, prefix = utils.ipFromCert(network, config.cert)
    openvpn_args = ovpnArgs(config.openvpn_args, config.ca, config.cert)
Guillaume Bury's avatar
Guillaume Bury committed
88

Guillaume Bury's avatar
Guillaume Bury committed
89 90 91
    # Set global variables
    tunnel.log = config.log
    utils.verbose = plib.verbose = config.verbose
Guillaume Bury's avatar
Guillaume Bury committed
92

Guillaume Bury's avatar
Guillaume Bury committed
93
    # Create and open read_only pipe to get server events
94
    utils.log('Creating pipe for server events', 3)
95 96 97
    r_pipe, write_pipe = os.pipe()
    read_pipe = os.fdopen(r_pipe)

Guillaume Bury's avatar
Guillaume Bury committed
98
    # Init db and tunnels
99 100 101
    if manual:
        utils.log('Manual external configuration', 3)
    else:
102
        utils.log('Attempting automatic configuration via UPnP', 4)
103
        try:
104 105
            ext_ip, ext_port = upnpigd.ForwardViaUPnP(config.internal_port, config.proto)
            config.address = list([ext_ip, str(ext_port), proto]
106
                                  for proto in config.proto)
107
        except Exception:
108
            utils.log('An atempt to forward a port via UPnP failed', 4)
109

110 111 112 113 114 115
    peer_db = db.PeerManager(config.state, config.server, config.server_port,
            config.peers_db_refresh, config.address, internal_ip, prefix,
            manual, config.proto, 200)
    tunnel_manager = tunnel.TunnelManager(write_pipe, peer_db, openvpn_args,
            config.hello, config.tunnel_refresh, config.connection_count,
            config.refresh_rate)
Guillaume Bury's avatar
Guillaume Bury committed
116

117
    # Launch routing protocol. WARNING : you have to be root to start babeld
Guillaume Bury's avatar
Guillaume Bury committed
118
    interface_list = ['vifibnet'] + list(tunnel_manager.free_interface_set)
119 120 121 122
    router = plib.router(network, internal_ip, interface_list, config.wireless,
            config.hello, os.path.join(config.state, 'vifibnet.babeld.state'),
            stdout=os.open(os.path.join(config.log, 'vifibnet.babeld.log'),
                os.O_WRONLY|os.O_CREAT|os.O_TRUNC), stderr=subprocess.STDOUT)
Guillaume Bury's avatar
Guillaume Bury committed
123

Ulysse Beaugnon's avatar
Ulysse Beaugnon committed
124
   # Establish connections
125
    server_process = list(plib.server(internal_ip, len(network) + len(prefix),
126 127
        config.connection_count, config.dh, write_pipe, config.internal_port,
        proto, config.hello, '--dev', 'vifibnet', *openvpn_args,
Guillaume Bury's avatar
Guillaume Bury committed
128 129
        stdout=os.open(os.path.join(config.log,
            'vifibnet.server.%s.log' % (proto,)),
130
            os.O_WRONLY | os.O_CREAT | os.O_TRUNC)) for proto in config.proto)
Guillaume Bury's avatar
Guillaume Bury committed
131
    tunnel_manager.refresh()
Guillaume Bury's avatar
Guillaume Bury committed
132

133 134 135
    # main loop
    try:
        while True:
136
            ready, tmp1, tmp2 = select.select([read_pipe], [], [],
137 138
                    max(0, min(tunnel_manager.next_refresh,
                               peer_db.next_refresh) - time.time()))
139
            if ready:
Guillaume Bury's avatar
Guillaume Bury committed
140
                peer_db.handle_message(read_pipe.readline())
141
            if time.time() >= peer_db.next_refresh:
142
                peer_db.refresh()
143
            if time.time() >= tunnel_manager.next_refresh:
Guillaume Bury's avatar
Guillaume Bury committed
144
                tunnel_manager.refresh()
145
    except KeyboardInterrupt:
146
        return 0
Guillaume Bury's avatar
wip  
Guillaume Bury committed
147 148 149

if __name__ == "__main__":
    main()
Guillaume Bury's avatar
Guillaume Bury committed
150