]> git.proxmox.com Git - mirror_frr.git/blame - vrrpd/vrrp_packet.c
lib: Fix overlapping memory type
[mirror_frr.git] / vrrpd / vrrp_packet.c
CommitLineData
acddc0ed 1// SPDX-License-Identifier: GPL-2.0-or-later
5435a2bf 2/*
63d4bd12
QY
3 * VRRP packet crafting.
4 * Copyright (C) 2018-2019 Cumulus Networks, Inc.
5 * Quentin Young
5435a2bf
QY
6 */
7#include <zebra.h>
91188ca6
QY
8#include <netinet/in.h>
9#include <netinet/ip.h>
10#include <netinet/ip6.h>
5435a2bf 11
3eca3857 12#include "lib/checksum.h"
91188ca6
QY
13#include "lib/ipaddr.h"
14#include "lib/memory.h"
5435a2bf 15
8071d5c3 16#include "vrrp.h"
b637bcd4 17#include "vrrp_debug.h"
5435a2bf
QY
18#include "vrrp_packet.h"
19
bf8d3d6a 20DEFINE_MTYPE_STATIC(VRRPD, VRRP_PKT, "VRRP packet");
7c136b08 21
91188ca6 22/* clang-format off */
2b64873d 23static const char *const vrrp_packet_names[16] = {
91188ca6
QY
24 [0] = "Unknown",
25 [VRRP_TYPE_ADVERTISEMENT] = "ADVERTISEMENT",
26 [2] = "Unknown",
27 [3] = "Unknown",
28 [4] = "Unknown",
29 [5] = "Unknown",
30 [6] = "Unknown",
31 [7] = "Unknown",
32 [8] = "Unknown",
33 [9] = "Unknown",
34 [10] = "Unknown",
35 [11] = "Unknown",
36 [12] = "Unknown",
37 [13] = "Unknown",
38 [14] = "Unknown",
39 [15] = "Unknown",
40};
41/* clang-format on */
42
d9e01e1c
QY
43/*
44 * Compute the VRRP checksum.
45 *
46 * Checksum is not set in the packet, just computed.
47 *
48 * pkt
49 * VRRP packet, fully filled out except for checksum field.
50 *
51 * pktsize
52 * sizeof(*pkt)
53 *
54 * src
55 * IP address that pkt will be transmitted from.
56 *
57 * Returns:
58 * VRRP checksum in network byte order.
59 */
60static uint16_t vrrp_pkt_checksum(struct vrrp_pkt *pkt, size_t pktsize,
9f2379bd 61 struct ipaddr *src, bool ipv4_ph)
d9e01e1c
QY
62{
63 uint16_t chksum;
64 bool v6 = (src->ipa_type == IPADDR_V6);
65
66 uint16_t chksum_pre = pkt->hdr.chksum;
2fff50ec 67
d9e01e1c
QY
68 pkt->hdr.chksum = 0;
69
70 if (v6) {
71 struct ipv6_ph ph = {};
2fff50ec 72
d9e01e1c
QY
73 ph.src = src->ipaddr_v6;
74 inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, &ph.dst);
75 ph.ulpl = htons(pktsize);
723123f3 76 ph.next_hdr = IPPROTO_VRRP;
d9e01e1c 77 chksum = in_cksum_with_ph6(&ph, pkt, pktsize);
99966840 78 } else if (!v6 && ((pkt->hdr.vertype >> 4) == 3)) {
9f2379bd
SY
79 if (ipv4_ph) {
80 struct ipv4_ph ph = {};
81
82 ph.src = src->ipaddr_v4;
83 inet_pton(AF_INET, VRRP_MCASTV4_GROUP_STR, &ph.dst);
84 ph.proto = IPPROTO_VRRP;
85 ph.len = htons(pktsize);
86 chksum = in_cksum_with_ph4(&ph, pkt, pktsize);
87 } else
88 chksum = in_cksum(pkt, pktsize);
99966840
QY
89 } else if (!v6 && ((pkt->hdr.vertype >> 4) == 2)) {
90 chksum = in_cksum(pkt, pktsize);
91 } else {
92 assert(!"Invalid VRRP protocol version");
d9e01e1c
QY
93 }
94
95 pkt->hdr.chksum = chksum_pre;
96
97 return chksum;
98}
99
100ssize_t vrrp_pkt_adver_build(struct vrrp_pkt **pkt, struct ipaddr *src,
101 uint8_t version, uint8_t vrid, uint8_t prio,
102 uint16_t max_adver_int, uint8_t numip,
9f2379bd 103 struct ipaddr **ips, bool ipv4_ph)
5435a2bf 104{
3d55d467
QY
105 bool v6 = false;
106 size_t addrsz = 0;
3eca3857 107
99966840 108 assert(version >= 2 && version <= 3);
99966840 109
3d55d467
QY
110 if (numip > 0) {
111 v6 = IS_IPADDR_V6(ips[0]);
112 addrsz = IPADDRSZ(ips[0]);
113 }
114
e1a32d76
QY
115 assert(!(version == 2 && v6));
116
c2034b25 117 size_t pktsize = VRRP_PKT_SIZE(v6 ? AF_INET6 : AF_INET, version, numip);
2fff50ec 118
72df9d93 119 *pkt = XCALLOC(MTYPE_VRRP_PKT, pktsize);
3eca3857 120
d9e01e1c 121 (*pkt)->hdr.vertype |= version << 4;
3eca3857
QY
122 (*pkt)->hdr.vertype |= VRRP_TYPE_ADVERTISEMENT;
123 (*pkt)->hdr.vrid = vrid;
124 (*pkt)->hdr.priority = prio;
125 (*pkt)->hdr.naddr = numip;
99966840
QY
126 if (version == 3)
127 (*pkt)->hdr.v3.adver_int = htons(max_adver_int);
128 else if (version == 2) {
129 (*pkt)->hdr.v2.auth_type = 0;
130 (*pkt)->hdr.v2.adver_int = MAX(max_adver_int / 100, 1);
131 }
3eca3857 132
862f2f37
QY
133 uint8_t *aptr = (void *)(*pkt)->addrs;
134
3eca3857 135 for (int i = 0; i < numip; i++) {
862f2f37
QY
136 memcpy(aptr, &ips[i]->ip.addr, addrsz);
137 aptr += addrsz;
3eca3857 138 }
8071d5c3 139
9f2379bd 140 (*pkt)->hdr.chksum = vrrp_pkt_checksum(*pkt, pktsize, src, ipv4_ph);
3eca3857
QY
141
142 return pktsize;
5435a2bf 143}
91188ca6 144
7c136b08
DL
145void vrrp_pkt_free(struct vrrp_pkt *pkt)
146{
147 XFREE(MTYPE_VRRP_PKT, pkt);
148}
149
d9e01e1c 150size_t vrrp_pkt_adver_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt)
91188ca6
QY
151{
152 if (buflen < 1)
153 return 0;
154
155 char tmpbuf[BUFSIZ];
156 size_t rs = 0;
157 struct vrrp_hdr *hdr = &pkt->hdr;
158
159 buf[0] = 0x00;
613b45b0 160 snprintf(tmpbuf, sizeof(tmpbuf), "version %u, ", (hdr->vertype >> 4));
91188ca6 161 rs += strlcat(buf, tmpbuf, buflen);
613b45b0 162 snprintf(tmpbuf, sizeof(tmpbuf), "type %u (%s), ",
91188ca6
QY
163 (hdr->vertype & 0x0F),
164 vrrp_packet_names[(hdr->vertype & 0x0F)]);
165 rs += strlcat(buf, tmpbuf, buflen);
613b45b0 166 snprintf(tmpbuf, sizeof(tmpbuf), "vrid %u, ", hdr->vrid);
91188ca6 167 rs += strlcat(buf, tmpbuf, buflen);
613b45b0 168 snprintf(tmpbuf, sizeof(tmpbuf), "priority %u, ", hdr->priority);
91188ca6 169 rs += strlcat(buf, tmpbuf, buflen);
613b45b0 170 snprintf(tmpbuf, sizeof(tmpbuf), "#%u addresses, ", hdr->naddr);
91188ca6 171 rs += strlcat(buf, tmpbuf, buflen);
613b45b0 172 snprintf(tmpbuf, sizeof(tmpbuf), "max adver int %u, ",
91188ca6
QY
173 ntohs(hdr->v3.adver_int));
174 rs += strlcat(buf, tmpbuf, buflen);
613b45b0 175 snprintf(tmpbuf, sizeof(tmpbuf), "checksum %x", ntohs(hdr->chksum));
91188ca6
QY
176 rs += strlcat(buf, tmpbuf, buflen);
177
178 return rs;
179}
180
9f2379bd
SY
181ssize_t vrrp_pkt_parse_datagram(int family, int version, bool ipv4_ph,
182 struct msghdr *m, size_t read,
183 struct ipaddr *src, struct vrrp_pkt **pkt,
184 char *errmsg, size_t errmsg_len)
91188ca6
QY
185{
186 /* Source (MAC & IP), Dest (MAC & IP) TTL validation done by kernel */
187 size_t addrsz = (family == AF_INET) ? sizeof(struct in_addr)
188 : sizeof(struct in6_addr);
189
190 size_t pktsize;
191 uint8_t *buf = m->msg_iov->iov_base;
192
193#define VRRP_PKT_VCHECK(cond, _f, ...) \
194 do { \
195 if (!(cond)) { \
196 if (errmsg) \
197 snprintf(errmsg, errmsg_len, (_f), \
198 ##__VA_ARGS__); \
199 return -1; \
200 } \
201 } while (0)
202
203 /* IPvX header check */
204
205 if (family == AF_INET) {
206 VRRP_PKT_VCHECK(
207 read >= sizeof(struct ip),
208 "Datagram not large enough to contain IP header");
209
210 struct ip *ip = (struct ip *)buf;
211
212 /* IP total length check */
213 VRRP_PKT_VCHECK(
214 ntohs(ip->ip_len) == read,
6cde4b45 215 "IPv4 packet length field does not match # received bytes; %hu!= %zu",
91188ca6
QY
216 ntohs(ip->ip_len), read);
217
218 /* TTL check */
dad18a2f 219 VRRP_PKT_VCHECK(ip->ip_ttl == 255,
6cde4b45 220 "IPv4 TTL is %hhu; should be 255",
dad18a2f 221 ip->ip_ttl);
91188ca6
QY
222
223 *pkt = (struct vrrp_pkt *)(buf + (ip->ip_hl << 2));
224 pktsize = read - (ip->ip_hl << 2);
225
226 /* IP empty packet check */
227 VRRP_PKT_VCHECK(pktsize > 0, "IPv4 packet has no payload");
d04bb25a
QY
228
229 /* Extract source address */
fa211f1c 230 struct sockaddr_in *sa = m->msg_name;
2fff50ec 231
d04bb25a 232 src->ipa_type = IPADDR_V4;
fa211f1c 233 src->ipaddr_v4 = sa->sin_addr;
91188ca6
QY
234 } else if (family == AF_INET6) {
235 struct cmsghdr *c;
2fff50ec 236
91188ca6
QY
237 for (c = CMSG_FIRSTHDR(m); c != NULL; CMSG_NXTHDR(m, c)) {
238 if (c->cmsg_level == IPPROTO_IPV6
239 && c->cmsg_type == IPV6_HOPLIMIT)
240 break;
241 }
242
243 VRRP_PKT_VCHECK(!!c, "IPv6 Hop Limit not received");
244
245 uint8_t *hoplimit = CMSG_DATA(c);
2fff50ec 246
dad18a2f 247 VRRP_PKT_VCHECK(*hoplimit == 255,
6cde4b45 248 "IPv6 Hop Limit is %hhu; should be 255",
dad18a2f 249 *hoplimit);
91188ca6
QY
250
251 *pkt = (struct vrrp_pkt *)buf;
252 pktsize = read;
d04bb25a
QY
253
254 /* Extract source address */
d04bb25a 255 struct sockaddr_in6 *sa = m->msg_name;
2fff50ec 256
fa211f1c 257 src->ipa_type = IPADDR_V6;
d04bb25a
QY
258 memcpy(&src->ipaddr_v6, &sa->sin6_addr,
259 sizeof(struct in6_addr));
91188ca6
QY
260 } else {
261 assert(!"Unknown address family");
262 }
263
264 /* Size check */
265 size_t minsize = (family == AF_INET) ? VRRP_MIN_PKT_SIZE_V4
266 : VRRP_MIN_PKT_SIZE_V6;
267 size_t maxsize = (family == AF_INET) ? VRRP_MAX_PKT_SIZE_V4
268 : VRRP_MAX_PKT_SIZE_V6;
269 VRRP_PKT_VCHECK(pktsize >= minsize,
bb95fd82
QY
270 "VRRP packet is undersized (%zu < %zu)", pktsize,
271 minsize);
91188ca6 272 VRRP_PKT_VCHECK(pktsize <= maxsize,
bb95fd82
QY
273 "VRRP packet is oversized (%zu > %zu)", pktsize,
274 maxsize);
d04bb25a 275
8cb3d803
QY
276 /* Version check */
277 uint8_t pktver = (*pkt)->hdr.vertype >> 4;
2fff50ec 278
8cb3d803
QY
279 VRRP_PKT_VCHECK(pktver == version, "Bad version %u", pktver);
280
d04bb25a 281 /* Checksum check */
9f2379bd 282 uint16_t chksum = vrrp_pkt_checksum(*pkt, pktsize, src, ipv4_ph);
2fff50ec 283
d04bb25a 284 VRRP_PKT_VCHECK((*pkt)->hdr.chksum == chksum,
6cde4b45 285 "Bad VRRP checksum %hx; should be %hx",
d04bb25a
QY
286 (*pkt)->hdr.chksum, chksum);
287
91188ca6 288 /* Type check */
0b1321e2 289 VRRP_PKT_VCHECK(((*pkt)->hdr.vertype & 0x0F) == 1, "Bad type %u",
91188ca6 290 (*pkt)->hdr.vertype & 0x0f);
8cb3d803 291
c2034b25
QY
292 /* Exact size check */
293 size_t ves = VRRP_PKT_SIZE(family, pktver, (*pkt)->hdr.naddr);
2fff50ec 294
c2034b25
QY
295 VRRP_PKT_VCHECK(pktsize == ves, "Packet has incorrect # addresses%s",
296 pktver == 2 ? " or missing auth fields" : "");
91188ca6 297
99966840
QY
298 /* auth type check */
299 if (version == 2)
300 VRRP_PKT_VCHECK((*pkt)->hdr.v2.auth_type == 0,
6cde4b45 301 "Bad authentication type %hhu",
99966840
QY
302 (*pkt)->hdr.v2.auth_type);
303
91188ca6
QY
304 /* Addresses check */
305 char vbuf[INET6_ADDRSTRLEN];
306 uint8_t *p = (uint8_t *)(*pkt)->addrs;
2fff50ec 307
91188ca6
QY
308 for (uint8_t i = 0; i < (*pkt)->hdr.naddr; i++) {
309 VRRP_PKT_VCHECK(inet_ntop(family, p, vbuf, sizeof(vbuf)),
6cde4b45 310 "Bad IP address, #%hhu", i);
91188ca6
QY
311 p += addrsz;
312 }
313
314 /* Everything checks out */
315 return pktsize;
316}