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

4715 lines
145 KiB
C

/*
******************************************************************************
* SOURCE FILE
*
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipnet_sock.c,v $ $Revision: 1.457.10.33 $
* $Source: /home/interpeak/CVSRoot/ipnet2/src/ipnet_sock.c,v $
* $Author: blan $
* $State: Exp $ $Locker: $
*
* INTERPEAK_COPYRIGHT_STRING
* Design and implementation by Kenneth Jonsson <kenneth@interpeak.se>
******************************************************************************
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
* Functions for socket API.
*/
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
#define IPCOM_SKIP_NATIVE_SOCK_API
#ifndef IPCOM_KERNEL
#define IPCOM_KERNEL
#endif
#include "ipnet_config.h"
#include <ipcom_time.h>
#ifdef IP_PORT_VXWORKS
#include <time.h>
#endif
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#define IPCOM_USE_CLIB_PROTO
#include <ipcom_clib.h>
#include <ipcom_cstyle.h>
#include <ipcom_err.h>
#include <ipcom_hash.h>
#include <ipcom_inet.h>
#include <ipcom_sock2.h>
#include <ipcom_spinlock.h>
#include <ipcom_syslog.h>
#include <ipcom_sysvar.h>
#include "ipnet_h.h"
#include "ipnet_loopback.h"
#include "ipnet_usr_sock.h"
#ifdef IPNET_USE_ROUTESOCK
#include "ipnet_routesock.h"
#endif
#ifdef IPNET_USE_NETLINKSOCK
#include "ipnet_netlink.h"
#include "ipnet_netlink_h.h"
#endif
#ifdef IPNET_USE_PER_SOCKET_VLAN_TAG
#include "ipnet_eth.h"
#endif
#ifdef IPTCP
#include <iptcp.h>
#endif
#ifdef IPSCTP
#include <ipsctp.h>
#endif
#ifdef IPCOM_WV_INSTRUMENTATION
#include <ipcom_windview.h>
#endif
#ifdef IPCOM_USE_FORWARDER
#include <ipcom_forwarder.h>
#endif
/*
****************************************************************************
* 4 DEFINES
****************************************************************************
*/
/*
****************************************************************************
* 5 TYPES
****************************************************************************
*/
typedef struct Ipnet_bound_port_struct
{
Ipcom_list list; /* List of Ipnet_bound_port instances
that hash to the same value */
Ipcom_atomic_t refcnt; /* Number of sockets that match [domain,
local port, proto, vr ] */
int domain; /* The domain this socket is part of */
int proto; /* The protocol the socket uses */
Ip_u16 vr; /* The virtual router the socket must be assigned to */
Ip_u16 local_port; /* The local port the socket is bound to */
Ip_u8 has_reuse_addr; /* !=0 if all matching sockets has reuse addr set */
Ip_u8 has_reuse_port; /* !=0 if all matching sockets has reuse port set */
} Ipnet_bound_port;
#ifdef IPNET_USE_ROUTER_ALERT
/*
* All sockets that want to receive IP-datagrams with RFC 2113 router
* alert IP-options will be put into the
* ipnet_sock_ip4_router_alert_list
*
*/
typedef struct Ipnet_router_alert_sock_struct
{
Ipcom_list list;
Ipnet_socket *sock;
}
Ipnet_router_alert_sock;
#endif /* IPNET_USE_ROUTER_ALERT */
/*
****************************************************************************
* 6 EXTERN PROTOTYPES
****************************************************************************
*/
#if defined(IP_PORT_VXWORKS) && defined(IPCOM_USE_SOCK_COMPAT)
IP_GLOBAL int ipcom_windnet_socklib_socket_init(Ipnet_socket *sock);
IP_GLOBAL void ipcom_windnet_socklib_socket_free(Ipnet_socket *sock);
#ifdef IPNET_SO_WAKEUP_HOOK
IP_GLOBAL void ipcom_windnet_socklib_sowakeupHook(Ipnet_socket *sock, int how);
#endif
#endif
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
#if defined(IP_PORT_VXWORKS) && defined(IPCOM_USE_SOCK_COMPAT)
IP_GLOBAL int ipcom_windnet_socklib_sock_size(void);
#endif
IP_STATIC void ipnet_signal_ready_sock(Ipnet_socket *sock,
enum Ipnet_sock_poll what,
Ipnet_is_sock_ready_cb is_sock_ready);
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
#ifdef IPNET_USE_ROUTER_ALERT
/*
* List of Ipnet_router_alert_sock instances. This is the set of
* sockets that are interested in packets that carries the router
* alert option.
*/
static Ipcom_list ipnet_sock_router_alert_list;
#endif
/*
* Array of socket operations for all known address families, socket
* types and protocols. Each socket keep a index ('ops_index' in
* Ipnet_socket) into this array to find the correction functions. The
* socket cannot direcly point to the Ipnet_sock_ops structure since
* each NAE must use the set of functions located in its image, even
* when running in uAMP mode where each socket function will be
* present in each NAE and visable from all other NAEs. New handlers
* MUST be appended at the end of this array.
*/
IP_FASTDATA Ipnet_sock_ops **ipnet_sock_ops = IP_NULL;
static int ipnet_sock_ops_len = 0;
/*
****************************************************************************
* 9 STATIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipnet_sock_bound_port_get
*===========================================================================
* Description: Compares if a socket matches the specified lookup key.
* Parameters: key - A lookup key.
* Returns: IP_NULL if no matchin bound port entry exists, a
* pointer to the the entry otherwise.
*
*/
IP_STATIC Ipnet_bound_port *
ipnet_sock_bound_port_get(Ipnet_socket_lookup_key *key)
{
Ipnet_bound_port *bp;
#ifdef IPCOM_FORWARDER_NAE
ipcom_enter_critical_region();
#endif
for (bp = IPCOM_LIST_FIRST(ipnet->bound_ports);
bp != IP_NULL;
bp = IPCOM_LIST_NEXT(&bp->list))
{
if (bp->local_port == key->local_port
&& bp->domain == key->domain
&& bp->proto == key->proto
&& bp->vr == key->vr)
break;
}
#ifdef IPCOM_FORWARDER_NAE
ipcom_exit_critical_region();
#endif
return bp;
}
/*
*===========================================================================
* ipnet_sock_bound_port_add
*===========================================================================
* Description: Allocates and initializes a new bound socket structure.
* Parameters: sock - A socket.
* Returns: The new bound port structure.
*
*/
IP_STATIC Ipnet_bound_port *
ipnet_sock_bound_port_add(const Ipnet_socket *sock)
{
Ipnet_bound_port *bp;
#ifdef IPCOM_FORWARDER_NAE
bp = ipcom_shm_malloc(sizeof(Ipnet_bound_port));
#else
bp = ipcom_slab_alloc(ipnet->slabs.bound_port);
#endif
if (bp == IP_NULL)
return IP_NULL;
ipcom_atomic_set(&bp->refcnt, 1);
bp->has_reuse_addr = (Ip_u8) IP_BIT_ISTRUE(sock->flags, IPNET_SOCKET_FLAG_REUSEADDR);
bp->has_reuse_port = (Ip_u8) IP_BIT_ISTRUE(sock->flags, IPNET_SOCKET_FLAG_REUSEPORT);
bp->domain = sock->ipcom.domain;
bp->vr = sock->vr_index;
bp->proto = sock->proto;
bp->local_port = sock->sport;
#ifdef IPCOM_FORWARDER_NAE
ipcom_enter_critical_region();
#endif
ipcom_list_insert_first(ipnet->bound_ports, &bp->list);
#ifdef IPCOM_FORWARDER_NAE
ipcom_exit_critical_region();
#endif
return bp;
}
/*
*===========================================================================
* ipnet_key_from_sock
*===========================================================================
* Description: Creates a search key from the specifed socket.
* Parameters: sock - A socket.
* key - The search key.
* Returns:
*
*/
IP_STATIC void
ipnet_key_from_sock(const Ipnet_socket *sock, Ipnet_socket_lookup_key *key)
{
key->domain = sock->ipcom.domain;
key->vr = sock->vr_index;
key->proto = sock->proto;
key->local_port = sock->sport;
key->peer_port = sock->dport;
switch (sock->ipcom.domain)
{
#ifdef IPCOM_USE_INET
case IP_AF_INET:
key->local_addr.in.s_addr = sock->ip4->saddr_n;
key->peer_addr.in.s_addr = sock->ip4->daddr_n;
break;
#endif
#ifdef IPCOM_USE_INET6
case IP_AF_INET6:
key->local_addr.in6 = sock->ip6->saddr;
key->peer_addr.in6 = sock->ip6->daddr;
break;
#endif
default:
break;
}
}
#ifdef IPCOM_USE_INET6
/*
*===========================================================================
* ipnet_find_extension_header
*===========================================================================
* Description: Tries to find the specifed extension header.
* Parameters: ip6hdr - The IPv6 header for the packet.
* type - The extension header type to find.
* Returns: Pointer to the extension header or IP_NULL if it was not found.
*
*/
IP_STATIC Ip_pkt_ip6_ext_hdr *
ipnet_find_extension_header(Ipnet_pkt_ip6 *ip6hdr, int type)
{
Ip_pkt_ip6_ext_hdr *ext;
int ext_type;
Ip_bool give_up = IP_FALSE;
ext_type = ip6hdr->nxt;
ext = (Ip_pkt_ip6_ext_hdr *) (ip6hdr + 1);
while (!give_up)
{
if (type == ext_type)
return ext;
switch (ext_type)
{
case IP_IPPROTO_HOPOPTS:
case IP_IPPROTO_ROUTING:
case IP_IPPROTO_FRAGMENT:
case IP_IPPROTO_DSTOPTS:
case IP_IPPROTO_AH:
case IP_IPPROTO_ESP:
break;
default:
give_up = IP_TRUE;
break;
}
ext_type = ext->nxt;
ext = (Ip_pkt_ip6_ext_hdr *) ((Ip_u8 *) ext + (ext->len + 1) * 8);
}
return IP_NULL;
}
/*
*===========================================================================
* ipnet_add_ancillary_ipv6_data
*===========================================================================
* Description: Adds ancillary data to the message.
* Parameters: msg - The message that will receive the ancillary data.
* sock - The socket used.
* pkt - The packet from which the data should be extracted.
* Returns:
*
*/
IP_STATIC void
ipnet_add_ancillary_ipv6_data(struct Ip_msghdr *msg,
struct Ip_cmsghdr **pcmsg,
Ip_socklen_t *pcmsg_len,
Ipnet_socket *sock,
Ipcom_pkt *pkt)
{
#ifdef IPCOM_USE_INET
Ipnet_pkt_ip6 ip6hdrbuf;
#endif
struct Ip_cmsghdr *cmsg = *pcmsg;
Ip_socklen_t cmsg_len = *pcmsg_len;
Ip_socklen_t len = 0;
Ipnet_pkt_ip6 *ip6hdr = (Ipnet_pkt_ip6 *) &pkt->data[pkt->ipstart];
Ip_pkt_ip6_ext_hdr *ext;
int i;
struct Ipnet_flag_proto_stype_tuple {
int flag; /* The socket flag that must be set to return the header */
int proto; /* The proto number of the header */
int sockopt_type; /* The socket options type that the ancillary data will have */
} fpst[] = {
{ IPNET_SOCKET_FLAG_IP6_RECVHOPOPTS,IP_IPPROTO_HOPOPTS, IP_IPV6_HOPOPTS },
{ IPNET_SOCKET_FLAG_IP6_RECVDSTOPTS,IP_IPPROTO_DSTOPTS, IP_IPV6_DSTOPTS },
{ IPNET_SOCKET_FLAG_IP6_RECVRTHDR, IP_IPPROTO_ROUTING, IP_IPV6_RTHDR },
{ 0, 0, 0 }
};
#ifdef IPCOM_USE_INET
if (IPNET_IP6_GET_VER(ip6hdr) == 4)
{
Ipnet_pkt_ip *iphdr = (Ipnet_pkt_ip *)ip6hdr;
/* Using IPv6 mapped IPv4 address, let's create a dummy IPv6
header matching the IPv4 header to support this part of
RFC 3542
...
13. IPv6-Specific Options with IPv4-Mapped IPv6 Addresses
...
In general, attempting to specify an IPv6-only option, such as the
Hop-by-Hop options, Destination options, or Routing header on an IPv6
socket that is using IPv4-mapped IPv6 addresses, will probably result
in an error. Some implementations, however, may provide access to
the packet information (source/destination address, send/receive
interface, and hop limit) on an IPv6 socket that is using IPv4-mapped
IPv6 addresses.
...
This will make all the packet information available.
*/
ip6hdr = &ip6hdrbuf;
ipcom_memset(ip6hdr, 0, sizeof(*ip6hdr));
IPNET_IP6_SET_VER_CLASS_FLOW(ip6hdr, 6, iphdr->tos, 0);
ip6hdr->hlim = iphdr->ttl;
ip6hdr->dst.in6.addr16[5] = 0xffff;
ipcom_memcpy(&ip6hdr->dst.in6.addr32[3], iphdr->dst, 4);
}
#endif /* IPCOM_USE_INET*/
if (IP_BIT_ISSET(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_RECVENCAP))
{
Ip_size_t s_size;
Ip_u8 *s_encap = ipcom_pkt_get_info_and_size(pkt, IPNET_PKT_INFO_ENCAP, &s_size);
if (s_encap != IP_NULL)
{
Ipnet_pkt_udp *s_udp = ipcom_pkt_get_info(pkt, IPNET_PKT_INFO_ENCAP_UDP);
union Ip_sockaddr_union s_un;
ipcom_memset(&s_un, 0, sizeof(s_un));
#ifdef IPCOM_USE_INET
if (s_size == sizeof(struct Ip_in_addr))
{
s_un.sin.sin_family = IP_AF_INET;
IPCOM_SA_LEN_SET(&s_un, sizeof(s_un.sin));
s_un.sin.sin_addr.s_addr = IP_GET_32ON8(s_encap);
s_un.sin.sin_port = s_udp? s_udp->sport : 0;
}
else
#endif
if (s_size == sizeof(struct Ip_in6_addr))
{
s_un.sin6.sin6_family = IP_AF_INET6;
IPCOM_SA_LEN_SET(&s_un, sizeof(s_un.sin6));
IPNET_IP6_SET_ADDR(&s_un.sin6.sin6_addr, s_encap);
s_un.sin6.sin6_port = s_udp? s_udp->sport : 0;
}
if (s_un.sa.sa_family != IP_AF_UNSPEC)
{
len = IPCOM_SA_LEN_GET(&s_un.sa);
cmsg_len += IP_CMSG_SPACE(len);
if (cmsg == IP_NULL || cmsg_len > msg->msg_controllen)
goto truncated;
cmsg->cmsg_len = IP_CMSG_LEN(len);
cmsg->cmsg_level = IP_IPPROTO_IPV6;
cmsg->cmsg_type = IP_IPV6_X_UDPENCAP;
ipcom_memcpy(IP_CMSG_DATA(cmsg), &s_un, IPCOM_SA_LEN_GET(&s_un.sa));
cmsg = IP_CMSG_NXTHDR(msg, cmsg);
}
}
}
if (IP_BIT_ISSET(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_RECVHOPLIMIT))
{
int *hoplimit_ptr;
len = sizeof(int);
cmsg_len += IP_CMSG_SPACE(len);
if (cmsg == IP_NULL || cmsg_len > msg->msg_controllen)
goto truncated;
cmsg->cmsg_len = IP_CMSG_LEN(len);
cmsg->cmsg_level = IP_IPPROTO_IPV6;
cmsg->cmsg_type = IP_IPV6_HOPLIMIT;
hoplimit_ptr = (int *) IP_CMSG_DATA(cmsg);
*hoplimit_ptr = ip6hdr->hlim;
cmsg = IP_CMSG_NXTHDR(msg, cmsg);
}
if (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_RECVPKTINFO))
{
struct Ip_in6_pktinfo *pktinfo;
Ipnet_netif *netif;
len = sizeof(struct Ip_in6_pktinfo);
cmsg_len += IP_CMSG_SPACE(len);
if (cmsg == IP_NULL || cmsg_len > msg->msg_controllen)
goto truncated;
cmsg->cmsg_len = IP_CMSG_LEN(len);
cmsg->cmsg_level = IP_IPPROTO_IPV6;
#ifdef IP_PORT_LKM
cmsg->cmsg_type = IP_IPV6_RECVPKTINFO;
#else
cmsg->cmsg_type = IP_IPV6_PKTINFO;
#endif
pktinfo = (struct Ip_in6_pktinfo *) IP_CMSG_DATA(cmsg);
IPNET_IP6_SET_ADDR(&pktinfo->ipi6_addr, &ip6hdr->dst);
netif = ipnet_if_indextonetif(pkt->vr_index, pkt->ifindex);
if (netif != IP_NULL)
pktinfo->ipi6_ifindex = netif->ipcom.ifindex;
else
pktinfo->ipi6_ifindex = 0;
cmsg = IP_CMSG_NXTHDR(msg, cmsg);
}
if (IP_BIT_ISSET(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_RECVTCLASS))
{
int *tclass_ptr;
len = sizeof(int);
cmsg_len += IP_CMSG_SPACE(len);
if (cmsg == IP_NULL || cmsg_len > msg->msg_controllen)
goto truncated;
cmsg->cmsg_len = IP_CMSG_LEN(len);
cmsg->cmsg_level = IP_IPPROTO_IPV6;
cmsg->cmsg_type = IP_IPV6_TCLASS;
tclass_ptr = (int *) IP_CMSG_DATA(cmsg);
*tclass_ptr = IPNET_IP6_GET_CLASS(ip6hdr);
cmsg = IP_CMSG_NXTHDR(msg, cmsg);
}
for (i = 0; fpst[i].flag != 0; i++)
{
if (IP_BIT_ISSET(sock->ip6->flags, fpst[i].flag) && cmsg != IP_NULL)
{
ext = ipnet_find_extension_header(ip6hdr, fpst[i].proto);
if (ext == IP_NULL)
/* This header is not present */
continue;
len = (ext->len + 1) * 8;
cmsg_len += IP_CMSG_SPACE(len);
if (cmsg == IP_NULL || cmsg_len > msg->msg_controllen)
goto truncated;
cmsg->cmsg_len = IP_CMSG_LEN(len);
cmsg->cmsg_level = IP_IPPROTO_IPV6;
cmsg->cmsg_type = fpst[i].sockopt_type;
ipcom_memcpy(IP_CMSG_DATA(cmsg), ext, len);
cmsg = IP_CMSG_NXTHDR(msg, cmsg);
}
}
truncated:
*pcmsg = cmsg;
*pcmsg_len = cmsg_len;
}
#endif /* IPCOM_USE_INET6 */
/*
*===========================================================================
* ipnet_add_ancillary_ipv4_data
*===========================================================================
* Description: Adds ancillary data to the message.
* Parameters: msg - The message that will receive the ancillary data.
* sock - The socket used.
* pkt - The packet from which the data should be extracted.
* Returns:
*
*/
#ifdef IPCOM_USE_INET
IP_STATIC void
ipnet_add_ancillary_ipv4_data(struct Ip_msghdr *msg,
struct Ip_cmsghdr **pcmsg,
Ip_socklen_t *pcmsg_len,
Ipnet_socket *sock,
Ipcom_pkt *pkt)
{
struct Ip_cmsghdr *cmsg = *pcmsg;
Ip_socklen_t cmsg_len = *pcmsg_len;
Ip_socklen_t len = 0;
Ipnet_pkt_ip *ip4hdr = (Ipnet_pkt_ip *) &pkt->data[pkt->ipstart];
if (IP_BIT_ISSET(sock->ip4->flags, IPNET_SOCKET_FLAG_IP4_RECVTTL))
{
int *ttl_ptr;
len = sizeof(int);
cmsg_len += (Ip_socklen_t)IP_CMSG_SPACE(len);
if (cmsg == IP_NULL || cmsg_len > msg->msg_controllen)
goto truncated;
cmsg->cmsg_len = (Ip_socklen_t)IP_CMSG_LEN(len);
cmsg->cmsg_level = IP_IPPROTO_IP;
cmsg->cmsg_type = IP_IP_TTL;
ttl_ptr = (int *) IP_CMSG_DATA(cmsg);
*ttl_ptr = ip4hdr->ttl;
cmsg = IP_CMSG_NXTHDR(msg, cmsg);
}
if (IP_BIT_ISSET(sock->ip4->flags, IPNET_SOCKET_FLAG_IP4_RECVTOS))
{
int *tos_ptr;
len = sizeof(int);
cmsg_len += (Ip_socklen_t)IP_CMSG_SPACE(len);
if (cmsg == IP_NULL || cmsg_len > msg->msg_controllen)
goto truncated;
cmsg->cmsg_len = (Ip_socklen_t)IP_CMSG_LEN(len);
cmsg->cmsg_level = IP_IPPROTO_IP;
cmsg->cmsg_type = IP_IP_TOS;
tos_ptr = (int *) IP_CMSG_DATA(cmsg);
*tos_ptr = ip4hdr->tos;
cmsg = IP_CMSG_NXTHDR(msg, cmsg);
}
if (IP_BIT_ISSET(sock->ip4->flags, IPNET_SOCKET_FLAG_IP4_RECVDSTADDR))
{
struct Ip_in_addr *to_addr;
len = sizeof(struct Ip_in_addr);
cmsg_len += (Ip_socklen_t)IP_CMSG_SPACE(len);
if (cmsg == IP_NULL || cmsg_len > msg->msg_controllen)
goto truncated;
cmsg->cmsg_len = (Ip_socklen_t)IP_CMSG_LEN(len);
cmsg->cmsg_level = IP_IPPROTO_IP;
cmsg->cmsg_type = IP_IP_RECVDSTADDR;
to_addr = (struct Ip_in_addr *) IP_CMSG_DATA(cmsg);
to_addr->s_addr = IP_GET_32ON16(ip4hdr->dst);
cmsg = IP_CMSG_NXTHDR(msg, cmsg);
}
if (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_RECVPKTINFO))
{
/* Pkt info socket option */
if (sock->ipcom.domain == IP_AF_INET)
{
struct Ip_in_pktinfo *pktinfo;
Ipnet_netif *netif;
len = sizeof(struct Ip_in_pktinfo);
cmsg_len += (Ip_socklen_t)IP_CMSG_SPACE(len);
if (cmsg == IP_NULL || cmsg_len > msg->msg_controllen)
goto truncated;
cmsg->cmsg_len = (Ip_socklen_t)IP_CMSG_LEN(len);
cmsg->cmsg_level = IP_IPPROTO_IP;
cmsg->cmsg_type = IP_IP_PKTINFO;
pktinfo = (struct Ip_in_pktinfo *) IP_CMSG_DATA(cmsg);
ipcom_memset(pktinfo, 0, sizeof(struct Ip_in_pktinfo));
pktinfo->ipi_addr.s_addr = IP_GET_32ON16(ip4hdr->dst);
netif = ipnet_if_indextonetif(pkt->vr_index, pkt->ifindex);
if (netif != IP_NULL)
{
#ifdef IP_PORT_LKM
Ipnet_ip4_addr_entry *ifa = netif->inet4_addr_list;
pktinfo->ipi_spec_dest.s_addr = ifa ? ifa->ipaddr_n : 0;
#endif /* IP_PORT_LKM */
pktinfo->ipi_ifindex = netif->ipcom.ifindex;
}
}
if (cmsg)
cmsg = IP_CMSG_NXTHDR(msg, cmsg);
}
truncated:
*pcmsg = cmsg;
*pcmsg_len = cmsg_len;
}
#endif /* IPCOM_USE_INET */
/*
*===========================================================================
* ipnet_bind_check_reuse
*===========================================================================
* Description: Checks if the source port is reused and if it is, whenever
* the reuse is ok or not.
* Parameters: sock - The socket for which to check port reuse.
* is_unicast - IP_TRUE if the address we are trying to bind
* to is an unicast address, IP_FALSE otherwise.
* Returns: 0 = success, <0 = error code.
*
*/
IP_STATIC int
ipnet_bind_check_reuse(Ipnet_socket *sock, Ip_bool is_unicast)
{
/*
* Check if there is a socket bound to same local port&addr ->
* EADDRINUSE (Consider socket option SO_REUSEADDR and
* SO_REUSEPORT).
*/
Ipnet_socket_lookup_key key;
Ipnet_bound_port *bp;
int reuse;
void *saddr = IP_NULL;
IP_CONST void *any_addr = IP_NULL;
Ipnet_socket *matched_sock;
Ip_u8 has_reuse_port;
Ip_u8 has_reuse_addr;
Ipnet_sock_ops *ops;
ipnet_key_from_sock(sock, &key);
bp = ipnet_sock_bound_port_get(&key);
if (bp == IP_NULL)
/*
* This port is currently not in use
*/
return 0;
reuse = 0;
has_reuse_port = bp->has_reuse_port;
has_reuse_addr = bp->has_reuse_addr;
if (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_REUSEPORT) && has_reuse_port)
reuse |= IPNET_SOCKET_FLAG_REUSEPORT;
else
bp->has_reuse_port = IP_FALSE;
if (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_REUSEADDR) && has_reuse_addr)
reuse |= IPNET_SOCKET_FLAG_REUSEADDR;
else
bp->has_reuse_addr = IP_FALSE;
if (reuse)
{
#if defined(IPTCP) || defined(IPSCTP)
#ifdef IPTCP
if (IP_NULL != sock->tcb)
/* Treat reuse port as reuse addr for TCP */
reuse = IPNET_SOCKET_FLAG_REUSEADDR;
else
#endif
#ifdef IPSCTP
if (IP_NULL != sock->sctp_inst)
/* Treat reuse port as reuse addr for SCTP */
reuse = IPNET_SOCKET_FLAG_REUSEADDR;
else
#endif
#endif /* IPTCP || IPSCTP */
if (!is_unicast)
/*
* UNIX Network Programming, 3 ed, chapter 7.5
*
* SO_REUSEADDR is considered equivalent to
* SO_REUSEPORT if the IP addres being bond is a
* multicast address.
*
* Most implementations seem to have this behavior for
* all non-unicast addresses.
*/
reuse = IPNET_SOCKET_FLAG_REUSEPORT;
}
if (IP_BIT_ISSET(reuse, IPNET_SOCKET_FLAG_REUSEPORT))
return 0;
/* else: reuse addr */
#ifdef IPCOM_USE_INET
if (sock->ipcom.domain == IP_AF_INET)
{
saddr = &sock->ip4->saddr_n;
if (sock->ip4->saddr_n == ip_inaddr_any.s_addr)
any_addr = saddr;
else
any_addr = &ip_inaddr_any;
}
#endif
#ifdef IPCOM_USE_INET6
if (sock->ipcom.domain == IP_AF_INET6)
{
saddr = &sock->ip6->saddr;
if (IP_IN6_IS_ADDR_UNSPECIFIED(saddr))
any_addr = saddr;
else
any_addr = &ip_in6addr_any;
}
#endif
ops = ipnet_sock_get_ops(sock);
if (!reuse)
{
if (any_addr == saddr)
/*
* This socket tried to bind to the ANY address when other
* sockets is already bound to this port, will only work
* if the reuse flag is set.
*/
goto eaddrinuse;
matched_sock = ops->lookup(sock->vr_index, sock->proto,
any_addr, sock->sport,
IP_NULL, 0);
if (matched_sock)
/*
* Socket bound to the ANY address is treated as if it has
* been bound to all local addresses when it comes to
* reuse
*/
goto eaddrinuse;
}
matched_sock = ops->lookup(sock->vr_index, sock->proto,
saddr, sock->sport,
IP_NULL, 0);
if (matched_sock == IP_NULL)
/*
* No other socket bound to the same [domain, vr, protocol,
* local addr, local port]
*/
return 0;
#ifdef IPCOM_USE_INET
if (sock->ipcom.domain == IP_AF_INET
#ifdef IPSCTP /* SCTP can have more than one local address */
&& (IP_IPPROTO_SCTP != sock->proto)
#endif
&& (matched_sock->ipcom.domain != IP_AF_INET
|| sock->ip4->saddr_n != matched_sock->ip4->saddr_n))
/*
* This socket is not bound to the ANY address and its local
* address does not match any other bound socket
*/
return 0;
#endif
#ifdef IPCOM_USE_INET6
if (sock->ipcom.domain == IP_AF_INET6
#ifdef IPSCTP /* SCTP can have more than one local address */
&& (IP_IPPROTO_SCTP != sock->proto)
#endif
&& IP_IN6_ARE_ADDR_EQUAL(&sock->ip6->saddr, &matched_sock->ip6->saddr) == IP_FALSE)
/*
* This socket is not bound to the ANY address and its local
* address does not match any other bound socket
*/
return 0;
#endif
#if defined(IPTCP) || defined(IPSCTP)
if (reuse && IP_BIT_ISSET(matched_sock->flags, IPNET_SOCKET_FLAG_CONNECTED))
{
#ifdef IPTCP
if (IP_NULL != sock->tcb)
return 0;
#endif
#ifdef IPSCTP
if (IP_NULL != sock->sctp_inst)
return 0;
#endif
}
#endif
eaddrinuse:
/*
* bind not ok.
*/
bp->has_reuse_addr = has_reuse_addr;
bp->has_reuse_port = has_reuse_port;
return IPNET_ERRNO(EADDRINUSE);
}
/*
*===========================================================================
* ipnet_sock_get_port_from_sockaddr
*===========================================================================
* Description: Returns the port stored in the socket address structure.
* Parameters: sa - A socket address.
* Returns: The port in host byte order.
*
*/
IP_STATIC Ip_u16
ipnet_sock_get_port_from_sockaddr(IP_CONST struct Ip_sockaddr *sa)
{
switch (sa->sa_family)
{
#ifdef IPCOM_USE_INET
case IP_AF_INET:
return ip_ntohs(((struct Ip_sockaddr_in *)sa)->sin_port);
#endif
#ifdef IPCOM_USE_INET6
case IP_AF_INET6:
return ip_ntohs(((struct Ip_sockaddr_in6 *)sa)->sin6_port);
#endif
default:
IP_PANIC();
return 0;
}
}
/*
*===========================================================================
* ipnet_sock_is_unicast
*===========================================================================
* Description: Returns if an address is an IPv4/IPv6 unicast address.
* Parameters: vr - virtual router index
* sa - the address to test.
* Returns: IP_TRUE if the address is unicast, IP_FALSE otherwise.
*
*/
IP_STATIC Ip_bool
ipnet_sock_is_unicast(Ip_u16 vr, IP_CONST struct Ip_sockaddr *sa)
{
#ifdef IPCOM_USE_INET
if (sa->sa_family == IP_AF_INET)
{
struct Ip_sockaddr_in *sin = (struct Ip_sockaddr_in *) sa;
return (ipnet_ip4_get_addr_type(sin->sin_addr.s_addr, vr, IP_NULL)
== IPNET_ADDR_TYPE_UNICAST);
}
#endif /* IPCOM_USE_INET */
#ifdef IPCOM_USE_INET6
if (sa->sa_family == IP_AF_INET6)
{
struct Ip_sockaddr_in6 *sin6 = (struct Ip_sockaddr_in6 *) sa;
return (ipnet_ip6_get_addr_type(&sin6->sin6_addr, vr, IP_NULL)
== IPNET_ADDR_TYPE_UNICAST);
}
#endif /* IPCOM_USE_INET6 */
return IP_FALSE;
}
/*
*===========================================================================
* ipnet_sock_pkt_dtor
*===========================================================================
* Description: Deallocates a packet from the socket send buffer.
* Parameters: pkt - packet to deallocate.
* Returns:
*
*/
IP_STATIC void
ipnet_sock_pkt_dtor(Ipcom_pkt *pkt)
{
Ipnet_socket *sock;
Ipnet_pkt_info_sock_snd_buf *snd_buf_info;
Ip_bool empty = IP_FALSE;
snd_buf_info = ipcom_pkt_get_info(pkt, IPNET_PKT_INFO_SOCK_SND_BUF);
ip_assert(snd_buf_info != IP_NULL);
sock = snd_buf_info->sock;
ip_assert(ipnet->sockets[sock->ipcom.fd] == sock);
if (ipcom_atomic_get(&sock->snd_bytes) == snd_buf_info->payload_len)
{
empty = IP_TRUE;
IPNET_DATA_LOCK(ipnet->sockets_lock);
}
/*
* Due to the design, this if case cannot be true unless 'empty'
* is TRUE.and the lock held
*/
if (ipcom_atomic_sub_and_return(&sock->snd_bytes, snd_buf_info->payload_len) == 0
&& IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_CLOSED)
&& ipcom_atomic_get(&sock->ref_count) == 0)
{
Ip_bool need_close;
/*
* Need to check this with sockets_lock held, another task
* might be running ipnet_do_close() on this socket
* co-currently as with this context.
* ipnet->sockets[sock->ipcom.fd] will only be equal to sock
* if ipnet_do_close() took the path that did not close the
* socket.
*/
need_close = (ipnet->sockets[sock->ipcom.fd] == sock);
if (!need_close)
/*
* ipnet_do_close() moved the sock->ref_count to zero and
* read the sock->snd_bytes variable between the point
* where this context decremented sock->snd_bytes and read
* sock->ref_count. ipnet_do_close() closed the socket in
* this case.
*/
IPNET_DATA_UNLOCK(ipnet->sockets_lock);
else
{
/*
* Socket has been closed but cleanup was postponed until
* all outstanding packets on this socket has been
* freed. This was the last outstanding packet, so it is
* now safe to free this socket
*/
if (ipnet_is_stack_task())
{
IPNET_DATA_UNLOCK(ipnet->sockets_lock);
ipnet_sock_free(sock, IP_TRUE);
}
else
{
IP_FD_SET(sock->ipcom.fd, &ipnet->async_socket_close_set);
if (ipnet->async_socket_close_pending)
IPNET_DATA_UNLOCK(ipnet->sockets_lock);
else
{
Ipnet_sig *sig = (Ipnet_sig *) ipnet->async_socket_close_sig;
ipnet->async_socket_close_pending = IP_TRUE;
/*
* Cannot free the socket in this context since
* ipnet_sock_free() might modified shared IPNET data
* that is not protected by a mutual exclusion
* lock.
*/
sig->sig_type = IPNET_SIG_SOCKETFREE_ASYNC;
IPNET_DATA_UNLOCK(ipnet->sockets_lock);
ipcom_pipe_send(ipnet->task.pipe, sig);
}
}
}
return;
}
ip_assert(ipcom_atomic_get(&sock->snd_bytes) >= 0);
if (ipcom_atomic_get(&sock->waiting_writers))
{
/*
* At least one waiting writer waiting on this socket
*/
if (ipnet_is_stack_task())
ipnet_sock_data_avail(sock, 0, IP_SHUT_WR);
else
{
Ipnet_sig *sig;
/*
* The caller is not the IPNET daemon, must schedule an
* asynchronous poll notification since
* ipnet_sock_data_avail() can only be called by the IPNET
* daemon
*/
sig = ipcom_slab_alloc(ipnet->slabs.poll);
if (sig != IP_NULL)
{
sig->sig_type = IPNET_SIG_POLL_ASYNC;
sig->d.poll.fd = sock->ipcom.fd;
sig->d.poll.what = IPNET_SOCK_POLL_WRITE;
if (!ipcom_pipe_try_send(ipnet->task.pipe, sig))
ipcom_slab_free(ipnet->slabs.poll, sig);
}
}
}
if (empty)
IPNET_DATA_UNLOCK(ipnet->sockets_lock);
}
/*
*===========================================================================
* ipnet_poll_done
*===========================================================================
* Description: Unregister a watch on socket(s).
* Parameters: poll_sig - a IPNET_SIG_POLL signal.
* sig_type - response to send IPNET_SIG_POLL_DONE or
* IPNET_SIG_POLL_TIMEOUT
* Returns:
*
*/
IP_STATIC void
ipnet_poll_done(Ipnet_sig *poll_sig, enum Ipnet_sig_type sig_type)
{
Ipnet_sig *sig = poll_sig;
do {
/*
* Unregister every socket that is part of this poll session.
*/
if (IP_LIKELY(sig->d.poll.list.head != IP_NULL))
ipcom_list_remove(&sig->d.poll.list);
/*
* else: the poll was interrupted before the poll signal was
* processed by the stack daemon.
*/
if (sig->d.poll.what == IPNET_SOCK_POLL_WRITE)
{
Ipnet_socket *sock = ipnet->sockets[sig->d.poll.fd];
/* One less waiting write on the socket */
if (sock != IP_NULL)
ipcom_atomic_dec(&sock->waiting_writers);
/* else: socket has been closed */
}
ipnet_timeout_cancel(sig->d.poll.tmo);
sig = sig->d.poll.next;
} while (sig != poll_sig);
sig->sig_type = sig_type;
ipcom_pipe_send(sig->response_pipe, sig);
}
/*
*===========================================================================
* ipnet_poll_timeout
*===========================================================================
* Description: Timeout handler for a poll request.
* Parameters: sig - a poll signal that has timed out
* Returns:
*
*/
IP_STATIC void
ipnet_poll_timeout(Ipnet_sig *sig)
{
ipnet_poll_done(sig, IPNET_SIG_POLL_TIMEOUT);
}
/*
*===========================================================================
* ipnet_poll_start
*===========================================================================
* Description: Register a watch on a socket that looks for socket
* to become readable, writable or exceptional.
* Parameters: sig - an IPNET_SIG_POLL signal.
* Returns:
*
*/
IP_STATIC Ip_bool
ipnet_poll_start(Ipnet_sig *sig)
{
struct Ipnet_sig_poll *poll = &sig->d.poll;
Ipnet_socket *sock = ipnet->sockets[poll->fd];
Ipnet_is_sock_ready_cb is_sock_ready;
ip_assert(sock != IP_NULL);
ipcom_list_insert_last(&sock->poll_list_head, &poll->list);
switch (poll->what)
{
case IPNET_SOCK_POLL_READ:
is_sock_ready = ipnet_is_sock_readable;
break;
case IPNET_SOCK_POLL_WRITE:
is_sock_ready = ipnet_is_sock_writable;
ipcom_atomic_inc(&sock->waiting_writers);
break;
default:
ip_assert(poll->what == IPNET_SOCK_POLL_EXCEPT);
is_sock_ready = ipnet_is_sock_exceptional;
break;
}
/*
* Check if the socket has become ready between now and when the
* signal was sent
*/
if (is_sock_ready(sock, poll->lowat))
{
/* It is ready now, wakeup the polling task */
ipnet_signal_ready_sock(sock, poll->what, is_sock_ready);
return IP_FALSE;
}
if (poll->timeout != IP_NULL)
(void)ipnet_timeout_schedule(IPNET_TIMEVAL_TO_MSEC(*poll->timeout),
(Ipnet_timeout_handler) ipnet_poll_timeout,
sig,
&poll->tmo);
return IP_TRUE;
}
/*
*===========================================================================
* ipnet_signal_ready_sock
*===========================================================================
* Description: Goes through all pending user tasks waiting on the
* socket and signals all task where the socket is
* now ready.
* Parameters: sock - the socket that the pending tasks are waiting
* to be ready.
* what - what to check for
* is_sock_ready - function that knows how to check if
* the socket is ready.
* Returns:
*
*/
IP_STATIC void
ipnet_signal_ready_sock(Ipnet_socket *sock,
enum Ipnet_sock_poll what,
Ipnet_is_sock_ready_cb is_sock_ready)
{
Ipcom_list *list_node;
Ipcom_list *next_node;
Ipnet_sig *poll_signal;
for (list_node = ipcom_list_first(&sock->poll_list_head);
list_node != IP_NULL;
list_node = next_node)
{
poll_signal = ipcom_list_entry(list_node, Ipnet_sig, d.poll.list);
if (what == poll_signal->d.poll.what
&& is_sock_ready(sock, poll_signal->d.poll.lowat))
{
ipnet_poll_done(poll_signal, IPNET_SIG_POLL_DONE);
/*
* Must restart scan since there is no guarantee that
* 'next_node' is still valid after running
* ipnet_poll_done().
*/
next_node = ipcom_list_first(&sock->poll_list_head);
}
else
next_node = ipcom_list_next(list_node);
}
}
/*
*===========================================================================
* ipnet_sock_has_waiting
*===========================================================================
* Description: Checks if the specified socket has any user task
* waiting for the socket to become ready in some way.
* Parameters: sock - socket to check
* what - that to check for
* Returns: IP_TRUE = socket has waiting readers.
* IP_FALSE = socket does not have waiting readers.
*
*/
IP_STATIC Ip_bool
ipnet_sock_has_waiting(Ipnet_socket *sock, enum Ipnet_sock_poll what)
{
Ipcom_list *list_node;
Ipnet_sig *poll_signal;
ip_assert(ipnet_is_stack_task());
if (sock->poll_list_head.size == 0)
return IP_FALSE;
list_node = ipcom_list_first(&sock->poll_list_head);
while (list_node != IP_NULL)
{
poll_signal = ipcom_list_entry(list_node, Ipnet_sig, d.poll.list);
if (what == poll_signal->d.poll.what)
return IP_TRUE;
list_node = ipcom_list_next(list_node);
}
return IP_FALSE;
}
/*
*===========================================================================
* ipnet_sock_router_alert_sock
*===========================================================================
* Description: Returns the router alert socket structure for the
* passed socket if it has joined the list of router
* alert sockets.
* Parameters: sock - a socket.
* Returns: IP_NULL = 'sock' has not joined the list of router
* alert sockets.
* The router alert socket instance otherwise.
*
*/
#ifdef IPNET_USE_ROUTER_ALERT
IP_STATIC Ipnet_router_alert_sock *
ipnet_sock_get_router_alert_sock(Ipnet_socket *sock)
{
Ipnet_router_alert_sock *ra_sock;
for (ra_sock = IPCOM_LIST_FIRST(&ipnet_sock_router_alert_list);
ra_sock != IP_NULL;
ra_sock = IPCOM_LIST_NEXT(&ra_sock->list))
{
if (ra_sock->sock == sock)
return ra_sock;
}
return IP_NULL;
}
#endif /* IPNET_USE_ROUTER_ALERT */
/*
****************************************************************************
* 10 GLOBAL FUNCTIONS
****************************************************************************
*/
#ifdef IPNET_USE_ROUTER_ALERT
/*
*===========================================================================
* ipnet_sock_join_router_alert_chain
*===========================================================================
* Description: Joins the list of sockets interested in IP-datagrams
* with the router alert option set.
* Parameters: sock - a socket.
* Returns: 0 = success
* -IP_ERRNO_EALREADY = socket is already a member
* -IP_ERRNO_ENOMEM = not enough memory.
*
*/
IP_GLOBAL int
ipnet_sock_join_router_alert_chain(Ipnet_socket *sock)
{
Ipnet_router_alert_sock *ra_sock;
ra_sock = ipnet_sock_get_router_alert_sock(sock);
if (ra_sock != IP_NULL)
/*
* Already a member
*/
return -IP_ERRNO_EALREADY;
ra_sock = ipcom_malloc(sizeof(*ra_sock));
if (ra_sock == IP_NULL)
return -IP_ERRNO_ENOMEM;
ipcom_list_insert_last(&ipnet_sock_router_alert_list,
&ra_sock->list);
ra_sock->sock = sock;
return 0;
}
/*
*===========================================================================
* ipnet_sock_leave_router_alert_chain
*===========================================================================
* Description: Leaves the list of sockets interested in IP-datagrams
* with the router alert option set.
* Parameters: sock - a socket.
* Returns: 0 = success
* -IP_ERRNO_ESRCH = socket was not part of the RA chain
*
*/
IP_GLOBAL int
ipnet_sock_leave_router_alert_chain(Ipnet_socket *sock)
{
Ipnet_router_alert_sock *ra_sock;
ra_sock = ipnet_sock_get_router_alert_sock(sock);
if (ra_sock == IP_NULL)
return -IP_ERRNO_ESRCH;
ipcom_list_remove(&ra_sock->list);
ipcom_free(ra_sock);
return 0;
}
/*
*===========================================================================
* ipnet_sock_router_alert_pkt
*===========================================================================
* Description: Delivers the packet to all sockets that has enabled
* the IP_ROUTER_ALERT socket option.
* Parameters: pkt - an IPv4 with router alert option.
* Returns: 0 = packet was not deliverd to any sockets, packet is
* still valid
* >0 = number of sockets the packet was queued on,
* packet cannot be used by the caller anymore.
*
*/
IP_GLOBAL int
ipnet_sock_router_alert_pkt(Ipcom_pkt *pkt)
{
Ipnet_router_alert_sock *ra_sock;
int num_ra_sock = 0;
for (ra_sock = IPCOM_LIST_FIRST(&ipnet_sock_router_alert_list);
ra_sock != IP_NULL && pkt != IP_NULL;
ra_sock = IPCOM_LIST_NEXT(&ra_sock->list))
{
Ipcom_pkt *clone;
#ifdef IPCOM_USE_INET
if (ra_sock->sock->ipcom.domain == IP_AF_INET
&& IP_BIT_ISFALSE(pkt->flags, IPCOM_PKT_FLAG_IPV4))
continue;
#endif
#ifdef IPCOM_USE_INET6
if (ra_sock->sock->ipcom.domain == IP_AF_INET6
&& IP_BIT_ISFALSE(pkt->flags, IPCOM_PKT_FLAG_IPV6))
continue;
#endif
clone = ipnet_pkt_clone(pkt, IP_TRUE);
if (clone != IP_NULL)
{
if (ipnet_queue_received_packet(clone, ra_sock->sock) >= 0)
num_ra_sock++;
else
ipcom_pkt_free(clone);
}
}
if (num_ra_sock > 0)
/*
* At least one matching socket, the packet should not
* continue to be processed.
*/
ipcom_pkt_free(pkt);
return num_ra_sock;
}
#elif defined(IPCOM_FORWARDER_NAE)
IP_GLOBAL int
ipnet_sock_router_alert_pkt(Ipcom_pkt *pkt)
{
/*
* This is an NAE. Routing alert must be processed by the master
* OS.
*/
ipcom_pkt_to_master_os(pkt);
/*
* This packet has been taken care of, return >0.
*/
return 1;
}
#else /* !IPNET_USE_ROUTER_ALERT && !IPCOM_FORWARDER_NAE */
IP_GLOBAL int
ipnet_sock_router_alert_pkt(Ipcom_pkt *pkt)
{
/*
* Stack build without routing support, so router alerts should
* not be processed.
*/
IPCOM_UNUSED_ARG(pkt);
return 0;
}
#endif
/*
*===========================================================================
* ipnet_sock_update_reuse
*===========================================================================
* Description: Update the reuse status for the port used by 'sock'
* Parameters: sock - socket for which reuse status has changed.
* Returns:
*
*/
IP_GLOBAL void
ipnet_sock_update_reuse(Ipnet_socket *sock)
{
int i;
Ipnet_socket_lookup_key key;
Ipnet_bound_port *bp;
if (IP_BIT_ISFALSE(sock->flags, IPNET_SOCKET_FLAG_BOUND))
/*
* No need to update reuse since the socket isn't bound yet.
*/
return;
ipnet_key_from_sock(sock, &key);
bp = ipnet_sock_bound_port_get(&key);
if (bp == IP_NULL)
return;
bp->has_reuse_addr =
(Ip_u8) IP_BIT_ISTRUE(sock->flags, IPNET_SOCKET_FLAG_REUSEADDR);
bp->has_reuse_port =
(Ip_u8) IP_BIT_ISTRUE(sock->flags, IPNET_SOCKET_FLAG_REUSEPORT);
if (ipcom_atomic_get(&bp->refcnt) > 1)
{
/*
* Must 'and' all the reuse status of all socket using the
* port.
*/
for (i = 0; i < ipnet_conf_max_sockets; i++)
{
Ipnet_socket *s = ipnet->sockets[i];
if (s != IP_NULL
&& s->ipcom.domain == sock->ipcom.domain
&& s->ipcom.type == sock->ipcom.type
&& s->proto == sock->proto
&& s->sport == sock->sport)
{
bp->has_reuse_addr = (Ip_u8) (bp->has_reuse_addr &
(Ip_u8) IP_BIT_ISTRUE(s->flags, IPNET_SOCKET_FLAG_REUSEADDR));
bp->has_reuse_port = (Ip_u8) (bp->has_reuse_port &
(Ip_u8) IP_BIT_ISTRUE(s->flags, IPNET_SOCKET_FLAG_REUSEPORT));
}
}
}
/*
* else: Take the reuse status of the only socket (sock) using
* this port.
*/
}
/*
*===========================================================================
* ipnet_sock_get_bound_port_size
*===========================================================================
* Description: Returns the size of the Ipnet_bound_ports structure
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_size_t
ipnet_sock_get_bound_port_size()
{
return sizeof(Ipnet_bound_port);
}
/*
*===========================================================================
* ipnet_sock_get
*===========================================================================
* Description: Checks if a socket matches a lookup key.
* Parameters: key - A socket lookup key
* head - will contain a pointer to the list head where
* the socket is located in/would be located in.
* Returns: IP_NULL - no matching socket
* !=IP_NULL - matching socket
*
*/
IP_GLOBAL Ipnet_socket *
ipnet_sock_get(Ipnet_socket_lookup_key *key, Ipcom_list **head)
{
unsigned idx;
Ipcom_list *bucket;
Ipcom_list *node;
Ipnet_socket *sock = IP_NULL;
/*
* Calculate which bucket the socket is located in if present.
*/
idx = key->domain + (key->proto << 8) + (key->vr << 16);
idx += key->local_port + (key->peer_port << 16);
switch (key->domain)
{
#ifdef IPCOM_USE_INET
case IP_AF_INET:
idx = ipcom_hash_update(&key->local_addr.in,
sizeof(key->local_addr.in),
idx);
idx = ipcom_hash_update(&key->peer_addr.in,
sizeof(key->peer_addr.in),
idx);
break;
#endif
#ifdef IPCOM_USE_INET6
case IP_AF_INET6:
idx = ipcom_hash_update(&key->local_addr.in6,
sizeof(key->local_addr.in6),
idx);
idx = ipcom_hash_update(&key->peer_addr.in6,
sizeof(key->peer_addr.in6),
idx);
break;
#endif
default:
break;
}
idx %= (unsigned)ipnet->reachable_sockets_len;
bucket = &ipnet->reachable_sockets[idx];
for (node = IPCOM_LIST_FIRST(bucket);
node != IP_NULL;
node = IPCOM_LIST_NEXT(node))
{
sock = ipcom_list_entry(node, Ipnet_socket, reachable_list);
if (sock->sport != key->local_port
|| sock->dport != key->peer_port
|| sock->ipcom.domain != key->domain
|| sock->proto != key->proto
|| sock->vr_index != key->vr)
continue;
switch (sock->ipcom.domain)
{
#ifdef IPCOM_USE_INET
case IP_AF_INET:
if (sock->ip4->saddr_n != key->local_addr.in.s_addr
|| sock->ip4->daddr_n != key->peer_addr.in.s_addr)
continue;
break;
#endif
#ifdef IPCOM_USE_INET6
case IP_AF_INET6:
if (IP_IN6_ARE_ADDR_EQUAL(&sock->ip6->saddr, &key->local_addr.in6) == IP_FALSE
|| IP_IN6_ARE_ADDR_EQUAL(&sock->ip6->daddr, &key->peer_addr.in6) == IP_FALSE)
continue;
break;
#endif
default:
break;
}
/*
* This socket matches
*/
break;
}
if (head)
*head = bucket;
return node ? sock : IP_NULL;
}
/*
*===========================================================================
* ipnet_sock_next
*===========================================================================
* Description: Returns the next socket matching the search key
* Parameters: key - a search key
* start_sock - socket to start from, the socket
* following this socket is the first one
* to be investigated.
* Returns:
*
*/
IP_GLOBAL Ipnet_socket *
ipnet_sock_next(Ipnet_socket *start_sock)
{
if (start_sock->first_sibling)
{
Ipcom_list *node = &start_sock->reachable_list;
while (IP_NULL != (node = IPCOM_LIST_NEXT(node)))
{
Ipnet_socket *sock;
sock = ipcom_list_entry(node, Ipnet_socket, reachable_list);
if (start_sock->first_sibling == sock->first_sibling)
return sock;
}
}
/*
* else: 'start_sock' has a unique lookup tuple.
*/
return IP_NULL;
}
/*
*===========================================================================
* ipnet_sock_has_waiting_writers
*===========================================================================
* Description: Checks if the specified socket has any user task
* waiting to write on this socket.
* Parameters: sock - a socket.
* Returns: IP_TRUE = socket has waiting writers.
* IP_FALSE = socket does not have waiting writers.
*
*/
IP_GLOBAL Ip_bool
ipnet_sock_has_waiting_writers(Ipnet_socket *sock)
{
return ipnet_sock_has_waiting(sock, IPNET_SOCK_POLL_WRITE);
}
/*
*===========================================================================
* ipnet_is_sock_readable
*===========================================================================
* Description: Checks if the specified socket is readable.
* Parameters: sock - the socket to check.
* lowat - minimum number of bytes that must be present
* in the receive queue to cont the socket as
* readable. 0 means to use the socket lo
* watermask value.
* Returns: IP_TRUE = socket is readable
* IP_FALSE = socket is not readable
*
*/
IP_GLOBAL Ip_bool
ipnet_is_sock_readable(Ipnet_socket *sock, int lowat)
{
static Ip_socklen_t lowat_len = sizeof(lowat);
Ipnet_sock_ops *ops;
int bytes_ready = sock->ipcom.rcv_bytes;
if (sock->ipcom.so_errno)
/* Sockets with pending error is always readable */
return IP_TRUE;
if(sock->ipcom.type == IP_SOCK_STREAM)
{
Ip_bool server = IP_FALSE;
#ifdef IPTCP
if((sock->proto == IP_IPPROTO_TCP)
&& (sock->tcb != IP_NULL)
&&(IP_BIT_ISFALSE(sock->tcb->flags, IPTCP_TCPC_FLAG_PASSIVE_OPEN)))
{
server = IP_TRUE;
}
#endif
#ifdef IPSCTP
if((sock->proto == IP_IPPROTO_SCTP)
&& (sock->sctp_inst != IP_NULL)
&& (IP_BIT_ISFALSE(sock->sctp_inst->inst_flags, IPSCTP_SOCKET_LISTENING)))
{
server = IP_TRUE;
}
#endif
if(server &&(IP_BIT_ISFALSE(sock->flags,
(IPNET_SOCKET_FLAG_CONNECTING
| IPNET_SOCKET_FLAG_CONNECTED))))
return IP_TRUE;
}
ops = ipnet_sock_get_ops(sock);
if (lowat == 0
&& (ops->getopt == IP_NULL
|| ops->getopt(sock, IP_SOL_SOCKET, IP_SO_RCVLOWAT, &lowat, &lowat_len) != 0))
lowat = 1;
#ifdef IPTCP
if (sock->tcb != IP_NULL
&& bytes_ready <= lowat
&& IP_BIT_ISFALSE(sock->flags, IPNET_SOCKET_FLAG_OOBINLINE)
&& ipnet_is_sock_exceptional(sock, 0))
{
int octets_to_mark;
(void)ops->ioctl(sock, IP_X_SIOCTOMARK, &octets_to_mark);
if (octets_to_mark == 0)
/*
* Next octet is the OOB octet, the socket is not readable
* in this case since the number of ready bytes is at
* least one less than the low-watermark.
*/
return IP_FALSE;
if (lowat > octets_to_mark && bytes_ready >= octets_to_mark)
/*
* 'lowat' is larger than the number of bytes left to the
* OOB octet, but all the octets before the OOB has been
* received. The socket must be readable in this case
* regardless of the low-watermark value.
*/
return IP_TRUE;
}
#endif
return (bytes_ready >= lowat
#ifdef IPSCTP
&& (sock->sctp_inst == IP_NULL
|| (ipcom_atomic_get(&sock->sctp_inst->wait_readable) == 0))
#endif
)
#ifdef IPTCP
|| (sock->tcb != IP_NULL
&& ((sock->tcb->backlog_done != IP_NULL
&& sock->tcb->backlog_done[0] != IP_SOCKERR)
|| IP_BIT_ISSET(sock->tcb->flags, IPTCP_TCPC_FLAG_CONNABORTED)))
#endif
#ifdef IPSCTP
|| (sock->sctp_inst != IP_NULL
&& ((sock->sctp_inst->backlog_done != IP_NULL
&& sock->sctp_inst->backlog_done[0] != IP_SOCKERR)
|| IP_BIT_ISSET(sock->sctp_inst->inst_flags, IPSCTP_SOCKET_CONNABORTED)))
#endif
|| IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_READ_CLOSED)
|| IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_PEER_WRITE_CLOSED);
}
/*
*===========================================================================
* ipnet_is_sock_writable
*===========================================================================
* Description: Checks if the specified socket is writable.
* Parameters: sock - the socket to check.
* lowat - minimum number of bytes that must be available
* in the transmit queue to count the socket as
* writable. 0 means to use the socket lo
* watermark value.
* Returns: IP_TRUE = socket is writable
* IP_FALSE = socket is not writable
*
*/
IP_GLOBAL Ip_bool
ipnet_is_sock_writable(Ipnet_socket *sock, int lowat)
{
static Ip_socklen_t lowat_len = sizeof(lowat);
Ipnet_sock_ops *ops;
ops = ipnet_sock_get_ops(sock);
if (lowat == 0
&& (ops->getopt == IP_NULL
|| ops->getopt(sock, IP_SOL_SOCKET, IP_SO_SNDLOWAT, &lowat, &lowat_len) != 0))
lowat = 1;
if (sock->ipcom.so_errno
|| IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_WRITE_CLOSED))
return IP_TRUE;
if ((IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_CONNECTED)
|| ((sock->ipcom.type != IP_SOCK_STREAM)
#ifdef IPSCTP
&& (IP_SOCK_SEQPACKET != sock->ipcom.type) /* SCTP is also based on connection */
#endif
))
&& sock->send_max_bytes - ipcom_atomic_get(&sock->snd_bytes) >= lowat)
return IP_TRUE;
return IP_FALSE;
}
/*
*===========================================================================
* ipnet_is_sock_exceptional
*===========================================================================
* Description: Checks if the specified socket is exceptional.
* Parameters: sock - the socket to check.
* lowat - unused. Added so this function has the same
* signature as the other ipnet_is_sock_xxx().
* Returns: IP_TRUE = socket is exceptional
* IP_FALSE = socket is not exceptional.
*
*/
IP_GLOBAL Ip_bool
ipnet_is_sock_exceptional(Ipnet_socket *sock, int lowat)
{
int in_urgent_mode;
Ipnet_sock_ops *ops;
IPCOM_UNUSED_ARG(lowat);
ops = ipnet_sock_get_ops(sock);
if (sock->ipcom.so_errno
|| (sock->ipcom.rcv_bytes > 0
&& ops->ioctl != IP_NULL
&& ops->ioctl(sock, IP_X_SIOCINURG, &in_urgent_mode) == 0
&& in_urgent_mode))
return IP_TRUE;
return IP_FALSE;
}
/*
*===========================================================================
* ipnet_sys_poll
*===========================================================================
* Description: Register a watch on a socket that looks for socket(s)
* to become readable, writable or exceptional.
* Parameters: poll_sig - an IPNET_SIG_POLL signal.
* Returns:
*
*/
IP_GLOBAL void
ipnet_sys_poll(Ipnet_sig *poll_sig)
{
Ipnet_sig *sig = poll_sig;
Ipnet_sig *next;
do {
next = sig->d.poll.next;
if (sig != poll_sig)
{
/*
* Must only run a single timer for all polls. Initialize
* the sender and receiver PID for all signals that wasn't
* sent through the pipe since any one of the signals
* might be used as response.
*/
sig->d.poll.timeout = IP_NULL;
sig->receiver_pid = poll_sig->receiver_pid;
sig->sender_pid = poll_sig->sender_pid;
}
if (!ipnet_poll_start(sig))
break;
sig = next;
} while (sig != poll_sig);
}
/*
*===========================================================================
* ipnet_sys_poll_async
*===========================================================================
* Description: Checks if a socket is ready and notifies any pending
* processes if it is.
* Parameters: sig - an IPNET_SIG_POLL_ASYNC signal.
* Returns:
*
*/
IP_GLOBAL void
ipnet_sys_poll_async(Ipnet_sig *sig)
{
int how;
switch (sig->d.poll.what)
{
case IPNET_SOCK_POLL_READ:
how = IP_SHUT_RD;
break;
case IPNET_SOCK_POLL_WRITE:
how = IP_SHUT_WR;
break;
default:
#ifdef IPTCP
how = IP_SHUT_EX;
#else
how = 0;
#endif /* IPTCP */
break;
}
/* Only poll if there's a socket available with the corresponding FD */
if (ipnet->sockets[sig->d.poll.fd] != IP_NULL)
{
#ifdef IPNET_USE_NETLINKSOCK
if (ipnet->sockets[sig->d.poll.fd]->nl != IP_NULL)
ipnet_netlink_dump_resume(ipnet->sockets[sig->d.poll.fd]);
#endif
ipnet_sock_data_avail(ipnet->sockets[sig->d.poll.fd],
0,
how);
}
ipcom_slab_free(ipnet->slabs.poll, sig);
}
/*
*===========================================================================
* ipnet_sock_pkt_drain
*===========================================================================
* Description: Tries to locate packets that can be freed since the stack
* is a low packet situation.
* Parameters:
* Returns:
*
*/
IP_GLOBAL void
ipnet_sock_pkt_drain(void)
{
int i;
Ipnet_socket *sock;
for (i = 0; i < ipnet_conf_max_sockets; i++)
{
sock = ipnet->sockets[i];
if (sock == IP_NULL
|| IP_BIT_ISFALSE(sock->flags, IPNET_SOCKET_FLAG_DRAINABLE))
continue;
while (sock->rcv_head != sock->rcv_tail)
{
struct Ip_msghdr msg;
/* Socket allows packet drain and has more than one packet queued */
ipcom_memset(&msg, 0, sizeof(msg));
(void)ipnet_usr_sock_recvmsg(sock, &msg, 0);
}
}
}
/*
*===========================================================================
* ipnet_sock_register_ops
*===========================================================================
* Description: Registers operations for a new domain/type/proto tuple.
* Parameters: ops - The socket operations to register
* Returns: 0 = success, <0 = error code.
*
*/
IP_GLOBAL int
ipnet_sock_register_ops(Ipnet_sock_ops *ops)
{
Ipnet_sock_ops **new_ops;
Ip_size_t this_sock_size;
if (ops->domain < 0)
{
IPCOM_LOG1(ERR, "Tried to register an invalid domain: %d", ops->domain);
return IPNET_ERRNO(EPFNOSUPPORT);
}
if (ops->type < 0)
{
IPCOM_LOG1(ERR, "Tried to register an invalid type: %d", ops->domain);
return IPNET_ERRNO(ESOCKTNOSUPPORT);
}
if (ops->proto < -1)
{
IPCOM_LOG1(ERR, "Tried to register an invalid protocol: %d", ops->domain);
return IPNET_ERRNO(EINVAL);
}
this_sock_size = sizeof(Ipnet_socket) + ops->extra_sock_space;
#if defined(IP_PORT_VXWORKS) && defined(IPCOM_USE_SOCK_COMPAT)
this_sock_size += ipcom_windnet_socklib_sock_size();
#endif
if (ipnet->largest_socket_size < this_sock_size)
ipnet->largest_socket_size = this_sock_size;
if (ipnet_sock_get_ops_handle(ops->domain,
ops->type,
ops->proto,
IP_NULL,
IP_NULL) >= 0)
/*
* A handler for (domain, type, proto) has already been
* registered.
*/
return IPNET_ERRNO(EEXIST);
new_ops = ipcom_realloc(ipnet_sock_ops,
sizeof(*new_ops) * (ipnet_sock_ops_len + 1));
if (new_ops == IP_NULL)
{
IP_PANIC2();
return IPNET_ERRNO(ENOMEM);
}
ipnet_sock_ops = new_ops;
ipnet_sock_ops[ipnet_sock_ops_len++] = ops;
IPCOM_LOG3(DEBUG, "New socket constructor added, domain:%2d, type:%2d, proto:%2d",
ops->domain, ops->type, ops->proto);
return 0;
}
/*
*===========================================================================
* ipnet_sock_alloc_private_data
*===========================================================================
* Description: Returns a pointer to a memory space inside the socket buffer
* that can be used by the layer. Available space for this
* buffer is allocated during socket creation via the
* 'extra_sock_space' variable.
* Parameters:
* Returns: 0 = success
* <0 = error code
*
*/
IP_GLOBAL void *
ipnet_sock_alloc_private_data(Ipnet_socket *sock, Ip_size_t size)
{
void *d;
if (sock->sock_end == IP_NULL)
sock->sock_end = sock + 1;
d = sock->sock_end;
sock->sock_end = (Ip_u8 *) sock->sock_end + size;
return d;
}
/*
*===========================================================================
* ipnet_sock_set_reachable
*===========================================================================
* Description: Inserts the socket into the reachable sockets table.
* Parameters: sock - The socket to make reachable.
* Returns: 0 = success, <0 = error code.
*
*/
IP_GLOBAL int
ipnet_sock_set_reachable(Ipnet_socket *sock)
{
Ipnet_socket_lookup_key key;
Ipcom_list *reachable_sock_list;
Ipnet_socket *sibling_sock;
if (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_REACHABLE))
return 0;
#ifdef IPSCTP
if (IP_IPPROTO_SCTP == sock->proto)
{
/*
* SCTP has its own socket lookup mechanism.
*/
IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_REACHABLE);
return 0;
}
#endif
ipnet_key_from_sock(sock, &key);
sibling_sock = ipnet_sock_get(&key, &reachable_sock_list);
if (sibling_sock)
{
/*
* There is, at least, one other socket matching the same
* key. All sockets matching the same key must have the same
* 'reachable_sibling_sock' value.
*/
sock->first_sibling = sibling_sock;
sibling_sock->first_sibling = sibling_sock;
}
ipcom_list_insert_last(reachable_sock_list, &sock->reachable_list);
IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_REACHABLE);
return 0;
}
/*
*===========================================================================
* ipnet_sock_set_unreachable
*===========================================================================
* Description: Removes the socket from the reachable socket table, a socklookup
* can will no longer find this socket.
* Parameters: sock - The socket to make unreachable.
* Returns:
*
*/
IP_GLOBAL void
ipnet_sock_set_unreachable(Ipnet_socket *sock)
{
if (IP_BIT_ISFALSE(sock->flags, IPNET_SOCKET_FLAG_REACHABLE))
return;
#ifdef IPSCTP
/*
* Directly return if SCTP socket
*/
if (IP_IPPROTO_SCTP == sock->proto)
{
IP_BIT_CLR(sock->flags, IPNET_SOCKET_FLAG_REACHABLE);
return;
}
#endif
if (sock->first_sibling == sock)
{
Ipnet_socket *new_first_sibling;
/*
* This socket was the first socket out of many that are using
* the same lookup tuple. The 'first_sibling' must be changed
* to the socket that now will be the first
* one. 'first_sibling' must be set to IP_NULL if the
* remaining socket is the only socket with the specific
* lookup tuple.
*/
new_first_sibling = ipnet_sock_next(sock);
if (new_first_sibling != IP_NULL)
{
if (ipnet_sock_next(new_first_sibling) == IP_NULL)
new_first_sibling->first_sibling = IP_NULL;
else
{
Ipnet_socket *prev;
Ipnet_socket *cur = new_first_sibling;
do
{
prev = cur;
cur = ipnet_sock_next(prev);
/*
* Must call ipnet_sock_next(prev) before changing the
* 'first_sibling' in order to find all siblings that
* has to update their 'first_sibling'.
*/
prev->first_sibling = new_first_sibling;
} while (cur != IP_NULL);
}
}
}
else if (sock->first_sibling)
{
/*
* This is not the first of the siblings, but it might be the
* only one except the first one. If that is the case,
* sock->first_sibling->first_sibling must be set to IP_NULL.
*/
struct Ipnet_socket_struct *next_sock = IP_NULL;
next_sock = ipnet_sock_next(sock->first_sibling);
if (next_sock != IP_NULL && ipnet_sock_next(next_sock) == IP_NULL)
sock->first_sibling->first_sibling = IP_NULL;
}
sock->first_sibling = IP_NULL;
ipcom_list_remove(&sock->reachable_list);
IP_BIT_CLR(sock->flags, IPNET_SOCKET_FLAG_REACHABLE);
}
/*
*===========================================================================
* ipnet_sock_unconnect
*===========================================================================
* Description: Unconnects the socket.
* Parameters: sock - The socket to unconnect.
* Returns: 1 = success, <0 = error code
*
*/
IP_GLOBAL int
ipnet_sock_unconnect(Ipnet_socket *sock)
{
int ret;
IP_BIT_CLR(sock->flags, IPNET_SOCKET_FLAG_CONNECTED);
sock->dport = 0;
ret = ipnet_sock_set_reachable(sock);
if (ret < 0)
return ret;
return 1;
}
/*
*===========================================================================
* ipnet_sock_unbind
*===========================================================================
* Description: Unbind the specific IP_AF_INET or IP_AF_INET6 socket.
* Parameters: sock - The socket to unbind.
* Returns: 0 = success, <0 = error code
*
*/
IP_GLOBAL void
ipnet_sock_unbind(Ipnet_socket *sock)
{
Ipnet_socket_lookup_key key;
ipnet_sock_set_unreachable(sock);
#ifndef IPCOM_FORWARDER_NAE
#ifdef IPCOM_USE_INET
if (sock->ipcom.domain == IP_AF_INET)
{
/*
* Remove the reference from the IPv4 address this socket was
* bound to.
*/
Ipnet_ip4_addr_entry *addr_entry = sock->addr_entry;
if (addr_entry != IP_NULL && addr_entry->socket_list == sock)
addr_entry->socket_list = sock->addr_next;
}
#endif /* IPCOM_USE_INET */
#ifdef IPCOM_USE_INET6
if (sock->ipcom.domain == IP_AF_INET6)
{
/*
* Remove the reference from the IPv6 address this socket was
* bound to.
*/
Ipnet_ip6_addr_entry *addr_entry = sock->addr_entry;
if (addr_entry != IP_NULL && addr_entry->socket_list == sock)
addr_entry->socket_list = sock->addr_next;
}
#endif /* IPCOM_USE_INET6 */
/*
* Remove this socket from the list of sockets bound to the
* address.
*/
if (sock->addr_next != IP_NULL)
sock->addr_next->addr_prev = sock->addr_prev;
if (sock->addr_prev != IP_NULL)
sock->addr_prev->addr_next = sock->addr_next;
sock->addr_entry = IP_NULL;
sock->addr_next = IP_NULL;
sock->addr_prev = IP_NULL;
#endif /* !IPCOM_FORWARDER_NAE */
if (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_BOUND))
{
Ipnet_bound_port *bp;
/*
* This socket is bound to a specific port, unbind it.
*/
ipnet_key_from_sock(sock, &key);
bp = ipnet_sock_bound_port_get(&key);
if (bp != IP_NULL && ipcom_atomic_sub_and_return(&bp->refcnt, 1) == 0)
{
ipcom_list_remove(&bp->list);
#ifdef IPCOM_FORWARDER_NAE
ipcom_shm_free(bp);
#else
ipcom_slab_free(ipnet->slabs.bound_port, bp);
#endif
}
}
sock->sport = 0;
IP_BIT_CLR(sock->flags, IPNET_SOCKET_FLAG_BOUND);
}
/*
*===========================================================================
* ipnet_sock_bind
*===========================================================================
* Description: Binds a IP_AF_INET or IP_AF_INET6 socket to a specific
* socket address and possibly port.
* Parameters: sock - A pointer to the socket data.
* name - The socket name.
* namelen - The length of the name variable.
* check_reuse - Set to IP_TRUE if the name should be checked
* for address reuse.
* Returns: 0 = success, <0 = error code
*
*/
IP_GLOBAL int
ipnet_sock_bind(Ipnet_socket *sock,
IP_CONST struct Ip_sockaddr *name,
Ip_socklen_t namelen,
Ip_bool check_reuse)
{
Ipnet_sock_inet_ops *ops;
int err;
ipnet_sock_set_unreachable(sock);
/*
* Call the transport layer bind (UDP and TCP)
* or network layer bind (RAW)
*/
ops = (Ipnet_sock_inet_ops *) ipnet_sock_get_ops(sock);
err = ops->network_bind(sock, name, namelen, check_reuse);
if (err >= 0)
{
IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_BOUND);
err = ipnet_sock_set_reachable(sock);
}
if (err < 0)
ipnet_sock_unbind(sock);
return err;
}
/*
*===========================================================================
* ipnet_sock_bind_to_port
*===========================================================================
* Description: Binds the socket to a specific port.
* Parameters: sock - A pointer to the socket data.
* name - The address the socket should be bound to.
* check_reuse - IP_TRUE if the port should be checked for reuse.
* Returns: 0 = success, <0 = error code
*
*/
IP_GLOBAL int
ipnet_sock_bind_to_port(Ipnet_socket *sock,
IP_CONST struct Ip_sockaddr *name,
Ip_bool check_reuse)
{
Ipnet_socket_lookup_key key;
Ipnet_bound_port *bp;
sock->sport = ipnet_sock_get_port_from_sockaddr(name);
/*
* If port 0, pick an ephemeral port
*/
if (sock->sport == 0)
{
sock->sport = ipnet_next_ephemeral_port(sock);
if (sock->sport == 0)
return IPNET_ERRNO(EADDRINUSE);
}
if (check_reuse)
{
int err;
err = ipnet_bind_check_reuse(sock,
ipnet_sock_is_unicast(sock->vr_index,
name));
if (err < 0)
return err;
}
ipnet_key_from_sock(sock, &key);
bp = ipnet_sock_bound_port_get(&key);
if (bp)
ipcom_atomic_inc(&bp->refcnt);
else
{
bp = ipnet_sock_bound_port_add(sock);
if (bp == IP_NULL)
return IPNET_ERRNO(ENOMEM);
}
return 0;
}
/*
*===========================================================================
* ipnet_sock_bind_addr_removed
*===========================================================================
* Description: The address used by the socket(s) in the passed list has been
* removed.
* Parameters: sock_list - The list of sockets bound to the removed address.
* Returns:
*
*/
IP_GLOBAL void
ipnet_sock_bind_addr_removed(Ipnet_socket *sock_list)
{
#ifdef IPCOM_FORWARDER_NAE
IPCOM_UNUSED_ARG(sock_list);
#else
Ipnet_socket *sock = sock_list;
Ipnet_socket *next_sock;
while (sock != IP_NULL)
{
next_sock = sock->addr_next;
sock->addr_next = IP_NULL;
sock->addr_prev = IP_NULL;
sock->addr_entry = IP_NULL;
if (ipnet->conf.sock.addr_notify)
{
#ifdef IPTCP
if (sock->tcb != IP_NULL)
(void)iptcp_drop_connection(sock, -IP_ERRNO_ENETDOWN);
else
#endif /* IPTCP */
ipnet_sock_data_avail(sock, IP_ERRNO_ENETDOWN, IP_SHUT_RDWR);
}
sock = next_sock;
}
#endif /* !IPCOM_FORWARDER_NAE */
}
/*
*===========================================================================
* ipnet_sock_change_addr_entry
*===========================================================================
* Description: The address the sockets in the old list is bound to has been
* removed, but there are an identical address assigned to
* another interface which can be used
* Parameters: old_sock_list - The list of socket bound to the removed address.
* pnew_sock_list - Pointer to the head of the socket list on
* the new address entry to use, this list may already contain
* sockets.
* addr_entry - Pointer to the new address entry.
* Returns:
*
*/
IP_GLOBAL void
ipnet_sock_change_addr_entry(Ipnet_socket *old_sock_list,
Ipnet_socket **pnew_sock_list,
void *addr_entry)
{
#ifdef IPCOM_FORWARDER_NAE
IPCOM_UNUSED_ARG(old_sock_list);
IPCOM_UNUSED_ARG(pnew_sock_list);
IPCOM_UNUSED_ARG(addr_entry);
#else
Ipnet_socket *sock;
Ipnet_socket *sock_last = IP_NULL;
for (sock = old_sock_list; sock != IP_NULL; sock = sock->addr_next)
{
/* Reassign all sockets in the old list to the new address entry */
sock->addr_entry = addr_entry;
sock_last = sock;
}
sock = *pnew_sock_list;
if (sock == IP_NULL)
*pnew_sock_list = old_sock_list;
else
{
/* Insert the old_sock_list after the first entry in the new list */
sock_last->addr_next = sock->addr_next;
if (sock->addr_next != IP_NULL)
sock->addr_next->addr_prev = sock_last;
sock->addr_next = old_sock_list;
old_sock_list->addr_prev = sock;
}
#endif /* !IPCOM_FORWARDER_NAE */
}
/*
*===========================================================================
* ipnet_next_ephemeral_port
*===========================================================================
* Description: Returns the next ephemeral port.
* Parameters: sock - The socket that will use the port.
* Returns: The next free ephemeral port or 0 if no port could be found.
* The port is retured in host byte order.
*
*/
IP_GLOBAL Ip_u16
ipnet_next_ephemeral_port(Ipnet_socket *sock)
{
Ipnet_socket_lookup_key key;
Ip_u16 eport;
Ip_u16 initial_port;
Ip_u16 portrange;
Ip_u16 portbase;
Ip_bool all_ports_tested = IP_FALSE;
portbase = (Ip_u16)ipnet->conf.sock.anon_port_min;
portrange = (Ip_u16)(ipnet->conf.sock.anon_port_max - portbase);
/*
* Select a port at random within the range of ephemeral ports.
*/
initial_port = (Ip_u16)(portbase + ipcom_rand() % (portrange + 1));
eport = initial_port;
ipcom_memset(&key, 0, sizeof(key));
key.domain = sock->ipcom.domain;
key.vr = sock->vr_index;
key.proto = sock->proto;
do
{
key.local_port = eport;
if (ipnet_sock_bound_port_get(&key) == IP_NULL)
/*
* This port is unused
*/
break;
if (eport++ >= portbase + portrange)
eport = portbase;
all_ports_tested = (eport == initial_port);
} while (!all_ports_tested);
return (Ip_u16)(all_ports_tested ? 0 : eport);
}
/*
*===========================================================================
* ipnet_pkt_get_maxlen
*===========================================================================
* Description: Count the sum of maxlen of packet, if it has fragment
* Parameters: pkt
* Returns: >=0 the sum of maxlen
*
*/
IP_GLOBAL int
ipnet_pkt_get_maxlen(Ipcom_pkt *pkt)
{
int sum_maxlen = 0;
if (pkt == IP_NULL)
return 0;
while (pkt != IP_NULL)
{
ip_assert(pkt->maxlen >= 0);
sum_maxlen += (pkt->maxlen);
pkt = pkt->next_part;
}
return sum_maxlen;
}
/*
*===========================================================================
* ipnet_queue_received_packet
*===========================================================================
* Description: Queues a packet on the passed socket. The start field of
* the packet should specify beginning of application data.
* Parameters: pkt - packet to queue.
* sock - socket to queue the packet on.
* Returns: 0 = success, <0 = error code
*
*/
IP_GLOBAL int
ipnet_queue_received_packet(Ipcom_pkt *pkt, Ipnet_socket *sock)
{
ip_assert(IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_ALLOC));
if (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_READ_CLOSED))
{
IPCOM_LOG1(DEBUG, "ipnet_queue_received_packet: socket %d closed", sock->ipcom.fd);
return IPNET_ERRNO(EPERM);
}
#ifdef IPCOM_FORWARDER_NAE
/*
* Must clear a number of flag since this packet might be reused
* for transmission. The tx-path will set the flags if needed.
*/
IP_BIT_CLR(pkt->flags, (IPCOM_PKT_FLAG_LOOPED_BACK
| IPCOM_PKT_FLAG_IPV4
| IPCOM_PKT_FLAG_IPV6
| IPCOM_PKT_FLAG_TUNNELED
| IPCOM_PKT_FLAG_HW_CHECKSUM
| IPCOM_PKT_FLAG_TL_CHECKSUM
| IPCOM_PKT_FLAG_VLAN_TAG
| IPCOM_PKT_FLAG_LINK_BROADCAST
| IPCOM_PKT_FLAG_LINK_MULTICAST
| IPCOM_PKT_FLAG_LINK_OTHER));
if (sock->nae_rx_action)
{
/*
* This socket has registered a callback that will take care
* of each received packet. Deliver the packet to that
* function and return.
*/
ipcom_nae_socket_atomic(sock);
ipnet_sock_rx_action(sock, pkt);
ipcom_nae_socket_ordered(sock);
return 0;
}
#endif
if (sock->bind_to_ifindex != 0)
{
switch (sock->ipcom.domain)
{
#if defined(IP_SO_USELOOPBACK) && (defined(IPNET_USE_ROUTESOCK) || defined(IPNET_USE_NETLINKSOCK))
case IP_AF_ROUTE:
case IP_AF_NETLINK:
if (sock->bind_to_ifindex == pkt->ifindex)
/* This socket has set IP_SO_USELOOPBACK */
return IPNET_ERRNO(EINVAL);
break;
#endif /* defined(IP_SO_USELOOPBACK) && (defined(IPNET_USE_ROUTESOCK) || defined(IPNET_USE_NETLINKSOCK)) */
default:
if (sock->bind_to_ifindex != pkt->ifindex)
{
/* This socket is bound to a interface (with IP_SO_BINDTODEVICE)
* that does not match this packet */
IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_WARNING,
1, 3, IPCOM_WV_NETDEVENT_WARNING, IPCOM_WV_NETD_RECV,
ipnet_queue_received_packet, IPCOM_WV_NETD_BADSOCK,
IPCOM_WV_IPNET_SOCK_MODULE, IPCOM_WV_NETD_SOCKET);
IPNET_STATS(pkt_input_bound_to_other_if++);
return IPNET_ERRNO(EINVAL);
}
break;
}
}
ipcom_spinlock_lock(&sock->rcv_lock);
/*
* Use pkt->maxlen instead of pkt->end-pkt->start for datagram
* packets to avoid DoS attack where huge amount of zero length
* UDP packets is sent. pkt->end-pkt->start==0 for UDP packets
* without payload, so those packets could used up all available
* memory regardless of the size of the socket receive buffer.
* Every socket is allowed to queue one single packet regardless
* of packet size and socket receive buffer size.
* This is done so packet that does not change the socket receive
* buffer size or change it to a very small size does not break.
*/
if (sock->rcv_head != IP_NULL)
{
#if defined(IPNET_USE_NETLINKSOCK)
/*
* If there's room in the last buffer to store this packet,
* copy it in.
*/
if (IP_UNLIKELY(sock->ipcom.domain == IP_AF_NETLINK))
{
int aligned_end = IP_NLMSG_ALIGN(sock->rcv_tail->end);
/* Store in tail in last buffer if possible */
if ((sock->rcv_tail->maxlen - aligned_end) >= (pkt->end - pkt->start))
{
ipcom_memcpy(&sock->rcv_tail->data[aligned_end], &pkt->data[pkt->start], pkt->end - pkt->start);
sock->rcv_tail->end += aligned_end - sock->rcv_tail->end;
sock->rcv_tail->end += pkt->end - pkt->start;
ipcom_pkt_free(pkt);
ipcom_spinlock_unlock(&sock->rcv_lock);
ipnet_sock_data_avail(sock, 0, IP_SHUT_RD);
return 0;
}
}
#endif /* defined(IPNET_USE_NETLINKSOCK)) */
if (sock->ipcom.rcv_bytes + ipnet_pkt_get_maxlen(pkt) > sock->recv_max_bytes)
{
ipcom_spinlock_unlock(&sock->rcv_lock);
/*
* Let the receiving process run if it has the same priority
* as the stack task
*/
ipcom_proc_yield();
ipcom_spinlock_lock(&sock->rcv_lock);
if (sock->ipcom.rcv_bytes + ipnet_pkt_get_maxlen(pkt) > sock->recv_max_bytes)
{
ipcom_spinlock_unlock(&sock->rcv_lock);
IPCOM_LOG4(DEBUG2,
"ipnet_queue_received_packet: socket %d receive buffer full (%d:%d), packet is %d",
sock->ipcom.fd,
sock->ipcom.rcv_bytes,
sock->recv_max_bytes,
pkt->maxlen);
IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_WARNING,
1, 4, IPCOM_WV_NETDEVENT_WARNING, IPCOM_WV_NETD_RECV,
ipnet_queue_received_packet, IPCOM_WV_NETD_FULLSOCK,
IPCOM_WV_IPNET_SOCK_MODULE, IPCOM_WV_NETD_SOCKET);
IPNET_STATS(pkt_queue_emsgsize++);
return IPNET_ERRNO(EMSGSIZE);
}
}
}
/* Add the packet to the socket receive queue */
sock->ipcom.rcv_bytes += ipnet_pkt_get_maxlen(pkt);
if (sock->rcv_head != IP_NULL)
sock->rcv_tail->next = pkt;
else
sock->rcv_head = pkt;
sock->rcv_tail = pkt;
pkt->next = IP_NULL;
ipcom_spinlock_unlock(&sock->rcv_lock);
IPCOM_PKT_TRACE(pkt, IPCOM_PKT_ID_SOCKRECVQ);
ipnet_sock_data_avail(sock, 0, IP_SHUT_RD);
#ifdef IPCOM_FORWARDER_NAE
if (sock->nae_rx_notifier)
{
/*
* This socket has registered a callback to get notification
* each time data is queued to this socket. The ownership of
* the packet is NOT transferred to the handler.
*/
ipcom_nae_socket_atomic(sock);
ipnet_sock_rx_notifier(sock, pkt);
ipcom_nae_socket_ordered(sock);
}
#endif
return 0;
}
/*
*===========================================================================
* ipnet_sock_data_avail
*===========================================================================
* Description: Is called each time data arrives to the socket so that
* ipcom_recvfrom() and ipcom_accept() can be called without
* blocking.
* Parameters: sock - The socket on which data arrived.
* so_errno: 0 = data, -1 = close else socket errno.
* how - Determines if processes/tasks sleeping on read and/or
* write should be woken.
* Returns:
*
*/
IP_GLOBAL void
ipnet_sock_data_avail(Ipnet_socket *sock, int so_errno, int how)
{
Ipcom_socket_eventcb event_cb = sock->ipcom.event_cb;
#ifdef IPSCTP
/* Set the flag to make SCTP socket readable */
if (sock->sctp_inst && (how == IP_SHUT_RD || how == IP_SHUT_RDWR))
{
ipcom_atomic_set(&sock->sctp_inst->wait_readable, 0);
}
#endif
if (so_errno != 0)
{
/* so_errno == -1 means a close event. */
if (so_errno != -1 && sock->ipcom.so_errno == 0)
{
IPCOM_LOG5(DEBUG, "errno '%s' set on socket %d, domain %d, type %d, proto %d",
ipcom_strerror(so_errno),
sock->ipcom.fd,
sock->ipcom.domain,
sock->ipcom.type,
sock->proto);
sock->ipcom.so_errno = so_errno;
ip_assert(sock->ipcom.so_errno > 0);
}
if (event_cb != IP_NULL
&& IP_BIT_ISFALSE(sock->flags, IPNET_SOCKET_FLAG_CLOSED))
{
event_cb(&sock->ipcom,
IP_NULL,
sock->ipcom.so_errno ? IP_SOEVENT_CB_ERRNO : IP_SOEVENT_CB_CLOSE);
#ifndef IP_PORT_LKM
/* There must be no more events after the close event. */
if (sock->ipcom.so_errno == 0)
{
sock->ipcom.event_cb = IP_NULL;
event_cb = IP_NULL;
}
#endif
}
}
if (how == IP_SHUT_RD || how == IP_SHUT_RDWR || sock->ipcom.so_errno)
{
if (event_cb != IP_NULL)
event_cb(&sock->ipcom, IP_NULL, IP_SOEVENT_CB_SELREAD);
ipnet_signal_ready_sock(sock,
IPNET_SOCK_POLL_READ,
ipnet_is_sock_readable);
}
if (how == IP_SHUT_WR || how == IP_SHUT_RDWR || sock->ipcom.so_errno)
{
if (event_cb != IP_NULL)
event_cb(&sock->ipcom, IP_NULL, IP_SOEVENT_CB_SELWRITE);
ipnet_signal_ready_sock(sock,
IPNET_SOCK_POLL_WRITE,
ipnet_is_sock_writable);
}
#ifdef IPTCP
if (how == IP_SHUT_EX || sock->ipcom.so_errno)
{
if (event_cb != IP_NULL)
event_cb(&sock->ipcom, IP_NULL, IP_SOEVENT_CB_SELEXCEPT);
ipnet_signal_ready_sock(sock,
IPNET_SOCK_POLL_EXCEPT,
ipnet_is_sock_exceptional);
}
#endif /* IPTCP */
#if defined(IP_PORT_VXWORKS) && defined(IPCOM_USE_SOCK_COMPAT)
#ifdef IPNET_SO_WAKEUP_HOOK
ipcom_windnet_socklib_sowakeupHook(sock, how);
#endif
#endif
}
/*
*===========================================================================
* ipnet_sock_free
*===========================================================================
* Description: Frees all memory allocated by a socket.
* Parameters: sock - The socket to free.
* clear_sockets_arr - IP_TRUE = this socket has not been
* removed from ipnet->sockets[] yet.
* Returns:
*/
IP_GLOBAL void
ipnet_sock_free(Ipnet_socket *sock, Ip_bool clear_sockets_arr)
{
Ipcom_pkt *pkt;
Ipcom_socket_eventcb event_cb = sock->ipcom.event_cb;
Ipnet_sock_ops *ops;
if (clear_sockets_arr)
{
IPNET_DATA_LOCK(ipnet->sockets_lock);
ipnet->sockets[sock->ipcom.fd] = IP_NULL;
IPNET_DATA_UNLOCK(ipnet->sockets_lock);
}
else
ip_assert(ipnet->sockets[sock->ipcom.fd] == IP_NULL);
ops = ipnet_sock_get_ops(sock);
if (ops->destroy != IP_NULL)
ops->destroy(sock);
if (event_cb != IP_NULL)
{
event_cb(&sock->ipcom, IP_NULL, IP_SOEVENT_CB_CLOSE);
sock->ipcom.event_cb = IP_NULL;
}
#if defined(IP_PORT_VXWORKS) && defined(IPCOM_USE_SOCK_COMPAT)
ipcom_windnet_socklib_socket_free(sock);
#endif
/* Free any pending packets */
while (sock->rcv_head != IP_NULL)
{
pkt = sock->rcv_head;
sock->rcv_head = pkt->next;
ipcom_pkt_free(pkt);
}
#if defined(IPCOM_USE_INET) && defined(IPCOM_USE_INET6)
if (sock->ipcom.domain == IP_AF_INET6)
/* Free memory that was used for IPv6 mapped IPv4 addresses */
ipcom_free(sock->ip4);
#endif
#ifdef IPCOM_FORWARDER_NAE
ipcom_shm_free(sock->nae_rx_action);
ipcom_shm_free(sock->nae_rx_notifier);
ipcom_shm_free(sock->nae_rx_child_action);
ipcom_shm_free(sock);
#else
ip_assert(sock->addr_entry == IP_NULL);
ip_assert(sock->addr_next == IP_NULL);
ip_assert(sock->addr_prev == IP_NULL);
ipcom_spinlock_delete(&sock->rcv_lock);
ipcom_free(sock->rcv_timeout);
#ifdef IPNET_USE_SO_CONNTIMEO
ipcom_free(sock->conn_timeout);
#endif
ipcom_slab_free(ipnet->slabs.socket, sock);
#endif
}
/*
*===========================================================================
* ipnet_do_close
*===========================================================================
* Description: Closes the socket and frees all resouces held by it.
* Parameters: sock - The socket to close.
* Returns: 0 = success, <0 error code,
*/
IP_GLOBAL int
ipnet_do_close(Ipnet_socket *sock)
{
int ret = 0;
int how;
Ipnet_sock_ops *ops;
ip_assert(sock != IP_NULL);
ip_assert(ipnet->sockets[sock->ipcom.fd] == sock);
switch (sock->flags & (IPNET_SOCKET_FLAG_READ_CLOSED | IPNET_SOCKET_FLAG_WRITE_CLOSED))
{
case 0:
how = IP_SHUT_RDWR;
break;
case IPNET_SOCKET_FLAG_READ_CLOSED:
how = IP_SHUT_WR;
break;
case IPNET_SOCKET_FLAG_WRITE_CLOSED:
how = IP_SHUT_RD;
break;
default:
how = -1;
break;
}
if (how >= 0)
ret = ipnet_sys_shutdown(sock->ipcom.fd, how, IP_FALSE);
ops = ipnet_sock_get_ops(sock);
if (ops->close != IP_NULL)
ops->close(sock);
(void) ipnet_sock_drop_all_multicast_memberships(sock,
IP_NULL,
IP_TRUE,
IP_TRUE);
IPNET_DATA_LOCK(ipnet->sockets_lock);
ip_assert(ipnet->sockets[sock->ipcom.fd] == sock);
if (ipcom_atomic_sub_and_return(&sock->ref_count, 1) > 0)
IPNET_DATA_UNLOCK(ipnet->sockets_lock);
else
{
/* This was the last reference held on this socket */
if (ipcom_atomic_get(&sock->snd_bytes) == 0)
{
/* This was the last reference to the socket and there is
no packets waiting for transmission in this socket. */
ipnet->sockets[sock->ipcom.fd] = IP_NULL;
IPNET_DATA_UNLOCK(ipnet->sockets_lock);
ipnet_sock_free(sock, IP_FALSE);
}
else
{
IPNET_DATA_UNLOCK(ipnet->sockets_lock);
if (sock->ipcom.type == IP_SOCK_RAW
|| sock->ipcom.type == IP_SOCK_DGRAM)
/* There is no session to tear-down for these socket
types, unbind them right now. Socket types that
keep some kind of session has to unbind them self
when appropriate. */
ipnet_sock_unbind(sock);
}
}
return ret;
}
/*
*===========================================================================
* ipnet_sock_drop_all_multicast_memberships
*===========================================================================
* Description: Drops all multicast memberships this socket has.
* Parameters: sock - a socket.
* netif - IP_NULL to dropp all multicast memberships or the
* interface for which all multicast memberships should be dropped.
* drop_ip4 - IP_TRUE if all IPv4 multicast groups should be dropped.
* drop_ip6 - IP_TRUE if all IPv6 multicast groups should be dropped.
* Returns: Number of multicast memberships that were dropped.
*/
IP_GLOBAL int
ipnet_sock_drop_all_multicast_memberships(Ipnet_socket *sock,
Ipnet_netif *netif,
Ip_bool drop_ip4,
Ip_bool drop_ip6)
{
int drop_count = 0;
int fd = sock->ipcom.fd;
#if defined(IPCOM_USE_INET)
IPCOM_UNUSED_ARG(drop_ip6);
if (drop_ip4 && sock->ipcom.domain == IP_AF_INET)
{
Ipnet_ip4_sock_mcast_data *mcd;
struct Ip_in_addr maddr;
while (ipnet->sockets[fd] != IP_NULL)
{
mcd = sock->ip4->multicast_addr_list;
while (mcd != IP_NULL && netif != IP_NULL && mcd->netif != netif)
mcd = mcd->next;
if (mcd == IP_NULL)
break;
drop_count++;
/* Must be copied since the 'mcd' structure will be freed */
maddr = mcd->maddr;
(void)ipnet_sock_ip4_mcast_delete_data(sock,
mcd->netif,
&maddr);
}
}
#endif /* IPCOM_USE_INET */
#if defined(IPCOM_USE_INET6)
IPCOM_UNUSED_ARG(drop_ip4);
if (drop_ip6 && sock->ipcom.domain == IP_AF_INET6)
{
Ipnet_ip6_sock_mcast_data *mcd;
struct Ip_in6_addr maddr;
while (ipnet->sockets[fd] != IP_NULL)
{
mcd = sock->ip6->multicast_addr_list;
while (mcd != IP_NULL && netif != IP_NULL && mcd->netif != netif)
mcd = mcd->next;
if (mcd == IP_NULL)
break;
drop_count++;
/* Must be copied since the 'mcd' structure will be freed */
maddr = mcd->maddr;
(void)ipnet_sock_ip6_mcast_delete_data(sock,
mcd->netif,
&maddr);
}
}
#endif /* IPCOM_USE_INET6 */
return drop_count;
}
/*
*===========================================================================
* ipnet_sock_get_route
*===========================================================================
* Description: Gets the route that would be used when sending on this socket.
* Parameters: sock - The socket to use when sending.
* Returns: IP_NULL or the route that would be used.
*
*/
IP_GLOBAL Ipnet_route_entry *
ipnet_sock_get_route(Ipnet_socket *sock)
{
Ipnet_route_entry *rt;
union Ip_sockaddr_union name;
union Ip_sockaddr_union peer;
Ip_socklen_t name_len = sizeof(name);
Ip_socklen_t peer_len = sizeof(peer);
Ipnet_flow_spec flow_spec;
if (ipnet_sys_getname(sock->ipcom.fd, &name.sa, &name_len, IP_TRUE) < 0
|| ipnet_sys_getname(sock->ipcom.fd, &peer.sa, &peer_len, IP_FALSE) < 0)
return IP_NULL;
#ifdef IPCOM_USE_INET
if (sock->ipcom.domain == IP_AF_INET)
ipnet_flow_spec_from_info(&flow_spec, sock->vr_index, &name.sin.sin_addr, &peer.sin.sin_addr, 0, sock->ip4->type_of_service, IP_NULL, IP_NULL, ipnet_ip4_flow_spec_from_info);
#endif
#ifdef IPCOM_USE_INET6
if (sock->ipcom.domain == IP_AF_INET6)
ipnet_flow_spec_from_info(&flow_spec, sock->vr_index, &name.sin6.sin6_addr, &peer.sin6.sin6_addr, sock->ip6->iscope_id, sock->ip6->tclass, IP_NULL, IP_NULL, ipnet_ip6_flow_spec_from_info);
#endif
if (ipnet_route_lookup_ecmp(sock->ipcom.domain,
sock->vr_index,
IPNET_RTL_FLAG_DONTCLONE,
&flow_spec.to,
0,
0,
IP_NULL,
&flow_spec,
&rt) < 0)
/* No route to the peer */
return IP_NULL;
return rt;
}
/*
*===========================================================================
* ipnet_sock_get_ops_handle
*===========================================================================
* Description: Find the socket operation structure by specified domain+type+
* protocol.
* Parameters: sa_family - the socket domain type.
* type - the socket type.
* protocol - the protocol type.
* no_domain - if != IP_NULL, will indicate if the
* requested domain was found or not.
* no_type - if != IP_NULL, will indicate if the
* requested socket type was found or not.
* Returns: NOT NULL = the result socket operation structure,
* NULL = no match.
*
*/
IP_GLOBAL int
ipnet_sock_get_ops_handle(int domain,
int type,
int protocol,
Ip_bool *no_domain,
Ip_bool *no_type)
{
int h;
if (no_domain)
*no_domain = IP_TRUE;
if (no_type)
*no_type = IP_TRUE;
for (h = 0; h < ipnet_sock_ops_len; h++)
{
Ipnet_sock_ops *ops = ipnet_sock_ops[h];
if (domain == ops->domain)
{
if (no_domain)
*no_domain = IP_FALSE;
if (type == ops->type)
{
if (no_type)
*no_type = IP_FALSE;
if (protocol == ops->proto)
return h;
}
}
}
return -1;
}
/*
****************************************************************************
* 11 HANDLER FOR PUBLIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipnet_sys_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.
* kernel_context - IP_TRUE if the socket is create by
* the stack kernel, IP_FALSE if it is created in
* user context.
* Returns: 0 >= The new socket descriptor.
* 0 < error, error code can be read from ipcom_errno.
*
*/
IP_GLOBAL Ip_fd
ipnet_sys_socket(int domain, int type, int protocol, Ip_bool user_context)
{
Ipnet_socket *sock = IP_NULL;
Ipnet_sock_ops *ops;
int sock_ops_handle;
Ip_bool no_domain;
Ip_bool no_type;
int i;
int ret = IPNET_ERRNO(EMFILE);
int send_max_bytes;
int recv_max_bytes;
sock_ops_handle = ipnet_sock_get_ops_handle(domain,
type,
protocol,
IP_NULL,
IP_NULL);
if (sock_ops_handle < 0)
{
/*
* Try wildcard protocol handler
*/
sock_ops_handle = ipnet_sock_get_ops_handle(domain,
type,
-1,
&no_domain,
&no_type);
}
if (sock_ops_handle < 0)
{
if (no_domain)
return IPNET_ERRNO(EAFNOSUPPORT);
if (no_type)
return IPNET_ERRNO(ESOCKTNOSUPPORT);
return IPNET_ERRNO(EPROTONOSUPPORT);
}
/*
* Find an available socket descriptor slot.
*/
for (i = 1; i <= ipnet_conf_max_sockets - IPNET_SOCKET_STARTFD; i++)
{
Ip_fd fd;
Ip_fd last_fd;
last_fd = ipnet->last_fd;
fd = (last_fd - IPNET_SOCKET_STARTFD + i)
% (ipnet_conf_max_sockets - IPNET_SOCKET_STARTFD) + IPNET_SOCKET_STARTFD;
if (ipnet->sockets[fd] != IP_NULL)
continue;
ret = IPNET_ERRNO(ENOMEM);
#ifdef IPCOM_FORWARDER_NAE
sock = ipcom_shm_calloc(1, ipnet->largest_socket_size);
#else
sock = ipcom_slab_alloc(ipnet->slabs.socket);
#endif
if (sock == IP_NULL)
goto errout;
#ifdef IPCOM_FORWARDER_NAE
#ifdef IPCOM_ATOMIC_TAG_VIA_SPINLOCK
ipcom_ipc_spinlock_init(&sock->nae_lock);
#else
sock->atomic_tag = fd;
#endif /* IPCOM_ATOMIC_TAG_VIA_SPINLOCK */
#else
/*
* Blocking operations is not supported in a NAE
*/
if (ipcom_spinlock_create(&sock->rcv_lock) != IPCOM_SUCCESS)
goto errout;
#endif
/*
* Initialize the socket
*/
ipcom_atomic_set(&sock->ref_count, 1);
sock->ipcom.fd = fd;
sock->ipcom.domain = (Ip_u16) domain;
sock->proto = (Ip_u16) protocol;
sock->ipcom.type = (Ip_u16) type;
sock->multi_hop_limit = 1;
sock->ipcom.pid = ipnet_get_effective_pid();
send_max_bytes = IP_MAX(IPNET_SOCKBUF_MULTIPLIER * ipnet->conf.sock.default_send_buf_size, \
IPNET_MIN_SNDBUF_SIZE);
recv_max_bytes = IP_MAX(IPNET_SOCKBUF_MULTIPLIER * ipnet->conf.sock.default_recv_buf_size, \
IPNET_MIN_RCVBUF_SIZE);
sock->send_max_bytes = send_max_bytes;
sock->recv_max_bytes = recv_max_bytes;
IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_LOOP_MULTICAST);
sock->vr_index = ipnet_get_effective_vr();
sock->pkt_dtor = ipnet_sock_pkt_dtor;
sock->snd_route_cache_id = ipnet->route_cache_id - 1;
sock->ops_handle = sock_ops_handle;
ops = ipnet_sock_get_ops(sock);
sock->max_hdrspace = (Ip_u16) ops->hdr_space;
/*
* Initialize the lists of user tasks that are waiting for
* the socket to be readable, writable and/or exceptional
*/
ipcom_list_init(&sock->poll_list_head);
#if defined(IP_PORT_VXWORKS) && defined(IPCOM_USE_SOCK_COMPAT)
ret = ipcom_windnet_socklib_socket_init(sock);
if (ret < 0)
goto errout;
#endif
if (ops->init != IP_NULL)
{
ret = ops->init(sock, user_context);
if (ret < 0)
goto errout;
}
ipnet->last_fd = fd;
ipnet->sockets[fd] = sock;
return fd;
}
/* No free socket slot found. */
errout:
if (sock != IP_NULL)
{
#if defined(IP_PORT_VXWORKS) && defined(IPCOM_USE_SOCK_COMPAT)
ipcom_windnet_socklib_socket_free(sock);
#endif
#ifdef IPCOM_FORWARDER_NAE
ipcom_shm_free(sock);
#else
ipcom_slab_free(ipnet->slabs.socket, sock);
#endif
}
return ret;
}
/*
*===========================================================================
* ipnet_sys_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.
* from_user_signal - from user shutdown flag.
* Returns: 0 = success, -1 = error
*/
IP_GLOBAL int
ipnet_sys_shutdown(Ip_fd fd, int how, Ip_bool from_user_signal)
{
Ipnet_socket *sock;
Ipnet_sock_ops *ops;
int ret;
int ret2;
int so_errno = 0;
IPNET_FD_TO_SOCKET(fd, sock);
/* if SCTP UDP-like socket, just return */
#ifdef IPSCTP
if ((IP_TRUE == from_user_signal)
&& (IP_IPPROTO_SCTP == sock->proto)
&& (IP_NULL != sock->sctp_inst) /* must not NULL */
&& (ipsctp_style(sock, UDP)))
return IPNET_ERRNO(EOPNOTSUPP);
#endif
ipnet_sock_make_next_sendmsg_synchronous(sock);
/*
* Make sure that read and/or write end close is only done once
*/
switch (how)
{
case IP_SHUT_RDWR:
ret = ipnet_sys_shutdown(fd, IP_SHUT_RD, from_user_signal);
ret2 = ipnet_sys_shutdown(fd, IP_SHUT_WR, from_user_signal);
return ret == 0 ? ret2 : ret;
case IP_SHUT_RD:
if (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_READ_CLOSED))
return IPNET_ERRNO(ESHUTDOWN);
IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_READ_CLOSED);
break;
case IP_SHUT_WR:
if (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_WRITE_CLOSED))
return IPNET_ERRNO(ESHUTDOWN);
IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_WRITE_CLOSED);
if (ipnet_sock_has_waiting_writers(sock))
so_errno = IP_ERRNO_EPIPE;
if (sock->snd_head != IP_NULL)
{
/*
* The octest stored in sock->snd_bytes has been allocated
* in the send buffer. Undo that allocation.
*/
ipcom_atomic_sub(&sock->snd_bytes,
sock->snd_head->end - sock->snd_head->start);
ipcom_pkt_free(sock->snd_head);
sock->snd_head = IP_NULL;
}
if (sock->snd_to != IP_NULL)
{
ipcom_free(sock->snd_to);
sock->snd_to = IP_NULL;
}
break;
default:
return IPNET_ERRNO(EINVAL);
}
ops = ipnet_sock_get_ops(sock);
if (ops->shutdown != IP_NULL)
ops->shutdown(sock, how);
ipnet_sock_data_avail(sock, so_errno, how);
return 0;
}
/*
*===========================================================================
* ipnet_sys_savesignal
*===========================================================================
* Description: Save close signal into a socket structure.
* Parameters: fd - The socket to close.
* signal - close signal from user
* Returns: 0= success, <0 = error code
*
*/
IP_GLOBAL int ipnet_sys_savesignal(Ip_fd fd,void* signal)
{
Ipnet_socket *sock;
ip_assert2(fd >= IPNET_SOCKET_STARTFD);
ip_assert2(fd < ipnet_conf_max_sockets);
ip_assert2(ipnet->sockets[fd] != IP_NULL);
IPNET_FD_TO_OPEN_SOCKET(fd, sock);
sock->signal = signal;
return 0;
}
/*
*===========================================================================
* ipnet_sys_checktcp
*===========================================================================
* Description: Check a socket for TCP.
* Parameters: fd - The socket to check.
* Returns: 1, If socket belongs to TCP; <= 0, if socket does not belongs to TCP
*/
IP_GLOBAL int ipnet_sys_checktcp(Ip_fd fd)
{
Ipnet_socket *sock;
Ipnet_sock_ops *ops;
ip_assert2(fd >= IPNET_SOCKET_STARTFD);
ip_assert2(fd < ipnet_conf_max_sockets);
ip_assert2(ipnet->sockets[fd] != IP_NULL);
IPNET_FD_TO_OPEN_SOCKET(fd, sock);
ops = ipnet_sock_get_ops(sock);
if ((ops->type == IP_SOCK_STREAM)
&& (ops->proto != IP_IPPROTO_SCTP)
&& ((sock->tcb != IP_NULL)
&& (sock->tcb->sec_to_linger != 0)
&& (sock->tcb->sec_to_linger != IPCOM_ADDR_INFINITE)))
return 1;
return 0;
}
/*
*===========================================================================
* ipcom_sys_socketclose
*===========================================================================
* Description: Closes a socket.
* Parameters: fd - The socket to close.
* Returns: 0= success, <0 = error code
*
*/
IP_GLOBAL int
ipnet_sys_socketclose(Ip_fd fd)
{
Ipnet_socket *sock;
ip_assert2(fd >= IPNET_SOCKET_STARTFD);
ip_assert2(fd < ipnet_conf_max_sockets);
ip_assert2(ipnet->sockets[fd] != IP_NULL);
IPNET_FD_TO_OPEN_SOCKET(fd, sock);
#ifdef IPNET_USE_ROUTER_ALERT
(void)ipnet_sock_leave_router_alert_chain(sock);
#endif
IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_CLOSED);
return ipnet_do_close(sock);
}
/*
*===========================================================================
* ipnet_sys_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, <0 = error code.
*
*/
IP_GLOBAL int
ipnet_sys_listen(Ip_fd fd, int backlog)
{
Ipnet_socket *sock;
Ipnet_sock_ops *ops;
ip_assert2(fd >= IPNET_SOCKET_STARTFD);
ip_assert2(fd < ipnet_conf_max_sockets);
ip_assert2(ipnet->sockets[fd] != IP_NULL);
IPNET_FD_TO_OPEN_SOCKET(fd, sock);
ops = ipnet_sock_get_ops(sock);
if (ops->listen == IP_NULL)
return IPNET_ERRNO(EOPNOTSUPP);
return ops->listen(sock, backlog);
}
/*
*===========================================================================
* ipnet_sys_accept
*===========================================================================
* Description: Accepts an incoming connection attempt on a socket.
* Parameters: fd - A socket that is in listen state (ipcom_listen()) has
* been called.
* name - Will hold the address of the peer (set to IP_NULL if
* that information is not wanted).
* addrlen - Must be initialized to the length of the 'name'
* buffer, will hold the actual 'name' length upon return.
* Set to IP_NULL if 'name' is IP_NULL.
* Returns: 0>= the socket to use for communication (success),
* <0 = error.
*
*/
IP_GLOBAL int
ipnet_sys_accept(Ip_fd fd, struct Ip_sockaddr *name, Ip_socklen_t *namelen)
{
Ipnet_socket *sock;
Ipnet_sock_ops *ops;
ip_assert2(fd >= IPNET_SOCKET_STARTFD);
ip_assert2(fd < ipnet_conf_max_sockets);
ip_assert2(ipnet->sockets[fd] != IP_NULL);
IPNET_FD_TO_OPEN_SOCKET(fd, sock);
ops = ipnet_sock_get_ops(sock);
if (ops->accept == IP_NULL)
return IPNET_ERRNO(EOPNOTSUPP);
return ops->accept(sock, name, namelen);
}
/*
*===========================================================================
* ipnet_sys_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, <0 = error code
*
*/
IP_GLOBAL int
ipnet_sys_bind(Ip_fd fd, IP_CONST struct Ip_sockaddr *name, Ip_socklen_t namelen)
{
Ipnet_socket *sock;
Ipnet_sock_ops *ops;
ip_assert2(fd >= IPNET_SOCKET_STARTFD);
ip_assert2(fd < ipnet_conf_max_sockets);
ip_assert2(ipnet->sockets[fd] != IP_NULL);
ip_assert2(name != IP_NULL);
ip_assert2(namelen > 0);
IPNET_FD_TO_OPEN_SOCKET(fd, sock);
ipnet_sock_make_next_sendmsg_synchronous(sock);
#if defined(IPNET_COMPAT_OLDSOCK) && defined(IPCOM_USE_SA_LEN)
if (name->sa_family == 0 && name->sa_len < IP_AF_MAX)
((struct Ip_sockaddr *)name)->sa_family = name->sa_len;
((struct Ip_sockaddr *)name)->sa_len = (Ip_u8) namelen;
#endif
#ifdef IPCOM_USE_SA_LEN
ip_assert(name->sa_len > 0);
#endif
ops = ipnet_sock_get_ops(sock);
if (ops->bind == IP_NULL)
{
/* This domain/type/proto does not support bind */
IPCOM_LOG1(WARNING, "socket %d does not support the bind operation", sock->ipcom.fd);
return IPNET_ERRNO(EOPNOTSUPP);
}
if (name->sa_family != sock->ipcom.domain)
{
/* Wrong domain in socket name */
IPCOM_LOG3(WARNING, "domain miss-match on socket %d in bind, socket domain %d, name domain %d",
sock->ipcom.fd, sock->ipcom.domain, name->sa_family);
return IPNET_ERRNO(EINVAL);
}
if (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_BOUND))
{
/* Socket already bound. */
IPCOM_LOG1(NOTICE, "socket %d already bound", sock->ipcom.fd);
return IPNET_ERRNO(EINVAL);
}
return ops->bind(sock, name, namelen, IP_TRUE);
}
/*
*===========================================================================
* ipnet_sys_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, <0 = error code.
*
*/
IP_GLOBAL int
ipnet_sys_connect(Ip_fd fd, IP_CONST struct Ip_sockaddr *to, Ip_socklen_t tolen)
{
Ipnet_socket *sock = IP_NULL;
Ipnet_sock_ops *ops;
int ret;
ip_assert2(fd >= IPNET_SOCKET_STARTFD);
ip_assert2(fd < ipnet_conf_max_sockets);
ip_assert2(ipnet->sockets[fd] != IP_NULL);
if (to == IP_NULL)
return IPNET_ERRNO(EINVAL);
IPNET_FD_TO_OPEN_SOCKET(fd, sock);
ipnet_sock_make_next_sendmsg_synchronous(sock);
if (sock->ipcom.so_errno != 0)
return -sock->ipcom.so_errno;
ops = ipnet_sock_get_ops(sock);
if (ops->connect == IP_NULL)
{
/* This domain does not support connect */
IPCOM_LOG1(NOTICE, "socket %d does not support the connect call", sock->ipcom.fd);
return IPNET_ERRNO(EOPNOTSUPP);
}
#if defined(IPNET_COMPAT_OLDSOCK) && defined(IPCOM_USE_SA_LEN)
if (to->sa_family == 0 && to->sa_len < IP_AF_MAX)
((struct Ip_sockaddr *)to)->sa_family = to->sa_len;
((struct Ip_sockaddr *)to)->sa_len = (Ip_u8) tolen;
#endif /* IPNET_COMPAT_OLDSOCK */
if (sock->ipcom.type == IP_SOCK_STREAM
&& IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_CONNECTED))
return IPNET_ERRNO(EISCONN);
#ifdef IPSCTP /* SCTP UDP-style socket allow multi-connect */
if ((IP_IPPROTO_SCTP != sock->proto)
&& (IP_SOCK_SEQPACKET != sock->ipcom.type))
#endif
IP_BIT_CLR(sock->flags, IPNET_SOCKET_FLAG_CONNECTED);
if (to->sa_family != sock->ipcom.domain && to->sa_family != IP_AF_UNSPEC)
return IPNET_ERRNO(EAFNOSUPPORT);
ret = ops->connect(sock, to, tolen);
if (ret != 0
#ifdef IPSCTP /* SCTP UDP-style socket allow multi-connect */
&& ((IP_IPPROTO_SCTP != sock->proto)
&& (IP_SOCK_SEQPACKET != sock->ipcom.type))
#endif
&& ret != -IP_ERRNO_EISCONN
&& ret != -IP_ERRNO_EWOULDBLOCK
&& ret != -IP_ERRNO_EINPROGRESS)
IP_BIT_CLR(sock->flags, IPNET_SOCKET_FLAG_CONNECTED);
return ret;
}
/*
*===========================================================================
* ipcom_pkt_hdrspace
*===========================================================================
* Description: Returns the minimum number of octets that has to be reserved
* before the transport layer payload in the packet when using
* the zero copy API.
* Parameters: fd - The socket descriptor to use when sending this packet.
* Returns: >=0 = the number of octets that must be reserved for
* transport, network and link header.
* =IP_SOCKERR = error, get the error from ipcom_errno.
*
*/
IP_GLOBAL int
ipcom_pkt_hdrspace(Ip_fd fd)
{
Ipnet_socket *sock;
IPNET_FD_TO_SOCKET(fd, sock);
if (sock->ipcom.type == IP_SOCK_STREAM)
return IPNET_ERRNO(EPROTONOSUPPORT);
return ipcom_conf_max_link_hdr_size + sock->max_hdrspace;
}
/*
*===========================================================================
* ipnet_sys_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.
* pkt - data to send.
* Returns: >=0 = number of bytes sent
<0 = error code.
*
*/
IP_GLOBAL Ip_ssize_t
ipnet_sys_sendmsg(Ip_fd fd, IP_CONST struct Ip_msghdr *msg, Ipcom_pkt *pkt)
{
Ipnet_socket *sock = IP_NULL;
Ipnet_sock_ops *ops;
int ret;
ip_assert2(fd >= IPNET_SOCKET_STARTFD);
ip_assert2(fd < ipnet_conf_max_sockets);
ip_assert2(ipnet->sockets[fd] != IP_NULL);
ip_assert2(msg != IP_NULL);
IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_INFO,
1, 7, IPCOM_WV_NETDEVENT_INFO, IPCOM_WV_NETD_SEND,
ipcom_sendmsg, IPCOM_WV_NETD_INFO_SEND,
IPCOM_WV_IPNET_SOCK_MODULE, IPCOM_WV_NETD_SOCKET);
IPNET_STATS(sock_sendmsg++);
if (IP_UNLIKELY(fd < IPNET_SOCKET_STARTFD)
|| IP_UNLIKELY(fd >= ipnet_conf_max_sockets)
|| IP_UNLIKELY(IP_NULL == (sock = ipnet->sockets[fd]))
|| (IP_UNLIKELY(IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_CLOSED))))
{
IPCOM_WV_MARKER_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_WARNING,
1, 1, IPCOM_WV_NETDEVENT_WARNING,
ipcom_socket, IPCOM_WV_NETD_NOSOCK,
IPCOM_WV_IPNET_SOCK_MODULE, IPCOM_WV_NETD_SOCKET);
IPNET_STATS(sock_ebadf++);
ret = IPNET_ERRNO(EBADF);
goto errout;
}
ret = -sock->ipcom.so_errno;
if (IP_UNLIKELY(ret))
{
Iptcp_tcb *tcb = sock->tcb;
/*
* Error condition has been set on socket between the point
* where this packet was sent and now. This packet should just
* be discarded.
*/
if (tcb)
{
iptcp_partial_lock(tcb->send.partial_lock);
tcb->send.partial = IP_NULL;
iptcp_partial_unlock(tcb->send.partial_lock);
}
goto errout;
}
if (IP_UNLIKELY(IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_WRITE_CLOSED)))
{
ret = IPNET_ERRNO(ESHUTDOWN);
goto errout;
}
#if defined(IPNET_COMPAT_OLDSOCK) && defined(IPCOM_USE_SA_LEN)
if (msg->msg_name != IP_NULL)
{
struct Ip_sockaddr *to = (struct Ip_sockaddr *) msg->msg_name;
if (to->sa_family == 0 && to->sa_len < IP_AF_MAX)
((struct Ip_sockaddr *)to)->sa_family = to->sa_len;
((struct Ip_sockaddr *)to)->sa_len = (Ip_u8) msg->msg_namelen;
}
#endif /* IPNET_COMPAT_OLDSOCK */
ip_assert(msg->msg_name == IP_NULL || msg->msg_namelen > 0);
ops = ipnet_sock_get_ops(sock);
if (IP_UNLIKELY(IP_BIT_ISFALSE(sock->flags, IPNET_SOCKET_FLAG_BOUND))
&& ops->bind != IP_NULL
&& ops->domain != IP_AF_PACKET)
{
union Ip_sockaddr_union name;
/*
* Do an implicit bind
*/
ipcom_memset(&name, 0, sizeof(name));
name.sa.sa_family = (Ip_sa_family_t)sock->ipcom.domain;
ret = ops->bind(sock, &name.sa, sizeof(name), IP_TRUE);
if (ret < 0)
goto errout;
}
ip_assert(ops->send != IP_NULL);
#ifdef IPNET_USE_RFC2890
if (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_GRE_KEY))
ipcom_pkt_set_info(pkt,
IPNET_PKT_INFO_GRE_KEY_OUTPUT,
sizeof(sock->gre_key),
&sock->gre_key);
#endif
#ifdef IPCOM_FORWARDER_NAE
if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_READONLY))
{
Ipcom_pkt *hdr = ipcom_pkt_malloc(sock->max_hdrspace, 0);
/*
* Write all headers in a separate buffer.
*/
IPCOM_PKT_ADD_REF(pkt);
if (IP_UNLIKELY(hdr == IP_NULL))
{
ret = IP_ERRNO_ENOMEM;
goto errout;
}
hdr->start = hdr->end = hdr->maxlen;
hdr->next_part = pkt;
if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_TL_CHECKSUM))
{
IP_BIT_SET(hdr->flags, IPCOM_PKT_FLAG_TL_CHECKSUM);
hdr->chk = pkt->chk;
}
pkt = hdr;
}
if (sock->ipcom.type == IP_SOCK_STREAM || sock->ipcom.type == IP_SOCK_SEQPACKET)
ipcom_nae_socket_atomic(sock);
#endif
ret = ops->send(sock, msg, pkt);
sock->snd_route_cache_id = ipnet->route_cache_id;
if (IP_UNLIKELY(ret < 0))
ipnet_sock_make_next_sendmsg_synchronous(sock);
return ret;
errout:
#ifdef IPTCP
if (sock != IP_NULL && sock->tcb != IP_NULL)
{
Iptcp_tcb *tcb = sock->tcb;
Ipcom_pkt *tail_pkt = pkt;
while (tail_pkt->next != IP_NULL)
tail_pkt = tail_pkt->next;
iptcp_partial_lock(tcb->send.partial_lock);
if (tail_pkt == tcb->send.partial)
tcb->send.partial = IP_NULL;
iptcp_partial_unlock(tcb->send.partial_lock);
}
#endif /* IPTCP */
while(pkt!=IP_NULL)
{
Ipcom_pkt *pkt1 = pkt;
pkt = pkt1->next;
#ifdef IPSCTP
if (sock->proto == IP_IPPROTO_SCTP)
ipcom_atomic_sub(&sock->snd_bytes,
pkt1->end - pkt1->start - sizeof(Ipsctp_data_chunk_hdr));
#endif
ipcom_pkt_free(pkt1);
}
return ret;
}
/*
*===========================================================================
* ipnet_sys_getname
*===========================================================================
* Description: Returns the socket address of the local side (if bound) or
* the peer side (if connected).
* Parameters: fd - Socket descriptor.
* name - Will hold the socket address if the call succeeds.
* namelenp - The length of the 'name' buffer, will be updated
* to hold the actual size of 'name'.
* local - Set to IP_TRUE if the name of local size should be
* returned and IP_FALSE if the peer socket name should be returned.
* Returns: 0 = success, <0 negative error code.
*
*/
IP_GLOBAL int
ipnet_sys_getname(Ip_fd fd,
struct Ip_sockaddr *name,
Ip_socklen_t *namelenp,
Ip_bool local)
{
Ipnet_socket *sock;
Ipnet_sock_ops *ops;
ip_assert(name != IP_NULL);
ip_assert(namelenp != IP_NULL);
IPNET_FD_TO_OPEN_SOCKET(fd, sock);
ops = ipnet_sock_get_ops(sock);
if (ops->getname == IP_NULL)
return IPNET_ERRNO(EOPNOTSUPP);
return ops->getname(sock, name, namelenp, local);
}
/*
*===========================================================================
* ipnet_sys_get_ancillary_data
*===========================================================================
* Description: Gets ancillary data that has is appropriate for the
* specified socket.
* Parameters: sig - signal payload for getting ancillary data.
* Returns:
*
*/
IP_GLOBAL int
ipnet_sys_get_ancillary_data(struct Ipnet_sig_ancillary_data *sig)
{
Ipnet_socket *sock;
struct Ip_cmsghdr *cmsg;
Ip_socklen_t cmsg_len = 0;
Ip_socklen_t len = 0;
struct Ip_msghdr *msg = sig->msg;
Ipcom_pkt *pkt = sig->pkt;
IPNET_FD_TO_SOCKET(sig->fd, sock);
cmsg = IP_CMSG_FIRSTHDR(msg);
if (cmsg == IP_NULL)
return 0;
if (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_RECV_IF))
{
/* Recv if socket option */
len = (Ip_socklen_t)ipnet_if_to_sockaddr_dl(ipnet_if_indextonetif(pkt->vr_index,
pkt->ifindex),
IP_NULL);
cmsg_len += (Ip_socklen_t)IP_CMSG_SPACE(len);
if (cmsg_len > msg->msg_controllen)
goto truncated;
cmsg->cmsg_len = (Ip_socklen_t)IP_CMSG_LEN(len);
#ifdef IPCOM_USE_INET
if (sock->ipcom.domain == IP_AF_INET)
{
cmsg->cmsg_level = IP_IPPROTO_IP;
cmsg->cmsg_type = IP_IP_RECVIF;
}
#endif /* IPCOM_USE_INET */
#ifdef IPCOM_USE_INET6
if (sock->ipcom.domain == IP_AF_INET6)
{
cmsg->cmsg_level = IP_IPPROTO_IPV6;
cmsg->cmsg_type = IP_IPV6_RECVIF;
}
#endif /* IPCOM_USE_INET6 */
(void)ipnet_if_to_sockaddr_dl(ipnet_if_indextonetif(pkt->vr_index,
pkt->ifindex),
IP_CMSG_DATA(cmsg));
cmsg = IP_CMSG_NXTHDR(msg, cmsg);
}
if (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_SENDER_LINK_ADDR))
{
/* Sender link address */
int start;
Ipnet_netif *netif;
struct Ip_sockaddr_dl *dl;
if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_LOOPED_BACK))
netif = ipnet_loopback_get_netif(pkt->vr_index);
else
netif = ipnet_if_indextonetif(pkt->vr_index, pkt->ifindex);
if (netif != IP_NULL)
{
len = (Ip_socklen_t)ipnet_if_to_sockaddr_dl(netif, IP_NULL);
cmsg_len += (Ip_socklen_t)IP_CMSG_SPACE(len);
if (cmsg == IP_NULL || cmsg_len > msg->msg_controllen)
goto truncated;
cmsg->cmsg_len = (Ip_socklen_t)IP_CMSG_LEN(len);
#ifdef IPCOM_USE_INET
if (sock->ipcom.domain == IP_AF_INET)
{
cmsg->cmsg_level = IP_IPPROTO_IP;
cmsg->cmsg_type = IP_IP_X_SENDERLINKADDR;
}
#endif /* IPCOM_USE_INET */
#ifdef IPCOM_USE_INET6
if (sock->ipcom.domain == IP_AF_INET6)
{
cmsg->cmsg_level = IP_IPPROTO_IPV6;
cmsg->cmsg_type = IP_IPV6_X_SENDERLINKADDR;
}
#endif /* IPCOM_USE_INET6 */
dl = IP_CMSG_DATA(cmsg);
(void)ipnet_if_to_sockaddr_dl(netif, dl);
/* Get the sender link address */
start = pkt->start;
pkt->start = pkt->ipstart - netif->ipcom.link_hdr_size;
(void)netif->link_get_frame_info(netif,
pkt,
IP_NULL,
IP_NULL,
(Ip_u8 *) IP_SOCKADDR_DL_LLADDR(dl),
IP_NULL);
pkt->start = start;
cmsg = IP_CMSG_NXTHDR(msg, cmsg);
}
/* else: interface has been detached */
}
if (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_TIMESTAMPING))
{
struct timespec * tv;
len = sizeof(struct Ip_timeval);
cmsg_len += (Ip_socklen_t)IP_CMSG_SPACE(len);
if (cmsg == IP_NULL || cmsg_len > msg->msg_controllen)
goto truncated;
cmsg->cmsg_len = (Ip_socklen_t)IP_CMSG_LEN(len);
cmsg->cmsg_level = IP_SOL_SOCKET;
cmsg->cmsg_type = SO_TIMESTAMP;
tv = (struct timespec *) IP_CMSG_DATA(cmsg);
#ifdef _WRS_CONFIG_COMPONENT_IPPTP
if(IP_BIT_ISSET(sock->timestamp_flag, SOF_TIMESTAMPING_RX_HARDWARE))
{
tv->tv_sec = pkt->timestamp_fetch.tv_sec;
tv->tv_nsec = pkt->timestamp_fetch.tv_nsec;
}
else /*SOF_TIMESTAMPING_RX_SOFTWARE */
#endif
{
#ifdef IP_PORT_VXWORKS
(void)clock_gettime(CLOCK_REALTIME, (struct timespec *)tv);
#endif
}
cmsg = IP_CMSG_NXTHDR(msg, cmsg);
}
#ifdef IPCOM_USE_INET
if (sock->ipcom.domain == IP_AF_INET)
ipnet_add_ancillary_ipv4_data(msg, &cmsg, &cmsg_len, sock, pkt);
#endif /* IPCOM_USE_INET */
#ifdef IPCOM_USE_INET6
if (sock->ipcom.domain == IP_AF_INET6)
ipnet_add_ancillary_ipv6_data(msg, &cmsg, &cmsg_len, sock, pkt);
#endif /* IPCOM_USE_INET6 */
truncated:
if (msg->msg_controllen < cmsg_len)
{
IP_BIT_SET(msg->msg_flags, IP_MSG_CTRUNC);
cmsg_len -= (Ip_socklen_t)IP_CMSG_SPACE(len);
}
msg->msg_controllen = cmsg_len;
return 0;
}
#ifdef IPCOM_FORWARDER_NAE
/*
*===========================================================================
* ipnet_sock_set_nae_ctx
*===========================================================================
* Description: Sets context specific data that is passed to all NAE
* handlers. NOTE: the memory pointed to by 'ctx' must be
* accessible by all NAE:s.
* Parameters: fd - socket descriptor
* ctx - pointer to context specific data.
* Returns:
*
*/
IP_GLOBAL int
ipnet_sock_set_nae_ctx(Ip_fd fd, void *ctx)
{
Ipnet_socket *sock;
IPNET_FD_TO_SOCKET(fd, sock);
sock->nae_action_ctx = ctx;
return 0;
}
/*
*===========================================================================
* ipnet_sock_shm_symdup
*===========================================================================
* Description: Copies the passed symbol name string into a shared memory
* area.
* Parameters: sym_name - the name of a symbol in the current .code segment
* d - location where the duplicated string should be stored.
* Returns:
*
*/
IP_STATIC int
ipnet_sock_shm_symdup(const char *sym_name, char **d)
{
char *shm_str;
if (*d != IP_NULL)
/*
* Would be relatively complicated to allow the handler to be
* changed.
*/
return -IP_ERRNO_EPERM;
if (ipcom_sym_from_name(sym_name) == IP_NULL)
/*
* No symbol with that name
*/
return -IP_ERRNO_EINVAL;
shm_str = ipcom_shm_malloc(ipcom_strlen(sym_name) + 1);
if (shm_str != IP_NULL)
strcpy(shm_str, sym_name);
*d = shm_str;
return shm_str ? 0 : -IP_ERRNO_ENOMEM;
}
/*
*===========================================================================
* ipnet_sock_set_nae_rx_action
*===========================================================================
* Description: Sets the NAE reception handler for a socket.
* Parameters: fd - socket descriptor
* nae_rx_action - handler for packets received on this socket
* Returns:
*
*/
IP_GLOBAL int
ipnet_sock_set_nae_rx_action(Ip_fd fd, const char *nae_rx_action)
{
Ipnet_socket *sock;
IPNET_FD_TO_SOCKET(fd, sock);
return ipnet_sock_shm_symdup(nae_rx_action, &sock->nae_rx_action);
}
/*
*===========================================================================
* ipnet_sock_set_nae_rx_notifier
*===========================================================================
* Description: Sets the NAE reception notifier for a socket.
* Parameters: fd - socket descriptor
* Returns:
*
*/
IP_GLOBAL int
ipnet_sock_set_nae_rx_notifier(Ip_fd fd,
const char *nae_rx_notifier)
{
Ipnet_socket *sock;
IPNET_FD_TO_SOCKET(fd, sock);
return ipnet_sock_shm_symdup(nae_rx_notifier, &sock->nae_rx_notifier);
}
/*
*===========================================================================
* ipnet_sock_set_nae_child_rx_action
*===========================================================================
* Description: Sets the NAE reception handler for socket returned via
* accept() on 'fd'.
* Parameters: fd - socket descriptor for a listen socket
* nae_rx_action - name of handler for packets received
* on childs to this socket.
* Returns:
*
*/
IP_GLOBAL int
ipnet_sock_set_nae_child_rx_action(Ip_fd fd, const char *nae_rx_action)
{
Ipnet_socket *sock;
IPNET_FD_TO_SOCKET(fd, sock);
return ipnet_sock_shm_symdup(nae_rx_action, &sock->nae_rx_child_action);
}
/*
*===========================================================================
* ipnet_sock_rx_action
*===========================================================================
* Description: Locates the RX action handler in the current code segment
* and calls it
* Parameters: sock - a socket with a registed action handler.
* pkt - a packet that will be delivered to the RX action hook
* Returns:
*
*/
IP_GLOBAL void
ipnet_sock_rx_action(Ipnet_socket *sock, Ipcom_pkt *pkt)
{
Ipnet_sock_nae_rx_func nae_rx_func;
/*
* TCP and SCTP socket should always call the application with the
* atomic tag taken
*/
ip_assert(pkt == IP_NULL
|| (sock->proto != IP_IPPROTO_TCP
&& sock->proto != IP_IPPROTO_SCTP)
|| ipcom_nae_socket_is_atomic(sock));
/*
* Cannot store a function pointer to the handler directly at the
* socket since the socket lives in shared memory and the
* function called must be located in the same .code segment as
* the caller.
*/
nae_rx_func = sock->nae_rx_action_funcs[ipcom_nae_ix()];
if (IP_UNLIKELY(nae_rx_func == IP_NULL))
{
/*
* First time this NAE is used to call the action. Locate the
* the address of the function to call based on the name
* supplied to the socket
*/
nae_rx_func = (Ipnet_sock_nae_rx_func) ipcom_sym_from_name(sock->nae_rx_action);
if (nae_rx_func == IP_NULL)
{
/*
* There is no function with the specified name in this
* image.
*/
IP_PANIC();
}
sock->nae_rx_action_funcs[ipcom_nae_ix()] = nae_rx_func;
}
nae_rx_func(sock->ipcom.fd, pkt, sock->nae_action_ctx);
}
/*
*===========================================================================
* ipnet_sock_rx_notifier
*===========================================================================
* Description: Locates the NAE RX notifier handler and calls it
* Parameters: sock - a socket with a registed action handler.
* pkt - a packet that will be presented to the RX notifer
* hook. The packet must not be modified
* Returns:
*
*/
IP_GLOBAL void
ipnet_sock_rx_notifier(Ipnet_socket *sock, Ipcom_pkt *pkt)
{
Ipnet_sock_nae_rx_func nae_rx_func;
/*
* Cannot store a function pointer to the handler directly at the
* socket since the socket lives in shared memory and the
* function called must be located in the same .code segment as
* the caller.
*/
nae_rx_func = sock->nae_rx_notifier_funcs[ipcom_nae_ix()];
if (IP_UNLIKELY(nae_rx_func == IP_NULL))
{
/*
* First time this NAE is used to call the action. Locate the
* the address of the function to call based on the name
* supplied to the socket
*/
nae_rx_func = (Ipnet_sock_nae_rx_func) ipcom_sym_from_name(sock->nae_rx_notifier);
if (nae_rx_func == IP_NULL)
{
/*
* There is no function with the specified name in this
* image.
*/
IP_PANIC();
}
sock->nae_rx_notifier_funcs[ipcom_nae_ix()] = nae_rx_func;
}
nae_rx_func(sock->ipcom.fd, pkt, sock->nae_action_ctx);
}
#endif /* IPCOM_FORWARDER_NAE*/
#ifdef IP_PORT_LKM
/*
*===========================================================================
* ipnet_sys_pkt_recv_raw_pkt
*===========================================================================
* Description: Receives a raw packet from a socket.
* This function is not allowed to lock neither the code locks
* nor any data locks, since it is called recursively from
* locked context.
* Parameters: fd - A socket descriptor.
* flags -
* Returns: A reference to a packet
* IP_NULL - failure
*
*/
IP_GLOBAL Ipcom_pkt*
ipnet_pkt_recv_raw_pkt(Ip_fd fd, int flags)
{
Ipcom_pkt *pkt = IP_NULL;
Ipnet_socket *sock;
if (IP_UNLIKELY(fd < IPNET_SOCKET_STARTFD)
|| IP_UNLIKELY(fd >= ipnet_conf_max_sockets)
|| IP_UNLIKELY(IP_NULL == (sock = ipnet->sockets[fd]))
|| IP_UNLIKELY(IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_CLOSED)))
{
IPNET_STATS(sock_ebadf++);
return IP_NULL;
}
ipcom_spinlock_lock(&sock->rcv_lock);
if (sock->ipcom.so_errno)
goto errout;
if (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_READ_CLOSED))
goto errout;
pkt = sock->rcv_head;
if (pkt == IP_NULL)
{
ipcom_errno_set(-IP_ERRNO_EWOULDBLOCK);
goto errout;
}
if (IP_LIKELY(IP_BIT_ISFALSE(flags, IP_MSG_PEEK)))
{
/* Unlink the first packet from the queue head. */
if (sock->ipcom.type == IP_SOCK_STREAM)
sock->ipcom.rcv_bytes -= (pkt->end - pkt->start);
else
sock->ipcom.rcv_bytes -= pkt->maxlen;
sock->rcv_head = pkt->next;
if (sock->rcv_head == IP_NULL)
sock->rcv_tail = IP_NULL;
}
if (sock->ipcom.type == IP_SOCK_DGRAM)
{
/* Datagram payload starts after IP header */
pkt->start -= sizeof(Ipnet_pkt_udp);
}
errout:
ipcom_spinlock_unlock(&sock->rcv_lock);
return pkt;
}
#endif /* IP_PORT_LKM */
/*
*===========================================================================
* ipnet_sock_init
*===========================================================================
* Description: Initialization of this module.
* Parameters:
* Returns:
*
*/
IP_GLOBAL void
ipnet_sock_init(void)
{
#ifdef IPNET_USE_ROUTER_ALERT
ipcom_list_init(&ipnet_sock_router_alert_list);
#endif
ipnet_sock_ops = ipcom_calloc(1, sizeof(ipnet_sock_ops[0]));
if (ipnet_sock_ops == IP_NULL)
IP_PANIC();
}
#define IPNET_SOCK_SHOW
#ifdef IPNET_SOCK_SHOW
IP_STATIC const char *
ipnet_sock_show_domain_to_str(int domain)
{
static char domain_buf[10];
switch (domain)
{
case IP_AF_INET:
return "INET";
case IP_AF_INET6:
return "INET6";
case IP_AF_ROUTE:
return "ROUTE";
case IP_AF_NETLINK:
return "NETLINK";
default:
ipcom_snprintf(domain_buf, sizeof(domain_buf), "%d", domain);
return domain_buf;
}
}
IP_STATIC const char *
ipnet_sock_show_proto_to_str(int proto)
{
static char proto_buf[10];
switch (proto)
{
case IP_IPPROTO_TCP:
return "TCP";
case IP_IPPROTO_UDP:
return "UDP";
case IP_IPPROTO_UDPLITE:
return "UDP-LITE";
case IP_IPPROTO_SCTP:
return "SCTP";
default:
ipcom_snprintf(proto_buf, sizeof(proto_buf), "%d", proto);
return proto_buf;
}
}
/*
*===========================================================================
* ipnet_sock_show_one
*===========================================================================
* Description: Display information about a single socket
* Parameters: sock - a socket
* Returns:
*
*/
IP_STATIC void
ipnet_sock_show_one(Ipnet_socket *sock)
{
Ipnet_socket_lookup_key key = { 0, };
char src_addr[48];
char dst_addr[48];
ipnet_key_from_sock(sock, &key);
ipcom_printf("%-5d%-8s%-10s%-6d%-6d%20s:%-6d%20s:%-6d\n",
sock->ipcom.fd,
ipnet_sock_show_domain_to_str(key.domain),
ipnet_sock_show_proto_to_str(key.proto),
sock->ipcom.rcv_bytes,
ipcom_atomic_get(&sock->snd_bytes),
ipcom_inet_ntop(key.domain,
&key.local_addr,
src_addr,
sizeof(src_addr)),
key.local_port,
ipcom_inet_ntop(key.domain,
&key.peer_addr,
dst_addr,
sizeof(dst_addr)),
key.peer_port);
}
/*
*===========================================================================
* sockShow
*===========================================================================
* Description: Dumps all socket to the console.
* Parameters:
* Returns:
*
*/
int sockShow(void);
int sockShow(void)
{
int i;
int count = 0;
ipcom_printf("%-5s%-8s%-10s%-6s%-6s%20s:%-6s%20s:%-6s\n",
"fd", "domain", "proto", "rcv", "snd", "saddr", "sport", "daddr", "dport");
for (i = 0; i < ipnet_conf_max_sockets; i++)
{
Ipnet_socket *sock = ipnet->sockets[i];
if (sock)
{
count++;
ipnet_sock_show_one(sock);
}
}
/*
* Return the number of elements printed
*/
return count;
}
#if 0
static void
printPkts(Ipcom_pkt *pkt)
{
if (pkt)
{
ipcom_printf("\tLength: %d, start: %d, end: %d, Ptr: %p\n",
ipcom_pkt_get_length(pkt),
pkt->start,
pkt->end, pkt);
printPkts(pkt->next);
}
}
typedef struct Iptcp_segment_struct
{
struct Iptcp_segment_struct *next;
/* Next packet in resend list for outgoing and next
out-of-order package for incoming */
Ipcom_pkt *pkt; /* Pointer to the packet that holds the TCP segment data */
Ip_u32 seq_start; /* First sequence number for segment */
Ip_u32 seq_end; /* Last sequence number for segment */
int app_start; /* (Only send) Start of application data */
int app_end; /* (Only send) End of application data */
Ip_u16 flags_n; /* The TCP flags for this segment */
#ifdef IPTCP_USE_SACK_OPT
Ip_u8 is_sacked; /* (Only send) IP_TRUE if the segment has
been ACK:ed in a selective ACK option
(Only receiver) IP_FALSE if this segment
has not been included in a SACK option yet */
#endif
Ip_u32 last_msec; /* Last timestamp this segment was processed */
}
Iptcp_segment;
static void
printSegs(struct Iptcp_segment_struct *seg)
{
if (seg)
{
Ipcom_pkt *pkt_next;
Ipcom_pkt *pkt = seg->pkt;
ipcom_printf("\tSEQ: %u - %u", seg->seq_start, seg->seq_end);
pkt_next = pkt->next;
pkt->next = IP_NULL;
printPkts(pkt);
pkt->next = pkt_next;
printSegs(seg->next);
}
}
static void
tcpSockInfo(Ipnet_socket *sock)
{
ipcom_printf("TCP socket with descriptor %d\n", sock->ipcom.fd);
ipcom_printf("%d bytes in the send queue, %d has not been ACK:ed\n",
ipcom_atomic_get(&sock->snd_bytes),
sock->tcb->send.uack_bytes);
ipcom_printf("Packets in snd queue are:\n");
printPkts(sock->tcb->send.head);
ipcom_printf("Packets in retrans queue are:\n");
printSegs(sock->tcb->send.resend_head);
}
int sockInfo(int fd);
int sockInfo(int fd)
{
Ipnet_socket *sock;
if (fd < 0 || fd >= ipnet_conf_max_sockets)
ipcom_printf("File descriptor %d is out of range\n", fd);
sock = ipnet->sockets[fd];
if (sock == IP_NULL)
ipcom_printf("%d is not a valid socket descriptor\n", fd);
switch (sock->proto)
{
case IP_IPPROTO_TCP:
tcpSockInfo(sock);
break;
default:
break;
}
return 0;
}
#endif
#endif /* IPNET_DST_SHOW */
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/