4372 lines
136 KiB
C
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
|
|
****************************************************************************
|
|
*/
|
|
|