]> git.proxmox.com Git - mirror_frr.git/blame - vrrpd/vrrp_packet.c
Revert "vrrpd: use parent interface LLA for advert tx"
[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"
b637bcd4 30#include "vrrp_debug.h"
72df9d93 31#include "vrrp_memory.h"
5435a2bf
QY
32#include "vrrp_packet.h"
33
91188ca6
QY
34/* clang-format off */
35const 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
d9e01e1c
QY
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 */
72static 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 pkt->hdr.chksum = 0;
80
81 if (v6) {
82 struct ipv6_ph ph = {};
83 ph.src = src->ipaddr_v6;
84 inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, &ph.dst);
85 ph.ulpl = htons(pktsize);
86 ph.next_hdr = 112;
87 chksum = in_cksum_with_ph6(&ph, pkt, pktsize);
99966840 88 } else if (!v6 && ((pkt->hdr.vertype >> 4) == 3)) {
d9e01e1c
QY
89 struct ipv4_ph ph = {};
90 ph.src = src->ipaddr_v4;
91 inet_pton(AF_INET, VRRP_MCASTV4_GROUP_STR, &ph.dst);
92 ph.proto = 112;
93 ph.len = htons(pktsize);
94 chksum = in_cksum_with_ph4(&ph, pkt, pktsize);
99966840
QY
95 } else if (!v6 && ((pkt->hdr.vertype >> 4) == 2)) {
96 chksum = in_cksum(pkt, pktsize);
97 } else {
98 assert(!"Invalid VRRP protocol version");
d9e01e1c
QY
99 }
100
101 pkt->hdr.chksum = chksum_pre;
102
103 return chksum;
104}
105
106ssize_t vrrp_pkt_adver_build(struct vrrp_pkt **pkt, struct ipaddr *src,
107 uint8_t version, uint8_t vrid, uint8_t prio,
108 uint16_t max_adver_int, uint8_t numip,
109 struct ipaddr **ips)
5435a2bf 110{
3d55d467
QY
111 bool v6 = false;
112 size_t addrsz = 0;
3eca3857 113
99966840
QY
114 assert(version >= 2 && version <= 3);
115 assert(!(version == 2 && v6));
116
3d55d467
QY
117 if (numip > 0) {
118 v6 = IS_IPADDR_V6(ips[0]);
119 addrsz = IPADDRSZ(ips[0]);
120 }
121
91188ca6 122 size_t pktsize = VRRP_PKT_SIZE(v6 ? AF_INET6 : AF_INET, numip);
72df9d93 123 *pkt = XCALLOC(MTYPE_VRRP_PKT, pktsize);
3eca3857 124
d9e01e1c 125 (*pkt)->hdr.vertype |= version << 4;
3eca3857
QY
126 (*pkt)->hdr.vertype |= VRRP_TYPE_ADVERTISEMENT;
127 (*pkt)->hdr.vrid = vrid;
128 (*pkt)->hdr.priority = prio;
129 (*pkt)->hdr.naddr = numip;
99966840
QY
130 if (version == 3)
131 (*pkt)->hdr.v3.adver_int = htons(max_adver_int);
132 else if (version == 2) {
133 (*pkt)->hdr.v2.auth_type = 0;
134 (*pkt)->hdr.v2.adver_int = MAX(max_adver_int / 100, 1);
135 }
3eca3857 136
862f2f37
QY
137 uint8_t *aptr = (void *)(*pkt)->addrs;
138
3eca3857 139 for (int i = 0; i < numip; i++) {
862f2f37
QY
140 memcpy(aptr, &ips[i]->ip.addr, addrsz);
141 aptr += addrsz;
3eca3857 142 }
8071d5c3 143
d9e01e1c 144 (*pkt)->hdr.chksum = vrrp_pkt_checksum(*pkt, pktsize, src);
3eca3857
QY
145
146 return pktsize;
5435a2bf 147}
91188ca6 148
d9e01e1c 149size_t vrrp_pkt_adver_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt)
91188ca6
QY
150{
151 if (buflen < 1)
152 return 0;
153
154 char tmpbuf[BUFSIZ];
155 size_t rs = 0;
156 struct vrrp_hdr *hdr = &pkt->hdr;
157
158 buf[0] = 0x00;
159 snprintf(tmpbuf, sizeof(tmpbuf), "Version: %u\n", (hdr->vertype >> 4));
160 rs += strlcat(buf, tmpbuf, buflen);
161 snprintf(tmpbuf, sizeof(tmpbuf), "Type: %u (%s)\n",
162 (hdr->vertype & 0x0F),
163 vrrp_packet_names[(hdr->vertype & 0x0F)]);
164 rs += strlcat(buf, tmpbuf, buflen);
165 snprintf(tmpbuf, sizeof(tmpbuf), "VRID: %u\n", hdr->vrid);
166 rs += strlcat(buf, tmpbuf, buflen);
167 snprintf(tmpbuf, sizeof(tmpbuf), "Priority: %u\n", hdr->priority);
168 rs += strlcat(buf, tmpbuf, buflen);
169 snprintf(tmpbuf, sizeof(tmpbuf), "Count IPvX: %u\n", hdr->naddr);
170 rs += strlcat(buf, tmpbuf, buflen);
171 snprintf(tmpbuf, sizeof(tmpbuf), "Max Adver Int: %u\n",
172 ntohs(hdr->v3.adver_int));
173 rs += strlcat(buf, tmpbuf, buflen);
174 snprintf(tmpbuf, sizeof(tmpbuf), "Checksum: %x\n", ntohs(hdr->chksum));
175 rs += strlcat(buf, tmpbuf, buflen);
176
177 return rs;
178}
179
8cb3d803
QY
180ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m,
181 size_t read, struct ipaddr *src,
182 struct vrrp_pkt **pkt, char *errmsg,
183 size_t errmsg_len)
91188ca6
QY
184{
185 /* Source (MAC & IP), Dest (MAC & IP) TTL validation done by kernel */
186 size_t addrsz = (family == AF_INET) ? sizeof(struct in_addr)
187 : sizeof(struct in6_addr);
188
189 size_t pktsize;
190 uint8_t *buf = m->msg_iov->iov_base;
191
192#define VRRP_PKT_VCHECK(cond, _f, ...) \
193 do { \
194 if (!(cond)) { \
195 if (errmsg) \
196 snprintf(errmsg, errmsg_len, (_f), \
197 ##__VA_ARGS__); \
198 return -1; \
199 } \
200 } while (0)
201
202 /* IPvX header check */
203
204 if (family == AF_INET) {
205 VRRP_PKT_VCHECK(
206 read >= sizeof(struct ip),
207 "Datagram not large enough to contain IP header");
208
209 struct ip *ip = (struct ip *)buf;
210
211 /* IP total length check */
212 VRRP_PKT_VCHECK(
213 ntohs(ip->ip_len) == read,
7e9fee6a
QY
214 "IPv4 packet length field does not match # received bytes; %" PRIu16
215 "!= %zu",
91188ca6
QY
216 ntohs(ip->ip_len), read);
217
218 /* TTL check */
dad18a2f
QY
219 VRRP_PKT_VCHECK(ip->ip_ttl == 255,
220 "IPv4 TTL is %" PRIu8 "; should be 255",
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;
d04bb25a 231 src->ipa_type = IPADDR_V4;
fa211f1c 232 src->ipaddr_v4 = sa->sin_addr;
91188ca6
QY
233 } else if (family == AF_INET6) {
234 struct cmsghdr *c;
235 for (c = CMSG_FIRSTHDR(m); c != NULL; CMSG_NXTHDR(m, c)) {
236 if (c->cmsg_level == IPPROTO_IPV6
237 && c->cmsg_type == IPV6_HOPLIMIT)
238 break;
239 }
240
241 VRRP_PKT_VCHECK(!!c, "IPv6 Hop Limit not received");
242
243 uint8_t *hoplimit = CMSG_DATA(c);
dad18a2f
QY
244 VRRP_PKT_VCHECK(*hoplimit == 255,
245 "IPv6 Hop Limit is %" PRIu8 "; should be 255",
246 *hoplimit);
91188ca6
QY
247
248 *pkt = (struct vrrp_pkt *)buf;
249 pktsize = read;
d04bb25a
QY
250
251 /* Extract source address */
d04bb25a 252 struct sockaddr_in6 *sa = m->msg_name;
fa211f1c 253 src->ipa_type = IPADDR_V6;
d04bb25a
QY
254 memcpy(&src->ipaddr_v6, &sa->sin6_addr,
255 sizeof(struct in6_addr));
91188ca6
QY
256 } else {
257 assert(!"Unknown address family");
258 }
259
260 /* Size check */
261 size_t minsize = (family == AF_INET) ? VRRP_MIN_PKT_SIZE_V4
262 : VRRP_MIN_PKT_SIZE_V6;
263 size_t maxsize = (family == AF_INET) ? VRRP_MAX_PKT_SIZE_V4
264 : VRRP_MAX_PKT_SIZE_V6;
265 VRRP_PKT_VCHECK(pktsize >= minsize,
bb95fd82
QY
266 "VRRP packet is undersized (%zu < %zu)", pktsize,
267 minsize);
91188ca6 268 VRRP_PKT_VCHECK(pktsize <= maxsize,
bb95fd82
QY
269 "VRRP packet is oversized (%zu > %zu)", pktsize,
270 maxsize);
d04bb25a 271
8cb3d803
QY
272 /* Version check */
273 uint8_t pktver = (*pkt)->hdr.vertype >> 4;
274 VRRP_PKT_VCHECK(pktver == version, "Bad version %u", pktver);
275
d04bb25a
QY
276 /* Checksum check */
277 uint16_t chksum = vrrp_pkt_checksum(*pkt, pktsize, src);
278 VRRP_PKT_VCHECK((*pkt)->hdr.chksum == chksum,
bb95fd82 279 "Bad VRRP checksum %" PRIx16 "; should be %" PRIx16 "",
d04bb25a
QY
280 (*pkt)->hdr.chksum, chksum);
281
91188ca6 282 /* Type check */
bb95fd82 283 VRRP_PKT_VCHECK(((*pkt)->hdr.vertype & 0x0F) == 1, "Bad type %" PRIu8,
91188ca6 284 (*pkt)->hdr.vertype & 0x0f);
8cb3d803 285
91188ca6
QY
286 /* # addresses check */
287 size_t ves = VRRP_PKT_SIZE(family, (*pkt)->hdr.naddr);
288 VRRP_PKT_VCHECK(pktsize == ves, "Packet has incorrect # addresses");
91188ca6 289
99966840
QY
290 /* auth type check */
291 if (version == 2)
292 VRRP_PKT_VCHECK((*pkt)->hdr.v2.auth_type == 0,
293 "Bad authentication type %" PRIu8,
294 (*pkt)->hdr.v2.auth_type);
295
91188ca6
QY
296 /* Addresses check */
297 char vbuf[INET6_ADDRSTRLEN];
298 uint8_t *p = (uint8_t *)(*pkt)->addrs;
299 for (uint8_t i = 0; i < (*pkt)->hdr.naddr; i++) {
300 VRRP_PKT_VCHECK(inet_ntop(family, p, vbuf, sizeof(vbuf)),
bb95fd82 301 "Bad IP address, #%" PRIu8, i);
91188ca6
QY
302 p += addrsz;
303 }
304
305 /* Everything checks out */
306 return pktsize;
307}