]>
git.proxmox.com Git - mirror_frr.git/blob - vrrpd/vrrp_packet.c
2 * VRRP packet crafting.
3 * Copyright (C) 2018-2019 Cumulus Networks, Inc.
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)
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
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
21 #include <netinet/in.h>
22 #include <netinet/ip.h>
23 #include <netinet/ip6.h>
25 #include "lib/checksum.h"
26 #include "lib/ipaddr.h"
27 #include "lib/memory.h"
30 #include "vrrp_debug.h"
31 #include "vrrp_packet.h"
33 DEFINE_MTYPE_STATIC(VRRPD
, VRRP_PKT
, "VRRP packet")
35 /* clang-format off */
36 static const char *const vrrp_packet_names
[16] = {
38 [VRRP_TYPE_ADVERTISEMENT
] = "ADVERTISEMENT",
57 * Compute the VRRP checksum.
59 * Checksum is not set in the packet, just computed.
62 * VRRP packet, fully filled out except for checksum field.
68 * IP address that pkt will be transmitted from.
71 * VRRP checksum in network byte order.
73 static uint16_t vrrp_pkt_checksum(struct vrrp_pkt
*pkt
, size_t pktsize
,
77 bool v6
= (src
->ipa_type
== IPADDR_V6
);
79 uint16_t chksum_pre
= pkt
->hdr
.chksum
;
84 struct ipv6_ph ph
= {};
86 ph
.src
= src
->ipaddr_v6
;
87 inet_pton(AF_INET6
, VRRP_MCASTV6_GROUP_STR
, &ph
.dst
);
88 ph
.ulpl
= htons(pktsize
);
89 ph
.next_hdr
= IPPROTO_VRRP
;
90 chksum
= in_cksum_with_ph6(&ph
, pkt
, pktsize
);
91 } else if (!v6
&& ((pkt
->hdr
.vertype
>> 4) == 3)) {
92 struct ipv4_ph ph
= {};
94 ph
.src
= src
->ipaddr_v4
;
95 inet_pton(AF_INET
, VRRP_MCASTV4_GROUP_STR
, &ph
.dst
);
96 ph
.proto
= IPPROTO_VRRP
;
97 ph
.len
= htons(pktsize
);
98 chksum
= in_cksum_with_ph4(&ph
, pkt
, pktsize
);
99 } else if (!v6
&& ((pkt
->hdr
.vertype
>> 4) == 2)) {
100 chksum
= in_cksum(pkt
, pktsize
);
102 assert(!"Invalid VRRP protocol version");
105 pkt
->hdr
.chksum
= chksum_pre
;
110 ssize_t
vrrp_pkt_adver_build(struct vrrp_pkt
**pkt
, struct ipaddr
*src
,
111 uint8_t version
, uint8_t vrid
, uint8_t prio
,
112 uint16_t max_adver_int
, uint8_t numip
,
118 assert(version
>= 2 && version
<= 3);
121 v6
= IS_IPADDR_V6(ips
[0]);
122 addrsz
= IPADDRSZ(ips
[0]);
125 assert(!(version
== 2 && v6
));
127 size_t pktsize
= VRRP_PKT_SIZE(v6
? AF_INET6
: AF_INET
, version
, numip
);
129 *pkt
= XCALLOC(MTYPE_VRRP_PKT
, pktsize
);
131 (*pkt
)->hdr
.vertype
|= version
<< 4;
132 (*pkt
)->hdr
.vertype
|= VRRP_TYPE_ADVERTISEMENT
;
133 (*pkt
)->hdr
.vrid
= vrid
;
134 (*pkt
)->hdr
.priority
= prio
;
135 (*pkt
)->hdr
.naddr
= numip
;
137 (*pkt
)->hdr
.v3
.adver_int
= htons(max_adver_int
);
138 else if (version
== 2) {
139 (*pkt
)->hdr
.v2
.auth_type
= 0;
140 (*pkt
)->hdr
.v2
.adver_int
= MAX(max_adver_int
/ 100, 1);
143 uint8_t *aptr
= (void *)(*pkt
)->addrs
;
145 for (int i
= 0; i
< numip
; i
++) {
146 memcpy(aptr
, &ips
[i
]->ip
.addr
, addrsz
);
150 (*pkt
)->hdr
.chksum
= vrrp_pkt_checksum(*pkt
, pktsize
, src
);
155 void vrrp_pkt_free(struct vrrp_pkt
*pkt
)
157 XFREE(MTYPE_VRRP_PKT
, pkt
);
160 size_t vrrp_pkt_adver_dump(char *buf
, size_t buflen
, struct vrrp_pkt
*pkt
)
167 struct vrrp_hdr
*hdr
= &pkt
->hdr
;
170 snprintf(tmpbuf
, sizeof(tmpbuf
), "version %u, ", (hdr
->vertype
>> 4));
171 rs
+= strlcat(buf
, tmpbuf
, buflen
);
172 snprintf(tmpbuf
, sizeof(tmpbuf
), "type %u (%s), ",
173 (hdr
->vertype
& 0x0F),
174 vrrp_packet_names
[(hdr
->vertype
& 0x0F)]);
175 rs
+= strlcat(buf
, tmpbuf
, buflen
);
176 snprintf(tmpbuf
, sizeof(tmpbuf
), "vrid %u, ", hdr
->vrid
);
177 rs
+= strlcat(buf
, tmpbuf
, buflen
);
178 snprintf(tmpbuf
, sizeof(tmpbuf
), "priority %u, ", hdr
->priority
);
179 rs
+= strlcat(buf
, tmpbuf
, buflen
);
180 snprintf(tmpbuf
, sizeof(tmpbuf
), "#%u addresses, ", hdr
->naddr
);
181 rs
+= strlcat(buf
, tmpbuf
, buflen
);
182 snprintf(tmpbuf
, sizeof(tmpbuf
), "max adver int %u, ",
183 ntohs(hdr
->v3
.adver_int
));
184 rs
+= strlcat(buf
, tmpbuf
, buflen
);
185 snprintf(tmpbuf
, sizeof(tmpbuf
), "checksum %x", ntohs(hdr
->chksum
));
186 rs
+= strlcat(buf
, tmpbuf
, buflen
);
191 ssize_t
vrrp_pkt_parse_datagram(int family
, int version
, struct msghdr
*m
,
192 size_t read
, struct ipaddr
*src
,
193 struct vrrp_pkt
**pkt
, char *errmsg
,
196 /* Source (MAC & IP), Dest (MAC & IP) TTL validation done by kernel */
197 size_t addrsz
= (family
== AF_INET
) ? sizeof(struct in_addr
)
198 : sizeof(struct in6_addr
);
201 uint8_t *buf
= m
->msg_iov
->iov_base
;
203 #define VRRP_PKT_VCHECK(cond, _f, ...) \
207 snprintf(errmsg, errmsg_len, (_f), \
213 /* IPvX header check */
215 if (family
== AF_INET
) {
217 read
>= sizeof(struct ip
),
218 "Datagram not large enough to contain IP header");
220 struct ip
*ip
= (struct ip
*)buf
;
222 /* IP total length check */
224 ntohs(ip
->ip_len
) == read
,
225 "IPv4 packet length field does not match # received bytes; %hu!= %zu",
226 ntohs(ip
->ip_len
), read
);
229 VRRP_PKT_VCHECK(ip
->ip_ttl
== 255,
230 "IPv4 TTL is %hhu; should be 255",
233 *pkt
= (struct vrrp_pkt
*)(buf
+ (ip
->ip_hl
<< 2));
234 pktsize
= read
- (ip
->ip_hl
<< 2);
236 /* IP empty packet check */
237 VRRP_PKT_VCHECK(pktsize
> 0, "IPv4 packet has no payload");
239 /* Extract source address */
240 struct sockaddr_in
*sa
= m
->msg_name
;
242 src
->ipa_type
= IPADDR_V4
;
243 src
->ipaddr_v4
= sa
->sin_addr
;
244 } else if (family
== AF_INET6
) {
247 for (c
= CMSG_FIRSTHDR(m
); c
!= NULL
; CMSG_NXTHDR(m
, c
)) {
248 if (c
->cmsg_level
== IPPROTO_IPV6
249 && c
->cmsg_type
== IPV6_HOPLIMIT
)
253 VRRP_PKT_VCHECK(!!c
, "IPv6 Hop Limit not received");
255 uint8_t *hoplimit
= CMSG_DATA(c
);
257 VRRP_PKT_VCHECK(*hoplimit
== 255,
258 "IPv6 Hop Limit is %hhu; should be 255",
261 *pkt
= (struct vrrp_pkt
*)buf
;
264 /* Extract source address */
265 struct sockaddr_in6
*sa
= m
->msg_name
;
267 src
->ipa_type
= IPADDR_V6
;
268 memcpy(&src
->ipaddr_v6
, &sa
->sin6_addr
,
269 sizeof(struct in6_addr
));
271 assert(!"Unknown address family");
275 size_t minsize
= (family
== AF_INET
) ? VRRP_MIN_PKT_SIZE_V4
276 : VRRP_MIN_PKT_SIZE_V6
;
277 size_t maxsize
= (family
== AF_INET
) ? VRRP_MAX_PKT_SIZE_V4
278 : VRRP_MAX_PKT_SIZE_V6
;
279 VRRP_PKT_VCHECK(pktsize
>= minsize
,
280 "VRRP packet is undersized (%zu < %zu)", pktsize
,
282 VRRP_PKT_VCHECK(pktsize
<= maxsize
,
283 "VRRP packet is oversized (%zu > %zu)", pktsize
,
287 uint8_t pktver
= (*pkt
)->hdr
.vertype
>> 4;
289 VRRP_PKT_VCHECK(pktver
== version
, "Bad version %u", pktver
);
292 uint16_t chksum
= vrrp_pkt_checksum(*pkt
, pktsize
, src
);
294 VRRP_PKT_VCHECK((*pkt
)->hdr
.chksum
== chksum
,
295 "Bad VRRP checksum %hx; should be %hx",
296 (*pkt
)->hdr
.chksum
, chksum
);
299 VRRP_PKT_VCHECK(((*pkt
)->hdr
.vertype
& 0x0F) == 1, "Bad type %u",
300 (*pkt
)->hdr
.vertype
& 0x0f);
302 /* Exact size check */
303 size_t ves
= VRRP_PKT_SIZE(family
, pktver
, (*pkt
)->hdr
.naddr
);
305 VRRP_PKT_VCHECK(pktsize
== ves
, "Packet has incorrect # addresses%s",
306 pktver
== 2 ? " or missing auth fields" : "");
308 /* auth type check */
310 VRRP_PKT_VCHECK((*pkt
)->hdr
.v2
.auth_type
== 0,
311 "Bad authentication type %hhu",
312 (*pkt
)->hdr
.v2
.auth_type
);
314 /* Addresses check */
315 char vbuf
[INET6_ADDRSTRLEN
];
316 uint8_t *p
= (uint8_t *)(*pkt
)->addrs
;
318 for (uint8_t i
= 0; i
< (*pkt
)->hdr
.naddr
; i
++) {
319 VRRP_PKT_VCHECK(inet_ntop(family
, p
, vbuf
, sizeof(vbuf
)),
320 "Bad IP address, #%hhu", i
);
324 /* Everything checks out */