/* ****************************************************************************** * SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: iplite_sock.c,v $ $Revision: 1.120.10.5 $ * $Source: /home/interpeak/CVSRoot/iplite2/src/iplite_sock.c,v $ * $Author: hxie $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Kenneth Jonsson ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** * Functions for socket API. */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #define IPCOM_SKIP_NATIVE_SOCK_API #ifndef IPCOM_KERNEL #define IPCOM_KERNEL #endif #include "iplite_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #if !defined(IPLITE_ONE_FILE) || defined(IPLITE_C) #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include "iplite_h.h" #include "iplite_loopback.h" #include "iplite_sock.h" #ifdef IPCOM_USE_TCP #include "iplite_tcp.h" #endif /* **************************************************************************** * 4 DEFINES **************************************************************************** */ /* **************************************************************************** * 5 TYPES **************************************************************************** */ /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ #ifdef IP_PORT_OSE5_disabled_code Ip_err ipcom_crt_block_wait(Ipcom_socket *sock, Ip_s32 milli_seconds, Ip_bool snd); void ipcom_crt_block_post(Ipcom_socket *sock, Ip_bool snd); #endif /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ IP_STATIC Ipcom_pkt *iplite_krecvfrom(Iplite_socket *sock, struct Ip_sockaddr *from, Ip_socklen_t *fromlen); /* **************************************************************************** * 8 DATA **************************************************************************** */ /* **************************************************************************** * 9 STATIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * iplite_bind_check_reuse *=========================================================================== * Description: Checks if the source port is reused and if it is, whenever * the reuse is ok or not. * Parameters: sock - The socket for which to check port reuse. * Returns: 0 = success, <0 = error code. * */ IP_STATIC int iplite_bind_check_reuse(Iplite_socket *sock) { /* Check if there is a socket bound to same local port&addr -> EADDRINUSE * (Consider socket option SO_REUSEADDR and SO_REUSEPORT). */ Iplite_socket *cachesock = IP_NULL; int reuse, last_sock = 0; for (;;) { #ifdef IPCOM_USE_INET if (sock->ipcom.domain == IP_AF_INET) { cachesock = iplite_ip4_socklookup(sock->proto, IP_NULL, sock->sport, IP_NULL, 0, &last_sock); #ifdef IPCOM_USE_INET6 if (cachesock && cachesock->ipcom.domain == IP_AF_INET6) cachesock = IP_NULL; #endif } #endif #ifdef IPCOM_USE_INET6 if (sock->ipcom.domain == IP_AF_INET6) cachesock = iplite_ip6_socklookup(sock->proto, IP_NULL, sock->sport, IP_NULL, 0, &last_sock); #endif if (cachesock == IP_NULL) break; reuse = (sock->flags & cachesock->flags) & (IPLITE_SOCKET_FLAG_REUSEPORT | IPLITE_SOCKET_FLAG_REUSEADDR); /* Both sockets must have at least one of the two reuse flags set. */ if (reuse == 0) return -IP_ERRNO_EADDRINUSE; #ifdef IPCOM_USE_INET if (sock->ipcom.domain == IP_AF_INET) { if (sock->d->ip4.saddr_n != IP_INADDR_ANY && iplite_ip4_get_addr_type(IP_NULL, sock->d->ip4.saddr_n) != IPLITE_ADDR_TYPE_UNICAST) return 0; if (sock->d->ip4.saddr_n != cachesock->d->ip4.saddr_n) continue; } #endif #ifdef IPCOM_USE_INET6 if (sock->ipcom.domain == IP_AF_INET6) { if (IP_IN6_IS_ADDR_MULTICAST(&sock->d->ip6.saddr)) return 0; if (!IP_IN6_ARE_ADDR_EQUAL(&sock->d->ip6.saddr, &cachesock->d->ip6.saddr)) continue; } #endif #ifdef IPCOM_USE_TCP if (sock->tcb != IP_NULL && IP_BIT_ISSET(cachesock->flags, IPLITE_SOCKET_FLAG_CONNECTED)) return 0; #endif if (IP_BIT_ISFALSE(reuse, IPLITE_SOCKET_FLAG_REUSEPORT) #ifdef IPCOM_USE_TCP || sock->tcb != IP_NULL #endif ) return -IP_ERRNO_EADDRINUSE; } /* bind ok. */ return 0; } /* *=========================================================================== * iplite_do_shutdown *=========================================================================== * Description: The actual ipcom_shutdown() implementation * Parameters: fd - The file descriptor to shut down. * how - How the socket should be shut down. * Returns: 0 = success, -1 = error */ IP_STATIC int iplite_do_shutdown(Ip_fd fd, int how) { Iplite_socket *sock; int ret; int ret2; int so_errno = -1; /* Close event */ ip_assert2(fd >= IPLITE_SOCKET_STARTFD); ip_assert2(iplite.sockets[fd] != IP_NULL); sock = iplite.sockets[fd]; if (sock == IP_NULL) SOCK_ERROUT(IP_ERRNO_EBADF); /* Set socket flags. */ switch (how) { case IP_SHUT_RDWR: ret = iplite_do_shutdown(fd, IP_SHUT_RD); ret2 = iplite_do_shutdown(fd, IP_SHUT_WR); return ret == 0 ? ret2 : ret; case IP_SHUT_RD: if (IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_READ_CLOSED)) SOCK_ERROUT(IP_ERRNO_ESHUTDOWN); IP_BIT_SET(sock->flags, IPLITE_SOCKET_FLAG_READ_CLOSED); break; case IP_SHUT_WR: if (IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_WRITE_CLOSED)) SOCK_ERROUT(IP_ERRNO_ESHUTDOWN); IP_BIT_SET(sock->flags, IPLITE_SOCKET_FLAG_WRITE_CLOSED); if (sock->snd_poll_list.size) so_errno = IP_ERRNO_EPIPE; break; default: SOCK_ERROUT(IP_ERRNO_EINVAL); } #ifdef IPCOM_USE_TCP if (sock->ipcom.type == IP_SOCK_STREAM) iplite_tcp_shutdown(sock, how); #endif iplite_sock_data_avail(sock, so_errno, how); return 0; errout: return IP_SOCKERR; } /* *=========================================================================== * iplite_sock_is_readable *=========================================================================== * Description: Returns if a socket is readable or not * Parameters: sock - socket to check for readability. * Returns: IP_TRUE - socket is readable * IP_FALSE - socket is not readable * */ IP_STATIC Ip_bool iplite_sock_is_readable(Iplite_socket *sock) { if (sock->ipcom.so_errno) return IP_TRUE; if (sock->ipcom.rcv_bytes > 0) return IP_TRUE; #ifdef IPCOM_USE_TCP if (sock->tcb) { Iplite_tcp_tcb *tcb = sock->tcb; if (tcb->state == IPLITE_TCPS_LISTEN) { if (IP_BIT_ISSET(tcb->flags, IPLITE_TCPC_FLAG_CONNABORTED)) return IP_TRUE; if (tcb->backlog_done[0] != IP_SOCKERR) return IP_TRUE; } } #endif /* IPCOM_USE_TCP */ return (IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_READ_CLOSED | IPLITE_SOCKET_FLAG_PEER_WRITE_CLOSED)); } /* *=========================================================================== * iplite_sock_is_writable *=========================================================================== * Description: Returns if a socket is writable or not * Parameters: sock - socket to check for writability. * Returns: IP_TRUE - socket is writable * IP_FALSE - socket is not writable * */ IP_STATIC Ip_bool iplite_sock_is_writable(Iplite_socket *sock) { #ifdef IPCOM_USE_TCP if (sock->tcb) { Iplite_tcp_tcb *tcb = sock->tcb; if (sock->ipcom.so_errno) return IP_TRUE; return (tcb->state >= IPLITE_TCPS_ESTABLISHED && sock->snd_bytes < sock->snd_max_bytes); } #endif /* IPCOM_USE_TCP */ return IP_TRUE; } /* *=========================================================================== * iplite_sock_select_wait *=========================================================================== * Description: Waits for sockets in a select to become ready. * Parameters: sem - a valid semaphore with timeout or IP_NULL if the * sockets should just be checked. * timeout - maximum number of milliseconds to wait for * sockets to become ready. * Returns: 0 * */ IP_STATIC int iplite_sock_select_wait(Ipcom_block sem, Ip_s32 msec) { if (sem != IPCOM_BLOCK_INVALID) { IPLITE_CODE_UNLOCK(); (void)ipcom_block_wait(sem, msec); IPLITE_CODE_LOCK(); } return 0; } /* *=========================================================================== * iplite_sock_wr_select *=========================================================================== * Description: Performs write select for sockets. * Parameters: fd - first socket to consider * nfds - maximum number of socket to consider * writefds - set of sockets to check for write, will contain * all sockets that are writable on return. * sem - a valid semaphore with timeout or IP_NULL if the * sockets should just be checked. * timeout - maximum number of milliseconds to wait for * sockets to become ready. * Returns: <0 = error code * ==0 = timeout * >0 = number of ready sockets * */ IP_STATIC int iplite_sock_wr_select(int fd, int nfds, Ip_fd_set *writefds, Ipcom_block *sem, Ip_s32 msec) { int ready_socks; Iplite_socket *sock; for (;;fd++) { if (writefds == IP_NULL || fd == nfds) return iplite_sock_select_wait(sem, msec); if (IP_FD_ISSET(fd, writefds)) { Iplite_poll_entry poll; sock = iplite.sockets[fd]; if (sock == IP_NULL) return -IP_ERRNO_EBADF; if (iplite_sock_is_writable(sock)) /* Change operation to just calculate the number of ready sockets */ sem = IP_NULL; poll.sem = sem; ipcom_list_insert_last(&iplite.sockets[fd]->snd_poll_list, &poll.list); ready_socks = iplite_sock_wr_select(fd + 1, nfds, writefds, sem, msec); ipcom_list_remove(&poll.list); break; } } if (ready_socks >= 0 && iplite_sock_is_writable(sock)) return ready_socks + 1; IP_FD_CLR(fd, writefds); return ready_socks; } /* *=========================================================================== * iplite_sock_select *=========================================================================== * Description: Performs read and write select for sockets. * Parameters: fd - first socket to consider * nfds - maximum number of socket to consider * readfds - set of sockets to check for read, will contain * all sockets that are readable on return. * writefds - set of sockets to check for write, will contain * all sockets that are writable on return. * sem - a valid semaphore with timeout or IP_NULL if the * sockets should just be checked. * timeout - maximum number of milliseconds to wait for * sockets to become ready. * Returns: <0 = error code * ==0 = timeout * >0 = number of ready sockets * */ IP_STATIC int iplite_sock_select(int fd, int nfds, Ip_fd_set *readfds, Ip_fd_set *writefds, Ipcom_block *sem, Ip_s32 msec) { int ready_socks; Iplite_socket *sock; for (;;fd++) { if (readfds == IP_NULL || fd == nfds) return iplite_sock_wr_select(IPLITE_SOCKET_STARTFD, nfds, writefds, sem, msec); if (IP_FD_ISSET(fd, readfds)) { Iplite_poll_entry poll; sock = iplite.sockets[fd]; if (sock == IP_NULL) return -IP_ERRNO_EBADF; if (iplite_sock_is_readable(sock)) /* Change operation to just calculate the number of ready sockets */ sem = IP_NULL; poll.sem = sem; ipcom_list_insert_last(&sock->rcv_poll_list, &poll.list); ready_socks = iplite_sock_select(fd + 1, nfds, readfds, writefds, sem, msec); ipcom_list_remove(&poll.list); break; } } if (ready_socks >= 0 && iplite_sock_is_readable(sock)) return ready_socks + 1; IP_FD_CLR(fd, readfds); return ready_socks; } /* *=========================================================================== * iplite_sock_wakeup_poll_list *=========================================================================== * Description: Wakes all processes polling for a socket * Parameters: head - head of poll list * Returns: * */ IP_STATIC void iplite_sock_wakeup_poll_list(Ipcom_list *head) { Iplite_poll_entry *poll_entry; for (poll_entry = IPCOM_LIST_FIRST(head); poll_entry != IP_NULL; poll_entry = IPCOM_LIST_NEXT(&poll_entry->list)) ipcom_block_post(poll_entry->sem); } /* **************************************************************************** * 10 GLOBAL FUNCTIONS **************************************************************************** */ /* *=========================================================================== * iplite_get_sock *=========================================================================== * Description: Returns the socket descriptor that matches the * specified file descriptor. The caller must * call ipnet_release_sock() when done using the * socket. * Parameters: fd - a file descriptor. * Returns: The matching socket descriptor or IP_NULL if no * matching socket exists. * */ IP_GLOBAL Iplite_socket * iplite_get_sock(int fd) { Iplite_socket *sock; IPLITE_CODE_LOCK(); sock = iplite.sockets[fd]; if (sock == IP_NULL) { IPLITE_CODE_UNLOCK(); return IP_NULL; } return sock; } /* *=========================================================================== * iplite_release_sock *=========================================================================== * Description: * Parameters: sock - socket to release. * Returns: * */ IP_GLOBAL void iplite_release_sock(Iplite_socket *sock) { IPCOM_UNUSED_ARG(sock); IPLITE_CODE_UNLOCK(); } /* *=========================================================================== * iplite_is_sock_readable *=========================================================================== * Description: Checks if the specified socket is readable. * Parameters: sock - the socket to check. * lowat - minimum number of bytes that must be present * in the receive queue to cont the socket as * readable. 0 means to use the socket lo * watermask value. * Returns: IP_TRUE = socket is readable * IP_FALSE = socket is not readable * */ IP_GLOBAL Ip_bool iplite_is_sock_readable(Iplite_socket *sock, int lowat) { if (sock->ipcom.so_errno) /* Sockets with pending error is always readable */ return IP_TRUE; if (sock->ipcom.rcv_bytes >= IP_MAX(lowat, 1)) return IP_TRUE; #ifdef IPCOM_USE_TCP if (sock->tcb != IP_NULL) { if (sock->tcb->backlog_done != IP_NULL && sock->tcb->backlog_done[0] != IP_SOCKERR) /* accept() would not block */ return IP_TRUE; if (IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_READ_CLOSED) || IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_PEER_WRITE_CLOSED)) /* recv() would return EOF */ return IP_TRUE; } #endif return IP_FALSE; } /* *=========================================================================== * iplite_is_sock_writable *=========================================================================== * Description: Checks if the specified socket is writable. * Parameters: sock - the socket to check. * lowat - minimum number of bytes that must be available * in the transmit queue to cont the socket as * writable. 0 means to use the socket lo * watermark value. * Returns: IP_TRUE = socket is writable * IP_FALSE = socket is not writable * */ IP_GLOBAL Ip_bool iplite_is_sock_writable(Iplite_socket *sock, int lowat) { if (sock->ipcom.so_errno || IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_WRITE_CLOSED)) return IP_TRUE; if ((IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_CONNECTED) || sock->ipcom.type != IP_SOCK_STREAM) && sock->snd_max_bytes - sock->snd_bytes >= IP_MAX(lowat, 1)) return IP_TRUE; return IP_FALSE; } /* *=========================================================================== * iplite_is_sock_exceptional *=========================================================================== * Description: Checks if the specified socket is exceptional. * Parameters: sock - the socket to check. * lowat - unused. Added so this function has the same * signature as the other iplite_is_sock_xxx(). * Returns: IP_TRUE = socket is exceptional * IP_FALSE = socket is not exceptional. * */ IP_GLOBAL Ip_bool iplite_is_sock_exceptional(Iplite_socket *sock, int lowat) { /* exeptional socket is currently not supported */ IPCOM_UNUSED_ARG(sock); IPCOM_UNUSED_ARG(lowat); return IP_FALSE; } /* *=========================================================================== * iplite_next_ephemeral_port *=========================================================================== * Description: Returns the next ephemeral port. * Parameters: * Returns: The next free ephemeral port or 0 if no port could be found. * The port is retured in host byte order. * */ IP_GLOBAL Ip_u16 iplite_next_ephemeral_port(void) { int i; Ip_u16 firstport; Ip_u16 eport; Ip_u16 anonportmax; Ip_u16 anonportmin; anonportmax = IPLITE_CONF_SOCK_ANONPORTMAX; anonportmin = IPLITE_CONF_SOCK_ANONPORTMIN; firstport = anonportmin + ipcom_rand() % (1 + anonportmax - anonportmin); eport = firstport; do { for (i = 0; i < IPLITE_CONF_MAX_SOCKETS; i++) { if (iplite.bound_sockets[i] == IP_NULL) break; if (iplite.bound_sockets[i]->sport == eport) goto next_port; } return eport; next_port: if (eport++ == anonportmax) eport = anonportmin; } while (eport != firstport); return 0; } /* *=========================================================================== * iplite_queue_received_packet *=========================================================================== * Description: Queues a packet on the passed socket. The start field of * the packet should specify beginning of application data. * Parameters: * Returns: 0 = success, <0 = error code * */ IP_GLOBAL int iplite_queue_received_packet(Ipcom_pkt *pkt, Iplite_socket *sock) { if (sock->ipcom.rcv_bytes + pkt->end - pkt->start > (int) sock->rcv_max_bytes) { IPLITE_STATS(pkt_queue_emsgsize++); return -IP_ERRNO_EMSGSIZE; } /* Add the packet to the socket receive queue */ sock->ipcom.rcv_bytes += pkt->end - pkt->start; if (sock->rcv_head != IP_NULL) sock->rcv_tail->next = pkt; else sock->rcv_head = pkt; sock->rcv_tail = pkt; pkt->next = IP_NULL; IPCOM_PKT_TRACE(pkt, IPCOM_PKT_ID_SOCKRECVQ); iplite_sock_data_avail(sock, 0, IP_SHUT_RD); return 0; } /* *=========================================================================== * iplite_sock_data_avail *=========================================================================== * Description: Is called each time data arrives to the socket so that * ipcom_recvfrom() and ipcom_accept() can be called without * blocking. * Parameters: sock - The socket on which data arrived. * so_errno: 0 = data, -1 = close else socket errno. * Returns: 0 = success * */ IP_GLOBAL void iplite_sock_data_avail(Iplite_socket *sock, int so_errno, int how) { unsigned select_mask; if (so_errno != 0) { /* so_errno == -1 means a close event. */ if (so_errno != -1) { sock->ipcom.so_errno = so_errno; ip_assert(sock->ipcom.so_errno > 0); } if (sock->ipcom.event_cb != IP_NULL && IP_BIT_ISFALSE(sock->flags, IPLITE_SOCKET_FLAG_CLOSED)) { (void)sock->ipcom.event_cb((Ipcom_socket *)sock, IP_NULL, sock->ipcom.so_errno ? IP_SOEVENT_CB_ERRNO : IP_SOEVENT_CB_CLOSE); /* There must be no more events after the close event. */ if (sock->ipcom.so_errno == 0) sock->ipcom.event_cb = IP_NULL; } } switch (how) { case IP_SHUT_RD: select_mask = IPLITE_SELECT_RD; break; case IP_SHUT_WR: select_mask = IPLITE_SELECT_WR; break; case IP_SHUT_RDWR: select_mask = IPLITE_SELECT_RD | IPLITE_SELECT_WR; break; #ifdef IPCOM_USE_TCP case IP_SHUT_EX: select_mask = 0; break; #endif default: IP_PANIC2(); select_mask = 0; break; } /* Socket blocked in recvfrom() */ if (IP_BIT_ISSET(select_mask, IPLITE_SELECT_RD) || sock->ipcom.so_errno) { /* Wake up the process that is sleeping on the socket */ if (sock->ipcom.event_cb != IP_NULL) sock->ipcom.event_cb(&sock->ipcom, IP_NULL, IP_SOEVENT_CB_SELREAD); #ifdef IP_PORT_OSE5_disabled_code if (sock->ipcom.descs) ipcom_crt_block_post((Ipcom_socket *)sock, IP_FALSE); else #endif iplite_sock_wakeup_poll_list(&sock->rcv_poll_list); } if (IP_BIT_ISSET(select_mask, IPLITE_SELECT_WR) || sock->ipcom.so_errno) { /* Wake up the process(es) that waiting to send on the socket */ if (sock->ipcom.event_cb != IP_NULL) sock->ipcom.event_cb(&sock->ipcom, IP_NULL, IP_SOEVENT_CB_SELWRITE); #ifdef IP_PORT_OSE5_disabled_code if (sock->ipcom.descs) ipcom_crt_block_post((Ipcom_socket *)sock, IP_TRUE); else #endif if (iplite_sock_is_writable(sock)) iplite_sock_wakeup_poll_list(&sock->snd_poll_list); } } /* *=========================================================================== * iplite_do_getname *=========================================================================== * Description: Returns the socket address of the local side (if bound) or * the peer side (if connected). * Parameters: fd - Socket descriptor. * name - Will hold the socket address if the call succeeds. * namelenp - The length of the 'name' buffer, will be updated * to hold the actual size of 'name'. * local - Set to IP_TRUE if the name of local size should be * returned and IP_FALSE if the peer socket name should be returned. * Returns: 0 = success, <0 negative error code. * */ IP_GLOBAL int iplite_do_getname(Ip_fd fd, struct Ip_sockaddr *name, Ip_socklen_t *namelenp, Ip_bool local) { Iplite_socket *sock; ip_assert(name != IP_NULL); ip_assert(namelenp != IP_NULL); if (fd >= IPLITE_CONF_MAX_SOCKETS || iplite.sockets[fd] == IP_NULL) return -IP_ERRNO_EBADF; sock = iplite.sockets[fd]; #ifdef IPCOM_USE_INET if (sock->ipcom.domain == IP_AF_INET) { struct Ip_sockaddr_in *name_in; if (*namelenp < sizeof(struct Ip_sockaddr_in)) return -IP_ERRNO_ENOBUFS; *namelenp = sizeof(struct Ip_sockaddr_in); name_in = (struct Ip_sockaddr_in *)name; ipcom_memset(name_in->sin_zero, 0, sizeof(name_in->sin_zero)); name_in->sin_len = sizeof(struct Ip_sockaddr_in); name_in->sin_family = IP_AF_INET; if (local) { name_in->sin_addr.s_addr = sock->d->ip4.saddr_n; name_in->sin_port = ip_htons(sock->sport); } else { name_in->sin_addr.s_addr = sock->d->ip4.daddr_n; name_in->sin_port = ip_htons(sock->dport); } } else #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 if (sock->ipcom.domain == IP_AF_INET6) { struct Ip_sockaddr_in6 *name_in6; if (*namelenp < sizeof(struct Ip_sockaddr_in6)) return -IP_ERRNO_ENOBUFS; *namelenp = sizeof(struct Ip_sockaddr_in6); name_in6 = (struct Ip_sockaddr_in6 *) name; name_in6->sin6_len = sizeof(struct Ip_sockaddr_in6); name_in6->sin6_family = IP_AF_INET6; name_in6->sin6_scope_id = sock->d->ip6.scope_id; name_in6->sin6_flowinfo = sock->d->ip6.flowinfo_n; if (local) { name_in6->sin6_addr = sock->d->ip6.saddr; name_in6->sin6_port = ip_htons(sock->sport); } else { name_in6->sin6_addr = sock->d->ip6.daddr; name_in6->sin6_port = ip_htons(sock->dport); } } else #endif /* IPCOM_USE_INET6 */ { IP_PANIC(); return -IP_ERRNO_EPROTONOSUPPORT; } if (!local && IP_BIT_ISFALSE(sock->flags, IPLITE_SOCKET_FLAG_CONNECTED)) return -IP_ERRNO_ENOTCONN; return 0; } /* *=========================================================================== * iplite_do_bind *=========================================================================== * Description: Bind a socket to the specified address and port. * Parameters: sock - A pointer to the socket data. * name - The socket name (address and port). * namelen - The length of the name variable. * check_reuse - Set to IP_TRUE if the name should be checked * for address reuse. * Returns: 0 = success, <0 = error code * */ IP_GLOBAL int iplite_do_bind(Iplite_socket *sock, IP_CONST struct Ip_sockaddr *name, Ip_socklen_t namelen, Ip_bool check_reuse) { Ip_bool is_addr_any = IP_FALSE; int insert_index; int end_index; int err; ip_assert(!IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_BOUND)); #ifdef IPCOM_USE_INET if (sock->ipcom.domain == IP_AF_INET) { struct Ip_sockaddr_in *name_in; if (namelen < sizeof(struct Ip_sockaddr_in)) { err = -IP_ERRNO_EINVAL; goto errout; } name_in = (struct Ip_sockaddr_in*)name; /* Verify that IP address is either ANY (0) or one of my addresses. * Note: it is ok to bind to any of the multicastcast addresses * or the broadcast address. */ if (iplite_ip4_get_addr_type(IP_NULL, name_in->sin_addr.s_addr) == IPLITE_ADDR_TYPE_NOT_LOCAL) { err = -IP_ERRNO_EADDRNOTAVAIL; goto errout; } sock->d->ip4.saddr_n = name_in->sin_addr.s_addr; is_addr_any = (sock->d->ip4.saddr_n == IP_INADDR_ANY); sock->sport = ip_ntohs(name_in->sin_port); } #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 if (sock->ipcom.domain == IP_AF_INET6) { struct Ip_sockaddr_in6 *name_in6; Iplite_netif *netif; if (namelen < sizeof(struct Ip_sockaddr_in6)) { err = -IP_ERRNO_EINVAL; goto errout; } name_in6 = (struct Ip_sockaddr_in6*) name; if (iplite_ip6_is_scoped_addr(&name_in6->sin6_addr) && name_in6->sin6_scope_id != IPLITE_IF_LOOPBACK_INDEX) { netif = iplite_if_indextonetif(name_in6->sin6_scope_id); if (netif == IP_NULL) { err = -IP_ERRNO_EADDRNOTAVAIL; goto errout; } } else netif = IP_NULL; /* Verify that IP address is either iplite_in6addr_any or one of my addresses. * Note: it is ok to bind to any of the multicast addresses. */ if (iplite_ip6_get_addr_type(netif, &name_in6->sin6_addr) == IPLITE_ADDR_TYPE_NOT_LOCAL) { err = -IP_ERRNO_EADDRNOTAVAIL; goto errout; } sock->d->ip6.saddr = name_in6->sin6_addr; sock->d->ip6.scope_id = name_in6->sin6_scope_id; if (IP_IN6_IS_ADDR_UNSPECIFIED(&sock->d->ip6.saddr)) is_addr_any = IP_TRUE; else is_addr_any = IP_FALSE; sock->sport = ip_ntohs(name_in6->sin6_port); } #endif /* IPCOM_USE_INET6 */ if (sock->proto == IP_IPPROTO_TCP || sock->proto == IP_IPPROTO_UDP) { /* If port 0, pick an ephemereal port */ if (sock->sport == 0) { sock->sport = iplite_next_ephemeral_port(); if (sock->sport == 0) { err = -IP_ERRNO_EADDRINUSE; goto errout; } } if (check_reuse && sock->ipcom.type != IP_SOCK_RAW) { err = iplite_bind_check_reuse(sock); if (err < 0) goto errout; } } /* Insert socket in socket port cache (iplite.bound_sockets) */ ip_assert(iplite.bound_sockets[IPLITE_CONF_MAX_SOCKETS - 1] == IP_NULL); for (end_index = 0; iplite.bound_sockets[end_index] != IP_NULL; end_index++) /* Empty block*/; for (insert_index = 0; iplite.bound_sockets[insert_index] != IP_NULL; insert_index++) { if (iplite.bound_sockets[insert_index]->sport > sock->sport) break; if (iplite.bound_sockets[insert_index]->sport == sock->sport) { if (iplite.bound_sockets[insert_index]->ipcom.domain > sock->ipcom.domain) break; if (!is_addr_any) break; } } /* insert_index contains the index of the first entry to move */ while (end_index != insert_index) { iplite.bound_sockets[end_index] = iplite.bound_sockets[end_index - 1]; end_index--; } iplite.bound_sockets[insert_index] = sock; #ifndef IPLITE_MEM_OPT iplite.socket_last = IP_NULL; #endif IP_BIT_SET(sock->flags, IPLITE_SOCKET_FLAG_BOUND); return 0; errout: if (sock != IP_NULL) sock->sport = 0; return err; } /* *=========================================================================== * iplite_do_socket *=========================================================================== * Description: Creates a new socket. * Parameters: domain - The address domain. * type - The socket type. * protcol - The protocol the stack must use for communication * when using this socket. * Returns: 0 >= The new socket descriptor. * 0 < error, error code can be read from ipcom_errno. * */ IP_GLOBAL Ip_fd iplite_do_socket(int domain, int type, int protocol, Ip_bool user_context) { Ip_fd fd; int i; Iplite_socket *sock = IP_NULL; Ip_size_t size, offset; /* Raw IP socket. */ if (type == IP_SOCK_RAW) goto type_ok; #ifdef IPCOM_USE_PACKET if (domain == IP_AF_PACKET && (type == IP_SOCK_DGRAM || type == IP_SOCK_RAW)) goto type_ok; #endif if (domain == IP_AF_INET || domain == IP_AF_INET6) { if (type == IP_SOCK_DGRAM) { /* UDP socket. */ if (protocol == 0) protocol = IP_IPPROTO_UDP; if (protocol == IP_IPPROTO_UDP) goto type_ok; } #ifdef IPCOM_USE_TCP if (type == IP_SOCK_STREAM) { if (protocol == 0) protocol = IP_IPPROTO_TCP; if (protocol == IP_IPPROTO_TCP) goto type_ok; } #endif /* IPCOM_USE_TCP */ } /* Bad type or protocol. */ SOCK_ERROUT(IP_ERRNO_EPROTONOSUPPORT); type_ok: #ifdef IPCOM_USE_INET if (domain == IP_AF_INET) goto domain_ok; #endif #ifdef IPCOM_USE_INET6 if (domain == IP_AF_INET6) goto domain_ok; #endif #ifdef IPIPSEC2 if (domain == IP_AF_KEY) goto domain_ok; #endif #ifdef IPCOM_USE_PACKET if (domain == IP_AF_PACKET) goto domain_ok; #endif /* Invalid domain. */ SOCK_ERROUT(IP_ERRNO_EAFNOSUPPORT); /* Find an available socket descriptor slot. */ domain_ok: for (i = 1; i <= IPLITE_CONF_MAX_SOCKETS - IPLITE_SOCKET_STARTFD; i++) { Ip_size_t max_hdrspace; fd = (iplite.last_fd - IPLITE_SOCKET_STARTFD + i) % (IPLITE_CONF_MAX_SOCKETS - IPLITE_SOCKET_STARTFD) + IPLITE_SOCKET_STARTFD; if (iplite.sockets[fd] != IP_NULL) continue; max_hdrspace = 0; size = sizeof(Iplite_socket); #ifdef IPCOM_USE_INET if (domain == IP_AF_INET) { max_hdrspace = IPLITE_IP_HDR_SIZE; size += sizeof(Iplite_ip4_socket); } #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 if (domain == IP_AF_INET6) { max_hdrspace = IPLITE_IP6_HDR_SIZE; size += sizeof(Iplite_ip6_socket); } #endif /* IPCOM_USE_INET6 */ #ifdef IPCOM_USE_TCP if (protocol == IP_IPPROTO_TCP) size += sizeof(Iplite_tcp_tcb); #endif /* IPCOM_USE_TCP */ #if defined(IP_PORT_VXWORKS) && defined(IPCOM_USE_SOCK_COMPAT) size += ipcom_windnet_socklib_sock_size(); #endif #ifdef IPIPSEC2 if (domain == IP_AF_KEY) max_hdrspace = 8; /* to get 64 bit alignment. */ #endif /* IPIPSEC2 */ sock = iplite_kmalloc(size); if (sock == IP_NULL) SOCK_ERROUT(IP_ERRNO_ENOMEM); /* Init socket */ ipcom_list_init(&sock->rcv_poll_list); ipcom_list_init(&sock->snd_poll_list); sock->ref_count = 1; sock->ipcom.fd = (Ip_fd)fd; sock->ipcom.domain = (Ip_u16)domain; sock->proto = (Ip_u16)protocol; sock->ipcom.type = (Ip_u16)type; sock->max_hdrspace = (Ip_u16)max_hdrspace; sock->uni_hop_limit = IPLITE_CONF_SOCK_BASE_HOP_LIMIT; sock->multi_hop_limit = 1; sock->ipcom.pid = ipcom_getpid(); sock->snd_max_bytes = IPLITE_CONF_SOCK_DEFAULT_SEND_BUF_SIZE; sock->rcv_max_bytes = IPLITE_CONF_SOCK_DEFAULT_RECV_BUF_SIZE; IP_BIT_SET(sock->flags, IPLITE_SOCKET_FLAG_LOOP_MULTICAST); sock->transport_output = iplite_raw_output; offset = sizeof(Iplite_socket); #if defined(IP_PORT_VXWORKS) && defined(IPCOM_USE_SOCK_COMPAT) if (size > offset) { sock->select_ctx = (void *)((Ip_u8 *)sock + offset); offset += ipcom_windnet_socklib_sock_size(); } #endif if (size > offset) sock->d = (Iplite_socket_domain *) ((Ip_u8 *)sock + offset); if (domain == IP_AF_INET || domain == IP_AF_INET6) { switch (protocol) { case IP_IPPROTO_UDP: sock->transport_output = iplite_udp_output; sock->max_hdrspace += IPLITE_UDP_HDR_SIZE; IP_BIT_SET(sock->flags, IPLITE_SOCKET_FLAG_ADDCHECKSUM); sock->chk_offset = 6; break; #ifdef IPCOM_USE_TCP case IP_IPPROTO_TCP: sock->tcb = (Iplite_tcp_tcb *) ((Ip_u8 *)sock + offset); sock->d = (Iplite_socket_domain *) (sock->tcb + 1); iplite_tcp_sock_init(sock); if (user_context) /* Socket create in user-context, add an extra reference for the TCP module */ IPLITE_SOCK_ADD_REF(sock); /* else: the TCP module will add an reference for the user when returning the socket in accept() */ sock->max_hdrspace += IPLITE_TCP_HDR_SIZE; IP_BIT_SET(sock->flags, IPLITE_SOCKET_FLAG_ADDCHECKSUM); sock->chk_offset = 16; break; #endif /* IPCOM_USE_TCP */ case IP_IPPROTO_ICMPV6: IP_BIT_SET(sock->flags, IPLITE_SOCKET_FLAG_ADDCHECKSUM); sock->chk_offset = 2; break; default: break; } } /* Success. */ iplite.last_fd = fd; iplite.sockets[fd] = sock; #ifdef IPCOM_USE_PACKET if (sock->ipcom.domain == IP_AF_PACKET) { sock->transport_output = iplite_packet_output; iplite.num_packet_sock++; } #endif return fd; } /* No free socket slot found. */ (void)ipcom_errno_set(IP_ERRNO_EMFILE); errout: if (sock != IP_NULL) iplite_kfree(sock); return IP_SOCKERR; } /* *=========================================================================== * iplite_sock_drop_all_multicast_memberships *=========================================================================== * Description: Drops all multicast memberships this socket has. * Parameters: sock - a socket. * netif - IP_NULL to dropp all multicast memberships or the * interface for which all multicast memberships should be dropped. * Returns: Number of multicast memberships that were dropped. */ IP_GLOBAL int iplite_sock_drop_all_multicast_memberships(Iplite_socket *sock, Iplite_netif *netif, Ip_bool drop_ip4, Ip_bool drop_ip6) { int drop_count = 0; #ifdef IPCOM_USE_INET if (drop_ip4 && sock->ipcom.domain == IP_AF_INET) { struct Iplite_ip4_socket_multicast_data **mlt_it; struct Iplite_ip4_socket_multicast_data *mlt; mlt_it = &sock->d->ip4.multicast_addr_list; while (*mlt_it != IP_NULL) { mlt = *mlt_it; if (netif == IP_NULL || mlt->netif == netif) { drop_count++; *mlt_it = mlt->next; (void) iplite_ip4_remove_addr(mlt->netif, mlt->addr.s_addr); iplite_kfree(mlt); } else mlt_it = &mlt->next; } } #else (void)drop_ip4; #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 if (drop_ip6 && sock->ipcom.domain == IP_AF_INET6) { struct Iplite_ip6_socket_multicast_data **mlt_it; struct Iplite_ip6_socket_multicast_data *mlt; mlt_it = &sock->d->ip6.multicast_addr_list; while (*mlt_it != IP_NULL) { mlt = *mlt_it; if (netif == IP_NULL || mlt->netif == netif) { drop_count++; *mlt_it = mlt->next; (void) iplite_ip6_remove_addr(mlt->netif, &mlt->addr); iplite_kfree(mlt); } else mlt_it = &mlt->next; } } #else (void)drop_ip6; #endif /* IPCOM_USE_INET6 */ return drop_count; } /* *=========================================================================== * iplite_do_socketselect *=========================================================================== * Description: Checks if any of the sockets in a set is ready for reading, * writing or have an exceptional condition. * Parameters: ndfs - The first 'ndfs' descriptors are checked in each set * i.e., the descriptors from 0 through nfds-1 in the descriptor * sets are examined. * readfds - The set of descriptors that should be checked for * reading. * writefds - The set of descriptors that should be checked for * writing (sockets are always writeable) * exceptfds - The set of descriptors that should be checked for * exceptions (not supported, must be IP_NULL). * Returns: The total number of ready descriptors in each set. * */ IP_GLOBAL int iplite_do_socketselect(int nfds, Ip_fd_set *readfds, Ip_fd_set *writefds, Ip_fd_set *exceptfds, struct Ip_timeval *timeout) { int num; Ip_s32 msec; Ipcom_block select_event = IPCOM_BLOCK_INVALID; (void)exceptfds; ip_assert2(nfds > IPLITE_SOCKET_STARTFD); IPLITE_CODE_ISLOCKED_ASSERT(); msec = iplite_timeval_to_msec(timeout); if (msec < -1) SOCK_ERROUT(IP_ERRNO_EINVAL); if (msec != 0) { if (ipcom_block_create(&select_event) != IPCOM_SUCCESS) SOCK_ERROUT(IP_ERRNO_ENOMEM); } num = iplite_sock_select(IPLITE_SOCKET_STARTFD, nfds, readfds, writefds, select_event, msec); if (select_event != IPCOM_BLOCK_INVALID) (void)ipcom_block_delete(&select_event); if (num < 0) SOCK_ERROUT(-num); return num; errout: return IP_SOCKERR; } /* *=========================================================================== * iplite_do_close *=========================================================================== * Description: Closes the socket (tear down connections). * Parameters: fd - A valid socket desriptor. * Returns: 0 = success */ IP_GLOBAL int iplite_do_close(Iplite_socket *sock) { ip_assert(sock != IP_NULL); IPLITE_SOCK_ADD_REF(sock); (void) iplite_do_shutdown(sock->ipcom.fd, IP_SHUT_RDWR); #ifdef IPCOM_USE_TCP if (sock->tcb != IP_NULL) (void)iplite_tcp_close(sock); #endif /* IPCOM_USE_TCP */ --sock->ref_count; IPLITE_SOCK_DEL_REF(sock); return 0; } /* *=========================================================================== * iplite_sock_free *=========================================================================== * Description: Frees all memory allocated by a socket. * Parameters: sock - The socket to free. * Returns: */ IP_PUBLIC void iplite_sock_free(Iplite_socket *sock) { Ipcom_pkt *pkt; ip_assert(sock != IP_NULL); ip_assert(sock->ipcom.fd < IPLITE_CONF_MAX_SOCKETS); ip_assert(sock->ipcom.fd >= IPLITE_SOCKET_STARTFD); ip_assert(iplite.sockets[sock->ipcom.fd] == sock); if (sock->ipcom.event_cb != IP_NULL) (void)sock->ipcom.event_cb((Ipcom_socket *)sock, IP_NULL, IP_SOEVENT_CB_CLOSE); iplite_sock_unbind(sock); (void) iplite_sock_drop_all_multicast_memberships(sock, IP_NULL, IP_TRUE, IP_TRUE); #ifdef IPCOM_USE_TCP if (sock->tcb != IP_NULL) iplite_tcp_sock_free(sock); #endif /* IPCOM_USE_TCP */ /* Free any pending packets */ while (sock->rcv_head != IP_NULL) { pkt = sock->rcv_head; sock->rcv_head = pkt->next; ipcom_pkt_free(pkt); } #ifndef IPLITE_MEM_OPT iplite.socket_last = IP_NULL; #endif iplite.sockets[sock->ipcom.fd] = IP_NULL; #ifdef IPCOM_USE_PACKET if (sock->ipcom.domain == IP_AF_PACKET) iplite.num_packet_sock--; #endif iplite_kfree(sock); } /* *=========================================================================== * iplite_ip4_socklookup *=========================================================================== * Description: Returns the IPv4 socket matching the parameters. * Parameters: proto - The protocol the returned socket should be using. * local_addr_ptr - The IPv4 address in network order of the * local address to match or IP_NULL if any address should match. * local_port - The source port in HOST byte order or 0 if any * source port should match. * peer_addr_n - The IPv4 address in network order of the * peer address to match or IP_NULL if any address should match. * peer_port - The destination port in HOST byte order or 0 if any * destination port should match. * Returns: A pointer to the matching socket or IP_NULL if no socket match. * */ #ifdef IPCOM_USE_INET IP_GLOBAL Iplite_socket * iplite_ip4_socklookup(int proto, Ip_u32 *local_addr_ptr, Ip_u16 local_port, Ip_u32 *peer_addr_ptr, Ip_u16 peer_port, int *next) { int i; Ip_u32 local_addr_n = (local_addr_ptr == IP_NULL ? IP_INADDR_ANY : *local_addr_ptr); Ip_u32 peer_addr_n = (peer_addr_ptr == IP_NULL ? IP_INADDR_ANY : *peer_addr_ptr); Iplite_socket *sock; #ifndef IPLITE_MEM_OPT if (next == IP_NULL && iplite.socket_last != IP_NULL && iplite.socket_last->ipcom.domain == IP_AF_INET && iplite.socket_last->proto == proto && iplite.socket_last->sport == local_port && iplite.socket_last->dport == peer_port && local_addr_n == iplite.socket_last->d->ip4.saddr_n && peer_addr_n == iplite.socket_last->d->ip4.daddr_n) { /* The socket that matched the last time also matched this packet */ return iplite.socket_last; } #endif for (i = (next == IP_NULL ? 0 : *next); i < IPLITE_CONF_MAX_SOCKETS; i++) { if (proto == IP_IPPROTO_TCP || proto == IP_IPPROTO_UDP) sock = iplite.bound_sockets[i]; else sock = iplite.sockets[i]; if (sock == IP_NULL) continue; if (sock->ipcom.domain != IP_AF_INET) continue; if (sock->proto && sock->proto != proto) continue; if (proto == IP_IPPROTO_TCP || proto == IP_IPPROTO_UDP) { if (local_port != 0 && sock->sport != local_port) continue; if (peer_port != 0 && sock->dport != 0 && sock->dport != peer_port) continue; if (local_addr_n != IP_INADDR_ANY && sock->d->ip4.saddr_n != IP_INADDR_ANY && local_addr_n != sock->d->ip4.saddr_n) continue; if (peer_addr_n != IP_INADDR_ANY && sock->d->ip4.daddr_n != IP_INADDR_ANY && peer_addr_n != sock->d->ip4.daddr_n) continue; } #ifndef IPLITE_MEM_OPT if (!(sock->flags & (IPLITE_SOCKET_FLAG_REUSEADDR | IPLITE_SOCKET_FLAG_REUSEPORT))) iplite.socket_last = sock; #endif if (next != IP_NULL) *next = i + 1; return sock; } return IP_NULL; } #endif /* IPCOM_USE_INET */ /* *=========================================================================== * iplite_ip6_socklookup *=========================================================================== * Description: Returns the IPv6 socket matching the parameters. Source is * always this host and the destination is the peer. * Parameters: proto - The protocol the returned socket should be using. * src_addr - The IPv6 source address in NETWORK byte order or * the undefined address (all zeros) if all addresses should * match. IP_NULL is will also be interpreted as the undefined * address. * local_port - The source port in HOST byte order or 0 if any * source port should match. * dst_addr - The IPv6 destination address in NETWORK byte order * or the undefined address (all zeros) if all addresses should * match. IP_NULL is will also be interpreted as the undefined * address. * peer_port - The destination port in HOST byte order or 0 if any * destination port should match. * next - Index of the next socket to test. Set to IP_NULL if * only the first match is interesting. * Returns: A pointer to the matching socket or IP_NULL if no socket match. * */ #ifdef IPCOM_USE_INET6 IP_GLOBAL Iplite_socket* iplite_ip6_socklookup(int proto, struct Ip_in6_addr *local_addr, Ip_u16 local_port, struct Ip_in6_addr *peer_addr, Ip_u16 peer_port, int *next) { int i; Iplite_socket *sock; if (local_addr != IP_NULL && IP_IN6_IS_ADDR_UNSPECIFIED(local_addr)) local_addr = IP_NULL; if (peer_addr != IP_NULL && IP_IN6_IS_ADDR_UNSPECIFIED(peer_addr)) peer_addr = IP_NULL; #ifndef IPLITE_MEM_OPT if (next == IP_NULL && iplite.socket_last != IP_NULL && iplite.socket_last->ipcom.domain == IP_AF_INET6 && iplite.socket_last->proto == proto && iplite.socket_last->sport == local_port && iplite.socket_last->dport == peer_port && local_addr != IP_NULL && IP_IN6_ARE_ADDR_EQUAL(local_addr, &iplite.socket_last->d->ip6.saddr) && peer_addr != IP_NULL && IP_IN6_ARE_ADDR_EQUAL(peer_addr, &iplite.socket_last->d->ip6.daddr)) { /* The socket that matched the last time also matched this packet */ return iplite.socket_last; } #endif for (i = (next == IP_NULL ? 0 : *next); i < IPLITE_CONF_MAX_SOCKETS; i++) { if (proto == IP_IPPROTO_TCP || proto == IP_IPPROTO_UDP) sock = iplite.bound_sockets[i]; else sock = iplite.sockets[i]; if (sock == IP_NULL) continue; if (sock->ipcom.domain != IP_AF_INET6) continue; if (sock->proto && sock->proto != proto) continue; if (proto == IP_IPPROTO_TCP || proto == IP_IPPROTO_UDP) { if (local_port != 0 && sock->sport != local_port) continue; if (peer_port != 0 && sock->dport != 0 && sock->dport != peer_port) continue; if (local_addr != IP_NULL && !IP_IN6_IS_ADDR_UNSPECIFIED(&sock->d->ip6.saddr) && !IP_IN6_ARE_ADDR_EQUAL(local_addr, &sock->d->ip6.saddr)) continue; if (peer_addr != IP_NULL && !IP_IN6_IS_ADDR_UNSPECIFIED(&sock->d->ip6.daddr) && !IP_IN6_ARE_ADDR_EQUAL(peer_addr, &sock->d->ip6.daddr)) continue; } #ifndef IPLITE_MEM_OPT if (!(sock->flags & (IPLITE_SOCKET_FLAG_REUSEADDR | IPLITE_SOCKET_FLAG_REUSEPORT))) iplite.socket_last = sock; #endif if (next != IP_NULL) *next = i + 1; return sock; } return IP_NULL; } #endif /* IPCOM_USE_INET6 */ /* *=========================================================================== * iplite_sock_unbind *=========================================================================== * Description: Unbind the specific UDP or TCP socket. * Parameters: sock - The socket to unbind. * Returns: * */ IP_GLOBAL void iplite_sock_unbind(Iplite_socket *sock) { int r; /* The index of the socket to remove */ if (!IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_BOUND)) return; /* Unbind the socket */ for (r = 0; r < IPLITE_CONF_MAX_SOCKETS && iplite.bound_sockets[r] != IP_NULL; r++) { if (iplite.bound_sockets[r] == sock) { int i; for (i = r; i < IPLITE_CONF_MAX_SOCKETS && iplite.bound_sockets[i] != IP_NULL; i++) { if (i + 1 < IPLITE_CONF_MAX_SOCKETS) iplite.bound_sockets[i] = iplite.bound_sockets[i + 1]; else iplite.bound_sockets[i] = IP_NULL; } break; } } ip_assert(r < IPLITE_CONF_MAX_SOCKETS); sock->sport = 0; IP_BIT_CLR(sock->flags, IPLITE_SOCKET_FLAG_BOUND); } /* **************************************************************************** * 11 PUBLIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipcom_socket *=========================================================================== * Description: Creates a new socket. * Parameters: domain - The address domain for this socket (IP_AF_xxx). * type - The type of socket (IPLITE_SOCK_xxx). * protocol - The protocol to use or 0 to select the default * protocol for the selected socket type. * Returns: >=0 the socket descriptor, -1 = error. * */ IP_PUBLIC Ip_fd ipcom_socket(int domain, int type, int protocol) { Ip_fd fd; /* Block all ipcom_socket() calls until the stack is configured */ while (IP_BIT_ISFALSE(iplite_flags, IPLITE_FLAG_CONFIGURED)) ipcom_millisleep(10); IPLITE_CODE_LOCK(); fd = iplite_do_socket(domain, type, protocol, IP_TRUE); IPLITE_CODE_UNLOCK(); return fd; } /* *=========================================================================== * ipcom_shutdown *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC int ipcom_shutdown(Ip_fd fd, int how) { int ret; IPLITE_CODE_LOCK(); ret = iplite_do_shutdown(fd, how); IPLITE_CODE_UNLOCK(); return ret; } /* *=========================================================================== * ipcom_socketclose *=========================================================================== * Description: Closes a socket. * Parameters: fd - The socket to close. * Returns: 0= success, -1 = error * */ IP_PUBLIC int ipcom_socketclose(Ip_fd fd) { int ret; Iplite_socket *sock; ip_assert2(fd >= IPLITE_SOCKET_STARTFD); ip_assert2(fd < IPLITE_CONF_MAX_SOCKETS); ip_assert2(iplite.sockets[fd] != IP_NULL); IPLITE_CODE_LOCK(); if (fd < IPLITE_SOCKET_STARTFD || fd >= IPLITE_CONF_MAX_SOCKETS || IP_NULL == (sock = iplite.sockets[fd]) || IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_CLOSED)) { ret = -IP_ERRNO_EBADF; goto errout; } IP_BIT_SET(sock->flags, IPLITE_SOCKET_FLAG_CLOSED); (void)iplite_do_close(sock); IPLITE_CODE_UNLOCK(); return 0; errout: ip_assert(ret < 0); (void)ipcom_errno_set(-ret); IPLITE_CODE_UNLOCK(); return IP_SOCKERR; } /* *=========================================================================== * ipcom_socketselect *=========================================================================== * Description: Checks if any of the sockets in a set is ready for reading, * writing or have an exceptional condition. * Parameters: ndfs - The first 'ndfs' descriptors are checked in each set * i.e., the descriptors from 0 through nfds-1 in the descriptor * sets are examined. * readfds - The set of descriptors that should be checked for * reading. * writefds - The set of descriptors that should be checked for * writing (sockets are always writeable) * exceptfds - The set of descriptors that should be checked for * exceptions (not supported, must be IP_NULL). * Returns: The total number of ready descriptors in each set. * */ IP_PUBLIC int ipcom_socketselect(int nfds, Ip_fd_set *readfds, Ip_fd_set *writefds, Ip_fd_set *exceptfds, struct Ip_timeval *timeout) { int num; IPLITE_CODE_LOCK(); num = iplite_do_socketselect(nfds, readfds, writefds, exceptfds, timeout); IPLITE_CODE_UNLOCK(); return num; } #ifdef IPCOM_USE_TCP /* *=========================================================================== * ipcom_listen *=========================================================================== * Description: Places a socket a state where it is listening for incoming * connection(s). * Parameters: fd - a bound, unconnected socket. * backlog - The maximum length of the queue of pending * connections. Use IPLITE_SOMAXCONN to get a maximum reasonable * value * Returns: 0 = success, -1 = error. * */ IP_PUBLIC int ipcom_listen(Ip_fd fd, int backlog) { Iplite_socket *sock; int err; ip_assert2(fd >= IPLITE_SOCKET_STARTFD); ip_assert2(fd < IPLITE_CONF_MAX_SOCKETS); ip_assert2(iplite.sockets[fd] != IP_NULL); IPLITE_CODE_LOCK(); sock = iplite.sockets[fd]; if (sock == IP_NULL) SOCK_ERROUT(IP_ERRNO_EBADF); err = iplite_tcp_listen(sock, backlog); if (err < 0) SOCK_ERROUT(-err); IPLITE_CODE_UNLOCK(); return err; errout: IPLITE_CODE_UNLOCK(); return IP_SOCKERR; } /* *=========================================================================== * ipcom_accept *=========================================================================== * Description: Accepts an incoming connection attempt on a socket. * Parameters: fd - A socket that is in listen state (ipcom_listen()) has * been called. * name - Will hold the address of the peer (set to IP_NULL if * that information is not wanted). * addrlen - Must be initialized to the length of the 'name' * buffer, will hold the actual 'name' length upon return. * Set to IP_NULL if 'name' is IP_NULL. * Returns: 0>= the socket to use for communication (success), -1 = error. * */ IP_PUBLIC int ipcom_accept(Ip_fd fd, struct Ip_sockaddr *name, Ip_socklen_t *namelen) { Iplite_socket *sock; Ip_fd new_fd; ip_assert2(fd >= IPLITE_SOCKET_STARTFD); ip_assert2(fd < IPLITE_CONF_MAX_SOCKETS); ip_assert2(iplite.sockets[fd] != IP_NULL); IPLITE_CODE_LOCK(); sock = iplite.sockets[fd]; if (sock == IP_NULL) SOCK_ERROUT(IP_ERRNO_EBADF); if (IP_BIT_ISFALSE(sock->flags, IPLITE_SOCKET_FLAG_NONBLOCKING)) { Ip_fd_set rd_set; IP_FD_ZERO(&rd_set); IP_FD_SET(fd, &rd_set); if (iplite_do_socketselect(fd + 1, &rd_set, IP_NULL, IP_NULL, IP_NULL) < 0) /* errno already set */ goto errout; } new_fd = iplite_tcp_accept(sock, name, namelen); if (new_fd < 0) SOCK_ERROUT(-new_fd); IPLITE_CODE_UNLOCK(); return new_fd; errout: IPLITE_CODE_UNLOCK(); return IP_SOCKERR; } #endif /* IPCOM_USE_TCP */ /* *=========================================================================== * ipcom_bind *=========================================================================== * Description: Assigns a name to a unnamed socket. * Parameters: fd - The socket to name. * name - The socket name. * namelen - The length of the name buffer. * Returns: 0 = success, -1 = error * */ IP_PUBLIC int ipcom_bind(Ip_fd fd, IP_CONST struct Ip_sockaddr *name, Ip_socklen_t namelen) { Iplite_socket *sock; int err; ip_assert2(fd >= IPLITE_SOCKET_STARTFD); ip_assert2(fd < IPLITE_CONF_MAX_SOCKETS); ip_assert2(iplite.sockets[fd] != IP_NULL); ip_assert2(name != IP_NULL); ip_assert2(namelen > 0); IPLITE_CODE_LOCK(); sock = iplite.sockets[fd]; if (sock == IP_NULL) SOCK_ERROUT(IP_ERRNO_EBADF); #ifdef IPLITE_COMPAT_OLDSOCK if (name->sa_family == 0 && name->sa_len < IP_AF_MAX) ((struct Ip_sockaddr *)name)->sa_family = name->sa_len; ((struct Ip_sockaddr *)name)->sa_len = (Ip_u8) namelen; #endif ip_assert(name->sa_len > 0); if (name->sa_family != sock->ipcom.domain) /* Wrong domain in socket name */ SOCK_ERROUT(IP_ERRNO_EINVAL); if (IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_BOUND)) /* Socket already bound. */ SOCK_ERROUT(IP_ERRNO_EINVAL); err = iplite_do_bind(sock, name, namelen, IP_TRUE); if (err < 0) SOCK_ERROUT(-err); /* success. */ IPLITE_CODE_UNLOCK(); return 0; errout: IPLITE_CODE_UNLOCK(); return (int)IP_SOCKERR; } /* *=========================================================================== * ipcom_connect *=========================================================================== * Description: Connects the socket to a specified peer. * Parameters: fd - The socket to connect. * to - The address of the peer. * tolen - The length of the to buffer. * Returns: 0= connection was successful, -1 = error. * */ IP_PUBLIC int ipcom_connect(Ip_fd fd, IP_CONST struct Ip_sockaddr *to, Ip_socklen_t tolen) { Iplite_socket *sock = IP_NULL; int retval; struct Ip_sockaddr *name = IP_NULL; #ifdef IPCOM_USE_INET struct Ip_sockaddr_in in_name; #endif #ifdef IPCOM_USE_INET6 struct Ip_sockaddr_in6 in6_name; #endif Iplite_netif *netif; ip_assert2(fd >= IPLITE_SOCKET_STARTFD); ip_assert2(fd < IPLITE_CONF_MAX_SOCKETS); ip_assert2(iplite.sockets[fd] != IP_NULL); IPLITE_CODE_LOCK(); if (to == IP_NULL) SOCK_ERROUT(IP_ERRNO_EINVAL); sock = iplite.sockets[fd]; if (sock == IP_NULL) SOCK_ERROUT(IP_ERRNO_EBADF); if (sock->ipcom.so_errno != 0) SOCK_ERROUT(sock->ipcom.so_errno); if (sock->ipcom.type == IP_SOCK_STREAM && IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_CONNECTED)) SOCK_ERROUT(IP_ERRNO_EISCONN); IP_BIT_CLR(sock->flags, IPLITE_SOCKET_FLAG_CONNECTED); #ifdef IPLITE_COMPAT_OLDSOCK if (to->sa_family == 0 && to->sa_len < IP_AF_MAX && to->sa_len != tolen) ((struct Ip_sockaddr *)to)->sa_family = to->sa_len; ((struct Ip_sockaddr *)to)->sa_len = (Ip_u8) tolen; #endif /* IPLITE_COMPAT_OLDSOCK */ if (to->sa_family != sock->ipcom.domain && to->sa_family != IP_AF_UNSPEC) SOCK_ERROUT(IP_ERRNO_EAFNOSUPPORT); /* Determine the destination address */ #ifdef IPCOM_USE_INET if (to->sa_family == IP_AF_INET) { /* Setup the IPv4 destination (peer) address */ struct Ip_sockaddr_in *in_to; struct Ip_in_addr src_addr; if (tolen < sizeof(struct Ip_sockaddr_in)) SOCK_ERROUT(IP_ERRNO_EINVAL); in_to = (struct Ip_sockaddr_in*) to; if (in_to->sin_addr.s_addr == IP_INADDR_ANY) goto disconnect; sock->d->ip4.daddr_n = in_to->sin_addr.s_addr; sock->dport = ip_ntohs(in_to->sin_port); if (sock->dport == 0) SOCK_ERROUT(IP_ERRNO_EINVAL); netif = iplite_ip4_get_netif(in_to->sin_addr.s_addr, IP_NULL); if (netif == IP_NULL) SOCK_ERROUT(IP_ERRNO_EHOSTUNREACH); if (IP_BIT_ISFALSE(sock->flags, IPLITE_SOCKET_FLAG_BOUND) || sock->d->ip4.saddr_n == IP_INADDR_ANY) { if (sock->d->ip4.saddr_n == IP_INADDR_ANY) { /* No address specified when the socket was bound, need to rebind */ in_name.sin_port = ip_htons(sock->sport); iplite_sock_unbind(sock); } else /* Not bound before, bind to an ephemeral port */ in_name.sin_port = 0; in_name.sin_family = IP_AF_INET; in_name.sin_len = sizeof(in_name); src_addr.s_addr = iplite_ip4_get_src_addr(netif, in_to->sin_addr.s_addr); if (src_addr.s_addr == 0) SOCK_ERROUT(IP_ERRNO_EHOSTUNREACH); in_name.sin_addr.s_addr = src_addr.s_addr; name = (struct Ip_sockaddr *) &in_name; } /* else: already bound to an address != IP_INADDR_ANY */ } else #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 if (to->sa_family == IP_AF_INET6) { /* Setup the IPv6 destination (peer) address */ struct Ip_sockaddr_in6 *in6_to; IP_CONST struct Ip_in6_addr* src_addr; Ip_u32 flowinfo = 0; if (tolen < sizeof(struct Ip_sockaddr_in6)) SOCK_ERROUT(IP_ERRNO_EINVAL); in6_to = (struct Ip_sockaddr_in6 *)to; if (IP_IN6_IS_ADDR_UNSPECIFIED(&in6_to->sin6_addr)) goto disconnect; flowinfo = in6_to->sin6_flowinfo; if (ip_ntohl(flowinfo) & 0xff000000) SOCK_ERROUT(IP_ERRNO_EINVAL); netif = iplite_ip6_get_netif(in6_to->sin6_scope_id, &in6_to->sin6_addr, IP_NULL, IP_TRUE); if (netif == IP_NULL) SOCK_ERROUT(IP_ERRNO_EHOSTUNREACH); sock->d->ip6.daddr = in6_to->sin6_addr; sock->d->ip6.flowinfo_n = flowinfo; sock->dport = ip_ntohs(in6_to->sin6_port); sock->d->ip6.scope_id = in6_to->sin6_scope_id; if (IP_BIT_ISFALSE(sock->flags, IPLITE_SOCKET_FLAG_BOUND) || IP_IN6_IS_ADDR_UNSPECIFIED(&sock->d->ip6.saddr)) { if (IP_IN6_IS_ADDR_UNSPECIFIED(&sock->d->ip6.saddr)) { /* No address specified when the socket was bound, need to rebind */ in6_name.sin6_port = ip_htons(sock->sport); iplite_sock_unbind(sock); } else /* Not bound before, bind to an ephemeral port */ in6_name.sin6_port = 0; in6_name.sin6_family = IP_AF_INET6; in6_name.sin6_len = sizeof(in6_name); src_addr = iplite_ip6_get_src_addr(netif, &sock->d->ip6.daddr, 0); if (src_addr == IP_NULL) SOCK_ERROUT(IP_ERRNO_EHOSTUNREACH); IPLITE_IP6_SET_ADDR(&in6_name.sin6_addr, src_addr); in6_name.sin6_flowinfo = flowinfo; in6_name.sin6_scope_id = in6_to->sin6_scope_id; name = (struct Ip_sockaddr *) &in6_name; } /* else: already bound to an address != unspecified address */ } else #endif /* IPCOM_USE_INET6 */ if (to->sa_family == IP_AF_UNSPEC) { disconnect: #ifdef IPCOM_USE_TCP if (sock->tcb) SOCK_ERROUT(IP_ERRNO_EAFNOSUPPORT); #endif #ifdef IPCOM_USE_INET if (sock->ipcom.domain == IP_AF_INET) sock->d->ip4.daddr_n = IP_INADDR_ANY; #endif #ifdef IPCOM_USE_INET6 if (sock->ipcom.domain == IP_AF_INET6) sock->d->ip6.daddr = ip_in6addr_any; #endif IP_BIT_CLR(sock->flags, IPLITE_SOCKET_FLAG_CONNECTED); sock->dport = 0; goto done; } else SOCK_ERROUT(IP_ERRNO_EAFNOSUPPORT); /* Make sure that the socket is bound so packet sent from the peer socket is accepted */ if (IP_BIT_ISFALSE(sock->flags, IPLITE_SOCKET_FLAG_BOUND)) { retval = iplite_do_bind(sock, name, name->sa_len, IP_TRUE); /*lint !e644 */ if (retval < 0) SOCK_ERROUT(-retval); } #ifdef IPCOM_USE_TCP if (sock->tcb != IP_NULL) { /* The socket is a TCP socket, do the TCP connect */ if (IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_CONNECTING)) /* Asynchronous connect is still not finished */ SOCK_ERROUT(IP_ERRNO_EALREADY); retval = iplite_tcp_connect(sock); /*lint !e644 */ if (retval < 0) SOCK_ERROUT(-retval); } else #endif /* IPCOM_USE_TCP */ { IP_BIT_SET(sock->flags, IPLITE_SOCKET_FLAG_CONNECTED); } done: /* success. */ IPLITE_CODE_UNLOCK(); return 0; errout: if (sock && (sock->ipcom.type != IP_SOCK_STREAM || ipcom_errno != IP_ERRNO_EISCONN)) IP_BIT_CLR(sock->flags, IPLITE_SOCKET_FLAG_CONNECTED); IPLITE_CODE_UNLOCK(); return (int)IP_SOCKERR; } /* *=========================================================================== * ipcom_socketwrite *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_ssize_t ipcom_socketwrite(Ip_fd sock, IP_CONST void *buf, Ip_size_t nbytes) { return ipcom_sendto(sock, buf, nbytes, 0, IP_NULL, 0); } /* *=========================================================================== * ipcom_send *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_ssize_t ipcom_send(Ip_fd sock, IP_CONST void *msg, Ip_size_t len, int flags) { return ipcom_sendto(sock, msg, len, flags, IP_NULL, IP_NULL); } /* *=========================================================================== * ipcom_sendto *=========================================================================== * Description: Sends a packet to the address to which the socket is connected * or to the 'to_param' address if the socket is not connected. * Parameters: fd - The descriptor for the socket to use. * buf - The buffer to send. * len - The length of the buffer in octets. * flags - Unused by IPLITE. * to - The destination for the data if the socket is * unconnected, ignored for connected sockets. * tolen - The size of the 'to_param' buffer in octets. * Returns: >=0 = number of bytes sent, IP_SOCKERR = error code is * retrivered by using ipcom_errno. * */ IP_PUBLIC Ip_ssize_t ipcom_sendto(Ip_fd fd, IP_CONST void *buf, Ip_size_t len, int flags, IP_CONST struct Ip_sockaddr *to, Ip_socklen_t tolen) { return ipcom_sendto_pktinfo(fd, buf, len, flags, to, tolen, IP_NULL); } /* *=========================================================================== * ipcom_sendto_pktinfo *=========================================================================== * Description: Sends a packet to the address to which the socket is connected * or to the 'to_param' address if the socket is not connected. * Parameters: fd - The descriptor for the socket to use. * buf - The buffer to send. * len - The length of the buffer in octets. * flags - Unused by IPLITE. * to - The destination for the data if the socket is * unconnected, ignored for connected sockets. * tolen - The size of the 'to_param' buffer in octets. * Returns: >=0 = number of bytes sent, IP_SOCKERR = error code is * retrivered by using ipcom_errno. * */ IP_PUBLIC Ip_ssize_t ipcom_sendto_pktinfo(Ip_fd fd, IP_CONST void *buf, Ip_size_t len, int flags, IP_CONST struct Ip_sockaddr *to, Ip_socklen_t tolen, IP_CONST struct Ip_msghdr *msg) { Ipcom_pkt *pkt; Iplite_socket *sock; int ret; Ip_bool non_blocking; ip_assert2(fd >= IPLITE_SOCKET_STARTFD); ip_assert2(fd < IPLITE_CONF_MAX_SOCKETS); ip_assert2(iplite.sockets[fd] != IP_NULL); ip_assert2(buf != IP_NULL); IPLITE_STATS(sock_sendto++); IPLITE_CODE_LOCK(); if (fd >= IPLITE_CONF_MAX_SOCKETS || iplite.sockets[fd] == IP_NULL) { IPLITE_STATS(sock_sendto_ebadf++); sock = IP_NULL; SOCK_ERROUT(IP_ERRNO_EBADF); } sock = iplite.sockets[fd]; ip_assert(sock != IP_NULL); IPLITE_SOCK_ADD_REF(sock); if (sock->ipcom.so_errno != 0) { ip_assert(sock->ipcom.so_errno > 0); SOCK_ERROUT(sock->ipcom.so_errno); } if (IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_WRITE_CLOSED)) SOCK_ERROUT(IP_ERRNO_ESHUTDOWN); #ifdef IPCOM_USE_TCP if (sock->tcb != IP_NULL) { ret = iplite_tcp_usr_send(sock, buf, len, flags); if (ret < 0) { IPLITE_STATS(sock_sendto_tcp_err++); SOCK_ERROUT(-ret); } goto leave; } #endif /* IPCOM_USE_TCP */ /* Check if the packet is small enough to be sent * (this value can be increased using IP_SO_SNDBUF socket option) */ if (len > (Ip_size_t) sock->snd_max_bytes) { IPLITE_STATS(sock_sendto_emsgsize++); SOCK_ERROUT(IP_ERRNO_EMSGSIZE); } non_blocking = IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_NONBLOCKING) || IP_BIT_ISSET(flags, IP_MSG_DONTWAIT); pkt = ipcom_pkt_malloc(len + sock->max_hdrspace, IP_FLAG_FC_STACKCONTEXT | (non_blocking ? 0 : IP_FLAG_FC_BLOCKOK)); if (pkt == IP_NULL) { IPLITE_STATS(sock_sendto_nomem++); if (non_blocking) SOCK_ERROUT(IP_ERRNO_EWOULDBLOCK); SOCK_ERROUT(IP_ERRNO_ENOMEM); } pkt->fd = sock->ipcom.fd; pkt->start = ipcom_conf_max_link_hdr_size + sock->max_hdrspace; #ifdef IPIPSEC2 if (sock->ipcom.domain == IP_AF_KEY) { while(((Ip_u32)&pkt->data[pkt->start] & 0x7) != 0) pkt->start++; } #endif #if defined(IPCOM_PKT_TX_ALIGN) if (sock->ipcom.domain != IP_AF_KEY) { /* Optimize alignment for Ethernet header */ IPCOM_PKT_TX_ALIGN(pkt, sock->max_hdrspace + IP_ETH_HDR_SIZE, 1); } #endif /* IPCOM_PKT_TX_ALIGN */ /* Copy user data into pkt->data with enough space for anything. 0 bytes is valid. */ if (len > 0) { #if !defined(IPCOM_PKT_TX_ALIGN) if (((int)&pkt->data[pkt->start] ^ (int)buf) & 0x3) pkt->start += 2; /* Align user buffer with pkt buffer */ #endif /* IPCOM_PKT_TX_ALIGN */ #ifdef IPCOM_USE_ASM if (IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_ADDCHECKSUM)) pkt->chk = ipcom_in_checksum_memcpy(&pkt->data[pkt->start], (void *)buf, len); else ipcom_memcpy(&pkt->data[pkt->start], buf, len); #else ipcom_memcpy(&pkt->data[pkt->start], buf, len); if (IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_ADDCHECKSUM)) pkt->chk = ipcom_in_checksum_update(&pkt->data[pkt->start], len); #endif } pkt->end = pkt->start + (int)len; /* Send the data using UDP or Raw IP. */ if (non_blocking) IP_BIT_SET(pkt->flags, IPCOM_PKT_FLAG_NONBLOCKING); #ifdef IPLITE_COMPAT_OLDSOCK if (to != IP_NULL) { if (to->sa_family == 0 && to->sa_len < IP_AF_MAX) ((struct Ip_sockaddr *)to)->sa_family = to->sa_len; ((struct Ip_sockaddr *)to)->sa_len = (Ip_u8) tolen; } #else (void)tolen; #endif ip_assert(to == IP_NULL || to->sa_len > 0); if (sock->ipcom.type == IP_SOCK_DGRAM && IP_BIT_ISFALSE(sock->flags, IPLITE_SOCKET_FLAG_BOUND)) { union Ip_sockaddr_union name; /* Do an implicit bind */ ipcom_memset(&name, 0, sizeof(name)); name.sa.sa_family = (Ip_sa_family_t)sock->ipcom.domain; ret = iplite_do_bind(sock, &name.sa, sizeof(name), IP_TRUE); if (ret < 0) SOCK_ERROUT(-ret); } ip_assert(sock->transport_output != IP_NULL); #if defined(IP_PORT_VXWORKS) && defined(IPCOM_USE_INET6) if ((msg != IP_NULL) && (msg->msg_control != IP_NULL) && (msg->msg_controllen >= sizeof(struct Ip_in6_pktinfo))) { struct Ip_in6_pktinfo *info = (struct Ip_in6_pktinfo *)IP_CMSG_DATA(msg->msg_control); struct Ip_in6_pktinfo *info_pkt = IP_NULL; ip_assert(info != IP_NULL); pkt->ifindex = info->ipi6_ifindex; info_pkt = (struct Ip_in6_pktinfo *)ipcom_pkt_alloc_info(pkt, IPLITE_PKT_INFO_PKTINFO, sizeof(struct Ip_in6_pktinfo)); if (info_pkt != IP_NULL) { ipcom_memcpy(info_pkt, info, sizeof(struct Ip_in6_pktinfo)); } } #else (void)msg; #endif ret = sock->transport_output(sock, to, pkt); if (ret == 0) ret = (int)len; else if (ret < 0) SOCK_ERROUT(-ret); leave: if (sock) IPLITE_SOCK_DEL_REF(sock); IPLITE_CODE_UNLOCK(); return (Ip_ssize_t)ret; errout: /* EWOULDBLOCK can occur due to two reasons here. Out of packet memory * or due to interface output queue full. In either case, the application * will get a PKTAVAIL callback event to notify when it can retry send. * Note: This behaviour is required for socket server to function! */ if (sock != IP_NULL && sock->ipcom.event_cb != IP_NULL && ipcom_errno == IP_ERRNO_EWOULDBLOCK) { IP_BIT_SET(iplite_flags, IPLITE_FLAG_PKTAVAIL_CB_PENDING); IP_BIT_SET(sock->flags, IPLITE_SOCKET_FLAG_PKTAVAIL_CB_PENDING); } ret = IP_SOCKERR; goto leave; } /* *=========================================================================== * ipcom_recv *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_ssize_t ipcom_recv(Ip_fd sock, void *buf, Ip_size_t len, int flags) { return ipcom_recvfrom(sock, buf, len, flags, IP_NULL, IP_NULL); } /* *=========================================================================== * ipcom_socketread *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_ssize_t ipcom_socketread(Ip_fd sock, void *buf, Ip_size_t nbytes) { return ipcom_recvfrom(sock, buf, nbytes, 0, IP_NULL, IP_NULL); } /* *=========================================================================== * iplite_recvfrom_waitall *=========================================================================== * Description: IPLITE does not have the required features to implement * the "low watermark" feature. This makes implementing * MSG_WAITALL a bit awkward. This helper function implements * the correct behavior of MSG_WAITALL, but with a far from * optimal solution. * Parameters: fd - a file descriptor * buf - buffer where the data will be stored * len - length of 'buf' * flags - flags with MSG_WAITALL set * from - buffer where the address of the sender should be * stored or IP_NULL if that information is not relevant * fromlen - initially the length of 'from', at return the * number octets written to 'from' * Returns: Number of octets received or negative error code. * */ #ifndef IP_PORT_OSE5 IP_STATIC Ip_ssize_t iplite_recvfrom_waitall(Ip_fd fd, void *buf, Ip_size_t len, int flags, struct Ip_sockaddr *from, Ip_socklen_t *fromlen) { int is_nonblock = IP_BIT_ISSET(flags, IP_MSG_DONTWAIT); int ret; struct Ip_timeval now; struct Ip_timeval end; Ip_socklen_t tv_len = sizeof(end); Ip_bool has_timeout = IP_FALSE; if (ipcom_getsockopt(fd, IP_SOL_SOCKET, IP_SO_RCVTIMEO, &end, &tv_len) == 0 && (end.tv_sec > 0 || end.tv_usec > 0)) { /* Receive timeout != infinite */ ipcom_microtime(&now); ip_timeval_add(&end, &now, &end); has_timeout = IP_TRUE; } if (!is_nonblock) ipcom_socketioctl(fd, IP_X_FIOGNBIO, &is_nonblock); flags &= ~IP_MSG_WAITALL; while (len > (ret = ipcom_recvfrom(fd, buf, len, flags | IP_MSG_PEEK, from, fromlen))) { /* * Not the best of solutions, but have to do for now... */ if (is_nonblock) return ret; if (has_timeout) { ipcom_microtime(&now); if (ip_timeval_cmp(&now, &end, >)) return -IP_ERRNO_EWOULDBLOCK; } ipcom_millisleep(100); } if (IP_BIT_ISFALSE(flags, IP_MSG_PEEK)) return ipcom_recvfrom(fd, buf, len, flags, from, fromlen); return ret; } #endif /* IP_PORT_OSE5 */ /* *=========================================================================== * ipcom_recvfrom *=========================================================================== * Description: Receives data from a socket. * Parameters: fd - A socket descriptor. * buf - A buffer to where the data should be copied. * len - The length of buf buffer. * flags - Currently unused (set to 0). * from - The sender of the data. * fromlen - The length of the from buffer, will be updated to * contain the number of octets copied to the from buffer. * Returns: >=0 = the number of bytes copied to buf, -1 = error. * */ /*lint -efunc(429,ipcom_recvfrom) Custodial pointer 'ptr' (line X) has not been freed or returned*/ IP_PUBLIC Ip_ssize_t ipcom_recvfrom(Ip_fd fd, void *buf, Ip_size_t len, int flags, struct Ip_sockaddr *from, Ip_socklen_t *fromlen) { return ipcom_recvfrom_pktinfo(fd, buf, len, flags, from, fromlen, IP_NULL); } /* *=========================================================================== * ipcom_recvfrom_pktinfo *=========================================================================== * Description: Receives data from a socket, at the same time, return pkt info. * Parameters: fd - A socket descriptor. * buf - A buffer to where the data should be copied. * len - The length of buf buffer. * flags - Currently unused (set to 0). * from - The sender of the data. * fromlen - The length of the from buffer, will be updated to * contain the number of octets copied to the from buffer. * msg - Ip_msgdhr, used to get packet information * Returns: >=0 = the number of bytes copied to buf, -1 = error. * */ /*lint -efunc(429,ipcom_recvfrom) Custodial pointer 'ptr' (line X) has not been freed or returned*/ IP_PUBLIC Ip_ssize_t ipcom_recvfrom_pktinfo(Ip_fd fd, void *buf, Ip_size_t len, int flags, struct Ip_sockaddr *from, Ip_socklen_t *fromlen, struct Ip_msghdr *msg) { int read_bytes = -1; Iplite_socket *sock = IP_NULL; Ipcom_pkt *pkt; #ifndef IP_PORT_OSE5 /* Note: OSE5 always read in non-blocking mode which will make below not work */ if (IP_BIT_ISSET(flags, IP_MSG_WAITALL)) { read_bytes = iplite_recvfrom_waitall(fd, buf, len, flags, from, fromlen); if (read_bytes >= 0) return (Ip_ssize_t) read_bytes; ipcom_errno_set(-read_bytes); return IP_SOCKERR; } #endif /* IP_PORT_OSE5 */ IPLITE_CODE_LOCK(); if (fd >= IPLITE_CONF_MAX_SOCKETS || iplite.sockets[fd] == IP_NULL) SOCK_ERROUT(IP_ERRNO_EBADF); sock = iplite.sockets[fd]; ip_assert(sock != IP_NULL); IPLITE_SOCK_ADD_REF(sock); if (sock->ipcom.so_errno) SOCK_ERROUT(sock->ipcom.so_errno); if (IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_READ_CLOSED)) { read_bytes = 0; goto errout; } /* Check if in blocking mode an no bytes to read, wait for data to arrive in that case */ if (sock->ipcom.rcv_bytes == 0 && IP_BIT_ISFALSE(sock->flags, IPLITE_SOCKET_FLAG_NONBLOCKING) && IP_BIT_ISFALSE(flags, IP_MSG_DONTWAIT)) { Ip_fd_set rd_set; struct Ip_timeval start; /* Time */ struct Ip_timeval tv_buf; struct Ip_timeval *tv = IP_NULL; if (sock->rcv_timeout.tv_sec != 0 || sock->rcv_timeout.tv_usec != 0) { /* Receive timeout != infinite */ ipcom_microtime(&start); tv = &tv_buf; *tv = sock->rcv_timeout; } IP_FD_ZERO(&rd_set); for (;;) { IP_FD_SET(fd, &rd_set); switch (iplite_do_socketselect(fd + 1, &rd_set, IP_NULL, IP_NULL, tv)) { case 1: /* Socket ready */ break; case 0: /* Timeout */ SOCK_ERROUT(IP_ERRNO_EWOULDBLOCK); break; default: /* Error */ /* errno has already been set */ goto errout; } if (iplite_sock_is_readable(sock)) break; if (tv != IP_NULL) { struct Ip_timeval elapsed; /* Another process grabbed the data before this process got hold of the lock. */ ipcom_microtime(&elapsed); ip_timeval_sub(&elapsed, &elapsed, &start); *tv = sock->rcv_timeout; if (ip_timeval_cmp(&elapsed, tv, <)) ip_timeval_sub(tv, tv, &elapsed); else { tv->tv_sec = 0; tv->tv_usec = 0; } } } } if (sock->ipcom.rcv_bytes == 0 && IP_UNLIKELY(IP_BIT_ISTRUE(sock->flags, IPLITE_SOCKET_FLAG_READ_CLOSED))) { /* the socket has been closed */ read_bytes = 0; goto errout; } #ifdef IPCOM_USE_TCP if (sock->tcb != IP_NULL) { #ifdef IP_PORT_VXWORKS if (msg != IP_NULL) { msg->msg_controllen = 0; } #endif read_bytes = iplite_tcp_usr_recv(sock, buf, len, flags); if (read_bytes < 0) { IPLITE_STATS(sock_recvfrom_tcp_err++); SOCK_ERROUT(-read_bytes); } } else #endif /* IPCOM_USE_TCP */ { if (sock->ipcom.rcv_bytes == 0) { ip_assert(IP_BIT_ISTRUE(sock->flags, IPLITE_SOCKET_FLAG_NONBLOCKING) || IP_BIT_ISTRUE(flags, IP_MSG_DONTWAIT)); SOCK_ERROUT(IP_ERRNO_EWOULDBLOCK); } pkt = iplite_krecvfrom(sock, from, fromlen); if (pkt == IP_NULL) { IPLITE_STATS(sock_recvfrom_err++); SOCK_ERROUT(IP_ERRNO_EAGAIN); } read_bytes = IP_MIN((int)len, pkt->end - pkt->start); if (read_bytes > 0) ipcom_memcpy(buf, &pkt->data[pkt->start], read_bytes); #if defined(IP_PORT_VXWORKS) && defined(IPCOM_USE_INET6) if ((read_bytes > 0) && ((sock->flags & IPLITE_SOCKET_FLAG_RECVPKTINFO) != 0) && (msg != IP_NULL) && (msg->msg_control != IP_NULL) && (msg->msg_controllen > 0)) { struct Ip_in6_pktinfo *pktinfo = IP_NULL; Iplite_netif *netif = IP_NULL; int cmsg_len = 0; int pktinfo_len = 0; struct Ip_cmsghdr *cmsg = (struct Ip_cmsghdr *)IP_CMSG_FIRSTHDR(msg); pktinfo_len = sizeof(struct Ip_in6_pktinfo); cmsg_len = IP_CMSG_SPACE(pktinfo_len); if (cmsg_len <= msg->msg_controllen) { cmsg->cmsg_len = IP_CMSG_LEN(pktinfo_len); cmsg->cmsg_level = IP_IPPROTO_IPV6; cmsg->cmsg_type = IP_IPV6_PKTINFO; pktinfo = (struct Ip_in6_pktinfo *)IP_CMSG_DATA(cmsg); IPLITE_IP6_SET_ADDR(&pktinfo->ipi6_addr, &((struct Ip_sockaddr_in6 *)from)->sin6_addr); netif = iplite_if_indextonetif(pkt->ifindex); if (netif != IP_NULL) pktinfo->ipi6_ifindex = netif->ipcom.ifindex; else pktinfo->ipi6_ifindex = 0; msg->msg_controllen = cmsg_len; } else { msg->msg_controllen = 0; } } else if (msg != IP_NULL) { msg->msg_controllen = 0; } #else if (msg != IP_NULL) { msg->msg_controllen = 0; } #endif ipcom_pkt_free(pkt); } errout: if (sock) IPLITE_SOCK_DEL_REF(sock); IPLITE_CODE_UNLOCK(); return (Ip_ssize_t) (read_bytes < 0 ? IP_SOCKERR : read_bytes); } /* *=========================================================================== * iplite_krecvfrom *=========================================================================== * Description: Returns the first packet in the in queue of the socket. * Parameters: sock - The socket on which the packet was queued. * from - The address of the sender of the packet. * fromlen - The length of the buffer pointed to by from. * Returns: The oldest recieved packet. * */ IP_STATIC Ipcom_pkt * iplite_krecvfrom(Iplite_socket *sock, struct Ip_sockaddr *from, Ip_socklen_t *fromlen) { Ipcom_pkt *pkt; ip_assert(sock != IP_NULL); ip_assert(sock->rcv_head != IP_NULL); IPLITE_STATS(sock_krecvfrom++); /* Unlink the first packet from the queue head. */ pkt = sock->rcv_head; sock->ipcom.rcv_bytes -= pkt->end - pkt->start; sock->rcv_head = pkt->next; if (sock->rcv_head == IP_NULL) sock->rcv_tail = IP_NULL; /* Copy of sender info. */ if (from != IP_NULL) { Ip_u16 sport_n = 0; ip_assert2(fromlen != IP_NULL); if (fromlen == IP_NULL) SOCK_ERROUT(IP_ERRNO_EINVAL); if (sock->proto == IP_IPPROTO_UDP) { Iplite_pkt_udp *udp; udp = (Iplite_pkt_udp*) &pkt->data[pkt->start - IPLITE_UDP_HDR_SIZE]; sport_n = udp->sport; } switch(sock->ipcom.domain) { #ifdef IPCOM_USE_INET case IP_AF_INET: if (*fromlen >= sizeof(struct Ip_sockaddr_in)) { Iplite_pkt_ip *ip4; struct Ip_sockaddr_in *from_in = (struct Ip_sockaddr_in *)from; ip4 = (Iplite_pkt_ip *) &pkt->data[pkt->ipstart]; from_in->sin_len = sizeof(struct Ip_sockaddr_in); from_in->sin_family = IP_AF_INET; ipcom_memcpy(&from_in->sin_addr.s_addr, ip4->src, sizeof(Ip_u32)); from_in->sin_port = sport_n; *fromlen = sizeof(struct Ip_sockaddr_in); } break; #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 case IP_AF_INET6: if (*fromlen >= sizeof(struct Ip_sockaddr_in6)) { Iplite_pkt_ip6 *ip6; struct Ip_sockaddr_in6 *from_in6 = (struct Ip_sockaddr_in6 *)from; ip6 = (Iplite_pkt_ip6*) &pkt->data[pkt->ipstart]; from_in6->sin6_len = sizeof(struct Ip_sockaddr_in6); from_in6->sin6_family = IP_AF_INET6; IPLITE_IP6_SET_ADDR(&from_in6->sin6_addr, &ip6->src); from_in6->sin6_flowinfo = ip_htonl(IPLITE_IP6_GET_FLOW(ip6)); from_in6->sin6_scope_id = pkt->ifindex; from_in6->sin6_port = sport_n; *fromlen = sizeof(struct Ip_sockaddr_in6); } break; #endif /* IPCOM_USE_INET6 */ #ifdef IPCOM_USE_PACKET case IP_AF_PACKET: { Iplite_netif *netif; struct Ip_sockaddr_ll *from_ll = (struct Ip_sockaddr_ll *)from; netif = iplite_if_indextonetif(pkt->ifindex); ip_assert2(netif != IP_NULL); if (netif != IP_NULL && *fromlen >= sizeof(*from_ll)) { netif->link_get_frame_info(netif, pkt, &from_ll->sll_pkttype, &from_ll->sll_protocol, from_ll->sll_addr, IP_NULL); from_ll->sll_len = sizeof(*from_ll); from_ll->sll_family = IP_AF_PACKET; from_ll->sll_ifindex = netif->ipcom.ifindex; from_ll->sll_hatype = netif->ipcom.type; from_ll->sll_halen = (Ip_u8) netif->ipcom.link_addr_size; *fromlen = sizeof(*from_ll); } } break; #endif /* IPCOM_USE_PACKET */ default: /* Domain not implemented (bug if this point is reached) */ IP_PANIC(); break; } } #ifdef IPCOM_USE_PACKET if (sock->ipcom.domain == IP_AF_PACKET && sock->ipcom.type == IP_SOCK_DGRAM) { /* The link header should not be included when the type is IP_SOCK_DGRAM */ Iplite_netif *netif = iplite_if_indextonetif(pkt->ifindex); if (netif != IP_NULL) pkt->start += netif->ipcom.link_hdr_size; } #endif /* IPCOM_USE_PACKET */ return pkt; errout: return IP_NULL; } /* *=========================================================================== * ipcom_getsockname *=========================================================================== * Description: Returns the name of the specified socket. * Parameters: fd - A socket descriptor. * name - A buffer that will receive the name of the socket. * namelenp - A pointer to the length of name, will be updated * to contain the actual number of octets copied to name. * Returns: 0 = success, -1 = error * */ IP_PUBLIC int ipcom_getsockname(Ip_fd fd, struct Ip_sockaddr *name, Ip_socklen_t *namelenp) { int ret; ip_assert2(name); ip_assert2(namelenp); IPLITE_CODE_LOCK(); ret = iplite_do_getname(fd, name, namelenp, IP_TRUE); IPLITE_CODE_UNLOCK(); if (ret < 0) SOCK_ERROUT(-ret); return 0; errout: return (int)IP_SOCKERR; } /* *=========================================================================== * ipcom_getpeername *=========================================================================== * Description: Returns the name of the peer connected to the specified socket. * Parameters: fd - A socket descriptor. * name - A buffer that will receive the name of the peer. * namelenp - A pointer to the length of name, will be updated * to contain the actual number of octets copied to name. * Returns: 0 = success, -1 = error * */ IP_PUBLIC int ipcom_getpeername(Ip_fd fd, struct Ip_sockaddr *name, Ip_socklen_t *namelenp) { int ret; ip_assert2(name); ip_assert2(namelenp); IPLITE_CODE_LOCK(); ret = iplite_do_getname(fd, name, namelenp, IP_FALSE); IPLITE_CODE_UNLOCK(); if (ret < 0) SOCK_ERROUT(-ret); return 0; errout: return (int)IP_SOCKERR; } #endif /* !defined(IPLITE_ONE_FILE) || defined(IPLITE_C) */ /* **************************************************************************** * END OF FILE **************************************************************************** */