#include "lib/memory.h"
#include "vrrp.h"
-#include "vrrp_memory.h"
+#include "vrrp_debug.h"
#include "vrrp_packet.h"
+DEFINE_MTYPE_STATIC(VRRPD, VRRP_PKT, "VRRP packet")
+
/* clang-format off */
-const char *vrrp_packet_names[16] = {
+static const char *const vrrp_packet_names[16] = {
[0] = "Unknown",
[VRRP_TYPE_ADVERTISEMENT] = "ADVERTISEMENT",
[2] = "Unknown",
bool v6 = (src->ipa_type == IPADDR_V6);
uint16_t chksum_pre = pkt->hdr.chksum;
+
pkt->hdr.chksum = 0;
if (v6) {
struct ipv6_ph ph = {};
+
ph.src = src->ipaddr_v6;
inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, &ph.dst);
ph.ulpl = htons(pktsize);
- ph.next_hdr = 112;
+ ph.next_hdr = IPPROTO_VRRP;
chksum = in_cksum_with_ph6(&ph, pkt, pktsize);
- } else {
+ } else if (!v6 && ((pkt->hdr.vertype >> 4) == 3)) {
struct ipv4_ph ph = {};
+
ph.src = src->ipaddr_v4;
inet_pton(AF_INET, VRRP_MCASTV4_GROUP_STR, &ph.dst);
- ph.proto = 112;
+ ph.proto = IPPROTO_VRRP;
ph.len = htons(pktsize);
chksum = in_cksum_with_ph4(&ph, pkt, pktsize);
+ } else if (!v6 && ((pkt->hdr.vertype >> 4) == 2)) {
+ chksum = in_cksum(pkt, pktsize);
+ } else {
+ assert(!"Invalid VRRP protocol version");
}
pkt->hdr.chksum = chksum_pre;
uint16_t max_adver_int, uint8_t numip,
struct ipaddr **ips)
{
+ bool v6 = false;
+ size_t addrsz = 0;
+
assert(version >= 2 && version <= 3);
- bool v6 = IS_IPADDR_V6(ips[0]);
+ if (numip > 0) {
+ v6 = IS_IPADDR_V6(ips[0]);
+ addrsz = IPADDRSZ(ips[0]);
+ }
+
+ assert(!(version == 2 && v6));
+
+ size_t pktsize = VRRP_PKT_SIZE(v6 ? AF_INET6 : AF_INET, version, numip);
- size_t addrsz = IPADDRSZ(ips[0]);
- size_t pktsize = VRRP_PKT_SIZE(v6 ? AF_INET6 : AF_INET, numip);
*pkt = XCALLOC(MTYPE_VRRP_PKT, pktsize);
(*pkt)->hdr.vertype |= version << 4;
(*pkt)->hdr.vrid = vrid;
(*pkt)->hdr.priority = prio;
(*pkt)->hdr.naddr = numip;
- (*pkt)->hdr.v3.adver_int = htons(max_adver_int);
+ if (version == 3)
+ (*pkt)->hdr.v3.adver_int = htons(max_adver_int);
+ else if (version == 2) {
+ (*pkt)->hdr.v2.auth_type = 0;
+ (*pkt)->hdr.v2.adver_int = MAX(max_adver_int / 100, 1);
+ }
uint8_t *aptr = (void *)(*pkt)->addrs;
return pktsize;
}
+void vrrp_pkt_free(struct vrrp_pkt *pkt)
+{
+ XFREE(MTYPE_VRRP_PKT, pkt);
+}
+
size_t vrrp_pkt_adver_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt)
{
if (buflen < 1)
struct vrrp_hdr *hdr = &pkt->hdr;
buf[0] = 0x00;
- snprintf(tmpbuf, sizeof(tmpbuf), "Version: %u\n", (hdr->vertype >> 4));
+ snprintf(tmpbuf, sizeof(tmpbuf), "version %u, ", (hdr->vertype >> 4));
rs += strlcat(buf, tmpbuf, buflen);
- snprintf(tmpbuf, sizeof(tmpbuf), "Type: %u (%s)\n",
+ snprintf(tmpbuf, sizeof(tmpbuf), "type %u (%s), ",
(hdr->vertype & 0x0F),
vrrp_packet_names[(hdr->vertype & 0x0F)]);
rs += strlcat(buf, tmpbuf, buflen);
- snprintf(tmpbuf, sizeof(tmpbuf), "VRID: %u\n", hdr->vrid);
+ snprintf(tmpbuf, sizeof(tmpbuf), "vrid %u, ", hdr->vrid);
rs += strlcat(buf, tmpbuf, buflen);
- snprintf(tmpbuf, sizeof(tmpbuf), "Priority: %u\n", hdr->priority);
+ snprintf(tmpbuf, sizeof(tmpbuf), "priority %u, ", hdr->priority);
rs += strlcat(buf, tmpbuf, buflen);
- snprintf(tmpbuf, sizeof(tmpbuf), "Count IPvX: %u\n", hdr->naddr);
+ snprintf(tmpbuf, sizeof(tmpbuf), "#%u addresses, ", hdr->naddr);
rs += strlcat(buf, tmpbuf, buflen);
- snprintf(tmpbuf, sizeof(tmpbuf), "Max Adver Int: %u\n",
+ snprintf(tmpbuf, sizeof(tmpbuf), "max adver int %u, ",
ntohs(hdr->v3.adver_int));
rs += strlcat(buf, tmpbuf, buflen);
- snprintf(tmpbuf, sizeof(tmpbuf), "Checksum: %x\n", ntohs(hdr->chksum));
+ snprintf(tmpbuf, sizeof(tmpbuf), "checksum %x", ntohs(hdr->chksum));
rs += strlcat(buf, tmpbuf, buflen);
return rs;
}
-ssize_t vrrp_pkt_parse_datagram(int family, struct msghdr *m, size_t read,
- struct ipaddr *src, struct vrrp_pkt **pkt,
- char *errmsg, size_t errmsg_len)
+ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m,
+ size_t read, struct ipaddr *src,
+ struct vrrp_pkt **pkt, char *errmsg,
+ size_t errmsg_len)
{
/* Source (MAC & IP), Dest (MAC & IP) TTL validation done by kernel */
size_t addrsz = (family == AF_INET) ? sizeof(struct in_addr)
/* IP total length check */
VRRP_PKT_VCHECK(
ntohs(ip->ip_len) == read,
- "IPv4 packet length field does not match # received bytes; %u != %lu",
+ "IPv4 packet length field does not match # received bytes; %" PRIu16
+ "!= %zu",
ntohs(ip->ip_len), read);
/* TTL check */
VRRP_PKT_VCHECK(pktsize > 0, "IPv4 packet has no payload");
/* Extract source address */
+ struct sockaddr_in *sa = m->msg_name;
+
src->ipa_type = IPADDR_V4;
- src->ipaddr_v4 = ip->ip_src;
+ src->ipaddr_v4 = sa->sin_addr;
} else if (family == AF_INET6) {
struct cmsghdr *c;
+
for (c = CMSG_FIRSTHDR(m); c != NULL; CMSG_NXTHDR(m, c)) {
if (c->cmsg_level == IPPROTO_IPV6
&& c->cmsg_type == IPV6_HOPLIMIT)
VRRP_PKT_VCHECK(!!c, "IPv6 Hop Limit not received");
uint8_t *hoplimit = CMSG_DATA(c);
+
VRRP_PKT_VCHECK(*hoplimit == 255,
"IPv6 Hop Limit is %" PRIu8 "; should be 255",
*hoplimit);
pktsize = read;
/* Extract source address */
- src->ipa_type = IPADDR_V6;
struct sockaddr_in6 *sa = m->msg_name;
+
+ src->ipa_type = IPADDR_V6;
memcpy(&src->ipaddr_v6, &sa->sin6_addr,
sizeof(struct in6_addr));
} else {
size_t maxsize = (family == AF_INET) ? VRRP_MAX_PKT_SIZE_V4
: VRRP_MAX_PKT_SIZE_V6;
VRRP_PKT_VCHECK(pktsize >= minsize,
- "VRRP packet is undersized (%lu < %lu)", pktsize,
- VRRP_MIN_PKT_SIZE);
+ "VRRP packet is undersized (%zu < %zu)", pktsize,
+ minsize);
VRRP_PKT_VCHECK(pktsize <= maxsize,
- "VRRP packet is oversized (%lu > %lu)", pktsize,
- VRRP_MAX_PKT_SIZE);
+ "VRRP packet is oversized (%zu > %zu)", pktsize,
+ maxsize);
+
+ /* Version check */
+ uint8_t pktver = (*pkt)->hdr.vertype >> 4;
+
+ VRRP_PKT_VCHECK(pktver == version, "Bad version %u", pktver);
/* Checksum check */
uint16_t chksum = vrrp_pkt_checksum(*pkt, pktsize, src);
+
VRRP_PKT_VCHECK((*pkt)->hdr.chksum == chksum,
- "Bad VRRP checksum %" PRIu16 "; should be %" PRIu16 "",
+ "Bad VRRP checksum %" PRIx16 "; should be %" PRIx16 "",
(*pkt)->hdr.chksum, chksum);
- /* Version check */
- VRRP_PKT_VCHECK(((*pkt)->hdr.vertype >> 4) != 2, "VRPPv2 unsupported");
- VRRP_PKT_VCHECK(((*pkt)->hdr.vertype >> 4) == 3, "Bad version %u",
- (*pkt)->hdr.vertype >> 4);
/* Type check */
- VRRP_PKT_VCHECK(((*pkt)->hdr.vertype & 0x0F) == 1, "Bad type %u",
+ VRRP_PKT_VCHECK(((*pkt)->hdr.vertype & 0x0F) == 1, "Bad type %" PRIu8,
(*pkt)->hdr.vertype & 0x0f);
- /* # addresses check */
- size_t ves = VRRP_PKT_SIZE(family, (*pkt)->hdr.naddr);
- VRRP_PKT_VCHECK(pktsize == ves, "Packet has incorrect # addresses");
+
+ /* Exact size check */
+ size_t ves = VRRP_PKT_SIZE(family, pktver, (*pkt)->hdr.naddr);
+
+ VRRP_PKT_VCHECK(pktsize == ves, "Packet has incorrect # addresses%s",
+ pktver == 2 ? " or missing auth fields" : "");
+
+ /* auth type check */
+ if (version == 2)
+ VRRP_PKT_VCHECK((*pkt)->hdr.v2.auth_type == 0,
+ "Bad authentication type %" PRIu8,
+ (*pkt)->hdr.v2.auth_type);
/* Addresses check */
char vbuf[INET6_ADDRSTRLEN];
uint8_t *p = (uint8_t *)(*pkt)->addrs;
+
for (uint8_t i = 0; i < (*pkt)->hdr.naddr; i++) {
VRRP_PKT_VCHECK(inet_ntop(family, p, vbuf, sizeof(vbuf)),
- "Bad IP address, #%u", i);
+ "Bad IP address, #%" PRIu8, i);
p += addrsz;
}