def main(): from optparse import OptionParser parser = OptionParser() parser.add_option("-d", "--devname", dest="devname", default=None, help="The name of the tap device to open.") parser.add_option("-i", "--ifname", dest="ifname", default=None, help="The name of the interface to listen on.") parser.add_option("-g", "--group", dest="group", default=None, help="The IPv4 group to use for UML-style multicasts.") parser.add_option("-p", "--port", dest="port", default=None, help="The IPv4 port to use for UML-style multicasts.") parser.add_option("-a", "--ifaddr", dest="ifaddr", default=None, help="The IP address to listen on.") parser.add_option("-S", "--ether_source", dest="ether_source", default=None, help="The Ethernet address to listen on.") (options, args) = parser.parse_args() if options.devname is not None: input = TapConnector(options.devname) output = input elif options.group is not None: if options.port is None: print "Non-optional argument missing." return # XXX Currently, UmlMcast4Connector has to use the same broken # semantics as QEMU does, to see its traffic -- apps SHOULD be # joining groups on specific interfaces. # Note that we'll also end up seeing our own traffic. #input = UmlMcast4Connector(options.group, options.port, options.ifaddr) input = UmlMcast4Connector(options.group, options.port, "0.0.0.0") output = input elif options.ifname is not None: input = PcapConnector(options.ifname) output = PcapConnector(options.ifname) input.setfilter("udp port 67 or udp port 68") if options.ifaddr is None or \ options.ether_source is None: print "Non-optional argument missing." return ifaddr = inet_atol(options.ifaddr) ether_source = ether_atob(options.ether_source) ip_id = int(random.random() * 32768) # XXX Should really have an API for extracting our # local ethernet address. running = True while running is True: packet = input.readpkt() chain = packet.chain() # Must have: ether + ip + udp + dhcp. # UDP checksum ignored. Assume pcap filter above did its job. if len(chain.packets) < 4 or \ not isinstance(chain.packets[3], dhcpv4): continue i_ether = chain.packets[0] i_ip = chain.packets[1] i_udp = chain.packets[2] i_dhcp = chain.packets[3] # check dhcp htype is ethernet. # check if dhcp.cid in map. if i_dhcp.op != pcs.packets.dhcpv4.BOOTREQUEST or \ i_dhcp.htype != pcs.packets.dhcpv4.HTYPE_ETHER or \ i_dhcp.hlen != 6: continue #print i_dhcp chaddr_s = ether_btoa(i_dhcp.chaddr[:i_dhcp.hlen]) if not chaddr_s in map: print "%s not in map" % chaddr_s continue ciaddr = inet_atol(map[chaddr_s]) # from map dhcp = dhcpv4() dhcp.op = pcs.packets.dhcpv4.BOOTREPLY dhcp.htype = pcs.packets.dhcpv4.HTYPE_ETHER dhcp.hlen = 6 dhcp.hops = 0 dhcp.xid = i_dhcp.xid dhcp.flags = 0 #dhcp.ciaddr = ciaddr dhcp.siaddr = ifaddr # XXX should only fill out if i_dhcp.ciaddr was 0.0.0.0 dhcp.yiaddr = ciaddr dhcp.chaddr = i_dhcp.chaddr #dhcp.sname = "myhost" #dhcp.file = "/vmunix" #dhcp.options.append(dhcpv4_options.cookie().field()) # server ID. # XXX Weeiiird! #sid = dhcpv4_options.dhcp_server_identifier() #sid.value = ifaddr #sid.value = inet_atol("0.0.0.0") #dhcp.options.append(sid.field()) # Subnet mask. #sm = dhcpv4_options.subnet_mask() #sm.value = inet_atol("255.255.255.0") #dhcp.options.append(sm.field()) # Default gateway. #dg = dhcpv4_options.routers() #dg.value = ifaddr #dhcp.options.append(dg.field()) # Add end marker. #end = dhcpv4_options.end() #dhcp.options.append(end.field()) # Pad BOOTP payload to 32-bit width. # XXX BOOTP is problematic because the field which contains # the options needs to be clamped to 64 bytes in total. This # means we need to know the encoded length of each option. # For now, guess it... total length of an RFC-951 payload # is always 300 bytes. # this shuts up wireshark. #padlen = 300 - (len(dhcp.bytes) % 4) #padlen = 50 - (len(dhcp.bytes) % 4) #padlen = 4 #pad = dhcpv4_options.pad(padlen) #dhcp.options.append(pad.field()) # Encapsulate ethernet. ether = ethernet() ether.type = 0x0800 ether.src = ether_source ether.dst = i_dhcp.chaddr[:i_dhcp.hlen] # Encapsulate IPv4. ip = ipv4() ip.version = 4 ip.hlen = 5 ip.tos = 0 ip.id = ip_id ip.flags = 0x00 ip.offset = 0 ip.ttl = 1 ip.protocol = IPPROTO_UDP ip.src = ifaddr ip.dst = ciaddr ip_id += 1 # Encapsulate UDPv4. udp = udpv4() udp.sport = 67 udp.dport = 68 udp.length = len(dhcp.bytes) udp.checksum = udp.cksum(ip, dhcp.bytes) # Compute header checksums. ip.length = len(ip.bytes) + len(udp.bytes) + len(dhcp.bytes) ip.checksum = ip.cksum() # Send the lot. packet = Chain([ether, ip, udp, dhcp]) packet.encode() out = output.write(packet.bytes, len(packet.bytes)) print out
def test_dhcpv4_encode(self): p = dhcpv4() assert (p != None) p.op = pcs.packets.dhcpv4.BOOTREQUEST p.htype = pcs.packets.dhcpv4.HTYPE_ETHER p.hlen = 6 # sizeof(struct ether_addr) p.hops = 99 p.xid = 0xABADCAFE p.secs = 123 p.flags = pcs.packets.dhcpv4.BOOTP_BROADCAST p.ciaddr = inet_atol("1.2.3.4") p.yiaddr = inet_atol("5.6.7.8") p.siaddr = inet_atol("9.10.11.12") p.giaddr = inet_atol("13.14.15.16") p.chaddr = ether_atob("00:01:02:03:04:05") p.sname = "fubar" p.file = "barfu" # Append DHCP options, which MUST include the cookie. p.options.append(dhcpv4_options.cookie().field()) # Maximum DHCP message size. msz = dhcpv4_options.dhcp_max_message_size() msz.value = 1460 p.options.append(msz.field()) # DHCP message type. dhcp = dhcpv4_options.dhcp_message_type() dhcp.value = DHCPDISCOVER p.options.append(dhcp.field()) # DHCP vendor class. vc = dhcpv4_options.dhcp_class_identifier() vc.value = "FreeBSD:amd64:7-CURRENT" p.options.append(vc.field()) # BOOTP options end marker. end = dhcpv4_options.end() p.options.append(end.field()) # Pad BOOTP payload to 32-bit width. padlen = 4 - (len(p.bytes) % 4) pad = dhcpv4_options.pad(padlen) p.options.append(pad.field()) p.encode() #hd = hexdumper() #print p #print hd.dump2(p.bytes) gotttted = p.bytes expected = \ "\x01\x01\x06\x63\xAB\xAD\xCA\xFE" \ "\x00\x7B\x80\x00\x01\x02\x03\x04" \ "\x05\x06\x07\x08\x09\x0A\x0B\x0C" \ "\x0D\x0E\x0F\x10\x00\x01\x02\x03" \ "\x04\x05\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x66\x75\x62\x61" \ "\x72\x00\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x62\x61\x72\x66" \ "\x75\x00\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x63\x82\x53\x63" \ "\x39\x02\x05\xB4\x35\x01\x01\x3C" \ "\x17\x46\x72\x65\x65\x42\x53\x44" \ "\x3A\x61\x6D\x64\x36\x34\x3A\x37" \ "\x2D\x43\x55\x52\x52\x45\x4E\x54" \ "\xFF\x00\x00\x00" self.assertEqual(expected, gotttted, "test encoding")