[ Top page ]

« Ethernet simulator for learning | Main | A Python library for 3D turtle graphics by 3D printers »

Network and communication

Raw socket communication on Linux in Python

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.

Keywords:

TrackBack

TrackBack URL for this entry:
https://www.kanadas.com/mt/mt-tb.cgi/6800

Post a comment

About

This page contains a single entry from the blog posted on August 5, 2014 8:09 PM.

Many more can be found on the main index page or by looking through the archives.

Creative Commons License
This weblog is licensed under a Creative Commons License.
Powered by Movable Type