vrrpd: add IPv4 pseudoheader option for VRRPv3

This commit adds a new option to control whether a VRRPv3 group
accepts / computes its checksum with a prepended IPv4 pseudoheader.
This should improve interoperability with other devices.

Signed-off-by: Siger Yang <siger.yang@outlook.com>
This commit is contained in:
Siger Yang 2022-11-25 13:52:46 +08:00
parent 89f74214e7
commit 9f2379bdd2
No known key found for this signature in database
GPG Key ID: 25F5E75AF97D5067
8 changed files with 124 additions and 23 deletions

View File

@ -647,6 +647,8 @@ struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid,
vr->priority = vd.priority;
vr->preempt_mode = vd.preempt_mode;
vr->accept_mode = vd.accept_mode;
vr->checksum_with_ipv4_pseudoheader =
vd.checksum_with_ipv4_pseudoheader;
vr->shutdown = vd.shutdown;
vr->v4 = vrrp_router_create(vr, AF_INET);
@ -789,7 +791,8 @@ static void vrrp_send_advertisement(struct vrrp_router *r)
pktsz = vrrp_pkt_adver_build(&pkt, &r->src, r->vr->version, r->vr->vrid,
r->priority, r->vr->advertisement_interval,
r->addrs->count, (struct ipaddr **)&addrs);
r->addrs->count, (struct ipaddr **)&addrs,
r->vr->checksum_with_ipv4_pseudoheader);
if (DEBUG_MODE_CHECK(&vrrp_dbg_pkt, DEBUG_MODE_ALL))
zlog_hexdump(pkt, (size_t)pktsz);
@ -1027,8 +1030,10 @@ static void vrrp_read(struct thread *thread)
zlog_hexdump(r->ibuf, nbytes);
}
pktsize = vrrp_pkt_parse_datagram(r->family, r->vr->version, &m, nbytes,
&src, &pkt, errbuf, sizeof(errbuf));
pktsize = vrrp_pkt_parse_datagram(
r->family, r->vr->version,
r->vr->checksum_with_ipv4_pseudoheader, &m, nbytes, &src, &pkt,
errbuf, sizeof(errbuf));
if (pktsize < 0)
DEBUGD(&vrrp_dbg_pkt,
@ -2347,6 +2352,12 @@ int vrrp_config_write_global(struct vty *vty)
vty_out(vty, "%svrrp default accept\n",
!vd.accept_mode ? "no " : "");
if (vd.checksum_with_ipv4_pseudoheader !=
VRRP_DEFAULT_CHECKSUM_WITH_IPV4_PSEUDOHEADER &&
++writes)
vty_out(vty, "%svrrp default checksum-with-ipv4-pseudoheader\n",
!vd.checksum_with_ipv4_pseudoheader ? "no " : "");
if (vd.shutdown != VRRP_DEFAULT_SHUTDOWN && ++writes)
vty_out(vty, "%svrrp default shutdown\n",
!vd.shutdown ? "no " : "");
@ -2387,6 +2398,8 @@ void vrrp_init(void)
vd.preempt_mode = yang_get_default_bool("%s/preempt", VRRP_XPATH_FULL);
vd.accept_mode =
yang_get_default_bool("%s/accept-mode", VRRP_XPATH_FULL);
vd.checksum_with_ipv4_pseudoheader = yang_get_default_bool(
"%s/checksum-with-ipv4-pseudoheader", VRRP_XPATH_FULL);
vd.shutdown = VRRP_DEFAULT_SHUTDOWN;
vrrp_autoconfig_version = 3;

View File

@ -53,6 +53,7 @@
#define VRRP_DEFAULT_ADVINT 100
#define VRRP_DEFAULT_PREEMPT true
#define VRRP_DEFAULT_ACCEPT true
#define VRRP_DEFAULT_CHECKSUM_WITH_IPV4_PSEUDOHEADER true
#define VRRP_DEFAULT_SHUTDOWN false
/* User compatibility constant */
@ -70,6 +71,7 @@ struct vrrp_defaults {
uint16_t advertisement_interval;
bool preempt_mode;
bool accept_mode;
bool checksum_with_ipv4_pseudoheader;
bool shutdown;
};
@ -266,6 +268,14 @@ struct vrrp_vrouter {
*/
bool accept_mode;
/*
* Indicates whether this router computes and accepts VRRPv3 checksums
* without pseudoheader, for device interoperability.
*
* This option should only affect IPv4 virtual routers.
*/
bool checksum_with_ipv4_pseudoheader;
struct vrrp_router *v4;
struct vrrp_router *v6;
};

View File

@ -602,6 +602,26 @@ lib_interface_vrrp_vrrp_group_shutdown_modify(struct nb_cb_modify_args *args)
return NB_OK;
}
/*
* XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/checksum-with-
* ipv4-pseudoheader
*/
static int lib_interface_vrrp_vrrp_group_checksum_with_ipv4_pseudoheader_modify(
struct nb_cb_modify_args *args)
{
if (args->event != NB_EV_APPLY)
return NB_OK;
struct vrrp_vrouter *vr;
bool checksum_with_ipv4_ph;
vr = nb_running_get_entry(args->dnode, NULL, true);
checksum_with_ipv4_ph = yang_dnode_get_bool(args->dnode, NULL);
vr->checksum_with_ipv4_pseudoheader = checksum_with_ipv4_ph;
return NB_OK;
}
/* clang-format off */
const struct frr_yang_module_info frr_vrrpd_info = {
.name = "frr-vrrpd",
@ -643,6 +663,13 @@ const struct frr_yang_module_info frr_vrrpd_info = {
.modify = lib_interface_vrrp_vrrp_group_accept_mode_modify,
}
},
{
.xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/checksum-with-ipv4-pseudoheader",
.cbs = {
.modify = lib_interface_vrrp_vrrp_group_checksum_with_ipv4_pseudoheader_modify,
.cli_show = cli_show_checksum_with_ipv4_pseudoheader,
}
},
{
.xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/advertisement-interval",
.cbs = {

View File

@ -71,7 +71,7 @@ static const char *const vrrp_packet_names[16] = {
* VRRP checksum in network byte order.
*/
static uint16_t vrrp_pkt_checksum(struct vrrp_pkt *pkt, size_t pktsize,
struct ipaddr *src)
struct ipaddr *src, bool ipv4_ph)
{
uint16_t chksum;
bool v6 = (src->ipa_type == IPADDR_V6);
@ -89,13 +89,16 @@ static uint16_t vrrp_pkt_checksum(struct vrrp_pkt *pkt, size_t pktsize,
ph.next_hdr = IPPROTO_VRRP;
chksum = in_cksum_with_ph6(&ph, pkt, pktsize);
} else if (!v6 && ((pkt->hdr.vertype >> 4) == 3)) {
struct ipv4_ph ph = {};
if (ipv4_ph) {
struct ipv4_ph ph = {};
ph.src = src->ipaddr_v4;
inet_pton(AF_INET, VRRP_MCASTV4_GROUP_STR, &ph.dst);
ph.proto = IPPROTO_VRRP;
ph.len = htons(pktsize);
chksum = in_cksum_with_ph4(&ph, pkt, pktsize);
ph.src = src->ipaddr_v4;
inet_pton(AF_INET, VRRP_MCASTV4_GROUP_STR, &ph.dst);
ph.proto = IPPROTO_VRRP;
ph.len = htons(pktsize);
chksum = in_cksum_with_ph4(&ph, pkt, pktsize);
} else
chksum = in_cksum(pkt, pktsize);
} else if (!v6 && ((pkt->hdr.vertype >> 4) == 2)) {
chksum = in_cksum(pkt, pktsize);
} else {
@ -110,7 +113,7 @@ static uint16_t vrrp_pkt_checksum(struct vrrp_pkt *pkt, size_t pktsize,
ssize_t vrrp_pkt_adver_build(struct vrrp_pkt **pkt, struct ipaddr *src,
uint8_t version, uint8_t vrid, uint8_t prio,
uint16_t max_adver_int, uint8_t numip,
struct ipaddr **ips)
struct ipaddr **ips, bool ipv4_ph)
{
bool v6 = false;
size_t addrsz = 0;
@ -147,7 +150,7 @@ ssize_t vrrp_pkt_adver_build(struct vrrp_pkt **pkt, struct ipaddr *src,
aptr += addrsz;
}
(*pkt)->hdr.chksum = vrrp_pkt_checksum(*pkt, pktsize, src);
(*pkt)->hdr.chksum = vrrp_pkt_checksum(*pkt, pktsize, src, ipv4_ph);
return pktsize;
}
@ -188,10 +191,10 @@ size_t vrrp_pkt_adver_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt)
return rs;
}
ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m,
size_t read, struct ipaddr *src,
struct vrrp_pkt **pkt, char *errmsg,
size_t errmsg_len)
ssize_t vrrp_pkt_parse_datagram(int family, int version, bool ipv4_ph,
struct msghdr *m, size_t read,
struct ipaddr *src, struct vrrp_pkt **pkt,
char *errmsg, size_t errmsg_len)
{
/* Source (MAC & IP), Dest (MAC & IP) TTL validation done by kernel */
size_t addrsz = (family == AF_INET) ? sizeof(struct in_addr)
@ -289,7 +292,7 @@ ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m,
VRRP_PKT_VCHECK(pktver == version, "Bad version %u", pktver);
/* Checksum check */
uint16_t chksum = vrrp_pkt_checksum(*pkt, pktsize, src);
uint16_t chksum = vrrp_pkt_checksum(*pkt, pktsize, src, ipv4_ph);
VRRP_PKT_VCHECK((*pkt)->hdr.chksum == chksum,
"Bad VRRP checksum %hx; should be %hx",

View File

@ -131,7 +131,7 @@ struct vrrp_pkt {
ssize_t vrrp_pkt_adver_build(struct vrrp_pkt **pkt, struct ipaddr *src,
uint8_t version, uint8_t vrid, uint8_t prio,
uint16_t max_adver_int, uint8_t numip,
struct ipaddr **ips);
struct ipaddr **ips, bool ipv4_ph);
/* free memory allocated by vrrp_pkt_adver_build's pkt arg */
void vrrp_pkt_free(struct vrrp_pkt *pkt);
@ -195,9 +195,9 @@ size_t vrrp_pkt_adver_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt);
* Returns:
* Size of VRRP packet, or -1 upon error
*/
ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m,
size_t read, struct ipaddr *src,
struct vrrp_pkt **pkt, char *errmsg,
size_t errmsg_len);
ssize_t vrrp_pkt_parse_datagram(int family, int version, bool ipv4_ph,
struct msghdr *m, size_t read,
struct ipaddr *src, struct vrrp_pkt **pkt,
char *errmsg, size_t errmsg_len);
#endif /* __VRRP_PACKET_H__ */

View File

@ -281,6 +281,35 @@ void cli_show_preempt(struct vty *vty, const struct lyd_node *dnode,
vty_out(vty, " %svrrp %s preempt\n", pre ? "" : "no ", vrid);
}
/*
* XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/checksum-with-
* ipv4-pseudoheader
*/
DEFPY_YANG(vrrp_checksum_with_ipv4_pseudoheader,
vrrp_checksum_with_ipv4_pseudoheader_cmd,
"[no] vrrp (1-255)$vrid checksum-with-ipv4-pseudoheader",
NO_STR
VRRP_STR
VRRP_VRID_STR
"Checksum mode in VRRPv3\n")
{
nb_cli_enqueue_change(vty, "./checksum-with-ipv4-pseudoheader",
NB_OP_MODIFY, no ? "false" : "true");
return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
}
void cli_show_checksum_with_ipv4_pseudoheader(struct vty *vty,
const struct lyd_node *dnode,
bool show_defaults)
{
const char *vrid = yang_dnode_get_string(dnode, "../virtual-router-id");
const bool pre = yang_dnode_get_bool(dnode, NULL);
vty_out(vty, " %svrrp %s checksum-with-ipv4-pseudoheader\n",
pre ? "" : "no ", vrid);
}
/* XXX: yang conversion */
DEFPY_YANG(vrrp_autoconfigure,
vrrp_autoconfigure_cmd,
@ -304,7 +333,7 @@ DEFPY_YANG(vrrp_autoconfigure,
/* XXX: yang conversion */
DEFPY_YANG(vrrp_default,
vrrp_default_cmd,
"[no] vrrp default <advertisement-interval$adv (10-40950)$advint|preempt$p|priority$prio (1-254)$prioval|shutdown$s>",
"[no] vrrp default <advertisement-interval$adv (10-40950)$advint|preempt$p|priority$prio (1-254)$prioval|checksum-with-ipv4-pseudoheader$ipv4ph|shutdown$s>",
NO_STR
VRRP_STR
"Configure defaults for new VRRP instances\n"
@ -313,6 +342,7 @@ DEFPY_YANG(vrrp_default,
"Preempt mode\n"
VRRP_PRIORITY_STR
"Priority value\n"
"Checksum mode in VRRPv3\n"
"Force VRRP router into administrative shutdown\n")
{
if (adv) {
@ -329,6 +359,8 @@ DEFPY_YANG(vrrp_default,
vd.preempt_mode = !no;
if (prio)
vd.priority = no ? VRRP_DEFAULT_PRIORITY : prioval;
if (ipv4ph)
vd.checksum_with_ipv4_pseudoheader = !no;
if (s)
vd.shutdown = !no;
@ -374,6 +406,8 @@ static struct json_object *vrrp_build_json(struct vrrp_vrouter *vr)
json_object_boolean_add(j, "shutdown", vr->shutdown);
json_object_boolean_add(j, "preemptMode", vr->preempt_mode);
json_object_boolean_add(j, "acceptMode", vr->accept_mode);
json_object_boolean_add(j, "checksumWithIpv4Pseudoheader",
vr->checksum_with_ipv4_pseudoheader);
json_object_string_add(j, "interface", vr->ifp->name);
json_object_int_add(j, "advertisementInterval",
vr->advertisement_interval * CS2MS);
@ -499,6 +533,8 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr)
vr->preempt_mode ? "Yes" : "No");
ttable_add_row(tt, "%s|%s", "Accept Mode",
vr->accept_mode ? "Yes" : "No");
ttable_add_row(tt, "%s|%s", "Checksum with IPv4 Pseudoheader",
vr->checksum_with_ipv4_pseudoheader ? "Yes" : "No");
ttable_add_row(tt, "%s|%d ms", "Advertisement Interval",
vr->advertisement_interval * CS2MS);
ttable_add_row(tt, "%s|%d ms (stale)",
@ -752,4 +788,6 @@ void vrrp_vty_init(void)
install_element(INTERFACE_NODE, &vrrp_ip_cmd);
install_element(INTERFACE_NODE, &vrrp_ip6_cmd);
install_element(INTERFACE_NODE, &vrrp_preempt_cmd);
install_element(INTERFACE_NODE,
&vrrp_checksum_with_ipv4_pseudoheader_cmd);
}

View File

@ -40,5 +40,8 @@ void cli_show_ipv6(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults);
void cli_show_preempt(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults);
void cli_show_checksum_with_ipv4_pseudoheader(struct vty *vty,
const struct lyd_node *dnode,
bool show_defaults);
#endif /* __VRRP_VTY_H__ */

View File

@ -110,6 +110,13 @@ module frr-vrrpd {
address is not owned by the router interface";
}
leaf checksum-with-ipv4-pseudoheader {
type boolean;
default "true";
description
"Enabled if VRRPv3 checksum for IPv4 involves pseudoheader";
}
leaf advertisement-interval {
type uint16 {
range "1..4095";