# encoding: utf-8 ''' Created on 2015年3月7日 @author: Sunday ''' import fcntl # @UnresolvedImport import socket import logging import struct logger = logging.getLogger('vpn') logger.addHandler(logging.StreamHandler()) logger.setLevel(logging.DEBUG) PYVPN_VERSION = '0.1' # find const values # grep IFF_UP -rl /usr/include/ IFF_UP = 0x1 IFF_RUNNING = 0x40 IFNAMSIZ = 16 SIOCSIFADDR = 0x8916 SIOCSIFNETMASK = 0x891c SIOCGIFFLAGS = 0x8913 SIOCSIFFLAGS = 0x8914 SIOCADDRT = 0x890B RTF_UP = 0x0001 RTF_GATEWAY = 0x0002 AF_INET = socket.AF_INET def to_int(s): try: return int(s) except ValueError as _unused: return None class exp_none(object): def __init__(self, fn): self.fn = fn def __call__(self, *args, **kwargs): try: return self.fn(*args, **kwargs) except Exception as e: logger.warn(e) return None def make_tun(): TUNSETIFF = 0x400454ca TUNSETOWNER = TUNSETIFF + 2 IFF_TUN = 0x0001 IFF_NO_PI = 0x1000 # Open TUN device file. tun = open('/dev/net/tun', 'r+b') # Tall it we want a TUN device named tun0. ifr = struct.pack('16sH', 'tun%d', IFF_TUN | IFF_NO_PI) ret = fcntl.ioctl(tun, TUNSETIFF, ifr) dev, _ = struct.unpack('16sH', ret) dev = dev.strip() # Optionally, we want it be accessed by the normal user. fcntl.ioctl(tun, TUNSETOWNER, 1000) return dev, tun @exp_none def ifconfig(dev, ipaddr, netmask): # http://stackoverflow.com/questions/6652384/how-to-set-the-ip-address-from-c-in-linux sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_IP) AF_INET = socket.AF_INET fd = sock.fileno() addrbuf = struct.pack('BBBB', *[int(el) for el in ipaddr.split('.')]) maskbuf = struct.pack('BBBB', *[int(el) for el in netmask.split('.')]) sockaddr_mt = '16sHH4s' flags_mt = '16sH' # ADDR siocsifaddr = struct.pack(sockaddr_mt, dev, AF_INET, 0, addrbuf) fcntl.ioctl(fd, SIOCSIFADDR, siocsifaddr) # MASK siocsifnetmask = struct.pack(sockaddr_mt, dev, AF_INET, 0, maskbuf) fcntl.ioctl(fd, SIOCSIFNETMASK, siocsifnetmask) # ifconfig tun0 up ifr2 = struct.pack(flags_mt, dev, 0) ifr_ret = fcntl.ioctl(fd, SIOCGIFFLAGS, ifr2) cur_flags = struct.unpack(flags_mt, ifr_ret)[1] flags = cur_flags | (IFF_UP | IFF_RUNNING) ifr_ret = struct.pack(flags_mt, dev, flags) ifr_ret = fcntl.ioctl(fd, SIOCSIFFLAGS, ifr_ret) return 0 @exp_none def add_route(dest, mask, gw): # sudo strace route add -net 192.168.0.0/24 gw 192.168.10.1 # ioctl(3, SIOCADDRT, ifr) # /usr/include/net/route.h pad = '\x00' * 8 inet_aton = socket.inet_aton sockaddr_in_fmt = 'hH4s8s' rtentry_fmt = 'L16s16s16sH38s' dst = struct.pack(sockaddr_in_fmt, AF_INET, 0, inet_aton(dest), pad) next_gw = struct.pack(sockaddr_in_fmt, AF_INET, 0, inet_aton(gw), pad) netmask = struct.pack(sockaddr_in_fmt, AF_INET, 0, inet_aton(mask), pad) rt_flags = RTF_UP | RTF_GATEWAY rtentry = struct.pack(rtentry_fmt, 0, dst, next_gw, netmask, rt_flags, '\x00' * 38) sock = socket.socket(AF_INET, socket.SOCK_DGRAM, 0) fcntl.ioctl(sock.fileno(), SIOCADDRT, rtentry) return 0 def enable_tcp_forward(): logger.info(u'Set ip_forward=1') with open('/proc/sys/net/ipv4/ip_forward', 'wb+') as f1: f1.seek(0) f1.write('1') def inet_ltoa(addr_long): ''' @summary: 转换 整数 到字符串的IP地址 :param addr_long: 整数地址,可以直接被ping的地址 ''' return socket.inet_ntoa(struct.pack('!I', addr_long)) def inet_atol(addr): ''' @summary: 转换字符串IP地址到整数地址 :param addr: like '192.168.2.121' ''' return struct.unpack('!I', socket.inet_aton(addr))[0] def is_valid_netmask(mask): ''' @summary: 校验是否为有效 掩码地址 :param mask: 字符串类型的掩码地址如 255.255.255.128 => True // 255.255.0.255 => False ''' all_mask = [0xffffffff ^ (0xffffffff >> i) for i in range(32)] return mask in [inet_ltoa(el) for el in all_mask] def is_valid_ip(ip): """Returns true if the given string is a well-formed IP address. Supports IPv4 and IPv6. //取自 tornado """ if not ip or '\x00' in ip: # getaddrinfo resolves empty strings to localhost, and truncates # on zero bytes. return False try: res = socket.getaddrinfo(ip, 0, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_NUMERICHOST) return bool(res) except socket.gaierror as e: if e.args[0] == socket.EAI_NONAME: return False raise return True def addr_netaddr(addr, netmask): ''' @summary: 获得某IP地址的网络地址,如 192.168.3.1, 255.255.255.0 => 192.168.3.0 :param addr: like '192.168.0.23' :param netmask: like '255.255.255.0' @return: 整形地址 ''' return inet_atol(addr) & inet_atol(netmask) def addr_boardcast(addr, netmask): ''' @summary: 获得某IP的广播地址,192.168.3.123, 255.255.255.0 => 192.168.3.255 //网络地址是该子网的最小地址,广播地址是该子网的最大地址,中间除却网关地址后剩余可自由分配的其他地址 :param addr: :param netmask: ''' return inet_atol(netmask) ^ 0xffffffff | inet_atol(addr) class PackageType(object): AUTH = 0 HEARTBEAT = 1 IFCONFIG = 2 DATA = 3 class User(object): def __init__(self): self.flow_count = 0 self.addr = '' gl_userlist = {} __all__ = ['to_int', 'exp_none', 'make_tun', 'ifconfig', 'add_route', 'enable_tcp_forward', 'PackageType', 'User', 'gl_userlist']