[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

BGPD and its listening sockets



currently, bgpd has two listening sockets, one for IPv4 and one for 
IPv6. There is no way to have it listen on only one address family, or 
on more than one address for address family.
if you have
  listen on 127.0.0.1
in your config file, it will listen in 127.0.0.1:179 and [::1]:179
if you have
  listen 10.0.0.1
  listen 10.0.0.2
  listen 10.0.0.3
it will listen on 10.0.0.3 and ::1
now, of course this is a bit fucked and needs fixing; and I finally got 
around to do it.

use a TAILQ for the listeners, pt the sockaddr and fd and such in a 
little struct. works beautifully for my limited testing.

now, by default, we listen on wildcard sockets for both v4 and v6 - 
like before. if you specify
  listen on 127.0.0.1
in your config, we will listen on 127.0.0.1, and that's it. No v6. If 
you have 4 listen statemenst we'll have 4 listening sockets... and so 
on.

reconfig is a major issue. we cannot bind to port 179 after we dropped 
privs. I have not fixed that yet; the solution is obviosuly to do the 
bind() in the parent process and pass the fd to the SE, which then does 
the listen() and continues like always. I hope to do that soonish.
But even without that this diff fixes a lot of issues around the 
listeners and is an improvement over what we have now. pls test & 
comment...

Index: bgpd.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.c,v
retrieving revision 1.94
diff -u -r1.94 bgpd.c
--- bgpd.c	21 May 2004 11:48:56 -0000	1.94
+++ bgpd.c	4 Jun 2004 18:08:03 -0000
@@ -100,6 +100,7 @@
 	struct network		*net;
 	struct filter_rule	*r;
 	struct mrt		*(mrt[POLL_MAX]), *m;
+	struct listen_addr	*la;
 	struct pollfd		 pfd[POLL_MAX];
 	pid_t			 io_pid = 0, rde_pid = 0, pid;
 	char			*conffile;
@@ -228,6 +229,11 @@
 		free(r);
 	}
 
+	while ((la = TAILQ_FIRST(conf.listen_addrs)) != NULL) {
+		TAILQ_REMOVE(conf.listen_addrs, la, entry);
+		free(la);
+	}
+
 	while (quit == 0) {
 		pfd[PFD_PIPE_SESSION].fd = ibuf_se.fd;
 		pfd[PFD_PIPE_SESSION].events = POLLIN;
@@ -368,6 +374,7 @@
 	struct network		*n;
 	struct peer		*p;
 	struct filter_rule	*r;
+	struct listen_addr	*la;
 
 	if (parse_config(conffile, conf, mrt_l, peer_l, &net_l, rules_l)) {
 		log_warnx("config file %s has errors, not reloading",
@@ -399,6 +406,16 @@
 		TAILQ_REMOVE(rules_l, r, entries);
 		free(r);
 	}
+	while ((la = TAILQ_FIRST(conf->listen_addrs)) != NULL) {
+		if (imsg_compose(&ibuf_se, IMSG_RECONF_LISTENER, 0,
+		    la, sizeof(struct listen_addr)) == -1)
+			return (-1);
+		TAILQ_REMOVE(conf->listen_addrs, la, entry);
+		free(la);
+	}
+	free(conf->listen_addrs);
+	conf->listen_addrs = NULL;
+
 	if (imsg_compose(&ibuf_se, IMSG_RECONF_DONE, 0, NULL, 0) == -1 ||
 	    imsg_compose(&ibuf_rde, IMSG_RECONF_DONE, 0, NULL, 0) == -1)
 		return (-1);
Index: bgpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v
retrieving revision 1.125
diff -u -r1.125 bgpd.h
--- bgpd.h	21 May 2004 15:36:40 -0000	1.125
+++ bgpd.h	4 Jun 2004 18:08:04 -0000
@@ -108,17 +108,28 @@
 #define addr32	ba.addr32
 };
 
+#define DEFAULT_LISTENER	0x01
+
+struct listen_addr {
+	TAILQ_ENTRY(listen_addr)	 entry;
+	struct sockaddr_storage		 sa;
+	int				 fd;
+	enum reconf_action		 reconf;
+	u_int8_t			 flags;
+};
+
+TAILQ_HEAD(listen_addrs, listen_addr);
+
 struct bgpd_config {
-	int			 opts;
-	u_int16_t		 as;
-	u_int32_t		 bgpid;
-	u_int32_t		 clusterid;
-	u_int16_t		 holdtime;
-	u_int16_t		 min_holdtime;
-	int			 flags;
-	int			 log;
-	struct sockaddr_in	 listen_addr;
-	struct sockaddr_in6	 listen6_addr;
+	int					 opts;
+	u_int16_t				 as;
+	u_int32_t				 bgpid;
+	u_int32_t				 clusterid;
+	u_int16_t				 holdtime;
+	u_int16_t				 min_holdtime;
+	int					 flags;
+	int					 log;
+	struct listen_addrs			*listen_addrs;
 };
 
 struct buf_read {
@@ -239,6 +250,7 @@
 	IMSG_RECONF_CONF,
 	IMSG_RECONF_PEER,
 	IMSG_RECONF_FILTER,
+	IMSG_RECONF_LISTENER,
 	IMSG_RECONF_DONE,
 	IMSG_UPDATE,
 	IMSG_UPDATE_ERR,
Index: config.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/config.c,v
retrieving revision 1.37
diff -u -r1.37 config.c
--- config.c	21 May 2004 15:36:40 -0000	1.37
+++ config.c	4 Jun 2004 18:08:04 -0000
@@ -37,10 +37,11 @@
 
 int
 merge_config(struct bgpd_config *xconf, struct bgpd_config *conf,
-    struct peer *peer_l)
+    struct peer *peer_l, struct listen_addrs *listen_addrs)
 {
-	struct peer	*p;
-	int		 errs = 0;
+	struct peer				*p;
+	struct listen_addr			*la;
+	int					 errs = 0;
 
 	/* preserve cmd line opts */
 	conf->opts = xconf->opts;
@@ -75,7 +76,17 @@
 		}
 	}
 
+	if (xconf->listen_addrs != NULL) {
+		while ((la = TAILQ_FIRST(xconf->listen_addrs)) != NULL) {
+			TAILQ_REMOVE(xconf->listen_addrs, la, entry);
+			free(la);
+		}
+		free(xconf->listen_addrs);
+	}
+
 	memcpy(xconf, conf, sizeof(struct bgpd_config));
+
+	xconf->listen_addrs = listen_addrs;
 
 	return (errs);
 }
Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v
retrieving revision 1.112
diff -u -r1.112 parse.y
--- parse.y	21 May 2004 15:36:40 -0000	1.112
+++ parse.y	4 Jun 2004 18:08:04 -0000
@@ -45,6 +45,7 @@
 static struct peer		*curpeer;
 static struct peer		*curgroup;
 static struct filter_head	*filter_l;
+static struct listen_addrs	*listen_addrs;
 static FILE			*fin = NULL;
 static int			 lineno = 1;
 static int			 errors = 0;
@@ -226,20 +227,37 @@
 			conf->min_holdtime = $3;
 		}
 		| LISTEN ON address	{
+			struct listen_addr	*la;
+			struct sockaddr_in	*in;
+			struct sockaddr_in6	*in6;
+
+			if ((la = calloc(1, sizeof(struct listen_addr))) ==
+			    NULL)
+				fatal("parse conf_main listen on calloc");
+
+			la->fd = -1;
+			la->sa.ss_family = $3.af;
 			switch ($3.af) {
 			case AF_INET:
-				conf->listen_addr.sin_addr.s_addr =
-				    $3.v4.s_addr;
+				la->sa.ss_len = sizeof(struct sockaddr_in);
+				in = (struct sockaddr_in *)&la->sa;
+				in->sin_addr.s_addr = $3.v4.s_addr;
+				in->sin_port = htons(BGP_PORT);
 				break;
 			case AF_INET6:
-				memcpy(&conf->listen6_addr.sin6_addr, &$3.v6,
-				    sizeof(conf->listen6_addr.sin6_addr));
+				la->sa.ss_len = sizeof(struct sockaddr_in6);
+				in6 = (struct sockaddr_in6 *)&la->sa;
+				memcpy(&in6->sin6_addr, &$3.v6,
+				    sizeof(in6->sin6_addr));
+				in6->sin6_port = htons(BGP_PORT);
 				break;
 			default:
 				yyerror("king bula does not like family %u",
 				    $3.af);
 				YYERROR;
 			}
+
+			TAILQ_INSERT_TAIL(listen_addrs, la, entry);
 		}
 		| FIBUPDATE yesno		{
 			if ($2 == 0)
@@ -1279,9 +1297,12 @@
 		fatal(NULL);
 	if ((mrtconf = calloc(1, sizeof(struct mrt_head))) == NULL)
 		fatal(NULL);
+	if ((listen_addrs = calloc(1, sizeof(struct listen_addrs))) == NULL)
+		fatal(NULL);
 	LIST_INIT(mrtconf);
 	netconf = nc;
 	TAILQ_INIT(netconf);
+	TAILQ_INIT(listen_addrs);
 
 	peer_l = NULL;
 	peer_l_old = *xpeers;
@@ -1293,16 +1314,6 @@
 	filter_l = xfilter_l;
 	TAILQ_INIT(filter_l);
 
-	conf->listen_addr.sin_len = sizeof(conf->listen_addr);
-	conf->listen_addr.sin_family = AF_INET;
-	conf->listen_addr.sin_addr.s_addr = INADDR_ANY;
-	conf->listen_addr.sin_port = htons(BGP_PORT);
-
-	bzero(&conf->listen6_addr, sizeof(conf->listen6_addr));
-	conf->listen6_addr.sin6_len = sizeof(conf->listen6_addr);
-	conf->listen6_addr.sin6_family = AF_INET6;
-	conf->listen6_addr.sin6_port = htons(BGP_PORT);
-
 	if ((fin = fopen(filename, "r")) == NULL) {
 		warn("%s", filename);
 		free(conf);
@@ -1336,7 +1347,7 @@
 		}
 	}
 
-	errors += merge_config(xconf, conf, peer_l);
+	errors += merge_config(xconf, conf, peer_l, listen_addrs);
 	errors += mrt_mergeconfig(xmconf, mrtconf);
 	*xpeers = peer_l;
 
Index: printconf.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/printconf.c,v
retrieving revision 1.20
diff -u -r1.20 printconf.c
--- printconf.c	8 May 2004 19:17:20 -0000	1.20
+++ printconf.c	4 Jun 2004 18:08:04 -0000
@@ -88,8 +88,8 @@
 void
 print_mainconf(struct bgpd_config *conf)
 {
-	struct in_addr	ina;
-	struct in6_addr	ina6;
+	struct in_addr		 ina;
+	struct listen_addr	*la;
 
 	printf("AS %u\n", conf->as);
 	ina.s_addr = conf->bgpid;
@@ -110,13 +110,9 @@
 	if (conf->log & BGPD_LOG_UPDATES)
 		printf("log updates\n");
 
-	if (conf->listen_addr.sin_addr.s_addr != INADDR_ANY)
-		printf("listen on %s\n", inet_ntoa(conf->listen_addr.sin_addr));
-
-	bzero(&ina6, sizeof(ina6));
-	if (bcmp(&ina6, &conf->listen6_addr.sin6_addr, sizeof(ina6)))
+	TAILQ_FOREACH(la, conf->listen_addrs, entry)
 		printf("listen on %s\n",
-		    log_sockaddr((struct sockaddr *)&conf->listen6_addr));
+		    log_sockaddr((struct sockaddr *)&la->sa));
 }
 
 void
Index: rde.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
retrieving revision 1.115
diff -u -r1.115 rde.c
--- rde.c	21 May 2004 15:36:40 -0000	1.115
+++ rde.c	4 Jun 2004 18:08:05 -0000
@@ -104,11 +104,12 @@
     struct filter_head *rules, struct mrt_head *mrt_l,
     int pipe_m2r[2], int pipe_s2r[2])
 {
-	pid_t		 pid;
-	struct passwd	*pw;
-	struct mrt	*m;
-	struct pollfd	 pfd[2];
-	int		 nfds;
+	pid_t			 pid;
+	struct passwd		*pw;
+	struct mrt		*m;
+	struct listen_addr	*la;
+	struct pollfd		 pfd[2];
+	int			 nfds;
 
 	switch (pid = fork()) {
 	case -1:
@@ -150,11 +151,16 @@
 	imsg_init(&ibuf_se, pipe_s2r[1]);
 	imsg_init(&ibuf_main, pipe_m2r[1]);
 
-	/* main mrt list is not used in the SE */
+	/* main mrt list and listener list are not used in the SE */
 	while ((m = LIST_FIRST(mrt_l)) != NULL) {
 		LIST_REMOVE(m, list);
 		free(m);
 	}
+	while ((la = TAILQ_FIRST(config->listen_addrs)) != NULL) {
+		TAILQ_REMOVE(config->listen_addrs, la, entry);
+		free(la);
+	}
+	free(config->listen_addrs);
 
 	pt_init();
 	path_init(pathhashsize);
Index: session.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/session.c,v
retrieving revision 1.172
diff -u -r1.172 session.c
--- session.c	28 May 2004 18:39:09 -0000	1.172
+++ session.c	4 Jun 2004 18:08:06 -0000
@@ -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_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 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 @@
 }
 
 int
-setup_listener(struct sockaddr *sa)
+setup_listeners(void)
 {
-	int	fd, opt;
-
-	if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6)
-		fatal("king bula sez: unknown address family");
+	int			 opt;
+	struct listen_addr	*la;
 
-	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);
+	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);
 	}
 
-	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");
-	}
+	TAILQ_FOREACH(la, conf->listen_addrs, entry) {
+		la->reconf = RECONF_NONE;
 
-	if (bind(fd, sa, sa->sa_len)) {
-		close(fd);
-		fatal("bind");
-	}
+		if (la->fd != -1)
+			continue;
+
+		if ((la->fd = socket(la->sa.ss_family, SOCK_STREAM,
+		    IPPROTO_TCP)) == -1)
+			fatal("socket");
+
+		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");
+		}
+
+		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");
+		}
+
+		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 @@
     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 @@
 	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 @@
 	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 @@
 
 	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 @@
 		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 @@
 			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 @@
 			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 @@
 		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 @@
 	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 @@
 			    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 @@
 			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 @@
 			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;
@@ -2066,6 +2167,41 @@
 		}
 		imsg_free(&imsg);
 	}
+}
+
+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 *
Index: session.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/session.h,v
retrieving revision 1.53
diff -u -r1.53 session.h
--- session.h	28 May 2004 18:39:09 -0000	1.53
+++ session.h	4 Jun 2004 18:08:06 -0000
@@ -207,7 +207,7 @@
 
 /* config.c */
 int	 merge_config(struct bgpd_config *, struct bgpd_config *,
-	    struct peer *);
+	    struct peer *, struct listen_addrs *);
 
 /* rde.c */
 int	 rde_main(struct bgpd_config *, struct network_head *,