git: 7eee0eaf1602 - main - netlink: automatically generate broadcast for IPv4 ifa if not set.

From: Alexander V. Chernikov <melifaro_at_FreeBSD.org>
Date: Sat, 20 May 2023 10:43:04 UTC
The branch main has been updated by melifaro:

URL: https://cgit.FreeBSD.org/src/commit/?id=7eee0eaf1602765bdf20c8e56884069085812c27

commit 7eee0eaf1602765bdf20c8e56884069085812c27
Author:     Alexander V. Chernikov <melifaro@FreeBSD.org>
AuthorDate: 2023-05-20 10:42:08 +0000
Commit:     Alexander V. Chernikov <melifaro@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"]