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