]> git.proxmox.com Git - mirror_frr.git/blob - vrrpd/vrrp_packet.c
Merge pull request #13332 from LabNConsulting/chopps/new-opts
[mirror_frr.git] / vrrpd / vrrp_packet.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * VRRP packet crafting.
4 * Copyright (C) 2018-2019 Cumulus Networks, Inc.
5 * Quentin Young
6 */
7 #include <zebra.h>
8 #include <netinet/in.h>
9 #include <netinet/ip.h>
10 #include <netinet/ip6.h>
11
12 #include "lib/checksum.h"
13 #include "lib/ipaddr.h"
14 #include "lib/memory.h"
15
16 #include "vrrp.h"
17 #include "vrrp_debug.h"
18 #include "vrrp_packet.h"
19
20 DEFINE_MTYPE_STATIC(VRRPD, VRRP_PKT, "VRRP packet");
21
22 /* clang-format off */
23 static const char *const vrrp_packet_names[16] = {
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
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 */
60 static uint16_t vrrp_pkt_checksum(struct vrrp_pkt *pkt, size_t pktsize,
61 struct ipaddr *src, bool ipv4_ph)
62 {
63 uint16_t chksum;
64 bool v6 = (src->ipa_type == IPADDR_V6);
65
66 uint16_t chksum_pre = pkt->hdr.chksum;
67
68 pkt->hdr.chksum = 0;
69
70 if (v6) {
71 struct ipv6_ph ph = {};
72
73 ph.src = src->ipaddr_v6;
74 inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, &ph.dst);
75 ph.ulpl = htons(pktsize);
76 ph.next_hdr = IPPROTO_VRRP;
77 chksum = in_cksum_with_ph6(&ph, pkt, pktsize);
78 } else if (!v6 && ((pkt->hdr.vertype >> 4) == 3)) {
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);
89 } else if (!v6 && ((pkt->hdr.vertype >> 4) == 2)) {
90 chksum = in_cksum(pkt, pktsize);
91 } else {
92 assert(!"Invalid VRRP protocol version");
93 }
94
95 pkt->hdr.chksum = chksum_pre;
96
97 return chksum;
98 }
99
100 ssize_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,
103 struct ipaddr **ips, bool ipv4_ph)
104 {
105 bool v6 = false;
106 size_t addrsz = 0;
107
108 assert(version >= 2 && version <= 3);
109
110 if (numip > 0) {
111 v6 = IS_IPADDR_V6(ips[0]);
112 addrsz = IPADDRSZ(ips[0]);
113 }
114
115 assert(!(version == 2 && v6));
116
117 size_t pktsize = VRRP_PKT_SIZE(v6 ? AF_INET6 : AF_INET, version, numip);
118
119 *pkt = XCALLOC(MTYPE_VRRP_PKT, pktsize);
120
121 (*pkt)->hdr.vertype |= version << 4;
122 (*pkt)->hdr.vertype |= VRRP_TYPE_ADVERTISEMENT;
123 (*pkt)->hdr.vrid = vrid;
124 (*pkt)->hdr.priority = prio;
125 (*pkt)->hdr.naddr = numip;
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 }
132
133 uint8_t *aptr = (void *)(*pkt)->addrs;
134
135 for (int i = 0; i < numip; i++) {
136 memcpy(aptr, &ips[i]->ip.addr, addrsz);
137 aptr += addrsz;
138 }
139
140 (*pkt)->hdr.chksum = vrrp_pkt_checksum(*pkt, pktsize, src, ipv4_ph);
141
142 return pktsize;
143 }
144
145 void vrrp_pkt_free(struct vrrp_pkt *pkt)
146 {
147 XFREE(MTYPE_VRRP_PKT, pkt);
148 }
149
150 size_t vrrp_pkt_adver_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt)
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;
160 snprintf(tmpbuf, sizeof(tmpbuf), "version %u, ", (hdr->vertype >> 4));
161 rs += strlcat(buf, tmpbuf, buflen);
162 snprintf(tmpbuf, sizeof(tmpbuf), "type %u (%s), ",
163 (hdr->vertype & 0x0F),
164 vrrp_packet_names[(hdr->vertype & 0x0F)]);
165 rs += strlcat(buf, tmpbuf, buflen);
166 snprintf(tmpbuf, sizeof(tmpbuf), "vrid %u, ", hdr->vrid);
167 rs += strlcat(buf, tmpbuf, buflen);
168 snprintf(tmpbuf, sizeof(tmpbuf), "priority %u, ", hdr->priority);
169 rs += strlcat(buf, tmpbuf, buflen);
170 snprintf(tmpbuf, sizeof(tmpbuf), "#%u addresses, ", hdr->naddr);
171 rs += strlcat(buf, tmpbuf, buflen);
172 snprintf(tmpbuf, sizeof(tmpbuf), "max adver int %u, ",
173 ntohs(hdr->v3.adver_int));
174 rs += strlcat(buf, tmpbuf, buflen);
175 snprintf(tmpbuf, sizeof(tmpbuf), "checksum %x", ntohs(hdr->chksum));
176 rs += strlcat(buf, tmpbuf, buflen);
177
178 return rs;
179 }
180
181 ssize_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)
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,
215 "IPv4 packet length field does not match # received bytes; %hu!= %zu",
216 ntohs(ip->ip_len), read);
217
218 /* TTL check */
219 VRRP_PKT_VCHECK(ip->ip_ttl == 255,
220 "IPv4 TTL is %hhu; should be 255",
221 ip->ip_ttl);
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");
228
229 /* Extract source address */
230 struct sockaddr_in *sa = m->msg_name;
231
232 src->ipa_type = IPADDR_V4;
233 src->ipaddr_v4 = sa->sin_addr;
234 } else if (family == AF_INET6) {
235 struct cmsghdr *c;
236
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);
246
247 VRRP_PKT_VCHECK(*hoplimit == 255,
248 "IPv6 Hop Limit is %hhu; should be 255",
249 *hoplimit);
250
251 *pkt = (struct vrrp_pkt *)buf;
252 pktsize = read;
253
254 /* Extract source address */
255 struct sockaddr_in6 *sa = m->msg_name;
256
257 src->ipa_type = IPADDR_V6;
258 memcpy(&src->ipaddr_v6, &sa->sin6_addr,
259 sizeof(struct in6_addr));
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,
270 "VRRP packet is undersized (%zu < %zu)", pktsize,
271 minsize);
272 VRRP_PKT_VCHECK(pktsize <= maxsize,
273 "VRRP packet is oversized (%zu > %zu)", pktsize,
274 maxsize);
275
276 /* Version check */
277 uint8_t pktver = (*pkt)->hdr.vertype >> 4;
278
279 VRRP_PKT_VCHECK(pktver == version, "Bad version %u", pktver);
280
281 /* Checksum check */
282 uint16_t chksum = vrrp_pkt_checksum(*pkt, pktsize, src, ipv4_ph);
283
284 VRRP_PKT_VCHECK((*pkt)->hdr.chksum == chksum,
285 "Bad VRRP checksum %hx; should be %hx",
286 (*pkt)->hdr.chksum, chksum);
287
288 /* Type check */
289 VRRP_PKT_VCHECK(((*pkt)->hdr.vertype & 0x0F) == 1, "Bad type %u",
290 (*pkt)->hdr.vertype & 0x0f);
291
292 /* Exact size check */
293 size_t ves = VRRP_PKT_SIZE(family, pktver, (*pkt)->hdr.naddr);
294
295 VRRP_PKT_VCHECK(pktsize == ves, "Packet has incorrect # addresses%s",
296 pktver == 2 ? " or missing auth fields" : "");
297
298 /* auth type check */
299 if (version == 2)
300 VRRP_PKT_VCHECK((*pkt)->hdr.v2.auth_type == 0,
301 "Bad authentication type %hhu",
302 (*pkt)->hdr.v2.auth_type);
303
304 /* Addresses check */
305 char vbuf[INET6_ADDRSTRLEN];
306 uint8_t *p = (uint8_t *)(*pkt)->addrs;
307
308 for (uint8_t i = 0; i < (*pkt)->hdr.naddr; i++) {
309 VRRP_PKT_VCHECK(inet_ntop(family, p, vbuf, sizeof(vbuf)),
310 "Bad IP address, #%hhu", i);
311 p += addrsz;
312 }
313
314 /* Everything checks out */
315 return pktsize;
316 }