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