Page MenuHomeFreeBSD

ping6: fix "connect() ssend: Network is unreachable" errors
Needs ReviewPublic

Authored by takahiro.kurosawa_gmail.com on Jun 11 2022, 7:27 AM.
Tags
None
Referenced Files
Unknown Object (File)
Sat, Dec 21, 5:17 AM
Unknown Object (File)
Dec 6 2024, 3:42 AM
Unknown Object (File)
Nov 21 2024, 6:36 AM
Unknown Object (File)
Nov 17 2024, 1:06 PM
Unknown Object (File)
Oct 29 2024, 3:17 PM
Unknown Object (File)
Oct 19 2024, 2:30 AM
Unknown Object (File)
Oct 10 2024, 11:09 AM
Unknown Object (File)
Oct 2 2024, 11:05 AM

Details

Reviewers
asomers
bz
ae
melifaro
Group Reviewers
network
Summary

"ping -6 -N -I iface hostname" had not work because of the
"connect() ssend: Network is unreachable" error.
The connect() call fails because sin6_scope_id of the destionation
address is zero whlie the address scope is link local.
To fix the problem, give the interface index to the socket before
connect by setsockopt(IPV6_PKTINFO) as the sending interface is
specified by the -I option.
This also enables ping to the unicast link-local address without
the zone index (using the percent sign) but the sending interface
is specified with -I.

Test Plan

ping -6 -N -I lo0 hostname

Diff Detail

Repository
rS FreeBSD src repository - subversion
Lint
Lint Passed
Unit
No Test Coverage
Build Status
Buildable 45941
Build 42829: arc lint + arc unit

Event Timeline

I think the patch is trying to accomplish a useful improvement to ping(6). It certainly makes sense to not require the user to specify the interface to use a second time.

I'm less sure about the implementation though. I'll immediately add that I'm not familiar with the ping6 code, so I might be missing things, but it looks a lot like there's already code there to try to accomplish the listed goal. The /* set the outgoing interface */ code block seems to do so. It also seems like this would work too, and wouldn't duplicate a setsockopt() call:

diff --git a/sbin/ping/ping6.c b/sbin/ping/ping6.c
index e780129c928d..54707c3b365c 100644
--- a/sbin/ping/ping6.c
+++ b/sbin/ping/ping6.c
@@ -949,6 +949,8 @@ ping6(int argc, char *argv[])
                /* pktinfo must have already been allocated */
                if ((pktinfo.ipi6_ifindex = if_nametoindex(ifname)) == 0)
                        errx(1, "%s: invalid interface name", ifname);
+               if (dst.sin6_scope_id == 0)
+                       dst.sin6_scope_id = pktinfo.ipi6_ifindex;
 #else
                if ((dst.sin6_scope_id = if_nametoindex(ifname)) == 0)
                        errx(1, "%s: invalid interface name", ifname);

@kp Thanks for the comment.
I don't know the detail, but the "NOTES" in ping.c states:

* USE_SIN6_SCOPE_ID assumes that sin6_scope_id has the same semantics
* as IPV6_PKTINFO.  Some people object it (sin6_scope_id specifies *link*
* while IPV6_PKTINFO specifies *interface*.  Link is defined as collection of
* network attached to 1 or more interfaces)

It seems that USE_SIN6_SCOPE_ID is not defined now, so according to this note we should avoid assigning an interface index to sockaddr_in6.sin6_scope_id. Probably the code had been written keeping implementation independent. But I'm not sure that keeping ping6.c implementation independent is still important.

@kp Thanks for the comment.
I don't know the detail, but the "NOTES" in ping.c states:

* USE_SIN6_SCOPE_ID assumes that sin6_scope_id has the same semantics
* as IPV6_PKTINFO.  Some people object it (sin6_scope_id specifies *link*
* while IPV6_PKTINFO specifies *interface*.  Link is defined as collection of
* network attached to 1 or more interfaces)

It seems that USE_SIN6_SCOPE_ID is not defined now, so according to this note we should avoid assigning an interface index to sockaddr_in6.sin6_scope_id. Probably the code had been written keeping implementation independent. But I'm not sure that keeping ping6.c implementation independent is still important.

I'm not familiar with the history of our sbin/ping code. Perhaps Alan knows more.

Looking at the kernel, in sys/netinet6/scope6.c we have in6_getscopezone(), which does use the interface index as scope, but only for interface local and link local addresses.
We should perhaps only do dst.sin6_scope_id = pktinfo.ipi6_ifindex if the address is link or interface local.

I'll copy Bjoern on this, because his IPv6 mojo is stronger than mine.