The RTR session was opened with a blocking connect() call. This is rather
bad if the RTR peer does not exist since then bgpd will block until the
connect timed out. This diff makes the connect() call non-blocking.
With this connecting to non-existing RTR servers no longer blocks the main
process.
--
:wq Claudio
Index: bgpd.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.c,v
retrieving revision 1.235
diff -u -p -r1.235 bgpd.c
--- bgpd.c 3 May 2021 13:18:06 -0000 1.235
+++ bgpd.c 3 May 2021 15:33:20 -0000
@@ -50,6 +50,7 @@ static void getsockpair(int [2]);
int imsg_send_sockets(struct imsgbuf *, struct imsgbuf *,
struct imsgbuf *);
void bgpd_rtr_connect(struct rtr_config *);
+void bgpd_rtr_connect_done(int, struct bgpd_config *);
int cflags;
volatile sig_atomic_t mrtdump;
@@ -64,6 +65,16 @@ struct rib_names ribnames = SIMPLEQ_HEA
char *cname;
char *rcname;
+struct connect_elm {
+ TAILQ_ENTRY(connect_elm) entry;
+ u_int32_t id;
+ int fd;
+};
+
+TAILQ_HEAD( ,connect_elm) connect_queue = \
+ TAILQ_HEAD_INITIALIZER(connect_queue);
+u_int connect_cnt;
+
void
sighdlr(int sig)
{
@@ -97,7 +108,7 @@ usage(void)
#define PFD_PIPE_RTR 2
#define PFD_SOCK_ROUTE 3
#define PFD_SOCK_PFKEY 4
-#define POLL_MAX 5
+#define PFD_CONNECT_START 5
#define MAX_TIMEOUT 3600
int cmd_opts;
@@ -109,11 +120,13 @@ main(int argc, char *argv[])
enum bgpd_process proc = PROC_MAIN;
struct rde_rib *rr;
struct peer *p;
- struct pollfd pfd[POLL_MAX];
+ struct pollfd *pfd = NULL;
+ struct connect_elm *ce;
time_t timeout;
pid_t se_pid = 0, rde_pid = 0, rtr_pid = 0, pid;
char *conffile;
char *saved_argv0;
+ u_int pfd_elms = 0, npfd, i;
int debug = 0;
int rfd, keyfd;
int ch, status;
@@ -289,7 +302,21 @@ BROKEN if (pledge("stdio rpath wpath cpa
quit = 1;
while (quit == 0) {
- bzero(pfd, sizeof(pfd));
+ if (pfd_elms < PFD_CONNECT_START + connect_cnt) {
+ struct pollfd *newp;
+
+ if ((newp = reallocarray(pfd,
+ PFD_CONNECT_START + connect_cnt,
+ sizeof(struct pollfd))) == NULL) {
+ log_warn("could not resize pfd from %u -> %u"
+ " entries", pfd_elms, PFD_CONNECT_START +
+ connect_cnt);
+ fatalx("exiting");
+ }
+ pfd = newp;
+ pfd_elms = PFD_CONNECT_START + connect_cnt;
+ }
+ bzero(pfd, sizeof(struct pollfd) * pfd_elms);
timeout = mrt_timeout(conf->mrt);
@@ -303,9 +330,17 @@ BROKEN if (pledge("stdio rpath wpath cpa
set_pollfd(&pfd[PFD_PIPE_RDE], ibuf_rde);
set_pollfd(&pfd[PFD_PIPE_RTR], ibuf_rtr);
+ npfd = PFD_CONNECT_START;
+ TAILQ_FOREACH(ce, &connect_queue, entry) {
+ pfd[npfd].fd = ce->fd;
+ pfd[npfd++].events = POLLOUT;
+ if (npfd > pfd_elms)
+ fatalx("polli pfd overflow");
+ }
+
if (timeout < 0 || timeout > MAX_TIMEOUT)
timeout = MAX_TIMEOUT;
- if (poll(pfd, POLL_MAX, timeout * 1000) == -1)
+ if (poll(pfd, npfd, timeout * 1000) == -1)
if (errno != EINTR) {
log_warn("poll error");
quit = 1;
@@ -357,6 +392,10 @@ BROKEN if (pledge("stdio rpath wpath cpa
}
}
+ for (i = PFD_CONNECT_START; i < npfd; i++)
+ if (pfd[i].revents != 0)
+ bgpd_rtr_connect_done(pfd[i].fd, conf);
+
if (reconfig) {
u_int error;
@@ -1261,32 +1300,97 @@ imsg_send_sockets(struct imsgbuf *se, st
void
bgpd_rtr_connect(struct rtr_config *r)
{
+ struct connect_elm *ce;
struct sockaddr *sa;
socklen_t len;
- int fd;
- /* XXX should be non-blocking */
- fd = socket(aid2af(r->remote_addr.aid), SOCK_STREAM, 0);
- if (fd == -1) {
+ if ((ce = calloc(1, sizeof(*ce))) == NULL) {
+ log_warn("rtr %s", r->descr);
+ return;
+ }
+
+ ce->id = r->id;
+ ce->fd = socket(aid2af(r->remote_addr.aid),
+ SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_TCP);
+ if (ce->fd == -1) {
log_warn("rtr %s", r->descr);
+ free(ce);
return;
}
+
if ((sa = addr2sa(&r->local_addr, 0, &len)) != NULL) {
- if (bind(fd, sa, len) == -1) {
+ if (bind(ce->fd, sa, len) == -1) {
log_warn("rtr %s: bind to %s", r->descr,
log_addr(&r->local_addr));
- close(fd);
+ close(ce->fd);
+ free(ce);
return;
}
}
sa = addr2sa(&r->remote_addr, r->remote_port, &len);
- if (connect(fd, sa, len) == -1) {
+ if (connect(ce->fd, sa, len) == -1) {
+ if (errno != EINPROGRESS) {
+ log_warn("rtr %s: connect to %s:%u", r->descr,
+ log_addr(&r->remote_addr), r->remote_port);
+ close(ce->fd);
+ free(ce);
+ return;
+ }
+ TAILQ_INSERT_TAIL(&connect_queue, ce, entry);
+ connect_cnt++;
+ return;
+ }
+
+ imsg_compose(ibuf_rtr, IMSG_SOCKET_CONN, ce->id, 0, ce->fd, NULL, 0);
+ free(ce);
+}
+
+void
+bgpd_rtr_connect_done(int fd, struct bgpd_config *conf)
+{
+ struct rtr_config *r;
+ struct connect_elm *ce;
+ int error = 0;
+ socklen_t len;
+
+ TAILQ_FOREACH(ce, &connect_queue, entry) {
+ if (ce->fd == fd)
+ break;
+ }
+ if (ce == NULL)
+ fatalx("connect entry not found");
+
+ TAILQ_REMOVE(&connect_queue, ce, entry);
+ connect_cnt--;
+
+ SIMPLEQ_FOREACH(r, &conf->rtrs, entry) {
+ if (ce->id == r->id)
+ break;
+ }
+ if (r == NULL) {
+ log_warnx("rtr id %d no longer exists", ce->id);
+ goto fail;
+ }
+
+ len = sizeof(error);
+ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) == -1) {
+ log_warn("rtr %s: getsockopt SO_ERROR", r->descr);
+ goto fail;
+ }
+
+ if (error != 0) {
+ errno = error;
log_warn("rtr %s: connect to %s:%u", r->descr,
log_addr(&r->remote_addr), r->remote_port);
- close(fd);
- return;
+ goto fail;
}
- imsg_compose(ibuf_rtr, IMSG_SOCKET_CONN, r->id, 0, fd, NULL, 0);
+ imsg_compose(ibuf_rtr, IMSG_SOCKET_CONN, ce->id, 0, ce->fd, NULL, 0);
+ free(ce);
+ return;
+
+fail:
+ close(fd);
+ free(ce);
}