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"]

Reply via email to