---
 networking/ping.c | 212 +++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 180 insertions(+), 32 deletions(-)

diff --git a/networking/ping.c b/networking/ping.c
index b7e6955a9..b2ff92b82 100644
--- a/networking/ping.c
+++ b/networking/ping.c
@@ -46,6 +46,16 @@
 //config:      With this option off, ping will say "HOST is alive!"
 //config:      or terminate with SIGALRM in 5 seconds otherwise.
 //config:      No command-line options will be recognized.
+//config:
+//config:config FEATURE_PING_NONROOT
+//config:    bool "Enable ping without root privileges"
+//config:    default n
+//config:    depends on PING || PING6
+//config:    help
+//config:    When enabled, ping will attempt to use SOCK_DGRAM instead of 
SOCK_RAW
+//config:    if root privileges are unavailable. This allows ping to work for
+//config:    non-root users on systems configured to permit it (e.g., Linux 
with
+//config:    net.ipv4.ping_group_range set). When disabled, ping requires root.
 
 /* Needs socket(AF_INET, SOCK_RAW, IPPROTO_ICMP), therefore BB_SUID_MAYBE: */
 //applet:IF_PING(APPLET(ping, BB_DIR_BIN, BB_SUID_MAYBE))
@@ -208,6 +218,10 @@ enum {
        pingsock = 0,
 };
 
+#if ENABLE_FEATURE_PING_NONROOT
+static int using_dgram;
+#endif
+
 static void
 #if ENABLE_PING6
 create_icmp_socket(len_and_sockaddr *lsa)
@@ -224,14 +238,57 @@ create_icmp_socket(void)
 #endif
                sock = socket(AF_INET, SOCK_RAW, 1); /* 1 == ICMP */
        if (sock < 0) {
-               if (errno == EPERM)
-                       
bb_simple_error_msg_and_die(bb_msg_perm_denied_are_you_root);
+        if (errno != EPERM)
                bb_simple_perror_msg_and_die(bb_msg_can_not_create_raw_socket);
+#if ENABLE_FEATURE_PING_NONROOT
+#if defined(__linux__) || defined(__APPLE__)
+        /* We don't have root privileges. Try SOCK_DGRAM instead.
+         * Linux needs net.ipv4.ping_group_range for this to work.
+         * MacOSX allows ICMP_ECHO, ICMP_TSTAMP or ICMP_MASKREQ
+         */
+
+       using_dgram = 1;
+
+#if ENABLE_PING6
+        if (lsa->u.sa.sa_family == AF_INET6)
+            sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
+        else
+#endif
+            sock = socket(AF_INET, SOCK_DGRAM, 1); /* 1 == ICMP */
+
+       if (sock < 0)
+#endif /* defined(__linux__) || defined(__APPLE__) */
+#endif /* ENABLE_FEATURE_PING_NONROOT */
+            bb_simple_error_msg_and_die(bb_msg_perm_denied_are_you_root);
        }
 
        xmove_fd(sock, pingsock);
 }
 
+#if ENABLE_FEATURE_PING_NONROOT
+static int get_source_port(int fd) {
+       len_and_sockaddr *my_lsa;
+       int port;
+       /* we need the correct address family, so get it from the
+       * already created socket here */
+       my_lsa = get_sock_lsa(fd);
+       if (my_lsa == NULL)
+               bb_simple_perror_msg_and_die("getsockname");
+       if ( (port = get_nport(&my_lsa->u.sa) == -1 ) )
+               bb_simple_perror_msg_and_die("get port");
+       if ( !port ) {
+               /* socket is not yet bound (happens in simple version)
+               * bind it explicitly so we can get assigned a source port */
+               xbind(fd, &my_lsa->u.sa, my_lsa->len);
+               getsockname(fd, &my_lsa->u.sa, &my_lsa->len );
+       }
+       port = get_nport(&my_lsa->u.sa);
+       if (ENABLE_FEATURE_CLEAN_UP)
+               free(my_lsa);
+       return port;
+}
+#endif
+
 #if !ENABLE_FEATURE_FANCY_PING
 
 /* Simple version */
@@ -279,15 +336,25 @@ static void ping4(len_and_sockaddr *lsa)
                                bb_simple_perror_msg("recvfrom");
                        continue;
                }
-               if (c >= 76) {                  /* ip + icmp */
-                       struct iphdr *iphdr = (struct iphdr *) G.packet;
 
-                       pkt = (struct icmp *) (G.packet + (iphdr->ihl << 2));   
/* skip ip hdr */
-                       if (pkt->icmp_id != G.myid)
-                               continue; /* not our ping */
-                       if (pkt->icmp_type == ICMP_ECHOREPLY)
-                               break;
+#if ENABLE_FEATURE_PING_NONROOT
+               /* no IP header, just 8 bytes ICMP header + 56 bytes of payload 
*/
+               if (using_dgram && c == DEFDATALEN + ICMP_MINLEN ) {
+                       pkt = (struct icmp *) G.packet;
+               } else
+#endif
+               if (c >= 76) {  /* ip + icmp */
+                       struct iphdr *iphdr = (struct iphdr *) G.packet;
+                       /* skip ip hdr */
+                       pkt = (struct icmp *) (G.packet + (iphdr->ihl << 2));
+               } else {
+                       continue; /* packet is too short */
                }
+
+               if (pkt->icmp_id != G.myid)
+                       continue; /* not our ping */
+               if (pkt->icmp_type == ICMP_ECHOREPLY)
+                       break;
        }
 }
 
@@ -373,12 +440,21 @@ static int common_ping_main(sa_family_t af, char **argv)
        alarm(5); /* give the host 5000ms to respond */
 
        create_icmp_socket(lsa);
-       G.myid = (uint16_t) getpid();
-       /* we can use native-endian ident, but other Unix ping/traceroute
-        * utils use *big-endian pid*, and e.g. traceroute on our machine may be
-        * *not* from busybox, idents may collide. Follow the convention:
-        */
-       G.myid = htons(G.myid);
+
+#if ENABLE_FEATURE_PING_NONROOT
+       if (using_dgram)
+               G.myid = get_source_port(pingsock);
+       else
+#endif
+       {
+               G.myid = (uint16_t) getpid();
+               /* we can use native-endian ident, but other Unix 
ping/traceroute
+                * utils use *big-endian pid*, and e.g. traceroute on our 
machine may be
+                * *not* from busybox, idents may collide. Follow the 
convention:
+                */
+               G.myid = htons(G.myid);
+       }
+
 #if ENABLE_PING6
        if (lsa->u.sa.sa_family == AF_INET6)
                ping6(lsa);
@@ -689,7 +765,11 @@ static void unpack_tail(int sz, uint32_t *tp,
        puts(dupmsg);
        fflush_all();
 }
+#if ENABLE_FEATURE_PING_NONROOT
+static int unpack4(char *buf, int sz, struct sockaddr_in *from, int ttl)
+#else
 static int unpack4(char *buf, int sz, struct sockaddr_in *from)
+#endif
 {
        struct icmp *icmppkt;
        struct iphdr *iphdr;
@@ -699,23 +779,35 @@ static int unpack4(char *buf, int sz, struct sockaddr_in 
*from)
        if (sz < (datalen + ICMP_MINLEN))
                return 0;
 
-       /* check IP header */
-       iphdr = (struct iphdr *) buf;
-       hlen = iphdr->ihl << 2;
-       sz -= hlen;
-       icmppkt = (struct icmp *) (buf + hlen);
+#if ENABLE_FEATURE_PING_NONROOT
+       if (using_dgram) {
+               icmppkt = (struct icmp *) buf;
+       } else
+#endif
+       {
+               /* check IP header */
+               iphdr = (struct iphdr *) buf;
+               hlen = iphdr->ihl << 2;
+               sz -= hlen;
+               icmppkt = (struct icmp *) (buf + hlen);
+       }
+
        if (icmppkt->icmp_id != myid)
                return 0;                               /* not our ping */
 
        if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
                uint16_t recv_seq = ntohs(icmppkt->icmp_seq);
                uint32_t *tp = NULL;
-
                if (sz >= ICMP_MINLEN + sizeof(uint32_t))
                        tp = (uint32_t *) icmppkt->icmp_data;
                unpack_tail(sz, tp,
-                       inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
-                       recv_seq, iphdr->ttl);
+                       inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr), 
recv_seq,
+#if ENABLE_FEATURE_PING_NONROOT
+                       using_dgram ? ttl : iphdr->ttl
+#else
+                       iphdr->ttl
+#endif
+                       );
                return 1;
        }
        if (icmppkt->icmp_type != ICMP_ECHO) {
@@ -763,6 +855,12 @@ static int unpack6(char *packet, int sz, struct 
sockaddr_in6 *from, int hoplimit
 static void ping4(len_and_sockaddr *lsa)
 {
        int sockopt;
+       struct sockaddr_in from;
+#if ENABLE_FEATURE_PING_NONROOT
+       struct msghdr msg;
+       struct iovec iov;
+       char control_buf[CMSG_SPACE(36)];
+#endif
 
        pingaddr.sin = lsa->u.sin;
        if (source_lsa) {
@@ -772,6 +870,14 @@ static void ping4(len_and_sockaddr *lsa)
                xbind(pingsock, &source_lsa->u.sa, source_lsa->len);
        }
 
+#if ENABLE_FEATURE_PING_NONROOT
+       if ( using_dgram ) {
+               myid =  get_source_port(pingsock);
+               if (setsockopt_1(pingsock, IPPROTO_IP, IP_RECVTTL))
+                       bb_simple_error_msg_and_die("setsockopt(IP_RECVTTL)");
+       }
+#endif
+
        /* enable broadcast pings */
        setsockopt_broadcast(pingsock);
 
@@ -789,23 +895,53 @@ static void ping4(len_and_sockaddr *lsa)
        signal(SIGINT, print_stats_and_exit);
 
        /* start the ping's going ... */
+#if ENABLE_FEATURE_PING_NONROOT
+       msg.msg_name = &from;
+       msg.msg_namelen = sizeof(from);
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+       msg.msg_control = control_buf;
+       iov.iov_base = G.rcv_packet;
+       iov.iov_len = G.sizeof_rcv_packet;
+       msg.msg_controllen = sizeof(control_buf);
+#endif
  send_ping:
        sendping4(0);
 
        /* listen for replies */
        while (1) {
-               struct sockaddr_in from;
-               socklen_t fromlen = (socklen_t) sizeof(from);
                int c;
+#if ENABLE_FEATURE_PING_NONROOT
+               struct cmsghdr *mp;
+               int ttl = -1;
+#else
+               socklen_t fromlen = (socklen_t) sizeof(from);
+#endif
 
+#if ENABLE_FEATURE_PING_NONROOT
+               c = recvmsg(pingsock, &msg, 0);
+#else
                c = recvfrom(pingsock, G.rcv_packet, G.sizeof_rcv_packet, 0,
                                (struct sockaddr *) &from, &fromlen);
+#endif
+
                if (c < 0) {
                        if (errno != EINTR)
                                bb_simple_perror_msg("recvfrom");
                        continue;
                }
+#if ENABLE_FEATURE_PING_NONROOT
+               for (mp = CMSG_FIRSTHDR(&msg); mp; mp = CMSG_NXTHDR(&msg, mp)) {
+                       if (mp->cmsg_level == SOL_IP
+                        && mp->cmsg_type == IP_TTL
+                       ) {
+                               move_from_unaligned_int(ttl, CMSG_DATA(mp));
+                       }
+               }
+               c = unpack4(G.rcv_packet, c, &from, ttl);
+#else
                c = unpack4(G.rcv_packet, c, &from);
+#endif
                if (pingcount && G.nreceived >= pingcount)
                        break;
                if (c && (option_mask32 & OPT_A)) {
@@ -826,7 +962,15 @@ static void ping6(len_and_sockaddr *lsa)
        if (source_lsa)
                xbind(pingsock, &source_lsa->u.sa, source_lsa->len);
 
+#if ENABLE_FEATURE_PING_NONROOT
+       if ( using_dgram )
+               myid = get_source_port(pingsock);
+#endif
+
 #ifdef ICMP6_FILTER
+#if ENABLE_FEATURE_PING_NONROOT
+       if (!using_dgram)
+#endif
        {
                struct icmp6_filter filt;
                if (!(option_mask32 & OPT_VERBOSE)) {
@@ -971,13 +1115,17 @@ static int common_ping_main(int opt, char **argv)
        if (interval > INT_MAX/1000000)
                interval = INT_MAX/1000000;
        G.interval_us = interval * 1000000;
-
-       myid = (uint16_t) getpid();
-       /* we can use native-endian ident, but other Unix ping/traceroute
-        * utils use *big-endian pid*, and e.g. traceroute on our machine may be
-        * *not* from busybox, idents may collide. Follow the convention:
-        */
-       myid = htons(myid);
+#if ENABLE_FEATURE_PING_NONROOT
+       if (!using_dgram)
+#endif
+       {
+               myid = (uint16_t) getpid();
+               /* we can use native-endian ident, but other Unix 
ping/traceroute
+                * utils use *big-endian pid*, and e.g. traceroute on our 
machine may be
+                * *not* from busybox, idents may collide. Follow the 
convention:
+                */
+               myid = htons(myid);
+       }
        hostname = argv[optind];
 #if ENABLE_PING6
        {
-- 
2.47.1

_______________________________________________
busybox mailing list
busybox@busybox.net
https://lists.busybox.net/mailman/listinfo/busybox

Reply via email to