netlink: automatically generate broadcast for IPv4 ifa if not set.

MFC after:	2 weeks
This commit is contained in:
Alexander V. Chernikov 2023-05-20 10:42:08 +00:00
parent a72b78905a
commit 7eee0eaf16
2 changed files with 62 additions and 7 deletions

View File

@ -1096,13 +1096,14 @@ static int
handle_newaddr_inet(struct nlmsghdr *hdr, struct nl_parsed_ifa *attrs,
struct ifnet *ifp, struct nlpcb *nlp, struct nl_pstate *npt)
{
if (attrs->ifa_prefixlen > 32) {
int plen = attrs->ifa_prefixlen;
int if_flags = if_getflags(ifp);
if (plen > 32) {
nlmsg_report_err_msg(npt, "invalid ifa_prefixlen");
return (EINVAL);
};
int if_flags = if_getflags(ifp);
if (if_flags & IFF_POINTOPOINT) {
if (attrs->ifa_addr == NULL || attrs->ifa_dst == NULL) {
nlmsg_report_err_msg(npt, "Empty IFA_LOCAL/IFA_ADDRESS");
@ -1115,13 +1116,32 @@ handle_newaddr_inet(struct nlmsghdr *hdr, struct nl_parsed_ifa *attrs,
}
attrs->ifa_dst = attrs->ifa_broadcast;
if (attrs->ifa_dst == NULL && !(if_flags & IFF_LOOPBACK)) {
nlmsg_report_err_msg(npt, "empty IFA_BROADCAST for BRD interface");
return (EINVAL);
/* Generate broadcast address if not set */
if ((if_flags & IFF_BROADCAST) && attrs->ifa_dst == NULL) {
uint32_t s_baddr;
struct sockaddr_in *sin_brd;
if (plen == 31)
s_baddr = INADDR_BROADCAST; /* RFC 3021 */
else {
struct sockaddr_in *addr;
uint32_t s_mask;
addr = (struct sockaddr_in *)attrs->ifa_addr;
s_mask = htonl(plen ? ~((1 << (32 - plen)) - 1) : 0);
s_baddr = addr->sin_addr.s_addr | ~s_mask;
}
sin_brd = (struct sockaddr_in *)npt_alloc(npt, sizeof(*sin_brd));
if (sin_brd == NULL)
return (ENOMEM);
sin_brd->sin_family = AF_INET;
sin_brd->sin_len = sizeof(*sin_brd);
sin_brd->sin_addr.s_addr = s_baddr;
attrs->ifa_dst = (struct sockaddr *)sin_brd;
}
}
int plen = attrs->ifa_prefixlen;
struct sockaddr_in mask = {
.sin_len = sizeof(struct sockaddr_in),
.sin_family = AF_INET,

View File

@ -254,6 +254,41 @@ def test_add_4(self):
assert rx_msg.get_nla(IfaAttrType.IFA_LOCAL).addr == str(ifa.ip)
assert rx_msg.get_nla(IfaAttrType.IFA_BROADCAST).addr == str(ifa_brd)
@pytest.mark.parametrize(
"brd",
[
pytest.param((32, True, "192.0.2.1"), id="auto_32"),
pytest.param((31, True, "255.255.255.255"), id="auto_31"),
pytest.param((30, True, "192.0.2.3"), id="auto_30"),
pytest.param((30, False, "192.0.2.2"), id="custom_30"),
pytest.param((24, False, "192.0.2.7"), id="custom_24"),
],
)
def test_add_4_brd(self, brd):
"""Tests proper broadcast setup when adding IPv4 ifa"""
plen, auto_brd, ifa_brd_str = brd
ifa = ipaddress.ip_interface("192.0.2.1/{}".format(plen))
iface = self.vnet.iface_alias_map["if1"]
ifa_brd = ipaddress.ip_address(ifa_brd_str)
msg = self.create_msg(ifa)
msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
if not auto_brd:
msg.add_nla(NlAttrIp(IfaAttrType.IFA_BROADCAST, str(ifa_brd)))
self.send_check_success(msg)
lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
assert len(lst) == 1
rx_msg = lst[0]
assert rx_msg.base_hdr.ifa_prefixlen == ifa.network.prefixlen
assert rx_msg.base_hdr.ifa_scope == RtScope.RT_SCOPE_UNIVERSE.value
assert rx_msg.get_nla(IfaAttrType.IFA_ADDRESS).addr == str(ifa.ip)
assert rx_msg.get_nla(IfaAttrType.IFA_LOCAL).addr == str(ifa.ip)
assert rx_msg.get_nla(IfaAttrType.IFA_BROADCAST).addr == str(ifa_brd)
def test_add_6(self):
ifa = ipaddress.ip_interface("2001:db8::1/64")
iface = self.vnet.iface_alias_map["if1"]