/* * ----------[ the problem ]---------- * This small example calculates/verifies the checksum for VRRP3 IPv4 packets * as described in RFC 5798 section 5.2.8 using a IPV4 pseudo header * see vrrp3_checksum(...) * * Additional it calculates the *different* checksum for VRRP3 IPv4 packets * JunOS is using. This shows JunOS is doing a simple ip checksum without * using any pseudo header at all (like in VRRP2). * see vrrp2_checksum(...) * * NOTE: JunOS VRRP3 checksum is correct for IPv6 packets (see other notes). * * ----------[ differences in pseudo code ]---------- * Correct VRRP flow: * if VRRP2: * sum = RFC1071_checksum(vrrp_data) * else VRRP3_IPv4: * sum = RFC1071_checksum(ipv4_pseudoheader + vrrp_data) * else if VRRP3_IPv6: * sum = RFC1071_checksum(ipv6_pseudoheader + vrrp_data) * * JunOS flow: * if VRRP2 or VRRP3_IPv4: * sum = RFC1071_checksum(vrrp_data) * else if VRRP3_IPv6: * sum = RFC1071_checksum(ipv6_pseudoheader + vrrp_data) * * ----------[ reproduce ]---------- * $ set protocol vrrp version-3 * $ set interfaces irb.1 family inet address 10.1.0.10/24 vrrp-group 2 virtual-address 10.1.0.1 * * ----------[ other notes ]---------- * Before JunOS Release 12.2, JunOS was doing a checksum without pseudo header * for IPv6 packets as well. This changed in order to comply to the RFC. * Additional a configuration knob "protocols vrrp checksum-without-pseudoheader" * was introduced for interoperability to older JunOS releases. * see http://www.juniper.net/techpubs/en_US/junos13.3/topics/concept/vrrpv3-junos-support.html * * ----------[ proposal ]---------- * - fix the VRRP3 IP4 implementation to comply to the RFC * - add an additional knob: protocols vrrp checksum-without-ipv4-pseudoheader */ #include #include #include #include #include // from https://www.ietf.org/rfc/rfc1071.txt // section 4.1 uint16_t ip_checksum(unsigned short *addr, size_t count) { /* Compute Internet Checksum for "count" bytes * beginning at location "addr". */ register long sum = 0; while (count > 1) { /* This is the inner loop */ sum += *addr++; count -= 2; } /* Add left-over byte, if any */ if (count > 0) sum += *(unsigned char *)addr; /* Fold 32-bit sum to 16 bits */ while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16); return ~sum; } // from RFC 5798 section 5.2.8 // http://tools.ietf.org/html/rfc5798#section-5.2.8 // // The checksum is the 16-bit one's complement of the one's complement // sum of the entire VRRP message starting with the version field // =====> and a "pseudo-header" <==== // as defined in Section 8.1 of [RFC2460]. The next // header field in the "pseudo-header" should be set to 112 (decimal) // for VRRP. For computing the checksum, the checksum field is set to // zero. See RFC1071 for more detail [RFC1071]. uint16_t vrrp3_checksum(uint32_t src_ip, uint32_t dst_ip, unsigned short *data, size_t data_len) { uint8_t proto = 112; uint16_t sum; // allocate buffer to prepend ipv4 pseudo header // see http://en.wikipedia.org/wiki/User_Datagram_Protocol#IPv4_Pseudo_Header unsigned buf_len = sizeof(unsigned short) * 6 + data_len; unsigned short *buf = malloc(buf_len); buf[0] = (src_ip >> 16); buf[1] = (src_ip & 0xffff); buf[2] = (dst_ip >> 16); buf[3] = (dst_ip & 0xffff); buf[4] = proto; buf[5] = data_len; // copy VRRP packet payload to buffer memcpy(buf + 6, data, data_len); // calculate checksum sum = ip_checksum(buf, buf_len); free(buf); return sum; } // VRRP2 checksum = ip checksum uint16_t vrrp2_checksum(unsigned short *data, size_t data_len) { return ip_checksum(data, data_len); } int main() { // make sure the checksum implementation is sane // example from http://en.wikipedia.org/wiki/IPv4_header_checksum unsigned short packet_test[] = { 0x4500, 0x0073, 0x0000, 0x4000, 0x4011, 0xb861, 0xc0a8, 0x0001, 0xc0a8, 0x00c7 }; assert(ip_checksum(packet_test, sizeof(packet_test)) == 0); // example VRRP3 IPv4 packet from MikroTik unsigned short vrrp3_mikrotik[] = { 0x3102, 0x6401, 0x0064, 0x75fb, 0x0a01, 0x0001 }; // example VRRP2 packet from JunOS unsigned short vrrp2_junos[] = { 0x210a, 0xc801, 0x0001, 0x0cf1, 0x0a01, 0x0001 }; // example VRRP3 IPv4 packet from JunOS unsigned short vrrp3_junos[] = { 0x3102, 0x6401, 0x0064, 0x6096, 0x0a01, 0x0001 }; // IP adresses of both routers uint32_t ip_junos = 0x0a01000a; // 10.1.0.10 uint32_t ip_mikrotik = 0x0a01000b; // 10.1.0.11 uint32_t dst_ip = 0xe0000012; // 224.0.0.18 // verify MikroTik checksum is correct according to RFC 5798 assert(vrrp3_checksum(ip_mikrotik, dst_ip, vrrp3_mikrotik, sizeof(vrrp3_mikrotik)) == 0); // verify VRRP2 checksum is correct in JunOS assert(vrrp2_checksum(vrrp2_junos, sizeof(vrrp2_junos)) == 0); // show JunOS is doing a VRRP2 checksum for VRRP3 IPv4 packets assert(vrrp2_checksum(vrrp3_junos, sizeof(vrrp3_junos)) == 0); // show that both checksum methods *are* incompatible assert(vrrp3_checksum(ip_junos, dst_ip, vrrp3_junos, sizeof(vrrp3_junos)) != 0); assert(vrrp2_checksum(vrrp3_mikrotik, sizeof(vrrp3_mikrotik)) != 0); return 0; }