]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Use netlink API to insert ARP entries on FreeBSD
authorNick Porter <nick@portercomputing.co.uk>
Tue, 15 Jul 2025 09:54:19 +0000 (10:54 +0100)
committerNick Porter <nick@portercomputing.co.uk>
Tue, 15 Jul 2025 09:54:19 +0000 (10:54 +0100)
Comparable to how FreeBSD's arp command adds entries using netlink.

src/listen/dhcpv4/proto_dhcpv4_udp.c
src/protocols/arp/arp.h
src/protocols/arp/base.c

index 1d67c36f05f07359aea2e83a02427da1dd5085fa..467e237e61b9a1a689566b2d0cdd19913754e8aa 100644 (file)
@@ -539,6 +539,29 @@ static ssize_t mod_write(fr_listen_t *li, void *packet_ctx, UNUSED fr_time_t req
                                        socket.inet.dst_ipaddr.addr.v4.s_addr = INADDR_BROADCAST;
                                }
 #endif
+#ifdef __FreeBSD__
+                       } else if (inst->broadcast &&
+#ifdef HAVE_LIBPCAP
+                                  !inst->use_pcap &&
+#endif
+                                  (inst->interface || if_name[0])) {
+                               uint8_t macaddr[6];
+                               uint8_t ipaddr[4];
+
+                               memcpy(&ipaddr, &packet->yiaddr, 4);
+                               memcpy(&macaddr, &packet->chaddr, 6);
+
+                               /*
+                                *      FreeBSD version of above using netlink API
+                                */
+                               if (fr_bsd_arp_entry_add(socket.inet.ifindex, ipaddr, macaddr) == 0) {
+                                       DEBUG("Reply will be unicast to YADDR, done ARP table updates.");
+                                       memcpy(&socket.inet.dst_ipaddr.addr.v4.s_addr, &packet->yiaddr, 4);
+                               } else {
+                                       DEBUG("Failed adding ARP table entry.  Reply will be broadcast.");
+                                       socket.inet.dst_ipaddr.addr.v4.s_addr = INADDR_BROADCAST;
+                               }
+#endif
 #ifdef HAVE_LIBPCAP
                        } else if (inst->use_pcap && inst->broadcast && (inst->interface || if_name[0])) {
                                proto_dhcpv4_pcap_t     *pcap;
@@ -889,7 +912,7 @@ static int mod_instantiate(module_inst_ctx_t const *mctx)
                inst->port = ntohl(s->s_port);
        }
 
-#ifdef SIOCSARP
+#if defined(SIOCSARP) || defined(__FreeBSD__)
        /*
         *      If we're listening for broadcast requests, we MUST
         */
index a7bcc2eea215a780785d07b9e88b212586490818..232796d502993bb8db590fecd7f618c2bc44c0f3 100644 (file)
@@ -45,6 +45,10 @@ ssize_t fr_arp_decode(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *packe
 
 int fr_arp_entry_add(int fd, char const *interface, uint8_t ipaddr[static 4], uint8_t macaddr[static 6]);
 
+#ifdef __FreeBSD__
+int fr_bsd_arp_entry_add(uint32_t ifindex, uint8_t ipaddr[static 4], uint8_t macaddr[static 6]);
+#endif
+
 /*
  *     ARP for ethernet && IPv4.
  */
index f3b58faa30befebf3f9fbb5d56834cfd6c9c07ab..e117e926279ba26426a55d52e293b23a72a8e86f 100644 (file)
@@ -38,6 +38,18 @@ RCSID("$Id$")
 
 #include <net/if_arp.h>
 
+#ifdef __FreeBSD__
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <netlink/netlink.h>
+#include <netlink/netlink_route.h>
+#include <netlink/netlink_snl.h>
+#include <netlink/netlink_snl_route.h>
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+#endif
+
 static uint32_t instance_count = 0;
 
 fr_dict_t const *dict_arp;
@@ -139,6 +151,82 @@ int fr_arp_entry_add(UNUSED int fd, UNUSED char const *interface,
 }
 #endif
 
+#ifdef __FreeBSD__
+/** Use FreeBSD netlink API to add ARP entries
+ */
+int fr_bsd_arp_entry_add(uint32_t ifindex, uint8_t ipaddr[static 4], uint8_t macaddr[static 6])
+{
+       struct sockaddr_in      sin;
+       struct sockaddr_dl      sdl;
+       struct snl_state        state;
+       struct snl_writer       nw;
+       struct nlmsghdr         *msghdr;
+       struct ndmsg            *msg;
+       struct snl_errmsg_data  errmsg = {};
+
+       if (ifindex == 0) {
+               fr_strerror_const("Missing interface index");
+               return -1;
+       }
+
+       sin.sin_family = AF_INET;
+       memcpy(&sin.sin_addr.s_addr, ipaddr, 4);
+
+       sdl = (struct sockaddr_dl){
+               .sdl_len = sizeof(sdl),
+               .sdl_family = AF_LINK,
+               .sdl_alen = ETHER_ADDR_LEN
+       };
+       memcpy(LLADDR(&sdl), macaddr, ETHER_ADDR_LEN);
+
+       if (!snl_init(&state, NETLINK_ROUTE)) {
+               if (modfind("netlink") == -1 && errno == ENOENT) {
+                       if (kldload("netlink") == -1) {
+                               fr_strerror_const("netlink is not loaded and load attempt failed");
+                               return -1;
+                       }
+               } else {
+               open_error:
+                       fr_strerror_const("Unable to open netlink socket");
+               }
+               if (!snl_init(&state, NETLINK_ROUTE)) goto open_error;
+       }
+
+       snl_init_writer(&state, &nw);
+       msghdr = snl_create_msg_request(&nw, RTM_NEWNEIGH);
+       msghdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
+
+       msg = snl_reserve_msg_object(&nw, struct ndmsg);
+       if (!msg) {
+               fr_strerror_const("Failed reserving message");
+       error:
+               snl_free(&state);
+               return -1;
+       }
+       msg->ndm_family = AF_INET;
+       msg->ndm_ifindex = ifindex;
+       msg->ndm_state = NUD_PERMANENT;
+       msg->ndm_flags = NTF_STICKY;
+
+       snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)&sin);
+       snl_add_msg_attr(&nw, NDA_LLADDR, sdl.sdl_alen, LLADDR(&sdl));
+
+       if (!(msghdr = snl_finalize_msg(&nw)) || !snl_send_message(&state, msghdr)) {
+               fr_strerror_const("Failed sending netlink message.");
+               goto error;
+       }
+
+       snl_read_reply_code(&state, msghdr->nlmsg_seq, &errmsg);
+       if (errmsg.error != 0) {
+               fr_strerror_printf("Failed adding ARP table entry: %s (%s)", strerror(errmsg.error), errmsg.error_str);
+               goto error;
+       }
+
+       snl_free(&state);
+
+       return 0;
+}
+#endif
 
 /** Encode VPS into a raw ARP packet.
  *