Skip to content

Commit 33ea708

Browse files
committed
ICMP: Implemented RFC 8337, Extended Echo Request/Reply
1 parent fa4c956 commit 33ea708

File tree

6 files changed

+262
-5
lines changed

6 files changed

+262
-5
lines changed

configure

+13-1
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,7 @@ infodir
676676
docdir
677677
oldincludedir
678678
includedir
679+
runstatedir
679680
localstatedir
680681
sharedstatedir
681682
sysconfdir
@@ -756,6 +757,7 @@ datadir='${datarootdir}'
756757
sysconfdir='${prefix}/etc'
757758
sharedstatedir='${prefix}/com'
758759
localstatedir='${prefix}/var'
760+
runstatedir='${localstatedir}/run'
759761
includedir='${prefix}/include'
760762
oldincludedir='/usr/include'
761763
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -1008,6 +1010,15 @@ do
10081010
| -silent | --silent | --silen | --sile | --sil)
10091011
silent=yes ;;
10101012

1013+
-runstatedir | --runstatedir | --runstatedi | --runstated \
1014+
| --runstate | --runstat | --runsta | --runst | --runs \
1015+
| --run | --ru | --r)
1016+
ac_prev=runstatedir ;;
1017+
-runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
1018+
| --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
1019+
| --run=* | --ru=* | --r=*)
1020+
runstatedir=$ac_optarg ;;
1021+
10111022
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
10121023
ac_prev=sbindir ;;
10131024
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1145,7 +1156,7 @@ fi
11451156
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
11461157
datadir sysconfdir sharedstatedir localstatedir includedir \
11471158
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
1148-
libdir localedir mandir
1159+
libdir localedir mandir runstatedir
11491160
do
11501161
eval ac_val=\$$ac_var
11511162
# Remove trailing slashes.
@@ -1298,6 +1309,7 @@ Fine tuning of the installation directories:
12981309
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
12991310
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
13001311
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
1312+
--runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
13011313
--libdir=DIR object code libraries [EPREFIX/lib]
13021314
--includedir=DIR C header files [PREFIX/include]
13031315
--oldincludedir=DIR C header files for non-gcc [/usr/include]

print-icmp.c

+233-4
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "udp.h"
3939
#include "ipproto.h"
4040
#include "mpls.h"
41+
#include "af.h"
4142

4243
/*
4344
* Interface Control Message Protocol Definitions.
@@ -56,14 +57,28 @@ struct icmp {
5657
nd_ipv4 ih_gwaddr; /* ICMP_REDIRECT */
5758
struct ih_idseq {
5859
nd_uint16_t icd_id;
59-
nd_uint16_t icd_seq;
60+
union {
61+
nd_uint16_t icd_seq;
62+
struct {
63+
nd_uint8_t icd_seq_ext;
64+
union {
65+
nd_uint8_t icd_local_ext;
66+
struct {
67+
nd_uint8_t icd_state_ext;
68+
} ih_ext_reply_state;
69+
} ih_ext_rest;
70+
} ih_seq_ext;
71+
} ih_seq;
6072
} ih_idseq;
6173
nd_uint32_t ih_void;
6274
} icmp_hun;
6375
#define icmp_pptr icmp_hun.ih_pptr
6476
#define icmp_gwaddr icmp_hun.ih_gwaddr
6577
#define icmp_id icmp_hun.ih_idseq.icd_id
66-
#define icmp_seq icmp_hun.ih_idseq.icd_seq
78+
#define icmp_seq icmp_hun.ih_idseq.ih_seq.icd_seq
79+
#define icmp_seq_ext icmp_hun.ih_idseq.ih_seq.ih_seq_ext.icd_seq_ext
80+
#define icmp_ext_lbit icmp_hun.ih_idseq.ih_seq.ih_seq_ext.ih_ext_rest.icd_local_ext
81+
#define icmp_ext_state icmp_hun.ih_idseq.ih_seq.ih_seq_ext.ih_ext_rest.ih_ext_reply_state.icd_state_ext
6782
#define icmp_void icmp_hun.ih_void
6883
union {
6984
struct id_ts {
@@ -143,8 +158,11 @@ struct icmp {
143158
#define ICMP_IREQREPLY 16 /* information reply */
144159
#define ICMP_MASKREQ 17 /* address mask request */
145160
#define ICMP_MASKREPLY 18 /* address mask reply */
161+
#define ICMP_EXT_ECHO 42 /* IPv4 extended echo request */
162+
#define ICMP_EXT_ECHOREPLY 43 /* IPv4 extended echo reply */
163+
#define ICMP_EXT_ECHO_IPV6 160 /* IPv6 extended echo requesti */
164+
#define ICMP_EXT_ECHOREPLY_IPV6 161 /* IPv6 extended echo reply */
146165

147-
#define ICMP_MAXTYPE 18
148166

149167
#define ICMP_ERRTYPE(type) \
150168
((type) == ICMP_UNREACH || (type) == ICMP_SOURCEQUENCH || \
@@ -220,6 +238,51 @@ struct id_rdiscovery {
220238
nd_uint32_t ird_pref;
221239
};
222240

241+
struct icmp_ext_header {
242+
nd_uint8_t icmp_ext_version;
243+
nd_uint8_t icmp_ext_reserved;
244+
nd_uint16_t icmp_ext_checksum;
245+
};
246+
247+
#define INTERFACE_NAME 1 /* identifies interface by name */
248+
#define INTERFACE_INDEX 2 /* identifies interface by index */
249+
#define INTERFACE_ADDRESS 3 /* identifies interface by address */
250+
251+
struct icmp_ext_obj_hdr {
252+
nd_uint16_t icmp_ext_length;
253+
nd_uint8_t icmp_ext_classnum;
254+
nd_uint8_t icmp_ext_ctype;
255+
};
256+
257+
struct icmp_ext_obj_ctype3 { /* When ctype value in ext echo obj header is 3 */
258+
nd_uint16_t icmp_ext_obj_afi; /* Address family identifier */
259+
nd_uint8_t icmp_ext_obj_addrlen; /* Length of probed address */
260+
nd_uint8_t icmp_ext_obj_reserved; /* reserved bits */
261+
};
262+
263+
/* cases for code value of ICMP EXT ECHO Reply */
264+
#define ICMP_CODE_NOERROR 0 /* no error in response */
265+
#define ICMP_CODE_MALQUERY 1 /* malformed query */
266+
#define ICMP_CODE_NOINTERFACE 2 /* no such interface */
267+
#define ICMP_CODE_NOTABLEENTRY 3 /* no such table entry */
268+
#define ICMP_CODE_MULTIPLEINTERFACES 4 /* multiple interfaces satisfy query */
269+
270+
/* RFC 8335 describes the state field of the ICMP Ext Echo reply as reflecting
271+
* the state of ARP table or neighbor cache entry associated with the probed
272+
* interface
273+
*/
274+
#define STATE_MASK 0xe0 /* masks first 3 bits of last byte in reply */
275+
#define STATE_RESERVED (0 << 5) /* ignored upon receipt */
276+
#define STATE_INCOMPLETE (1 << 5)
277+
#define STATE_REACHABLE (2 << 5)
278+
#define STATE_STALE (3 << 5)
279+
#define STATE_DELAY (4 << 5)
280+
#define STATE_PROBE (5 << 5)
281+
#define STATE_FAILED (6 << 5)
282+
#define ABIT_MASK 0x01
283+
#define ABIT_SET (1 << 7)
284+
285+
223286
/*
224287
* draft-bonica-internet-icmp-08
225288
*
@@ -330,7 +393,17 @@ icmp_print(netdissect_options *ndo, const u_char *bp, u_int plen, const u_char *
330393
GET_BE_U_2(dp->icmp_id),
331394
GET_BE_U_2(dp->icmp_seq));
332395
break;
333-
396+
397+
case ICMP_EXT_ECHO:
398+
case ICMP_EXT_ECHOREPLY:
399+
ND_TCHECK_1(dp->icmp_seq_ext);
400+
(void)snprintf(buf, sizeof(buf), "IPv4 extended echo %s, id %u, seq %u",
401+
icmp_type == ICMP_EXT_ECHO ?
402+
"request" : "reply",
403+
GET_BE_U_2(dp->icmp_id),
404+
GET_U_1(dp->icmp_seq_ext));
405+
break;
406+
334407
case ICMP_UNREACH:
335408
switch (icmp_code) {
336409

@@ -662,6 +735,161 @@ icmp_print(netdissect_options *ndo, const u_char *bp, u_int plen, const u_char *
662735
/* ndo_protocol reassignment after ip_print() call */
663736
ndo->ndo_protocol = "icmp";
664737

738+
/* print out icmp extended echo request from RFC 8335 */
739+
if (icmp_type == ICMP_EXT_ECHO) {
740+
const struct icmp_ext_header *ext_hdr;
741+
ext_hdr = (const struct icmp_ext_header*)&dp->icmp_data;
742+
const struct icmp_ext_obj_hdr *ext_obj_hdr;
743+
ext_obj_hdr = (const struct icmp_ext_obj_hdr*)(ext_hdr + 1);
744+
const char* payload = (const char*)(ext_obj_hdr + 1);
745+
uint8_t ctype = GET_U_1(ext_obj_hdr->icmp_ext_ctype);
746+
uint8_t local = GET_U_1(dp->icmp_ext_lbit);
747+
ND_PRINT(", local %u", local);
748+
749+
if (GET_U_1(ext_hdr->icmp_ext_version) != 0x20) { /* error */
750+
ND_PRINT(", Packet version not supported");
751+
return;
752+
}
753+
if (GET_U_1(ext_hdr->icmp_ext_reserved) != 0) { /* error */
754+
ND_PRINT(", Error: Reserved bits of extension header != 0");
755+
return;
756+
757+
}
758+
/* Checking packet length */
759+
ND_TCHECK_SIZE(ext_obj_hdr);
760+
uint16_t payloadLen;
761+
payloadLen = GET_BE_U_2(ext_obj_hdr->icmp_ext_length) - sizeof(*ext_obj_hdr);
762+
ND_TCHECK_LEN(ext_obj_hdr, payloadLen);
763+
764+
/* validate checksum in extended object header */
765+
if (ndo->ndo_vflag) {
766+
if (ND_TTEST_LEN(bp, plen)) {
767+
uint16_t sum;
768+
769+
vec[0].ptr = (const uint8_t *)(const void *)ext_hdr;
770+
vec[0].len = ((const uint8_t *)dp + plen) - (const uint8_t *)ext_hdr;
771+
sum = in_cksum(vec, 1);
772+
if (sum != 0) {
773+
uint16_t icmp_sum = GET_BE_U_2(ext_hdr->icmp_ext_checksum);
774+
ND_PRINT(" (wrong icmp ext cksum %x (->%x)!)",
775+
icmp_sum,
776+
in_cksum_shouldbe(icmp_sum, sum));
777+
}
778+
}
779+
}
780+
switch (ctype) {
781+
case INTERFACE_NAME: /* identifies interface by name */
782+
if (local) {
783+
ND_PRINT(", Type 1: Address Name %.*s", payloadLen, payload);
784+
} else {
785+
ND_PRINT(", Error: Must identify interface by address if local unset");
786+
}
787+
break;
788+
789+
case INTERFACE_INDEX: /* identifies interface by index */
790+
{
791+
uint32_t ifIndex = GET_BE_U_4((const uint32_t*)payload);
792+
if (local) {
793+
ND_PRINT(", Type 2: Address Index %u", ifIndex);
794+
} else {
795+
ND_PRINT(", Error: Must identify interface by address if local unset");
796+
}
797+
break;
798+
}
799+
case INTERFACE_ADDRESS: /* identifies interface by address */
800+
{
801+
const struct icmp_ext_obj_ctype3 *ext_obj_ctype3;
802+
ext_obj_ctype3 = (const struct icmp_ext_obj_ctype3*)(ext_obj_hdr + 1);
803+
uint16_t afi = GET_BE_U_2((const uint16_t*)payload);
804+
const char *name = tok2str(af_values, "Unknown", afi);
805+
const int* addr = (const int*)(ext_obj_ctype3 + 1);
806+
807+
switch(afi) {
808+
case AFNUM_INET:
809+
{
810+
ND_PRINT(", Type 3: Address %s %s", name, GET_IPADDR_STRING(addr));
811+
break;
812+
}
813+
case AFNUM_INET6:
814+
ND_PRINT(", Type 3: Address %s %s", name, GET_IP6ADDR_STRING(addr));
815+
break;
816+
default:
817+
ND_PRINT(", Unknown name: %s", name);
818+
break;
819+
}
820+
break;
821+
default:
822+
printf("Unrecognized ctype value");
823+
break;
824+
}
825+
}
826+
}
827+
828+
/* decode icmp ext echo reply */
829+
if (icmp_type == ICMP_EXT_ECHOREPLY) {
830+
uint8_t reply_state = GET_U_1(dp->icmp_ext_state);
831+
if (icmp_code != 0) {
832+
switch (icmp_code) {
833+
case ICMP_CODE_MALQUERY:
834+
ND_PRINT(", Error: Malformed Query");
835+
break;
836+
case ICMP_CODE_NOINTERFACE:
837+
ND_PRINT(", Error: No such interface");
838+
break;
839+
case ICMP_CODE_NOTABLEENTRY:
840+
ND_PRINT(", Error: No such table entry");
841+
break;
842+
case ICMP_CODE_MULTIPLEINTERFACES:
843+
ND_PRINT(", Error: Multiple interfaces satisfy query");
844+
break;
845+
default:
846+
ND_PRINT(", Error: Code value not recognized");
847+
break;
848+
}
849+
if ((reply_state & STATE_MASK) == 0) {
850+
ND_PRINT(", Error: State not zero while code was zero");
851+
}
852+
} else {
853+
ND_PRINT(", Reply recognized");
854+
switch (reply_state & STATE_MASK) {
855+
case STATE_RESERVED:
856+
ND_PRINT(", State 0: Disregarded");
857+
break;
858+
case STATE_INCOMPLETE:
859+
ND_PRINT(", State: Incomplete");
860+
break;
861+
case STATE_REACHABLE:
862+
ND_PRINT(", State: Reachable");
863+
break;
864+
case STATE_STALE:
865+
ND_PRINT(", State: Stale");
866+
break;
867+
case STATE_DELAY:
868+
ND_PRINT(", State: delay");
869+
break;
870+
case STATE_PROBE:
871+
ND_PRINT(", State: probe");
872+
break;
873+
case STATE_FAILED:
874+
ND_PRINT(", State: Failed");
875+
break;
876+
default:
877+
ND_PRINT(", State: Unknown");
878+
break;
879+
}
880+
881+
if ((reply_state & (1 << 2)) != 0) {
882+
ND_PRINT(", A-bit: %d", 1);
883+
if ((reply_state & (1 << 1)) != 0) {
884+
ND_PRINT(", 4-bit: %d", 1);
885+
}
886+
if ((reply_state & 1) != 0) {
887+
ND_PRINT(", 6-bit: %d", 1);
888+
}
889+
}
890+
}
891+
}
892+
665893
/*
666894
* Attempt to decode the MPLS extensions only for some ICMP types.
667895
*/
@@ -768,3 +996,4 @@ icmp_print(netdissect_options *ndo, const u_char *bp, u_int plen, const u_char *
768996
trunc:
769997
nd_print_trunc(ndo);
770998
}
999+

tests/TESTLIST

+4
Original file line numberDiff line numberDiff line change
@@ -827,3 +827,7 @@ unsupported-link-type-dbus unsupported-link-type-dbus.pcap unsupported-link-type
827827

828828
# LSP Ping
829829
lsp-ping-timestamp lsp-ping-timestamp.pcap lsp-ping-timestamp.out -vv
830+
831+
# ICMP Extended Echo IPv4 test
832+
icmp-ext-ipv4 icmp-ext-ipv4.pcap icmp-ext-ipv4.out
833+
icmp-ext-ipv4-v icmp-ext-ipv4.pcap icmp-ext-ipv4-v.out -v

tests/icmp-ext-ipv4-v.out

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
1 20:42:53.959853 IP (tos 0x0, ttl 64, id 61486, offset 0, flags [none], proto ICMP (1), length 28)
2+
192.168.0.35 > 10.10.10.10: ICMP IPv4 extended echo reply, id 200, seq 100, length 8, Reply recognized, State: Stale, A-bit: 1, 4-bit: 1
3+
2 20:42:53.959854 IP (tos 0x0, ttl 64, id 54450, offset 0, flags [none], proto ICMP (1), length 40)
4+
192.168.0.35 > 10.10.10.10: ICMP IPv4 extended echo request, id 200, seq 100, length 20, local 1, Type 2: Address Index 911
5+
3 20:42:53.959871 IP (tos 0x0, ttl 64, id 41129, offset 0, flags [none], proto ICMP (1), length 44)
6+
192.168.0.35 > 10.10.10.10: ICMP IPv4 extended echo request, id 200, seq 100, length 24, local 1, Type 1: Address Name en101
7+
4 20:42:53.959892 IP (tos 0x0, ttl 64, id 57417, offset 0, flags [none], proto ICMP (1), length 44)
8+
192.168.0.35 > 10.10.10.10: ICMP IPv4 extended echo request, id 200, seq 100, length 24, local 1, Type 3: Address IPv4 192.168.0.1

tests/icmp-ext-ipv4.out

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
1 20:42:53.959853 IP 192.168.0.35 > 10.10.10.10: ICMP IPv4 extended echo reply, id 200, seq 100, length 8, Reply recognized, State: Stale, A-bit: 1, 4-bit: 1
2+
2 20:42:53.959854 IP 192.168.0.35 > 10.10.10.10: ICMP IPv4 extended echo request, id 200, seq 100, length 20, local 1, Type 2: Address Index 911
3+
3 20:42:53.959871 IP 192.168.0.35 > 10.10.10.10: ICMP IPv4 extended echo request, id 200, seq 100, length 24, local 1, Type 1: Address Name en101
4+
4 20:42:53.959892 IP 192.168.0.35 > 10.10.10.10: ICMP IPv4 extended echo request, id 200, seq 100, length 24, local 1, Type 3: Address IPv4 192.168.0.1

tests/icmp-ext-ipv4.pcap

300 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)