diff --git a/sbin/ping/ping.8 b/sbin/ping/ping.8 --- a/sbin/ping/ping.8 +++ b/sbin/ping/ping.8 @@ -28,7 +28,7 @@ .\" @(#)ping.8 8.2 (Berkeley) 12/11/93 .\" $FreeBSD$ .\" -.Dd November 20, 2022 +.Dd January 10, 2023 .Dt PING 8 .Os .Sh NAME @@ -429,6 +429,20 @@ This flag only applies if the ping destination is a multicast address. .It Fl z Ar tos Use the specified type of service. +.Ar tos +may be given as one of +.Ar critical , +.Ar inetcontrol , +.Ar lowdelay , +.Ar netcontrol , +.Ar throughput , +.Ar reliability , +or one of the DiffServ Code Points: +.Ar ef , +.Ar va , +.Ar af11 No ... Ar af43 , +.Ar cs0 No ... Ar cs7 ; +or as either hex or decimal. .It Ar IPv4-host hostname or IPv4 address of the final destination node. .It Ar IPv4-mcast-group diff --git a/sbin/ping/ping.c b/sbin/ping/ping.c --- a/sbin/ping/ping.c +++ b/sbin/ping/ping.c @@ -128,6 +128,11 @@ int32_t tv32_nsec; }; +struct keywords { + const char *k_name; + int k_val; +}; + /* various options */ static int options; #define F_FLOOD 0x0001 @@ -226,6 +231,8 @@ static char *pr_ntime(n_time); static void pr_icmph(struct icmp *, struct ip *, const u_char *const); static void pr_iph(struct ip *); +static int kw_casecmp(const void *, const void *); +static int map_tos(char *string, int *); static void pr_pack(char *, ssize_t, struct sockaddr_in *, struct timespec *); static void pr_retip(struct ip *, const u_char *); static void status(int); @@ -544,8 +551,15 @@ break; case 'z': options |= F_HDRINCL; - ltmp = strtol(optarg, &ep, 0); - if (*ep || ep == optarg || ltmp > MAXTOS || ltmp < 0) + if (map_tos(optarg, &tos)) + break; + if (strlen(optarg) > 1 && optarg[0] == '0' && + optarg[1] == 'x') + ltmp = strtol(optarg, &ep, 16); + else + ltmp = strtonum(optarg, 0, MAXTOS, &errstr); + if (*ep || ep == optarg || ltmp > MAXTOS || ltmp < 0 || + errstr || errno) errx(EX_USAGE, "invalid TOS: `%s'", optarg); tos = ltmp; break; @@ -1737,6 +1751,58 @@ (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); } +static int +kw_casecmp(const void *k, const void *e) +{ + return (strcasecmp(k, ((const struct keywords *)e)->k_name)); +} + +static int +map_tos(char *s, int *val) +{ + /* DiffServ Codepoints and other TOS mappings */ + const struct keywords toswords[] = { + { "af11", IPTOS_DSCP_AF11 }, + { "af12", IPTOS_DSCP_AF12 }, + { "af13", IPTOS_DSCP_AF13 }, + { "af21", IPTOS_DSCP_AF21 }, + { "af22", IPTOS_DSCP_AF22 }, + { "af23", IPTOS_DSCP_AF23 }, + { "af31", IPTOS_DSCP_AF31 }, + { "af32", IPTOS_DSCP_AF32 }, + { "af33", IPTOS_DSCP_AF33 }, + { "af41", IPTOS_DSCP_AF41 }, + { "af42", IPTOS_DSCP_AF42 }, + { "af43", IPTOS_DSCP_AF43 }, + { "critical", IPTOS_PREC_CRITIC_ECP }, + { "cs0", IPTOS_DSCP_CS0 }, + { "cs1", IPTOS_DSCP_CS1 }, + { "cs2", IPTOS_DSCP_CS2 }, + { "cs3", IPTOS_DSCP_CS3 }, + { "cs4", IPTOS_DSCP_CS4 }, + { "cs5", IPTOS_DSCP_CS5 }, + { "cs6", IPTOS_DSCP_CS6 }, + { "cs7", IPTOS_DSCP_CS7 }, + { "ef", IPTOS_DSCP_EF }, + { "inetcontrol", IPTOS_PREC_INTERNETCONTROL }, + { "lowdelay", IPTOS_LOWDELAY }, + { "netcontrol", IPTOS_PREC_NETCONTROL }, + { "reliability", IPTOS_RELIABILITY }, + { "throughput", IPTOS_THROUGHPUT }, + { "va", IPTOS_DSCP_VA }, + }; + const struct keywords *p; + + p = bsearch(s, toswords, nitems(toswords), sizeof(toswords[0]), + kw_casecmp); + + if (p) { + *val = p->k_val; + return (1); + } + return (0); +} + static char * pr_ntime(n_time timestamp) { diff --git a/sbin/ping/tests/test_ping.py b/sbin/ping/tests/test_ping.py --- a/sbin/ping/tests/test_ping.py +++ b/sbin/ping/tests/test_ping.py @@ -698,6 +698,63 @@ }, id="_q_c3_2001_db8__2", ), + pytest.param( + { + "args": "ping -4 -zx localhost", + "returncode": os.EX_USAGE, + "stdout": "", + "stderr": "ping: invalid TOS: `x'\n", + }, + id="_4_zx_localhost", + ), + pytest.param( + { + "args": "ping -4 -c1 -z48 localhost", + "returncode": 0, + "stdout": """\ +PING localhost: 56 data bytes +64 bytes from: icmp_seq=0 ttl= time= ms + +--- localhost ping statistics --- +1 packets transmitted, 1 packets received, 0.0% packet loss +round-trip min/avg/max/stddev = /// ms +""", + "stderr": "", + }, + id="_4_c1_z48_localhost", + ), + pytest.param( + { + "args": "ping -4 -c1 -z0x30 localhost", + "returncode": 0, + "stdout": """\ +PING localhost: 56 data bytes +64 bytes from: icmp_seq=0 ttl= time= ms + +--- localhost ping statistics --- +1 packets transmitted, 1 packets received, 0.0% packet loss +round-trip min/avg/max/stddev = /// ms +""", + "stderr": "", + }, + id="_4_c1_z0x30_localhost", + ), + pytest.param( + { + "args": "ping -4 -c1 -zinetcontrol localhost", + "returncode": 0, + "stdout": """\ +PING localhost: 56 data bytes +64 bytes from: icmp_seq=0 ttl= time= ms + +--- localhost ping statistics --- +1 packets transmitted, 1 packets received, 0.0% packet loss +round-trip min/avg/max/stddev = /// ms +""", + "stderr": "", + }, + id="_4_c1_zinetcontrol_localhost", + ), ] @pytest.mark.parametrize("expected", testdata)