Line data Source code
1 : #ifndef HEADER_fd_src_disco_net_fd_net_sock_h 2 : #define HEADER_fd_src_disco_net_fd_net_sock_h 3 : 4 : #include "../fd_disco_base.h" 5 : #include "../../util/net/fd_ip4.h" 6 : 7 : #include <errno.h> 8 : #include <sys/socket.h> 9 : #include <netinet/in.h> 10 : 11 : /* fd_net_sock_ops_t is the syscall interface used by fd_net_sock. 12 : Signatures and semantics match libc. Tests substitute mocks. */ 13 : struct fd_net_sock_ops { 14 : int (*socket)( int domain, int type, int protocol ); 15 : long (*sendto)( int sock, void const * buf, ulong len, int flags, struct sockaddr const * addr, socklen_t addr_len ); 16 : }; 17 : typedef struct fd_net_sock_ops fd_net_sock_ops_t; 18 : 19 : struct fd_net_sock { 20 : fd_net_sock_ops_t const * ops; 21 : int sock; 22 : }; 23 : typedef struct fd_net_sock fd_net_sock_t; 24 : 25 : FD_PROTOTYPES_BEGIN 26 : 27 : /* fd_net_sock_init_ops is the same as fd_net_sock_init but uses the 28 : given ops instead of libc. ops and each of its members must be 29 : non-NULL. */ 30 : static fd_net_sock_t * 31 : fd_net_sock_init_ops( fd_net_sock_t * net_sock, 32 0 : fd_net_sock_ops_t const * ops ) { 33 0 : if( FD_UNLIKELY( !net_sock ) ) { 34 0 : FD_LOG_WARNING(( "NULL net_sock" )); 35 0 : errno = EINVAL; 36 0 : return NULL; 37 0 : } 38 0 : if( FD_UNLIKELY( !ops ) ) { 39 0 : FD_LOG_WARNING(( "NULL ops" )); 40 0 : errno = EINVAL; 41 0 : return NULL; 42 0 : } 43 0 : if( FD_UNLIKELY( !ops->socket ) ) { 44 0 : FD_LOG_WARNING(( "NULL ops->socket" )); 45 0 : errno = EINVAL; 46 0 : return NULL; 47 0 : } 48 0 : if( FD_UNLIKELY( !ops->sendto ) ) { 49 0 : FD_LOG_WARNING(( "NULL ops->sendto" )); 50 0 : errno = EINVAL; 51 0 : return NULL; 52 0 : } 53 0 : int const sock = ops->socket( AF_INET, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_RAW ); 54 0 : if( FD_UNLIKELY( sock<0 ) ) { 55 0 : int const err = errno; 56 0 : FD_LOG_WARNING(( "socket(AF_INET,SOCK_RAW|SOCK_NONBLOCK,IPPROTO_RAW) failed (%i-%s)", err, fd_io_strerror( err ) )); 57 0 : errno = err; 58 0 : return NULL; 59 0 : } 60 0 : net_sock->ops = ops; 61 0 : net_sock->sock = sock; 62 0 : return net_sock; 63 0 : } 64 : 65 : /* fd_net_sock_init initializes net_sock and opens a non-blocking 66 : send-only IPPROTO_RAW socket. It is not bound to an interface and 67 : relies on the kernel to route packets. Returns net_sock on success 68 : and NULL on error with errno set. */ 69 : static fd_net_sock_t * 70 0 : fd_net_sock_init( fd_net_sock_t * net_sock ) { 71 0 : static fd_net_sock_ops_t const ops = { 72 0 : .socket = socket, 73 0 : .sendto = sendto 74 0 : }; 75 0 : return fd_net_sock_init_ops( net_sock, &ops ); 76 0 : } 77 : 78 : /* fd_net_sock_send sends one packet on net_sock with a single 79 : non-blocking sendto. ip4 points to the beginning of an IPv4 header. 80 : ip4_sz is the size of the IP header and payload. The L2 header will 81 : be constructed by the kernel. The destination is ip4->daddr. 82 : Returns 1 if the whole packet was sent and 0 if the packet was 83 : not (the send would block, failed, or was short). Failed packets 84 : are not retried. */ 85 : FD_FN_UNUSED static int 86 : fd_net_sock_send( fd_net_sock_t * net_sock, 87 : fd_ip4_hdr_t const * ip4, 88 0 : ulong ip4_sz ) { 89 0 : struct sockaddr_in sa = { 90 0 : .sin_family = AF_INET, 91 0 : .sin_addr.s_addr = ip4->daddr, 92 0 : .sin_port = 0 /* ignored */ 93 0 : }; 94 : /* Single non-blocking attempt. If the send would block, fails, or 95 : is short, the packet is dropped. */ 96 0 : long const sent_sz = net_sock->ops->sendto( net_sock->sock, ip4, ip4_sz, MSG_DONTWAIT, fd_type_pun_const( &sa ), sizeof(struct sockaddr_in) ); 97 0 : return sent_sz==(long)ip4_sz; 98 0 : } 99 : 100 : FD_PROTOTYPES_END 101 : 102 : #endif /* HEADER_fd_src_disco_net_fd_net_sock_h */