summaryrefslogtreecommitdiff
path: root/usr.sbin/bgpd/session.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/bgpd/session.c')
-rw-r--r--usr.sbin/bgpd/session.c248
1 files changed, 192 insertions, 56 deletions
diff --git a/usr.sbin/bgpd/session.c b/usr.sbin/bgpd/session.c
index 603d68c27ac..9d581aedee6 100644
--- a/usr.sbin/bgpd/session.c
+++ b/usr.sbin/bgpd/session.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: session.c,v 1.172 2004/05/28 18:39:09 henning Exp $ */
+/* $OpenBSD: session.c,v 1.173 2004/06/06 17:38:10 henning Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -44,15 +44,13 @@
#include "mrt.h"
#include "session.h"
-#define PFD_LISTEN 0
-#define PFD_LISTEN6 1
-#define PFD_PIPE_MAIN 2
-#define PFD_PIPE_ROUTE 3
-#define PFD_SOCK_CTL 4
-#define PFD_PEERS_START 5
+#define PFD_PIPE_MAIN 0
+#define PFD_PIPE_ROUTE 1
+#define PFD_SOCK_CTL 2
+#define PFD_LISTENERS_START 3
void session_sighdlr(int);
-int setup_listener(struct sockaddr *);
+int setup_listeners(void);
void init_conf(struct bgpd_config *);
void init_peer(struct peer *);
int timer_due(time_t);
@@ -81,6 +79,7 @@ void session_dispatch_imsg(struct imsgbuf *, int);
void session_up(struct peer *);
void session_down(struct peer *);
+int la_cmp(struct listen_addr *, struct listen_addr *);
struct peer *getpeerbyip(struct sockaddr *);
int session_match_mask(struct peer *, struct sockaddr *);
struct peer *getpeerbyid(u_int32_t);
@@ -91,8 +90,6 @@ struct bgpd_sysdep sysdep;
struct peer *npeers;
volatile sig_atomic_t session_quit = 0;
int pending_reconf = 0;
-int sock = -1;
-int sock6 = -1;
int csock = -1;
struct imsgbuf ibuf_rde;
struct imsgbuf ibuf_main;
@@ -111,41 +108,77 @@ session_sighdlr(int sig)
}
int
-setup_listener(struct sockaddr *sa)
+setup_listeners(void)
{
- int fd, opt;
+ int opt;
+ struct listen_addr *la;
+
+ if (TAILQ_EMPTY(conf->listen_addrs)) {
+ if ((la = calloc(1, sizeof(struct listen_addr))) == NULL)
+ fatal("setup_listeners calloc");
+ la->fd = -1;
+ la->flags = DEFAULT_LISTENER;
+ la->sa.ss_len = sizeof(struct sockaddr_in);
+ ((struct sockaddr_in *)&la->sa)->sin_family = AF_INET;
+ ((struct sockaddr_in *)&la->sa)->sin_addr.s_addr =
+ htonl(INADDR_ANY);
+ ((struct sockaddr_in *)&la->sa)->sin_port = htons(BGP_PORT);
+ TAILQ_INSERT_TAIL(conf->listen_addrs, la, entry);
+
+ if ((la = calloc(1, sizeof(struct listen_addr))) == NULL)
+ fatal("setup_listeners calloc");
+ la->fd = -1;
+ la->flags = DEFAULT_LISTENER;
+ la->sa.ss_len = sizeof(struct sockaddr_in6);
+ ((struct sockaddr_in6 *)&la->sa)->sin6_family = AF_INET6;
+ ((struct sockaddr_in6 *)&la->sa)->sin6_port = htons(BGP_PORT);
+ TAILQ_INSERT_TAIL(conf->listen_addrs, la, entry);
+ }
+
+ TAILQ_FOREACH(la, conf->listen_addrs, entry) {
+ la->reconf = RECONF_NONE;
+
+ if (la->fd != -1)
+ continue;
- if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6)
- fatal("king bula sez: unknown address family");
+ if ((la->fd = socket(la->sa.ss_family, SOCK_STREAM,
+ IPPROTO_TCP)) == -1)
+ fatal("socket");
- if ((fd = socket(sa->sa_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
- log_warn("error setting up %s listener",
- sa->sa_family == AF_INET ? "IPv4" : "IPv6");
- return (fd);
- }
+ opt = 1;
+ if (setsockopt(la->fd, IPPROTO_TCP, TCP_MD5SIG,
+ &opt, sizeof(opt)) == -1) {
+ if (errno == ENOPROTOOPT) { /* system w/o md5sig */
+ log_warnx("md5sig not available, disabling");
+ sysdep.no_md5sig = 1;
+ } else
+ fatal("setsockopt TCP_MD5SIG");
+ }
- opt = 1;
- if (setsockopt(fd, IPPROTO_TCP, TCP_MD5SIG, &opt, sizeof(opt)) == -1) {
- if (errno == ENOPROTOOPT) { /* system w/o md5sig support */
- log_warnx("md5 signatures not available, disabling");
- sysdep.no_md5sig = 1;
- } else
- fatal("setsockopt TCP_MD5SIG");
- }
+ if (bind(la->fd, (struct sockaddr *)&la->sa, la->sa.ss_len) ==
+ -1) {
+ if (errno == EACCES) {
+ log_warnx("can't establish listener on %s",
+ log_sockaddr((struct sockaddr *)&la->sa));
+ close(la->fd);
+ la->fd = -1;
+ return (-1);
+ } else
+ fatal("bind");
+ }
- if (bind(fd, sa, sa->sa_len)) {
- close(fd);
- fatal("bind");
- }
+ session_socket_blockmode(la->fd, BM_NONBLOCK);
- session_socket_blockmode(fd, BM_NONBLOCK);
+ if (listen(la->fd, MAX_BACKLOG)) {
+ close(la->fd);
+ fatal("listen");
+ }
- if (listen(fd, MAX_BACKLOG)) {
- close(fd);
- fatal("listen");
+ log_info("listening on %s",
+ log_sockaddr((struct sockaddr *)&la->sa));
}
- return (fd);
+ return (0);
}
int
@@ -153,7 +186,7 @@ session_main(struct bgpd_config *config, struct peer *cpeers,
struct network_head *net_l, struct filter_head *rules,
struct mrt_head *m_l, int pipe_m2s[2], int pipe_s2r[2])
{
- int nfds, i, j, timeout, idx_peers;
+ int nfds, i, j, timeout, idx_peers, idx_listeners;
pid_t pid;
time_t nextaction;
struct passwd *pw;
@@ -164,6 +197,7 @@ session_main(struct bgpd_config *config, struct peer *cpeers,
struct filter_rule *r;
struct pollfd pfd[OPEN_MAX];
struct ctl_conn *ctl_conn;
+ struct listen_addr *la;
short events;
conf = config;
@@ -193,8 +227,7 @@ session_main(struct bgpd_config *config, struct peer *cpeers,
setproctitle("session engine");
bgpd_process = PROC_SE;
- sock = setup_listener((struct sockaddr *)&conf->listen_addr);
- sock6 = setup_listener((struct sockaddr *)&conf->listen6_addr);
+ setup_listeners();
if (pfkey_init(&sysdep) == -1)
fatalx("pfkey setup failed");
@@ -240,10 +273,6 @@ session_main(struct bgpd_config *config, struct peer *cpeers,
while (session_quit == 0) {
bzero(&pfd, sizeof(pfd));
- pfd[PFD_LISTEN].fd = sock;
- pfd[PFD_LISTEN].events = POLLIN;
- pfd[PFD_LISTEN6].fd = sock6;
- pfd[PFD_LISTEN6].events = POLLIN;
pfd[PFD_PIPE_MAIN].fd = ibuf_main.fd;
pfd[PFD_PIPE_MAIN].events = POLLIN;
if (ibuf_main.w.queued > 0)
@@ -256,7 +285,15 @@ session_main(struct bgpd_config *config, struct peer *cpeers,
pfd[PFD_SOCK_CTL].events = POLLIN;
nextaction = time(NULL) + 240; /* loop every 240s at least */
- i = PFD_PEERS_START;
+ i = PFD_LISTENERS_START;
+
+ TAILQ_FOREACH(la, conf->listen_addrs, entry) {
+ pfd[i].fd = la->fd;
+ pfd[i].events = POLLIN;
+ i++;
+ }
+
+ idx_listeners = i;
last = NULL;
for (p = peers; p != NULL; p = next) {
@@ -353,16 +390,6 @@ session_main(struct bgpd_config *config, struct peer *cpeers,
if (errno != EINTR)
fatal("poll error");
- if (nfds > 0 && pfd[PFD_LISTEN].revents & POLLIN) {
- nfds--;
- session_accept(sock);
- }
-
- if (nfds > 0 && pfd[PFD_LISTEN6].revents & POLLIN) {
- nfds--;
- session_accept(sock6);
- }
-
if (nfds > 0 && pfd[PFD_PIPE_MAIN].revents & POLLOUT)
if (msgbuf_write(&ibuf_main.w) < 0)
fatal("pipe write error");
@@ -386,7 +413,14 @@ session_main(struct bgpd_config *config, struct peer *cpeers,
control_accept(csock);
}
- for (j = PFD_PEERS_START; nfds > 0 && j < idx_peers; j++)
+ for (j = PFD_LISTENERS_START; nfds > 0 && j < idx_listeners;
+ j++)
+ if (pfd[j].revents & POLLIN) {
+ nfds--;
+ session_accept(pfd[j].fd);
+ }
+
+ for (; nfds > 0 && j < idx_peers; j++)
nfds -= session_dispatch_msg(&pfd[j], peer_l[j]);
for (; nfds > 0 && j < i; j++)
@@ -405,6 +439,12 @@ session_main(struct bgpd_config *config, struct peer *cpeers,
free(mrt);
}
+ while ((la = TAILQ_FIRST(conf->listen_addrs)) != NULL) {
+ TAILQ_REMOVE(conf->listen_addrs, la, entry);
+ free(la);
+ }
+ free(conf->listen_addrs);
+
msgbuf_write(&ibuf_rde.w);
msgbuf_clear(&ibuf_rde.w);
msgbuf_write(&ibuf_main.w);
@@ -1914,6 +1954,7 @@ session_dispatch_imsg(struct imsgbuf *ibuf, int idx)
struct mrt_config *mrt;
struct peer_config *pconf;
struct peer *p, *next;
+ struct listen_addr *la, *nla;
u_char *data;
enum reconf_action reconf;
int n;
@@ -1940,6 +1981,10 @@ session_dispatch_imsg(struct imsgbuf *ibuf, int idx)
NULL)
fatal(NULL);
memcpy(nconf, imsg.data, sizeof(struct bgpd_config));
+ if ((nconf->listen_addrs = calloc(1,
+ sizeof(struct listen_addrs))) == NULL)
+ fatal(NULL);
+ TAILQ_INIT(nconf->listen_addrs);
npeers = NULL;
init_conf(nconf);
pending_reconf = 1;
@@ -1963,6 +2008,29 @@ session_dispatch_imsg(struct imsgbuf *ibuf, int idx)
memcpy(&p->conf, pconf, sizeof(struct peer_config));
p->conf.reconf_action = reconf;
break;
+ case IMSG_RECONF_LISTENER:
+ if (idx != PFD_PIPE_MAIN)
+ fatalx("reconf request not from parent");
+ nla = imsg.data;
+ TAILQ_FOREACH(la, conf->listen_addrs, entry) {
+ if (!la_cmp(la, nla))
+ break;
+ }
+
+ if (la == NULL) {
+ la = calloc(1, sizeof(struct listen_addr));
+ if (la == NULL)
+ fatal(NULL);
+ memcpy(&la->sa, &nla->sa, sizeof(la->sa));
+ la->fd = nla->fd;
+ la->flags = nla->flags;
+ la->reconf = RECONF_REINIT;
+ TAILQ_INSERT_TAIL(nconf->listen_addrs, la,
+ entry);
+ } else {
+ la->reconf = RECONF_KEEP;
+ }
+ break;
case IMSG_RECONF_DONE:
if (idx != PFD_PIPE_MAIN)
fatalx("reconf request not from parent");
@@ -1972,17 +2040,50 @@ session_dispatch_imsg(struct imsgbuf *ibuf, int idx)
conf->holdtime = nconf->holdtime;
conf->bgpid = nconf->bgpid;
conf->min_holdtime = nconf->min_holdtime;
+
/* add new peers */
for (p = npeers; p != NULL; p = next) {
next = p->next;
p->next = peers;
peers = p;
}
- /* find peers to be deleted */
+ /* find ones to be deleted */
for (p = peers; p != NULL; p = p->next)
if (p->conf.reconf_action == RECONF_NONE &&
!p->conf.cloned)
p->conf.reconf_action = RECONF_DELETE;
+
+ /* if there are no new listeners, keep default ones */
+ if (TAILQ_EMPTY(nconf->listen_addrs))
+ TAILQ_FOREACH(la, conf->listen_addrs, entry)
+ if (la->flags & DEFAULT_LISTENER)
+ la->reconf = RECONF_KEEP;
+
+ /* delete old listeners */
+ for (la = TAILQ_FIRST(conf->listen_addrs); la != NULL;
+ la = nla) {
+ nla = TAILQ_NEXT(la, entry);
+ if (la->reconf == RECONF_NONE) {
+ log_info("not listening on %s any more",
+ log_sockaddr(
+ (struct sockaddr *)&la->sa));
+ TAILQ_REMOVE(conf->listen_addrs, la,
+ entry);
+ close(la->fd);
+ free(la);
+ }
+ }
+
+ /* add new listeners */
+ while ((la = TAILQ_FIRST(nconf->listen_addrs)) !=
+ NULL) {
+ TAILQ_REMOVE(nconf->listen_addrs, la, entry);
+ TAILQ_INSERT_TAIL(conf->listen_addrs, la,
+ entry);
+ }
+
+ setup_listeners();
+ free(nconf->listen_addrs);
free(nconf);
nconf = NULL;
pending_reconf = 0;
@@ -2068,6 +2169,41 @@ session_dispatch_imsg(struct imsgbuf *ibuf, int idx)
}
}
+int
+la_cmp(struct listen_addr *a, struct listen_addr *b)
+{
+ struct sockaddr_in *in_a, *in_b;
+ struct sockaddr_in6 *in6_a, *in6_b;
+
+ if (a->sa.ss_family != b->sa.ss_family)
+ return (1);
+
+ switch (a->sa.ss_family) {
+ case AF_INET:
+ in_a = (struct sockaddr_in *)&a->sa;
+ in_b = (struct sockaddr_in *)&b->sa;
+ if (in_a->sin_addr.s_addr != in_b->sin_addr.s_addr)
+ return (1);
+ if (in_a->sin_port != in_b->sin_port)
+ return (1);
+ break;
+ case AF_INET6:
+ in6_a = (struct sockaddr_in6 *)&a->sa;
+ in6_b = (struct sockaddr_in6 *)&b->sa;
+ if (bcmp(&in6_a->sin6_addr, &in6_b->sin6_addr,
+ sizeof(struct in6_addr)))
+ return (1);
+ if (in6_a->sin6_port != in6_b->sin6_port)
+ return (1);
+ break;
+ default:
+ fatal("king bula sez: unknown address family");
+ /* not reached */
+ }
+
+ return (0);
+}
+
struct peer *
getpeerbyaddr(struct bgpd_addr *addr)
{