]> The Tcpdump Group git mirrors - tcpdump/blobdiff - print-isoclns.c
Add changes in 4.2.1.
[tcpdump] / print-isoclns.c
index 8e1572aea78c563b67e2bc588c4f1daf226060cc..57637055377b2c6c9c60f67e1b0ff076d08fee8e 100644 (file)
@@ -26,7 +26,7 @@
 
 #ifndef lint
 static const char rcsid[] _U_ =
-    "@(#) $Header: /tcpdump/master/tcpdump/print-isoclns.c,v 1.156 2006-04-06 13:43:30 hannes Exp $ (LBL)";
+    "@(#) $Header: /tcpdump/master/tcpdump/print-isoclns.c,v 1.165 2008-08-16 13:38:15 hannes Exp $ (LBL)";
 #endif
 
 #ifdef HAVE_CONFIG_H
@@ -46,6 +46,7 @@ static const char rcsid[] _U_ =
 #include "extract.h"
 #include "gmpls.h"
 #include "oui.h"
+#include "signature.h"
 
 /*
  * IS-IS is defined in ISO 10589.  Look there for protocol definitions.
@@ -335,7 +336,7 @@ static struct tok clnp_option_qos_global_values[] = {
 };
 
 #define ISIS_SUBTLV_EXT_IS_REACH_ADMIN_GROUP           3 /* draft-ietf-isis-traffic-05 */
-#define ISIS_SUBTLV_EXT_IS_REACH_LINK_LOCAL_REMOTE_ID  4 /* draft-ietf-isis-gmpls-extensions */
+#define ISIS_SUBTLV_EXT_IS_REACH_LINK_LOCAL_REMOTE_ID  4 /* rfc4205 */
 #define ISIS_SUBTLV_EXT_IS_REACH_LINK_REMOTE_ID        5 /* draft-ietf-isis-traffic-05 */
 #define ISIS_SUBTLV_EXT_IS_REACH_IPV4_INTF_ADDR        6 /* draft-ietf-isis-traffic-05 */
 #define ISIS_SUBTLV_EXT_IS_REACH_IPV4_NEIGHBOR_ADDR    8 /* draft-ietf-isis-traffic-05 */
@@ -344,8 +345,9 @@ static struct tok clnp_option_qos_global_values[] = {
 #define ISIS_SUBTLV_EXT_IS_REACH_UNRESERVED_BW        11 /* rfc4124 */
 #define ISIS_SUBTLV_EXT_IS_REACH_BW_CONSTRAINTS_OLD   12 /* draft-ietf-tewg-diff-te-proto-06 */
 #define ISIS_SUBTLV_EXT_IS_REACH_TE_METRIC            18 /* draft-ietf-isis-traffic-05 */
-#define ISIS_SUBTLV_EXT_IS_REACH_LINK_PROTECTION_TYPE 20 /* draft-ietf-isis-gmpls-extensions */
-#define ISIS_SUBTLV_EXT_IS_REACH_INTF_SW_CAP_DESCR    21 /* draft-ietf-isis-gmpls-extensions */
+#define ISIS_SUBTLV_EXT_IS_REACH_LINK_ATTRIBUTE       19 /* draft-ietf-isis-link-attr-01 */
+#define ISIS_SUBTLV_EXT_IS_REACH_LINK_PROTECTION_TYPE 20 /* rfc4205 */
+#define ISIS_SUBTLV_EXT_IS_REACH_INTF_SW_CAP_DESCR    21 /* rfc4205 */
 #define ISIS_SUBTLV_EXT_IS_REACH_BW_CONSTRAINTS       22 /* rfc4124 */
 
 static struct tok isis_ext_is_reach_subtlv_values[] = {
@@ -358,6 +360,7 @@ static struct tok isis_ext_is_reach_subtlv_values[] = {
     { ISIS_SUBTLV_EXT_IS_REACH_RESERVABLE_BW,          "Reservable link bandwidth" },
     { ISIS_SUBTLV_EXT_IS_REACH_UNRESERVED_BW,          "Unreserved bandwidth" },
     { ISIS_SUBTLV_EXT_IS_REACH_TE_METRIC,              "Traffic Engineering Metric" },
+    { ISIS_SUBTLV_EXT_IS_REACH_LINK_ATTRIBUTE,         "Link Attribute" },
     { ISIS_SUBTLV_EXT_IS_REACH_LINK_PROTECTION_TYPE,   "Link Protection Type" },
     { ISIS_SUBTLV_EXT_IS_REACH_INTF_SW_CAP_DESCR,      "Interface Switching Capability" },
     { ISIS_SUBTLV_EXT_IS_REACH_BW_CONSTRAINTS_OLD,     "Bandwidth Constraints (old)" },
@@ -382,13 +385,22 @@ static struct tok isis_ext_ip_reach_subtlv_values[] = {
     { 0, NULL }
 };
 
+static struct tok isis_subtlv_link_attribute_values[] = {
+    { 0x01, "Local Protection Available" },
+    { 0x02, "Link excluded from local protection path" },
+    { 0x04, "Local maintenance required"},
+    { 0, NULL }
+};
+
 #define ISIS_SUBTLV_AUTH_SIMPLE        1
+#define ISIS_SUBTLV_AUTH_GENERIC       3 /* rfc 5310 */
 #define ISIS_SUBTLV_AUTH_MD5          54
 #define ISIS_SUBTLV_AUTH_MD5_LEN      16
 #define ISIS_SUBTLV_AUTH_PRIVATE     255
 
 static struct tok isis_subtlv_auth_values[] = {
     { ISIS_SUBTLV_AUTH_SIMPLE, "simple text password"},
+    { ISIS_SUBTLV_AUTH_GENERIC, "Generic Crypto key-id"},
     { ISIS_SUBTLV_AUTH_MD5,    "HMAC-MD5 password"},
     { ISIS_SUBTLV_AUTH_PRIVATE,        "Routing Domain private password"},
     { 0, NULL }
@@ -429,8 +441,8 @@ static struct tok clnp_flag_values[] = {
 #define ISIS_MASK_MTFLAGS(x)               ((x)&0xf000)
 
 static struct tok isis_mt_flag_values[] = {
-    { 0x4000,                  "sub-TLVs present"},
-    { 0x8000,                  "ATT bit set"},
+    { 0x4000,                  "ATT bit set"},
+    { 0x8000,                  "Overload bit set"},
     { 0, NULL}
 };
 
@@ -472,7 +484,7 @@ static struct tok isis_lsp_istype_values[] = {
     { ISIS_LSP_TYPE_UNUSED0,   "Unused 0x0 (invalid)"},
     { ISIS_LSP_TYPE_LEVEL_1,   "L1 IS"},
     { ISIS_LSP_TYPE_UNUSED2,   "Unused 0x2 (invalid)"},
-    { ISIS_LSP_TYPE_LEVEL_2,   "L1L2 IS"},
+    { ISIS_LSP_TYPE_LEVEL_2,   "L2 IS"},
     { 0, NULL }
 };
 
@@ -499,7 +511,8 @@ struct isis_tlv_ptp_adj {
     u_int8_t neighbor_extd_local_circuit_id[4];
 };
 
-static int osi_cksum(const u_int8_t *, u_int);
+static void osi_print_cksum(const u_int8_t *pptr, u_int16_t checksum,
+                            u_int checksum_offset, u_int length);
 static int clnp_print(const u_int8_t *, u_int);
 static void esis_print(const u_int8_t *, u_int);
 static int isis_print(const u_int8_t *, u_int);
@@ -605,10 +618,6 @@ struct isis_tlv_lsp {
 
 void isoclns_print(const u_int8_t *p, u_int length, u_int caplen)
 {
-       const struct isis_common_header *header;
-
-       header = (const struct isis_common_header *)p;
-
         if (caplen <= 1) { /* enough bytes on the wire ? */
             printf("|OSI");
             return;
@@ -651,7 +660,7 @@ void isoclns_print(const u_int8_t *p, u_int length, u_int caplen)
 
 #ifdef INET6
         case NLPID_IP6:
-                ip6_print(p+1, length-1);
+                ip6_print(gndo, p+1, length-1);
                 break;
 #endif
 
@@ -762,7 +771,7 @@ static int clnp_print (const u_int8_t *pptr, u_int length)
         }
         printf("%slength %u",eflag ? "" : ", ",length);
 
-        printf("\n\t%s PDU, hlen: %u, v: %u, lifetime: %u.%us, Segment PDU length: %u, checksum: 0x%04x ",
+        printf("\n\t%s PDU, hlen: %u, v: %u, lifetime: %u.%us, Segment PDU length: %u, checksum: 0x%04x",
                tok2str(clnp_pdu_values, "unknown (%u)",clnp_pdu_type),
                clnp_header->length_indicator,
                clnp_header->version,
@@ -771,10 +780,8 @@ static int clnp_print (const u_int8_t *pptr, u_int length)
                EXTRACT_16BITS(clnp_header->segment_length),
                EXTRACT_16BITS(clnp_header->cksum));
 
-        /* do not attempt to verify the checksum if it is zero */
-        if (EXTRACT_16BITS(clnp_header->cksum) == 0)
-                printf("(unverified)");
-            else printf("(%s)", osi_cksum(optr, clnp_header->length_indicator) ? "incorrect" : "correct");
+        osi_print_cksum(optr, EXTRACT_16BITS(clnp_header->cksum), 7,
+                        clnp_header->length_indicator);
 
         printf("\n\tFlags [%s]",
                bittok2str(clnp_flag_values,"none",clnp_flags));
@@ -1028,12 +1035,9 @@ esis_print(const u_int8_t *pptr, u_int length)
                    esis_pdu_type);
 
         printf(", v: %u%s", esis_header->version, esis_header->version == ESIS_VERSION ? "" : "unsupported" );
-        printf(", checksum: 0x%04x ", EXTRACT_16BITS(esis_header->cksum));
-        /* do not attempt to verify the checksum if it is zero */
-        if (EXTRACT_16BITS(esis_header->cksum) == 0)
-                printf("(unverified)");
-        else
-                printf("(%s)", osi_cksum(pptr, li) ? "incorrect" : "correct");
+        printf(", checksum: 0x%04x", EXTRACT_16BITS(esis_header->cksum));
+
+        osi_print_cksum(pptr, EXTRACT_16BITS(esis_header->cksum), 7, li);
 
         printf(", holding time: %us, length indicator: %u",EXTRACT_16BITS(esis_header->holdtime),li);
 
@@ -1171,15 +1175,15 @@ esis_print(const u_int8_t *pptr, u_int length)
        }
 
         /* now walk the options */
-        while (li >= 2) {
+        while (li != 0) {
             u_int op, opli;
             const u_int8_t *tptr;
             
-            TCHECK2(*pptr, 2);
             if (li < 2) {
                 printf(", bad opts/li");
                 return;
             }
+            TCHECK2(*pptr, 2);
             op = *pptr++;
             opli = *pptr++;
             li -= 2;
@@ -1198,8 +1202,11 @@ esis_print(const u_int8_t *pptr, u_int length)
             switch (op) {
 
             case ESIS_OPTION_ES_CONF_TIME:
-                TCHECK2(*pptr, 2);
-                printf("%us", EXTRACT_16BITS(tptr));
+                if (opli == 2) {
+                    TCHECK2(*pptr, 2);
+                    printf("%us", EXTRACT_16BITS(tptr));
+                } else
+                    printf("(bad length)");
                 break;
 
             case ESIS_OPTION_PROTOCOLS:
@@ -1408,7 +1415,7 @@ trunctlv:
 static int
 isis_print_is_reach_subtlv (const u_int8_t *tptr,u_int subt,u_int subl,const char *ident) {
 
-        u_int te_class,priority_level;
+        u_int te_class,priority_level,gmpls_switch_cap;
         union { /* int to float conversion buffer for several subTLVs */
             float f; 
             u_int32_t i;
@@ -1432,7 +1439,7 @@ isis_print_is_reach_subtlv (const u_int8_t *tptr,u_int subt,u_int subl,const cha
         case ISIS_SUBTLV_EXT_IS_REACH_LINK_REMOTE_ID:
            if (subl >= 4) {
              printf(", 0x%08x", EXTRACT_32BITS(tptr));
-             if (subl == 8) /* draft-ietf-isis-gmpls-extensions */
+             if (subl == 8) /* rfc4205 */
                printf(", 0x%08x", EXTRACT_32BITS(tptr+4));
            }
            break;
@@ -1481,6 +1488,15 @@ isis_print_is_reach_subtlv (const u_int8_t *tptr,u_int subt,u_int subl,const cha
             if (subl >= 3)
               printf(", %u", EXTRACT_24BITS(tptr));
             break;
+        case ISIS_SUBTLV_EXT_IS_REACH_LINK_ATTRIBUTE:
+            if (subl == 2) {
+               printf(", [ %s ] (0x%04x)",
+                      bittok2str(isis_subtlv_link_attribute_values,
+                                 "Unknown",
+                                 EXTRACT_16BITS(tptr)),
+                      EXTRACT_16BITS(tptr));
+            }
+            break;
         case ISIS_SUBTLV_EXT_IS_REACH_LINK_PROTECTION_TYPE:
             if (subl >= 2) {
               printf(", %s, Priority %u",
@@ -1490,9 +1506,10 @@ isis_print_is_reach_subtlv (const u_int8_t *tptr,u_int subt,u_int subl,const cha
             break;
         case ISIS_SUBTLV_EXT_IS_REACH_INTF_SW_CAP_DESCR:
             if (subl >= 36) {
+              gmpls_switch_cap = *tptr;
               printf("%s  Interface Switching Capability:%s",
                    ident,
-                   tok2str(gmpls_switch_cap_values, "Unknown", *(tptr)));
+                   tok2str(gmpls_switch_cap_values, "Unknown", gmpls_switch_cap));
               printf(", LSP Encoding: %s",
                    tok2str(gmpls_encoding_values, "Unknown", *(tptr+1)));
              tptr+=4;
@@ -1506,12 +1523,29 @@ isis_print_is_reach_subtlv (const u_int8_t *tptr,u_int subt,u_int subl,const cha
                tptr+=4;
               }
               subl-=36;
-              /* there is some optional stuff left to decode but this is as of yet
-                 not specified so just lets hexdump what is left */
-              if(subl>0){
-                if(!print_unknown_data(tptr,"\n\t\t    ",
-                                      subl))
+              switch (gmpls_switch_cap) {
+              case GMPLS_PSC1:
+              case GMPLS_PSC2:
+              case GMPLS_PSC3:
+              case GMPLS_PSC4:
+                bw.i = EXTRACT_32BITS(tptr);
+                printf("%s  Min LSP Bandwidth: %.3f Mbps", ident, bw.f*8/1000000);
+                printf("%s  Interface MTU: %u", ident, EXTRACT_16BITS(tptr+4));
+                break;
+              case GMPLS_TSC:
+                bw.i = EXTRACT_32BITS(tptr);
+                printf("%s  Min LSP Bandwidth: %.3f Mbps", ident, bw.f*8/1000000);
+                printf("%s  Indication %s", ident,
+                       tok2str(gmpls_switch_cap_tsc_indication_values, "Unknown (%u)", *(tptr+4)));
+                break;
+              default:
+                /* there is some optional stuff left to decode but this is as of yet
+                   not specified so just lets hexdump what is left */
+                if(subl>0){
+                  if(!print_unknown_data(tptr,"\n\t\t    ",
+                                         subl))
                     return(0);
+                }
               }
             }
             break;
@@ -1690,8 +1724,11 @@ isis_print_extd_ip_reach (const u_int8_t *tptr, const char *ident, u_int16_t afi
                ISIS_MASK_TLV_EXTD_IP6_SUBTLV(status_byte) ? ", sub-TLVs present" : "");
 #endif
     
-    if ((ISIS_MASK_TLV_EXTD_IP_SUBTLV(status_byte)  && afi == AF_INET) ||
-        (ISIS_MASK_TLV_EXTD_IP6_SUBTLV(status_byte) && afi == AF_INET6)) {
+    if ((afi == AF_INET  && ISIS_MASK_TLV_EXTD_IP_SUBTLV(status_byte))
+#ifdef INET6
+     || (afi == AF_INET6 && ISIS_MASK_TLV_EXTD_IP6_SUBTLV(status_byte))
+#endif
+       ) {
         /* assume that one prefix can hold more
            than one subTLV - therefore the first byte must reflect
            the aggregate bytecount of the subTLVs for this prefix
@@ -1729,7 +1766,7 @@ static int isis_print (const u_int8_t *p, u_int length)
 
     const struct isis_iih_lan_header *header_iih_lan;
     const struct isis_iih_ptp_header *header_iih_ptp;
-    const struct isis_lsp_header *header_lsp;
+    struct isis_lsp_header *header_lsp;
     const struct isis_csnp_header *header_csnp;
     const struct isis_psnp_header *header_psnp;
 
@@ -1741,18 +1778,20 @@ static int isis_print (const u_int8_t *p, u_int length)
     u_int8_t pdu_type, max_area, id_length, tlv_type, tlv_len, tmp, alen, lan_alen, prefix_len;
     u_int8_t ext_is_len, ext_ip_len, mt_len;
     const u_int8_t *optr, *pptr, *tptr;
-    u_short packet_len,pdu_len;
+    u_short packet_len,pdu_len, key_id;
     u_int i,vendor_id;
+    int sigcheck;
 
     packet_len=length;
     optr = p; /* initialize the _o_riginal pointer to the packet start -
-                 need it for parsing the checksum TLV */
+                 need it for parsing the checksum TLV and authentication
+                 TLV verification */
     isis_header = (const struct isis_common_header *)p;
     TCHECK(*isis_header);
     pptr = p+(ISIS_COMMON_HEADER_SIZE);
     header_iih_lan = (const struct isis_iih_lan_header *)pptr;
     header_iih_ptp = (const struct isis_iih_ptp_header *)pptr;
-    header_lsp = (const struct isis_lsp_header *)pptr;
+    header_lsp = (struct isis_lsp_header *)pptr;
     header_csnp = (const struct isis_csnp_header *)pptr;
     header_psnp = (const struct isis_psnp_header *)pptr;
 
@@ -1974,15 +2013,18 @@ static int isis_print (const u_int8_t *p, u_int length)
                EXTRACT_16BITS(header_lsp->remaining_lifetime),
                EXTRACT_16BITS(header_lsp->checksum));
 
-        /* if this is a purge do not attempt to verify the checksum */
-        if ( EXTRACT_16BITS(header_lsp->remaining_lifetime) == 0 &&
-             EXTRACT_16BITS(header_lsp->checksum) == 0)
-            printf(" (purged)");
-        else
-            /* verify the checksum -
-             * checking starts at the lsp-id field at byte position [12]
-             * hence the length needs to be reduced by 12 bytes */
-            printf(" (%s)", (osi_cksum((u_int8_t *)header_lsp->lsp_id, length-12)) ? "incorrect" : "correct");
+
+        osi_print_cksum((u_int8_t *)header_lsp->lsp_id,
+                        EXTRACT_16BITS(header_lsp->checksum), 12, length-12);
+
+        /*
+         * Clear checksum and lifetime prior to signature verification.
+         */
+        header_lsp->checksum[0] = 0;
+        header_lsp->checksum[1] = 0;
+        header_lsp->remaining_lifetime[0] = 0;
+        header_lsp->remaining_lifetime[1] = 0;
+        
 
        printf(", PDU length: %u, Flags: [ %s",
                pdu_len,
@@ -2105,7 +2147,7 @@ static int isis_print (const u_int8_t *p, u_int length)
                tlv_len);
 
         if (tlv_len == 0) /* something is malformed */
-            break;
+           continue;
 
         /* now check if we have a decoder otherwise do a hexdump at the end*/
        switch (tlv_type) {
@@ -2159,13 +2201,12 @@ static int isis_print (const u_int8_t *p, u_int length)
            break;
 
         case ISIS_TLV_MT_IS_REACH:
+            mt_len = isis_print_mtid(tptr, "\n\t      ");
+            if (mt_len == 0) /* did something go wrong ? */
+                goto trunctlv;
+            tptr+=mt_len;
+            tmp-=mt_len;
             while (tmp >= 2+NODE_ID_LEN+3+1) {
-                mt_len = isis_print_mtid(tptr, "\n\t      ");
-                if (mt_len == 0) /* did something go wrong ? */
-                    goto trunctlv;
-                tptr+=mt_len;
-                tmp-=mt_len;
-
                 ext_is_len = isis_print_ext_is_reach(tptr,"\n\t      ",tlv_type);
                 if (ext_is_len == 0) /* did something go wrong ? */
                     goto trunctlv;
@@ -2244,13 +2285,14 @@ static int isis_print (const u_int8_t *p, u_int length)
            break;
 
         case ISIS_TLV_MT_IP_REACH:
-           while (tmp>0) {
-                mt_len = isis_print_mtid(tptr, "\n\t      ");
-                if (mt_len == 0) /* did something go wrong ? */
-                    goto trunctlv;
-                tptr+=mt_len;
-                tmp-=mt_len;
+            mt_len = isis_print_mtid(tptr, "\n\t      ");
+            if (mt_len == 0) { /* did something go wrong ? */
+                goto trunctlv;
+            }
+            tptr+=mt_len;
+            tmp-=mt_len;
 
+            while (tmp>0) {
                 ext_ip_len = isis_print_extd_ip_reach(tptr, "\n\t      ", AF_INET);
                 if (ext_ip_len == 0) /* did something go wrong ? */
                     goto trunctlv;
@@ -2271,13 +2313,14 @@ static int isis_print (const u_int8_t *p, u_int length)
            break;
 
        case ISIS_TLV_MT_IP6_REACH:
-           while (tmp>0) {
-                mt_len = isis_print_mtid(tptr, "\n\t      ");
-                if (mt_len == 0) /* did something go wrong ? */
-                    goto trunctlv;
-                tptr+=mt_len;
-                tmp-=mt_len;
+            mt_len = isis_print_mtid(tptr, "\n\t      ");
+            if (mt_len == 0) { /* did something go wrong ? */
+                goto trunctlv;
+            }
+            tptr+=mt_len;
+            tmp-=mt_len;
 
+           while (tmp>0) {
                 ext_ip_len = isis_print_extd_ip_reach(tptr, "\n\t      ", AF_INET6);
                 if (ext_ip_len == 0) /* did something go wrong ? */
                     goto trunctlv;
@@ -2324,7 +2367,25 @@ static int isis_print (const u_int8_t *p, u_int length)
                }
                if (tlv_len != ISIS_SUBTLV_AUTH_MD5_LEN+1)
                     printf(", (malformed subTLV) ");
+
+#ifdef HAVE_LIBCRYPTO
+                sigcheck = signature_verify(optr, length,
+                                            (unsigned char *)tptr + 1);
+#else
+                sigcheck = CANT_CHECK_SIGNATURE;
+#endif
+                printf(" (%s)", tok2str(signature_check_values, "Unknown", sigcheck));
+
                break;
+            case ISIS_SUBTLV_AUTH_GENERIC:
+                key_id = EXTRACT_16BITS((tptr+1));
+                printf("%u, password: ", key_id); 
+                for(i=1 + sizeof(u_int16_t);i<tlv_len;i++) {
+                    if (!TTEST2(*(tptr+i), 1))
+                        goto trunctlv;
+                    printf("%02x",*(tptr+i));
+                }
+                break;
            case ISIS_SUBTLV_AUTH_PRIVATE:
            default:
                if(!print_unknown_data(tptr+1,"\n\t\t  ",tlv_len-1))
@@ -2483,9 +2544,7 @@ static int isis_print (const u_int8_t *p, u_int length)
              * to avoid conflicts the checksum TLV is zeroed.
              * see rfc3358 for details
              */
-            if (EXTRACT_16BITS(tptr) == 0)
-                printf("(unverified)");
-            else printf("(%s)", osi_cksum(optr, length) ? "incorrect" : "correct");
+            osi_print_cksum(optr, EXTRACT_16BITS(tptr), tptr-optr, length);
            break;
 
        case ISIS_TLV_MT_SUPPORTED:
@@ -2677,25 +2736,25 @@ static int isis_print (const u_int8_t *p, u_int length)
     return(1);
 }
 
-/*
- * Verify the checksum.  See 8473-1, Appendix C, section C.4.
- */
-
-static int
-osi_cksum(const u_int8_t *tptr, u_int len)
+static void
+osi_print_cksum (const u_int8_t *pptr, u_int16_t checksum,
+                    u_int checksum_offset, u_int length)
 {
-       int32_t c0 = 0, c1 = 0;
+        u_int16_t calculated_checksum;
 
-       while ((int)--len >= 0) {
-               c0 += *tptr++;
-               c0 %= 255;
-               c1 += c0;
-               c1 %= 255;
-       }
-       return (c0 | c1);
+        /* do not attempt to verify the checksum if it is zero */
+        if (!checksum) {
+                printf("(unverified)");
+        } else {
+                calculated_checksum = create_osi_cksum(pptr, checksum_offset, length);
+                if (checksum == calculated_checksum) {
+                        printf(" (correct)");
+                } else {
+                        printf(" (incorrect should be 0x%04x)", calculated_checksum);
+                }
+        }
 }
 
-
 /*
  * Local Variables:
  * c-style: whitesmith