/* ****************************************************************************** * 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 ****************************************************************************** */ /* **************************************************************************** * 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 #ifdef IP_PORT_VXWORKS #include #endif /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include #include #include #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 #endif #ifdef IPSCTP #include #endif #ifdef IPCOM_WV_INSTRUMENTATION #include #endif #ifdef IPCOM_USE_FORWARDER #include #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 **************************************************************************** */