diff options
author | Jon Harrison <jon.harrison@metaswitch.com> | 2019-07-09 16:48:07 +0100 |
---|---|---|
committer | Sami Kerola <kerolasa@iki.fi> | 2019-07-14 15:05:39 +0100 |
commit | 9e08707d743b29e853df81bd7def1729e3afe55d (patch) | |
tree | 4360bfaf3d223f8f53f5bc5a6e1cc2f0f6dcb329 | |
parent | f3aeb4567fc9b92deb16875b88b44ba0c7959268 (diff) | |
download | iputils-9e08707d743b29e853df81bd7def1729e3afe55d.tar.gz |
ping: allow user to specify VRF and source IP
Without this, the options for sending a ping in the context of a VRF are
limited.
We can send a ping with a specific source IP address. For example:
ping 10.1.1.3 -I 10.1.1.2
We can send a ping in the context of a Linux VRF. For example:
ping 10.1.1.3 -I vrf_red
However, when pinging in the context of a VRF, Linux does not always choose
a sensible source IP address – the source IP might not belong to the VRF.
As a result, the ping won’t get a response. As a result, we want to be able
to specify both a VRF and a source IP address when initiating a ping. For
example:
ping 10.1.1.3 -I vrf_red -I 10.1.1.2
Ping reads in the command line parameters fine and sets up the 'source' and
'device' variables, but currently ignores the device if the source IP
address is non-zero. This commit adds a branch to ping.c that does the
socket bind to the device even in the case where the source IP is non-zero.
This branch is based on the existing case where source IP is zero, but
simplified a bit because we've already got a source IP address to use.
-rw-r--r-- | doc/ping.xml | 8 | ||||
-rw-r--r-- | ping.c | 32 |
2 files changed, 37 insertions, 3 deletions
diff --git a/doc/ping.xml b/doc/ping.xml index b5cea9c..f567523 100644 --- a/doc/ping.xml +++ b/doc/ping.xml @@ -269,11 +269,15 @@ xml:id="man.ping"> <listitem> <para> <emphasis remap="I">interface</emphasis> is either an - address, or an interface name. If + address, an interface name or a VRF name. If <emphasis remap="I">interface</emphasis> is an address, it sets source address to specified interface address. If - <emphasis remap="I">interface</emphasis> in an interface + <emphasis remap="I">interface</emphasis> is an interface name, it sets source interface to specified interface. + If <emphasis remap="I">interface</emphasis> is a VRF + name, each packet is routed using the corresponding + routing table; in this case, the <option>-I</option> option + can be repeated to specify a source address. NOTE: For IPv6, when doing ping to a link-local scope address, link specification (by the '%'-notation in <emphasis remap="I">destination</emphasis>, or by this @@ -663,7 +663,37 @@ int ping4_run(int argc, char **argv, struct addrinfo *ai, socket_st *sock) error(0, 0, _("Warning: source address might be selected on device other than: %s"), device); } close(probe_fd); - } while (0); + + } else if (device) { + struct sockaddr_in dst = whereto; + struct ifreq ifr; + int fd = sock->fd; + int rc; + int errno_save; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, device, IFNAMSIZ - 1); + + enable_capability_raw(); + rc = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device) + 1); + errno_save = errno; + disable_capability_raw(); + + if (rc == -1) { + if (IN_MULTICAST(ntohl(dst.sin_addr.s_addr))) { + struct ip_mreqn imr; + + if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) + error(2, 0, "%s: %s", _("unknown interface"), device); + memset(&imr, 0, sizeof(imr)); + imr.imr_ifindex = ifr.ifr_ifindex; + if (setsockopt(fd, SOL_IP, IP_MULTICAST_IF, + &imr, sizeof(imr)) == -1) + error(2, errno, "IP_MULTICAST_IF"); + } else + error(2, errno_save, "SO_BINDTODEVICE %s", device); + } + } if (whereto.sin_addr.s_addr == 0) whereto.sin_addr.s_addr = source.sin_addr.s_addr; |