10

I would like to know if it is possible in linux to receive an UDP packet in userspace that has a 127.0.0.0/8 dst address but coming from an external interface.

I tested it with nc and I can see although nc binds to all addresses it doesn't receive the packet.

On Device 1 I manipulated the local routing table to route this packet to desired interface then I send a test packet.
Device 1:

frr:~# ip route show table local 
...  
127.1.1.1 nhid 17  encap mpls  16 via 10.10.10.5 dev eth5
...
frr:~# echo "foo" | nc -w1 -u -v -s 3.3.3.3 127.1.1.1 3503

Device 2:

frr:~# nc -l -u -p 3503

The following packet is generated and captured in wireshark on Device 2 interface: enter image description here

I know that according to RFC 1812 this should never happen. On the other hand this is a valid use case according RFC4379. The trick here that the packet I'm sending is actually not IP routed but MPLS switched and on the last hop the MPLS label is missing due to PHP (Penultimate Hop Popping) and the goal of using 127.0.0.0/8 address as dst is to make sure that when label stack runs out or no valid nexthop then the router will not forward based on IP address but process the packet. This is called MPLS OAM or LSP Ping.

4
  • Is this a real intention or just a sandbox for learning? I've found that trying to be "fancy" with IP blocks can trip you up later. For example 192.168.x.y must not be supernetted to anything larger than a /24 according to the RFCs. Darn near everything supports it, but there's always an exception. Perhaps you want to use some range from 198.18/15 instead of building yourself pain later ?
    – Criggie
    Mar 6 at 3:30
  • @Criggie, hmm, which RFCs would those be? There's a lot of obsolete stuff in the pre-CIDR specifications, probably none of which is relevant any more (well, unless you're Cisco). "Supernetting" seems to imply some base-level net, which sounds a lot like classful addressing.
    – ilkkachu
    Mar 6 at 8:04
  • @ilkkachu yeah it was linksys kit - WRT54GL that said "you can't enter a 192.168 IP address with a netmask larger than a /24" or words to that effect. It was definitely obsolete at the time, but the routers still blocked. I ended up flashing them with something aftermarket, but this is an example of how doing "tricky" can stuff you up later. Boring design is good, long-lived design.
    – Criggie
    Mar 6 at 10:01
  • @Criggie This is a real life scenario in a sense of simulating carrier grade routing/switching features on a linux data plane. I understand what you suggest, but this is not the usual home network area and the mentioned RFC clearly states why the loopback address range needs to be used and why others are not good enough. These are carrier level features, mostly available in devices from vendors like Cisco, Juniper, Huawei, Ericsson etc. These devices are obviously not in the same ballpark as a WRT54GL.
    – zskr
    Mar 6 at 15:23

2 Answers 2

12

By default the Linux kernel enforces RFC 1812 when performing route validation on packets with such source or destination. Here are a few samples: IPv4: 1 2 3 4 5, ARP: 6. They all look about the same:

  if (ipv4_is_loopback(saddr) && !IN_DEV_ROUTE_LOCALNET(in_dev))
      return -EINVAL;

If the source or destination of address is within 127.0.0.0/8 and this doesn't happen within the host, ie through the loopback interface, the packet will be dropped... unless the dedicated sysctl toggle route_localnet (checked by the macro above) is enabled, in which case the address is considered a normal routable address:

route_localnet - BOOLEAN

Do not consider loopback addresses as martian source or destination while routing. This enables the use of 127/8 for local routing purposes.

default FALSE

So to have an interface accept to receive or emit such traffic without it being dropped by the routing stack, the toggle has to be enabled on it. For an interface eth5 this would be done with:

sysctl -w net.ipv4.conf.eth5.route_localnet=1

Depending on how the encapsulation is done and on the overall setup, possibly other interfaces could be involved, use all instead of eth5 first, in case of doubt.


Here's a question I answered on SU about such packet on the wire:

Sending packets meant for an address on 127.0.0.1/8 to the network on Ubuntu

Hint: also disable rp_filter everywhere (including all and default) when debugging until it starts working. That's what causes some of the RTNETLINK answers: Invalid argument until all routes for both directions are properly configured.


Beside this RFC 4379 use case, one possibly more common case is when a Destination NAT is done to change a normal address into a loopback address to cheaply overcome an application that binds its service only on a loopback address. Simplified example:

iptables -t nat -I PREROUTING -p tcp --dport 80 -j DNAT --to-destination 127.0.0.1:8080

It can work only with the toggle enabled, because the routing stack itself sees packets not on the loopback with a loopback address inside (destination for incoming, source for reply). No such packet actually appears on the wire (NAT happens) but the routing stack doesn't know this, so the check has to be ignored.

3
  • Hi this was actually it! Really thanks. Although there might be some other implications of this, but at least theoretically it is possible to create MPLS OAM functionality without custom kernel modifications.
    – zskr
    Mar 4 at 9:42
  • TIL the term "martian sources". Quite a useful shortcut for talking about the concept. Mar 4 at 19:01
  • 2
    @Indigenuity : I've heard of "martian packets", along the same lines. The assumption is that no packets should come from there. Hopefully this terminology doesn't cause too much trouble after Mars gets colonized!
    – TOOGAM
    Mar 5 at 14:58
3

When lo0 is created, it is assigned 127.0.0.1 with a netmask of /8. This is done by the kernel (see net/ipv4/devinet.c). So, is you want to try to reuse some of the 127.0.0.0/8 space, you must re-assign the IP mask after the initialisation. So, you should probably set the network on lo0 as 127.0.0.0/24, before setting your ethernet to 127.1.1.0/24.

Note that there are many processes that expect 127.0.0.0/8 to be on lo0. So, setting the network on lo0 to a /24 will probably work, but may break other processes. You might want to use a systemd-free distribution.

There are discussions about reclaiming, at least part of, 127.0.0.0/8. See for example an IETF draft.

You will not be able to go through standard routers. You will need to customize them as well.

2
  • Hello, Thanks for your answer. I think this would solve the simplified test I showed in the question, but would not work for LSP Ping. According to RFC when an LSP Ping is sent a random address is allocated from 127.0.0.0/8 at sender so the receiver cannot have knowledge what subnet to assign to an interface + many different mpls tunnels can terminate at an interface. I think it is actually good that 127.0.0.0/8 is assigned to lo0 in this sense. It just that the packet somehow should be routed from ingress if to lo0 and then to process udp but I don't know how to do that.
    – zskr
    Mar 4 at 8:53
  • I would add another example for this. In this scenario when I change the 127.1.1.1 dst address to 1.1.1.1 which is also assigned to lo and because routing protocols populated the routing tables this udp packet will arrive properly. So it arrives on ethX which has IP 10.10.10.5 assigned, then it is routed to 1.1.1.1 which assigned to lo and then the nc receives the packet.
    – zskr
    Mar 4 at 8:55

You must log in to answer this question.

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