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