Skip to content

Instantly share code, notes, and snippets.

@sigeryang
Created September 20, 2022 13:14
Show Gist options
  • Save sigeryang/a73e353d0fe5e7ef7e8feed600c63cbb to your computer and use it in GitHub Desktop.
Save sigeryang/a73e353d0fe5e7ef7e8feed600c63cbb to your computer and use it in GitHub Desktop.
VRRP Checksum Test
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
// https://github.com/FRRouting/frr/blob/master/lib/checksum.c
#define add_carry(dst, add) \
do { \
typeof(dst) _add = (add); \
dst += _add; \
if (dst < _add) \
dst++; \
} while (0)
uint16_t in_cksumv(const struct iovec *iov, size_t iov_len)
{
const struct iovec *iov_end;
uint32_t sum = 0;
union {
uint8_t bytes[2];
uint16_t word;
} wordbuf;
bool have_oddbyte = false;
for (iov_end = iov + iov_len; iov < iov_end; iov++) {
const uint8_t *ptr, *end;
ptr = (const uint8_t *)iov->iov_base;
end = ptr + iov->iov_len;
if (ptr == end)
continue;
if (have_oddbyte) {
have_oddbyte = false;
wordbuf.bytes[1] = *ptr++;
add_carry(sum, wordbuf.word);
}
while (ptr + 8 <= end) {
add_carry(sum, *(const uint32_t *)(ptr + 0));
add_carry(sum, *(const uint32_t *)(ptr + 4));
ptr += 8;
}
while (ptr + 2 <= end) {
add_carry(sum, *(const uint16_t *)ptr);
ptr += 2;
}
if (ptr + 1 <= end) {
wordbuf.bytes[0] = *ptr++;
have_oddbyte = true;
}
}
if (have_oddbyte) {
wordbuf.bytes[1] = 0;
add_carry(sum, wordbuf.word);
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
return ~sum;
}
#define array_size(x) (sizeof(x) / sizeof(x[0]))
// https://github.com/FRRouting/frr/blob/master/lib/checksum.h
struct ipv4_ph {
struct in_addr src;
struct in_addr dst;
uint8_t rsvd;
uint8_t proto;
uint16_t len;
} __attribute__((packed));
struct ipv6_ph {
struct in6_addr src;
struct in6_addr dst;
uint32_t ulpl;
uint8_t zero[3];
uint8_t next_hdr;
} __attribute__((packed));
static inline uint16_t in_cksum(const void *data, size_t nbytes)
{
struct iovec iov[1];
iov[0].iov_base = (void *)data;
iov[0].iov_len = nbytes;
return in_cksumv(iov, array_size(iov));
}
static inline uint16_t in_cksum_with_ph4(const struct ipv4_ph *ph,
const void *data, size_t nbytes)
{
struct iovec iov[2];
iov[0].iov_base = (void *)ph;
iov[0].iov_len = sizeof(*ph);
iov[1].iov_base = (void *)data;
iov[1].iov_len = nbytes;
return in_cksumv(iov, array_size(iov));
}
enum ipaddr_type_t {
IPADDR_NONE = AF_UNSPEC,
IPADDR_V4 = AF_INET,
IPADDR_V6 = AF_INET6,
};
struct ipaddr {
enum ipaddr_type_t ipa_type;
union {
uint8_t addr;
struct in_addr _v4_addr;
struct in6_addr _v6_addr;
} ip;
#define ipaddr_v4 ip._v4_addr
#define ipaddr_v6 ip._v6_addr
};
#define IPPROTO_VRRP 112
struct vrrp_hdr {
uint8_t vertype;
uint8_t vrid;
uint8_t priority;
uint8_t naddr;
union {
struct {
uint8_t auth_type;
uint8_t adver_int;
} v2;
struct {
uint16_t adver_int;
} v3;
};
uint16_t chksum;
} __attribute__((packed));
struct vrrp_pkt {
struct vrrp_hdr hdr;
union {
struct in_addr v4;
struct in6_addr v6;
} addrs[];
} __attribute__((packed));
#define VRRP_MCASTV4_GROUP_STR "224.0.0.18"
#define VRRP_MCASTV6_GROUP_STR "ff02:0:0:0:0:0:0:12"
#define DATA_LENGTH(x) (sizeof(x) - 1)
#define FRR_VRRP_MESSAGE "\x31\x32\xbe\x01\x00\x64\x86\x83\xc0\xa8\x14\x03"
#define FRR_SOURCE "192.168.20.1"
#define CISCO_VRRP_MESSAGE "\x31\x32\x64\x01\x00\xc8\x95\x58\xc0\xa8\x14\x03"
#define CISCO_SOURCE "192.168.20.4"
struct test {
const char *name;
const char *message;
size_t length;
const char *source;
} tests[] = {
{ "FRR", FRR_VRRP_MESSAGE, DATA_LENGTH(FRR_VRRP_MESSAGE), FRR_SOURCE },
{ "CISCO", CISCO_VRRP_MESSAGE, DATA_LENGTH(CISCO_VRRP_MESSAGE), CISCO_SOURCE },
};
int main() {
for (int i = 0; i < array_size(tests); i++) {
const struct test *t = &tests[i];
struct vrrp_pkt *pkt = malloc(t->length);
memcpy(pkt, t->message, t->length);
struct in_addr src;
inet_aton(t->source, &src);
uint16_t real_chksum = pkt->hdr.chksum;
pkt->hdr.chksum = 0;
// Checksum WITHOUT pseudo-header
uint16_t chksum_without_ph = in_cksum(pkt, t->length);
// Checksum WITH pseudo-header
uint16_t chksum_with_ph;
// https://github.com/FRRouting/frr/blob/master/vrrpd/vrrp_packet.c
struct ipv4_ph ph = {};
ph.src = src;
inet_pton(AF_INET, VRRP_MCASTV4_GROUP_STR, &ph.dst);
ph.proto = IPPROTO_VRRP;
ph.len = htons(t->length);
chksum_with_ph = in_cksum_with_ph4(&ph, pkt, t->length);
if (real_chksum == chksum_without_ph) {
printf("%s (%s) checksum method: WITHOUT PH\n", t->name, t->source);
} else if (real_chksum == chksum_with_ph) {
printf("%s (%s) checksum method: WITH PH\n", t->name, t->source);
} else {
printf("%s (%s) checksum method: N/A\n", t->name, t->source);
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment