ospfd: support write socket per interface

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>
This commit is contained in:
Mark Stapp 2023-03-29 16:58:25 -04:00
parent e80c797a1f
commit 04a0401f2d
8 changed files with 179 additions and 34 deletions

View 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;
}

View 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;

View 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;
}

View 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,

View 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(

View File

@ -35,12 +35,11 @@
#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();

View 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;

View 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);