--- 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