Skip to content

Commit 3585393

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

File tree

5 files changed

+247
-5
lines changed

5 files changed

+247
-5
lines changed

print-icmp.c

+231-5
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,8 @@ 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 */
146-
147-
#define ICMP_MAXTYPE 18
161+
#define ICMP_EXT_ECHO 42 /* IPv4 extended echo request */
162+
#define ICMP_EXT_ECHOREPLY 43 /* IPv4 extended echo reply */
148163

149164
#define ICMP_ERRTYPE(type) \
150165
((type) == ICMP_UNREACH || (type) == ICMP_SOURCEQUENCH || \
@@ -220,6 +235,51 @@ struct id_rdiscovery {
220235
nd_uint32_t ird_pref;
221236
};
222237

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

@@ -662,6 +732,161 @@ icmp_print(netdissect_options *ndo, const u_char *bp, u_int plen, const u_char *
662732
/* ndo_protocol reassignment after ip_print() call */
663733
ndo->ndo_protocol = "icmp";
664734

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

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)