From: Guy Harris Date: Sat, 2 May 2015 17:15:42 +0000 (-0700) Subject: Fix checksumming of PIMv2 Register messages. X-Git-Tag: tcpdump-4.8.0~264 X-Git-Url: https://git.tcpdump.org/tcpdump/commitdiff_plain/9dd6f72a8cb559e122b73ebd82dcddf36870df19 Fix checksumming of PIMv2 Register messages. The checksum only covers the header, not the encapsulated packet, so only checksum that. However, if that checksum fails, try checksumming the entire packet, as, according to RFC 4601, packets with the entire packet checksummed should also be accepted, for interoperability. --- diff --git a/netdissect.h b/netdissect.h index 0bf80c16..e1a384c7 100644 --- a/netdissect.h +++ b/netdissect.h @@ -511,7 +511,7 @@ extern void tftp_print(netdissect_options *, const u_char *, u_int); extern void vrrp_print(netdissect_options *, const u_char *, u_int, const u_char *, int); extern void pimv1_print(netdissect_options *, const u_char *, u_int); extern void cisco_autorp_print(netdissect_options *, const u_char *, u_int); -extern void pim_print(netdissect_options *, const u_char *, u_int, u_int); +extern void pim_print(netdissect_options *, const u_char *, u_int, const u_char *); extern const u_char * ns_nprint (netdissect_options *, register const u_char *, register const u_char *); extern void ns_print(netdissect_options *, const u_char *, u_int, int); extern void bootp_print(netdissect_options *, const u_char *, u_int); diff --git a/print-ip.c b/print-ip.c index bdead1fa..a7a3b470 100644 --- a/print-ip.c +++ b/print-ip.c @@ -323,7 +323,6 @@ ip_print_demux(netdissect_options *ndo, struct ip_print_demux_state *ipds) { struct protoent *proto; - struct cksum_vec vec[1]; again: switch (ipds->nh) { @@ -454,9 +453,7 @@ again: break; case IPPROTO_PIM: - vec[0].ptr = ipds->cp; - vec[0].len = ipds->len; - pim_print(ndo, ipds->cp, ipds->len, in_cksum(vec, 1)); + pim_print(ndo, ipds->cp, ipds->len, (const u_char *)ipds->ip); break; case IPPROTO_VRRP: diff --git a/print-ip6.c b/print-ip6.c index fa461926..98d6adf5 100644 --- a/print-ip6.c +++ b/print-ip6.c @@ -222,8 +222,7 @@ ip6_print(netdissect_options *ndo, const u_char *bp, u_int length) } case IPPROTO_PIM: - pim_print(ndo, cp, len, nextproto6_cksum(ip6, cp, len, len, - IPPROTO_PIM)); + pim_print(ndo, cp, len, (const u_char *)ip6); return; case IPPROTO_OSPF: diff --git a/print-pim.c b/print-pim.c index 51fa06f4..16bfa625 100644 --- a/print-pim.c +++ b/print-pim.c @@ -30,6 +30,7 @@ #include "extract.h" #include "ip.h" +#include "ip6.h" #define PIMV1_TYPE_QUERY 0 #define PIMV1_TYPE_REGISTER 1 @@ -133,7 +134,7 @@ struct pim { u_short pim_cksum; /* IP style check sum */ }; -static void pimv2_print(netdissect_options *, register const u_char *bp, register u_int len, u_int cksum); +static void pimv2_print(netdissect_options *, register const u_char *bp, register u_int len, const u_char *); static void pimv1_join_prune_print(netdissect_options *ndo, @@ -414,7 +415,7 @@ trunc: void pim_print(netdissect_options *ndo, - register const u_char *bp, register u_int len, u_int cksum) + register const u_char *bp, register u_int len, const u_char *bp2) { register const u_char *ep; register const struct pim *pim = (const struct pim *)bp; @@ -439,7 +440,7 @@ pim_print(netdissect_options *ndo, PIM_VER(pim->pim_typever), len, tok2str(pimv2_type_values,"Unknown Type",PIM_TYPE(pim->pim_typever)))); - pimv2_print(ndo, bp, len, cksum); + pimv2_print(ndo, bp, len, bp2); } break; default: @@ -619,13 +620,45 @@ trunc: return -1; } +enum checksum_status { + CORRECT, + INCORRECT, + UNVERIFIED +}; + +static enum checksum_status +pimv2_check_checksum(const u_char *bp, const u_char *bp2, u_int len) +{ + const struct ip *ip; + u_int cksum; + + ip = (const struct ip *)bp2; + if (IP_V(ip) == 4) { + struct cksum_vec vec[1]; + + vec[0].ptr = bp; + vec[0].len = len; + cksum = in_cksum(vec, 1); + return (cksum ? INCORRECT : CORRECT); + } else if (IP_V(ip) == 6) { + const struct ip6_hdr *ip6; + + ip6 = (const struct ip6_hdr *)bp2; + cksum = nextproto6_cksum(ip6, bp, len, len, IPPROTO_PIM); + return (cksum ? INCORRECT : CORRECT); + } else { + return (UNVERIFIED); + } +} + static void pimv2_print(netdissect_options *ndo, - register const u_char *bp, register u_int len, u_int cksum) + register const u_char *bp, register u_int len, const u_char *bp2) { register const u_char *ep; register const struct pim *pim = (const struct pim *)bp; int advance; + enum checksum_status cksum_status; ep = (const u_char *)ndo->ndo_snapend; if (bp >= ep) @@ -641,7 +674,41 @@ pimv2_print(netdissect_options *ndo, if (EXTRACT_16BITS(&pim->pim_cksum) == 0) { ND_PRINT((ndo, "(unverified)")); } else { - ND_PRINT((ndo, "(%scorrect)", ND_TTEST2(bp[0], len) && cksum ? "in" : "" )); + if (PIM_TYPE(pim->pim_typever) == PIMV2_TYPE_REGISTER) { + /* + * The checksum only covers the packet header, + * not the encapsulated packet. + */ + cksum_status = pimv2_check_checksum(bp, bp2, 8); + if (cksum_status == INCORRECT) { + /* + * To quote RFC 4601, "For interoperability + * reasons, a message carrying a checksum + * calculated over the entire PIM Register + * message should also be accepted." + */ + cksum_status = pimv2_check_checksum(bp, bp2, len); + } + } else { + /* + * The checksum covers the entire packet. + */ + cksum_status = pimv2_check_checksum(bp, bp2, len); + } + switch (cksum_status) { + + case CORRECT: + ND_PRINT((ndo, "(correct)")); + break; + + case INCORRECT: + ND_PRINT((ndo, "(incorrect)")); + break; + + case UNVERIFIED: + ND_PRINT((ndo, "(unverified)")); + break; + } } switch (PIM_TYPE(pim->pim_typever)) {