int ci_udp_shutdown(citp_socket* ep, ci_fd_t fd, int how) { ci_fd_t os_sock; int rc; CHECK_UEP(ep); LOG_UV(log(LPF "shutdown("SF_FMT", %d)", SF_PRI_ARGS(ep,fd), how)); os_sock = ci_get_os_sock_fd(fd); if( CI_IS_VALID_SOCKET( os_sock ) ) { rc = ci_sys_shutdown(os_sock, how); ci_rel_os_sock_fd( os_sock ); if( rc < 0 ) return CI_SOCKET_ERROR; } rc = __ci_udp_shutdown(ep->netif, SOCK_TO_UDP(ep->s), how); if( rc < 0 ) { CI_SET_ERROR(rc, -rc); return rc; } return 0; }
void ci_udp_handle_force_reuseport(ci_fd_t fd, citp_socket* ep, const struct sockaddr* sa, socklen_t sa_len) { int rc; if( CITP_OPTS.udp_reuseports != 0 && ((struct sockaddr_in*)sa)->sin_port != 0 ) { struct ci_port_list *force_reuseport; CI_DLLIST_FOR_EACH2(struct ci_port_list, force_reuseport, link, (ci_dllist*)(ci_uintptr_t)CITP_OPTS.udp_reuseports) { if( force_reuseport->port == ((struct sockaddr_in*)sa)->sin_port ) { int one = 1; ci_fd_t os_sock = ci_get_os_sock_fd(fd); ci_assert(CI_IS_VALID_SOCKET(os_sock)); rc = ci_sys_setsockopt(os_sock, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); ci_rel_os_sock_fd(os_sock); /* Fixme: shouldn't we handle errors? */ if( rc != 0 ) { log("%s: failed to set SO_REUSEPORT on OS socket: " "rc=%d errno=%d", __func__, rc, errno); } ep->s->s_flags |= CI_SOCK_FLAG_REUSEPORT; LOG_UC(log("%s "SF_FMT", applied legacy SO_REUSEPORT flag for port %u", __FUNCTION__, SF_PRI_ARGS(ep, fd), force_reuseport->port)); } } } }
static int ci_tcp_shutdown_listen(citp_socket* ep, int how, ci_fd_t fd) { ci_tcp_socket_listen* tls = SOCK_TO_TCP_LISTEN(ep->s); if( how == SHUT_WR ) return 0; ci_sock_lock(ep->netif, &tls->s.b); ci_netif_lock(ep->netif); LOG_TC(ci_log(SK_FMT" shutdown(SHUT_RD)", SK_PRI_ARGS(ep))); __ci_tcp_listen_shutdown(ep->netif, tls, fd); __ci_tcp_listen_to_normal(ep->netif, tls); { ci_fd_t os_sock = ci_get_os_sock_fd(ep, fd); int flags = ci_sys_fcntl(os_sock, F_GETFL); flags &= (~O_NONBLOCK); CI_TRY(ci_sys_fcntl(os_sock, F_SETFL, flags)); ci_rel_os_sock_fd(os_sock); } ci_netif_unlock(ep->netif); ci_sock_unlock(ep->netif, &tls->s.b); return 0; }
/* create a pt->pt association with a server * This uses the OS to do all the work so that we don't have to emulate * some of the more unpleasant "tricks" of Linux. * * When we're either handing-over OS-dest connects or when we're "no * failing" connects we may return -2 (unhandled). In this case the * OS socket _has_ been connected & we therefore are handing-over to * a socket in the right state. * * NOTE: WINDOWS the WSPConnect() API is quite a lot more complex than * the BSD one. Therefore, to stop polluting the core code with masses * of Windows frippery, the backing socket connection is successfully * established _before_ this function is called. This function will use * the state of the backing socket to configure the Efab socket - so the * end result is the same (right down to the race between the OS socket * connection being established and our filters being inserted). */ int ci_udp_connect(citp_socket* ep, ci_fd_t fd, const struct sockaddr* serv_addr, socklen_t addrlen ) { int rc; ci_fd_t os_sock; CHECK_UEP(ep); LOG_UC(log("%s("SF_FMT", addrlen=%d)", __FUNCTION__, SF_PRI_ARGS(ep,fd), addrlen)); os_sock = ci_get_os_sock_fd(fd); if( !CI_IS_VALID_SOCKET( os_sock ) ) { LOG_U(ci_log("%s: no backing socket", __FUNCTION__)); return -1; } /* Because we have not handed over the fd to the OS all calls to bind() * and connect() will have been seen by us - therefore our copies of * the local/remote address & port will be accurate. */ /* Let the OS do the connection - it'll also do the data validation * for free. On failure the OS changes nothing - therefore we * need to leave the filters in place (if such they were). * Because the OS socket and our socket are socket-options-synchronized, * the following call will also check the supplied address according to * the SO_BROADCAST socket option settings. */ rc = ci_sys_connect(os_sock, serv_addr, addrlen); if( rc != 0 ) { LOG_U(log("%s: sys_connect failed errno:%d", __FUNCTION__, errno)); ci_rel_os_sock_fd(os_sock); return -1; } rc = ci_udp_connect_conclude( ep, fd, serv_addr, addrlen, os_sock); ci_rel_os_sock_fd(os_sock); return rc; }
/* In this bind handler we just check that the address to which * are binding is either "any" or one of ours. * In the Linux kernel version [fd] is unused. */ int ci_tcp_bind(citp_socket* ep, const struct sockaddr* my_addr, socklen_t addrlen, ci_fd_t fd ) { struct sockaddr_in* my_addr_in; ci_uint16 new_port; ci_uint32 addr_be32; ci_sock_cmn* s = ep->s; ci_tcp_state* c = &SOCK_TO_WAITABLE_OBJ(s)->tcp; int rc; CHECK_TEP(ep); my_addr_in = (struct sockaddr_in*) my_addr; /* Check if state of the socket is OK for bind operation. */ /* \todo Earlier (TS_TCP( epi->tcpep.state )->tcp_source_be16) is used. * What is better? */ if (my_addr == NULL) RET_WITH_ERRNO( EINVAL ); if (s->b.state != CI_TCP_CLOSED) RET_WITH_ERRNO( EINVAL ); if (c->tcpflags & CI_TCPT_FLAG_WAS_ESTAB) RET_WITH_ERRNO( EINVAL ); if( my_addr->sa_family != s->domain ) RET_WITH_ERRNO( s->domain == PF_INET ? EAFNOSUPPORT : EINVAL ); /* Bug 4884: Windows regularly uses addrlen > sizeof(struct sockaddr_in) * Linux is also relaxed about overlength data areas. */ if (s->domain == PF_INET && addrlen < sizeof(struct sockaddr_in)) RET_WITH_ERRNO( EINVAL ); #if CI_CFG_FAKE_IPV6 if (s->domain == PF_INET6 && addrlen < SIN6_LEN_RFC2133) RET_WITH_ERRNO( EINVAL ); if( s->domain == PF_INET6 && !ci_tcp_ipv6_is_ipv4(my_addr) ) return CI_SOCKET_HANDOVER; #endif addr_be32 = ci_get_ip4_addr(s->domain, my_addr); /* Using the port number provided, see if we can do this bind */ new_port = my_addr_in->sin_port; if( CITP_OPTS.tcp_reuseports != 0 && new_port != 0 ) { struct ci_port_list *force_reuseport; CI_DLLIST_FOR_EACH2(struct ci_port_list, force_reuseport, link, (ci_dllist*)(ci_uintptr_t)CITP_OPTS.tcp_reuseports) { if( force_reuseport->port == new_port ) { int one = 1; ci_fd_t os_sock = ci_get_os_sock_fd(ep, fd); ci_assert(CI_IS_VALID_SOCKET(os_sock)); rc = ci_sys_setsockopt(os_sock, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); ci_rel_os_sock_fd(os_sock); if( rc != 0 && errno == ENOPROTOOPT ) ep->s->s_flags |= CI_SOCK_FLAG_REUSEPORT_LEGACY; ep->s->s_flags |= CI_SOCK_FLAG_REUSEPORT; LOG_TC(log("%s "SF_FMT", applied legacy SO_REUSEPORT flag for port %u", __FUNCTION__, SF_PRI_ARGS(ep, fd), new_port)); } } } if( !(ep->s->s_flags & CI_SOCK_FLAG_REUSEPORT_LEGACY) ) CI_LOGLEVEL_TRY_RET(LOG_TV, __ci_bind(ep->netif, ep->s, addr_be32, &new_port)); ep->s->s_flags |= CI_SOCK_FLAG_BOUND; sock_lport_be16(s) = new_port; sock_laddr_be32(s) = addr_be32; if( CI_IP_IS_MULTICAST(addr_be32) ) s->cp.ip_laddr_be32 = 0; else s->cp.ip_laddr_be32 = addr_be32; s->cp.lport_be16 = new_port; sock_rport_be16(s) = sock_raddr_be32(s) = 0; LOG_TC(log(LPF "bind to %s:%u n_p:%u lp:%u", ip_addr_str(addr_be32), (unsigned) CI_BSWAP_BE16(my_addr_in->sin_port), CI_BSWAP_BE16(new_port), CI_BSWAP_BE16(sock_lport_be16(s)))); return 0; }