summaryrefslogtreecommitdiff
path: root/vrrpd/vrrp_packet.c
diff options
context:
space:
mode:
authorQuentin Young <qlyoung@cumulusnetworks.com>2018-12-19 16:48:36 +0000
committerQuentin Young <qlyoung@cumulusnetworks.com>2019-05-17 00:27:08 +0000
commit91188ca6c19868cfbf45df693cc599c3850f6526 (patch)
tree0e341f4e0122c5d241b560a5d2882aed995b71ae /vrrpd/vrrp_packet.c
parent862f2f374f27a541887a6dfc333583bdd4ad4de5 (diff)
vrrpd: read and validate vrrp advertisements
* Validate IPvX headers and packet contents * Remove filter of non-255 TTL IPv4 packets; better to receive, log and drop them ourselves * Set outgoing packet TTL / hop limit to 255 * Use existing sockopt functions Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
Diffstat (limited to 'vrrpd/vrrp_packet.c')
-rw-r--r--vrrpd/vrrp_packet.c166
1 files changed, 163 insertions, 3 deletions
diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c
index 631b8d7865..d305081574 100644
--- a/vrrpd/vrrp_packet.c
+++ b/vrrpd/vrrp_packet.c
@@ -18,13 +18,37 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <zebra.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
-#include "lib/memory.h"
-#include "lib/ipaddr.h"
#include "lib/checksum.h"
+#include "lib/ipaddr.h"
+#include "lib/memory.h"
#include "vrrp_packet.h"
+/* clang-format off */
+const char *vrrp_packet_names[16] = {
+ [0] = "Unknown",
+ [VRRP_TYPE_ADVERTISEMENT] = "ADVERTISEMENT",
+ [2] = "Unknown",
+ [3] = "Unknown",
+ [4] = "Unknown",
+ [5] = "Unknown",
+ [6] = "Unknown",
+ [7] = "Unknown",
+ [8] = "Unknown",
+ [9] = "Unknown",
+ [10] = "Unknown",
+ [11] = "Unknown",
+ [12] = "Unknown",
+ [13] = "Unknown",
+ [14] = "Unknown",
+ [15] = "Unknown",
+};
+/* clang-format on */
+
ssize_t vrrp_pkt_build(struct vrrp_pkt **pkt, uint8_t vrid, uint8_t prio,
uint16_t max_adver_int, uint8_t numip,
struct ipaddr **ips)
@@ -32,7 +56,7 @@ ssize_t vrrp_pkt_build(struct vrrp_pkt **pkt, uint8_t vrid, uint8_t prio,
bool v6 = IS_IPADDR_V6(ips[0]);
size_t addrsz = v6 ? sizeof(struct in6_addr) : sizeof(struct in_addr);
- size_t pktsize = sizeof(struct vrrp_hdr) + addrsz * numip;
+ size_t pktsize = VRRP_PKT_SIZE(v6 ? AF_INET6 : AF_INET, numip);
*pkt = XCALLOC(MTYPE_TMP, pktsize);
(*pkt)->hdr.vertype |= VRRP_VERSION << 4;
@@ -50,8 +74,144 @@ ssize_t vrrp_pkt_build(struct vrrp_pkt **pkt, uint8_t vrid, uint8_t prio,
}
(*pkt)->hdr.chksum = 0;
+ /* FIXME: v6 checksum */
uint16_t chksum = in_cksum(*pkt, pktsize);
(*pkt)->hdr.chksum = htons(chksum);
return pktsize;
}
+
+size_t vrrp_pkt_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt)
+{
+ if (buflen < 1)
+ return 0;
+
+ char tmpbuf[BUFSIZ];
+ size_t rs = 0;
+ struct vrrp_hdr *hdr = &pkt->hdr;
+
+ buf[0] = 0x00;
+ snprintf(tmpbuf, sizeof(tmpbuf), "Version: %u\n", (hdr->vertype >> 4));
+ rs += strlcat(buf, tmpbuf, buflen);
+ snprintf(tmpbuf, sizeof(tmpbuf), "Type: %u (%s)\n",
+ (hdr->vertype & 0x0F),
+ vrrp_packet_names[(hdr->vertype & 0x0F)]);
+ rs += strlcat(buf, tmpbuf, buflen);
+ snprintf(tmpbuf, sizeof(tmpbuf), "VRID: %u\n", hdr->vrid);
+ rs += strlcat(buf, tmpbuf, buflen);
+ snprintf(tmpbuf, sizeof(tmpbuf), "Priority: %u\n", hdr->priority);
+ rs += strlcat(buf, tmpbuf, buflen);
+ snprintf(tmpbuf, sizeof(tmpbuf), "Count IPvX: %u\n", hdr->naddr);
+ rs += strlcat(buf, tmpbuf, buflen);
+ snprintf(tmpbuf, sizeof(tmpbuf), "Max Adver Int: %u\n",
+ ntohs(hdr->v3.adver_int));
+ rs += strlcat(buf, tmpbuf, buflen);
+ snprintf(tmpbuf, sizeof(tmpbuf), "Checksum: %x\n", ntohs(hdr->chksum));
+ rs += strlcat(buf, tmpbuf, buflen);
+
+ return rs;
+}
+
+ssize_t vrrp_parse_datagram(int family, struct msghdr *m, size_t read,
+ 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)
+ : sizeof(struct in6_addr);
+
+ size_t pktsize;
+ uint8_t *buf = m->msg_iov->iov_base;
+
+#define VRRP_PKT_VCHECK(cond, _f, ...) \
+ do { \
+ if (!(cond)) { \
+ if (errmsg) \
+ snprintf(errmsg, errmsg_len, (_f), \
+ ##__VA_ARGS__); \
+ return -1; \
+ } \
+ } while (0)
+
+ /* IPvX header check */
+
+ if (family == AF_INET) {
+ VRRP_PKT_VCHECK(
+ read >= sizeof(struct ip),
+ "Datagram not large enough to contain IP header");
+
+ struct ip *ip = (struct ip *)buf;
+
+ /* IP total length check */
+ VRRP_PKT_VCHECK(
+ ntohs(ip->ip_len) == read,
+ "IPv4 packet length field does not match # received bytes; %u != %lu",
+ ntohs(ip->ip_len), read);
+
+ /* TTL check */
+ VRRP_PKT_VCHECK(ip->ip_ttl == 255, "IPv4 TTL is not 255");
+
+ *pkt = (struct vrrp_pkt *)(buf + (ip->ip_hl << 2));
+ pktsize = read - (ip->ip_hl << 2);
+
+ /* IP empty packet check */
+ VRRP_PKT_VCHECK(pktsize > 0, "IPv4 packet has no payload");
+ } 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)
+ break;
+ }
+
+ VRRP_PKT_VCHECK(!!c, "IPv6 Hop Limit not received");
+
+ uint8_t *hoplimit = CMSG_DATA(c);
+ VRRP_PKT_VCHECK(*hoplimit == 255, "IPv6 Hop Limit is not 255");
+
+ *pkt = (struct vrrp_pkt *)buf;
+ pktsize = read;
+ } else {
+ assert(!"Unknown address family");
+ }
+
+ /* Size check */
+ size_t minsize = (family == AF_INET) ? VRRP_MIN_PKT_SIZE_V4
+ : VRRP_MIN_PKT_SIZE_V6;
+ 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_PKT_VCHECK(pktsize <= maxsize,
+ "VRRP packet is oversized (%lu > %lu)", pktsize,
+ VRRP_MAX_PKT_SIZE);
+ /* 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",
+ (*pkt)->hdr.vertype & 0x0f);
+ /* Priority check */
+ VRRP_PKT_VCHECK((*pkt)->hdr.priority == 255
+ || (*pkt)->hdr.priority == 0,
+ "Bad priority %u", (*pkt)->hdr.priority);
+ /* # addresses check */
+ size_t ves = VRRP_PKT_SIZE(family, (*pkt)->hdr.naddr);
+ VRRP_PKT_VCHECK(pktsize == ves, "Packet has incorrect # addresses");
+ /* FIXME: checksum check */
+ /* ... */
+
+ /* 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);
+ p += addrsz;
+ }
+
+ /* Everything checks out */
+ return pktsize;
+}