├── README.md └── udp.py /README.md: -------------------------------------------------------------------------------- 1 | # UDP 2 | 3 | UDP implementation using RAW SOCKET in Python 3.4 4 | 5 | ## UDP checksum 6 | 7 | ### one’s complement 8 | 9 | 1. Construct the UDP header and UDP Pseudo header, as illustrated below, where Checksum is set zero at first. 10 | 2. Check the length of data, if it is an odd length of bytes, supplement a byte of zero (0x00) at the end of the data when counting checksum (This is important, not on the real data). 11 | 3. Form the sequence of bytes in the order: Pseudo header-\>UDP header-\>data. 12 | 4. Loop through the sequence and pull out two bytes each time (It is an even sequence because of the supplement), left shift the first byte for eight bits, then add the second byte. As a consequence, they become a 2-byte-long number. 13 | 5. Sum these 2-byte-long numbers. Add also the carries if there’s any of them. Make sure checksum stays 2-byte-long. 14 | 6. At the end of the loop, invert all the bits of the checksum, and take the last 16 bits as the final checksum. 15 | 16 | ## UDP package field: 17 | 18 | ``` 19 | 0 7 8 15 16 23 24 31 20 | +--------+--------+--------+--------+ 21 | | Source | Destination | 22 | | Port | Port | 23 | +--------+--------+--------+--------+ 24 | | Length | Checksum | 25 | +--------+--------+--------+--------+ 26 | | 27 | | data octets ... 28 | +--------------- ... 29 | ``` 30 | UDP Pseudo Header 31 | ``` 32 | 0 7 8 15 16 23 24 31 33 | +--------+--------+--------+--------+ 34 | | source address | 35 | +--------+--------+--------+--------+ 36 | | destination address | 37 | +--------+--------+--------+--------+ 38 | | zero |protocol| UDP length | 39 | +--------+--------+--------+--------+ 40 | ``` 41 | IP Header 42 | ``` 43 | 0 7 8 15 16 23 24 31 44 | +--------+--------+--------+--------+ 45 | |Ver.|IHL|DSCP|ECN| Total length | 46 | +--------+--------+--------+--------+ 47 | | Identification |Flags| Offset | 48 | +--------+--------+--------+--------+ 49 | | TTL |Protocol| Header Checksum | 50 | +--------+--------+--------+--------+ 51 | | Source IP address | 52 | +--------+--------+--------+--------+ 53 | | Destination IP address | 54 | +--------+--------+--------+--------+ 55 | ``` 56 | -------------------------------------------------------------------------------- /udp.py: -------------------------------------------------------------------------------- 1 | ''' 2 | UDP Field: 3 | 0 7 8 15 16 23 24 31 4 | +--------+--------+--------+--------+ 5 | | Source | Destination | 6 | | Port | Port | 7 | +--------+--------+--------+--------+ 8 | | Length | Checksum | 9 | +--------+--------+--------+--------+ 10 | | 11 | | data octets ... 12 | +--------------- ... 13 | 14 | UDP Pseudo Header 15 | 0 7 8 15 16 23 24 31 16 | +--------+--------+--------+--------+ 17 | | source address | 18 | +--------+--------+--------+--------+ 19 | | destination address | 20 | +--------+--------+--------+--------+ 21 | | zero |protocol| UDP length | 22 | +--------+--------+--------+--------+ 23 | 24 | IP Header 25 | 0 7 8 15 16 23 24 31 26 | +--------+--------+--------+--------+ 27 | |Ver.|IHL|DSCP|ECN| Total length | 28 | +--------+--------+--------+--------+ 29 | | Identification |Flags| Offset | 30 | +--------+--------+--------+--------+ 31 | | TTL |Protocol| Header Checksum | 32 | +--------+--------+--------+--------+ 33 | | Source IP address | 34 | +--------+--------+--------+--------+ 35 | | Destination IP address | 36 | +--------+--------+--------+--------+ 37 | ''' 38 | 39 | import socket 40 | import struct 41 | import pprint 42 | 43 | VERSION_OFF = 0 44 | IHL_OFF = VERSION_OFF 45 | DSCP_OFF = IHL_OFF + 1 46 | ECN_OFF = DSCP_OFF 47 | LENGTH_OFF = DSCP_OFF + 1 48 | ID_OFF = LENGTH_OFF + 2 49 | FLAGS_OFF = ID_OFF + 2 50 | OFF_OFF = FLAGS_OFF 51 | TTL_OFF = OFF_OFF + 2 52 | PROTOCOL_OFF = TTL_OFF + 1 53 | IP_CHECKSUM_OFF = PROTOCOL_OFF + 1 54 | SRC_IP_OFF = IP_CHECKSUM_OFF + 2 55 | DEST_IP_OFF = SRC_IP_OFF + 4 56 | SRC_PORT_OFF = DEST_IP_OFF + 4 57 | DEST_PORT_OFF = SRC_PORT_OFF + 2 58 | UDP_LEN_OFF = DEST_PORT_OFF + 2 59 | UDP_CHECKSUM_OFF= UDP_LEN_OFF + 2 60 | DATA_OFF = UDP_CHECKSUM_OFF + 2 61 | 62 | IP_PACKET_OFF = VERSION_OFF 63 | UDP_PACKET_OFF = SRC_PORT_OFF 64 | 65 | def parse(data): 66 | packet = {} 67 | packet['version'] = data[VERSION_OFF] >> 4 68 | packet['IHL'] = data[IHL_OFF] & 0x0F 69 | packet['DSCP'] = data[DSCP_OFF] >> 2 70 | packet['ECN'] = data[ECN_OFF] & 0x03 71 | packet['length'] = (data[LENGTH_OFF] << 8) + data[LENGTH_OFF + 1] 72 | packet['Identification']= (data[ID_OFF] << 8) + data[ID_OFF + 1] 73 | packet['Flags'] = data[FLAGS_OFF] >> 5 74 | packet['Offset'] = ((data[OFF_OFF] & 0b11111) << 8) + data[OFF_OFF + 1] 75 | packet['TTL'] = data[TTL_OFF] 76 | packet['Protocol'] = data[PROTOCOL_OFF] 77 | packet['Checksum'] = (data[IP_CHECKSUM_OFF] << 8) + data[IP_CHECKSUM_OFF + 1] 78 | packet['src_ip'] = '.'.join(map(str, [data[x] for x in range(SRC_IP_OFF, SRC_IP_OFF + 4)])) 79 | packet['dest_ip'] = '.'.join(map(str, [data[x] for x in range(DEST_IP_OFF, DEST_IP_OFF + 4)])) 80 | packet['src_port'] = (data[SRC_PORT_OFF] << 8) + data[SRC_PORT_OFF + 1] 81 | packet['dest_port'] = (data[DEST_PORT_OFF] << 8) + data[DEST_PORT_OFF + 1] 82 | packet['udp_length'] = (data[UDP_LEN_OFF] << 8) + data[UDP_LEN_OFF + 1] 83 | packet['UDP_checksum'] = (data[UDP_CHECKSUM_OFF] << 8) + data[UDP_CHECKSUM_OFF + 1] 84 | packet['data'] = ''.join(map(chr, [data[DATA_OFF + x] for x in range(0, packet['udp_length'] - 8)])) 85 | 86 | return packet 87 | 88 | def udp_send(data, dest_addr, src_addr=('127.0.0.1', 35869)): 89 | #Generate pseudo header 90 | src_ip, dest_ip = ip2int(src_addr[0]), ip2int(dest_addr[0]) 91 | src_ip = struct.pack('!4B', *src_ip) 92 | dest_ip = struct.pack('!4B', *dest_ip) 93 | 94 | zero = 0 95 | 96 | protocol = socket.IPPROTO_UDP 97 | 98 | #Check the type of data 99 | try: 100 | data = data.encode() 101 | except AttributeError: 102 | pass 103 | 104 | src_port = src_addr[1] 105 | dest_port = dest_addr[1] 106 | 107 | data_len = len(data) 108 | 109 | udp_length = 8 + data_len 110 | 111 | checksum = 0 112 | pseudo_header = struct.pack('!BBH', zero, protocol, udp_length) 113 | pseudo_header = src_ip + dest_ip + pseudo_header 114 | udp_header = struct.pack('!4H', src_port, dest_port, udp_length, checksum) 115 | checksum = checksum_func(pseudo_header + udp_header + data) 116 | udp_header = struct.pack('!4H', src_port, dest_port, udp_length, checksum) 117 | with socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP) as s: 118 | s.sendto(udp_header + data, dest_addr) 119 | 120 | def checksum_func(data): 121 | checksum = 0 122 | data_len = len(data) 123 | if (data_len % 2): 124 | data_len += 1 125 | data += struct.pack('!B', 0) 126 | 127 | for i in range(0, data_len, 2): 128 | w = (data[i] << 8) + (data[i + 1]) 129 | checksum += w 130 | 131 | checksum = (checksum >> 16) + (checksum & 0xFFFF) 132 | checksum = ~checksum & 0xFFFF 133 | return checksum 134 | 135 | def ip2int(ip_addr): 136 | if ip_addr == 'localhost': 137 | ip_addr = '127.0.0.1' 138 | return [int(x) for x in ip_addr.split('.')] 139 | 140 | def udp_recv(addr, size): 141 | zero = 0 142 | protocol = 17 143 | with socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP) as s: 144 | s.bind(addr) 145 | while True: 146 | data, src_addr = s.recvfrom(size) 147 | packet = parse(data) 148 | ip_addr = struct.pack('!8B', *[data[x] for x in range(SRC_IP_OFF, SRC_IP_OFF + 8)]) 149 | udp_psuedo = struct.pack('!BB5H', zero, protocol, packet['udp_length'], packet['src_port'], packet['dest_port'], packet['udp_length'], 0) 150 | 151 | verify = verify_checksum(ip_addr + udp_psuedo + packet['data'].encode(), packet['UDP_checksum']) 152 | if verify == 0xFFFF: 153 | print(packet['data']) 154 | else: 155 | print('Checksum Error!Packet is discarded') 156 | 157 | def verify_checksum(data, checksum): 158 | data_len = len(data) 159 | if (data_len % 2) == 1: 160 | data_len += 1 161 | data += struct.pack('!B', 0) 162 | 163 | for i in range(0, data_len, 2): 164 | w = (data[i] << 8) + (data[i + 1]) 165 | checksum += w 166 | checksum = (checksum >> 16) + (checksum & 0xFFFF) 167 | 168 | return checksum 169 | 170 | if __name__ == '__main__': 171 | udp_send("hello",('localhost', 12345)) 172 | --------------------------------------------------------------------------------