sending ICMP packets in scapy and choosing the correct interface

Pradeep picture Pradeep · Oct 9, 2014 · Viewed 23.1k times · Source

Can we use the srp() function for a Layer 3 ICMP packet? I see that when we craft an ICMP echo-request packet and use the sr() to send/receive, we do NOT see it getting sent out of the interface , hence no response from the destination. But the same packet if we use the srp() function we see the response. When should we use sr() and when srp()? In the documentation it states sr() is to be used for L3 packet and srp() to be used for L2? But in my case I am not sure why sr() is not working for an ICMP packet? Can some experts please help me understand?

Also can someone let me know if "iface" argument is needed always. Without that how will scapy know through which interface its supposed to send the packet?

Case 1: sr() function with iface as argument:

sr(icmp,iface="eth0")

Begin emission:

WARNING: Mac address to reach destination not found. Using broadcast.
Finished to send 1 packets.
^C
Received 0 packets, got 0 answers, remaining 1 packets
(<Results: TCP:0 UDP:0 ICMP:0 Other:0>, <Unanswered: TCP:0 UDP:0 ICMP:1 Other:0>)

Above I do NOT see any ICMP response from the IP 192.168.25.1

Case 2: sr() function without iface:

sr(icmp)   
.Begin emission:
......WARNING: Mac address to reach destination not found. Using broadcast.
.Finished to send 1 packets.
...............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................^C
Received 887 packets, got 0 answers, remaining 1 packets
(<Results: TCP:0 UDP:0 ICMP:0 Other:0>, <Unanswered: TCP:0 UDP:0 ICMP:1 Other:0>)

If you see above the received packets is more but I do not see any ICMP response.

Case 3: Sending the ICMP packet with srp() instead of sr():

srp(icmp,iface="eth0")
Begin emission:
Finished to send 1 packets.
*
Received 1 packets, got 1 answers, remaining 0 packets
(<Results: TCP:0 UDP:0 ICMP:1 Other:0>, <Unanswered: TCP:0 UDP:0 ICMP:0 Other:0>)

Here I used the srp() function instead of sr() function and now I see that the ICMP echo request was sent properly and I received the response also.

>>> icmp.show2()
###[ Ethernet ]###
  dst: 02:00:00:11:01:03
  src: 02:00:20:ee:64:01
  type: 0x800
###[ IP ]###
     version: 4L
     ihl: 5L
     tos: 0x0
     len: 28
     id: 1
     flags:
     frag: 0L
     ttl: 64
     proto: icmp
     chksum: 0xc78c
     src: 192.168.25.2
     dst: 192.168.25.1
     \options\
###[ ICMP ]###
        type: echo-request
        code: 0
        chksum: 0xf7ff
        id: 0x0
        seq: 0x0
>>>                  

Answer

Yoel picture Yoel · Oct 9, 2014

The sr function per the official API documentation:

sr(pkts, filter=None, iface=None, timeout=2, inter=0, verbose=None, chainCC=0, retry=0, multi=0)

Send and receive packets at layer 3 using the conf.L3socket supersocket.

The srp function:

srp(pkts, filter=None, iface=None, timeout=2, inter=0, verbose=None, chainCC=0, retry=0, multi=0, iface hint=None)

Same as srp but for working at layer 2 with conf.L2socket supersocket.

Since your ICMP packet has its layer 2 fields filled as well, as shown by the output of ICMP.show2(), you should use the srp function. Had you left them untouched, as done in this tutorial, you could have used the sr function.


Now, regarding your question about ICMP's classification as a layer 2 protocol or a layer 3 protocol. Many think it's a layer 3 protocol, such as here, since it uses the IP header and "sits" on top of it. However, others consider it to be a layer 2 protocol such as here. This is a question with some good answers on this issue, but note that they refer to the OSI model so the layering scheme numbering is a bit different. This is the best I've managed to locate, from here:

IP itself has no mechanism for establishing and maintaining a connection, or even containing data as a direct payload. Internet Control Messaging Protocol is merely an addition to IP to carry error, routing and control messages and data, and is often considered as a protocol of the network layer.

EDIT - I've just encountered this link, and thought it's worth a mention:

ICMP is a protocol within the TCP/IP stack that exist basically to provide control, troubleshooting, and error messages. It runs over IP, like TCP and UDP do, but is a network-layer protocol, like IP, rather than a transport layer protocol like TCP and UDP are. (Yes, this is kind of weird, that ICMP is encapsulated within IP while being on the same layer as IP. But then again, you can encapsulate IP within IP as well.)

RFC 792 is also pretty explicit:

ICMP, uses the basic support of IP as if it were a higher level protocol, however, ICMP is actually an integral part of IP.

And so is RFC 1122:

ICMP is a control protocol that is considered to be an integral part of IP, although it is architecturally layered upon IP, i.e., it uses IP to carry its data end-to-end just as a transport protocol like TCP or UDP does.
...
Although ICMP messages are encapsulated within IP datagrams, ICMP processing is considered to be (and is typically implemented as) part of the IP layer.


Regarding your last question about explicitly specifying the interface, see scapy's tutorial:

The send() function will send packets at layer 3. That is to say it will handle routing and layer 2 for you. The sendp() function will work at layer 2. It’s up to you to choose the right interface and the right link layer protocol.

The official API documentation is a bit more detailed:

When Scapy is launched, its routing tables are synchronized with the host’s routing table. For a packet sent at layer 3, the destination IP determines the output interface, source address and gateway to be used. For a layer 2 packet, the output interface can be precised, or an hint can be given in the form of an IP to determine the output interface. If no output interface nor hint are given, conf.iface is used.

Specifically, the iface parameter is used for setting the input interface (but sets also the output interface, if iface_hint is not used):

iface: listen answers only on the provided interface

For hinting on the output interface, use iface_hint for the layer 2 functions:

There is also an additional parameter, iface_hint, which give an hint that can help choosing the right output interface. By default, if not specified by iface, conf.iface is chosen. The hint takes the form of an IP to which the layer 2 packet might be destinated. The Scapy routing table (conf.route) is used to determine which interface to use to reach this IP.