Suppose I have this situation where I wrote a program to poison the ARP cache of 2 devices (let's say A and B), both in the local network to successfully able to MITM from device M. The program runs on device M. When I enable IP forwarding with the command sysctl net.ipv4.ip_forward=1 on device M, HTTP connection from device A to B can be established without any issues, and I am able to see the traffic on device M.

But, the same situation where ARP caches are poisoned after I disable the IP forwarding with the command sysctl net.ipv4.ip_forward=0 on device M, HTTP connection can't be established from device A to B. I can see the TCP SYN packet from device A on device M. In my program, after receiving the SYN packet on device M, I modify the src MAC address in the packet with M's MAC address (from A's MAC address) and dst MAC address to B's MAC address (from M's MAC address) and inject it into the network. I don't modify anything from the network layer onwards. I can see the packet at B with new src and dst MACs with TCPdump command, which means the packet gets to the B. But B doesn't respond to that packet, which I can't comprehend why.

So, the question is what special does ip_forward=1 does that makes this kind of MITM situation work? To clarify, all the machines are linux. With forwarding enabled on device M, I don't need to modify the MAC addresses in the packets. I just poison the cache and things work fine from there.

3 Answers 3


M's kernel receives a packet whose destination IP address indicates it's not meant for M. What will it do?

When ip_forward=0, it thinks: "I don't know why this got sent to me and I don't really care. To the trash it goes!"

With ip_forward=1, "Hmm, this is not for me. But I know where the recipient is, so I'll just resend it with the correct MAC address."

In other words, with ip_forward=1, you don't have to modify the MAC address because your kernel does it for you.

  • If the packet sent for a different IP address gets discarded I am curious why I am still able to read the packet programmatically? Also, when I send a ping using ICMP, which also uses IP protocol, B receives the packet with the exact setup. That got me thinking - does this possible drop behavior related to TCP only? Nov 23, 2021 at 3:59
  • @telcoM Why does B don't establish TCP/HTTP connection when the packet gets to B even in the case of when ip_forward=0 because I programmatically inject it in the network? How does B know ip_forward is 0 on M and why does it care as long as it gets the packet? Feb 23, 2023 at 2:49
  • @InvisibleWolf old question, but still fascinating. TCP is equally susceptible, but things would break if you messed up the sequence numbers mid-stream (which you probably didn't). Couldn't it simply have been a refresh of the ARP cache on B?
    – zwets
    Jan 26 at 0:04

Minimal sample experiment where net.ipv4.ip_forward=1 is crucial

Consider the following topology:

Internet --- Wi-Fi --- Computer 1 --- Ethernet --- Computer 2

and suppose you want Computer 2 to be able to access the Internet through Computer 1, e.g. because Computer 2 does not have Wi-Fi. This basically means making Computer 1 into a router I think.

At: https://askubuntu.com/questions/3063/share-wireless-connection-with-wired-ethernet-port/1502850#1502850 I managed to achieve this setup with two Ubuntu laptops, and:

sudo sysctl net.ipv4.ip_forward=1

is a crucial step, because without it, Computer 1 just won't forward the IP packets from Computer 2 to the Internet.

With net.ipv4.ip_forward=0, we are able to access Computer 1 from Computer 2, but we aren't able to go beyond Computer 1 to the Internet and back.

A cool thing you can do from Computer 1 when net.ipv4.ip_forward=1 is to observe packets flowing with:

sudo wireshark -k -f 'icmp' -i enp1s0f0 -i wlp2s0

If you do from Computer 2:

ping example.com

then each ping will produce 4 Wireshark lines, and you can clearly see how Computer 1 consumes request packets from Computer 2 and forwards them to the Internet, and then takes the response back from the Internet and sends it to Computer 2:

         Time         Source           Dest             Hw src             Hw dst  Protocol
1 0.000000000  54:e1:ad:b5:5b:08  fc:5c:ee:24:fb:b4      ICMP  request  id=0x79ee, seq=8/2048, ttl=64 (reply in 4)
2 0.000074761  04:7b:cb:cc:1b:10  9c:53:22:17:e2:0e      ICMP  request  id=0x79ee, seq=8/2048, ttl=63 (reply in 3)
3 0.098882299  9c:53:22:17:e2:0e  04:7b:cb:cc:1b:10      ICMP  reply    id=0x79ee, seq=8/2048, ttl=51 (request in 2)
4 0.098952451  fc:5c:ee:24:fb:b4  54:e1:ad:b5:5b:08      ICMP  reply    id=0x79ee, seq=8/2048, ttl=50 (request in 1)

It is quite fun to think that basically any box that runs Linux and has more than one network interface can work as a router.

Tested with computer 1 = Lenovo ThinkPad P14s, Computer 2 = Lenovo ThinkPad P51 with Wi-Fi turned off, both on Ubuntu 23.10.


When you poison the ARP cache, A and B start sending datagrams to M that have the MAC-Adress of M but the IP addresses of B and A respectively. So these datagrams are directed at M in layer 2 (ethernet) but directed at the other device in layer 3 (IP). In order to send these datagrams out to the layer 3 recipient (according to IP address) M has to do IP forwarding.

With net.ipv4.ip_forward=0 you disable IP forwarding, with net.ipv4.ip_forward=1 you enable it.

Why is IP forwarding needed?

Take a look at the OSI model. There you find

  • HTTP at layer 7
  • TCP at layer 4
  • IP at layer 3
  • Ethernet at layer 2

Your ARP poisoning affects layer 2.

If there is no ARP poisoning the HTTP message from A to B is wrapped into a TCP stream (directed at port 80) which is sent with IP to the IP address of B. The IP address of B is associated with the Ethernet address of B and the datagrams go straight (through the hub or switch) from A to B and vice versa. There is no necessity to forward IP.

When you poison the ARP cache of A and B to point to M, the following happens. The HTTP message from A to B is wrapped into a TCP stream (directed at port 80) which is sent with IP to the IP address of B. The IP address of B is associated with the Ethernet address of M and the datagrams go to M. M is not the intended recipient of these datagrams.

In order ot make the HTTP connection work, M has to forward the IP datagrams from A to B and vice versa. This may happen either through the kernel with the setting above or with a special program that listens for these datagrams and resends them.

  • Yes, but I wanted to know why IP forwarding is needed when I am reading the packet from A on M and sending it to B. Nov 18, 2021 at 18:45

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .