diff options
Diffstat (limited to 'bgpd/bgp_network.c')
-rw-r--r-- | bgpd/bgp_network.c | 345 |
1 files changed, 196 insertions, 149 deletions
diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 856447d7..710dc51e 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -86,10 +86,21 @@ typedef struct bgp_listener* bgp_listener ; static bgp_listener bgp_listeners[] = { [AF_INET] = NULL, +#if HAVE_IPV6 [AF_INET6] = NULL +#endif } ; -CONFIRM((AF_INET < 20) && (AF_INET6 < 20)) ; +CONFIRM(AF_INET < 20) ; /* The bgp_listeners array is not a silly size */ +#if HAVE_IPV6 +CONFIRM(AF_INET6 < 20) ; /* The bgp_listeners array is not a silly size */ +#endif + +#if defined(HAVE_IPV6) && !defined(NRL) +# define BGP_USE_ADDRINFO 1 +#else +# define BGP_USE_ADDRINFO 0 +#endif /* BGP listening socket. */ struct bgp_listener @@ -100,8 +111,11 @@ struct bgp_listener } ; /* Forward reference */ -static int bgp_open_listener_on(const char* address, unsigned short port); -static int bgp_init_listener(int sock_fd, struct sockaddr *sa, socklen_t salen); +static int bgp_open_listeners_addrinfo(const char* address, + unsigned short port) ; +static int bgp_open_listeners_simple(const char* address, unsigned short port) ; +static int bgp_open_listener(sockunion su, unsigned short port, + int sock_type, int sock_protocol) ; /*------------------------------------------------------------------------------ * Open Listeners. @@ -119,7 +133,7 @@ static int bgp_init_listener(int sock_fd, struct sockaddr *sa, socklen_t salen); * "80.177.246.130," -- will list on that address and * any other local address. * - * NB: only listens on AF_INET and (if HAVE_IPV6) AF_INET6. + * NB: only listens on AF_INET and AF_INET6 (if HAVE_IPV6). * * Returns: > 0 => OK -- number of listeners set up * -1 => failed -- no listeners set up @@ -128,39 +142,39 @@ extern int bgp_open_listeners(const char* address, unsigned short port) { int count ; - bool do_null ; + bool done_null ; + char* copy ; + char* next ; - count = 0 ; - do_null = (address == NULL) ; + if (address == NULL) + address = "" ; + + copy = XSTRDUP(MTYPE_TMP, address) ; - if (!do_null) + done_null = false ; + next = copy ; + count = 0 ; + do { - char* copy ; - char* next ; - char* this ; + address = next ; + next = strchr(address, ',') ; - copy = XSTRDUP(MTYPE_TMP, address) ; + if (next != NULL) + *next++ = '\0' ; /* replace ',' and step past */ - next = copy ; - while (next != NULL) + if (*address == '\0') { - this = next ; - next = strchr(this, ',') ; - - if (next != NULL) - *next++ = '\0' ; - - if (*this == '\0') - do_null = true ; /* empty address => do_null */ + if (done_null) + continue ; /* don't do "" more than once */ else - count += bgp_open_listener_on(this, port) ; + done_null = true ; } ; - XFREE(MTYPE_TMP, copy) ; - } ; + count += BGP_USE_ADDRINFO ? bgp_open_listeners_addrinfo(address, port) + : bgp_open_listeners_simple(address, port) ; + } while (next != NULL) ; - if (do_null) - count += bgp_open_listener_on(NULL, port) ; + XFREE(MTYPE_TMP, copy) ; if (count == 0) { @@ -172,22 +186,20 @@ bgp_open_listeners(const char* address, unsigned short port) } ; /*------------------------------------------------------------------------------ - * Open Listeners. - * - * Using given address and port, get all possible addresses and set up a - * listener on each one. + * Open listeners using getaddrinfo() to find the addresses. * - * NB: only listens on AF_INET and (if HAVE_IPV6) AF_INET6. + * Note that this will accept names as well as numeric addresses. * - * Returns: 0 => OK - * -1 => failed -- no listeners set up + * Returns: count of listeners opened successfully. */ static int -bgp_open_listener_on(const char* address, unsigned short port) +bgp_open_listeners_addrinfo(const char* address, unsigned short port) { -#if defined (HAVE_IPV6) && ! defined (NRL) /*----------------------------*/ +#if BGP_USE_ADDRINFO - /* IPv6 supported version of BGP server socket setup. */ +# ifndef HAVE_IPV6 +# error Using getaddrinfo() but HAVE_IPV6 is not defined ?? +# endif struct addrinfo *ainfo; struct addrinfo *ainfo_save; @@ -203,119 +215,92 @@ bgp_open_listener_on(const char* address, unsigned short port) snprintf (port_str, sizeof(port_str), "%d", port); port_str[sizeof (port_str) - 1] = '\0'; + if (*address == '\0') + address = NULL ; + ret = getaddrinfo (address, port_str, &req, &ainfo_save); if (ret != 0) { - zlog_err ("getaddrinfo: %s", gai_strerror (ret)); + zlog_err ("%s: getaddrinfo: %s", __func__, eaitoa(ret, errno, 0).str); return 0 ; } count = 0; for (ainfo = ainfo_save; ainfo; ainfo = ainfo->ai_next) { - int sock_fd; + union sockunion su ; + int err ; - if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6) + if ((ainfo->ai_family != AF_INET) && (ainfo->ai_family != AF_INET6)) continue; - sock_fd = sockunion_socket(ainfo->ai_family, ainfo->ai_socktype, - ainfo->ai_protocol); - if (sock_fd < 0) - { - zlog_err ("socket: %s", errtoa(errno, 0).str); - continue; - } - - ret = bgp_init_listener(sock_fd, ainfo->ai_addr, ainfo->ai_addrlen); - - if (ret == 0) + sockunion_new_sockaddr(&su, ainfo->ai_addr) ; + err = bgp_open_listener(&su, port, + ainfo->ai_socktype, ainfo->ai_protocol) ; + if (err == 0) ++count; - else - close(sock_fd); } freeaddrinfo (ainfo_save); return count ; -} -#else /*----------------------------------------------------*/ - - /* Traditional IPv4 only version. */ - - int sock_fd; - int socklen; - struct sockaddr_in sin; - int ret, en; - - memset (&sin, 0, sizeof (struct sockaddr_in)); - sin.sin_family = AF_INET; - sin.sin_port = htons (port); - socklen = sizeof (struct sockaddr_in); - - if (address && ((ret = inet_aton(address, &sin.sin_addr)) < 1)) - { - zlog_err("bgp_socket: could not parse ip address %s: %s", - address, errtoa(errno, 0).str); - return ret; - } -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sin.sin_len = socklen; -#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - - sock_fd = socket (AF_INET, SOCK_STREAM, 0); - if (sock_fd < 0) - { - zlog_err ("socket: %s", errtoa(errno, 0).str); - return sock_fd; - } - ret = bgp_init_listener (sock_fd, (struct sockaddr *) &sin, socklen); - if (ret < 0) - { - close (sock_fd); - return ret; - } - return sock_fd; +#else + zabort("bgp_open_listeners_addrinfo not implemented") ; +#endif /* BGP_USE_ADDRINFO */ } -#endif /* HAVE_IPV6 && !NRL --------------------------------------------------*/ - /*------------------------------------------------------------------------------ - * Close Listeners. + * Open listener the old fashioned way. * - * Empty the listener lists, close files, remove from the selection. + * NB: if address is "" tries IPv4 and IPv6 (if supported). * + * NB: if address is not NULL, must be a numeric IP address (which may be IPv6 + * if that is supported). + * + * Returns: count of listeners opened successfully. */ -static void bgp_reset_listeners(bgp_listener* p_listener) ; - -extern void -bgp_close_listeners(void) -{ - bgp_reset_listeners(&bgp_listeners[AF_INET]) ; - bgp_reset_listeners(&bgp_listeners[AF_INET6]) ; -} ; - -static void -bgp_reset_listeners(bgp_listener* p_listener) +static int +bgp_open_listeners_simple(const char* address, unsigned short port) { - bgp_listener listener ; - bgp_listener next ; - - next = *p_listener ; - *p_listener = NULL ; + union sockunion su ; + int err ; + int count ; - while (next != NULL) + /* If address is not null, must be a single, specific, numeric address */ + if (*address != '\0') { - listener = next ; - next = listener->next ; + int ret = str2sockunion (address, &su) ; + if (ret < 0) + { + zlog_err("bgp_socket: could not parse ip address %s: %s", + address, errtoa(errno, 0).str); + return 0 ; + } - close(qps_file_fd(&listener->qf)) ; - qps_remove_file(&listener->qf) ; + err = bgp_open_listener(&su, port, SOCK_STREAM, 0) ; - XFREE(MTYPE_BGP_LISTENER, listener) ; + return (err == 0) ? 1 : 0 ; } ; + + /* Null address, try <any> for IPv4 and (if supported) IPv6 */ + count = 0 ; + + sockunion_init_new(&su, AF_INET) ; + err = bgp_open_listener(&su, port, SOCK_STREAM, 0) ; + if (err == 0) + ++count ; + +#ifdef HAVE_IPV6 + sockunion_init_new(&su, AF_INET6) ; + err = bgp_open_listener(&su, port, SOCK_STREAM, 0) ; + if (err == 0) + ++count ; +#endif + + return count ; } ; /*------------------------------------------------------------------------------ - * Initialise Listener Socket + * Open Listener Socket * * Sets up socket with the usual options. Binds to given address and listens. * @@ -328,50 +313,77 @@ bgp_reset_listeners(bgp_listener* p_listener) * != 0 : error number (from errno or otherwise) */ static int -bgp_init_listener(int sock_fd, struct sockaddr *sa, socklen_t salen) +bgp_open_listener(sockunion su, unsigned short port, + int sock_type, int sock_protocol) { bgp_listener listener ; int ret, err ; + int slen ; + int sock_fd ; - err = bgp_socket_set_common_options(sock_fd, (union sockunion*)sa, 0, NULL) ; - if (err != 0) - return err ; + /* Construct socket and set the common options. */ + sock_fd = socket (sockunion_family(su), sock_type, sock_protocol) ; + if (sock_fd < 0) + { + err = errno ; + zlog_err ("%s: could not open socket for family %d: %s", __func__, + sockunion_family(su), errtoa(err, 0).str) ; + return errno = err ; + } + + err = bgp_socket_set_common_options(sock_fd, su, 0, NULL) ; -#if defined(HAVE_IPV6) && defined(IPV6_V6ONLY) /* Want only IPV6 on ipv6 socket (not mapped addresses) * * This distinguishes 0.0.0.0 from :: -- without this, bind() will reject the * attempt to bind to :: after binding to 0.0.0.0. + * + * Also, for all the apparent utility of IPv4-mapped addresses, the semantics + * are simpler if IPv6 sockets speak IPv6 and IPv4 sockets speak IPv4. */ - if (sa->sa_family == AF_INET6) - { - int on = 1; - ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); - if (ret < 0) - return errno ; - } -#endif - - if (bgpd_privs.change(ZPRIVS_RAISE)) +#if defined(HAVE_IPV6) && defined(IPV6_V6ONLY) + if ((err == 0) && (sockunion_family(su) == AF_INET6)) { - err = errno ; - zlog_err("%s: could not raise privs: %s", __func__, errtoa(errno, 0).str); + int on = 1; + ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); + if (ret < 0) + { + err = errno ; + zlog_err("%s: could not set IPV6_V6ONLY: %s", __func__, + errtoa(err, 0).str) ; + } } ; +#endif - ret = bind(sock_fd, sa, salen) ; - if (ret < 0) + /* Bind to port and address (if any) */ + if (err == 0) { - err = errno ; - zlog_err ("%s: bind: %s", __func__, errtoa(err, 0).str); - } ; + if (bgpd_privs.change(ZPRIVS_RAISE)) + { + err = errno ; + zlog_err("%s: could not raise privs: %s", __func__, + errtoa(errno, 0).str) ; + } ; - if (bgpd_privs.change(ZPRIVS_LOWER)) - { - if (err == 0) - err = errno ; - zlog_err("%s: could not lower privs: %s", __func__, errtoa(errno, 0).str); + slen = sockunion_set_port(su, port) ; + + ret = bind(sock_fd, &su->sa, slen) ; + if (ret < 0) + { + err = errno ; + zlog_err ("%s: bind: %s", __func__, errtoa(err, 0).str); + } ; + + if (bgpd_privs.change(ZPRIVS_LOWER)) + { + if (err == 0) + err = errno ; + zlog_err("%s: could not lower privs: %s", __func__, + errtoa(errno, 0).str) ; + } ; } ; + /* Last lap... listen() */ if (err == 0) { ret = listen (sock_fd, 43); @@ -392,22 +404,57 @@ bgp_init_listener(int sock_fd, struct sockaddr *sa, socklen_t salen) * again, add it to the BGP Engine Nexus file selection and enable it for * reading. */ - listener = XCALLOC(MTYPE_BGP_LISTENER, sizeof(struct bgp_listener)) ; qps_file_init_new(&listener->qf, NULL) ; qps_add_file(bgp_nexus->selection, &listener->qf, sock_fd, listener) ; qps_enable_mode(&listener->qf, qps_read_mnum, bgp_accept_action) ; - memcpy(&listener->su, sa, salen) ; + sockunion_copy(&listener->su, su) ; - listener->next = bgp_listeners[sa->sa_family] ; - bgp_listeners[sa->sa_family] = listener ; + listener->next = bgp_listeners[sockunion_family(su)] ; + bgp_listeners[sockunion_family(su)] = listener ; return 0 ; } ; /*------------------------------------------------------------------------------ + * Close Listeners. + * + * Empty the listener lists, close files, remove from the selection. + * + */ +static void bgp_reset_listeners(bgp_listener* p_listener) ; + +extern void +bgp_close_listeners(void) +{ + bgp_reset_listeners(&bgp_listeners[AF_INET]) ; + bgp_reset_listeners(&bgp_listeners[AF_INET6]) ; +} ; + +static void +bgp_reset_listeners(bgp_listener* p_listener) +{ + bgp_listener listener ; + bgp_listener next ; + + next = *p_listener ; + *p_listener = NULL ; + + while (next != NULL) + { + listener = next ; + next = listener->next ; + + close(qps_file_fd(&listener->qf)) ; + qps_remove_file(&listener->qf) ; + + XFREE(MTYPE_BGP_LISTENER, listener) ; + } ; +} ; + +/*------------------------------------------------------------------------------ * Prepare to accept() connection * * If the session has a password, then this is where the listener(s) for the |