Here is an attempt to fix ping(8) and traceroute(8) source selection.

Currently these tools do not obey route sourceaddr set by the operator. This
leads to frustration at best and erroneous diagnosis at worse on multi-homed
systems.

The "real" fix would be to rework source selection in the kernel stack but this
is a huge work which not happen overnight nor in the coming days.

In the mean time, I propose the following diff.

I removed -R (route recording) in ping(8) because it is not compatible with
sending a full IP header to the rip_output(). It should not impact anyone as RR
is most of the time ignored by routers.

Denis

Index: sbin/ping/ping.c
===================================================================
RCS file: /cvs/src/sbin/ping/ping.c,v
retrieving revision 1.245
diff -u -p -r1.245 ping.c
--- sbin/ping/ping.c    12 Jul 2021 15:09:19 -0000      1.245
+++ sbin/ping/ping.c    17 Dec 2021 20:27:31 -0000
@@ -143,16 +143,14 @@ int options;
 #define        F_HOSTNAME      0x0004
 #define        F_PINGFILLED    0x0008
 #define        F_QUIET         0x0010
-#define        F_RROUTE        0x0020
-#define        F_SO_DEBUG      0x0040
-#define        F_SHOWCHAR      0x0080
-#define        F_VERBOSE       0x0100
+#define        F_SO_DEBUG      0x0020
+#define        F_SHOWCHAR      0x0040
+#define        F_VERBOSE       0x0080
 /*                     0x0200 */
-#define        F_HDRINCL       0x0400
-#define        F_TTL           0x0800
-#define        F_TOS           0x1000
-#define        F_AUD_RECV      0x2000
-#define        F_AUD_MISS      0x4000
+#define        F_TTL           0x0100
+#define        F_TOS           0x0200
+#define        F_AUD_RECV      0x0400
+#define        F_AUD_MISS      0x0800
 
 /* multicast options */
 int moptions;
@@ -256,7 +254,6 @@ main(int argc, char *argv[])
        u_char *datap, *packet;
        u_char ttl = MAXTTL;
        char *e, *target, hbuf[NI_MAXHOST], *source = NULL;
-       char rspace[3 + 4 * NROUTES + 1];       /* record route space */
        const char *errstr;
        double fraction, integral, seconds;
        uid_t ouid, uid;
@@ -308,7 +305,6 @@ main(int argc, char *argv[])
                                    errstr, optarg);
                        break;
                case 'D':
-                       options |= F_HDRINCL;
                        df = 1;
                        break;
                case 'd':
@@ -383,7 +379,7 @@ main(int argc, char *argv[])
                        options |= F_QUIET;
                        break;
                case 'R':
-                       options |= F_RROUTE;
+                       printf("-R option is not supported anymore\n");
                        break;
                case 's':               /* size of packet to send */
                        datalen = strtonum(optarg, 0, maxpayload, &errstr);
@@ -393,7 +389,6 @@ main(int argc, char *argv[])
                        break;
 #ifndef SMALL
                case 'T':
-                       options |= F_HDRINCL;
                        options |= F_TOS;
                        errno = 0;
                        errstr = NULL;
@@ -509,7 +504,7 @@ main(int argc, char *argv[])
                        if (bind(s, from, from->sa_len) == -1)
                                err(1, "bind");
                }
-       } else if (options & F_VERBOSE) {
+       } else {
                /*
                 * get the source address. XXX since we revoked the root
                 * privilege, we cannot use a raw socket for this.
@@ -711,51 +706,26 @@ main(int argc, char *argv[])
                        err(1, "setsockopt(IPV6_RECVHOPLIMIT)");
        } else {
                u_char loop = 0;
+               struct ip *ip = (struct ip *)outpackhdr;
 
                if (options & F_TTL) {
                        if (IN_MULTICAST(ntohl(dst4.sin_addr.s_addr)))
                                moptions |= MULTICAST_TTL;
-                       else
-                               options |= F_HDRINCL;
                }
 
-               if ((options & F_RROUTE) && (options & F_HDRINCL))
-                       errx(1, "-R option and -D or -T, or -t to unicast"
-                           " destinations are incompatible");
-
-               if (options & F_HDRINCL) {
-                       struct ip *ip = (struct ip *)outpackhdr;
-
-                       if (setsockopt(s, IPPROTO_IP, IP_HDRINCL,
-                           &optval, sizeof(optval)) == -1)
-                               err(1, "setsockopt(IP_HDRINCL)");
-                       ip->ip_v = IPVERSION;
-                       ip->ip_hl = sizeof(struct ip) >> 2;
-                       ip->ip_tos = tos;
-                       ip->ip_id = 0;
-                       ip->ip_off = htons(df ? IP_DF : 0);
-                       ip->ip_ttl = ttl;
-                       ip->ip_p = IPPROTO_ICMP;
-                       if (source)
-                               ip->ip_src = from4.sin_addr;
-                       else
-                               ip->ip_src.s_addr = INADDR_ANY;
-                       ip->ip_dst = dst4.sin_addr;
-               }
+               if (setsockopt(s, IPPROTO_IP, IP_HDRINCL,
+                   &optval, sizeof(optval)) == -1)
+                       err(1, "setsockopt(IP_HDRINCL)");
 
-               /* record route option */
-               if (options & F_RROUTE) {
-                       if (IN_MULTICAST(ntohl(dst4.sin_addr.s_addr)))
-                               errx(1, "record route not valid to multicast"
-                                   " destinations");
-                       memset(rspace, 0, sizeof(rspace));
-                       rspace[IPOPT_OPTVAL] = IPOPT_RR;
-                       rspace[IPOPT_OLEN] = sizeof(rspace)-1;
-                       rspace[IPOPT_OFFSET] = IPOPT_MINOFF;
-                       if (setsockopt(s, IPPROTO_IP, IP_OPTIONS,
-                           rspace, sizeof(rspace)) == -1)
-                               err(1, "record route");
-               }
+               ip->ip_v = IPVERSION;
+               ip->ip_hl = sizeof(struct ip) >> 2;
+               ip->ip_tos = tos;
+               ip->ip_id = 0;
+               ip->ip_off = htons(df ? IP_DF : 0);
+               ip->ip_ttl = ttl;
+               ip->ip_p = IPPROTO_ICMP;
+               ip->ip_src = from4.sin_addr;
+               ip->ip_dst = dst4.sin_addr;
 
                if ((moptions & MULTICAST_NOLOOP) &&
                    setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP,
@@ -1166,14 +1136,12 @@ pinger(int s)
                /* compute ICMP checksum here */
                icp->icmp_cksum = in_cksum((u_short *)icp, cc);
 
-               if (options & F_HDRINCL) {
-                       struct ip *ip = (struct ip *)outpackhdr;
+               struct ip *ip = (struct ip *)outpackhdr;
 
-                       smsgiov.iov_base = (caddr_t)outpackhdr;
-                       cc += sizeof(struct ip);
-                       ip->ip_len = htons(cc);
-                       ip->ip_sum = in_cksum((u_short *)outpackhdr, cc);
-               }
+               smsgiov.iov_base = (caddr_t)outpackhdr;
+               cc += sizeof(struct ip);
+               ip->ip_len = htons(cc);
+               ip->ip_sum = in_cksum((u_short *)outpackhdr, cc);
        }
 
        smsgiov.iov_len = cc;
Index: usr.sbin/traceroute/traceroute.c
===================================================================
RCS file: /cvs/src/usr.sbin/traceroute/traceroute.c,v
retrieving revision 1.168
diff -u -p -r1.168 traceroute.c
--- usr.sbin/traceroute/traceroute.c    3 Sep 2021 09:13:00 -0000       1.168
+++ usr.sbin/traceroute/traceroute.c    17 Dec 2021 20:27:32 -0000
@@ -710,12 +710,14 @@ main(int argc, char *argv[])
                     &on, sizeof(on)) == -1)
                        err(6, "IP_HDRINCL");
 
+               /*
+                * Source selection
+                */
+               memset(&from4, 0, sizeof(from4));
                if (conf->source) {
-                       memset(&from4, 0, sizeof(from4));
                        from4.sin_family = AF_INET;
                        if (inet_aton(conf->source, &from4.sin_addr) == 0)
                                errx(1, "unknown host %s", conf->source);
-                       ip->ip_src = from4.sin_addr;
                        if (ouid != 0 &&
                            (ntohl(from4.sin_addr.s_addr) & 0xff000000U) ==
                            0x7f000000U && (ntohl(to4.sin_addr.s_addr) &
@@ -725,7 +727,29 @@ main(int argc, char *argv[])
                        if (ouid && bind(sndsock, (struct sockaddr *)&from4,
                            sizeof(from4)) == -1)
                                err(1, "bind");
+               } else {
+                       struct sockaddr_in sin;
+                       int dummy;
+
+                       sin = to4;
+                       sin.sin_port = htons(DUMMY_PORT);
+                       if ((dummy = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+                               err(1, "socket");
+                       if (conf->rtableid > 0 &&
+                           setsockopt(dummy, SOL_SOCKET, SO_RTABLE,
+                           &conf->rtableid, sizeof(conf->rtableid)) == -1)
+                               err(1, "setsockopt(SO_RTABLE)");
+                       if (connect(dummy, (struct sockaddr *)&sin,
+                           sin.sin_len) == -1)
+                               err(1, "connect");
+                       len = sizeof(from4);
+                       if (getsockname(dummy, (struct sockaddr *)&from4,
+                           &len) == -1)
+                               err(1, "getsockname");
+                       close(dummy);
                }
+               ip->ip_src = from4.sin_addr;
+
                packetlen = datalen;
                break;
        case AF_INET6:

Reply via email to