"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.
Details
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.
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.