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