]>
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_memory.h"
31 #include "vrrp_packet.h"
33 /* clang-format off */
34 const char *vrrp_packet_names
[16] = {
36 [VRRP_TYPE_ADVERTISEMENT
] = "ADVERTISEMENT",
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
,
58 bool v6
= IS_IPADDR_V6(ips
[0]);
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
);
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
);
71 uint8_t *aptr
= (void *)(*pkt
)->addrs
;
73 for (int i
= 0; i
< numip
; i
++) {
74 memcpy(aptr
, &ips
[i
]->ip
.addr
, addrsz
);
78 (*pkt
)->hdr
.chksum
= 0;
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
);
86 (*pkt
)->hdr
.chksum
= in_cksum_with_ph6(&ph
, *pkt
, pktsize
);
88 struct ipv4_ph ph
= {};
89 ph
.src
= src
->ipaddr_v4
;
90 inet_pton(AF_INET
, VRRP_MCASTV4_GROUP_STR
, &ph
.dst
);
92 ph
.len
= htons(pktsize
);
93 (*pkt
)->hdr
.chksum
= in_cksum_with_ph4(&ph
, *pkt
, pktsize
);
99 size_t vrrp_pkt_dump(char *buf
, size_t buflen
, struct vrrp_pkt
*pkt
)
106 struct vrrp_hdr
*hdr
= &pkt
->hdr
;
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
);
130 ssize_t
vrrp_parse_datagram(int family
, struct msghdr
*m
, size_t read
,
131 struct vrrp_pkt
**pkt
, char *errmsg
,
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
);
139 uint8_t *buf
= m
->msg_iov
->iov_base
;
141 #define VRRP_PKT_VCHECK(cond, _f, ...) \
145 snprintf(errmsg, errmsg_len, (_f), \
151 /* IPvX header check */
153 if (family
== AF_INET
) {
155 read
>= sizeof(struct ip
),
156 "Datagram not large enough to contain IP header");
158 struct ip
*ip
= (struct ip
*)buf
;
160 /* IP total length check */
162 ntohs(ip
->ip_len
) == read
,
163 "IPv4 packet length field does not match # received bytes; %u != %lu",
164 ntohs(ip
->ip_len
), read
);
167 VRRP_PKT_VCHECK(ip
->ip_ttl
== 255,
168 "IPv4 TTL is %" PRIu8
"; should be 255",
171 *pkt
= (struct vrrp_pkt
*)(buf
+ (ip
->ip_hl
<< 2));
172 pktsize
= read
- (ip
->ip_hl
<< 2);
174 /* IP empty packet check */
175 VRRP_PKT_VCHECK(pktsize
> 0, "IPv4 packet has no payload");
176 } else if (family
== AF_INET6
) {
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
)
184 VRRP_PKT_VCHECK(!!c
, "IPv6 Hop Limit not received");
186 uint8_t *hoplimit
= CMSG_DATA(c
);
187 VRRP_PKT_VCHECK(*hoplimit
== 255,
188 "IPv6 Hop Limit is %" PRIu8
"; should be 255",
191 *pkt
= (struct vrrp_pkt
*)buf
;
194 assert(!"Unknown address family");
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
,
205 VRRP_PKT_VCHECK(pktsize
<= maxsize
,
206 "VRRP packet is oversized (%lu > %lu)", pktsize
,
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);
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 */
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
);
230 /* Everything checks out */