Download | Plain Text | No Line Numbers


  1. /*
  2.  * ----------[ the problem ]----------
  3.  * This small example calculates/verifies the checksum for VRRP3 IPv4 packets
  4.  * as described in RFC 5798 section 5.2.8 using a IPV4 pseudo header
  5.  * see vrrp3_checksum(...)
  6.  *
  7.  * Additional it calculates the *different* checksum for VRRP3 IPv4 packets
  8.  * JunOS is using. This shows JunOS is doing a simple ip checksum without
  9.  * using any pseudo header at all (like in VRRP2).
  10.  * see vrrp2_checksum(...)
  11.  *
  12.  * NOTE: JunOS VRRP3 checksum is correct for IPv6 packets (see other notes).
  13.  *
  14.  * ----------[ differences in pseudo code ]----------
  15.  * Correct VRRP flow:
  16.  * if VRRP2:
  17.  * sum = RFC1071_checksum(vrrp_data)
  18.  * else VRRP3_IPv4:
  19.  * sum = RFC1071_checksum(ipv4_pseudoheader + vrrp_data)
  20.  * else if VRRP3_IPv6:
  21.  * sum = RFC1071_checksum(ipv6_pseudoheader + vrrp_data)
  22.  *
  23.  * JunOS flow:
  24.  * if VRRP2 or VRRP3_IPv4:
  25.  * sum = RFC1071_checksum(vrrp_data)
  26.  * else if VRRP3_IPv6:
  27.  * sum = RFC1071_checksum(ipv6_pseudoheader + vrrp_data)
  28.  *
  29.  * ----------[ reproduce ]----------
  30.  * $ set protocol vrrp version-3
  31.  * $ set interfaces irb.1 family inet address 10.1.0.10/24 vrrp-group 2 virtual-address 10.1.0.1
  32.  *
  33.  * ----------[ other notes ]----------
  34.  * Before JunOS Release 12.2, JunOS was doing a checksum without pseudo header
  35.  * for IPv6 packets as well. This changed in order to comply to the RFC.
  36.  * Additional a configuration knob "protocols vrrp checksum-without-pseudoheader"
  37.  * was introduced for interoperability to older JunOS releases.
  38.  * see http://www.juniper.net/techpubs/en_US/junos13.3/topics/concept/vrrpv3-junos-support.html
  39.  *
  40.  * ----------[ proposal ]----------
  41.  * - fix the VRRP3 IP4 implementation to comply to the RFC
  42.  * - add an additional knob: protocols vrrp checksum-without-ipv4-pseudoheader
  43.  */
  44.  
  45. #include <stdint.h>
  46. #include <stdio.h>
  47. #include <assert.h>
  48. #include <stdlib.h>
  49. #include <string.h>
  50.  
  51. // from https://www.ietf.org/rfc/rfc1071.txt
  52. // section 4.1
  53. uint16_t ip_checksum(unsigned short *addr, size_t count)
  54. {
  55. /* Compute Internet Checksum for "count" bytes
  56.   * beginning at location "addr".
  57.   */
  58. register long sum = 0;
  59.  
  60. while (count > 1)
  61. {
  62. /* This is the inner loop */
  63. sum += *addr++;
  64. count -= 2;
  65. }
  66.  
  67. /* Add left-over byte, if any */
  68. if (count > 0)
  69. sum += *(unsigned char *)addr;
  70.  
  71. /* Fold 32-bit sum to 16 bits */
  72. while (sum >> 16)
  73. sum = (sum & 0xffff) + (sum >> 16);
  74.  
  75. return ~sum;
  76. }
  77.  
  78. // from RFC 5798 section 5.2.8
  79. // http://tools.ietf.org/html/rfc5798#section-5.2.8
  80. //
  81. // The checksum is the 16-bit one's complement of the one's complement
  82. // sum of the entire VRRP message starting with the version field
  83. // =====> and a "pseudo-header" <====
  84. // as defined in Section 8.1 of [RFC2460]. The next
  85. // header field in the "pseudo-header" should be set to 112 (decimal)
  86. // for VRRP. For computing the checksum, the checksum field is set to
  87. // zero. See RFC1071 for more detail [RFC1071].
  88. uint16_t vrrp3_checksum(uint32_t src_ip, uint32_t dst_ip,
  89. unsigned short *data, size_t data_len)
  90. {
  91. uint8_t proto = 112;
  92. uint16_t sum;
  93.  
  94. // allocate buffer to prepend ipv4 pseudo header
  95. // see http://en.wikipedia.org/wiki/User_Datagram_Protocol#IPv4_Pseudo_Header
  96. unsigned buf_len = sizeof(unsigned short) * 6 + data_len;
  97. unsigned short *buf = malloc(buf_len);
  98. buf[0] = (src_ip >> 16);
  99. buf[1] = (src_ip & 0xffff);
  100. buf[2] = (dst_ip >> 16);
  101. buf[3] = (dst_ip & 0xffff);
  102. buf[4] = proto;
  103. buf[5] = data_len;
  104.  
  105. // copy VRRP packet payload to buffer
  106. memcpy(buf + 6, data, data_len);
  107.  
  108. // calculate checksum
  109. sum = ip_checksum(buf, buf_len);
  110.  
  111. free(buf);
  112. return sum;
  113. }
  114.  
  115. // VRRP2 checksum = ip checksum
  116. uint16_t vrrp2_checksum(unsigned short *data, size_t data_len)
  117. {
  118. return ip_checksum(data, data_len);
  119. }
  120.  
  121. int main()
  122. {
  123. // make sure the checksum implementation is sane
  124. // example from http://en.wikipedia.org/wiki/IPv4_header_checksum
  125. unsigned short packet_test[] = {
  126. 0x4500, 0x0073, 0x0000, 0x4000, 0x4011, 0xb861, 0xc0a8, 0x0001, 0xc0a8, 0x00c7
  127. };
  128. assert(ip_checksum(packet_test, sizeof(packet_test)) == 0);
  129.  
  130. // example VRRP3 IPv4 packet from MikroTik
  131. unsigned short vrrp3_mikrotik[] = {
  132. 0x3102, 0x6401, 0x0064, 0x75fb, 0x0a01, 0x0001
  133. };
  134. // example VRRP2 packet from JunOS
  135. unsigned short vrrp2_junos[] = {
  136. 0x210a, 0xc801, 0x0001, 0x0cf1, 0x0a01, 0x0001
  137. };
  138. // example VRRP3 IPv4 packet from JunOS
  139. unsigned short vrrp3_junos[] = {
  140. 0x3102, 0x6401, 0x0064, 0x6096, 0x0a01, 0x0001
  141. };
  142. // IP adresses of both routers
  143. uint32_t ip_junos = 0x0a01000a; // 10.1.0.10
  144. uint32_t ip_mikrotik = 0x0a01000b; // 10.1.0.11
  145. uint32_t dst_ip = 0xe0000012; // 224.0.0.18
  146.  
  147. // verify MikroTik checksum is correct according to RFC 5798
  148. assert(vrrp3_checksum(ip_mikrotik, dst_ip, vrrp3_mikrotik,
  149. sizeof(vrrp3_mikrotik)) == 0);
  150.  
  151. // verify VRRP2 checksum is correct in JunOS
  152. assert(vrrp2_checksum(vrrp2_junos, sizeof(vrrp2_junos)) == 0);
  153.  
  154. // show JunOS is doing a VRRP2 checksum for VRRP3 IPv4 packets
  155. assert(vrrp2_checksum(vrrp3_junos, sizeof(vrrp3_junos)) == 0);
  156.  
  157. // show that both checksum methods *are* incompatible
  158. assert(vrrp3_checksum(ip_junos, dst_ip, vrrp3_junos,
  159. sizeof(vrrp3_junos)) != 0);
  160. assert(vrrp2_checksum(vrrp3_mikrotik, sizeof(vrrp3_mikrotik)) != 0);
  161.  
  162. return 0;
  163. }
  164.