traviscross / mtr

Official repository for mtr, a network diagnostic tool
http://www.bitwizard.nl/mtr/
GNU General Public License v2.0
2.7k stars 341 forks source link

Option -a/--address does not work. #250

Open bd46 opened 6 years ago

bd46 commented 6 years ago

Option "-a|--address" does not work in package "mtr-tiny" (build without X11) on Debian Linux, version 0.92-1. Running under strace shows that program does NOT call bind(2).

Breif examination of source code from Github reveals that -a parameter value is assigned to ctl->InterfaceAddress, then is validated in net.c::net_validate_interface_address(), and never used later. Probably some piece of code was lost.

In previous mtr versions option "-a" worked as expected.

bd46 commented 6 years ago

After bug submission I found the same bug as #232, sorry for duplicaion. Testcase is quite simple: multihomed host (several interfaces with different addresses), mtr sent packets via default route regardless of -a option.

rkandilarov commented 6 years ago

Just to confirm that I also came in practice to the same bug -a source_IP not working with ICMP, it sends the packets with the proper source_IP but trough the default interface (gateway) not the one associated with the provided source IP. With --tcp it works as expected. Ubuntu 18.04

mtr --version 
mtr 0.92
rewolff commented 6 years ago

Test case. Normal PC, ifconfig eth0:1 <a second IP addres> Now ping -I <second ip adress> a host and see that the second IP address is being used. Now mtr -a <second ip address> that host and see that the second IP address is being used.

The -I on "ping" hints at using an INTERFACE as the source, but I don't think that can be done with the networking-interface on Unix-like machines. the -a option on mtr explains better what it does: The source address is set. The kernel then often decides to use the interface that you meant to use, but if it doesn't... well mtr can't do much about that.

When mtr submits packet FROM your second interface IP address, TO "random IP on the internet", you still expect the routing tables to work and tell that packet where to go. As an example: If you ping-or-mtr a host not on your local network, you expect the kernel to look up how to reach that host and often then use the default route to try to reach that host, right?

bd46 commented 6 years ago

On Thu, Aug 02, 2018 at 11:36:19PM -0700, Roger Wolff wrote:

Test case. Normal PC, ifconfig eth0:1 Now ping -I a host and see that the second IP address is being used. Now mtr -a that host and see that the second IP address is being used.

The -I on "ping" hints at using an INTERFACE as the source, but I don't think that can be done with the networking-interface on Unix-like machines. the -a option on mtr explains better what it does: The source address is set. The kernel then often decides to use the interface that you meant to use, but if it doesn't... well mtr can't do much about that.

To set ip-address of source packets application MUST(!) do bind(2) syscall. For check run "strace -e bind -o mtr.trace mtr -a " and look into resulting mtr.trace, you will see that mtr does not call bind().

It's a bug. And it's practice, not abstract theory. Ping works fine. -- Eugene Berdnikov

rkandilarov commented 6 years ago

@rewolff , on the same machine ping -I source_IP provides the expected result - it works! As I said in the previous post, the bug is when the source_IP is not on the interface of default gw for that scope. We have tested it even without any network namespaces and VRFs or ip rules...

cerberek commented 4 years ago

I have same issue with mtr package in version 0.92-2 on Debian buster.

rewolff commented 4 years ago

I have simple machines. If any of you can provide me a "howto reproduce" that I can run to reproduce this, then the chances that I can look into it improve dramatically.

bd46 commented 4 years ago

On Wed, Jan 15, 2020 at 09:33:04AM -0800, Roger Wolff wrote:

I have simple machines. If any of you can provide me a "howto reproduce" that I can run to reproduce this, then the chances that I can look into it improve dramatically.

What do you mean by "simple machines"? Machines with one (single) network interface except loopback? You can't reproduce problem with one interface. The point is: if you have several interfaces to destination with different addresses, then wrong interface may be chosen.

Manual page told:

   -a ADDRESS, --address ADDRESS
          Use this option to bind the outgoing socket to ADDRESS, so  that
          all  packets  will be sent with ADDRESS as source address.  NOTE
          that this option doesn't apply to DNS requests (which  could  be
          and could not be what you want).

This is a lie: mtr do not bind outgoing socket to this address. Really, it does not call bind(2) at all, that can be easily proved with strace. There no invocation of bind() in the source code!

Mtr uses raw socket and tries to feed kernel with packet having "-a" value as its source address field in ip header. Strace shows:

9580 socket(AF_INET, SOCK_RAW, IPPROTO_RAW) = 3 9580 setsockopt(3, SOL_IP, IP_HDRINCL, [1], 4) = 0

then something like that:

9580 read(0, "33000 send-probe ip-4 192.168.16.47 local-ip-4 192.168.30.1 protocol icmp size 64 bit-pattern 0 tos 0 ttl 1 timeout 10\n", 4095) = 119 9580 getpid() = 9580 9580 sendto(3, "E\0\0@\0\0\0\0\1\1\0\0\300\250\36\1\300\250\20/\10\0Q\253%l\200\350\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 64, 0, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("192.168.16.47")}, 16) = 64

This logic is defective: kernel DOES NOT scan packet ip-headers in order to make route lookup. That's why filling source ip field in ip-4 header is not enouph for correct routing. You must additionally do bind() on socket to the source address, otherwise kernel peeks wrong interface to send. Namely, without bind() it peaks interface corresponding to default route.

How to reproduce on Linux: connect 2 interfaces (say, eth0 and eth1) to some switch, and configure them to one subnet:

ip addr add 192.168.0.2/24 dev eth0 ip rule add pref 2 from 192.168.0.2 lookup 2 ip route add table 2 192.168.0.0/24 dev eth0 sysctl -w net.ipv4.conf.eth0.rp_filter=1 sysctl -w net.ipv4.conf.eth0.arp_filter=1

ip addr add 192.168.0.3/24 dev eth1 ip rule add pref 3 from 192.168.0.3 lookup 3 ip route add table 3 192.168.0.0/24 dev eth1 sysctl -w net.ipv4.conf.eth1.rp_filter=1 sysctl -w net.ipv4.conf.eth1.arp_filter=1

Plug a target host with ip=192.168.0.1 (mask /24) to the same switch. Check that depending on source ip-address ping sends packets via right interface and get replies on the same interface:

ping -n -I 192.168.0.2 192.168.0.1 # use eth0 ping -n -I 192.168.0.3 192.168.0.1 # use eth1

It can be watched by interfaces packet counters on host and switch, or by blinking leds, or by tcpdump, or even by disconnecting cables.

Then run mtr:

mtr -n -a 192.168.0.2 192.168.0.1 mtr -n -a 192.168.0.3 192.168.0.1

You should see that mtr in both cases use one interface. However, old versions of mtr worked right. -- Eugene Berdnikov

davidjmemmett commented 4 years ago

Hi,

This issue is still present in latest release 0.93. Neither -I or -a change the source address of a packet.

Cheers, David

totoCZ commented 4 years ago

A raw socket can be bound to a specific local address using the bind(2) call. If it isn't bound, all packets with the specified IP protocol are received. In addition, a RAW socket can be bound to a specific network device using SO_BINDTODEVICE; see socket(7).

One way of fixing it. One small problem is, you need to find out the device name first.

bd46 commented 4 years ago

On Thu, Sep 24, 2020 at 11:08:46AM -0700, Tom Hetmer wrote:

 A raw socket can be bound to a specific local address using the bind(2)
 call. If it isn't bound, all packets with the specified IP protocol are
 received. In addition, a RAW socket can be bound to a specific network
 device using SO_BINDTODEVICE; see socket(7).

One way of fixing it. One small problem is, you need to find out the device name first.

It's not a problem but small task to get device name from CLI.

There is already a command line option to get bind address, so 1st variant "bind to IP addr" is even simpler. It might be good to implement both. -- Eugene Berdnikov

totoCZ commented 4 years ago

Please check commit https://github.com/TomHetmer/mtr/commit/19a1fbaf4efc232c4f4a29067297cf5c93fb2794 I rewrote the v4 sockets to get rid of IPPROTO_RAW.

cc @rewolff i'll do a pull request if it's okay.

rewolff commented 4 years ago

The "HDR_INCL" define has completely vanished. Could it be that this is going to be a problem on some machines that you did not test?

totoCZ commented 4 years ago
 >Linux doesn't require this, but BSD derived network stacks do.

Any idea what those machines are?

Would be weird since it's copied from the IPv6 code and that one doesn't use IPV6_HDRINCL. Runs fine on Linux and OS X, anything else I don't have.

Best Tom Hetmer

On Fri, Sep 25, 2020 at 11:54 AM Roger Wolff notifications@github.com wrote:

The "HDR_INCL" define has completely vanished. Could it be that this is going to be a problem on some machines that you did not test?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/traviscross/mtr/issues/250#issuecomment-698838537, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABIR5GUZREMZP2BTVN7XTRTSHRSD5ANCNFSM4EYKLEQA .

douniwan5788 commented 2 years ago

sigh…… still broken in 0.93

tapionx commented 2 years ago

any updates on this?

bitroniq commented 2 years ago

I was able to reproduce it using

# dpkg -l | grep mtr
ii  mtr-tiny                              0.93-1 

on Ubuntu 20.04

bitroniq commented 2 years ago

mtr-tiny 0.93-1 on Ubuntu 20.04 also doesn't work properly using

mtr -I <IFACE> 1.1.1.1 It ignores both -I and -a

shakibamoshiri commented 1 year ago

mtr 0.95 on Arch does not work properly

nickyang777 commented 1 year ago

The configuration of my environment is as follows: [root@mtr ~]# mtr --version mtr 0.95

[root@mtr ~]# ip rule show 0: from all lookup local 32765: from 99.99.23.6 lookup net_252 32766: from all lookup main 32767: from all lookup default

[root@mtr ~]# ip route show default via 192.168.0.0 dev eth0 proto dhcp metric 100 99.99.23.0/24 dev eth1 proto kernel scope link src 99.99.23.6 192.168.0.0/16 dev eth0 proto kernel scope link src 192.168.. metric 100

[root@mtr ~]# ip route show table net_252 default dev eth1 scope link

[root@mtr ~]# mtr -r -n -a 99.99.23.6 1.1.1.1

Use tcpdump to capture packets. It is found that packets are not sent from eth1 but from eth0. [root@mtr ~]# tcpdump -n -i eth0 icmp dropped privs to tcpdump tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes 11:21:34.060252 IP 99.99.23.6 > 1.1.1.1: ICMP echo request, id 7550, seq 33000, length 44 11:21:34.160450 IP 99.99.23.6 > 1.1.1.1: ICMP echo request, id 7550, seq 33001, length 44 11:21:34.260649 IP 99.99.23.6 > 1.1.1.1: ICMP echo request, id 7550, seq 33002, length 44 11:21:34.360844 IP 99.99.23.6 > 1.1.1.1: ICMP echo request, id 7550, seq 33003, length 44 11:21:34.461050 IP 99.99.23.6 > 1.1.1.1: ICMP echo request, id 7550, seq 33004, length 44 11:21:34.561246 IP 99.99.23.6 > 1.1.1.1: ICMP echo request, id 7550, seq 33005, length 44 11:21:34.661405 IP 99.99.23.6 > 1.1.1.1: ICMP echo request, id 7550, seq 33006, length 44 11:21:34.761648 IP 99.99.23.6 > 1.1.1.1: ICMP echo request, id 7550, seq 33007, length 44 11:21:34.861836 IP 99.99.23.6 > 1.1.1.1: ICMP echo request, id 7550, seq 33008, length 44 11:21:34.962096 IP 99.99.23.6 > 1.1.1.1: ICMP echo request, id 7550, seq 33009, length 44 11:21:35.062263 IP 99.99.23.6 > 1.1.1.1: ICMP echo request, id 7550, seq 33010, length 44 11:21:35.162424 IP 99.99.23.6 > 1.1.1.1: ICMP echo request, id 7550, seq 33011, length 44 11:21:35.262651 IP 99.99.23.6 > 1.1.1.1: ICMP echo request, id 7550, seq 33012, length 44

Do you have any plans to repair it?

Amoled commented 11 months ago

I have the same problem, it looks like no one is going to solve it ....

matt-kimball commented 11 months ago

My impression was that 7a0320038a64594c138e69b1200c30428bd1c75c fixed this issue. Instead of reporting issues about the mtr included with your distro of choice, perhaps some of the people submitting complaints to this thread could try the latest from master and comment on whether or not it is indeed fixed when building from that commit, or more recent.

bd46 commented 11 months ago

On Mon, Nov 20, 2023 at 04:16:22PM -0800, Matt Kimball wrote:

My impression was that 7a03200 fixed this issue.

My impression is that this patch has relation to "-I" option, while subject mentions "-a". Binding to source IP address and binding to interface (SO_BINDTODEVICE) are different things.

References

Visible links

  1. https://github.com/traviscross/mtr/commit/7a0320038a64594c138e69b1200c30428bd1c75c
  2. https://github.com/traviscross/mtr/issues/250#issuecomment-1820012990

-- Eugene Berdnikov

davidjmemmett commented 11 months ago

That fix was committed on 5th August 2022, however the last release of mtr was v0.95 on 11th January 2022; therefore, the suggested fix has not made its way into any release as far as I can see.

putnam commented 10 months ago

Why not cut a release? It's been 2 years since 0.95. (Same problem with 0.95 in current OPNsense)

xqzr commented 9 months ago

-a or -I and -M work

robert-scheck commented 8 months ago

Based on my tests --address and --interface in MTR 0.95 do not work (anymore) when used with source/policy based routing, such as two internet upstreams ("two default gateways"). For me it starts working for both when switching from ICMP to TCP using --tcp.

Edit: MTR 0.95 with commit 74d312d (from #484) additionally applied works for me in the above scenario on Linux (without the workaround to switch from ICMP to TCP).