Skip to content

Commit

Permalink
ping: Lower max allowed -s value to 65507 (IPv4) or 65527 (IPv6)
Browse files Browse the repository at this point in the history
Maximum value for ping -s option cannot be higher than maximum IPv4/v6
packet size, which is 65535. More precisely, the value should respect
maximum IPv4/v6 ICMP data length.

Therefore limit:
* ICMP payload: 65507 = 65535 (IPv4 packet size) - 20 (min IPv4 header
  size) - 8 (ICMP header size)
* ICMPv6 payload: 65527 = 65535 (IPv6 packet size) - 8 (ICMPv6 header size)

Use the higher value unless user chose the protocol.

Linux kernel limits IPv4/6 packet size size exactly to these values:

* ICMP datagram socket net/ipv4/ping.c in ping_common_sendmsg() (used in
both ping_v4_sendmsg() and ping_v6_sendmsg()):

    if (len > 0xFFFF)
	    return -EMSGSIZE;

* raw socket IPv4 in raw_sendmsg() in net/ipv4/raw.c:

    err = -EMSGSIZE;
    if (len > 0xFFFF)
	    goto out;

* raw IPv6 socket is limited similarly in __ip6_append_data() in
  include/net/ip6_route.h:

    /* We do not (yet ?) support IPv6 jumbograms (RFC 2675)
    * Unlike IPv4, hdr->seg_len doesn't include the IPv6 header
    */
    #define IP6_MAX_MTU (0xFFFF + sizeof(struct ipv6hdr))

Because Big TCP support for IPv4/IPv6 does does not support raw socket
nor ICMP datagram socket, it would have to be sent as multiple packets
(IPv4 fragmentation, IPv6 next_header packet chain).

Other ping implementations on Linux also limit it to similar values
(Busybox: 65535, fping: 65507, inetutils: IPv4: 65399, IPv6: 65527,
likely kernel limitation, FreeBSD limits on IPv4: 65507).

Closes: iputils#550
Signed-off-by: Petr Vorel <[email protected]>
  • Loading branch information
pevik committed Aug 21, 2024
1 parent 5f91567 commit a50946d
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 7 deletions.
5 changes: 4 additions & 1 deletion doc/ping.xml
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,10 @@ xml:id="man.ping">
<para>Specifies the number of data bytes to be sent. The
default is 56, which translates into 64 ICMP data bytes
when combined with the 8 bytes of ICMP header data.
Maximum allowed value is 127992, but though most systems
The maximum allowed value is 65507 for IPv4
(65467 when <option>-R</option> or <option>-T</option>
or Intermediate <emphasis remap="I">hop</emphasis>s)
or 65527 for IPv6, but most systems
limit this to a smaller, system-dependent number.</para>
</listitem>
</varlistentry>
Expand Down
32 changes: 26 additions & 6 deletions ping/ping.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
#include <ifaddrs.h>
#include <math.h>
#include <locale.h>
#include <sys/param.h>

/* FIXME: global_rts will be removed in future */
struct ping_rts *global_rts;
Expand All @@ -84,6 +85,12 @@ ping_func_set_st ping4_func_set = {
#define NROUTES 9 /* number of record route slots */
#define TOS_MAX 255 /* 8-bit TOS field */

/* max. IPv4 packet size - IPv4 header size - ICMP header size */
#define ICMP_MAX_DATALEN (65535 - 20 - 8)

/* max. IPv6 payload size - ICMPv6 Echo Reply Header */
#define ICMPV6_MAX_DATALEN (65535 - sizeof (struct icmp6_hdr))

#define CASE_TYPE(x) case x: return #x;

static char *str_family(int family)
Expand Down Expand Up @@ -337,6 +344,8 @@ main(int argc, char **argv)
.ni.query = -1,
.ni.subject_type = -1,
};
int max_s = MAX(ICMP_MAX_DATALEN, ICMPV6_MAX_DATALEN);

/* FIXME: global_rts will be removed in future */
global_rts = &rts;

Expand Down Expand Up @@ -527,7 +536,7 @@ main(int argc, char **argv)
rts.opt_so_dontroute = 1;
break;
case 's':
rts.datalen = strtol_or_err(optarg, _("invalid argument"), 0, MAXPACKET - 8);
rts.datalen = strtol_or_err(optarg, _("invalid argument"), 0, max_s);
break;
case 'S':
rts.sndbuf = strtol_or_err(optarg, _("invalid argument"), 1, INT_MAX);
Expand Down Expand Up @@ -614,6 +623,22 @@ main(int argc, char **argv)
hints.ai_family = AF_INET;
}

switch (hints.ai_family) {
case AF_INET:
max_s = ICMP_MAX_DATALEN;
/* rts->optlen */
if (rts.opt_rroute || rts.opt_timestamp || rts.opt_sourceroute)
max_s -= 40;
break;
case AF_INET6:
max_s = ICMPV6_MAX_DATALEN;
break;
}

if (rts.datalen > max_s)
error(EXIT_FAILURE, 0, "invalid -s value: '%d': out of range: %d <= value <= %d",
rts.datalen, 0, max_s);

if (rts.opt_verbose)
error(0, 0, "sock4.fd: %d (socktype: %s), sock6.fd: %d (socktype: %s),"
" hints.ai_family: %s\n",
Expand Down Expand Up @@ -1004,11 +1029,6 @@ int ping4_run(struct ping_rts *rts, int argc, char **argv, struct addrinfo *ai,
error(2, errno, _("cannot set unicast time-to-live"));
}


if (rts->datalen > 0xFFFF - 8 - rts->optlen - 20)
error(2, 0, _("packet size %d is too large. Maximum is %d"),
rts->datalen, 0xFFFF - 8 - 20 - rts->optlen);

if (rts->datalen >= (int)sizeof(struct timeval)) /* can we time transfer */
rts->timing = 1;
packlen = rts->datalen + MAXIPLEN + MAXICMPLEN;
Expand Down

0 comments on commit a50946d

Please sign in to comment.