Sending DHCP Discover using python scapy
Asked Answered
S

3

8

I am new to Python and learning some network programming, I wish to send an DHCP packet through my tap interface to my DHCP server and then wait to a response from it. I tried with several packet building techniques such a structs and ctypes and ended up with using the scapy package. Here I am able to send DHCP packet but unable to get any response from the DHCP server (analyzed using wireshark and tcpdump).My packet looked like same as original DHCP packet but failed to get response. Here is my code:

import socket
from scapy.all import *

def main():

 if len(sys.argv)<3:
   print " fewer arguments."
   sys.exit(1)
 else:
   tap_interface = sys.argv[1]
   src_mac_address = sys.argv[2]

 ethernet = Ether(dst='ff:ff:ff:ff:ff:ff',src=src_mac_address,type=0x800)
 ip = IP(src ='0.0.0.0',dst='255.255.255.255')
 udp =UDP (sport=68,dport=67)
 fam,hw = get_if_raw_hwaddr(tap_interface)
 bootp = BOOTP(chaddr = hw, ciaddr = '0.0.0.0',xid =  0x01020304,flags= 1)
 dhcp = DHCP(options=[("message-type","discover"),"end"])
 packet = ethernet / ip / udp / bootp / dhcp

 fd = open('/dev/net/tun','r+')
 TUNSETIFF = 0x400454ca
 IFF_TAP = 0x0002
 IFF_NO_PI = 0x1000
 mode = IFF_TAP | IFF_NO_PI
 ifr = struct.pack('16sH', tap_interface, IFF_TAP | IFF_NO_PI)
 fcntl.ioctl(fd,TUNSETIFF,ifr)


 while True:
    sendp(packet, iface = tap_interface)
    time.sleep(10)


 if __name__ == '__main__':
     main()

Is there any other ways of achieving this? If so please do mention them as well. Thanks in Advance.

Silicium answered 4/8, 2014 at 17:42 Comment(2)
what's the implementation of your dhcp server?Helotism
I have a virtual router which will take care of routing related tasksSilicium
S
6

Solved ! I had the same problem,

The problem I think was on the srp() function, it can't receive packets on port 68, but I've created a new function with a new thread that sniffs BOOTP messages and displays the packet fields. you can simulate it :

sniff(iface=myiface, filter="port 68 and port 67")

then send the packet using srp() or sendp() func :)

NOTE: I have used multithreading mechanism cause my program sends messages and sniffs if a rogue DHCP Server is on the network

Stallard answered 27/8, 2014 at 10:45 Comment(1)
Can I ask how you know that srp can't receive on port 68? I'm dealing with the same problem. It seems like it may be due to hashing though.Sebastian
F
2

I am not sure if this would qualify as an answer, but we use scapy to simulate DHCP server/client exchange, and the following does the job for us:

discover = Ether(dst='ff:ff:ff:ff:ff:ff', src=cliMAC, type=0x0800) / IP(src='0.0.0.0', dst='255.255.255.255') / UDP(dport=67,sport=68) / BOOTP(op=1, chaddr=cliMACchaddr) / DHCP(options=[('message-type','discover'), ('end')])

The main difference between my code and yours seem to be how the BOOTP header is defined. Maybe you could try my packet definition and see if it works?

Fluorescent answered 4/8, 2014 at 21:32 Comment(0)
K
0

Here is an example that I did that gets a dhcp address and assigns it to an ip interface:

My rough POC, while creating code for my project:

 #!/usr/bin/python

    from scapy.all import Ether,IP,UDP,DHCP,BOOTP,get_if_raw_hwaddr,get_if_hwaddr,conf,sniff,sendp
    from pyroute2 import IPDB
    from Queue import Empty
    from multiprocessing import Process, Queue, Manager
    from wpa_supplicant.core import WpaSupplicantDriver
    from twisted.internet.selectreactor import SelectReactor
    import threading
    import time
    import errno
    import sys
    import types
    import netifaces
    import dbus
    import json 
    import re

    class PythonDHCPScanner:

        def change_ip(self,ipObject,netInterface):
            ipdb = IPDB()
            ips= ipdb.interfaces[self.get_interface(netInterface)]
            ipAddrs = ips.ipaddr.ipv4[0]
            ips.del_ip(ipAddrs['address'],ipAddrs['prefixlen'])
            ips.add_ip(ipObject['ipAddr'],24)
            ipdb.commit()
            ipdb.routes.add(dst="default",gateway=ipObject['router'])
            ipdb.commit()


        def queue_get_all(self):
            items = []
            maxItems = 50
            for numOfItemsRetrieved in range(0, maxItems):
               try:
                   items.append(self.q.get_nowait())
               except Empty, e:
                   break
            return items

        def __init__(self):
            self.net_iface = netifaces.interfaces()

        def dhcp_print(self,pkt):
            self.q.put(str(pkt))

        def get_interface(self,number):
            return str(self.net_iface[number].decode())

        def get_interfaces(self):
            return self.net_iface

        def get_dhcp_object(self,interfaceNumber):
            self.q = Manager().Queue()
            c = Process(target=self.callSniffer,args=(interfaceNumber,)).start()
            time.sleep(0.1)
            p = Process(target=self.callPacket(interfaceNumber)).start()
            time.sleep(5)
            if c is not None:
                c.join()
            dhcp = {}
            for strPkt in self.queue_get_all():
                try:
                    pkt = Ether(strPkt)
                    pkt.show()
                    if pkt[Ether].dst == get_if_hwaddr(self.get_interface(interfaceNumber)):
                        if pkt[DHCP]:
                            if pkt.getlayer(DHCP).fields['options'][0][1] == 2:
                                 if pkt[IP]:
                                     dhcp['ipAddr'] = pkt[IP].dst
                                 for option in pkt.getlayer(DHCP).fields['options']:
                                     if option == 'end':
                                        break
                                     dhcp[option[0]] = option[1]
                                 print dhcp['router']
                                 print dhcp['subnet_mask']
                                 break
                except:
                        continue
            return dhcp

        def callSniffer(self,interfaceNumber):
            inter = self.get_interface(interfaceNumber)
            conf.iface = inter
            print inter
            sniff(iface=inter,filter="udp",prn=self.dhcp_print, timeout=10)

        def callPacket(self,interfaceNumber):
            inter = self.get_interface(interfaceNumber) 
            print inter
            fam,hw = get_if_raw_hwaddr(inter)
            macaddress= get_if_hwaddr(inter)
            conf.iface = inter
            ethernet = Ether(dst="ff:ff:ff:ff:ff:ff",src=macaddress,type=0x800)
            ip = IP(src="0.0.0.0",dst="255.255.255.255")
            udp = UDP(sport=68,dport=67)
            bootp = BOOTP(chaddr =hw,xid=0x10000000)
            dhcp = DHCP(options=[("message-type","discover"),("end")])
            packet=ethernet/ip/udp/bootp/dhcp
            sendp(packet,iface=inter)

    # get dhcp object

    dave  = PythonDHCPScanner()
    dhcpObject = dave.get_dhcp_object(3)

 # Pick interface number 3 on my box

    time.sleep(1)
    for dhcpKey in dhcpObject.keys():
        print str(dhcpKey) + ":" + str(dhcpObject[dhcpKey])
    time.sleep(1)
    dave.change_ip(dhcpObject,3)
Knowle answered 27/8, 2017 at 3:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.