]> git.proxmox.com Git - mirror_frr.git/blame - vrrpd/vrrp_packet.c
vrrpd: handle address deletion, don't accept dupes
[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"
5435a2bf
QY
30#include "vrrp_packet.h"
31
91188ca6
QY
32/* clang-format off */
33const char *vrrp_packet_names[16] = {
34 [0] = "Unknown",
35 [VRRP_TYPE_ADVERTISEMENT] = "ADVERTISEMENT",
36 [2] = "Unknown",
37 [3] = "Unknown",
38 [4] = "Unknown",
39 [5] = "Unknown",
40 [6] = "Unknown",
41 [7] = "Unknown",
42 [8] = "Unknown",
43 [9] = "Unknown",
44 [10] = "Unknown",
45 [11] = "Unknown",
46 [12] = "Unknown",
47 [13] = "Unknown",
48 [14] = "Unknown",
49 [15] = "Unknown",
50};
51/* clang-format on */
52
8071d5c3
QY
53ssize_t vrrp_pkt_build(struct vrrp_pkt **pkt, struct ipaddr *src, uint8_t vrid,
54 uint8_t prio, uint16_t max_adver_int, uint8_t numip,
862f2f37 55 struct ipaddr **ips)
5435a2bf 56{
862f2f37 57 bool v6 = IS_IPADDR_V6(ips[0]);
3eca3857 58
5435a2bf 59 size_t addrsz = v6 ? sizeof(struct in6_addr) : sizeof(struct in_addr);
91188ca6 60 size_t pktsize = VRRP_PKT_SIZE(v6 ? AF_INET6 : AF_INET, numip);
3eca3857
QY
61 *pkt = XCALLOC(MTYPE_TMP, pktsize);
62
3eca3857
QY
63 (*pkt)->hdr.vertype |= VRRP_VERSION << 4;
64 (*pkt)->hdr.vertype |= VRRP_TYPE_ADVERTISEMENT;
65 (*pkt)->hdr.vrid = vrid;
66 (*pkt)->hdr.priority = prio;
67 (*pkt)->hdr.naddr = numip;
68 (*pkt)->hdr.v3.adver_int = htons(max_adver_int);
69
862f2f37
QY
70 uint8_t *aptr = (void *)(*pkt)->addrs;
71
3eca3857 72 for (int i = 0; i < numip; i++) {
862f2f37
QY
73 memcpy(aptr, &ips[i]->ip.addr, addrsz);
74 aptr += addrsz;
3eca3857 75 }
8071d5c3 76
3eca3857
QY
77 (*pkt)->hdr.chksum = 0;
78
8071d5c3
QY
79 if (v6) {
80 struct ipv6_ph ph = {};
81 ph.src = src->ipaddr_v6;
82 inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, &ph.dst);
83 ph.ulpl = htons(pktsize);
84 ph.next_hdr = 112;
85 (*pkt)->hdr.chksum = in_cksum_with_ph6(&ph, *pkt, pktsize);
86 } else {
87 struct ipv4_ph ph = {};
88 ph.src = src->ipaddr_v4;
89 inet_pton(AF_INET, VRRP_MCASTV4_GROUP_STR, &ph.dst);
90 ph.proto = 112;
91 ph.len = htons(pktsize);
92 (*pkt)->hdr.chksum = in_cksum_with_ph4(&ph, *pkt, pktsize);
93 }
3eca3857
QY
94
95 return pktsize;
5435a2bf 96}
91188ca6
QY
97
98size_t vrrp_pkt_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt)
99{
100 if (buflen < 1)
101 return 0;
102
103 char tmpbuf[BUFSIZ];
104 size_t rs = 0;
105 struct vrrp_hdr *hdr = &pkt->hdr;
106
107 buf[0] = 0x00;
108 snprintf(tmpbuf, sizeof(tmpbuf), "Version: %u\n", (hdr->vertype >> 4));
109 rs += strlcat(buf, tmpbuf, buflen);
110 snprintf(tmpbuf, sizeof(tmpbuf), "Type: %u (%s)\n",
111 (hdr->vertype & 0x0F),
112 vrrp_packet_names[(hdr->vertype & 0x0F)]);
113 rs += strlcat(buf, tmpbuf, buflen);
114 snprintf(tmpbuf, sizeof(tmpbuf), "VRID: %u\n", hdr->vrid);
115 rs += strlcat(buf, tmpbuf, buflen);
116 snprintf(tmpbuf, sizeof(tmpbuf), "Priority: %u\n", hdr->priority);
117 rs += strlcat(buf, tmpbuf, buflen);
118 snprintf(tmpbuf, sizeof(tmpbuf), "Count IPvX: %u\n", hdr->naddr);
119 rs += strlcat(buf, tmpbuf, buflen);
120 snprintf(tmpbuf, sizeof(tmpbuf), "Max Adver Int: %u\n",
121 ntohs(hdr->v3.adver_int));
122 rs += strlcat(buf, tmpbuf, buflen);
123 snprintf(tmpbuf, sizeof(tmpbuf), "Checksum: %x\n", ntohs(hdr->chksum));
124 rs += strlcat(buf, tmpbuf, buflen);
125
126 return rs;
127}
128
129ssize_t vrrp_parse_datagram(int family, struct msghdr *m, size_t read,
130 struct vrrp_pkt **pkt, char *errmsg,
131 size_t errmsg_len)
132{
133 /* Source (MAC & IP), Dest (MAC & IP) TTL validation done by kernel */
134 size_t addrsz = (family == AF_INET) ? sizeof(struct in_addr)
135 : sizeof(struct in6_addr);
136
137 size_t pktsize;
138 uint8_t *buf = m->msg_iov->iov_base;
139
140#define VRRP_PKT_VCHECK(cond, _f, ...) \
141 do { \
142 if (!(cond)) { \
143 if (errmsg) \
144 snprintf(errmsg, errmsg_len, (_f), \
145 ##__VA_ARGS__); \
146 return -1; \
147 } \
148 } while (0)
149
150 /* IPvX header check */
151
152 if (family == AF_INET) {
153 VRRP_PKT_VCHECK(
154 read >= sizeof(struct ip),
155 "Datagram not large enough to contain IP header");
156
157 struct ip *ip = (struct ip *)buf;
158
159 /* IP total length check */
160 VRRP_PKT_VCHECK(
161 ntohs(ip->ip_len) == read,
162 "IPv4 packet length field does not match # received bytes; %u != %lu",
163 ntohs(ip->ip_len), read);
164
165 /* TTL check */
dad18a2f
QY
166 VRRP_PKT_VCHECK(ip->ip_ttl == 255,
167 "IPv4 TTL is %" PRIu8 "; should be 255",
168 ip->ip_ttl);
91188ca6
QY
169
170 *pkt = (struct vrrp_pkt *)(buf + (ip->ip_hl << 2));
171 pktsize = read - (ip->ip_hl << 2);
172
173 /* IP empty packet check */
174 VRRP_PKT_VCHECK(pktsize > 0, "IPv4 packet has no payload");
175 } else if (family == AF_INET6) {
176 struct cmsghdr *c;
177 for (c = CMSG_FIRSTHDR(m); c != NULL; CMSG_NXTHDR(m, c)) {
178 if (c->cmsg_level == IPPROTO_IPV6
179 && c->cmsg_type == IPV6_HOPLIMIT)
180 break;
181 }
182
183 VRRP_PKT_VCHECK(!!c, "IPv6 Hop Limit not received");
184
185 uint8_t *hoplimit = CMSG_DATA(c);
dad18a2f
QY
186 VRRP_PKT_VCHECK(*hoplimit == 255,
187 "IPv6 Hop Limit is %" PRIu8 "; should be 255",
188 *hoplimit);
91188ca6
QY
189
190 *pkt = (struct vrrp_pkt *)buf;
191 pktsize = read;
192 } else {
193 assert(!"Unknown address family");
194 }
195
196 /* Size check */
197 size_t minsize = (family == AF_INET) ? VRRP_MIN_PKT_SIZE_V4
198 : VRRP_MIN_PKT_SIZE_V6;
199 size_t maxsize = (family == AF_INET) ? VRRP_MAX_PKT_SIZE_V4
200 : VRRP_MAX_PKT_SIZE_V6;
201 VRRP_PKT_VCHECK(pktsize >= minsize,
202 "VRRP packet is undersized (%lu < %lu)", pktsize,
203 VRRP_MIN_PKT_SIZE);
204 VRRP_PKT_VCHECK(pktsize <= maxsize,
205 "VRRP packet is oversized (%lu > %lu)", pktsize,
206 VRRP_MAX_PKT_SIZE);
207 /* Version check */
208 VRRP_PKT_VCHECK(((*pkt)->hdr.vertype >> 4) != 2, "VRPPv2 unsupported");
209 VRRP_PKT_VCHECK(((*pkt)->hdr.vertype >> 4) == 3, "Bad version %u",
210 (*pkt)->hdr.vertype >> 4);
211 /* Type check */
212 VRRP_PKT_VCHECK(((*pkt)->hdr.vertype & 0x0F) == 1, "Bad type %u",
213 (*pkt)->hdr.vertype & 0x0f);
91188ca6
QY
214 /* # addresses check */
215 size_t ves = VRRP_PKT_SIZE(family, (*pkt)->hdr.naddr);
216 VRRP_PKT_VCHECK(pktsize == ves, "Packet has incorrect # addresses");
217 /* FIXME: checksum check */
218 /* ... */
219
220 /* Addresses check */
221 char vbuf[INET6_ADDRSTRLEN];
222 uint8_t *p = (uint8_t *)(*pkt)->addrs;
223 for (uint8_t i = 0; i < (*pkt)->hdr.naddr; i++) {
224 VRRP_PKT_VCHECK(inet_ntop(family, p, vbuf, sizeof(vbuf)),
225 "Bad IP address, #%u", i);
226 p += addrsz;
227 }
228
229 /* Everything checks out */
230 return pktsize;
231}