]> git.proxmox.com Git - mirror_frr.git/commitdiff
ospfd: support write socket per interface
authorMark Stapp <mjs@labn.net>
Wed, 29 Mar 2023 20:58:25 +0000 (16:58 -0400)
committerMark Stapp <mjs@labn.net>
Tue, 11 Apr 2023 14:16:07 +0000 (10:16 -0400)
Add support for a write socket per interface, enabled by
default at the ospf instance level. An ospf instance-level
config allows this to be disabled, reverting to the older
behavior where a single per-instance socket is used for
sending and receiving packets.

Signed-off-by: Mark Stapp <mjs@labn.net>
ospfd/ospf_interface.c
ospfd/ospf_interface.h
ospfd/ospf_network.c
ospfd/ospf_network.h
ospfd/ospf_packet.c
ospfd/ospf_vty.c
ospfd/ospfd.c
ospfd/ospfd.h

index 649ba70e02445835dd08fae31fc2b8a208e9a556..5742ece1f7d0ede078a80665e130c37588c25d7f 100644 (file)
@@ -651,6 +651,8 @@ int ospf_if_new_hook(struct interface *ifp)
 
        ifp->info = XCALLOC(MTYPE_OSPF_IF_INFO, sizeof(struct ospf_if_info));
 
+       IF_OSPF_IF_INFO(ifp)->oii_fd = -1;
+
        IF_OIFS(ifp) = route_table_init();
        IF_OIFS_PARAMS(ifp) = route_table_init();
 
@@ -691,6 +693,8 @@ static int ospf_if_delete_hook(struct interface *ifp)
 {
        int rc = 0;
        struct route_node *rn;
+       struct ospf_if_info *oii;
+
        rc = ospf_opaque_del_if(ifp);
 
        /*
@@ -707,6 +711,13 @@ static int ospf_if_delete_hook(struct interface *ifp)
        route_table_finish(IF_OIFS(ifp));
        route_table_finish(IF_OIFS_PARAMS(ifp));
 
+       /* Close per-interface socket */
+       oii = ifp->info;
+       if (oii && oii->oii_fd > 0) {
+               close(oii->oii_fd);
+               oii->oii_fd = -1;
+       }
+
        XFREE(MTYPE_OSPF_IF_INFO, ifp->info);
 
        return rc;
@@ -1367,6 +1378,16 @@ static int ospf_ifp_up(struct interface *ifp)
        struct ospf_interface *oi;
        struct route_node *rn;
        struct ospf_if_info *oii = ifp->info;
+       struct ospf *ospf;
+
+       if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE))
+               zlog_debug("Zebra: Interface[%s] state change to up.",
+                          ifp->name);
+
+       /* Open per-intf write socket if configured */
+       ospf = ifp->vrf->info;
+       if (ospf && ospf->intf_socket_enabled)
+               ospf_ifp_sock_init(ifp);
 
        ospf_if_recalculate_output_cost(ifp);
 
@@ -1384,10 +1405,6 @@ static int ospf_ifp_up(struct interface *ifp)
                return 0;
        }
 
-       if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE))
-               zlog_debug("Zebra: Interface[%s] state change to up.",
-                          ifp->name);
-
        for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) {
                if ((oi = rn->info) == NULL)
                        continue;
@@ -1416,6 +1433,9 @@ static int ospf_ifp_down(struct interface *ifp)
                ospf_if_down(oi);
        }
 
+       /* Close per-interface write socket if configured */
+       ospf_ifp_sock_close(ifp);
+
        return 0;
 }
 
index 8625a72ac13209ea460539482a2704f701d72388..649df437a4f162d4c3d90bf7db9f1f15cd778355 100644 (file)
@@ -121,6 +121,9 @@ struct ospf_if_info {
                membership_counts[MEMBER_MAX]; /* multicast group refcnts */
 
        uint32_t curr_mtu;
+
+       /* Per-interface write socket, configured via 'ospf' object */
+       int oii_fd;
 };
 
 struct ospf_interface;
index bd5cd7682a6034be245ec99825b2691d78d8f764..aff8ed05c723542ed1118ce70c20799695ace683 100644 (file)
@@ -15,6 +15,7 @@
 #include "sockopt.h"
 #include "privs.h"
 #include "lib_errors.h"
+#include "lib/table.h"
 
 #include "ospfd/ospfd.h"
 #include "ospfd/ospf_network.h"
@@ -119,61 +120,60 @@ int ospf_if_drop_alldrouters(struct ospf *top, struct prefix *p,
        return ret;
 }
 
-int ospf_if_ipmulticast(struct ospf *top, struct prefix *p, ifindex_t ifindex)
+int ospf_if_ipmulticast(int fd, struct prefix *p, ifindex_t ifindex)
 {
        uint8_t val;
        int ret, len;
 
        /* Prevent receiving self-origined multicast packets. */
-       ret = setsockopt_ipv4_multicast_loop(top->fd, 0);
+       ret = setsockopt_ipv4_multicast_loop(fd, 0);
        if (ret < 0)
                flog_err(EC_LIB_SOCKET,
                         "can't setsockopt IP_MULTICAST_LOOP(0) for fd %d: %s",
-                        top->fd, safe_strerror(errno));
+                        fd, safe_strerror(errno));
 
        /* Explicitly set multicast ttl to 1 -- endo. */
        val = 1;
        len = sizeof(val);
-       ret = setsockopt(top->fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&val,
-                        len);
+       ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&val, len);
        if (ret < 0)
                flog_err(EC_LIB_SOCKET,
                         "can't setsockopt IP_MULTICAST_TTL(1) for fd %d: %s",
-                        top->fd, safe_strerror(errno));
+                        fd, safe_strerror(errno));
 #ifndef GNU_LINUX
        /* For GNU LINUX ospf_write uses IP_PKTINFO, in_pktinfo to send
         * packet out of ifindex. Below would be used Non Linux system.
         */
-       ret = setsockopt_ipv4_multicast_if(top->fd, p->u.prefix4, ifindex);
+       ret = setsockopt_ipv4_multicast_if(fd, p->u.prefix4, ifindex);
        if (ret < 0)
                flog_err(EC_LIB_SOCKET,
                         "can't setsockopt IP_MULTICAST_IF(fd %d, addr %pI4, ifindex %u): %s",
-                        top->fd, &p->u.prefix4, ifindex,
+                        fd, &p->u.prefix4, ifindex,
                         safe_strerror(errno));
 #endif
 
        return ret;
 }
 
-int ospf_sock_init(struct ospf *ospf)
+/*
+ * Helper to open and set up a socket; returns the new fd on success,
+ * -1 on error.
+ */
+static int sock_init_common(vrf_id_t vrf_id, const char *name, int *pfd)
 {
        int ospf_sock;
        int ret, hincl = 1;
 
-       /* silently ignore. already done */
-       if (ospf->fd > 0)
-               return -1;
-
-       if (ospf->vrf_id == VRF_UNKNOWN) {
+       if (vrf_id == VRF_UNKNOWN) {
                /* silently return since VRF is not ready */
                return -1;
        }
+
        frr_with_privs(&ospfd_privs) {
                ospf_sock = vrf_socket(AF_INET, SOCK_RAW, IPPROTO_OSPFIGP,
-                                      ospf->vrf_id, ospf->name);
+                                      vrf_id, name);
                if (ospf_sock < 0) {
-                       flog_err(EC_LIB_SOCKET,
-                                "ospf_read_sock_init: socket: %s",
+                       flog_err(EC_LIB_SOCKET, "%s: socket: %s", __func__,
                                 safe_strerror(errno));
                        return -1;
                }
@@ -212,10 +212,8 @@ int ospf_sock_init(struct ospf *ospf)
                                 ospf_sock);
        }
 
-       /* Update socket buffer sizes */
-       ospf_sock_bufsize_update(ospf, ospf_sock, OSPF_SOCK_BOTH);
+       *pfd = ospf_sock;
 
-       ospf->fd = ospf_sock;
        return ret;
 }
 
@@ -237,3 +235,79 @@ void ospf_sock_bufsize_update(const struct ospf *ospf, int sock,
                setsockopt_so_sendbuf(sock, bufsize);
        }
 }
+
+int ospf_sock_init(struct ospf *ospf)
+{
+       int ret;
+
+       /* silently ignore. already done */
+       if (ospf->fd > 0)
+               return -1;
+
+       ret = sock_init_common(ospf->vrf_id, ospf->name, &(ospf->fd));
+
+       if (ret >= 0) /* Update socket buffer sizes */
+               ospf_sock_bufsize_update(ospf, ospf->fd, OSPF_SOCK_BOTH);
+
+       return ret;
+}
+
+/*
+ * Open per-interface write socket
+ */
+int ospf_ifp_sock_init(struct interface *ifp)
+{
+       struct ospf_if_info *oii;
+       struct ospf_interface *oi;
+       struct ospf *ospf;
+       struct route_node *rn;
+       int ret;
+
+       oii = IF_OSPF_IF_INFO(ifp);
+       if (oii == NULL)
+               return -1;
+
+       if (oii->oii_fd > 0)
+               return 0;
+
+       rn = route_top(IF_OIFS(ifp));
+       if (rn && rn->info) {
+               oi = rn->info;
+               ospf = oi->ospf;
+       } else
+               return -1;
+
+       ret = sock_init_common(ifp->vrf->vrf_id, ifp->name, &oii->oii_fd);
+
+       if (ret >= 0) /* Update socket buffer sizes */
+               ospf_sock_bufsize_update(ospf, oii->oii_fd, OSPF_SOCK_BOTH);
+
+       if (IS_DEBUG_OSPF_EVENT)
+               zlog_debug("%s: ifp %s, oii %p, fd %d", __func__, ifp->name,
+                          oii, oii->oii_fd);
+
+       return ret;
+}
+
+/*
+ * Close per-interface write socket
+ */
+int ospf_ifp_sock_close(struct interface *ifp)
+{
+       struct ospf_if_info *oii;
+
+       oii = IF_OSPF_IF_INFO(ifp);
+       if (oii == NULL)
+               return 0;
+
+       if (oii->oii_fd > 0) {
+               if (IS_DEBUG_OSPF_EVENT)
+                       zlog_debug("%s: ifp %s, oii %p, fd %d", __func__,
+                                  ifp->name, oii, oii->oii_fd);
+
+               close(oii->oii_fd);
+               oii->oii_fd = -1;
+       }
+
+       return 0;
+}
index d9b579c04a448260e76667cf1d72c31ea6d13264..b810bad50bef3e359ba906a2d912fea4a987f2c3 100644 (file)
@@ -13,8 +13,11 @@ extern int ospf_if_drop_allspfrouters(struct ospf *, struct prefix *,
                                      ifindex_t);
 extern int ospf_if_add_alldrouters(struct ospf *, struct prefix *, ifindex_t);
 extern int ospf_if_drop_alldrouters(struct ospf *, struct prefix *, ifindex_t);
-extern int ospf_if_ipmulticast(struct ospf *, struct prefix *, ifindex_t);
+extern int ospf_if_ipmulticast(int fd, struct prefix *, ifindex_t);
 extern int ospf_sock_init(struct ospf *ospf);
+/* Open, close per-interface write socket */
+int ospf_ifp_sock_init(struct interface *ifp);
+int ospf_ifp_sock_close(struct interface *ifp);
 
 enum ospf_sock_type_e {
        OSPF_SOCK_NONE = 0,
index 5f7d49e0bbc4afc1efd714fd2ef54ea91d7a88f3..552acfd6d32be08f5c5424f5614b928828c8afd0 100644 (file)
@@ -618,7 +618,7 @@ static void ospf_write(struct event *thread)
        struct msghdr msg;
        struct iovec iov[2];
        uint8_t type;
-       int ret;
+       int ret, fd;
        int flags = 0;
        struct listnode *node;
 #ifdef WANT_OSPF_WRITE_FRAGMENT
@@ -633,11 +633,12 @@ static void ospf_write(struct event *thread)
        struct cmsghdr *cm = (struct cmsghdr *)cmsgbuf;
        struct in_pktinfo *pi;
 #endif
+       fd = ospf->fd;
 
-       if (ospf->fd < 0 || ospf->oi_running == 0) {
+       if (fd < 0 || ospf->oi_running == 0) {
                if (IS_DEBUG_OSPF_EVENT)
                        zlog_debug("%s failed to send, fd %d, instance %u",
-                                  __func__, ospf->fd, ospf->oi_running);
+                                  __func__, fd, ospf->oi_running);
                return;
        }
 
@@ -657,6 +658,15 @@ static void ospf_write(struct event *thread)
                /* convenience - max OSPF data per packet */
                maxdatasize = oi->ifp->mtu - sizeof(struct ip);
 #endif /* WANT_OSPF_WRITE_FRAGMENT */
+
+               /* Reset socket fd to use. */
+               fd = ospf->fd;
+
+               /* Check for per-interface socket */
+               if (ospf->intf_socket_enabled &&
+                   (IF_OSPF_IF_INFO(oi->ifp))->oii_fd > 0)
+                       fd = (IF_OSPF_IF_INFO(oi->ifp))->oii_fd;
+
                /* Get one packet from queue. */
                op = ospf_fifo_head(oi->obuf);
                assert(op);
@@ -664,8 +674,7 @@ static void ospf_write(struct event *thread)
 
                if (op->dst.s_addr == htonl(OSPF_ALLSPFROUTERS)
                    || op->dst.s_addr == htonl(OSPF_ALLDROUTERS))
-                       ospf_if_ipmulticast(ospf, oi->address,
-                                           oi->ifp->ifindex);
+                       ospf_if_ipmulticast(fd, oi->address, oi->ifp->ifindex);
 
                /* Rewrite the md5 signature & update the seq */
                ospf_make_md5_digest(oi, op);
@@ -760,13 +769,13 @@ static void ospf_write(struct event *thread)
 
 #ifdef WANT_OSPF_WRITE_FRAGMENT
                if (op->length > maxdatasize)
-                       ospf_write_frags(ospf->fd, op, &iph, &msg, maxdatasize,
+                       ospf_write_frags(fd, op, &iph, &msg, maxdatasize,
                                         oi->ifp->mtu, flags, type);
 #endif /* WANT_OSPF_WRITE_FRAGMENT */
 
                /* send final fragment (could be first) */
                sockopt_iphdrincl_swab_htosys(&iph);
-               ret = sendmsg(ospf->fd, &msg, flags);
+               ret = sendmsg(fd, &msg, flags);
                sockopt_iphdrincl_swab_systoh(&iph);
                if (IS_DEBUG_OSPF_EVENT)
                        zlog_debug(
index 9b918798c04c11ff30e5c5bdbdbe75067f77fec4..3c0e0fcb635994ea1b379967bb00edcefbdd46b7 100644 (file)
 #include "ospfd/ospf_spf.h"
 #include "ospfd/ospf_route.h"
 #include "ospfd/ospf_zebra.h"
-/*#include "ospfd/ospf_routemap.h" */
 #include "ospfd/ospf_vty.h"
 #include "ospfd/ospf_dump.h"
 #include "ospfd/ospf_bfd.h"
 #include "ospfd/ospf_ldp_sync.h"
-
+#include "ospfd/ospf_network.h"
 
 FRR_CFG_DEFAULT_BOOL(OSPF_LOG_ADJACENCY_CHANGES,
        { .val_bool = true, .match_profile = "datacenter", },
@@ -12511,6 +12510,9 @@ static int ospf_config_write_one(struct vty *vty, struct ospf *ospf)
        if (ospf->fr_configured)
                vty_out(vty, " flood-reduction\n");
 
+       if (!ospf->intf_socket_enabled)
+               vty_out(vty, " no socket-per-interface\n");
+
        /* Redistribute information print. */
        config_write_ospf_redistribute(vty, ospf);
 
@@ -13075,6 +13077,35 @@ DEFPY(ospf_socket_bufsizes,
        return CMD_SUCCESS;
 }
 
+DEFPY (per_intf_socket,
+       per_intf_socket_cmd,
+       "[no] socket-per-interface",
+       NO_STR
+       "Use write socket per interface\n")
+{
+       VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
+       struct listnode *node;
+       struct ospf_interface *oi;
+
+       if (no) {
+               if (ospf->intf_socket_enabled) {
+                       ospf->intf_socket_enabled = false;
+
+                       /* Iterate and close any sockets */
+                       for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi))
+                               ospf_ifp_sock_close(oi->ifp);
+               }
+       } else if (!ospf->intf_socket_enabled) {
+               ospf->intf_socket_enabled = true;
+
+               /* Iterate and open sockets */
+               for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi))
+                       ospf_ifp_sock_init(oi->ifp);
+       }
+
+       return CMD_SUCCESS;
+}
+
 void ospf_vty_clear_init(void)
 {
        install_element(ENABLE_NODE, &clear_ip_ospf_interface_cmd);
@@ -13239,6 +13270,7 @@ void ospf_vty_init(void)
        install_element(OSPF_NODE, &no_flood_reduction_area_cmd);
 
        install_element(OSPF_NODE, &ospf_socket_bufsizes_cmd);
+       install_element(OSPF_NODE, &per_intf_socket_cmd);
 
        /* Init interface related vty commands. */
        ospf_vty_if_init();
index 15ce1c48a436d6fa773bc7db5acfc80e825644a4..7e83714c0ae18d99b5b113ad6d5739876d93e427 100644 (file)
@@ -419,6 +419,7 @@ struct ospf *ospf_new_alloc(unsigned short instance, const char *name)
        QOBJ_REG(new, ospf);
 
        new->fd = -1;
+       new->intf_socket_enabled = true;
 
        new->recv_sock_bufsize = OSPF_DEFAULT_SOCK_BUFSIZE;
        new->send_sock_bufsize = OSPF_DEFAULT_SOCK_BUFSIZE;
index 1b66d4d9304c97e2fc2b7d744962f4c41a97bfbe..1f8d1a32e6b7a2ff2d514aaaa75b3599d9dd2dd0 100644 (file)
@@ -431,6 +431,9 @@ struct ospf {
        uint32_t recv_sock_bufsize;
        uint32_t send_sock_bufsize;
 
+       /* Per-interface write socket */
+       bool intf_socket_enabled;
+
        QOBJ_FIELDS;
 };
 DECLARE_QOBJ_TYPE(ospf);