/** * Put a SO_TIMESTAMPING control message into msg ancillary data buffer. */ void ip_cmsg_recv_timestamping(ci_netif *ni, ci_uint64 sys_timestamp, struct timespec* hw_timestamp, int flags, struct cmsg_state *cmsg_state) { struct { struct timespec systime; struct timespec hwtimetrans; struct timespec hwtimeraw; } ts; int c_flags = 0; if( hw_timestamp->tv_sec != 0 ) c_flags = flags & (ONLOAD_SOF_TIMESTAMPING_RAW_HARDWARE | ONLOAD_SOF_TIMESTAMPING_SYS_HARDWARE); if( sys_timestamp != 0 ) c_flags |= flags & ONLOAD_SOF_TIMESTAMPING_SOFTWARE; memset(&ts, 0, sizeof(ts)); if( c_flags & ONLOAD_SOF_TIMESTAMPING_SOFTWARE ) ci_udp_compute_stamp(ni, sys_timestamp, &ts.systime); if( c_flags & ONLOAD_SOF_TIMESTAMPING_RAW_HARDWARE ) ts.hwtimeraw = *hw_timestamp; if( c_flags & ONLOAD_SOF_TIMESTAMPING_SYS_HARDWARE ) ts.hwtimetrans = *hw_timestamp; ci_put_cmsg(cmsg_state, SOL_SOCKET, ONLOAD_SO_TIMESTAMPING, sizeof(ts), &ts); }
/** * Put an IP_RECVTOS control message into msg ancillary data buffer. */ ci_inline void ip_cmsg_recv_tos(const ci_ip_pkt_fmt *pkt, struct cmsg_state *cmsg_state) { int tos = oo_ip_hdr_const(pkt)->ip_tos; ci_put_cmsg(cmsg_state, IPPROTO_IP, IP_TOS, sizeof(tos), &tos); }
/** * Put an IP_RECVTTL control message into msg ancillary data buffer. */ ci_inline void ip_cmsg_recv_ttl(const ci_ip_pkt_fmt *pkt, struct cmsg_state *cmsg_state) { int ttl = oo_ip_hdr_const(pkt)->ip_ttl; ci_put_cmsg(cmsg_state, IPPROTO_IP, IP_TTL, sizeof(ttl), &ttl); }
/** * Put a SO_TIMESTAMPNS control message into msg ancillary data buffer. */ void ip_cmsg_recv_timestampns(ci_netif *ni, ci_uint64 timestamp, struct cmsg_state *cmsg_state) { struct timespec ts; ci_udp_compute_stamp(ni, timestamp, &ts); ci_put_cmsg(cmsg_state, SOL_SOCKET, SO_TIMESTAMPNS, sizeof(ts), &ts); }
/** * Put a SO_TIMESTAMP control message into msg ancillary data buffer. */ void ip_cmsg_recv_timestamp(ci_netif *ni, ci_uint64 timestamp, struct cmsg_state *cmsg_state) { struct timespec ts; struct timeval tv; ci_udp_compute_stamp(ni, timestamp, &ts); tv.tv_sec = ts.tv_sec; tv.tv_usec = ts.tv_nsec / 1000; ci_put_cmsg(cmsg_state, SOL_SOCKET, SO_TIMESTAMP, sizeof(tv), &tv); }
/** * Put an IP_PKTINFO control message into msg ancillary data buffer. */ static void ip_cmsg_recv_pktinfo(ci_netif* netif, const ci_ip_pkt_fmt* pkt, struct cmsg_state *cmsg_state) { /* TODO: This is horribly inefficient -- two system calls. Could be made * cheap with a user-level llap table. */ struct in_pktinfo info; ci_uint32 addr; int hwport; addr = oo_ip_hdr_const(pkt)->ip_daddr_be32; info.ipi_addr.s_addr = addr; /* Set the ifindex the pkt was received at. */ { ci_ifid_t ifindex = 0; int rc = 0; hwport = netif->state->intf_i_to_hwport[pkt->intf_i]; rc = cicp_llap_find(CICP_HANDLE(netif), &ifindex, CI_HWPORT_ID(hwport), pkt->vlan); if( rc != 0 ) LOG_E(ci_log("%s: cicp_llap_find(intf_i=%d, hwport=%d) failed rc=%d", __FUNCTION__, pkt->intf_i, hwport, rc)); info.ipi_ifindex = ifindex; } /* RFC1122: The specific-destination address is defined to be the * destination address in the IP header unless the header contains a * broadcast or multicast address, in which case the specific-destination * is an IP address assigned to the physical interface on which the * datagram arrived. */ /*\ FIXME: we should drop the packet if this call fails */ cicp_ipif_pktinfo_query(CICP_HANDLE(netif), netif, OO_PKT_P(pkt), info.ipi_ifindex, &info.ipi_spec_dst.s_addr ); ci_put_cmsg(cmsg_state, IPPROTO_IP, IP_PKTINFO, sizeof(info), &info); }
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; }