The branch main has been updated by melifaro: URL: https://cgit.FreeBSD.org/src/commit/?id=7eee0eaf1602765bdf20c8e56884069085812c27
commit 7eee0eaf1602765bdf20c8e56884069085812c27 Author: Alexander V. Chernikov <melif...@freebsd.org> AuthorDate: 2023-05-20 10:42:08 +0000 Commit: Alexander V. Chernikov <melif...@freebsd.org> CommitDate: 2023-05-20 10:42:08 +0000 netlink: automatically generate broadcast for IPv4 ifa if not set. MFC after: 2 weeks --- sys/netlink/route/iface.c | 34 +++++++++++++++++++++++++++------- tests/sys/netlink/test_rtnl_ifaddr.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/sys/netlink/route/iface.c b/sys/netlink/route/iface.c index 976b485b3f56..33a5dbfec3a3 100644 --- a/sys/netlink/route/iface.c +++ b/sys/netlink/route/iface.c @@ -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, diff --git a/tests/sys/netlink/test_rtnl_ifaddr.py b/tests/sys/netlink/test_rtnl_ifaddr.py index 11c08b32674a..c7d6d86e781b 100644 --- a/tests/sys/netlink/test_rtnl_ifaddr.py +++ b/tests/sys/netlink/test_rtnl_ifaddr.py @@ -254,6 +254,41 @@ class TestRtNlIfaddrOpsBroadcast(RtnlIfaOps): 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"]