Download | Plain Text | No Line Numbers
- /*
- * ----------[ 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 <stdint.h>
- #include <stdio.h>
- #include <assert.h>
- #include <stdlib.h>
- #include <string.h>
-
- // 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;
- 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
-
- // calculate checksum
- sum = ip_checksum(buf, buf_len);
-
- 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
- };
-
- // 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
- sizeof(vrrp3_mikrotik)) == 0);
-
- // verify VRRP2 checksum is correct in JunOS
-
- // show JunOS is doing a VRRP2 checksum for VRRP3 IPv4 packets
-
- // show that both checksum methods *are* incompatible
- sizeof(vrrp3_junos)) != 0);
-
- return 0;
- }
-