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; }
/* Conclude the EP's binding. This function is abstracted from the * main bind code to allow implicit binds that occur when sendto() is * called on an OS socket. [lport] and CI_SIN(addr)->sin_port do not * have to be the same value. */ static int ci_udp_bind_conclude(citp_socket* ep, const struct sockaddr* addr, ci_uint16 lport ) { ci_udp_state* us; ci_uint32 addr_be32; int rc; CHECK_UEP(ep); ci_assert(addr != NULL); if( ci_udp_should_handover(ep, addr, lport) ) goto handover; addr_be32 = ci_get_ip4_addr(ep->s->domain, addr); ci_udp_set_laddr(ep, addr_be32, lport); us = SOCK_TO_UDP(ep->s); if( addr_be32 != 0 ) us->s.cp.sock_cp_flags |= OO_SCP_LADDR_BOUND; /* reset any rx/tx that have taken place already */ UDP_CLR_FLAG(us, CI_UDPF_EF_SEND); #ifdef ONLOAD_OFE if( ep->netif->ofe != NULL ) us->s.ofe_code_start = ofe_socktbl_find( ep->netif->ofe, OFE_SOCKTYPE_UDP, udp_laddr_be32(us), udp_raddr_be32(us), udp_lport_be16(us), udp_rport_be16(us)); #endif /* OS source addrs have already been handed-over, so this must be one of * our src addresses. */ rc = ci_udp_set_filters( ep, us); ci_assert( !UDP_GET_FLAG(us, CI_UDPF_EF_BIND) ); /*! \todo FIXME isn't the port the thing to be testing here? */ if( udp_laddr_be32(us) != INADDR_ANY_BE32 ) UDP_SET_FLAG(us, CI_UDPF_EF_BIND); CI_UDPSTATE_SHOW_EP( ep ); if( rc == CI_SOCKET_ERROR && CITP_OPTS.no_fail) { CITP_STATS_NETIF(++ep->netif->state->stats.udp_bind_no_filter); goto handover; } return rc; handover: LOG_UV(log("%s: "SK_FMT" HANDOVER", __FUNCTION__, SK_PRI_ARGS(ep))); return CI_SOCKET_HANDOVER; }
/* Encapsulation of sys_getsockname for UDP EPs */ static int ci_udp_sys_getsockname( ci_fd_t sock, citp_socket* ep ) { socklen_t salen; int rc; union ci_sockaddr_u sa_u; ci_assert(ep); #if CI_CFG_FAKE_IPV6 ci_assert(ep->s->domain == AF_INET || ep->s->domain == AF_INET6); #else ci_assert(ep->s->domain == AF_INET); #endif salen = sizeof(sa_u); rc = ci_sys_getsockname( sock, &sa_u.sa, &salen ); if( rc ) return rc; if( sa_u.sa.sa_family != ep->s->domain || salen < sizeof(struct sockaddr_in) #if CI_CFG_FAKE_IPV6 || (ep->s->domain == AF_INET6 && salen < sizeof(struct sockaddr_in6) ) #endif ) { LOG_UV(log("%s: OS sock domain %d != expected domain %d or " "sys_getsockname struct small (%d exp %d)", __FUNCTION__, sa_u.sa.sa_family, ep->s->domain, salen, (int)(ep->s->domain == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)))); return -1; } #if CI_CFG_FAKE_IPV6 if( ep->s->domain == AF_INET ) { ci_udp_set_laddr( ep, ci_get_ip4_addr(sa_u.sa.sa_family, &sa_u.sa), sa_u.sin.sin_port ); } else { ci_udp_set_laddr( ep, ci_get_ip4_addr(sa_u.sa.sa_family, &sa_u.sa), sa_u.sin6.sin6_port ); } #else ci_udp_set_laddr( ep, ci_get_ip4_addr(sa_u.sa.sa_family, &sa_u.sa), sa_u.sin.sin_port ); #endif return 0; }
static int ci_udp_recvmsg_socklocked_slowpath(ci_udp_iomsg_args* a, ci_msghdr* msg, ci_iovec_ptr *piov, int flags) { int rc = 0; ci_netif* ni = a->ni; ci_udp_state* us = a->us; if(CI_UNLIKELY( ni->state->rxq_low )) ci_netif_rxq_low_on_recv(ni, &us->s, 1 /* assume at least one pkt freed */); /* In the kernel recv() with flags is not called. * only read(). So flags may only contain MSG_DONTWAIT */ #ifdef __KERNEL__ ci_assert_equal(flags, 0); #endif #ifndef __KERNEL__ if( flags & MSG_ERRQUEUE_CHK ) { if( OO_PP_NOT_NULL(us->timestamp_q.extract) ) { ci_ip_pkt_fmt* pkt; struct timespec ts[3]; struct cmsg_state cmsg_state; ci_udp_hdr* udp; int paylen; /* TODO is this necessary? - mirroring ci_udp_recvmsg_get() */ ci_rmb(); pkt = PKT_CHK_NNL(ni, us->timestamp_q.extract); if( pkt->tx_hw_stamp.tv_sec == CI_PKT_TX_HW_STAMP_CONSUMED ) { if( OO_PP_IS_NULL(pkt->tsq_next) ) goto errqueue_empty; us->timestamp_q.extract = pkt->tsq_next; pkt = PKT_CHK_NNL(ni, us->timestamp_q.extract); ci_assert(pkt->tx_hw_stamp.tv_sec != CI_PKT_TX_HW_STAMP_CONSUMED); } udp = oo_ip_data(pkt); paylen = CI_BSWAP_BE16(oo_ip_hdr(pkt)->ip_tot_len_be16) - sizeof(ci_ip4_hdr) - sizeof(udp); msg->msg_flags = 0; cmsg_state.msg = msg; cmsg_state.cm = msg->msg_control; cmsg_state.cmsg_bytes_used = 0; ci_iovec_ptr_init_nz(piov, msg->msg_iov, msg->msg_iovlen); memset(ts, 0, sizeof(ts)); if( us->s.timestamping_flags & ONLOAD_SOF_TIMESTAMPING_RAW_HARDWARE ) { ts[2].tv_sec = pkt->tx_hw_stamp.tv_sec; ts[2].tv_nsec = pkt->tx_hw_stamp.tv_nsec; } if( (us->s.timestamping_flags & ONLOAD_SOF_TIMESTAMPING_SYS_HARDWARE) && (pkt->tx_hw_stamp.tv_nsec & CI_IP_PKT_HW_STAMP_FLAG_IN_SYNC) ) { ts[1].tv_sec = pkt->tx_hw_stamp.tv_sec; ts[1].tv_nsec = pkt->tx_hw_stamp.tv_nsec; } ci_put_cmsg(&cmsg_state, SOL_SOCKET, ONLOAD_SCM_TIMESTAMPING, sizeof(ts), &ts); oo_offbuf_set_start(&pkt->buf, udp + 1); oo_offbuf_set_len(&pkt->buf, paylen); rc = oo_copy_pkt_to_iovec_no_adv(ni, pkt, piov, paylen); /* Mark this packet/timestamp as consumed */ pkt->tx_hw_stamp.tv_sec = CI_PKT_TX_HW_STAMP_CONSUMED; ci_ip_cmsg_finish(&cmsg_state); msg->msg_flags |= MSG_ERRQUEUE_CHK; return rc; } errqueue_empty: /* ICMP is handled via OS, so get OS error */ rc = oo_os_sock_recvmsg(ni, SC_SP(&us->s), msg, flags); if( rc < 0 ) { ci_assert(-rc == errno); return -1; } else return rc; } #endif if( (rc = ci_get_so_error(&us->s)) != 0 ) { CI_SET_ERROR(rc, rc); return rc; } if( msg->msg_iovlen > 0 && msg->msg_iov == NULL ) { CI_SET_ERROR(rc, EFAULT); return rc; } #if MSG_OOB_CHK if( flags & MSG_OOB_CHK ) { CI_SET_ERROR(rc, EOPNOTSUPP); return rc; } #endif #if CI_CFG_POSIX_RECV if( ! udp_lport_be16(us)) { LOG_UV(log("%s: -1 (ENOTCONN)", __FUNCTION__)); CI_SET_ERROR(rc, ENOTCONN); return rc; } #endif if( msg->msg_iovlen == 0 ) { /* We have a difference in behaviour from the Linux stack here. When ** msg_iovlen is 0 Linux 2.4.21-15.EL does not set MSG_TRUNC when a ** datagram has non-zero length. We do. */ CI_IOVEC_LEN(&piov->io) = piov->iovlen = 0; return IOVLEN_WORKAROUND_RC_VALUE; } return 0; }