Minimum L3 IP Packet with Scapy in Python & Wireshark

jason19970210
4 min readJan 19, 2024

--

Tags: scapy tcp/ip wireshark python ipv4

Photo by Erwan Hesry on Unsplash

IP (Internet Protocol) is the network layer protocol that is responsible for routing packets across the internet. The IP header contains information about the source and destination addresses, the version, the length, the identification, the flags, the fragment offset, the time to live, the protocol, the checksum, and the options.

The IP header is usually 20 bytes long, but it can be up to 60 bytes if options are used. The IP header is also different from the TCP header in that it does not have any port numbers, sequence or acknowledgment numbers, window size, or flags. The IP header also has a different checksum algorithm and a different set of options.

IP Packet Header (src)

According to Section 3 in document Security Assessment of the Transmission Control Protocol (TCP) draft-ietf-tcpm-tcp-security-02.txt from IETF (Internet Engineering Task Force) [2], the minimum TCP header size is 20 bytes, and corresponds to a TCP segment with no options and no data. We can treat the IP packets which header length is less than 20 bytes as malicious traffic, aim to trigger a DoS attack, thus the device should drop the packet.

This article will deep dive with sending IP packets with less than minimum TCP header length as DoS attack by using Scapy package [4].

Experiences

Build a simple IP packet with basic usage from Scapy

from scapy.data import ETHER_BROADCAST
from scapy.layers.l2 import Ether
from scapy.layers.inet import IP
from scapy.arch import get_if_hwaddr
from scapy.sendrecv import sendp

## setup interface
src_mac_addr = get_if_hwaddr("<interface>")

packet = Ether(src=src_mac_addr, dst=ETHER_BROADCAST) / IP()

packet.show2()

print(f"packet length (bytes): {len(packet)}\n")

sendp(packet)

## Output

###[ Ethernet ]###
dst = ff:ff:ff:ff:ff:ff
src = <interface_MAC_addr>
type = IPv4 <- EtherType: 0x0800
###[ IP ]###
version = 4
ihl = 5
tos = 0x0
len = 20
id = 1
flags =
frag = 0
ttl = 64
proto = hopopt
chksum = 0x7ce7
src = 127.0.0.1
dst = 127.0.0.1
\options \

packet length (bytes): 34

As the result, the basic TCP packet comes with 34 bytes, including 14 bytes as Ether packet and 20 bytes as TCP packet header.

The above code snippet also shows the EtherType [wiki] [Value Table] for a L2 packet is 0x0800 which indicates as IPv4 type in Wireshark.

If there is no IP packet section right after the Ether packet, the EtherType will be filled with default value: 0x0900 which indicates loopback in Wireshark.

Basic TCP packet with 34 bytes

Try building the IP packet with 4 bytes header by using Raw without IP packet section

from scapy.data import ETHER_BROADCAST
from scapy.packet import Raw
from scapy.arch import get_if_hwaddr
from scapy.sendrecv import sendp

## setup interface
src_mac_addr = get_if_hwaddr("<interface>")

packet = Ether(src=src_mac_addr, dst=ETHER_BROADCAST, type=0x0800) / Raw(b'\x45\x00\x00\x04')

packet.show2()

print(f"packet length (bytes): {len(packet)}\n")

sendp(packet)

## Output

###[ Ethernet ]###
dst = ff:ff:ff:ff:ff:ff
src = <interface_MAC_addr>
type = IPv4
###[ IP ]###
version = 4
ihl = 5
tos = 0x0
len = 4
id = 1
flags =
frag = 0
ttl = 64
proto = hopopt
chksum = None
src = 127.0.0.1
dst = 127.0.0.1
\options \

packet length (bytes): 18

According to the previous test, the minimum IP packet which Wireshark can recognize must contain 4 bytes as IP packet header right after Ether frame (14 Bytes), so the packet length is 18 bytes.

TCP packet with less than 20 bytes header

Another test for sending full 20 bytes IP packet header with \x00 , please modify the Total Length value to 0x14 which is integer 20 as decimal notation.

from scapy.data import ETHER_BROADCAST
from scapy.packet import Raw

## setup interface
src_mac_addr = get_if_hwaddr("<interface>")

packet = Ether(src=src_mac_addr, dst=ETHER_BROADCAST, type=0x0800) / Raw(b'\x45\x00\x00\x14') / Raw(b'\x00' * 16)
^
Total Length Value
packet.show2()

print(f"packet length (bytes): {len(packet)}\n")

## Output

###[ Ethernet ]###
dst = ff:ff:ff:ff:ff:ff
src = <interface_MAC_addr>
type = IPv4
###[ IP ]###
version = 4
ihl = 5
tos = 0x0
len = 4
id = 1
flags =
frag = 0
ttl = 64
proto = hopopt
chksum = None
src = 127.0.0.1
dst = 127.0.0.1
\options \

packet length (bytes): 34

What if we remove the Raw(b'\x00' * 16) ?

Wireshark will mark this packet as malformed IPv4 packet.

Conclusion

Based on the tests above, for sending out a malformed IP packet which header length is less than 20 bytes should be at least 14 bytes as Ether frame and 4 bytes if we desired to mark it as IPv4 packet section.

The DoS protection mechanism / policy should be set its filter which the IP packet length less than 20 bytes to avoid processing meaningless packet.

Reference

[1]: https://www.networkurge.com/2017/10/tcp-header-details.html
[2]: https://www.paloaltonetworks.com/cyberpedia/what-is-a-denial-of-service-attack-dos
[3]: https://datatracker.ietf.org/doc/html/draft-ietf-tcpm-tcp-security-02#section-3
[4]: https://scapy.net/

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

jason19970210
jason19970210

Written by jason19970210

a slashie from Taiwan 🇹🇼! Fields: Software Dev, Networking, Digital Forensics Investigator, SA, UIUX, 3D Design, etc.

No responses yet

Write a response