Every time I write a program in C, I am quire tired. I thought it was easier to write one by Python. So I wrote a demo program of raw socket communication. It was not so easy as I expected, but the following program sends (and receives) an Ethernet packet almost every second. (Whole this article is licensed by Creative Commons, but the program is in public domain; that is, it can be copied without any restriction.)
It is easy to specify "raw socket", but I actually wanted to write a program that can communicate by promiscuous mode. However, I found it was quite difficult to communicate by promiscuous mode in Python in a normative method. So MAC addresses must be specified in this program.
### Ethernet packet sender/receiver (for Linux raw socket) ### # # Public domain software # # Coded by Yasusi Kanada # 2014-7-29 import optparse, socket, time, binascii BUF_SIZE = 1600 # > 1500 ETH_P_ALL = 3 # To receive all Ethernet protocols # Interface = "eth0" Interface = "eth1" host = socket.gethostbyname(socket.gethostname()) ### Packet field access ### def SMAC(packet): return binascii.hexlify(packet[6:12]).decode() def DMAC(packet): return binascii.hexlify(packet[0:6]).decode() def EtherType(packet): return binascii.hexlify(packet[12:14]).decode() def Payload(packet): return binascii.hexlify(packet[14:]).decode() ### Packet handler ### def printPacket(packet, now, message): # print(message, len(packet), "bytes time:", now, # "\n SMAC:", SMAC(packet), " DMAC:", DMAC(packet), # " Type:", EtherType(packet), "\n Payload:", Payload(packet)) # !! Python 3 !! print message, len(packet), "bytes time:", now, \ "\n SMAC:", SMAC(packet), " DMAC:", DMAC(packet), " Type:", \ EtherType(packet), "\n Payload:", Payload(packet) # !! Python 2 !! def terminal(): # Parse command line parser = optparse.OptionParser() parser.add_option("--p", "--port", dest = "port", type="int", help = "Local network port id") parser.add_option("--lm", "--lmac", "--localMAC", dest = "lmac", type="str", help = "Local MAC address") parser.add_option("--rm", "--rmac", "--remoteMAC", dest = "rmac", type="str", help = "Remote MAC address") parser.add_option("--receiveOnly", "--receiveonly", dest = "receiveOnly", action = "store_true") # parser.add_option("--promiscuous", dest = "promiscuous", action = "store_true") parser.set_defaults(lmac = "ffffffffffff", rmac = "ffffffffffff") opts, args = parser.parse_args() # Open socket sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(ETH_P_ALL)) sock.bind((Interface, ETH_P_ALL)) sock.setblocking(0) # Contents of packet to send (constant) sendPacket = binascii.unhexlify(opts.rmac) + binascii.unhexlify(opts.lmac) + \ b'\x88\xb5' + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f' # Repeat sending and receiving packets interval = 1 lastTime = time.time() while True: now = time.time() try: packet = sock.recv(BUF_SIZE) except socket.error: pass else: dmac = DMAC(packet) printPacket(packet, now, "Received:") if not opts.receiveOnly: if now > lastTime + interval: sendBytes = sock.send(sendPacket) printPacket(sendPacket, now, "Sent: ") lastTime = now else: time.sleep(0.001001) else: time.sleep(0.001001) terminal()
Packets to be sent contains 0x88b5 as its Ethernet type, and 0x000102… as its contents. You can easily change them.
The above code is for Python 2, but it can be used for Python 3 by modifying only "printPacket". The code for Python 2 is to be commented out and the code for Python 3 is to be used. When communicating by Ethernet, the interface name must be specified. This program specifies "eth1", but you can easily rewrite the value of variable "Interface".
The usage is as follows. The file name of the above program is assumed to be "term.py". Simple unicast communication (sending and receiving) can be done as follows.
python eterm.py --lm 00123456789a --rm 00a987654321
Here, the MAC address must be specified. (it can be obtained and copied using ifconfig command).
If the remote address is omitted as follows, the program broadcasts packets. (The local address can be omitted too).
python eterm.py --lm 00123456789a
To receive packets without sending ones, the usage is as follows.
python eterm.py --receiveOnly
The above program can be executed by two machines to send and to receive packets each other. Otherwise, if one machine only receives packets and the other sends and receives packets, a one-way communication can be tested. Such one-way communication is useful for testing Ethernet switch functions.