/* TCP connection open. Next we send open message to remote peer. And add read thread for reading open message. */ static int bgp_connect_success (struct peer *peer) { char buf1[BUFSIZ]; if (peer->fd < 0) { zlog_err ("bgp_connect_success peer's fd is negative value %d", peer->fd); return -1; } BGP_READ_ON (peer->t_read, bgp_read, peer->fd); if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) bgp_getsockname (peer); if (BGP_DEBUG (normal, NORMAL)) { if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) zlog_debug ("%s open active, local address %s", peer->host, sockunion2str (peer->su_local, buf1, SU_ADDRSTRLEN)); else zlog_debug ("%s passive open", peer->host); } if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) bgp_open_send (peer); return 0; }
/* This function is the first starting point of all BGP connection. It try to connect to remote peer with non-blocking IO. */ int bgp_start (struct peer *peer) { int status; /* If the peer is passive mode, force to move to Active mode. */ if (CHECK_FLAG (peer->flags, PEER_FLAG_PASSIVE)) { BGP_EVENT_ADD (peer, TCP_connection_open_failed); return 0; } status = bgp_connect (peer); switch (status) { case connect_error: if (BGP_DEBUG (fsm, FSM)) plog_info (peer->log, "%s [FSM] Connect error", peer->host); BGP_EVENT_ADD (peer, TCP_connection_open_failed); break; case connect_success: if (BGP_DEBUG (fsm, FSM)) plog_info (peer->log, "%s [FSM] Connect immediately success", peer->host); BGP_EVENT_ADD (peer, TCP_connection_open); break; case connect_in_progress: /* To check nonblocking connect, we wait until socket is readable or writable. */ if (BGP_DEBUG (fsm, FSM)) plog_info (peer->log, "%s [FSM] Non blocking connect waiting result", peer->host); if (peer->fd < 0) { zlog_err ("bgp_start peer's fd is negative value %d", peer->fd); return -1; } BGP_READ_ON (peer->t_read, bgp_read, peer->fd); BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); break; } return 0; }
/* TCP connection open. Next we send open message to remote peer. And add read thread for reading open message. */ int bgp_connect_success (struct peer *peer) { if (peer->fd < 0) { zlog_err ("bgp_connect_success peer's fd is negative value %d", peer->fd); return -1; } BGP_READ_ON (peer->t_read, bgp_read, peer->fd); /* bgp_getsockname (peer); */ if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) bgp_open_send (peer); return 0; }
/* Add thread to Listen Thread List */ tmp_lnode = XCALLOC (MTYPE_TMP, sizeof (struct bgp_listen_sock_lnode)); if (! tmp_lnode) { zlog_err (&BLG, "[NETWORK] Server Sock:" " Cannot allocate memory (%d) @ %s:%d", sizeof (struct bgp_peer_inconn_req), __FILE__, __LINE__); SSOCK_FD_CLOSE (&BLG, bgp_sock); BGP_READ_OFF (&BLG, t_accept); continue; } tmp_lnode->listen_sock = bgp_sock; tmp_lnode->t_accept = t_accept; if (bgp->listen_sock_lnode) tmp_lnode->next = bgp->listen_sock_lnode; bgp->listen_sock_lnode = tmp_lnode; } pal_sock_freeaddrinfo (ainfo_head); return ret; } #else /* HAVE_IPV6 && !NRL */ s_int32_t bpn_sock_listen (struct bgp *bgp, u_int16_t port) { struct bgp_listen_sock_lnode *tmp_lnode; struct thread *t_accept; union sockunion su; s_int32_t bgp_sock; fib_id_t fib_id; s_int32_t ret; pal_mem_set (&su, 0, sizeof (union sockunion)); t_accept = NULL; ret = 0; if (! bgp) { zlog_err (&BLG, "[NETWORK] Server Sock: Invalid 'bgp' instance"); ret = -1; goto EXIT; } fib_id = LIB_VRF_GET_FIB_ID (bgp->owning_ivrf); /* Specify address family. */ su.sa.sa_family = AF_INET; bgp_sock = pal_sock (&BLG, su.sa.sa_family, SOCK_STREAM, 0); if (bgp_sock < 0) { zlog_err (&BLG, "[NETWORK] Server Sock: socket() Failed, FIB-ID %d, " "Err:%d-%s", fib_id, errno, pal_strerror (errno)); ret = -1; goto EXIT; } pal_sock_set_reuseaddr (bgp_sock, PAL_TRUE); pal_sock_set_reuseport (bgp_sock, PAL_TRUE); /* Bind socket to FIB. */ ret = pal_sock_set_bindtofib (bgp_sock, fib_id); if (ret < 0) { zlog_err (&BLG, "[NETWORK] Server Sock: bindtofib() Failed, Sock %d" ", FIB-ID %d, Err:%d-%s", bgp_sock, fib_id, errno, pal_strerror (errno)); SSOCK_FD_CLOSE (&BLG, bgp_sock); /* Ignore platform error */ ret = 0; goto EXIT; } ret = sockunion_bind (&BLG, bgp_sock, &su, port, NULL); if (ret < 0) { zlog_err (&BLG, "[NETWORK] Server Sock: bind() Failed, Err: %d-%s", errno, pal_strerror (errno)); SSOCK_FD_CLOSE (&BLG, bgp_sock); /* Ignore platform error */ ret = 0; goto EXIT; } #ifdef HAVE_TCP_MD5SIG bgp_md5_set_server (bgp, bgp_sock); #endif /* TCP_MD5SIG */ ret = pal_sock_listen (bgp_sock, BGP_SOCK_LISTEN_BACKLOG); if (ret < 0) { zlog_err (&BLG, "[NETWORK] Server Sock: listen() Failed, Sock %d, " "FIB-ID %d, Err:%d-%s", bgp_sock, fib_id, errno, pal_strerror (errno)); SSOCK_FD_CLOSE (&BLG, bgp_sock); /* Ignore platform error */ ret = 0; goto EXIT; } /* Start a fresh Accept Thread */ BGP_READ_ON (&BLG, t_accept, bgp, bpn_sock_accept, bgp_sock); /* Add thread to Listen Thread List */ tmp_lnode = XCALLOC (MTYPE_TMP, sizeof (struct bgp_listen_sock_lnode)); if (! tmp_lnode) { zlog_err (&BLG, "[NETWORK] Server Sock:" " Cannot allocate memory (%d) @ %s:%d", sizeof (struct bgp_peer_inconn_req), __FILE__, __LINE__); SSOCK_FD_CLOSE (&BLG, bgp_sock); BGP_READ_OFF (&BLG, t_accept); ret = -1; goto EXIT; } tmp_lnode->listen_sock = bgp_sock; tmp_lnode->t_accept = t_accept; if (bgp->listen_sock_lnode) tmp_lnode->next = bgp->listen_sock_lnode; bgp->listen_sock_lnode = tmp_lnode; EXIT: return ret; }
s_int32_t bpn_sock_listen (struct bgp *bgp, u_int16_t port) { struct bgp_listen_sock_lnode *tmp_lnode; u_int8_t port_str [SU_ADDRSTRLEN]; struct pal_addrinfo *ainfo_save[2], *ainfo_head; struct pal_addrinfo *ainfo; struct pal_sockaddr_in4 ain4; struct pal_sockaddr_in6 ain6; struct pal_addrinfo req; struct thread *t_accept; s_int32_t bgp_sock; u_int8_t addr_set; fib_id_t fib_id; s_int32_t ret; int i, flags = 1; pal_mem_set (&req, 0, sizeof (struct pal_addrinfo)); t_accept = NULL; ainfo_save[0] = NULL; ainfo_save[1] = NULL; ret = 0; addr_set = 0; if (! bgp) { zlog_err (&BLG, "[NETWORK] Server Sock: Invalid 'bgp' instance"); return -1; } fib_id = LIB_VRF_GET_FIB_ID (bgp->owning_ivrf); req.ai_flags = AI_PASSIVE; req.ai_family = AF_UNSPEC; req.ai_socktype = SOCK_STREAM; pal_snprintf (port_str, SU_ADDRSTRLEN, "%d", port); port_str[sizeof (port_str) - 1] = '\0'; ret = pal_sock_getaddrinfo (NULL, port_str, &req, &ainfo); if (ret != 0) { zlog_err (&BLG, "[NETWORK] Server Sock: getaddrinfo() failed: %d-%s", errno, pal_strerror (errno)); return ret; } ainfo_head = ainfo; /* IPv4 can connect to IPv6 socket, not other way around. */ while (ainfo) { if (ainfo->ai_family == AF_INET) ainfo_save[1] = ainfo; else if (ainfo->ai_family == AF_INET6) ainfo_save[0] = ainfo; ainfo = ainfo->ai_next; } for (i = 0; i < 2; i++) { ainfo = ainfo_save[i]; if (! ainfo) continue; if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6) continue; bgp_sock = pal_sock (&BLG, ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol); if (bgp_sock < 0) { zlog_err (&BLG, "[NETWORK] Server Sock: socket() Failed for AF" "=%d, FIB-ID %d, Err:%d-%s", ainfo->ai_family, fib_id, errno, pal_strerror (errno)); continue; } /* set socket as ipv6 only */ if (ainfo->ai_family == AF_INET6) { ret = setsockopt(bgp_sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &flags, sizeof(flags)); if (ret < 0) { zlog_err (&BLG, "[NETWORK] Server Sock: socket() failed to" "set option for AF=%d, Err:%d-%s", ainfo->ai_family, errno, pal_strerror (errno)); } } ret = pal_sock_set_reuseaddr (bgp_sock, PAL_TRUE); pal_sock_set_reuseport (bgp_sock, PAL_TRUE); /* Bind socket to FIB. */ ret = pal_sock_set_bindtofib (bgp_sock, fib_id); if (ret < 0) { zlog_err (&BLG, "[NETWORK] Server Sock: bindtofib() Failed for" " AF=%d, Sock %d, FIB-ID %d, Err:%d-%s", ainfo->ai_family, bgp_sock, fib_id, errno, pal_strerror (errno)); SSOCK_FD_CLOSE (&BLG, bgp_sock); ret = 0; continue; } /* If the ai_addr is NULL, bind it to the port. */ if (ainfo->ai_addr == NULL) { if (ainfo->ai_family == AF_INET) { pal_mem_set (&ain4, 0, sizeof (ain4)); ain4.sin_family = AF_INET; ain4.sin_port = pal_hton16 (port); ainfo->ai_addr = (struct pal_sockaddr *) &ain4; ainfo->ai_addrlen = sizeof (struct pal_sockaddr_in4); addr_set = 1; } else if (ainfo->ai_family == AF_INET6) { pal_mem_set (&ain6, 0, sizeof (ain6)); ain6.sin6_family = AF_INET6; ain6.sin6_port = pal_hton16 (port); ainfo->ai_addr = (struct pal_sockaddr *) &ain6; ainfo->ai_addrlen = sizeof (struct pal_sockaddr_in6); addr_set = 1; } else { zlog_err (&BLG, "[NETWORK] Server Sock: getaddrinfo() returned" "invalid address" " AF=%d, Sock %d", ainfo->ai_family, bgp_sock); SSOCK_FD_CLOSE (&BLG, bgp_sock); ret = 0; continue; } } ret = pal_sock_bind (bgp_sock, ainfo->ai_addr, ainfo->ai_addrlen); if (addr_set) ainfo->ai_addr = NULL; if (ret < 0) { zlog_err (&BLG, "[NETWORK] Server Sock: bind() Failed for AF=" "%d, Err: %d-%s, port[%d]", ainfo->ai_family, errno, pal_strerror (errno), port); SSOCK_FD_CLOSE (&BLG, bgp_sock); ret = 0; continue; } #ifdef HAVE_TCP_MD5SIG bgp_md5_set_server (bgp, bgp_sock); #endif /* TCP_MD5SIG */ ret = pal_sock_listen (bgp_sock, BGP_SOCK_LISTEN_BACKLOG); if (ret < 0) { zlog_err (&BLG, "[NETWORK] Server Sock: listen() Failed for " "AF=%d, Sock %d, FIB-ID %d, Err:%d-%s", ainfo->ai_family, bgp_sock, fib_id, errno, pal_strerror (errno)); SSOCK_FD_CLOSE (&BLG, bgp_sock); ret = 0; continue; } /* Start a fresh Accept Thread */ t_accept = NULL; BGP_READ_ON (&BLG, t_accept, bgp, bpn_sock_accept, bgp_sock); /* Add thread to Listen Thread List */ tmp_lnode = XCALLOC (MTYPE_TMP, sizeof (struct bgp_listen_sock_lnode)); if (! tmp_lnode) { zlog_err (&BLG, "[NETWORK] Server Sock:" " Cannot allocate memory (%d) @ %s:%d", sizeof (struct bgp_peer_inconn_req), __FILE__, __LINE__); SSOCK_FD_CLOSE (&BLG, bgp_sock); BGP_READ_OFF (&BLG, t_accept); continue; } tmp_lnode->listen_sock = bgp_sock; tmp_lnode->t_accept = t_accept; if (bgp->listen_sock_lnode) tmp_lnode->next = bgp->listen_sock_lnode; bgp->listen_sock_lnode = tmp_lnode; } pal_sock_freeaddrinfo (ainfo_head); return ret; }
/* BGP Peer Incoming Connection Accept thread handler */ s_int32_t bpn_sock_accept (struct thread *t_accept) { struct bgp_listen_sock_lnode *tmp_lnode; struct bgp_peer_inconn_req *peer_icr; u_int8_t su_buf [SU_ADDRSTRLEN]; pal_sock_handle_t accept_sock; pal_sock_handle_t bgp_sock; struct lib_globals *blg; struct bgp_peer *peer; union sockunion su; struct bgp *bgp; s_int32_t ret; bgp_sock = THREAD_FD (t_accept); blg = THREAD_GLOB (t_accept); bgp = THREAD_ARG (t_accept); ret = 0; /* Sanity check thread variables */ if (! blg || &BLG != blg) { ret = -1; goto EXIT; } if (! bgp) { zlog_err (&BLG, "[NETWORK] Accept Thread: Invalid Vital Vars, " "blg(%p) bgp(%p)", blg, bgp); ret = -1; goto EXIT; } /* Verify integrity of thread variables */ for (tmp_lnode = bgp->listen_sock_lnode; tmp_lnode; tmp_lnode = tmp_lnode->next) { if (tmp_lnode->listen_sock == bgp_sock) break; } if (! tmp_lnode) { zlog_err (&BLG, "[NETWORK] Accept Thread: Mismatch in thread args" "blg(%p) bgp(%p)", blg, bgp); ret = -1; goto EXIT; } /* Set BGP VR Context */ BGP_SET_VR_CONTEXT (&BLG, bgp->owning_bvr); /* Re-regiser accept thread */ t_accept = NULL; BGP_READ_ON (&BLG, t_accept, bgp, bpn_sock_accept, bgp_sock); /* Update the Accept Thread List Node */ tmp_lnode->t_accept = t_accept; /* Accept Incoming Connection (Blocking) */ accept_sock = sockunion_accept (&BLG, bgp_sock, &su); if (accept_sock < 0) { zlog_err (&BLG, "[NETWORK] Accept Thread: accept() Failed for Server" " Sock %d, Err:%d-%s", bgp_sock, errno, pal_strerror (errno)); ret = -1; goto EXIT; } if (BGP_DEBUG (events, EVENTS)) zlog_info (&BLG, "[NETWORK] Accept Thread: Incoming conn from host" " %s (FD=%u)", inet_sutop (&su, su_buf), accept_sock); /* Search for Configured Peer with same Remote IP address */ peer = bgp_peer_search (bgp, &su); if (! peer) { if (BGP_DEBUG (events, EVENTS)) zlog_info (&BLG, "[NETWORK] Accept Thread: %s - No such Peer " "configured", inet_sutop (&su, su_buf)); SSOCK_FD_CLOSE (&BLG, accept_sock); ret = -1; goto EXIT; } /* Prepare an Incoming Connection Req. Info structure */ peer_icr = XCALLOC (MTYPE_TMP, sizeof (struct bgp_peer_inconn_req)); if (! peer_icr) { zlog_err (&BLG, "[NETWORK] Accept Thread:" " Cannot allocate memory (%d) @ %s:%d", sizeof (struct bgp_peer_inconn_req), __FILE__, __LINE__); SSOCK_FD_CLOSE (&BLG, accept_sock); ret = -1; goto EXIT; } /* Initialize the FIFO Node */ FIFO_INIT (&peer_icr->icr_fifo); /* Store the ICR Information */ peer_icr->icr_sock = accept_sock; switch (su.sa.sa_family) { case AF_INET: peer_icr->icr_port = su.sin.sin_port; break; #ifdef HAVE_IPV6 case AF_INET6: peer_icr->icr_port = su.sin6.sin6_port; break; #endif /* HAVE_IPV6 */ } /* Enqueue into Peer's 'bicr_fifo' */ FIFO_ADD (&peer->bicr_fifo, &peer_icr->icr_fifo); /* Generate BGP Peer FSM ICR Event */ BGP_PEER_FSM_EVENT_ADD (&BLG, peer, BPF_EVENT_TCP_CONN_VALID); EXIT: return ret; }
/* This function is the first starting point of all BGP connection. It try to connect to remote peer with non-blocking IO. */ int bgp_start (struct peer *peer) { int status; if (BGP_PEER_START_SUPPRESSED (peer)) { if (BGP_DEBUG (fsm, FSM)) plog_err (peer->log, "%s [FSM] Trying to start suppressed peer" " - this is never supposed to happen!", peer->host); return -1; } /* Scrub some information that might be left over from a previous, * session */ /* Connection information. */ if (peer->su_local) { sockunion_free (peer->su_local); peer->su_local = NULL; } if (peer->su_remote) { sockunion_free (peer->su_remote); peer->su_remote = NULL; } /* Clear remote router-id. */ peer->remote_id.s_addr = 0; /* Clear peer capability flag. */ peer->cap = 0; /* If the peer is passive mode, force to move to Active mode. */ if (CHECK_FLAG (peer->flags, PEER_FLAG_PASSIVE)) { BGP_EVENT_ADD (peer, TCP_connection_open_failed); return 0; } status = bgp_connect (peer); switch (status) { case connect_error: if (BGP_DEBUG (fsm, FSM)) plog_debug (peer->log, "%s [FSM] Connect error", peer->host); BGP_EVENT_ADD (peer, TCP_connection_open_failed); break; case connect_success: if (BGP_DEBUG (fsm, FSM)) plog_debug (peer->log, "%s [FSM] Connect immediately success", peer->host); BGP_EVENT_ADD (peer, TCP_connection_open); break; case connect_in_progress: /* To check nonblocking connect, we wait until socket is readable or writable. */ if (BGP_DEBUG (fsm, FSM)) plog_debug (peer->log, "%s [FSM] Non blocking connect waiting result", peer->host); if (peer->fd < 0) { zlog_err ("bgp_start peer's fd is negative value %d", peer->fd); return -1; } BGP_READ_ON (peer->t_read, bgp_read, peer->fd); BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); break; } return 0; }