class TapLoop: ''' This class creates a pair of interconnected TAP interfaces. If address and netmask are provided, they get assigned to the "remote" interface. The users are supposed to call get_iface() method to obtain the name of the "local" interface. Then the users are expected to use raw sockets to send/receive traffic on that local interface, possibly to interact with the remote end. ''' def __init__(self, local_address = None, remote_address = None, netmask = None): self.t1 = TapDevice(mode = IFF_TAP) self.t2 = TapDevice(mode = IFF_TAP) if local_address != None and netmask != None: self.t1.ifconfig(address = local_address, netmask = netmask) if remote_address != None and netmask != None: self.t2.ifconfig(address = remote_address, netmask = netmask) self.t1.up() self.t2.up() self.pump1 = Pump(self.t1, self.t2) self.pump2 = Pump(self.t2, self.t1) logger.debug("Allocated TAP interfaces %s/%s" % (self.t1.name, self.t2.name)) def start(self): self.pump1.start() self.pump2.start() logger.debug("Started pump threads") def get_iface(self): return self.t1.name
class LoadbalancerControl(asyncore.file_dispatcher): shutdown = False # Loadbalancers shouldn't reconnect rr = 0 # RoundRobin iterator counter def __init__(self, options={}): self.secret = options.get('secret','') self.port = options.get('port',11111) self.buffer = [] self.loadbalancer_pool = [] _tap = options.get('tap', True) _name = options.get('name', '') self.dev = TapDevice(tap=_tap, name=_name) asyncore.file_dispatcher.__init__(self, self.dev.getFD()) self.dev.up() if options.get('tunnel_address', False): self.dev.ifconfig(address=options.get('tunnel_address')) logging.debug('Interface ready') def eth_addr (self, a) : b = "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x" % (ord(a[0]) , ord(a[1]) , ord(a[2]), ord(a[3]), ord(a[4]) , ord(a[5])) return b def validate(self, data): eth_length = 14 eth_header = data[:eth_length] eth = struct.unpack('!6s6sH' , eth_header) eth_protocol = socket.ntohs(eth[2]) # print 'Destination MAC : ' + self.eth_addr(data[0:6]) + ' Source MAC : ' + self.eth_addr(data[6:12]) + ' Protocol : ' + str(eth_protocol) ip_header = data[eth_length:20+eth_length] iph = struct.unpack('!BBHHHBBH4s4s' , ip_header) version_ihl = iph[0] ihl = version_ihl & 0xF iph_length = ihl * 4 protocol = iph[6] s_addr = socket.inet_ntoa(iph[8]) d_addr = socket.inet_ntoa(iph[9]) # udp_header = data[iph_length+eth_length:iph_length+20+eth_length] # udph = struct.unpack('!HHLLBBHHH' , udp_header) # print "[{}] - {}:{} -> {}:{}".format(protocol, s_addr,udph[0], d_addr,udph[1]) def writable(self): return len(self.buffer) > 0 def handle_write(self): """ Write local buffer to interface """ try: packet = self.buffer.pop() logging.debug('Writing {}byte packet to device'.format(len(packet))) self.dev.write(packet) except IndexError: pass except OSError as e: if e.errno == 22: # Invalid packet logging.debug('Failed to write invalid packet "{}"'.format(packet)) else: logging.exception('Writing "{}" to device caused exception: {}'.format(packet, e.strerror)) except Exception as e: logging.exception('Writing "{}" to device caused exception: {}'.format(packet, e.strerror)) def write(self, packet): """ Forward data from loadbalancers to local buffer """ if packet: # self.validate(packet) self.buffer.append(packet) def handle_read_event(self): """ Forward received packets to loadbalancers """ packet = self.dev.read() self.balance_data_round_robin(packet) def balance_data_round_robin(self, packet): """ Use Round Robin to distribute each packet via revolving loadbalancers """ if len(self.loadbalancer_pool) == 0: return # logging.debug('Sending {}byte packet via {}'.format(len(packet), repr(self.loadbalancer_pool[self.rr]))) self.loadbalancer_pool[self.rr].send(packet) self.rr = self.rr+1 if self.rr < len(self.loadbalancer_pool)-1 else 0 def quit(self): """ Gracefully close all loadbalancers and self """ self.shutdown = True logging.debug('Closing loadbalancers...') # Loadbalancer modifies the pool in close, so make a static list for s in list(self.loadbalancer_pool): s.handle_close() if self.loadbalancer_pool: logging.debug('Waiting for loadbalancers to close...') for i in range(5): if len(self.loadbalancer_pool) == 0: break time.sleep(1) else: for s in self.loadbalancer_pool: s.close() del s logging.debug('Closing self...') self.dev.close()