Files
m241Neelima/components/ip_net2-6.9/ipnet2/src/ipnet_usr_sock.c
T
2026-07-30 11:19:44 +05:30

4372 lines
136 KiB
C

/*
******************************************************************************
* INTERPEAK SOURCE FILE
*
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipnet_usr_sock.c,v $ $Revision: 1.104.10.45 $
* $Source: /home/interpeak/CVSRoot/ipnet2/src/ipnet_usr_sock.c,v $
* $Author: svc-cmnet $
* $State: Exp $ $Locker: $
*
* INTERPEAK_COPYRIGHT_STRING
* Design and implementation by Kenneth Jonsson <kenneth.jonsson@windriver.com>
******************************************************************************
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
*/
/* Implementation of all public socket calls. This implementation will
translate every call into a signal and sent it to the "ipnetd" task
trough an IPCOM pipe. All IP_PUBLIC function in this module must be
called in kernel address space and in the context of the calling
user task.
*/
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
#ifndef IPCOM_KERNEL
#define IPCOM_KERNEL
#endif
#define IPCOM_SKIP_NATIVE_SOCK_API
#include "ipnet_config.h"
/*
* Smallest amount a packet may allocate out of the socket send
* buffer.
*/
#define IPNET_USE_SOCK_MIN_ALLOC_LEN 1280
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#define IPCOM_USE_CLIB_PROTO
#include <ipcom_clib.h>
#include <ipcom_cstyle.h>
#include <ipcom_errno.h>
#include <ipcom_pipe.h>
#include <ipcom_sysctl.h>
#include <ipcom_syslog.h>
#include <ipcom_type.h>
#ifdef IPCOM_WV_INSTRUMENTATION
#include <ipcom_windview.h>
#endif
#include "ipnet_ctrl.h"
#include "ipnet_h.h"
#include "ipnet_signal.h"
#include "ipnet_usr_sock.h"
#ifdef IPSCTP
#include <ipsctp.h>
#endif
/*
****************************************************************************
* 4 DEFINES
****************************************************************************
*/
#define IPNET_SOCK_TASK_PIPE_SIZE IPCOM_PIPE_MSG_LIMIT_FEW
#ifdef IPCOM_ZEROCOPY
/* Maximum length (in bytes) of transport-, network- and link-layer
header that can be added to a packet using the zBuf API */
#define IPNET_ZBUF_MAX_HEADER_LEN 100
#endif
/*
****************************************************************************
* 5 TYPES
****************************************************************************
*/
/*
*===========================================================================
* Ipnet_for_each_sock_in_set_cb
*===========================================================================
* Description: Callback that will be called for each present socket
* in an Ip_fd_set structure.
* Parameters: sock - a socket.
* arg1 - user defined argument
* arg2 - user defined argument
* arg3 - user defined argument
* Returns: IP_TRUE if the socket is ready.
*
*/
typedef Ip_bool (*Ipnet_for_each_sock_in_set_cb)(struct Ipnet_socket_struct *sock,
void *arg1,
void *arg2,
void *arg3);
/*
****************************************************************************
* 6 EXTERN PROTOTYPES
****************************************************************************
*/
#ifdef IPTCP
IP_EXTERN void
ipnet_sock_tcp_zombie_send_pkt_free(Ipnet_socket *sock,
IP_CONST struct Ip_msghdr *msg,
Ipcom_pkt *pkt);
#endif
#ifdef IPSCTP
IP_EXTERN void
ipnet_sock_sctp_zombie_send_pkt_free(Ipnet_socket *sock,
IP_CONST struct Ip_msghdr *msg,
Ipcom_pkt *pkt);
#endif
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
/*
****************************************************************************
* 9 STATIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipnet_user_vr
*===========================================================================
* Description: Returns the virtual router for the current user.
* Parameters:
* Returns: The user VR.
*
*/
#if IPCOM_VR_MAX > 1
IP_STATIC Ip_u16
ipnet_user_vr(void)
{
#ifdef IP_PORT_LKM
return ipcom_get_user_vr();
#else
return (Ip_u16)ipcom_proc_vr_get();
#endif
}
#endif /* IPCOM_VR_MAX > 1 */
/*
*===========================================================================
* ipnet_usr_sock_pipe_recv
*===========================================================================
* Description: Recieves a specific message from the specified pipe.
* Parameters: pipe - pipe to receive the message from
* psig - location where the buffer pointer must be stored
* rsig - pointer value that the received buffer must
* have or IP_NULL if any pointer is OK
* types - array of acceptable types of the received message.
* types_len - length of the "types" array.
* Returns: 0 = if success
* <0 = error code
*
*/
IP_STATIC int
ipnet_usr_sock_pipe_recv(Ipcom_pipe *pipe,
Ipnet_sig **psig,
Ipnet_sig *rsig,
enum Ipnet_sig_type *types,
Ip_size_t types_len)
{
int ret;
do
{
ret = ipcom_pipe_recv(pipe, (void **) psig);
if (ret >= 0)
{
Ip_size_t i;
Ipnet_sig *reply_sig = *psig;
if (rsig == reply_sig)
return 0;
for (i = 0; i < types_len; i++)
if (reply_sig->sig_type == types[i])
return 0;
/*
* Will only reach this line if the task using this the
* same PID as the current task has been restarted/killed
* while that task was blocked in a socket call.
*/
ipnet_sig_free(reply_sig);
}
} while (ret >= 0);
return ret;
}
/*
*===========================================================================
* ipnet_pipe_basic_send_recv
*===========================================================================
* Description: Sends a socket operator signal and waits for the response.
* Parameters: sig - The signal to send.
* Returns: The returns code from the operation.
*
*/
IP_INLINE int
ipnet_pipe_basic_send_recv(Ipnet_sig *sig)
{
Ipnet_sig *reply_sig;
int ret;
ipnet_usr_sock_init_sig(sig, sig->sig_type);
ipcom_pipe_send(ipnet->task.pipe, sig);
do
{
ret = ipnet_usr_sock_pipe_recv(sig->response_pipe,
&reply_sig,
sig,
IP_NULL,
0);
}
while (ret == IPCOM_ERR_INTERRUPT);
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipnet_usr_sock_basic_send_recv
*===========================================================================
* Description: Sends a socket operator signal and waits for the response.
* Parameters: sig - The signal to send.
* ret - The return code from the operation.
* Returns: IP_TRUE - operation successful
* IP_FALSE - operation was interrupted before it finished.
* 'sig' should NOT be freed if this function
* returns IP_FALSE, the stack will do that.
*/
IP_STATIC Ip_bool
ipnet_usr_sock_basic_send_recv(Ipnet_sig *sig, int *ret)
{
Ipnet_sig *reply_sig;
int res;
ipnet_usr_sock_init_sig(sig, sig->sig_type);
ipcom_pipe_send(ipnet->task.pipe, sig);
do
{
res = ipnet_usr_sock_pipe_recv(sig->response_pipe,
&reply_sig,
sig,
IP_NULL,
0);
}
while (res == IPCOM_ERR_INTERRUPT);
ip_assert(sig == reply_sig);
if (reply_sig->reply.return_code < 0)
(void)ipcom_errno_set(reply_sig->reply.sock_errno);
if (ret != IP_NULL)
*ret = reply_sig->reply.return_code;
return IP_TRUE;
}
/*
*===========================================================================
* ipnet_usr_sock_pkt_to_sig
*===========================================================================
* Description: Turns the specified packet structure into a signal.
* Parameters: pkt - packet to convert into a signal.
* Returns: Signal header that contains the packet.
*
*/
IP_INLINE Ipnet_sig *
ipnet_usr_sock_pkt_to_sig(Ipcom_pkt *pkt)
{
return ipcom_pkt_alloc_info(pkt,
IPNET_PKT_INFO_SIG,
sizeof(Ipnet_sig));
}
/*
*===========================================================================
* ipnet_usr_sockopt
*===========================================================================
* Description: Sends setsockopt or getsockopt signal to the stack deamon.
* Parameters: fd - A socket descriptor.
* level - The protocol level that should be affected by the
* option.
* optname - The option name.
* optval - Data to set for the option.
* optlen - The length of the buffer poited to by optval.
* Returns: 0 = success, -1 = error.
*
*/
IP_STATIC int
ipnet_usr_sockopt(Ip_fd fd,
int level,
int optname,
void *optval,
Ip_socklen_t *optlen,
enum Ipnet_sig_type sig_type)
{
Ipnet_sig *sig;
int ret;
/*
* Allocate an extra sizeof(void*) byte to be sure that
* IP_PTR_ALIGN() does cause this function to reference data
* beyond the end of the buffer.
*/
sig = ipcom_malloc(sizeof(Ipnet_sig) + *optlen + sizeof(void*));
if (sig == IP_NULL)
{
ipcom_errno_set(IP_ERRNO_ENOMEM);
return IP_SOCKERR;
}
sig->sig_type = sig_type;
sig->d.sockopt.fd = fd;
sig->d.sockopt.level = level;
sig->d.sockopt.optname = optname;
sig->d.sockopt.optval = IP_PTR_ALIGN((sig + 1), sizeof(void*));
sig->d.sockopt.optlen = *optlen;
/*
* Cannot pass the "optval" pointer, since the memory pointed to
* will not be valid in the network task when the caller is a
* VxWorks RTP
*/
ipcom_memcpy(sig->d.sockopt.optval, optval, *optlen);
if (ipnet_usr_sock_basic_send_recv(sig, &ret))
{
if (ret == 0 && sig_type == IPNET_SIG_GETSOCKOPT)
{
*optlen = sig->d.sockopt.optlen;
ipcom_memcpy(optval, sig->d.sockopt.optval, *optlen);
}
ipcom_free(sig);
}
return ret;
}
/*
*===========================================================================
* ipnet_usr_sock_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, -1 error.
*
*/
IP_STATIC int
ipnet_usr_sock_getname(Ip_fd fd,
struct Ip_sockaddr *name,
Ip_socklen_t *namelenp,
Ip_bool local)
{
Ipnet_sig *sig;
int ret;
sig = ipcom_malloc(sizeof(Ipnet_sig));
if (sig == IP_NULL)
{
(void)ipcom_errno_set(IP_ERRNO_ENOMEM);
return IP_SOCKERR;
}
sig->sig_type = IPNET_SIG_GETNAME;
sig->d.getname.fd = fd;
sig->d.getname.get_local_name = local;
if (ipnet_usr_sock_basic_send_recv(sig, &ret))
{
if (ret == 0)
{
if (*namelenp < IPCOM_SA_LEN_GET(&sig->d.getname.name))
{
ipcom_errno_set(IP_ERRNO_ENOBUFS);
ipcom_free(sig);
return IP_SOCKERR;
}
*namelenp = IPCOM_SA_LEN_GET(&sig->d.getname.name);
ipcom_memcpy(name, &sig->d.getname.name, *namelenp);
}
ipcom_free(sig);
}
return ret;
}
/*
*===========================================================================
* ipnet_copy_sender_info
*===========================================================================
* Description: Copies the sender info from the received packet.
* Parameters: sock - The socket.
* pkt - A received packet
* from - The address of the sender of the packet.
* fromlen - The length of the buffer pointed to by from.
* Returns:
*
*/
IP_STATIC void
ipnet_copy_sender_info(Ipnet_socket *sock,
Ipcom_pkt *pkt,
struct Ip_sockaddr *from,
Ip_size_t *fromlen)
{
Ip_u16 sport_n = 0;
/* Fill in sender port. */
if (sock->ipcom.domain == IP_AF_INET || sock->ipcom.domain == IP_AF_INET6)
{
if (sock->proto == IP_IPPROTO_UDP)
{
Ipnet_pkt_udp *udp;
udp = ipcom_pkt_get_data(pkt, -IPNET_UDP_HDR_SIZE);
sport_n = udp->sport;
}
else
{
ip_assert(sock->proto != IP_IPPROTO_TCP);
sport_n = 0;
}
}
switch(sock->ipcom.domain)
{
#ifdef IPCOM_USE_INET
case IP_AF_INET:
/* Fill in sender IPv4 address. */
if (*fromlen >= sizeof(struct Ip_sockaddr_in))
{
Ipnet_pkt_ip *ip4;
struct Ip_sockaddr_in *from_in = (struct Ip_sockaddr_in *) from;
ipcom_memset(from, 0, sizeof(struct Ip_sockaddr_in));
if (pkt->end - pkt->ipstart >= sizeof(Ipnet_pkt_ip))
{
ip4 = ipcom_pkt_get_iphdr(pkt);
IPCOM_SA_LEN_SET(from_in, sizeof(struct Ip_sockaddr_in));
from_in->sin_family = IP_AF_INET;
from_in->sin_port = sport_n;
ipcom_memcpy(&from_in->sin_addr, ip4->src, sizeof(struct Ip_in_addr));
}
}
*fromlen = sizeof(struct Ip_sockaddr_in);
break;
#endif /* IPCOM_USE_INET */
#ifdef IPCOM_USE_INET6
case IP_AF_INET6:
/* Fill in sender IPv6 address. */
if (*fromlen >= sizeof(struct Ip_sockaddr_in6))
{
int ip_dgram_sz = pkt->end - pkt->ipstart;
struct Ip_sockaddr_in6 *from_in6 = (struct Ip_sockaddr_in6 *) from;
ipcom_memset(from, 0, sizeof(struct Ip_sockaddr_in6));
if (ip_dgram_sz > 0)
{
void *iphdr = ipcom_pkt_get_iphdr(pkt);
IPCOM_SA_LEN_SET(from_in6, sizeof(struct Ip_sockaddr_in6));
from_in6->sin6_family = IP_AF_INET6;
from_in6->sin6_port = sport_n;
#ifdef IPCOM_USE_INET
if (((*(Ip_u8*)iphdr & 0xf0) == 0x40) &&
(ip_dgram_sz >= sizeof(Ipnet_pkt_ip)))
{
/* Use Ipv6 mapped IPv4 address (packet is Ipv4) */
if (*fromlen >= sizeof(struct Ip_sockaddr_in6))
{
Ipnet_pkt_ip *ip4 = iphdr;
ipnet_ip6_create_mapped_addr((struct Ip_in_addr *) ip4->src,
&from_in6->sin6_addr);
from_in6->sin6_flowinfo = 0;
from_in6->sin6_scope_id = 0;
}
}
else if (ip_dgram_sz >= sizeof(Ipnet_pkt_ip6))
#endif /* IPCOM_USE_INET */
{
/* Normal Ipv6 packet */
if (*fromlen >= sizeof(struct Ip_sockaddr_in6))
{
Ipnet_pkt_ip6 *ip6 = iphdr;
IPNET_IP6_SET_ADDR(&from_in6->sin6_addr, &ip6->src);
from_in6->sin6_flowinfo = ip_htonl(IPNET_IP6_GET_FLOW(ip6));
if (ipnet_ip6_is_scoped_addr(&ip6->src))
from_in6->sin6_scope_id = ipnet_ip6_get_zone(pkt->ifindex,
&ip6->src);
else
/* Use default scope of global addresses */
from_in6->sin6_scope_id = IPNET_IP6_DEFAULT_ZONE;
}
}
}
}
*fromlen = sizeof(struct Ip_sockaddr_in6);
break;
#endif /* IPCOM_USE_INET6 */
#ifdef IPCOM_USE_PACKET
case IP_AF_PACKET:
if (*fromlen >= sizeof(struct Ip_sockaddr_ll))
{
Ipnet_netif *netif;
struct Ip_sockaddr_ll *ll_addr = (struct Ip_sockaddr_ll *) from;
ipcom_memset(from, 0, sizeof(struct Ip_sockaddr_ll));
netif = ipnet_if_indextonetif(pkt->vr_index, pkt->ifindex);
ip_assert(netif != IP_NULL);
(void)netif->link_get_frame_info(netif,
pkt,
&ll_addr->sll_pkttype,
&ll_addr->sll_protocol,
ll_addr->sll_addr,
IP_NULL);
IPCOM_SA_LEN_SET(ll_addr, sizeof(struct Ip_sockaddr_ll));
ll_addr->sll_family = IP_AF_PACKET;
ll_addr->sll_ifindex = netif->ipcom.ifindex;
ll_addr->sll_hatype = netif->ipcom.type;
ll_addr->sll_halen = (Ip_u8) netif->ipcom.link_addr_size;
}
*fromlen = sizeof(struct Ip_sockaddr_ll);
break;
#endif /* IPCOM_USE_PACKET */
#ifdef IPNET_USE_NETLINKSOCK
case IP_AF_NETLINK:
if (*fromlen >= sizeof(struct Ip_sockaddr_nl))
{
struct Ip_sockaddr_nl *nl = (struct Ip_sockaddr_nl *)from;
ipcom_memset(nl, 0, sizeof(*nl));
IPCOM_SA_LEN_SET(nl, sizeof(*nl));
nl->nl_family = IP_AF_NETLINK;
/* WARNING: netlink represents process PID as 32 bits! */
nl->nl_pid = (Ip_u32)pkt->link_cookie;
}
*fromlen = sizeof(struct Ip_sockaddr_nl);
break;
#endif
default:
#ifdef IPCOM_USE_SA_LEN
if (*fromlen >= sizeof(from->sa_len) + sizeof(from->sa_family))
#else
if (*fromlen >= sizeof(from->sa_family))
#endif
{
#ifdef IPCOM_USE_SA_LEN
from->sa_len = (Ip_u8) (sizeof(from->sa_len) + sizeof(from->sa_family));
#endif
from->sa_family = (Ip_u8) sock->ipcom.domain;
}
#ifdef IPCOM_USE_SA_LEN
*fromlen = from->sa_len;
#else
*fromlen = IPCOM_SA_LEN_GET(from);
#endif
break;
}
}
/*
*===========================================================================
* ipnet_usr_sock_poll_free
*===========================================================================
* Description: Frees all resources allocated in a poll operation
* Parameters: poll_sig - the poll-done signal that acts as the
* session tracker for this operation.
* Returns:
*
*/
IP_STATIC void
ipnet_usr_sock_poll_free(Ipnet_sig *poll_sig)
{
Ipnet_sig *sig = poll_sig;
Ipnet_sig *next;
do {
next = sig->d.poll.next;
ipcom_slab_free(ipnet->slabs.poll, sig);
sig = next;
} while (sig != poll_sig);
}
/*
*===========================================================================
* ipnet_usr_sock_poll
*===========================================================================
* Description: Registers/unregister a watch for a socket.
* Parameters: sock - a socket
* poll_done - payload of the poll done signal that will
* finish this poll session.
* timeout - maximum time to wait for the poll to finish,
* IP_NULL means infinite.
* what - what to poll for.
* lowat - minimum number of bytes that must be available
* to consider the socket to be ready. 0 will use
* the lo water mark set on the socket.
* Returns: !IP_NULL - operation successfull
* IP_NULL - failed to allocate memory (critical error).
*
*/
IP_STATIC Ipnet_sig *
ipnet_usr_sock_poll(Ipnet_socket *sock,
struct Ip_timeval *timeout,
enum Ipnet_sock_poll what,
int lowat)
{
Ipnet_sig *sig;
sig = ipcom_slab_alloc(ipnet->slabs.poll);
if (sig == IP_NULL)
{
IP_PANIC2();
return IP_NULL;
}
ipnet_usr_sock_init_sig(sig, IPNET_SIG_POLL);
sig->free = ipnet_usr_sock_poll_free;
sig->freesig = sig;
sig->d.poll.fd = sock->ipcom.fd;
sig->d.poll.what = what;
sig->d.poll.lowat = lowat;
sig->d.poll.timeout = timeout;
sig->d.poll.tmo = IP_NULL;
sig->d.poll.list.head = IP_NULL;
sig->d.poll.next = sig;
return sig;
}
/*
*===========================================================================
* ipnet_usr_sock_add_poll
*===========================================================================
* Description: Registers/unregister a watch for readability on the
* specified socket.
* Parameters: sock - a socket
* phead - pointer to the head of the list of poll signals
* ptail - pointer to the tail of the list of poll signals
* timeout - maximum time to wait for the poll to finish,
* IP_NULL means infinite.
* what - what to poll for.
* Returns: Will always return IP_TRUE.
*
*/
IP_STATIC Ip_bool
ipnet_usr_sock_add_poll(Ipnet_socket *sock,
Ipnet_sig **phead,
Ipnet_sig **ptail,
struct Ip_timeval *timeout,
enum Ipnet_sock_poll what)
{
Ipnet_sig *sig;
sig = ipnet_usr_sock_poll(sock,
timeout,
what,
0);
if (sig)
{
if (*ptail == IP_NULL)
*ptail = sig;
sig->d.poll.next = *phead;
*phead = sig;
}
return IP_TRUE;
}
/*
*===========================================================================
* ipnet_usr_sock_add_poll_readable
*===========================================================================
* Description: Registers/unregister a watch for readability on the
* specified socket.
* Parameters: sock - a socket
* phead - pointer to the head of the list of poll signals
* ptail - pointer to the tail of the list of poll signals
* timeout - maximum time to wait for the poll to finish,
* IP_NULL means infinite.
* Returns: Will always return IP_TRUE.
*
*/
IP_STATIC Ip_bool
ipnet_usr_sock_add_poll_readable(Ipnet_socket *sock,
Ipnet_sig **phead,
Ipnet_sig **ptail,
struct Ip_timeval *timeout)
{
return ipnet_usr_sock_add_poll(sock,
phead,
ptail,
timeout,
IPNET_SOCK_POLL_READ);
}
/*
*===========================================================================
* ipnet_usr_sock_add_poll_writable
*===========================================================================
* Description: Registers/unregister a watch for writability on the
* specified socket.
* Parameters: sock - a socket
* phead - pointer to the head of the list of poll signals
* ptail - pointer to the tail of the list of poll signals
* timeout - maximum time to wait for the poll to finish,
* IP_NULL means infinite.
* Returns: Will always return IP_TRUE.
*
*/
IP_STATIC Ip_bool
ipnet_usr_sock_add_poll_writable(Ipnet_socket *sock,
Ipnet_sig **phead,
Ipnet_sig **ptail,
struct Ip_timeval *timeout)
{
return ipnet_usr_sock_add_poll(sock,
phead,
ptail,
timeout,
IPNET_SOCK_POLL_WRITE);
}
/*
*===========================================================================
* ipnet_usr_sock_add_poll_exceptional
*===========================================================================
* Description: Registers/unregister a watch for exceptional event on the
* specified socket.
* Parameters: sock - a socket
* phead - pointer to the head of the list of poll signals
* ptail - pointer to the tail of the list of poll signals
* timeout - maximum time to wait for the poll to finish,
* IP_NULL means infinite.
* Returns: Will always return IP_TRUE.
*
*/
IP_STATIC Ip_bool
ipnet_usr_sock_add_poll_exceptional(Ipnet_socket *sock,
Ipnet_sig **phead,
Ipnet_sig **ptail,
struct Ip_timeval *timeout)
{
return ipnet_usr_sock_add_poll(sock,
phead,
ptail,
timeout,
IPNET_SOCK_POLL_EXCEPT);
}
/*
*===========================================================================
* ipnet_is_sock_readable_adapter
*===========================================================================
* Description: Checks if the specified socket is readable.
* Parameters: sock - a socket
* Returns: IP_TRUE if the socket is readable.
*
*/
IP_STATIC Ip_bool
ipnet_is_sock_readable_adapter(Ipnet_socket *sock)
{
return ipnet_is_sock_readable(sock, 0);
}
/*
*===========================================================================
* ipnet_is_sock_writable_adapter
*===========================================================================
* Description: Checks if the specified socket is writable.
* Parameters: sock - a socket
* Returns: IP_TRUE if the socket is writable.
*
*/
IP_STATIC Ip_bool
ipnet_is_sock_writable_adapter(Ipnet_socket *sock)
{
return ipnet_is_sock_writable(sock, 0);
}
/*
*===========================================================================
* ipnet_is_sock_exceptional_adapter
*===========================================================================
* Description: Checks if the specified socket is exceptional.
* Parameters: sock - a socket
* Returns: IP_TRUE if the socket is exceptional.
*
*/
IP_STATIC Ip_bool
ipnet_is_sock_exceptional_adapter(Ipnet_socket *sock)
{
return ipnet_is_sock_exceptional(sock, 0);
}
/*
*===========================================================================
* ipnet_usr_sock_for_each_sock_in_set
*===========================================================================
* Description: Calls the specified function for each socket set in
* the set.
* Parameters: nfds - The highest socket number in the set rounded up
* to nearest number that can be evenly divided by
* 32.
* fds - The set to iterate over.
* unset_non_ready_sock - IP_TRUE if socket that is
* currently no ready should be cleared in the
* 'fds' set.
* is_sock_ready - function that knows how to check if
* the socket is ready.
* arg1-3 - user defined argument that is passed verbatim
* to the 'is_ready_sock' function.
* Returns: >=0 = the number of ready sockets, <0 = error code.
*
*/
IP_STATIC int
ipnet_for_each_sock_in_set(int nfds,
Ip_fd_set *fds,
Ip_bool unset_non_ready_sock,
Ipnet_for_each_sock_in_set_cb is_sock_ready,
void *arg1,
void *arg2,
void *arg3)
{
Ipnet_socket *sock;
Ip_fd fd;
Ip_fd fd_mask_idx;
Ip_fd nfds_fd_mask = (Ip_fd)(nfds / IP_NFDBITS);
Ip_fd fd8;
Ip_fd fd_bit;
int ready_sock_count = 0;
int err = 0;
ip_assert((nfds % IP_NFDBITS) == 0);
if (fds == IP_NULL)
return 0;
for (fd_mask_idx = 0; fd_mask_idx < nfds_fd_mask; fd_mask_idx++)
{
if (fds->fds_bits[fd_mask_idx] == 0)
continue;
for (fd8 = 0; fd8 < IP_NFDBITS; fd8 += 8)
{
if ((fds->fds_bits[fd_mask_idx] & ((Ip_fd_mask)0xff << fd8)) == 0)
continue;
fd = (Ip_fd)((fd_mask_idx * IP_NFDBITS) + fd8);
for (fd_bit = 0; fd_bit < 8; fd_bit++)
if (IP_FD_ISSET(fd + fd_bit, fds))
{
sock = ipnet_get_sock(fd + fd_bit);
if (sock == IP_NULL)
err = -IP_ERRNO_EBADF;
else
{
if (is_sock_ready(sock, arg1, arg2, arg3))
ready_sock_count++;
else if (unset_non_ready_sock)
IP_FD_CLR(fd + fd_bit, fds);
ipnet_release_sock(sock);
}
}
}
}
return err ? err : ready_sock_count;
}
/*
*===========================================================================
* ipnet_usr_sock_wait_until
*===========================================================================
* Description: Waits until the specified descriptor is ready.
* Parameters: sock - the socket to wait for.
* lowat - number of bytes that must be available for the
* socket to be considered ready
* timeout - maximum time to wait for socket to become ready.
* what - what to wait for.
* Returns: 0 - socket is ready.
* IP_ERRNO_EWOULDBLOCK - poll operation timed out.
* IP_ERRNO_EINTR - poll operation returned early due to
* signal delivery.
* IP_ERRNO_ENOMEM - failed to allocate a poll signal
*/
IP_STATIC int
ipnet_usr_sock_wait_until(Ipnet_socket *sock,
int lowat,
struct Ip_timeval *timeout,
enum Ipnet_sock_poll what)
{
Ipnet_sig *reply;
Ipnet_sig *poll_sig;
Ipcom_pipe *pipe;
int ret;
if ((poll_sig = ipnet_usr_sock_poll(sock,
timeout,
what,
lowat)) == IP_NULL)
{
IP_PANIC2();
return IP_ERRNO_ENOMEM;
}
ipcom_pipe_send(ipnet->task.pipe, poll_sig);
pipe = ipnet_usr_sock_get_response_pipe();
ret = ipnet_usr_sock_pipe_recv(pipe,
&reply,
poll_sig,
IP_NULL,
0);
if (ret == IPCOM_SUCCESS)
{
ret = (reply->sig_type == IPNET_SIG_POLL_TIMEOUT ? IP_ERRNO_EWOULDBLOCK : 0);
ip_assert(reply->sig_type == IPNET_SIG_POLL_DONE
|| reply->sig_type == IPNET_SIG_POLL_TIMEOUT);
ipnet_sig_free(reply);
}
else
{
ret = IP_ERRNO_EINTR;
}
return ret;
}
/*
*===========================================================================
* ipnet_usr_sock_wait_until_readable
*===========================================================================
* Description: Waits until the specified descriptor is readable.
* Parameters: sock - the socket to wait for.
* lowat - number of bytes that must be available for the
* socket to be considered ready
* timeout - maximum time to wait for the socket to become
* readable or IP_NULL for indefinite.
* Returns: 0 - socket is ready.
* IP_ERRNO_EWOULDBLOCK - poll operation timed out.
* IP_ERRNO_EINTR - poll operation returned early due to
* signal delivery.
* IP_ERRNO_ENOMEM - failed to allocate a poll signal
*/
IP_STATIC int
ipnet_usr_sock_wait_until_readable(Ipnet_socket *sock,
int lowat,
struct Ip_timeval *timeout)
{
if (ipnet_is_sock_readable(sock, lowat))
return 0;
return ipnet_usr_sock_wait_until(sock,
lowat,
timeout,
IPNET_SOCK_POLL_READ);
}
/*
*===========================================================================
* ipnet_usr_sock_create_pkt_from_iov
*===========================================================================
* Description: Default packet creator handler. All datagram and raw
* socket types should be able to use this one. Stream
* based protocols like TCP and SCTP must have their
* own 'iov' to packet(s) handler.
* Parameters: sock - The socket the data is beeing sent at.
* msg - The send parameters specified by the sender.
* flags - IP_MSG_xxx flags.
* offset - must always be zero.
* total_buf_len - Total length of all buffers in the
* msg->msg_iov parameter.
* prslt - Points to result structure where the constructed packet
* and transient error (if any) are returned.
* prslt->pkt->next points to the next packet in case
* more than one packet was allocated.
* Returns: <0 = error code (-IPNET_ERRNO_xxx)
* >=0 = number of bytes that have been allocated in the
* socket send buffer and copied into the
* packet(s) pointed to by prslt->pkt.
*
*/
IP_STATIC int
ipnet_usr_sock_create_pkt_from_iov(Ipnet_socket *sock,
IP_CONST struct Ip_msghdr *msg,
int flags,
int offset,
int total_buf_len,
Ipnet_pkt_result *prslt)
{
Ipcom_pkt *pkt;
void *pkt_data;
int len = 0;
int buflen;
int i;
Ip_size_t dynamic_hdrspace = 0;
Ip_bool non_blocking;
Ip_bool align_buf_on_64_bit = IP_FALSE;
Ip_bool fast_path;
IPCOM_UNUSED_ARG(offset);
ip_assert(offset == 0);
non_blocking = IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_NONBLOCKING)
|| IP_BIT_ISSET(flags, IP_MSG_DONTWAIT);
len = total_buf_len;
if (IP_UNLIKELY((unsigned)total_buf_len > (unsigned)sock->send_max_bytes))
goto emsgsize;
prslt->pkt = IP_NULL;
prslt->transient_err = 0;
while (IP_UNLIKELY(! ipnet_alloc_snd_bytes(sock, total_buf_len)))
{
int err;
/*
* The requested number of bytes cannot be sent at the moment
*/
if (non_blocking)
return -IP_ERRNO_EWOULDBLOCK;
err = ipnet_usr_sock_wait_until_writable(sock, total_buf_len, IP_NULL);
if (err != 0 || 0 != (err = sock->ipcom.so_errno))
return -err;
}
fast_path = (msg->msg_iovlen == 1
&& sock->snd_head == IP_NULL
&& IP_BIT_ISFALSE(flags, IP_MSG_MORE));
/*
* Note, sock->snd_head is not protected against concurrent access
* by multiple threads. If the IP_MSG_MORE flag is used, the
* application must be responsible for serializing access to the socket...
*/
len = total_buf_len;
if (IP_UNLIKELY(sock->snd_head != IP_NULL))
len += ipcom_pkt_get_length(sock->snd_head);
/*
* Check if the packet is small enough to be sent (this value can
* be increased using IP_SO_SNDBUF socket option)
*/
if (IP_UNLIKELY(len > sock->send_max_bytes))
{
ipcom_atomic_sub(&sock->snd_bytes, total_buf_len);
emsgsize:
IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_WARNING,
1, 5, IPCOM_WV_NETDEVENT_WARNING, IPCOM_WV_NETD_SEND,
ipnet_usr_sock_create_pkt_from_iov, IPCOM_WV_NETD_FULLSOCK,
IPCOM_WV_IPNET_SOCK_MODULE, IPCOM_WV_NETD_SOCKET);
IPNET_STATS(usr_iov_emsgsize++);
IPCOM_LOG3(WARNING, "sent message discarded on socket %d since the send buffer is only %d, %d was required",
sock->ipcom.fd, sock->send_max_bytes, len);
return -IP_ERRNO_EMSGSIZE;
}
if (sock->dynamic_header_space != IP_NULL
&& (msg->msg_control != IP_NULL
|| IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_HAS_STICKY_OPT)))
dynamic_hdrspace = sock->dynamic_header_space(sock, msg);
pkt = ipcom_pkt_malloc(len + sock->max_hdrspace + dynamic_hdrspace,
IP_FLAG_FC_STACKCONTEXT | (non_blocking ? 0 : IP_FLAG_FC_BLOCKOK));
if (IP_UNLIKELY(pkt == IP_NULL))
{
/* caller holds a reference to the socket */
ipcom_atomic_sub(&sock->snd_bytes, total_buf_len);
IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_CRITICAL,
1, 6, IPCOM_WV_NETDEVENT_CRITICAL, IPCOM_WV_NETD_SEND,
ipnet_usr_sock_create_pkt_from_iov, IPCOM_WV_NETD_NOBUFS,
IPCOM_WV_IPNET_SOCK_MODULE, IPCOM_WV_NETD_SOCKET);
IPNET_STATS(usr_iov_nomem++);
if (sock->ipcom.event_cb != IP_NULL)
{
IP_BIT_SET(ipnet_flags, IPNET_FLAG_PKTAVAIL_CB_PENDING);
IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_PKTAVAIL_CB_PENDING);
}
IPCOM_LOG2(NOTICE, "failed to allocate %d bytes packet on socket %d",
len + sock->max_hdrspace + dynamic_hdrspace, sock->ipcom.fd);
return -IP_ERRNO_ENOMEM;
}
#ifdef _WRS_CONFIG_COMPONENT_IPPTP
if(IP_BIT_ISSET(sock->timestamp_flag, SOF_TIMESTAMPING_TX_SOFTWARE)||
IP_BIT_ISSET(sock->timestamp_flag, SOF_TIMESTAMPING_TX_HARDWARE))
{
if(IP_BIT_ISSET(flags, IP_MSG_TX_TIMESTAMP))
IP_BIT_SET(pkt->time_flags, PKT_TSN_TIME_STAMP_FETCH_EN);
}
#endif
if (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_DONTROUTE)
|| IP_BIT_ISSET(flags, IP_MSG_DONTROUTE))
IP_BIT_SET(pkt->flags, IPCOM_PKT_FLAG_DONTROUTE);
if (sock->ipcom.domain == IP_AF_ROUTE
|| sock->ipcom.domain == IP_AF_NETLINK
|| sock->ipcom.domain == IP_AF_KEY)
{
/*
* These (internal) protocols contains C structures and which
* must have all data on the correct alignment
*/
align_buf_on_64_bit = IP_TRUE;
pkt->start = (int)((-(Ip_ptrdiff_t)pkt->data) & 0x7);
}
else
pkt->start = (int)(ipcom_conf_max_link_hdr_size + sock->max_hdrspace + dynamic_hdrspace);
pkt->vr_index = sock->vr_index;
pkt->end = pkt->start;
if (fast_path)
{
if (align_buf_on_64_bit == IP_FALSE
&& ((Ip_ptrdiff_t) ipcom_pkt_get_data(pkt, 0)
^ (Ip_ptrdiff_t) msg->msg_iov[0].iov_base) & 0x3)
{
/*
* Align user buffer with pkt buffer for faster memcpy().
*/
pkt->start += 2;
pkt->end += 2;
}
buflen = (int)msg->msg_iov[0].iov_len;
pkt_data = ipcom_pkt_push_back(pkt, buflen);
if (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_ADDCHECKSUM))
pkt->chk = ipnet_in_checksum_memcpy(pkt_data,
msg->msg_iov[0].iov_base,
buflen);
else
ipcom_memcpy(pkt_data,
msg->msg_iov[0].iov_base,
buflen);
}
else /* normal path */
{
if (sock->snd_head != IP_NULL)
{
buflen = ipcom_pkt_get_length(sock->snd_head);
ipcom_memcpy(ipcom_pkt_push_back(pkt, buflen),
ipcom_pkt_pop_front(sock->snd_head, 0),
buflen);
len -= buflen;
ipcom_pkt_free(sock->snd_head);
}
if (IP_BIT_ISFALSE(flags, IP_MSG_MORE))
sock->snd_head = IP_NULL;
else
{
sock->snd_head = pkt;
IPCOM_PKT_TRACE(pkt, IPCOM_PKT_ID_SOCKSENDQ);
if (msg->msg_name != IP_NULL)
{
/*
* Save this to address since later sendmsg() might
* skip to specify it
*/
if (sock->snd_to != IP_NULL
&& IPCOM_SA_LEN_GET(sock->snd_to) < msg->msg_namelen)
{
ipcom_free(sock->snd_to);
sock->snd_to = IP_NULL;
}
if (sock->snd_to == IP_NULL)
sock->snd_to = ipcom_malloc(msg->msg_namelen);
if (sock->snd_to != IP_NULL && sock->snd_to != msg->msg_name)
ipcom_memcpy(sock->snd_to,
msg->msg_name,
msg->msg_namelen);
}
}
for (i = 0; i < msg->msg_iovlen; i++)
{
/*
* Copy user data into pkt->data with enough space for
* anything. 0 bytes is valid.
*/
buflen = (int)msg->msg_iov[i].iov_len;
if (buflen > 0)
ipcom_memcpy(ipcom_pkt_push_back(pkt, buflen),
msg->msg_iov[i].iov_base,
buflen);
}
if (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_ADDCHECKSUM)
&& IP_BIT_ISFALSE(flags, IP_MSG_MORE))
pkt->chk = ipcom_in_checksum_update(ipcom_pkt_get_data(pkt, 0),
ipcom_pkt_get_length(pkt));
if (IP_BIT_ISSET(flags, IP_MSG_MORE))
{
/*
* Just append to the packet until send is called with the
* MSG_MORE flag cleared
*/
return len;
}
}
#ifdef IPCOM_USE_INET6
if (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_ADDCHECKSUM)
&& sock->ipcom.domain == IP_AF_INET6
&& sock->ipcom.type == IP_SOCK_RAW)
{
/*
* The checksum must be calculated by the stack, the
* application might have left the checksum field
* uninitialized.
*/
Ip_u16 *chksum_ptr = ipcom_pkt_get_data(pkt, sock->chk_offset);
#ifdef IPCOM_USE_HW_CHECKSUM_TX
*chksum_ptr = 0;
#else
if (*chksum_ptr)
{
*chksum_ptr = 0;
pkt->chk = ipcom_in_checksum_update(ipcom_pkt_get_data(pkt, 0),
ipcom_pkt_get_length(pkt));
}
#endif
}
#endif /* IPCOM_USE_INET6 */
pkt->dtor = sock->pkt_dtor;
if (sock->pkt_dtor)
{
Ipnet_pkt_info_sock_snd_buf snd_buf_info;
int alloc_len;
alloc_len = ipcom_pkt_get_length(pkt);
if (alloc_len < IPNET_USE_SOCK_MIN_ALLOC_LEN)
{
/*
* Make sure that each sent packet allocates a non-zero
* amount of bytes in the socket send buffer. This avoids
* cases where a single application can allocates all
* available buffers when sending very small UDP packets.
*
* Possible overflow to negative byte counts here, but only
* for the very largest send socket buffer sizes, which setsockopt
* now forbids.
*/
ipcom_atomic_add(&sock->snd_bytes,
IPNET_USE_SOCK_MIN_ALLOC_LEN - alloc_len);
alloc_len = IPNET_USE_SOCK_MIN_ALLOC_LEN;
}
snd_buf_info.sock = sock;
snd_buf_info.payload_len = alloc_len;
ipcom_pkt_set_info(pkt,
IPNET_PKT_INFO_SOCK_SND_BUF,
sizeof(snd_buf_info),
&snd_buf_info);
}
prslt->pkt = pkt;
return len;
}
/*
*===========================================================================
* ipcom_do_if_attach
*===========================================================================
* Description: Attaches a new network interface to IPNET.
*
* Parameters: netif - An initialized netif structure.
* locked - Tells wheter IPNET shall lock the interface or not.
*
*
* Returns: The interface index for this interface or negative errno.
*
*/
IP_STATIC int
ipnet_do_if_attach(Ipcom_netif *netif, Ip_bool locked)
{
Ipnet_sig *sig;
int ret;
sig = ipcom_malloc(sizeof(Ipnet_sig));
if (sig == IP_NULL)
{
(void)ipcom_errno_set(IP_ERRNO_ENOMEM);
goto errout;
}
sig->sig_type = IPNET_SIG_IF_ATTACH;
sig->d.netif.netif = netif;
sig->d.netif.locked = locked;
if (ipnet_pipe_basic_send_recv(sig) == IPCOM_ERR_INTERRUPT)
{
(void)ipcom_errno_set(IP_ERRNO_EINTR);
goto errout;
}
ret = sig->reply.return_code;
if (ret < 0)
(void)ipcom_errno_set(IP_ERRNO_ENXIO);
ipcom_free(sig);
return ret;
errout:
return -ipcom_errno;
}
/*
****************************************************************************
* 10 GLOBAL FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipnet_usr_sock_init
*===========================================================================
* Description: Initializes the user socket module.
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipnet_usr_sock_init()
{
ipnet->slabs.poll = ipcom_slab_new("IPNET poll",
IPCOM_SLAB_F_POISON,
ip_offsetof(Ipnet_sig, d.poll.last),
0,
IP_NULL,
IP_NULL,
ipnet->memory_pool);
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipnet_usr_sock_get_ops
*===========================================================================
* Description: Sets all members that can be used by most socket types.
* Parameters: ops - Socket operation structure.
* Returns:
*
*/
IP_GLOBAL void
ipnet_usr_sock_get_ops(Ipnet_sock_ops *ops)
{
ops->usr_recv = ipnet_usr_sock_recvmsg;
ops->pkts_from_iov = ipnet_usr_sock_create_pkt_from_iov;
}
/*
*===========================================================================
* ipnet_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 Ipnet_socket *
ipnet_get_sock(int fd)
{
Ipnet_socket *sock;
IPNET_DATA_LOCK(ipnet->sockets_lock);
if (IP_LIKELY(fd >= IPNET_SOCKET_STARTFD)
&& IP_LIKELY(fd < ipnet_conf_max_sockets)
&& IP_LIKELY(IP_NULL != (sock = ipnet->sockets[fd]))
&& IP_LIKELY(IP_BIT_ISFALSE(sock->flags, IPNET_SOCKET_FLAG_CLOSED)))
{
ipcom_atomic_inc(&sock->ref_count);
}
else
{
sock = IP_NULL;
ipcom_errno_set(IP_ERRNO_EBADF);
}
IPNET_DATA_UNLOCK(ipnet->sockets_lock);
return sock;
}
/*
*===========================================================================
* ipnet_usr_sock_get_pipe
*===========================================================================
* Description: Returns a pipe that is directed from an user-application
* to the IPNET stack task.
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ipcom_pipe *
ipnet_usr_sock_get_pipe()
{
return ipnet->task.pipe;
}
/*
*===========================================================================
* ipnet_usr_sock_get_response_pipe
*===========================================================================
* Description: Returns a pipe that is directed from the IPNET stack task
* back to the calling user-application.
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ipcom_pipe *
ipnet_usr_sock_get_response_pipe(void)
{
return ipnet->task.rpipe;
}
/*
*===========================================================================
* ipnet_usr_sock_init_sig
*===========================================================================
* Description: Initialize a new signal.
* Parameters: sig - the signal to initialize
* sig_type - the signal type
* Returns:
*
*/
IP_GLOBAL void
ipnet_usr_sock_init_sig(Ipnet_sig *sig, enum Ipnet_sig_type sig_type)
{
sig->sig_type = sig_type;
sig->response_pipe = ipnet_usr_sock_get_response_pipe();
sig->sender_pid = ipcom_getpid();
sig->receiver_pid = ipnet->task.pid;
sig->free = IP_NULL;
sig->freesig = IP_NULL;
#if IPCOM_VR_MAX > 1
sig->sender_vr = ipnet_user_vr();
#endif
}
/*
*===========================================================================
* ipnet_usr_sock_get_route_cache_id
*===========================================================================
* Description: Returns a pointer to the 'route_cache_id', which changes when the
* 'route_cache' is invalidated
* Parameters:
* Returns:
*
*/
IP_GLOBAL int volatile *
ipnet_usr_sock_get_route_cache_id(void)
{
return &ipnet->route_cache_id;
}
/*
*===========================================================================
* ipnet_release_sock
*===========================================================================
* Description:
* Parameters: sock - socket to release.
* Returns:
*
*/
IP_GLOBAL void
ipnet_release_sock(Ipnet_socket *sock)
{
if (IP_UNLIKELY(ipcom_atomic_sub_and_return(&sock->ref_count, 1) == 0))
{
Ipnet_sig *sig;
sig = ipcom_malloc(sizeof(Ipnet_sig));
if (sig == IP_NULL)
return;
sig->sig_type = IPNET_SIG_SOCKETFREE;
sig->d.close = sock->ipcom.fd;
if (ipnet_usr_sock_basic_send_recv(sig, IP_NULL))
ipcom_free(sig);
}
}
/*
*===========================================================================
* ipnet_usr_sock_wait_until_writable
*===========================================================================
* Description: Waits until the specified descriptor is writable.
* Parameters: fd - file descriptor that must be writable.
* Returns: 0 - socket is ready.
* IP_ERRNO_EWOULDBLOCK - poll operation timed out.
* IP_ERRNO_EINTR - poll operation returned early due to
* signal delivery.
* IP_ERRNO_ENOMEM - failed to allocate a poll signal
*/
IP_GLOBAL int
ipnet_usr_sock_wait_until_writable(Ipnet_socket *sock,
int lowat,
struct Ip_timeval *timeout)
{
if (ipnet_is_sock_writable(sock, lowat))
return 0;
return ipnet_usr_sock_wait_until(sock,
lowat,
timeout,
IPNET_SOCK_POLL_WRITE);
}
/*
*===========================================================================
* ipnet_usr_sock_recvmsg
*===========================================================================
* Description: Receive implementation that works for all protocols that
* has its data queued as Ipcom_pkt:s at the sock->rcv_head
* and sock->rcv_tail pointers. One packet will be returned
* in each call and removed from the queue.
* Parameters: sock - The socket to receive the packet from.
* msg - Receive parameters.
* flags - IP_MSG_xxx flags.
* Returns: 0 = a packet was successfully received, -1 = error.
*
*/
IP_GLOBAL int
ipnet_usr_sock_recvmsg(Ipnet_socket *sock, struct Ip_msghdr *msg, int flags)
{
Ipcom_pkt *pkt;
int pkt_offset;
int buf_len;
Ip_u8 *buf;
int bytes;
int i;
int read_bytes;
struct Ip_msghdr sig_msg;
Ipcom_pkt *next_part;
int bytes_next_part;
IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_INFO,
1, 12, IPCOM_WV_NETDEVENT_INFO, IPCOM_WV_NETD_RECV,
ipnet_usr_sock_recvmsg, IPCOM_WV_NETD_INFO_RECEIVE,
IPCOM_WV_IPNET_SOCK_MODULE, IPCOM_WV_NETD_SOCKET);
IPNET_STATS(usr_sock_recvmsg++);
ip_assert(sock != IP_NULL);
ipcom_spinlock_lock(&sock->rcv_lock);
pkt = sock->rcv_head;
if (pkt == IP_NULL)
{
ipcom_spinlock_unlock(&sock->rcv_lock);
IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_INFO,
1, 13, IPCOM_WV_NETDEVENT_INFO, IPCOM_WV_NETD_RECV,
ipnet_usr_sock_recvmsg, IPCOM_WV_NETD_INFO_WAIT,
IPCOM_WV_IPNET_SOCK_MODULE, IPCOM_WV_NETD_SOCKET);
IPNET_STATS(usr_sock_recvmsg_ewouldblock++);
return -IP_ERRNO_EWOULDBLOCK;
}
if (IP_LIKELY(IP_BIT_ISFALSE(flags, IP_MSG_PEEK)))
{
/*
* Unlink the first packet from the queue head.
*/
sock->ipcom.rcv_bytes -= ipnet_pkt_get_maxlen(pkt);
sock->rcv_head = pkt->next;
if (sock->rcv_head == IP_NULL)
sock->rcv_tail = IP_NULL;
}
ipcom_spinlock_unlock(&sock->rcv_lock);
/*
* Copy of sender info.
*/
if (msg->msg_name != IP_NULL)
{
Ip_size_t namelen = msg->msg_namelen;
ipnet_copy_sender_info(sock,
pkt,
msg->msg_name,
&namelen);
msg->msg_namelen = (Ip_socklen_t)namelen;
}
#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
*/
Ipnet_netif *netif = ipnet_if_indextonetif(pkt->vr_index, pkt->ifindex);
if (netif != IP_NULL)
pkt->start += netif->ipcom.link_hdr_size;
}
#endif /* IPCOM_USE_PACKET */
#ifdef IPCOM_ZEROCOPY
if (IP_BIT_ISSET(flags, IP_MSG_ZBUF))
{
/*
* The buffer will contain a pointer to a Ipcom_pkt upon
* return
*/
ip_assert(msg->msg_iov[0].iov_len >= sizeof(Ipcom_pkt*));
*(Ipcom_pkt**)msg->msg_iov[0].iov_base = pkt;
return ipcom_pkt_get_length(pkt);
}
#endif /*IPCOM_ZEROCOPY*/
if (msg->msg_control != IP_NULL)
{
#ifdef IP_PORT_OSE5
Ipnet_sig sig;
sig.d.ancillary_data.fd = sock->ipcom.fd;
sig.d.ancillary_data.msg = msg;
sig.d.ancillary_data.pkt = pkt;
(void)ipnet_sys_get_ancillary_data(&sig.d.ancillary_data);
#else
Ipnet_sig *sig;
/* Malloc memory for sig and msg control(for msg in sig) */
sig = ipcom_malloc(sizeof(Ipnet_sig)+ msg->msg_controllen);
if (sig == IP_NULL)
{
(void)ipcom_errno_set(IP_ERRNO_ENOMEM);
goto errout;
}
/* Copy original msg to msg in sig */
ipcom_memcpy((void *)&sig_msg, msg, sizeof(struct Ip_msghdr));
sig_msg.msg_control = (void *)(sig + 1);
sig->sig_type = IPNET_SIG_GETANCILLARYDATA;
sig->d.ancillary_data.fd = sock->ipcom.fd;
/*
* msg pointer from RTP space can not be accessed by tNet0, so
* sig_msg is used here
*/
sig->d.ancillary_data.msg = &sig_msg;
sig->d.ancillary_data.pkt = pkt;
if (!ipnet_usr_sock_basic_send_recv(sig, IP_NULL))
goto errout;
/* Copy the info get by IPNET_SIG_GETANCILLARYDATA back to RTP space */
ipcom_memcpy(msg->msg_control,sig_msg.msg_control,sig_msg.msg_controllen);
msg->msg_controllen = sig_msg.msg_controllen;
msg->msg_flags = sig_msg.msg_flags;
ipcom_free(sig);
#endif /* IP_PORT_OSE5 */
}
next_part = pkt;
pkt_offset = next_part->start;
for (bytes = 0, i = 0; i < msg->msg_iovlen && next_part != IP_NULL; i++)
{
buf = (Ip_u8 *) msg->msg_iov[i].iov_base;
buf_len = (int)msg->msg_iov[i].iov_len;
bytes_next_part = 0;
while (next_part != IP_NULL)
{
if (pkt_offset >= next_part->end)
{
next_part = next_part->next_part;
if (IP_NULL == next_part)
break;
pkt_offset = next_part->start;
}
if (bytes_next_part >= buf_len)
break;
read_bytes = IP_MIN(buf_len-bytes_next_part, (int) next_part->end - pkt_offset);
ipcom_memcpy(buf+bytes_next_part, &next_part->data[pkt_offset], read_bytes);
bytes += read_bytes;
bytes_next_part += read_bytes;
pkt_offset += read_bytes;
}
}
if (bytes != ipcom_pkt_get_length(pkt))
IP_BIT_SET(msg->msg_flags, IP_MSG_TRUNC);
if (IP_BIT_ISFALSE(flags, IP_MSG_PEEK))
ipcom_pkt_free(pkt);
return bytes;
errout:
return IP_SOCKERR;
}
/*
****************************************************************************
* 11 PUBLIC FUNCTIONS
****************************************************************************
* The prototypes for these functions are located in
* <IPCOM>/include/ipnet_sock.h
*/
/*
*===========================================================================
* 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.
* from - 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
* 'from' buffer, will hold the actual 'from'
* length upon return. Set to IP_NULL if 'from' 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 *from,
Ip_socklen_t *fromlen)
{
Ipnet_sig *sig;
Ipnet_socket *sock;
int ret = 0;
sock = ipnet_get_sock(fd);
if (sock == IP_NULL)
return IP_SOCKERR;
if (IP_BIT_ISFALSE(sock->flags, IPNET_SOCKET_FLAG_NONBLOCKING))
ret = ipnet_usr_sock_wait_until_readable(sock, 0, IP_NULL);
ipnet_release_sock(sock);
if (ret != 0)
{
ipcom_errno_set(ret); /* EINTR or ENOMEM */
return IP_SOCKERR;
}
sig = ipcom_malloc(sizeof(Ipnet_sig));
if (sig == IP_NULL)
{
(void)ipcom_errno_set(IP_ERRNO_ENOMEM);
return IP_SOCKERR;
}
if ((fromlen != IP_NULL) && (*fromlen > sizeof (sig->d.accept.from)))
{
(void)ipcom_errno_set(IP_ERRNO_EINVAL);
ipcom_free(sig);
return IP_SOCKERR;
}
sig->sig_type = IPNET_SIG_ACCEPT;
sig->d.accept.fd = fd;
if (from == IP_NULL || fromlen == IP_NULL)
sig->d.accept.fromlen = 0;
else
sig->d.accept.fromlen = *fromlen;
if (ipnet_usr_sock_basic_send_recv(sig, &ret))
{
if (ret >= 0 && from && fromlen)
{
ipcom_memcpy(from,
&sig->d.accept.from,
sig->d.accept.fromlen);
*fromlen = sig->d.accept.fromlen;
}
ipcom_free(sig);
}
return ret;
}
/*
*===========================================================================
* 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)
{
Ipnet_sig *sig;
int ret;
sig = ipcom_malloc(sizeof(Ipnet_sig));
if (sig == IP_NULL)
{
(void)ipcom_errno_set(IP_ERRNO_ENOMEM);
return IP_SOCKERR;
}
sig->sig_type = IPNET_SIG_BIND;
sig->d.bind.fd = fd;
if (name == IP_NULL || namelen == IP_NULL)
sig->d.bind.namelen = 0;
else
{
sig->d.bind.namelen = namelen;
ipcom_memcpy(&sig->d.bind.name,
name,
IP_MIN(namelen, sizeof(sig->d.bind.name)));
}
if (ipnet_usr_sock_basic_send_recv(sig, &ret))
ipcom_free(sig);
return ret;
}
/*
*===========================================================================
* 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)
{
Ipnet_sig *sig;
int ret;
sig = ipcom_malloc(sizeof(Ipnet_sig));
if (sig == IP_NULL)
{
(void)ipcom_errno_set(IP_ERRNO_ENOMEM);
return IP_SOCKERR;
}
sig->sig_type = IPNET_SIG_CONNECT;
sig->d.connect.fd = fd;
if (to == IP_NULL || tolen == IP_NULL)
sig->d.connect.tolen = 0;
else
{
sig->d.connect.tolen = tolen;
ipcom_memcpy(&sig->d.connect.to,
to,
IP_MIN(tolen, sizeof(sig->d.connect.to)));
}
if (ipnet_usr_sock_basic_send_recv(sig, &ret))
{
ipcom_free(sig);
if (ret < 0 && ipcom_errno == IP_ERRNO_EINPROGRESS)
{
Ipnet_socket *sock;
Ip_bool is_blocking;
sock = ipnet_get_sock(fd);
if (sock == IP_NULL)
return IP_SOCKERR;
is_blocking = IP_BIT_ISFALSE(sock->flags, IPNET_SOCKET_FLAG_NONBLOCKING);
if (is_blocking)
{
int err = ipnet_usr_sock_wait_until_writable(sock, IP_NULL, 0);
if (0 == err && 0 == (err = sock->ipcom.so_errno))
{
(void)ipcom_errno_set(0);
ret = 0;
}
else
(void)ipcom_errno_set(err);
}
ipnet_release_sock(sock);
}
}
return ret;
}
/*
*===========================================================================
* 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)
{
return ipnet_usr_sock_getname(fd, name, namelenp, IP_TRUE);
}
/*
*===========================================================================
* ipcom_getsockopt
*===========================================================================
* Description: Gets the specified option on a socket. Options may
* exist at multiple protocol levels.
* Parameters: fd - A socket descriptor.
* level - The protocol level that should be affected by the
* option.
* optname - The option name.
* optval - Pointer to a buffer where the option data should be
* copied..
* optlen - The length of the buffer poited to by optval.
* Returns: 0 = success, -1 = error.
*
*/
IP_PUBLIC int
ipcom_getsockopt(Ip_fd fd,
int level,
int optname,
void *optval,
Ip_socklen_t *optlen)
{
return ipnet_usr_sockopt(fd,
level,
optname,
optval,
optlen,
IPNET_SIG_GETSOCKOPT);
}
/*
*===========================================================================
* 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)
{
return ipnet_usr_sock_getname(fd, name, namelenp, IP_FALSE);
}
/*
*===========================================================================
* ipcom_socketioctl
*===========================================================================
* Description: IO control.
* Parameters: fd - A socket descriptor.
* request - The IO control request.
* argp - Request specific data.
* Returns: 0 = success, -1 = error (error code from ipcom_errno).
*
*/
IP_PUBLIC int
ipcom_socketioctl(Ip_fd fd, unsigned long request, void *argp)
{
#ifdef IPCOM_FORWARDER_NAE
return ipnet_sys_socketioctl(fd, request, argp);
#else
int ret = -1;
Ipnet_sig *sig;
Ip_size_t arg_len;
#ifdef IP_PORT_VXWORKS
struct Ip_ifconf *arg_ifconf = argp;
struct Ip_ifconf *ifconf = IP_NULL;
#endif
/*
* Handle possible port issues with conversion of ioctl code
* from 32 bit signed int -> 64-bit unsigned long.
*/
request &= 0xffffffff;
#ifdef IP_PORT_VXWORKS
/* Only vxWorks RTP requires deep copy of *argp */
arg_len = IP_IOCPARM_LEN(request);
if (request == IP_SIOCGIFCONF)
arg_len += arg_ifconf->ifc_len;
#else
arg_len = 0;
#endif
/* parameter check, for exception. */
if (IP_NULL == argp)
{
ipcom_errno_set(IP_ERRNO_EINVAL);
return ret;
}
/*
* Allocate an extra sizeof(void*) byte to be sure that
* IP_PTR_ALIGN() does cause this function to reference data
* beyond the end of the buffer.
*/
sig = ipcom_malloc(sizeof(Ipnet_sig) + sizeof(void*) + arg_len);
if (sig == IP_NULL)
{
ipcom_errno_set(IP_ERRNO_ENOMEM);
return ret;
}
sig->sig_type = IPNET_SIG_IOCTL;
sig->d.ioctl.fd = fd;
sig->d.ioctl.request = request;
#ifdef IP_PORT_VXWORKS
sig->d.ioctl.argp = IP_PTR_ALIGN((sig + 1), sizeof(void*));
/* vxWorks RTP copy user data */
ipcom_memcpy(sig->d.ioctl.argp, argp, IP_IOCPARM_LEN(request));
if (request == IP_SIOCGIFCONF)
{
ifconf = sig->d.ioctl.argp;
ifconf->ifc_ifcu.ifcu_buf = (void *) (ifconf + 1);
}
if (!ipnet_usr_sock_basic_send_recv(sig, &ret))
/* EINTER */
return IP_SOCKERR;
if (ret >= 0)
{
/* Copy results back to RTP space */
if (request == IP_SIOCGIFCONF)
{
/* Copy back the embedded buffer */
arg_ifconf->ifc_len = ifconf->ifc_len;
memcpy(arg_ifconf->ifc_ifcu.ifcu_buf,
ifconf->ifc_ifcu.ifcu_buf,
ifconf->ifc_len);
}
else
ipcom_memcpy(argp, sig->d.ioctl.argp, IP_IOCPARM_LEN(request));
}
#else
sig->d.ioctl.argp = argp;
if (!ipnet_usr_sock_basic_send_recv(sig, &ret))
/* EINTER */
return IP_SOCKERR;
#endif
ipcom_free(sig);
return ret;
#endif /* IPCOM_FORWARDER_NAE */
}
/*
*===========================================================================
* ipcom_listen
*===========================================================================
* Description: Places a socket a state where it is listening for
* incoming connection(s).
* Parameters: fd - the socket to create a backlog for.
* backlog - The maximum length of the queue of pending
* connections. Set to 0 to allow the stack to
* select a reasonably sized backlog.
* Returns: 0 = success, -1 = error.
*
*/
IP_PUBLIC int
ipcom_listen(Ip_fd fd, int backlog)
{
Ipnet_sig *sig;
int ret;
sig = ipcom_malloc(sizeof(Ipnet_sig));
if (sig == IP_NULL)
{
(void)ipcom_errno_set(IP_ERRNO_ENOMEM);
return IP_SOCKERR;
}
sig->sig_type = IPNET_SIG_LISTEN;
sig->d.listen.fd = fd;
sig->d.listen.backlog = backlog;
if (ipnet_usr_sock_basic_send_recv(sig, &ret))
ipcom_free(sig);
return ret;
}
/*
*===========================================================================
* 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_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 - Receive flags
* 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.
*
*/
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)
{
struct Ip_msghdr msg;
struct Ip_iovec iov[1];
int ret;
iov[0].iov_base = buf;
iov[0].iov_len = len;
if (fromlen != IP_NULL)
msg.msg_namelen = *fromlen;
else
msg.msg_namelen = 0;
msg.msg_name = from;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = IP_NULL;
msg.msg_controllen = 0;
msg.msg_flags = flags;
ret = (int)ipcom_recvmsg(fd, &msg, flags);
if (fromlen != IP_NULL)
*fromlen = msg.msg_namelen;
return (Ip_ssize_t)ret;
}
/*
*===========================================================================
* ipcom_recvmsg
*===========================================================================
* Description: Receives data from a socket.
* Parameters: fd - A socket descriptor.
* msg - Receive parameters.
* flags - IP_MSG_xxx flags.
* Returns: 0 = a packet was successfully received, -1 = error.
*
*/
IP_PUBLIC Ip_ssize_t
ipcom_recvmsg(Ip_fd fd, struct Ip_msghdr *msg, int flags)
{
int bytes;
int lowat;
Ipnet_socket *sock;
Ip_bool is_blocking;
int err;
IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_INFO,
1, 10, IPCOM_WV_NETDEVENT_INFO, IPCOM_WV_NETD_RECV,
ipcom_recvmsg, IPCOM_WV_NETD_INFO_RECEIVE,
IPCOM_WV_IPNET_SOCK_MODULE, IPCOM_WV_NETD_SOCKET);
IPNET_STATS(sock_recvmsg++);
ip_assert2(msg != IP_NULL);
retry:
lowat = 0;
sock = ipnet_get_sock(fd);
if (IP_UNLIKELY(sock == IP_NULL))
{
(void)ipcom_errno_set(IP_ERRNO_EBADF);
return IP_SOCKERR;
}
if (IP_UNLIKELY (IP_BIT_ISSET(flags, IP_MSG_OOB)) &&
sock->ipcom.type != IP_SOCK_STREAM)
{
/* IP_MSG_OOB is only used by IP_SOCK_STREAM */
ipnet_release_sock (sock);
(void)ipcom_errno_set(IP_ERRNO_EOPNOTSUPP);
return IP_SOCKERR;
}
err = 0;
is_blocking = IP_BIT_ISFALSE(sock->flags, IPNET_SOCKET_FLAG_NONBLOCKING)
&& IP_BIT_ISFALSE(flags, IP_MSG_DONTWAIT);
if (is_blocking)
{
int i;
bytes = 0;
for (i = 0; i < msg->msg_iovlen; i++)
bytes += (int)msg->msg_iov[i].iov_len;
if (IP_UNLIKELY(IP_BIT_ISSET(flags, IP_MSG_WAITALL)))
lowat = bytes;
if (IP_LIKELY(IP_BIT_ISFALSE(flags, IP_MSG_OOB))
&& IP_LIKELY(bytes > 0))
{
err = ipnet_usr_sock_wait_until_readable(sock,
lowat,
sock->rcv_timeout);
is_blocking = (err == 0);
}
/* else: always possible to read zero bytes */
}
if (err == 0)
err = sock->ipcom.so_errno;
if (sock->ipcom.rcv_bytes > 0
|| (IP_LIKELY(err == 0)
&& IP_LIKELY(IP_BIT_ISFALSE(sock->flags, IPNET_SOCKET_FLAG_READ_CLOSED))))
{
Ipnet_sock_ops *ops;
/*
* Any queued data should be delivered before the error is
* delivered
*/
ops = ipnet_sock_get_ops(sock);
bytes = ops->usr_recv(sock, msg, flags);
}
else
{
if (err == IP_ERRNO_EINTR && sock->ipcom.so_errno == IP_ERRNO_EINTR)
sock->ipcom.so_errno = 0;
bytes = (-err);
msg->msg_controllen = 0;
}
ipnet_release_sock(sock);
if (is_blocking
&& IP_UNLIKELY(bytes == -IP_ERRNO_EWOULDBLOCK)
&& IP_BIT_ISFALSE(flags, IP_MSG_OOB))
goto retry;
if (IP_UNLIKELY(bytes < 0))
{
(void)ipcom_errno_set(-bytes);
return IP_SOCKERR;
}
return bytes;
}
/*
*===========================================================================
* 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_sendmsg
*===========================================================================
* 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.
* msg - Send parameters.
* flags - IP_MSG_xxx flags.
* Returns: >=0 = number of bytes sent, IP_SOCKERR = error code is
* retrieved by using ipcom_errno.
*
*/
IP_PUBLIC Ip_ssize_t
ipcom_sendmsg(Ip_fd fd, IP_CONST struct Ip_msghdr *msg, int flags)
{
Ip_ssize_t len;
int total_buf_len = 0;
int offset = 0;
int err;
int i;
Ip_socklen_t namelen;
Ipnet_socket *sock;
Ipnet_sig *sig;
Ipnet_sig *rsig = IP_NULL;
Ip_bool is_synchronous;
Ip_bool non_blocking;
Ip_bool basic_dgram;
Ipcom_pkt *pkt;
Ipcom_pkt *next_pkt;
struct Ip_msghdr msgbuf;
Ipnet_sock_ops *ops;
Ip_u16 type;
Ip_u16 domain;
Ipnet_pkt_result rslt;
#ifdef IPCOM_ZEROCOPY
Ipcom_pkt * orig_pkt = IP_NULL;
#endif
sock = ipnet_get_sock(fd);
if (IP_UNLIKELY(sock == IP_NULL))
{
err = IP_ERRNO_EBADF;
goto errout;
}
type = sock->ipcom.type;
domain = sock->ipcom.domain;
#ifdef IPCOM_ZEROCOPY
if (IP_BIT_ISSET(flags, IP_MSG_ZBUF))
{
/*
* For anything other than UDP, we currently support
* zbufs only for API compatibility, and must resort
* to a copying send call.
*/
if ((domain != IP_AF_INET && domain != IP_AF_INET6)
|| type != IP_SOCK_DGRAM)
{
struct Ip_msghdr lmsg;
struct Ip_iovec *iovec = IP_NULL;
int ret = 0;
Ipcom_pkt *zpkt;
ipnet_release_sock (sock);
sock = IP_NULL;
zpkt = msg->msg_iov[0].iov_base;
lmsg = *msg;
lmsg.msg_iov = iovec;
lmsg.msg_iovlen = 0;
for (pkt = zpkt, i = 0;
pkt != IP_NULL;
pkt = pkt->next_part, i++);
if (IP_LIKELY(i > 0))
{
iovec = ipcom_malloc(sizeof(struct Ip_iovec) * i);
if (IP_UNLIKELY(iovec == IP_NULL))
{
/*
* Only for EWOULDBLOCK does the caller keep
* ownership of the packet.
*/
ipcom_pkt_free (zpkt);
err = IP_ERRNO_ENOMEM;
goto errout;
}
lmsg.msg_iov = iovec;
for (pkt = zpkt, i = 0;
pkt != IP_NULL;
pkt = pkt->next_part, i++)
{
lmsg.msg_iov[i].iov_base = ipcom_pkt_get_data(pkt, 0);
lmsg.msg_iov[i].iov_len = pkt->end - pkt->start;
lmsg.msg_iovlen++;
}
}
/* Do non-zbuf send */
ret = (int)ipcom_sendmsg(fd, &lmsg, flags & ~IP_MSG_ZBUF);
if (zpkt != IP_NULL)
{
if (ret != -1 || ipcom_errno != IP_ERRNO_EWOULDBLOCK)
ipcom_pkt_free(zpkt);
ipcom_free(iovec);
}
return ret;
}
}
#endif /* IPCOM_ZEROCOPY */
/*
* Check for illegal flags.
* IP_MSG_EXP is only used by COMP so far.
* IP_MSG_OOB is only used by IP_SOCK_STREAM.
*/
if ( (IP_UNLIKELY(IP_BIT_ISSET(flags, IP_MSG_EXP)) &&
(domain != IP_AF_LOCAL || type != IP_SOCK_SEQPACKET))
|| (IP_UNLIKELY(IP_BIT_ISSET(flags, IP_MSG_OOB)) &&
type != IP_SOCK_STREAM))
{
err = IP_ERRNO_EOPNOTSUPP;
goto errout; /* errout releases sock */
}
if (IP_UNLIKELY(sock->ipcom.so_errno != 0))
{
err = sock->ipcom.so_errno;
goto errout;
}
ops = ipnet_sock_get_ops(sock);
if (ops->connect != IP_NULL)
{
if (IP_BIT_ISSET(sock->flags,
IPNET_SOCKET_FLAG_CONNECTED |
IPNET_SOCKET_FLAG_CONNECTING))
{
if (IP_UNLIKELY(msg->msg_name != IP_NULL))
{
IPCOM_LOG1(DEBUG,
"SOCKET: a 'to' address must NOT be specified, socket %d is connected",
sock->ipcom.fd);
err = IP_ERRNO_EISCONN;
goto errout;
}
}
else
{
if (IP_UNLIKELY(msg->msg_name == IP_NULL))
{
if (sock->snd_head == IP_NULL)
{
IPCOM_LOG1(DEBUG,
"SOCKET: a 'to' address must be specified for socket %d",
sock->ipcom.fd);
err = IP_ERRNO_EDESTADDRREQ;
goto errout;
}
/* else: the to address has been specified in an
earlier call that had IP_MSG_MORE set, reuse the
address */
ipcom_memcpy(&msgbuf, msg, sizeof(msgbuf));
msg = &msgbuf;
msgbuf.msg_name = sock->snd_to;
msgbuf.msg_namelen = IPCOM_SA_LEN_GET(sock->snd_to);
}
else if (IP_UNLIKELY(((struct Ip_sockaddr *)msg->msg_name)->sa_family
!= sock->ipcom.domain))
{
IPCOM_LOG3(DEBUG, "invalid domain for socket %d, must be %d, found %d",
sock->ipcom.fd,
sock->ipcom.domain,
((struct Ip_sockaddr *)msg->msg_name)->sa_family);
err = IP_ERRNO_EAFNOSUPPORT;
goto errout;
}
#ifdef IPCOM_USE_INET
else if (IP_UNLIKELY((sock->ipcom.domain == IP_AF_INET) &&
(msg->msg_namelen != sizeof(struct Ip_sockaddr_in))))
{
IPCOM_LOG0(DEBUG, "invalid length of sockaddr structure for IPv4 address family");
err = IP_ERRNO_EINVAL;
goto errout;
}
#endif
#ifdef IPCOM_USE_INET6
else if (IP_UNLIKELY((sock->ipcom.domain == IP_AF_INET6) &&
(msg->msg_namelen != sizeof(struct Ip_sockaddr_in6))))
{
IPCOM_LOG0(DEBUG, "invalid length of sockaddr structure for IPv6 address family");
err = IP_ERRNO_EINVAL;
goto errout;
}
#endif
}
}
non_blocking = IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_NONBLOCKING)
|| IP_BIT_ISSET(flags, IP_MSG_DONTWAIT);
namelen = (Ip_socklen_t)IP_MIN(msg->msg_namelen, sizeof(sig->d.sendmsg.to));
basic_dgram =
(ops->pkts_from_iov == ipnet_usr_sock_create_pkt_from_iov);
#ifdef IPCOM_ZEROCOPY
if (IP_BIT_ISSET(flags, IP_MSG_ZBUF))
{
/*
* zbuf sends are documented to ignore the length specified in msg_iovlen,
* and send the whole packet -- see zbufSockSendto() documentation.
* In case the user specified the wrong length, we want total_buf_len
* to be the full packet length; we never want to go around the loop more
* than once for UDP.
*/
orig_pkt = msg->msg_iov[0].iov_base;
total_buf_len = ipcom_pkt_get_length(orig_pkt);
}
else
#endif
{
total_buf_len = 0;
for (i = 0; i < msg->msg_iovlen; i++)
total_buf_len += (int) msg->msg_iov[i].iov_len;
}
if (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_SENDMSG_ASYNCHRONOUS))
{
is_synchronous = IP_FALSE;
}
else
{
if (namelen == 0)
is_synchronous = (sock->ipcom.domain == IP_AF_ROUTE);
else
{
/*
* Must use synchronous send if the 'to' address is
* different from last call.
*/
is_synchronous = (ipcom_memcmp(&sock->last_to,
msg->msg_name,
namelen) != 0);
ipcom_memcpy(&sock->last_to,
msg->msg_name,
namelen);
}
if (is_synchronous == IP_FALSE && basic_dgram && total_buf_len > 65000)
{
/*
* To get a better performance when sending a max udp buffer len, here we make
* a synchronous send when sending the first packet of this flow and another
* synchronous send when successfully sending 100 times.
*/
if (total_buf_len > sock->biggest_msg_size_in_sync)
{
sock->large_send_countdown = 99;
sock->biggest_msg_size_in_sync = (Ip_u16)total_buf_len;
is_synchronous = IP_TRUE;
}
else
{
if (IP_UNLIKELY(sock->large_send_countdown == 0))
{
sock->large_send_countdown = 100;
is_synchronous = IP_TRUE;
}
sock->large_send_countdown--;
}
}
}
#ifdef IPCOM_ZEROCOPY
/* zbuf UDP packets go here */
if (IP_BIT_ISSET(flags, IP_MSG_ZBUF))
{
Ipcom_pkt *header;
Ipnet_pkt_info_sock_snd_buf snd_buf_info;
pkt = orig_pkt;
len = total_buf_len;
if (len > sock->send_max_bytes)
{
IPNET_STATS(usr_iov_emsgsize++);
IPCOM_LOG3(WARNING, "sent message discarded on socket %d since the send buffer is only %d, %d was required",
sock->ipcom.fd, sock->send_max_bytes, len);
ipcom_pkt_free(pkt);
err = IP_ERRNO_EMSGSIZE;
goto errout;
}
/*
* Just in case the caller specified an incorrect length
* in the argument to zbufSockSendto(), which is documented
* to ignore the provided length and always send the whole
* packet. We never want to go around the loop more than
* once for UDP.
*/
if ((total_buf_len = (int)len) > 65000
&& IP_BIT_ISFALSE(sock->flags, IPNET_SOCKET_FLAG_SENDMSG_ASYNCHRONOUS))
is_synchronous = IP_TRUE;
header = ipcom_pkt_malloc(IPNET_ZBUF_MAX_HEADER_LEN + 32, IP_FLAG_FC_STACKCONTEXT);
if (header == IP_NULL)
{
err = IP_ERRNO_ENOMEM;
if (pkt != IP_NULL)
ipcom_pkt_free(pkt);
goto errout;
}
header->start = header->end = header->maxlen - 32;
header->chk = 0;
/* Calculate the checksum for zbuf UDP packet payload */
if (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_ADDCHECKSUM))
header->chk = ipcom_in_checksum_update_pkt(pkt, 0);
/*
* VXW6-85066: Avoid cases where we cannot zero-pad short Ethernet
* frames due to lack of space in the zbuf application data buffer.
* We copy the data of such short frames into the header buffer.
* Note, 28 (IPv4 + UDP) + 32 (UDP payload) = 60 bytes, the minimum
* Ethernet frame size.
*/
if (len <= 32)
{
for (; pkt != IP_NULL; pkt = pkt->next_part)
{
int l = pkt->end - pkt->start;
ipcom_memcpy(ipcom_pkt_push_back(header, l),
ipcom_pkt_get_data(pkt, 0),
(Ip_size_t)l);
}
}
else
header->next_part = pkt;
pkt = header;
while (! ipnet_alloc_snd_bytes(sock, (int)len))
{
/*
* The requested number of bytes cannot be sent at the
* moment
*/
if (non_blocking)
{
/*
* Non-blocking send. Note that zbufSockSendto() expects
* to maintain ownership of the original packet when there
* is an EWOULDBLOCK error return.
*/
pkt = orig_pkt;
header->next_part = IP_NULL;
ipcom_pkt_free(header);
err = IP_ERRNO_EWOULDBLOCK;
goto errout;
}
err = ipnet_usr_sock_wait_until_writable(sock, (int)len, IP_NULL);
if (err != 0 || 0 != (err = sock->ipcom.so_errno))
{
if (err == IP_ERRNO_EINTR || err == IP_ERRNO_EWOULDBLOCK)
{
/*
* For these errors the caller should maintain ownership
* of the original packet.
* (Note, EINTR should not actually occur for a kernel task,
* and zbufs don't support RTP usage.)
*/
header->next_part = IP_NULL;
}
ipcom_pkt_free(header);
goto errout;
}
}
snd_buf_info.sock = sock;
snd_buf_info.payload_len = (int)len;
ipcom_pkt_set_info(pkt,
IPNET_PKT_INFO_SOCK_SND_BUF,
sizeof(snd_buf_info),
&snd_buf_info);
pkt->dtor = sock->pkt_dtor;
/*
* In case len <= 32, we would like to free orig_pkt here. However,
* it's still possible that we return EWOULDBLOCK, if we are non_blocking and
* the ipnet task pipe is full. So we have to wait until later to find out,
* and ensure that orig_pkt is freed (or not) as appropriate in all
* code paths.
*/
rslt.transient_err = 0;
goto havelen;
}
#endif /* IPCOM_ZEROCOPY */
for (;;) /* main loop */
{
len = ops->pkts_from_iov(sock,
msg,
flags,
offset,
total_buf_len,
&rslt);
if (IP_UNLIKELY(len < 0))
{
if (offset)
break;
err = (int)-len;
goto errout;
}
pkt = rslt.pkt;
#ifdef IPCOM_ZEROCOPY
havelen:
#endif
/*
* Packet will be IP_NULL if all the data was appended to a
* previous packet that has not been sent yet
*/
if (pkt != IP_NULL)
{
void *msg_control = IP_NULL;
IPCOM_PKT_TRACE(pkt, IPCOM_PKT_ID_SENT_PIPE);
/* coverity[returned_null] */
sig = ipnet_usr_sock_pkt_to_sig(pkt);
/* coverity[dereference] */
ipnet_usr_sock_init_sig(sig,
IPNET_SIG_SENDMSG);
sig->d.sendmsg.fd = fd;
sig->d.sendmsg.pkt = pkt;
sig->d.sendmsg.msg.msg_namelen = namelen;
sig->d.sendmsg.msg.msg_name = msg->msg_namelen ? &sig->d.sendmsg.to : IP_NULL;
if (msg->msg_control == IP_NULL
|| msg->msg_controllen == 0)
{
sig->d.sendmsg.msg.msg_control = IP_NULL;
sig->d.sendmsg.msg.msg_controllen = 0;
}
else
{
sig->d.sendmsg.msg.msg_controllen = msg->msg_controllen;
msg_control = sig->d.sendmsg.msg.msg_control
= ipcom_malloc(msg->msg_controllen);
if (msg_control == IP_NULL)
{
err = IP_ERRNO_ENOMEM;
goto cannot_send_pkt;
}
ipcom_memcpy(msg_control,
msg->msg_control,
msg->msg_controllen);
}
/*
* The 'msg_flags' is not used by send operations, let us
* store the send 'flags' in the 'msg_flags' field.
*/
ip_assert(sig->sig_type == IPNET_SIG_SENDMSG);
sig->d.sendmsg.msg.msg_flags = flags;
ipcom_memcpy(&sig->d.sendmsg.to,
msg->msg_name,
namelen);
/*
* Note, ipnet_sys_sendmsg() sets sock->snd_route_cache_id
* to the ipnet global route_cache_id upon completing a successful
* send, and clears it upon an unsuccessful send.
* Configuration changes may modify ipnet->route_cache_id
* or clear sock->snd_route_cache_id, and suggest a synchronous
* send. But never do a synchronous send if the user has demanded
* asynchronous sends always on this socket. Note that in this
* case, send failures detected by ipnet_sys_sendmsg() are not
* reported to the application.
*/
if (IP_BIT_ISFALSE(sock->flags, IPNET_SOCKET_FLAG_SENDMSG_ASYNCHRONOUS)
&& IP_UNLIKELY(*ipnet_usr_sock_get_route_cache_id()
!= sock->snd_route_cache_id))
{
is_synchronous = IP_TRUE;
}
if (IP_UNLIKELY(is_synchronous))
{
if (rsig == IP_NULL)
{
/* TODO: consider using an object slab cache here */
rsig = ipcom_malloc(sizeof(*rsig));
if (rsig == IP_NULL)
{
if (msg_control)
ipcom_free(msg_control);
err = IP_ERRNO_ENOMEM;
goto cannot_send_pkt;
}
}
/*
* First call or last call failed, must make a
* synchronous call to see if the send succeeds
*/
ipnet_usr_sock_init_sig(rsig,
IPNET_SIG_SENDMSG);
rsig->d.sendmsg.fd = -1; /* fd not needed in reply signal */
sig->d.sendmsg.rsig = rsig;
}
else
{
/*
* Stack state unchanged since last successful call,
* this call will succeed as well, so the send can be
* asynchronous
*/
sig->d.sendmsg.rsig = IP_NULL;
}
if (non_blocking && basic_dgram)
{
/*
* For non-blocking datagram sends, we choose
* not to wait if the ipnet task pipe is full,
* even though the waiting would only last until
* the network task serviced some requests from
* the pipe.
*
* Freeing the packet will discharge bytes in
* the packet charged against the socket's send
* buffer. Also, the 'do' loop is traversed only
* once for basic datagram sockets.
*
* For TCP or SCTP, freeing the packet does not
* subtract bytes charged against the socket
* send buffer, and we would also have to worry
* about data sent on a previous iteration of
* the loop, or added to tcb->send.partial for TCP.
*
*/
if (IP_FALSE == ipcom_pipe_try_send(ipnet->task.pipe,sig))
{
#ifdef IPCOM_ZEROCOPY
/* Do not free original packet when zbuf send returns EWOULDBLOCK */
if (orig_pkt != IP_NULL)
pkt->next_part = IP_NULL;
#endif
while (pkt != IP_NULL)
{
next_pkt = pkt->next;
ipcom_pkt_free(pkt);
pkt = next_pkt;
}
err = IP_ERRNO_EWOULDBLOCK;
ip_assert(offset == 0);
goto errout;
}
}
else
ipcom_pipe_send(ipnet->task.pipe, sig);
if (IP_UNLIKELY(is_synchronous))
{
/*
* We've decided that this is a send that could perhaps
* fail, so we must wait for a reply. Note that presently
* we wait here for a send result reply even if the send
* call is non-blocking, unless IPNET_SOCKET_FLAG_SENDMSG_ASYNCHRONOUS
* was set in the socket flags.
*
* For a synchronous IPNET_SIG_SENDMSG request,
* the response is expected as soon as the network
* task processes the request. There is no waiting
* for any external event. So we restart the
* wait for the reply in case it is interrupted;
* but we remember that the interrupt occurred
* so that we do not attempt to go around the main
* for (;;) loop another time.
*/
while (IP_TRUE)
{
int tmp;
tmp = ipnet_usr_sock_pipe_recv(rsig->response_pipe,
&sig,
rsig,
IP_NULL,
0);
if (IP_LIKELY(tmp >= 0))
break;
if (rslt.transient_err == 0 && tmp == IPCOM_ERR_INTERRUPT)
rslt.transient_err = IP_ERRNO_EINTR;
/* Restart operation, but remember transient err */
}
ip_assert(sig == rsig);
ip_assert(sig->sig_type == IPNET_SIG_SENDMSG);
if (sig->reply.return_code < 0)
{
/* Failed to send this msg, zero the sync msg size and large_send_countdown */
sock->large_send_countdown = 0;
sock->biggest_msg_size_in_sync = 0;
if (offset)
break; /* At least one byte has successfully been sent */
err = sig->reply.sock_errno;
#ifdef IPCOM_ZEROCOPY
ip_assert (err != IP_ERRNO_EWOULDBLOCK && err != IP_ERRNO_EINTR);
if (orig_pkt != IP_NULL && len <= 32)
ipcom_pkt_free(orig_pkt);
#endif
goto errout;
}
} /* synchronous send */
} /* pkt != IP_NULL */
offset += (int)len;
/* Note, the basic_dgram case always exits the loop here! */
if (total_buf_len <= offset)
break; /* done sending */
if (IP_UNLIKELY(rslt.transient_err != 0))
{
if (offset != 0)
break;
err = rslt.transient_err;
goto errout;
}
if (IP_UNLIKELY(sock->ipcom.so_errno != 0))
{
if (offset != 0)
break;
err = sock->ipcom.so_errno;
goto errout;
}
/* just in case some other context closes the socket... */
if (IP_UNLIKELY(IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_CLOSED)))
{
if (offset != 0)
break;
err = IP_ERRNO_EBADF;
goto errout;
}
/*
* We never loop for basic datagram sockets (including IP_AF_ROUTE).
* Also, the address we are sending to cannot have changed.
* So, is_synchronous initialization is simpler.
*/
is_synchronous = IP_FALSE;
} /* for (;;) */
release:
ipnet_release_sock(sock);
#ifdef IPCOM_ZEROCOPY
if (orig_pkt != IP_NULL && len <= 32)
ipcom_pkt_free(orig_pkt);
#endif
if (rsig)
ipcom_free(rsig);
return offset;
cannot_send_pkt:
ip_assert(pkt != IP_NULL);
ip_assert(sock != IP_NULL);
if (basic_dgram)
{
#ifdef IPCOM_ZEROCOPY
if (orig_pkt != IP_NULL && len <= 32)
ipcom_pkt_free(orig_pkt);
#endif
while (pkt != IP_NULL)
{
next_pkt = pkt->next;
ipcom_pkt_free(pkt);
pkt = next_pkt;
}
}
else
{
/*
* We could add an operation to sock->ops to handle
* this, but for now we do not.
*/
#ifdef IPTCP
if (sock->tcb != IP_NULL) /* TCP socket */
ipnet_sock_tcp_zombie_send_pkt_free(sock, msg, pkt);
else
#endif
#ifdef IPSCTP
if (sock->sctp_inst != IP_NULL) /* SCTP socket */
ipnet_sock_sctp_zombie_send_pkt_free(sock, msg, pkt);
else
#endif
{
while (pkt != IP_NULL)
{
next_pkt = pkt->next;
ipcom_pkt_free(pkt);
pkt = next_pkt;
}
}
if (offset > 0)
goto release;
/* else fall through to errout */
}
errout:
if (rsig)
ipcom_free(rsig);
if (sock)
ipnet_release_sock(sock);
(void)ipcom_errno_set(err);
return IP_SOCKERR;
}
/*
*===========================================================================
* 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 - send flags
* 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)
{
struct Ip_msghdr msg;
struct Ip_iovec iov[1];
msg.msg_name = (struct Ip_sockaddr *) to;
msg.msg_namelen = tolen;
iov[0].iov_base = (void *) buf;
iov[0].iov_len = len;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = IP_NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
return ipcom_sendmsg(fd, &msg, flags);
}
/*
*===========================================================================
* ipcom_setsockopt
*===========================================================================
* Description: Sets the specified option on a socket. Options may
* exist at multiple protocol levels.
* Parameters: fd - A socket descriptor.
* level - The protocol level that should be affected by the
* option.
* optname - The option name.
* optval - Data to set for the option.
* optlen - The length of the buffer poited to by optval.
* Returns: 0 = success, -1 = error.
*
*/
IP_PUBLIC int
ipcom_setsockopt(Ip_fd fd,
int level,
int optname,
IP_CONST void *optval,
Ip_socklen_t optlen)
{
int ret;
ret = ipnet_usr_sockopt(fd,
level,
optname,
(void *) optval,
&optlen,
IPNET_SIG_SETSOCKOPT);
#ifdef IPSCTP
/* for sctp_connectx, do like what conect done */
if ((IP_IPPROTO_SCTP == level)
&& (IPSCTP_CONNECTX == optname))
{
if ((ret < 0 )
&& (IP_ERRNO_EINPROGRESS == ipcom_errno))
{
Ipnet_socket *sock;
Ip_bool is_blocking;
sock = ipnet_get_sock(fd);
if (sock == IP_NULL)
return IP_SOCKERR;
/* SCTP use this FLAG as TCP */
is_blocking = IP_BIT_ISFALSE(sock->flags,
IPNET_SOCKET_FLAG_NONBLOCKING);
if (is_blocking)
{
int err = ipnet_usr_sock_wait_until_writable(sock, IP_NULL, 0);
if (0 == err && 0 == (err = sock->ipcom.so_errno))
ret = 0;
else
(void)ipcom_errno_set(err);
}
ipnet_release_sock(sock);
}
}
#endif
return ret;
}
/*
*===========================================================================
* 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.
* writeds - The set of descriptors that should be checked for
* writing.
* exceptfds - The set of descriptors that should be checked for
* exceptions.
* 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 ready_socks;
int round;
Ip_fd_set *fds[3];
int err;
static Ipnet_for_each_sock_in_set_cb is_ready_func[] = {
(Ipnet_for_each_sock_in_set_cb)ipnet_is_sock_readable_adapter,
(Ipnet_for_each_sock_in_set_cb)ipnet_is_sock_writable_adapter,
(Ipnet_for_each_sock_in_set_cb)ipnet_is_sock_exceptional_adapter
};
static Ipnet_for_each_sock_in_set_cb create_poll_signal_func[] = {
(Ipnet_for_each_sock_in_set_cb) ipnet_usr_sock_add_poll_readable,
(Ipnet_for_each_sock_in_set_cb) ipnet_usr_sock_add_poll_writable,
(Ipnet_for_each_sock_in_set_cb) ipnet_usr_sock_add_poll_exceptional
};
/*
* Round nfds up to nearest number evenly dividable by the bits
* size of Ip_fd_mask.
*/
nfds = (int)((nfds + IP_NFDBITS - 1) & ~(IP_NFDBITS - 1));
fds[0] = readfds;
fds[1] = writefds;
fds[2] = exceptfds;
for (round = (ipnet_timeval_to_msec(timeout) == 0 ? 1 : 2);
round > 0;
round--)
{
int r;
int i;
ready_socks = 0;
err = 0;
for (i = 0; i < 3; i++)
{
r = ipnet_for_each_sock_in_set(nfds,
fds[i],
round == 1,
is_ready_func[i],
IP_NULL,
IP_NULL,
IP_NULL);
if (r >= 0)
ready_socks += r;
else if (err == 0)
err = r;
}
if (err != 0)
{
ipcom_errno_set(-err);
return IP_SOCKERR;
}
if (round == 2 && ready_socks == 0)
{
Ipnet_sig *poll_head = IP_NULL;
Ipnet_sig *poll_tail = IP_NULL;
/*
* Create a poll signal for each sockets we are interested
* in.
*/
for (i = 0; i < 3 && err >= 0; i++)
err = ipnet_for_each_sock_in_set(nfds,
fds[i],
IP_FALSE,
create_poll_signal_func[i],
&poll_head,
&poll_tail,
timeout);
/* Make the list of poll signals circular */
if (poll_tail)
poll_tail->d.poll.next = poll_head;
if (IP_LIKELY(err >= 0))
{
Ipnet_sig *sig;
enum Ipnet_sig_type types[] = {
IPNET_SIG_POLL_DONE,
IPNET_SIG_POLL_TIMEOUT
};
/*
* Wait until at least one socket gets ready or the
* timeout expires
*/
ipcom_pipe_send(ipnet->task.pipe, poll_head);
while (ipnet_usr_sock_pipe_recv(poll_head->response_pipe,
&sig,
IP_NULL,
types,
2) == IPCOM_ERR_INTERRUPT)
/* Restart the operation */;
ip_assert(sig->sig_type == IPNET_SIG_POLL_DONE
|| (timeout
&& sig->sig_type == IPNET_SIG_POLL_TIMEOUT));
ipnet_sig_free(sig);
}
}
}
return ready_socks;
}
/*
*===========================================================================
* 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);
}
/*
*===========================================================================
* 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_socketwritev
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_ssize_t
ipcom_socketwritev(Ip_fd fd, IP_CONST struct Ip_iovec *iov, int iovlen)
{
struct Ip_msghdr msghdr;
ipcom_memset(&msghdr, 0, sizeof(msghdr));
msghdr.msg_iov = (struct Ip_iovec *) iov;
msghdr.msg_iovlen = iovlen;
return ipcom_sendmsg(fd, &msghdr, 0);
}
/*
*===========================================================================
* ipcom_socket
*===========================================================================
* Description: Creates a new socket.
* Parameters: domain - The address domain.
* type - The socket type.
* protocol - 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_PUBLIC Ip_fd
ipcom_socket(int domain, int type, int protocol)
{
#ifdef IPCOM_FORWARDER_NAE
return ipnet_sys_socket(domain, type, protocol, IP_TRUE);
#else
Ipnet_sig *sig;
int ret;
#ifdef IPCOM_NAE
return ipnet_sys_socket(domain, type, protocol, IP_TRUE);
#endif
sig = ipcom_malloc(sizeof(Ipnet_sig));
if (sig == IP_NULL)
{
(void)ipcom_errno_set(IP_ERRNO_ENOMEM);
return IP_SOCKERR;
}
sig->sig_type = IPNET_SIG_SOCKET;
sig->d.socket.domain = domain;
sig->d.socket.type = type;
sig->d.socket.protocol = protocol;
if (ipnet_usr_sock_basic_send_recv(sig, &ret))
ipcom_free(sig);
return ret;
#endif /* IPCOM_FORWARDER_NAE */
}
/*
*===========================================================================
* ipcom_socketclose
*===========================================================================
* Description: Closes the socket and frees all resouces held by it.
* Parameters: sock - The socket to close.
* Returns: 0 = success, <0 error code,
*/
IP_PUBLIC int
ipcom_socketclose(Ip_fd fd)
{
#ifdef IPCOM_FORWARDER_NAE
return ipnet_sys_socketclose(fd);
#else
Ipnet_sig *sig;
int ret;
sig = ipcom_malloc(sizeof(Ipnet_sig));
if (sig == IP_NULL)
{
(void)ipcom_errno_set(IP_ERRNO_ENOMEM);
return IP_SOCKERR;
}
sig->sig_type = IPNET_SIG_CLOSE;
sig->d.close = fd;
if (ipnet_usr_sock_basic_send_recv(sig, &ret))
ipcom_free(sig);
return ret;
#endif /* IPCOM_FORWARDER_NAE */
}
/*
*===========================================================================
* ipcom_shutdown
*===========================================================================
* Description: Shuts down the read and/or the write end of a socket.
* Parameters: fd - The file descriptor to shut down.
* how - How the socket should be shut down.
* Returns: 0 = success, -1 = error
*/
IP_PUBLIC int
ipcom_shutdown(Ip_fd fd, int how)
{
Ipnet_sig *sig;
int ret;
sig = ipcom_malloc(sizeof(Ipnet_sig));
if (sig == IP_NULL)
{
(void)ipcom_errno_set(IP_ERRNO_ENOMEM);
return IP_SOCKERR;
}
sig->sig_type = IPNET_SIG_SHUTDOWN;
sig->d.shutdown.fd = fd;
sig->d.shutdown.how = how;
if (ipnet_usr_sock_basic_send_recv(sig, &ret))
ipcom_free(sig);
return ret;
}
/*
*===========================================================================
* ipcom_sysctl
*===========================================================================
* Description: System control for IPNET.
* Parameters: name - the hierarchical sysctl name.
* namelen - the size of the 'name' array.
* oldp - buffer to where the route entries will be copied
* oldlenp - [in] the length of 'oldp' buffer
* [out] the number of bytes copied to 'oldp' buffer or
* the number of bytes needed (if 'oldp' is IP_NULL).
* newp - the new value for the variable
* newlen - the length of the 'newp' buffer.
* Returns: 0 == success, <0 == error code.
*
*/
IP_PUBLIC int
ipcom_sysctl(int *name, unsigned int namelen,
void *oldp, Ip_size_t *oldlenp,
void *newp, Ip_size_t newlen)
{
#ifdef IPCOM_FORWARDER_NAE
struct Ipnet_sig_sysctl sig_sysctl;
sig_sysctl.name = name;
sig_sysctl.namelen = namelen;
sig_sysctl.oldp = oldp;
sig_sysctl.oldlenp = oldlenp;
sig_sysctl.newp = newp;
sig_sysctl.newlen = newlen;
return ipnet_sys_sysctl(&sig_sysctl);
#else
Ipnet_sig *sig;
int ret;
Ip_size_t oldlen = (oldp ? *oldlenp : 0);
sig = ipcom_malloc(sizeof(Ipnet_sig)
+ oldlen
+ newlen
+ sizeof(void*));
if (sig == IP_NULL)
{
(void)ipcom_errno_set(IP_ERRNO_ENOMEM);
return IP_SOCKERR;
}
sig->sig_type = IPNET_SIG_SYSCTL;
/*
* 'name' does not need to be copied since it has already been
* copied to a memory buffer residing within the kernel when using
* VxWorks RTPs
*/
sig->d.sysctl.name = name;
sig->d.sysctl.namelen = namelen;
sig->d.sysctl.oldp = (sig + 1);
sig->d.sysctl.oldlenp = &oldlen;
sig->d.sysctl.newp = IP_PTR_ALIGN(((char*) sig->d.sysctl.oldp + oldlen), sizeof(void*));
sig->d.sysctl.newlen = newlen;
if (oldp == IP_NULL)
sig->d.sysctl.oldp = IP_NULL;
if (newp == IP_NULL)
sig->d.sysctl.newp = IP_NULL;
else
ipcom_memcpy(sig->d.sysctl.newp, newp, newlen);
if (ipnet_usr_sock_basic_send_recv(sig, &ret))
{
if (ret == 0)
{
if (oldp)
ipcom_memcpy(oldp, sig->d.sysctl.oldp, oldlen);
if (oldlenp)
*oldlenp = oldlen;
}
ipcom_free(sig);
}
return ret;
#endif /* IPCOM_FORWARDER_NAE */
}
/*
*===========================================================================
* ipcom_if_attach
*===========================================================================
* Description: Attaches a new network interface to IPNET.
*
* Parameters: netif - An initialized netif structure.
*
* Returns: The interface index for this interface or negative errno.
*
*/
IP_PUBLIC int
ipcom_if_attach(Ipcom_netif *netif)
{
if (ipnet_is_stack_task())
return ipnet_sys_if_attach(netif);
return ipnet_do_if_attach(netif, IP_FALSE);
}
/*
*===========================================================================
* ipcom_if_attach_and_lock
*===========================================================================
* Description: Attaches a new network interface to IPNET and lock it.
* The caller is responsible for unlocking it when finished.
*
* Parameters: netif - An initialized netif structure.
*
* Returns: The interface index for this interface or negative errno.
*
*/
IP_PUBLIC int
ipcom_if_attach_and_lock(Ipcom_netif *ipcomif)
{
if (ipnet_is_stack_task())
{
Ipnet_netif *netif = (Ipnet_netif*)ipcomif;
int ret;
ret = ipnet_sys_if_attach(ipcomif);
if (ret >= 0)
IPNET_IF_LOCK(netif);
return ret;
}
return ipnet_do_if_attach(ipcomif, IP_TRUE);
}
/*
*===========================================================================
* ipcom_if_changelinkaddr
*===========================================================================
* Description: Change the link address on a network interface.
* Parameters: ipcomif - The interface for which the lladdr has changed.
* Returns:
*
*/
IP_PUBLIC void
ipcom_if_changelinkaddr(Ipcom_netif *ipcomif, Ip_u8 *lladdr, Ip_u16 lladdr_sz)
{
Ipnet_netif *netif = (Ipnet_netif*)ipcomif;
Ipnet_sig *sig;
/* IPNET does not call this directly, if that changes in the
future then make sure ipnet_sys_if_changemtu() is called if
the caller is the IPNET daemon */
ip_assert(ipnet_is_stack_task() == IP_FALSE);
IPNET_IF_LOCK(netif);
sig = ipcom_malloc(sizeof(Ipnet_sig));
if (sig == IP_NULL)
{
(void)ipcom_errno_set(IP_ERRNO_ENOMEM);
goto done;
}
sig->sig_type = IPNET_SIG_IF_CHANGELINKADDR;
sig->d.netif.netif = ipcomif;
sig->d.netif.link_addr = lladdr;
sig->d.netif.size = lladdr_sz;
if (ipnet_pipe_basic_send_recv(sig) == IPCOM_ERR_INTERRUPT)
(void)ipcom_errno_set(IP_ERRNO_EINTR);
else
ipcom_free(sig);
done:
IPNET_IF_UNLOCK(netif);
}
/*
*===========================================================================
* ipcom_if_changemtu
*===========================================================================
* Description: Change the mtu of a network interface.
* Parameters: ipcomif - The interface for which the name has changed.
* Returns:
*
*/
IP_PUBLIC void
ipcom_if_changemtu(Ipcom_netif *ipcomif, Ip_u32 new_mtu)
{
Ipnet_netif *netif = (Ipnet_netif*)ipcomif;
Ipnet_sig *sig;
/* IPNET does not call this directly, if that changes in the
future then make sure ipnet_sys_if_changemtu() is called if
the caller is the IPNET daemon */
ip_assert(ipnet_is_stack_task() == IP_FALSE);
IPNET_IF_LOCK(netif);
sig = ipcom_malloc(sizeof(Ipnet_sig));
if (sig == IP_NULL)
{
(void)ipcom_errno_set(IP_ERRNO_ENOMEM);
goto done;
}
sig->sig_type = IPNET_SIG_IF_CHANGEMTU;
sig->d.netif.netif = ipcomif;
sig->d.netif.mtu = new_mtu;
if (ipnet_pipe_basic_send_recv(sig) == IPCOM_ERR_INTERRUPT)
(void)ipcom_errno_set(IP_ERRNO_EINTR);
else
ipcom_free(sig);
done:
IPNET_IF_UNLOCK(netif);
}
/*
*===========================================================================
* ipcom_if_changename
*===========================================================================
* Description: Change the name of a network interface.
* Parameters: ipcomif - The interface for which the name has changed.
* Returns:
*
*/
IP_PUBLIC void
ipcom_if_changename(Ipcom_netif *ipcomif, const char *new_name)
{
Ipnet_netif *netif = (Ipnet_netif*)ipcomif;
Ipnet_sig *sig;
/* IPNET does not call this directly, if that changes in the
future then make sure ipnet_sys_if_changename() is called if
the caller is the IPNET daemon */
ip_assert(ipnet_is_stack_task() == IP_FALSE);
IPNET_IF_LOCK(netif);
sig = ipcom_malloc(sizeof(Ipnet_sig));
if (sig == IP_NULL)
{
(void)ipcom_errno_set(IP_ERRNO_ENOMEM);
goto done;
}
sig->sig_type = IPNET_SIG_IF_CHANGENAME;
sig->d.netif.netif = ipcomif;
ipcom_strncpy(sig->d.netif.ifname, new_name, IP_IFNAMSIZ);
if (ipnet_pipe_basic_send_recv(sig) == IPCOM_ERR_INTERRUPT)
(void)ipcom_errno_set(IP_ERRNO_EINTR);
else
ipcom_free(sig);
done:
IPNET_IF_UNLOCK(netif);
}
/*
*===========================================================================
* ipcom_if_detach
*===========================================================================
* Description: Detach a network interface with IPNET.
* Parameters: netif - The interface to detach.
* Returns: The interface index for this interface or negative errno.
*
*/
IP_PUBLIC int
ipcom_if_detach(Ipcom_netif *ipcomif)
{
Ipnet_netif *netif = (Ipnet_netif*)ipcomif;
Ipnet_sig *sig;
int ret;
if (ipnet_is_stack_task())
return ipnet_sys_if_detach(ipcomif);
IPNET_IF_LOCK(netif);
sig = ipcom_malloc(sizeof(Ipnet_sig));
if (sig == IP_NULL)
{
(void)ipcom_errno_set(IP_ERRNO_ENOMEM);
goto errout;
}
sig->sig_type = IPNET_SIG_IF_DETACH;
sig->d.netif.netif = ipcomif;
if (ipnet_pipe_basic_send_recv(sig) == IPCOM_ERR_INTERRUPT)
{
(void)ipcom_errno_set(IP_ERRNO_EINTR);
goto errout;
}
IPNET_IF_UNLOCK(netif);
ret = sig->reply.return_code;
ipcom_free(sig);
if (ret < 0)
(void)ipcom_errno_set(IP_ERRNO_ENXIO);
return ret;
errout:
IPNET_IF_UNLOCK(netif);
return -ipcom_errno;
}
/*
*===========================================================================
* ipcom_if_free
*===========================================================================
* Description: Frees the resources held by the network interface. The
* interface must be detached for this call to succeed.
* Parameters: ipcomif - network interface to free
* Returns: 0 = success, -1 = error
*
*/
IP_PUBLIC int
ipcom_if_free(Ipcom_netif *ipcomif)
{
Ipnet_sig *sig;
int ret;
if (ipnet_is_stack_task())
return ipnet_sys_if_free(ipcomif);
sig = ipcom_malloc(sizeof(Ipnet_sig));
if (sig == IP_NULL)
{
(void)ipcom_errno_set(IP_ERRNO_ENOMEM);
return IP_SOCKERR;
}
ipnet_usr_sock_init_sig(sig, IPNET_SIG_IF_FREE);
sig->d.netif.netif = ipcomif;
if (ipnet_usr_sock_basic_send_recv(sig, &ret))
ipcom_free(sig);
return ret;
}
/*
*===========================================================================
* ipcom_if_malloc
*===========================================================================
* Description: Creates a new uninitialized network interface structure.
* Parameters: type - One of the IP_IFT_xxx types.
* Returns: The network interface structure of IP_NULL if out of memory.
*
*/
IP_PUBLIC Ipcom_netif *
ipcom_if_malloc(Ip_u16 type)
{
Ipcom_netif *ipcomif;
Ipnet_sig *sig;
if (ipnet_is_stack_task())
return ipnet_sys_if_malloc(type);
sig = ipcom_malloc(sizeof(Ipnet_sig));
if (sig == IP_NULL)
{
(void)ipcom_errno_set(IP_ERRNO_ENOMEM);
goto errout;
}
sig->sig_type = IPNET_SIG_IF_MALLOC;
sig->d.netif.type = type;
sig->d.netif.netif = IP_NULL;
if (ipnet_pipe_basic_send_recv(sig) == IPCOM_ERR_INTERRUPT)
{
(void)ipcom_errno_set(IP_ERRNO_EINTR);
goto errout;
}
ipcomif = sig->d.netif.netif;
ipcom_free(sig);
return ipcomif;
errout:
return IP_NULL;
}
/*
*===========================================================================
* ipcom_if_nametoindex
*===========================================================================
* Description: Maps the interface name to the index of the interface.
* Parameters: ifname - The name of the index.
* Returns: The index of the interface or 0 if no interface has the
* specified name.
*
*/
IP_PUBLIC Ip_u32
ipcom_if_nametoindex(IP_CONST char *ifname)
{
#ifdef IPCOM_FORWARDER_NAE
Ipnet_netif *netif = IP_NULL;
Ip_u16 vr;
vr = ipnet_get_effective_vr();
netif = ipnet_if_nametonetif(vr, ifname);
return netif->ipcom.ifindex;
#else
Ipnet_sig *sig;
Ipnet_sig *reply_sig;
Ip_u32 ifindex;
sig = ipcom_malloc(sizeof(Ipnet_sig));
if (sig == IP_NULL)
{
(void)ipcom_errno_set(IP_ERRNO_ENOMEM);
return 0;
}
ipnet_usr_sock_init_sig(sig, IPNET_SIG_IF_NAMETOINDEX);
ipcom_memcpy(sig->d.indexname.ifname,
ifname,
IP_MIN(ipcom_strlen(ifname) + 1, IP_IFNAMSIZ));
sig->d.indexname.ifname[IP_IFNAMSIZ] = '\0';
if (ipnet_is_stack_task())
ipnet_sys_if_indexname(reply_sig = sig);
else
{
int ret;
ipcom_pipe_send(ipnet->task.pipe, sig);
do
{
ret = ipnet_usr_sock_pipe_recv(sig->response_pipe,
&reply_sig,
sig,
IP_NULL,
0);
}
while (ret == IPCOM_ERR_INTERRUPT);
}
ifindex = reply_sig->d.indexname.ifindex;
ipcom_free(sig);
if (ifindex == 0)
(void)ipcom_errno_set(IP_ERRNO_ENXIO);
return ifindex;
#endif /* IPCOM_FORWARDER_NAE */
}
/*
*===========================================================================
* ipcom_if_indextoname
*===========================================================================
* Description: Maps the interface index to the name of the interface.
* Parameters: ifindex - The index of the interface.
* ifname - A buffer to hold the name, must be at least
* IP_IFNAMSIZ characters long.
* Returns: The ifname pointer containing the interface name or IP_NULL
* if no interface has the specified index.
*
*/
IP_PUBLIC char *
ipcom_if_indextoname(Ip_u32 ifindex, char *ifname)
{
#ifdef IPCOM_FORWARDER_NAE
Ipnet_netif *netif = IP_NULL;
Ip_u16 vr;
vr = ipnet_get_effective_vr();
netif = ipnet_if_indextonetif(vr, ifindex);
if(netif == IP_NULL)
return IP_NULL;
ipcom_memcpy(ifname, netif->ipcom.name, IP_IFNAMSIZ);
return ifname;
#else
Ipnet_sig *sig;
sig = ipcom_malloc(sizeof(Ipnet_sig));
if (sig == IP_NULL)
{
(void)ipcom_errno_set(IP_ERRNO_ENOMEM);
goto errout;
}
sig->sig_type = IPNET_SIG_IF_INDEXTONAME;
sig->d.indexname.ifindex = ifindex;
sig->d.indexname.ifname[IP_IFNAMSIZ] = '\0';
if (ipnet_pipe_basic_send_recv(sig) == IPCOM_ERR_INTERRUPT)
{
(void)ipcom_errno_set(IP_ERRNO_EINTR);
goto errout;
}
if (sig->d.indexname.ifindex == 0)
{
(void)ipcom_errno_set(IP_ERRNO_ENXIO);
ipcom_free(sig);
goto errout;
}
ipcom_memcpy(ifname,
sig->d.indexname.ifname,
ipcom_strlen(sig->d.indexname.ifname) + 1);
ipcom_free(sig);
return ifname;
errout:
return IP_NULL;
#endif /* IPCOM_FORWARDER_NAE */
}
/*
*===========================================================================
* ipcom_if_nameindex
*===========================================================================
* Description: Returns an array of Ip_if_nameindex, one for each
* interface installed. The array ends with a entry with all
* fields set to 0.
* Parameters:
* Returns: Interface array.
*
*/
IP_PUBLIC struct Ip_if_nameindex *
ipcom_if_nameindex(void)
{
struct Ip_if_nameindex *pif;
Ipnet_sig *sig;
Ip_u32 ifindex;
Ip_u32 num_attached;
Ip_u32 highest_index;
Ip_u32 if_added;
char *ifname;
sig = ipcom_malloc(sizeof(Ipnet_sig));
if (sig == IP_NULL)
{
(void)ipcom_errno_set(IP_ERRNO_ENOMEM);
goto errout;
}
sig->sig_type = IPNET_SIG_IF_INDEXTONAME;
sig->d.indexname.ifindex = 0;
sig->d.indexname.ifname[IP_IFNAMSIZ] = '\0';
if (ipnet_pipe_basic_send_recv(sig) == IPCOM_ERR_INTERRUPT)
{
(void)ipcom_errno_set(IP_ERRNO_EINTR);
goto errout;
}
num_attached = sig->d.indexname.num_attached;
highest_index = sig->d.indexname.highest_assigned_ifindex;
ipcom_free(sig);
pif = ipcom_calloc((num_attached + 1), sizeof(*pif) + IP_IFNAMSIZ);
if (pif == IP_NULL)
{
(void)ipcom_errno_set(IP_ERRNO_ENOMEM);
return IP_NULL;
}
ifname = (char *) &pif[num_attached + 1];
for (ifindex = 1, if_added = 0;
ifindex <= highest_index && if_added < num_attached;
ifindex++)
{
if (ipcom_if_indextoname(ifindex, ifname))
{
pif[if_added].if_name = ifname;
pif[if_added++].if_index = ifindex;
ifname += IP_IFNAMSIZ;
}
}
(void)ipcom_errno_set(0);
return pif;
errout:
return IP_NULL;
}
/*
*===========================================================================
* ipcom_if_freenameindex
*===========================================================================
* Description: Frees the array returned from ipcom_if_nameindex().
* Parameters: pif - The array to free.
* Returns:
*
*/
IP_PUBLIC void
ipcom_if_freenameindex(struct Ip_if_nameindex *pif)
{
if (pif != IP_NULL)
ipcom_free(pif);
}
/*
*===========================================================================
* ipcom_if_nametonetif
*===========================================================================
* Description: Returns a pointer to the interface descriptor identified by
* its name.
* This function ensures that the interface is locked (reference counter
* has been increased).
* The user MUST self unlock the interface when finished with it.
*
* Parameters: vr - Actual VR to search for interface within.
* ifname - A buffer to hold the name, must be at least
* IP_IFNAMSIZ characters long.
* Returns: A pointer to the interface descriptor if found
* IP_NULL otherwise.
*
*/
IP_PUBLIC Ipcom_netif*
ipcom_if_nametonetif(Ip_u16 vr, IP_CONST char *ifname)
{
Ipcom_netif *ipcomif;
Ipnet_sig *sig;
#ifdef IP_PORT_LKM
if (ipnet_is_stack_task())
{
Ipnet_netif *netif = ipnet_if_nametonetif(vr, ifname);
if (netif)
IPNET_IF_LOCK(netif);
return (Ipcom_netif*)netif;
}
#endif /* IP_PORT_LKM */
sig = ipcom_malloc(sizeof(Ipnet_sig));
if (sig == IP_NULL)
{
(void)ipcom_errno_set(IP_ERRNO_ENOMEM);
goto errout;
}
ipcom_memcpy(sig->d.netif.ifname,
ifname,
IP_MIN(ipcom_strlen(ifname) + 1, IP_IFNAMSIZ));
sig->d.netif.ifname[IP_IFNAMSIZ] = '\0';
sig->sig_type = IPNET_SIG_IF_NAMETONETIF;
sig->d.netif.vr = vr;
sig->d.netif.netif = IP_NULL;
if (ipnet_pipe_basic_send_recv(sig) == IPCOM_ERR_INTERRUPT)
{
(void)ipcom_errno_set(IP_ERRNO_EINTR);
goto errout;
}
ipcomif = sig->d.netif.netif;
ipcom_free(sig);
if (ipcomif == IP_NULL)
(void)ipcom_errno_set(IP_ERRNO_ENXIO);
return ipcomif;
errout:
return IP_NULL;
}
/*
*===========================================================================
* ipnet_suspend_stack
*===========================================================================
* Description: Mimics the semantic of the old IPNET_CODE_LOCK() macro.
* Parameters:
* Returns: A handle that must be passed to ipnet_resume_stack()
* when the stack should resume again. IP_NULL is
* return if the stack refused or could not resume.
*
*/
IP_PUBLIC void *
ipnet_suspend_stack(void)
{
Ipnet_sig sig;
Ipnet_sig *psig;
enum Ipnet_sig_type types[] = {
IPNET_SIG_SUSPEND,
IPNET_SIG_RESUME
};
#ifdef IP_PORT_OSE5
if (ipnet_is_stack_task())
return (void *)0xffffffff;
#endif /* IP_PORT_OSE5 */
ipnet_usr_sock_init_sig(&sig, IPNET_SIG_SUSPEND);
ipcom_pipe_send(ipnet->task.pipe, &sig);
(void)ipnet_usr_sock_pipe_recv(sig.response_pipe,
&psig,
IP_NULL,
types,
2);
if (psig && psig->sig_type == IPNET_SIG_RESUME)
return IP_NULL;
return psig;
}
/*
*===========================================================================
* ipnet_resume_stack
*===========================================================================
* Description: Mimics the semantic of the old IPNET_CODE_UNLOCK() macro.
* Parameters: suspend_handle - handle return from ipnet_suspend_stack()
* Returns:
*
*/
IP_PUBLIC void
ipnet_resume_stack(void *suspend_handle)
{
Ipnet_sig *sig = suspend_handle;
if (sig)
{
sig->sig_type = IPNET_SIG_RESUME;
ipcom_pipe_send(sig->response_pipe, sig);
}
}
/*
*===========================================================================
* ipnet_ctrl
*===========================================================================
* Description: Sets/gets runtime configuration for IPNET.
* Parameters: tag - The data to set or get.
* Returns: 0 = succcess, -1 = error
* (get error code from ipcom_errno)
*
*/
IP_PUBLIC int
ipnet_ctrl(struct Ipnet_tag *tag)
{
int ret = IP_SOCKERR;
if (tag == IP_NULL)
ipcom_errno_set(IP_ERRNO_EINVAL);
else
{
Ipnet_sig *sig;
sig = ipcom_malloc(sizeof(Ipnet_sig));
if (sig == IP_NULL)
ipcom_errno_set(IP_ERRNO_ENOMEM);
else
{
sig->sig_type = IPNET_SIG_CTRL;
sig->d.ctrl_tag = tag;
#ifdef IPCOM_FORWARDER_NAE
ret = ipnet_ctrl_sig(sig);
#else
if (ipnet_usr_sock_basic_send_recv(sig, &ret))
#endif
ipcom_free(sig);
}
}
return ret;
}
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/