Source code for ipktgen.gen_netutils

'''utils for pkt creation and decoding'''

from math import ceil
from struct import pack
from socket import inet_aton, inet_pton, inet_ntoa, inet_ntop, AF_INET6, error as err
from sys import platform
import logging
import netifaces
from subprocess import Popen, PIPE
import re
import dpkt
import time
import json
import netifaces


logger = logging.getLogger(__name__)


[docs]def fake_sleep(delay_secs): '''fake sleep that stalls a process for delaySecs while using 100% cpu''' logger = logging.getLogger('genmodel.netutils.fake_sleep') now = time.time() while now + delay_secs > time.time(): pass
[docs]def fake_sleep_until(time_x): '''fake sleep that stalls a process for delaySecs while using 100% cpu''' logger = logging.getLogger('genmodel.netutils.fake_sleep_until') while time.time() < time_x: pass
[docs]def create_ether_hdr(dst_mac, src_mac, e_type, vlan_tag=None): '''creates an ethernet packet using given parameters etype can be a number<1500, or ip, or arp''' logger = logging.getLogger('genmodel.netutils.create_ether_hdr') smac = mac_str_2_hex(src_mac) dmac = mac_str_2_hex(dst_mac) etype = get_ether_type(e_type) if vlan_tag is None: #create normal packet eth_format = '!6s6sH' eth_header = pack(eth_format, dmac, smac, etype) else: vtag = hex(vlan_tag).zfill(8) eth_format = '!6s6siH' eth_header = pack(eth_format, dmac, smac, vtag, etype) return eth_header
def create_ipv4_hdr(src, dest, proto, dscp=0, ecn=0, idf=0, fl_resvd=0, fl_df=0, dl_mf=0, frag_off=0, ttl=64, opts="", payload=None): logger = logging.getLogger('genmodel.netutils.create_ipv4_hdr') '''creates an IP packet from supplied components''' print("creating ip pkt") #TODO validate all the arguments ipv4_format = '!BBHHHBBH4s4s' hdr_len = 5 + ceil(float(len(opts.encode('hex')))/4) print(('len opts = \'', len(opts), '\'')) total_len = (hdr_len * 4) + len(payload) print(('total_len', total_len)) hdr_chksm = 0 ver = 4 ver_ihl = (ver << 4) + hdr_len dcsp_ecn = (dscp << 2) + ecn flags_frag_off = ((fl_resvd << 15) + (fl_df << 15 >> 1) + (dl_mf << 15 >> 2) + (frag_off << 3 >> 3)) temp_hdr = pack(ipv4_format, ver_ihl, dcsp_ecn, total_len, idf, flags_frag_off, ttl, proto, hdr_chksm, src, dest) hdr_chksm = calc_ipv4_checksum(temp_hdr) src = inet_aton(src) dest = inet_aton(dest) ipv4_hdr = pack(ipv4_format, ver_ihl, dcsp_ecn, total_len, idf, flags_frag_off, ttl, proto, hdr_chksm, src, dest) return ipv4_hdr
[docs]def create_udp_hdr(src_port, dst_port, l3_proto='ipv4', payload=None): '''creates an IP packet from supplied components''' logger = logging.getLogger('genmodel.netutils.create_udp_hdr') #TODO validate all the arguments udp_format = '!HHHH' udp_len = 8 + len(payload) if l3_proto == 'ipv4': checksum = 0 #TODO: change to real checksum UDP calculation elif l3_proto == 'ipv6': checksum = 0 #TODO: do UDP ipv6 checksum calculation udp_hdr = pack(udp_format, src_port, dst_port, udp_len, checksum) return udp_hdr
[docs]def dpktMacAddr2Str(addr): """Convert MAC address to normal string """ logger = logging.getLogger('genmodel.netutils.getMacAddr') return ':'.join('%02x' % dpkt.compat.compat_ord(b) for b in addr)
[docs]def mac_str_2_hex(mac_addr): '''removes the colums in a mac address''' logger = logging.getLogger('genmodel.netutils.mac_str_2_hex') mac_hex = "" for hex2bytes in mac_addr.split(':'): if len(hex2bytes) == 1: mac_hex = mac_hex + '0' mac_hex = mac_hex + hex2bytes return mac_hex.decode('hex')
[docs]def mac_hex_2_str(mac_addr): '''removes the colums in a mac address of format aa:11:bb:22:cc:33''' #pass logger = logging.getLogger('genmodel.netutils.mac_hex_2_str') striped_mac = mac_addr.encode('hex') ':'.join([striped_mac[i:i+2] for i in range(0, len(striped_mac), 2)])
[docs]def get_ether_type(eth_val): '''returns the string eth type''' logger = logging.getLogger('genmodel.netutils.get_ether_type') if isinstance(eth_val, int): etype = hex(eth_val).split('x')[-1].zfill(4) elif eth_val.lower() == 'ip': etype = '0800' elif eth_val.lower() == 'arp': etype = '0806' return etype
[docs]def is_valid_mac_addr(mac_addr): '''return True if mac_addr is a valid mac address''' logger = logging.getLogger('genmodel.netutils.is_valid_mac_addr') return re.match("[0-9a-f]{2}([-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", mac_addr.lower())
[docs]def modify_ipv4_hdr(pkt, change): '''modify a packet with given info''' logger = logging.getLogger('genmodel.netutils.__init__') #TODO: use dpkt to process the packet components, and re-pack pass
[docs]def ipv4_string_2_hex(ip_addr_string): '''converts IP addrs in a.b.c.d format to hex''' logger = logging.getLogger('genmodel.netutils.ipv4_string_2_hex') ip_hex = '' for dec in ip_addr_string.split('.'): hex_v = hex(int(dec)) if len(hex_v) == 1: ip_hex = ip_hex + '0' ip_hex = ip_hex + hex_v return ip_hex
[docs]def ipv4_hex_2_str(ip_addr_hex): '''converts IP addrs in a.b.c.d format to hex''' logger = logging.getLogger('genmodel.netutils.ipv4_hex_2_str') return inet_ntoa(ip_addr_hex)
[docs]def ipv4_cidr_to_netmask(cidr): '''converts IP in CIDR format to tuple (ip,netmask)''' logger = logging.getLogger('genmodel.netutils.ipv4_cidr_to_netmask') network, net_bits = cidr.split('/') host_bits = 32 - int(net_bits) netmask = inet_ntoa(pack('!I', (1 << 32) - (1 << host_bits))) return (network, netmask)
[docs]def calc_ipv4_checksum(ip_hdr): '''calculates the checksum of an IP header''' logger = logging.getLogger('genmodel.netutils.calc_ipv4_checksum') ''' print "ip hdr = ", ip_hdr #ip_hdr = ':'.join(x.encode('hex') for x in 'Hello World!') ip_hdr = ip_hdr.encode('hex') if len(ip_hdr) % 2 == 1: ip_hdr += b"\0" print "ip hdr = ", ip_hdr print [ip_hdr[i:i+2] for i in range(0, len(ip_hdr), 2)] sum_x = sum([ip_hdr[i:i+2] for i in range(0, len(ip_hdr), 2)]) sum_x = (sum_x >> 16) + (sum_x & 0xffff) sum_x += sum_x >> 16 sum_x = ~sum_x if pack("H", 1) == b"\x00\x01": # big endian return sum_x & 0xffff else: return (((sum_x>>8)&0xff)|sum_x<<8) & 0xffff ''' s = 0 for i in range(0, len(ip_hdr), 2): w = ord(ip_hdr[i]) + (ord(ip_hdr[i+1]) << 8) c = s + w s = (c & 0xffff) + (c >> 16) return ~s & 0xffff
def is_valid_ipv4(ip_addr): logger = logging.getLogger('genmodel.netutils.is_valid_ipv4') '''return True if ip_addr is a valid ipv4''' try: inet_aton(ip_addr) return True except err: return False def create_ipv6_hdr(): logger = logging.getLogger('genmodel.netutils.create_ipv6_hdr') '''creates an IP packet from supplied components''' #TODO validate all the arguments pass def modify_ipv6_hdr(pkt, change): logger = logging.getLogger('genmodel.netutils.modify_ipv6_hdr') '''modify a packet with given info''' #TODO: use dpkt to process the packet components, and re-pack pass def is_valid_ipv6(ip_addr): logger = logging.getLogger('genmodel.netutils.is_valid_ipv6') '''return True if ip_addr is a valid ipv6''' try: inet_pton(AF_INET6, ip_addr) return True except err: return False def ipv6_hex_2_str(ip_addr_hex): logger = logging.getLogger('genmodel.netutils.ipv6_hex_2_str') '''converts IP addrs in a.b.c.d format to hex''' return inet_ntop(AF_INET6, ip_addr_hex) def get_ips_masks(iface): logger = logging.getLogger('genmodel.netutils.get_ips_masks') '''returns a list of tuples [(ip_addresses, netmask)] of an interface''' #assert is_valid_iface(iface), 'the interface: ' + iface + 'is not valid' ips = [] if platform.startswith('freebsd') or platform == 'darwin': #intf = os.popen("route get "+dest_ip).read().partition('interface: ')[2].split('\n')[0] cmd = ['ifconfig', iface] pipex = Popen(cmd, stdout=PIPE, stderr=PIPE) stdout_data, stderr_data = pipex.communicate() if pipex.returncode == 0: list_inet = stdout_data.split('inet ')[1:] for line in list_inet: listx = line.split() ips.append((listx[0], listx[3])) else: raise RuntimeError('%r failed, status code %s stdout %r stderr %r' % (cmd, pipex.returncode, stdout_data, stderr_data)) elif platform.startswith('linux') or platform == 'cygwin': #intf = os.popen("ip route get "+dest_ip).read().partition(' dev ')[2].split(' ')[0] cmd = ['ip', 'addr', 'show', iface] pipex = Popen(cmd, stdout=PIPE, stderr=PIPE) stdout_data, stderr_data = pipex.communicate() if pipex.returncode == 0: list_inet = stdout_data.split('inet ')[1:] for line in list_inet: ip_cidr = line.split()[0] ips.append(ipv4_cidr_to_netmask(ip_cidr)) else: raise RuntimeError('%r failed, status code %s stdout %r stderr %r' % (cmd, pipex.returncode, stdout_data, stderr_data)) if len(ips) is 0: raise RuntimeError('unable to get the ips of ' + iface + "'\n possibly IPs have not been configured, or Unsupported OS") return ips
[docs]def get_mac(iface): '''gets the mac address of an interface iface''' logger = logging.getLogger('genmodel.netutils.get_mac') #assert is_valid_iface(iface), 'the destination IP address is not a valid IPv4' mac_addr = None if platform.startswith('freebsd') or platform == 'darwin': #intf = os.popen("route get "+dest_ip).read().partition('interface: ')[2].split('\n')[0] cmd = ['ifconfig', iface] pipex = Popen(cmd, stdout=PIPE, stderr=PIPE) stdout_data, stderr_data = pipex.communicate() if pipex.returncode == 0: mac_addr = stdout_data.partition('ether ')[2].strip().split(' ')[0] else: raise RuntimeError('%r failed, status code %s stdout %r stderr %r' % (cmd, pipex.returncode, stdout_data, stderr_data)) elif platform.startswith('linux') or platform == 'cygwin': #intf = os.popen("ip route get "+dest_ip).read().partition(' dev ')[2].split(' ')[0] cmd = ['ifconfig', iface] pipex = Popen(cmd, stdout=PIPE, stderr=PIPE) stdout_data, stderr_data = pipex.communicate() if pipex.returncode == 0: mac_addr = stdout_data.partition('HWaddr ')[2].split(' ')[0] else: raise RuntimeError('%r failed, status code %s stdout %r stderr %r' % (cmd, pipex.returncode, stdout_data, stderr_data)) if mac_addr is None: raise RuntimeError('unable to get the MAC of ' + iface + "'\nUnsupported OS") return mac_addr
[docs]def get_exit_iface(dest_ip): '''gets the exit interface for a target destination IP''' logger = logging.getLogger('genmodel.netutils.get_exit_iface') assert is_valid_ipv4(dest_ip), 'the destination IP address is not a valid IPv4' exit_iface = None if platform.startswith('freebsd') or platform == 'darwin': #intf = os.popen("route get "+dest_ip).read().partition('interface: ')[2].split('\n')[0] cmd = ['route', '-n', 'get', dest_ip] pipex = Popen(cmd, stdout=PIPE, stderr=PIPE) stdout_data, stderr_data = pipex.communicate() if pipex.returncode == 0: exit_iface = stdout_data.partition('interface: ')[2].split('\n')[0] else: raise RuntimeError('%r failed, status code %s stdout %r stderr %r' % (cmd, pipex.returncode, stdout_data, stderr_data)) elif platform.startswith('linux') or platform == 'cygwin': #intf = os.popen("ip route get "+dest_ip).read().partition(' dev ')[2].split(' ')[0] cmd = ['ip', 'route', 'get', dest_ip] pipex = Popen(cmd, stdout=PIPE, stderr=PIPE) stdout_data, stderr_data = pipex.communicate() if pipex.returncode == 0: exit_iface = stdout_data.partition(' dev ')[2].split(' ')[0] else: raise RuntimeError('%r failed, status code %s stdout %r stderr %r' % (cmd, pipex.returncode, stdout_data, stderr_data)) if exit_iface is None: raise RuntimeError('unable to get the exit interface' + dest_ip + "'\nUnsupported OS") return exit_iface
[docs]def get_next_hop_ip(dest_ip): '''gets the next hop IP for a target destination IP''' logger = logging.getLogger('genmodel.netutils.get_next_hop_ip') assert is_valid_ipv4(dest_ip), 'the destination IP address is not a valid IPv4' next_hop_ip = None if platform.startswith('freebsd') or platform == 'darwin': #intf = os.popen("route get "+dest_ip).read().partition('interface: ')[2].split('\n')[0] cmd = ['route', '-n', 'get', dest_ip] pipex = Popen(cmd, stdout=PIPE, stderr=PIPE) stdout_data, stderr_data = pipex.communicate() if pipex.returncode == 0: if 'gateway' in stdout_data: next_hop_ip = stdout_data.partition('gateway: ')[2].split('\n')[0] else: next_hop_ip = dest_ip else: raise RuntimeError('%r failed, status code %s stdout %r stderr %r' % (cmd, pipex.returncode, stdout_data, stderr_data)) elif platform.startswith('linux') or platform == 'cygwin': #intf = os.popen("ip route get "+dest_ip).read().partition(' dev ')[2].split(' ')[0] cmd = ['ip', 'route', 'get', dest_ip] pipex = Popen(cmd, stdout=PIPE, stderr=PIPE) stdout_data, stderr_data = pipex.communicate() if pipex.returncode == 0: if 'via' in stdout_data: next_hop_ip = stdout_data.partition(' via ')[2].split(' ')[0] else: next_hop_ip = dest_ip else: raise RuntimeError('%r failed, status code %s stdout %r stderr %r' % (cmd, pipex.returncode, stdout_data, stderr_data)) if next_hop_ip is None: raise RuntimeError('unable to get the next hop IP' + dest_ip + "'\nUnsupported OS") return next_hop_ip
[docs]def get_mac_by_arp(ip_addr): '''gets the mac address for a given IP address using ARP''' logger = logging.getLogger('genmodel.netutils.get_mac_by_arp') assert is_valid_ipv4(ip_addr), 'the destination IP address is not a valid IPv4' mac_addr = None if platform.startswith('freebsd') or platform == 'darwin': #intf = os.popen("route get "+dest_ip).read().partition('interface: ')[2].split('\n')[0] cmd = ['arp', '-n', ip_addr] pipex = Popen(cmd, stdout=PIPE, stderr=PIPE) stdout_data, stderr_data = pipex.communicate() if pipex.returncode == 0: mac_addr = stdout_data.partition(' at ')[2].strip().split(' ')[0] else: raise RuntimeError('%r failed, status code %s stdout %r stderr %r' % (cmd, pipex.returncode, stdout_data, stderr_data)) elif platform.startswith('linux') or platform == 'cygwin': #intf = os.popen("ip route get "+dest_ip).read().partition(' dev ')[2].split(' ')[0] cmd = ['arp', '-n', ip_addr] pipex = Popen(cmd, stdout=PIPE, stderr=PIPE) stdout_data, stderr_data = pipex.communicate() if pipex.returncode == 0: mac_addr = stdout_data.partition(' ether ')[2].split(' ')[0] else: raise RuntimeError('%r failed, status code %s stdout %r stderr %r' % (cmd, pipex.returncode, stdout_data, stderr_data)) if mac_addr is None: raise RuntimeError('unable to get the MAC of ' + ip_addr + "'\nUnsupported OS") return mac_addr
[docs]def get_pkt_data_len(buf): '''gets tje packet data lenth for a packet that is arp icmp udp or tcp''' logger = logging.getLogger('genmodel.netutils.get_pkt_data_len') if isinstance(buf, dpkt.ethernet.Ethernet): eth = dpkt.ethernet.Ethernet(buf) elif isinstance(buf, dpkt.loopback.Loopback): eth = dpkt.loopback.Loopback(buf) if isinstance(eth.data, dpkt.ip.IP): ip_pkt = eth.data total_l4_len = ip_pkt.len - ip_pkt.hl*4 if isinstance(ip_pkt.data, dpkt.udp.UDP): app_data_size = total_l4_len - 8 #udp hdr length = 8bytes elif isinstance(ip_pkt.data, dpkt.tcp.TCP): tcp = ip_pkt.data app_data_size = total_l4_len - tcp.off*4 elif isinstance(ip_pkt.data, dpkt.icmp.ICMP): app_data_size = total_l4_len - 8 else: raise RuntimeError('unhandled packet type') elif isinstance(eth.data, dpkt.arp.ARP): app_data_size = 0 return app_data_size
[docs]def modify_udp_hdr(pkt, change): '''modify a packet with given info''' logger = logging.getLogger('genmodel.netutils.modify_udp_hdr') #TODO: use dpkt to process the packet components, and re-pack pass
[docs]def calc_udp_checksum(data): '''calculates the checksum of a UDP header''' logger = logging.getLogger('genmodel.netutils.calc_udp_checksum') if len(data) % 2 == 1: data += b"\0" sum_x = sum([data[i:i+2] for i in range(0, len(data), 2)]) sum_x = (sum_x >> 16) + (sum_x & 0xffff) sum_x += sum_x >> 16 sum_x = ~sum_x if pack("H", 1) == b"\x00\x01": # big endian return sum_x & 0xffff else: return (((sum_x>>8)&0xff)|sum_x<<8) & 0xffff
def getMacPairs(netdata): logger = logging.getLogger('genmodel.netutils.getMacPairs') mac_ip_pairs = [] macs = set() mac_ip_dict = json.load(netdata)['hname_mac_ip'] for host in mac_ip_dict: macs.add(str(host['mac'])) macs = list(macs) macs.remove('00:00:00:00:00:00') macs.insert(0,'ff:ff:ff:ff:ff:ff') for i in range(len(macs)): for j in range(i+1, len(macs)): mac_ip_pairs.append(tuple(sorted([macs[i], macs[j]], key=str.lower))) return mac_ip_pairs
[docs]def get_ipv4_addresses(ifaces=netifaces.interfaces()): '''ifaces must be a list of ifaces, default is all interfaces include lo''' ip_list = {} #print (ifaces) for interface in ifaces: ip_list[interface] = [] if netifaces.AF_INET in netifaces.ifaddresses(interface): for link in netifaces.ifaddresses(interface)[netifaces.AF_INET]: if 'addr' in link: ip_list[interface].append(link['addr']) return ip_list