/* ipsctp_sock.c - IPNET SCTP Socket API Extensions */ /* * Copyright (c) 2007 Wind River Systems, Inc. * * The right to copy, distribute or otherwise make use of this software * may be licensed only pursuant to the terms of an applicable Wind River * license agreement. No license to Wind River intellectual property rights * is granted herein. All rights not licensed by Wind River are reserved * by Wind River. */ /* ****************************************************************************** * INTERPEAK SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipsctp_sock.c,v $ $Revision: 1.30.10.9 $ * $Source: /home/interpeak/CVSRoot/ipsctp/src/ipsctp_sock.c,v $ * $Author: hsun $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Fei Guo ****************************************************************************** */ /* modification history -------------------- 01a,24jul08,fei Created, base on kcn 19nov07 version. */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** * * Sockets API Extensions for SCTP. Implementation is based on SCTP Socket * draft-15. * * The socket layer for SCTP is an optional layer which sits above the * SCTP ULP. It is the implementation goal to have this layer handles all * the socket specific details and simply calls the SCTP ULP routine for * futher SCTP specific processing. */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #ifndef IPCOM_KERNEL #define IPCOM_KERNEL #endif #include /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include #include #include #include /* **************************************************************************** * 4 DEFINES **************************************************************************** */ #define IPSCTP_MAX_SACK_DELAY 500 #define IPSCTP_SOCKOPT_R 0 #define IPSCTP_SOCKOPT_W 1 #define IPSCTP_SOCKOPT_E -1 IP_STATIC /*inline*/ int ipsctp_sockaddr_check(struct Ip_sockaddr * addr) { if (IP_NULL == addr) return IPNET_ERRNO(EFAULT); if ((IP_AF_INET != ((struct Ip_sockaddr *)addr)->sa_family) #ifdef IPCOM_USE_INET6 && (IP_AF_INET6 != ((struct Ip_sockaddr *)addr)->sa_family) #endif ) return IPNET_ERRNO(EFAULT); return 0; } #define IPSCTP_SOCKADDR_CHECK(addr) \ do { \ int retV = ipsctp_sockaddr_check((struct Ip_sockaddr *)addr); \ if (0 < retV) \ return retV; \ } while(0) #define IPSCTP_SOCK_CHECK(sock, listen_sock) \ do { \ ip_assert((sock)->ipcom.type == (listen_sock)->ipcom.type); \ ip_assert((sock)->ipcom.domain == (listen_sock)->ipcom.domain); \ ip_assert((sock)->proto == (listen_sock)->proto); \ } while(0) #define IPSCTP_SOCK_INST_CHECK(sock, inst) \ do { \ (inst) = (sock)->sctp_inst; \ if (IP_NULL == (inst)) \ return IPNET_ERRNO(EINVAL); \ } while(0) #define IPSCTP_SOCK_GETCB_BY_ASID(assoc_id) \ do { \ if (assoc_id) \ { \ tcb = ipsctp_tcb_lookup_by_instance(inst, \ assoc_id, \ (IP_CONST Ipsctp_addr *)IP_NULL, \ IP_NULL); \ } \ /* this case for TCP, NOT specified */ \ else \ { \ tcb = (Ipsctp_tcb *)ipcom_list_first(&inst->assoc_list); \ } \ } while(0) #define IPSCTP_RTOINFO_CPY(dst, src) \ do { \ (dst)->srto_initial = (src)->srto_initial; \ (dst)->srto_max = (src)->srto_max; \ (dst)->srto_min = (src)->srto_min; \ } while(0) #define IPSCTP_SNDRCV_CPY(dst, src) \ do { \ (dst)->sinfo_stream = (src)->sinfo_stream; \ (dst)->sinfo_ssn = (src)->sinfo_ssn; \ (dst)->sinfo_flags = (src)->sinfo_flags; \ (dst)->sinfo_pr_policy = (src)->sinfo_pr_policy; \ (dst)->sinfo_ppid = (src)->sinfo_ppid; \ (dst)->sinfo_context = (src)->sinfo_context; \ (dst)->sinfo_pr_value = (src)->sinfo_pr_value; \ (dst)->sinfo_tsn = (src)->sinfo_tsn; \ (dst)->sinfo_cumtsn = (src)->sinfo_cumtsn; \ } while(0) #define IPSCTP_IS_V6ONLY(inst) \ ((IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V6)) \ && (IP_BIT_ISFALSE(inst->inst_flags, IPSCTP_SOCKET_BOUND_V4))) /* **************************************************************************** * 5 TYPES **************************************************************************** */ /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 8 DATA **************************************************************************** */ /* **************************************************************************** * 9 LOCAL FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipsctp_is_any *=========================================================================== * Description: check the SCTP address if any net address. * Parameters: name - The SCTP address that you want be checked. * Returns: IP_TRUE is any address, * IP_FALSE not any address. * */ IP_STATIC Ip_bool ipsctp_is_any(IP_CONST Ipsctp_addr *name) { ip_assert(IP_NULL != name); switch (name->sa.sa_family) { #ifdef IPCOM_USE_INET case IP_AF_INET: if (IP_INADDR_ANY == name->sin.sin_addr.s_addr) return IP_TRUE; break; #endif #ifdef IPCOM_USE_INET6 case IP_AF_INET6: { if (IP_IN6_IS_ADDR_UNSPECIFIED(&name->sin6.sin6_addr)) return IP_TRUE; break; } #endif default: break; } return IP_FALSE; } /* *=========================================================================== * ipsctp_is_addr_valid *=========================================================================== * Description: check the SCTP address if address valid or not. * Parameters: name - The SCTP address that you want be checked. * Returns: IP_TRUE is valid address, * IP_FALSE not valid address. * */ IP_STATIC Ip_bool ipsctp_is_addr_valid(IP_CONST Ipsctp_addr *name) { ip_assert(IP_NULL != name); switch (name->sa.sa_family) { #ifdef IPCOM_USE_INET case IP_AF_INET: if (IP_INADDR_ANY == name->sin.sin_addr.s_addr) return IP_FALSE; break; #endif #ifdef IPCOM_USE_INET6 case IP_AF_INET6: if (IP_IN6_IS_ADDR_UNSPECIFIED(&name->sin6.sin6_addr)) return IP_FALSE; break; #endif default: return IP_FALSE; } return IP_TRUE; } /* *=========================================================================== * ipsctp_server_add_to_backlog *=========================================================================== * Description: Adds a socket to the listening socket backlog. * Parameters: listen_sock - The socket that received the initial connection * request. * sock - The socket to add. * from_done - Set to IP_TRUE if the socket should be added to * the list of finished sockets. * should be searched * Returns: None * */ IP_STATIC void ipsctp_server_add_to_backlog(Ipsctp_socket *listen_sock, Ipsctp_socket *sock, Ip_bool from_done) { Ipsctp_instance *inst = IP_NULL; int i = 0; int *backlog = IP_NULL ; if ((!listen_sock) || (!listen_sock->sctp_inst) || (!sock) || (!sock->sctp_inst)) { ip_assert(IP_FALSE); return; } /* select which backlog list need to add */ inst = listen_sock->sctp_inst ; if (IP_TRUE == from_done) backlog = inst->backlog_done; else backlog = inst->backlog_pending; /* choose a NULL entry */ i = 0; while (backlog[i] != IP_SOCKERR) i++; ip_assert(i < inst->backlog_len); backlog[i] = sock->ipcom.fd; inst->backlog_used++; sock->sctp_inst->parent = listen_sock; IPSCTP_SOCK_CHECK(sock, listen_sock); return; } /* *=========================================================================== * ipsctp_server_remove_from_backlog *=========================================================================== * Description: Removes a socket from the listening socket backlog. * Parameters: listen_sock - The socket that received the initial connection * request. * sock - The socket to remove. * from_done - Set to IP_TRUE if the socket should be removed from * the list of finished sockets. * should be searched * Returns: None * */ IP_PUBLIC void ipsctp_server_remove_from_backlog(Ipsctp_socket *listen_sock, Ipsctp_socket *sock, Ip_bool from_done) { Ipsctp_instance *inst = IP_NULL; int i = 0; int *backlog = IP_NULL ; if ((!listen_sock) || (!listen_sock->sctp_inst) || (!sock) || (!sock->sctp_inst)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument when removing socket from backlog."); return; } /* select the backlog list */ inst = listen_sock->sctp_inst; if (IP_TRUE == from_done) backlog = inst->backlog_done; else backlog = inst->backlog_pending; /* loop the backlog list and delete the socket */ for (i = 0; i < listen_sock->sctp_inst->backlog_used; i++) { /* The specified socket descriptor was found */ if (backlog[i] == sock->ipcom.fd) { /* delete specified socket from the backlog list */ do { if (i + 1 == inst->backlog_len) backlog[i] = IP_SOCKERR; else backlog[i] = backlog[i+1]; } while (backlog[i++] != IP_SOCKERR); inst->backlog_used--; sock->sctp_inst->parent = IP_NULL; /* The reseted socket was the only socket that had * finished the handshake */ if ((sock->ipcom.so_errno || IP_BIT_ISSET(sock->sctp_inst->inst_flags, IPSCTP_SOCKET_ACCEPTING)) && (IP_TRUE == from_done) && (backlog[0] == IP_SOCKERR)) IP_BIT_SET(inst->inst_flags, IPSCTP_SOCKET_CONNABORTED); IPSCTP_SOCK_CHECK(sock, listen_sock); return; } } /* It's an error to call this function with a socket that is not in the backlog */ IP_PANIC(); return; } /* *=========================================================================== * ipsctp_sock_kclose *=========================================================================== * Description: Closes a SCTP socket from the kernel. * Parameters: sock - The SCTP socket to close. * Returns: None */ IP_PUBLIC void ipsctp_sock_kclose(Ipsctp_socket *sock) { Ipsctp_instance *inst = IP_NULL; if ((!sock) || (!(inst = sock->sctp_inst))) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument when kernal closing socket."IP_LF); return; } /* set with the CLOSED bitmap and do close operation. * NOTE : NOT release socket until finish close operation, * so you can not use this resource. [20081120] */ if (IP_BIT_ISFALSE(inst->inst_flags, IPSCTP_SOCKET_KCLOSED)) { IP_BIT_SET(inst->inst_flags, IPSCTP_SOCKET_KCLOSED); /* ipcom_accept/ipcom_connect and ipnet_sock_sctp_init * will operate reference count, so do it again */ (void) IP_STACK(do_close)(sock); } return; } /* *=========================================================================== * ipsctp_drop_sock *=========================================================================== * Description: Drops all the connections for the specified socket. * Parameters: sock - The socket to be droped. * Returns: None. * */ IP_STATIC void ipsctp_drop_sock(Ipsctp_socket *sock) { Ipsctp_instance *inst = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; Ipsctp_tcb *tcb_t = IP_NULL; if ((!sock)||(!(inst = sock->sctp_inst))) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument when droping socket."IP_LF); return; } /* if no TCB, just release the socket, e.g. : new socket setup error. */ if (0 == ipsctp_list_size(&inst->assoc_list)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB created when droping socket."IP_LF); ipsctp_sock_kclose(sock); return; } /* loop for all the association and then drop them */ ipsctp_list_for_each_safe(&inst->assoc_list, tcb, tcb_t, Ipsctp_tcb) { if (tcb->state > IPSCTP_STATE_CLOSED) { /* Try to inform the peer that the connection was dropped */ ipsctp_abort_assoc_tcb_with_err(tcb, 0, IPSCTP_ECAUSE_USER_INITIATED_ABORT, IPSCTP_FROM_SOCK + SCTP_LOC_01); } } return; } /* *=========================================================================== * ipsctp_server_handshake_done *=========================================================================== * Description: Moves the socket to the queue of sockets that can be returned * by ipcom_accept(). * Parameters: sock - The socket that received the initial connection * request. * Returns: None * */ IP_PUBLIC void ipsctp_server_handshake_done(Ipsctp_socket *sock) { Ipsctp_socket *listen_sock = IP_NULL; if ((!sock) || (!sock->sctp_inst) || (!sock->sctp_inst->parent)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument when four-way handshake done."); return; } listen_sock = sock->sctp_inst->parent; ipcom_enter_critical_region(); ipsctp_server_remove_from_backlog(listen_sock, sock, IP_FALSE); ipsctp_server_add_to_backlog(listen_sock, sock, IP_TRUE); ipcom_exit_critical_region(); IPSCTP_MIB_STATS(sctp_Passive_Estabs++); /* ipcom_accept() will no longer block (which it could do, even if * even if ipcom_socketselect() was used, if a socket in the backlog * recieved a SHUTDOWN after reaching connected state) */ IP_BIT_CLR(listen_sock->sctp_inst->inst_flags, IPSCTP_SOCKET_CONNABORTED); if (listen_sock->ipcom.event_cb != IP_NULL) (void)listen_sock->ipcom.event_cb(&listen_sock->ipcom, IP_NULL, IP_SOEVENT_CB_ACCEPT); IP_STACK(sock_data_avail)(listen_sock, 0, IP_SHUT_RDWR); #ifdef IPCOM_FORWARDER_NAE if (listen_sock->nae_rx_notifier) { /* * This socket has registered a callback to get notification * each time data is queued to this socket. The ownerhip of * the packet is NOT transfered to the handler. */ ipnet_sock_rx_notifier(listen_sock, IP_NULL); } #endif return; } /* *=========================================================================== * ipsctp_add_laddr *=========================================================================== * Description: Add SCTP local address to local address list. * Parameters: head - The local IP address list head. * laddr - The local IP address. * laddr_state - The local IP address state. * Returns: 0 = success * <0 = error code */ IP_STATIC int ipsctp_add_laddr(Ipsctp_instance *inst, Ipsctp_addr *laddr, Ipsctp_laddr_state laddr_state) { Ipsctp_laddr_entry *laddr_entry = IP_NULL; Ipsctp_laddr_entry *laddr_tmp = IP_NULL; Ipsctp_af_ops *af_ops = IP_NULL; if ((IP_NULL == inst)||(IP_NULL == inst->sock)||(IP_NULL == laddr)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument when adding local address."); return IPNET_ERRNO(EINVAL); } /* if socket is V4, only V4 address can be added */ if ((IP_AF_INET == inst->sock->ipcom.domain) && (IP_AF_INET != laddr->sa.sa_family)) { IPSCTP_DBG2(IPSCTP_DBG_SOCK, "IPSCTP : Invalid AF when adding local address(%u != %u).", inst->sock->ipcom.domain, laddr->sa.sa_family); return IPNET_ERRNO(EINVAL); } /* get the AF operation pointer by socket domain */ af_ops = ipsctp_get_af_ops(inst->sock->ipcom.domain); if (af_ops == IP_NULL) { IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : Invalid domain %d.", inst->sock->ipcom.domain); return IPNET_ERRNO(EINVAL); } /* check if added address in the list already */ ipsctp_list_for_each(&inst->laddr_list, laddr_tmp, Ipsctp_laddr_entry) { if (af_ops->cmp_addr(&laddr_tmp->laddr, laddr)) return 0; } /* malloc entry memory */ #ifdef IPCOM_FORWARDER_NAE laddr_entry = ipcom_shm_malloc(IPSCTP_SIZE_LADDR_ENTRY); #else laddr_entry = ipcom_malloc(IPSCTP_SIZE_LADDR_ENTRY); #endif if (IP_NULL == laddr_entry) return IPNET_ERRNO(ENOMEM); /* init the data structure */ ipsctp_memzero(laddr_entry, IPSCTP_SIZE_LADDR_ENTRY); (void)ipcom_memcpy((void *)&laddr_entry->laddr, (void *)laddr, IPSCTP_SIZE_LADDR); laddr_entry->state = laddr_state; /*ipcom_microtime(&laddr_entry->start_time);*/ laddr_entry->start_time = ipsctp_time_now(); /* add the bind address to the list */ ipcom_list_insert_last(&inst->laddr_list, &laddr_entry->next); return 0; } /* *=========================================================================== * ipsctp_server_socket_new *=========================================================================== * Description: Creates a new socket that will serve as the endpoint for * the connection request just received on the listen socket. * Parameters: [in,out] listen_sock - The socket that received the * connection request. * [in] local_addr - The local IPv4 or IPv6 address to which a * SCTP connection is requested. * [in] local_port - The local port (in host byte order) to * which a SCTP connection is requested. * [in] peer_addr - The IPv4 or IPv6 address from where the * connection request was sent. * [in] peer_port - The remote port from where the connection * request was sent. * [in] netif - The interface the segment was received on. * [in] vr - The virtual router to use. * Returns: NOT NULL is for the new created socket, * NULL is for failing. * */ IP_PUBLIC Ipsctp_socket * ipsctp_server_socket_new(Ipsctp_socket *listen_sock, IP_CONST void *local_addr, Ip_u16 local_port, IP_CONST void *peer_addr, Ip_u16 peer_port, Ipsctp_netif *netif, Ip_u16 vr, Ip_sa_family_t sa_family) { Ipsctp_laddr_entry *laddr_cur = IP_NULL; int ret; int fd; Ipsctp_socket *sock = IP_NULL; Ipnet_sock_ops *ops; /* Backlog is full */ if (listen_sock->sctp_inst->backlog_used >= listen_sock->sctp_inst->backlog_len) { IPSCTP_DBG3(IPSCTP_DBG_SOCK, "IPSCTP : backlog is full for (%u), current is %u, MAX len is %u."IP_LF, listen_sock->ipcom.fd, listen_sock->sctp_inst->backlog_used, listen_sock->sctp_inst->backlog_len); if (listen_sock->sctp_inst->backlog_done[0] != IP_SOCKERR) goto errout; } fd = IP_STACK(sys_socket)(listen_sock->ipcom.domain, listen_sock->ipcom.type, listen_sock->proto, IP_FALSE); if (fd == IP_SOCKERR) { IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : create new socket for listen(%u) error."IP_LF, listen_sock->ipcom.fd); goto errout; } sock = ipstack->sockets[fd]; ip_assert(sock != IP_NULL); ip_assert(sock->sctp_inst != IP_NULL); /* Copy some flags from the listen socket */ sock->flags |= listen_sock->flags & ( IPSCTP_STACK_CONST(SOCKET_FLAG_NONBLOCKING) | IPSCTP_STACK_CONST(SOCKET_FLAG_REUSEADDR) | IPSCTP_STACK_CONST(SOCKET_FLAG_REUSEPORT) | IPSCTP_STACK_CONST(SOCKET_FLAG_DONTROUTE) | IPSCTP_STACK_CONST(SOCKET_FLAG_RECV_IF) | IPSCTP_STACK_CONST(SOCKET_FLAG_TCP_NODELAY)); sock->vr_index = vr; sock->flags |= listen_sock->flags & IPSCTP_STACK_CONST(SOCKET_FLAG_RECVPKTINFO); /* This socket has not yet been given to any user */ IP_BIT_CLR(sock->sctp_inst->inst_flags, IPSCTP_SOCKET_FLAG_USER_REF); /* set new socket accepting flag */ IP_BIT_SET(sock->sctp_inst->inst_flags, IPSCTP_SOCKET_ACCEPTING); #ifdef IPCOM_FORWARDER_NAE (void)ipnet_sock_set_nae_rx_action(sock->ipcom.fd, listen_sock->nae_rx_child_action); sock->nae_action_ctx = listen_sock->nae_action_ctx; #endif ipcom_enter_critical_region(); /* add to backlog list and set to parent */ ipsctp_server_add_to_backlog(listen_sock, sock, IP_FALSE); ipcom_exit_critical_region(); sock->recv_max_bytes = listen_sock->recv_max_bytes; sock->send_max_bytes = listen_sock->send_max_bytes; sock->keep_alive_probe_delay = listen_sock->keep_alive_probe_delay; sock->sctp_inst->linger_time = listen_sock->sctp_inst->linger_time; sock->sctp_inst->feature_flags = listen_sock->sctp_inst->feature_flags; sock->sctp_inst->frag_point = listen_sock->sctp_inst->frag_point; sock->sctp_inst->pd_point = listen_sock->sctp_inst->pd_point; sock->sctp_inst->context = listen_sock->sctp_inst->context; sock->sctp_inst->congestion_control_module = listen_sock->sctp_inst->congestion_control_module; sock->sctp_inst->autoclose_time = listen_sock->sctp_inst->autoclose_time; ipsctp_list_for_each(&listen_sock->sctp_inst->laddr_list, laddr_cur, Ipsctp_laddr_entry) { ret = ipsctp_add_laddr(sock->sctp_inst, &laddr_cur->laddr, IPSCTP_LADDR_NORMAL); if (0 != ret) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : copy local address to new socket error."IP_LF); } if (IP_BIT_ISSET(listen_sock->sctp_inst->inst_flags, IPSCTP_SOCKET_BOUNDALL)) { IP_BIT_SET(sock->sctp_inst->inst_flags, IPSCTP_SOCKET_BOUNDALL); sock->sctp_inst->lport = listen_sock->sctp_inst->lport; } ops = ipnet_sock_get_ops(sock); #ifdef IPCOM_USE_INET if (sock->ipcom.domain == IP_AF_INET) { struct Ip_sockaddr_in name; /* Bind the socket */ IPCOM_SA_LEN_SET(&name, sizeof(name)); name.sin_family = IP_AF_INET; name.sin_addr.s_addr = *(Ip_u32 *) local_addr; name.sin_port = ip_htons(local_port); ret = ops->bind(sock, (struct Ip_sockaddr*) &name, sizeof(name), IP_FALSE); if (ret >= 0) { #ifdef IPNET sock->ip4->flags = listen_sock->ip4->flags; sock->ip4->type_of_service = listen_sock->ip4->type_of_service; name.sin_addr.s_addr = *(Ip_u32 *) peer_addr; name.sin_port = ip_htons(peer_port); ret = ops->connect(sock, (struct Ip_sockaddr *) &name, sizeof(name)); #else sock->d->ip4.daddr_n = *(Ip_u32 *) peer_addr; sock->dport = peer_port; #endif } } else #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 if (sock->ipcom.domain == IP_AF_INET6) { switch (sa_family) { #ifdef IPCOM_USE_INET case IP_AF_INET: { struct Ip_sockaddr_in name; /* Bind the socket */ IPCOM_SA_LEN_SET(&name, sizeof(name)); name.sin_family = IP_AF_INET; name.sin_addr.s_addr = *(Ip_u32 *) local_addr; name.sin_port = ip_htons(local_port); ret = ops->bind(sock, (struct Ip_sockaddr*) &name, sizeof(name), IP_FALSE); if (ret >= 0) { #ifdef IPNET ipnet_sock_set_unreachable(sock); sock->ip4->daddr_n = name.sin_addr.s_addr; sock->dport = local_port; ipnet_sock_set_reachable(sock); #else sock->d->ip4.daddr_n = *(Ip_u32 *) peer_addr; sock->dport = peer_port; #endif } break; } #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 case IP_AF_INET6: { struct Ip_sockaddr_in6 name; /* Bind the socket */ IPCOM_SA_LEN_SET(&name, sizeof(name)); name.sin6_family = IP_AF_INET6; name.sin6_flowinfo = 0; name.sin6_port = ip_htons(local_port); IPSCTP_STACK_CONST(IP6_SET_ADDR)(&name.sin6_addr, (struct Ip_in6_addr *) local_addr); name.sin6_scope_id = ipnet_ip6_get_zone(netif->ipcom.ifindex, &name.sin6_addr); ret = ops->bind(sock, (struct Ip_sockaddr*) &name, sizeof(name), IP_FALSE); if (ret >= 0) { sock->ip6->flags = listen_sock->ip6->flags; sock->ip6->tclass = listen_sock->ip6->tclass; IPNET_IP6_SET_ADDR(&name.sin6_addr, (struct Ip_in6_addr *) peer_addr); name.sin6_port = ip_htons(peer_port); ret = ops->connect(sock, (struct Ip_sockaddr *) &name, sizeof(name)); } break; } #endif /* IPCOM_USE_INET6 */ default: IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : invalid sa_family = %u."IP_LF, sa_family); goto errout; } } else #endif /* IPCOM_USE_INET6 */ { /* Unsupported domain */ IP_PANIC(); goto errout; } if (ret < 0) { IPSCTP_DBG2(IPSCTP_DBG_SOCK, "IPSCTP : bind(connect)[%u] new socket for listen(%u) error."IP_LF, fd, listen_sock->ipcom.fd); goto errout; } /* * The owner of the new socket must be the same as the owner of * the listen socket */ sock->ipcom.pid = listen_sock->ipcom.pid; IP_BIT_SET(sock->flags, IPSCTP_STACK_CONST(SOCKET_FLAG_CONNECTING)); return sock; errout: if (sock) ipsctp_drop_sock(sock); return IP_NULL; } /* *=========================================================================== * ipsctp_resize_backlog *=========================================================================== * Description: change the socket backlog queue size. * Parameters: inst - The SCTP instance that you want change. * Returns: 0 = success, * <0 = error code. * */ IP_STATIC int ipsctp_resize_backlog(Ipsctp_instance *inst, int backlog_size) { int *old_backlog[2]; int *backlog[2]; int old_backlog_size = 0; int i = 0; int b = 0; if (IP_NULL == inst) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument when resizing backlog."); return IPNET_ERRNO(EINVAL); } #ifdef IPCOM_FORWARDER_NAE backlog[0] = ipcom_shm_malloc(backlog_size * sizeof(*inst->backlog_pending) * 2); #else backlog[0] = ipcom_malloc(backlog_size * sizeof(*inst->backlog_pending) * 2); #endif if (IP_NULL == backlog[0]) { ip_assert(IP_FALSE); return IPNET_ERRNO(ENOMEM); } old_backlog[0] = inst->backlog_pending; old_backlog[1] = inst->backlog_done; backlog[1] = backlog[0] + backlog_size; old_backlog_size = inst->backlog_len; for (b = 0; b < 2; b++) { /* Move all done and pending connections to the new backlog */ for (i = 0; i < IP_MIN(old_backlog_size, backlog_size); i++) backlog[b][i] = old_backlog[b][i]; /* Mark remaning slots as unused */ for (; i < backlog_size; i++) backlog[b][i] = IP_SOCKERR; /* Close all sockets that did not fit into the new backlog size */ for (i = IP_MIN(old_backlog_size, backlog_size); i < old_backlog_size; i++) { if (old_backlog[b][i] != IP_SOCKERR) { IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : drop socket (%u) for changing backlog size."IP_LF, old_backlog[b][i]); ipsctp_drop_sock(ipstack->sockets[old_backlog[b][i]]); inst->backlog_used--; } } } #ifdef IPCOM_FORWARDER_NAE ipcom_shm_free(old_backlog[0]); #else ipcom_free(old_backlog[0]); #endif inst->backlog_len = (Ip_u16) backlog_size; inst->backlog_pending = backlog[0]; inst->backlog_done = backlog[1]; return 0; } /* *=========================================================================== * ipsctp_drop_tcb *=========================================================================== * Description: Drops the connection for the specified association. * Parameters: tcb - The tcb associated with the connection to drop. * error_code - The reason for dropping the connection. * Returns: The specified error code. * */ IP_GLOBAL int ipsctp_drop_tcb(Ipsctp_tcb *tcb, int error_code) { Ipsctp_instance *inst = tcb->instance; #if 0 int ret = 0; #endif Ipsctp_socket *sock = tcb->sock; #if 0 if (tcb->state >= IPSCTP_STATE_CLOSED)/* TODO : check */ { /* Try to inform the peer that the connection was dropped */ ret = ipsctp_send_abort_u32(tcb, IPSCTP_ECAUSE_USER_INITIATED_ABORT, 0/* TODO(td) */); } #endif if (tcb->tcb_errno && error_code == -IP_ERRNO_ETIMEDOUT) /* Report the soft-error to the user */ IP_STACK(sock_data_avail(sock, tcb->tcb_errno, IP_SHUT_RDWR)); else /* Report the socket error to the user */ IP_STACK(sock_data_avail(sock, -error_code, IP_SHUT_RDWR)); if (IP_NULL != inst->parent) { ipcom_enter_critical_region(); ipsctp_server_remove_from_backlog(inst->parent, sock, tcb->state >= IPSCTP_STATE_ESTABLISHED); ipcom_exit_critical_region(); } /* change state to IPSCTP_STATE_CLOSED */ (void)ipsctp_change_state(tcb, IPSCTP_STATE_CLOSED); return error_code; } /* *=========================================================================== * ipsctp_cleanup_receive_buffers *=========================================================================== * Description: Frees TCB's chunks in receive queues. * Parameters: sock - The socket for cleanup. * Returns: None. * */ IP_STATIC void ipsctp_cleanup_receive_buffer(Ipsctp_socket *sock) { Ipsctp_instance *inst = IP_NULL; Ipsctp_chunk_recv *chunk_recv = IP_NULL; Ip_bool notify_usr = IP_FALSE; if ((IP_NULL == sock) || (IP_NULL == (inst = sock->sctp_inst))) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument when cleanuping receive buffers."IP_LF); return; } /* Lock the receive buffers */ IPSCTP_DATA_LOCK(sock->rcv_lock); /* Free receive buffers */ chunk_recv = (Ipsctp_chunk_recv *)ipcom_list_first(&inst->recv_list); while (chunk_recv) { Ipsctp_chunk_recv *chunk_next = IP_NULL; /* get next chunk */ chunk_next = (Ipsctp_chunk_recv *)ipcom_list_next(&chunk_recv->chunk.chunk_next); ipcom_list_remove(&chunk_recv->chunk.chunk_next); if (IP_BIT_ISFALSE(chunk_recv->chunk.flag, IPSCTP_CHUNK_FLAG_RECV_FIN)) { /* notify the user that data is available */ notify_usr = IP_TRUE; } /* Free the chunk */ #if 0 if (IP_NULL != chunk_recv->chunk.tcb) { IPSCTP_RECVBUF_LEN_SUB(sock, tcb, (chunk_recv->chunk.pkt->end - chunk_recv->chunk.pkt->start)); IPSCTP_RECVBUF_CNT(sock, tcb)--; } #endif IPSCTP_FREE_RECV_CHUNK(chunk_recv); chunk_recv = chunk_next; } /* zero the receive bytes in socket */ sock->ipcom.rcv_bytes = 0; /* unlock the receive buffer */ IPSCTP_DATA_UNLOCK(sock->rcv_lock); if (notify_usr) { /* notify the user that data is available */ IP_STACK(sock_data_avail)(sock, 0, IP_SHUT_RD); } return; } /* *=========================================================================== * ipsctp_sock_cleanup *=========================================================================== * Description: Frees all data in send and receive queues and removes + frees * all timer events for this connection. * Parameters: sock - The SCTP socket to free resouces for. * Returns: None * */ IP_PUBLIC void ipsctp_sock_cleanup(Ipsctp_socket *sock) { if (IP_NULL == sock) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument when clearnup."); return; } /* Set socket status, only CLOSE and free do this. */ IP_BIT_CLR(sock->flags, IPSCTP_STACK_CONST(SOCKET_FLAG_CONNECTING) | IPSCTP_STACK_CONST(SOCKET_FLAG_CONNECTED)); return; } /* *=========================================================================== * ipsctp_getname *=========================================================================== * Description: Returns the socket address of the local side (if bound) or * the peer side (if connected). * Parameters: sock - Socket descriptor pointer. * 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_PUBLIC int ipsctp_getname(Ipsctp_socket *sock, struct Ip_sockaddr *name, Ip_socklen_t *namelen, Ip_bool local) { #ifdef IPCOM_USE_INET6 struct Ip_sockaddr_in6 *name_in6 = IP_NULL; #endif /* IPCOM_USE_INET6 */ #ifdef IPCOM_USE_INET struct Ip_sockaddr_in *name_in = IP_NULL; #endif /* IPCOM_USE_INET */ Ipsctp_instance *inst = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; Ipsctp_laddr_entry *laddrs = IP_NULL; Ipsctp_transport *tp = IP_NULL; Ipsctp_addr *sctp_addr = IP_NULL; Ipsctp_addr zero_addr; Ip_in_port_t addr_port = 0; int ret_val = 0; if ((IP_NULL == sock) || (IP_NULL == name) || (IP_NULL == namelen) || (IP_NULL == (inst = sock->sctp_inst))) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EBADF); } #ifdef IPCOM_USE_INET if ((IP_AF_INET == sock->ipcom.domain) && (*namelen < sizeof(struct Ip_sockaddr_in))) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid name length."IP_LF); return IPNET_ERRNO(ENOBUFS); } #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 if ((IP_AF_INET6 == sock->ipcom.domain) && (*namelen < sizeof(struct Ip_sockaddr_in6))) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid name length."IP_LF); return IPNET_ERRNO(ENOBUFS); } #endif /* IPCOM_USE_INET6 */ switch (sock->ipcom.domain) { #ifdef IPCOM_USE_INET case IP_AF_INET: *namelen = sizeof(struct Ip_sockaddr_in); name_in = (struct Ip_sockaddr_in *)name; ipcom_memset(name_in->sin_zero, 0, sizeof(name_in->sin_zero)); IPCOM_SA_LEN_SET(name_in, sizeof(struct Ip_sockaddr_in)); name_in->sin_family = IP_AF_INET; break; #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 case IP_AF_INET6: *namelen = sizeof(struct Ip_sockaddr_in6); name_in6 = (struct Ip_sockaddr_in6 *) name; IPCOM_SA_LEN_SET(name_in6, sizeof(struct Ip_sockaddr_in6)); name_in6->sin6_family = IP_AF_INET6; name_in6->sin6_scope_id = sock->ip6->iscope_id; name_in6->sin6_flowinfo = ip_htonl(sock->ip6->flowinfo); break; #endif /* IPCOM_USE_INET6 */ default: IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : invalid socket domain = %u."IP_LF, sock->ipcom.domain); return IPNET_ERRNO(EINVAL); } ipsctp_memzero(&zero_addr, sizeof(zero_addr)); sctp_addr = &zero_addr; if (IP_TRUE == local) { if (IP_BIT_ISFALSE(sock->flags, IPNET_SOCKET_FLAG_BOUND)) return IPNET_ERRNO(ENOENT); addr_port = inst->lport; if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUNDALL)) { tcb = (Ipsctp_tcb *)ipcom_list_first(&inst->assoc_list); if (IP_NULL == tcb) { /* ret_val = IPNET_ERRNO(ENOTCONN); */ goto set_addr; } ipsctp_list_for_each(&tcb->transport_list, tp, Ipsctp_transport) { if ((sock->ipcom.domain == tp->paddr.sa.sa_family) && (IP_BIT_ISFALSE(tp->state, IPSCTP_ADDR_UNCONFIRMED)) && (IP_BIT_ISSET(tp->state,IPSCTP_ADDR_REACHABLE))) { #ifdef IPCOM_USE_INET6 IP_CONST struct Ip_in6_addr *src6_addr = IP_NULL; #endif /* IPCOM_USE_INET6 */ #ifdef IPCOM_USE_INET IP_CONST struct Ip_in_addr *src_addr = IP_NULL; #endif /* IPCOM_USE_INET */ switch (sock->ipcom.domain) { #ifdef IPCOM_USE_INET case IP_AF_INET: src_addr = ipnet_ip4_get_src_addr(sock->vr_index, &tp->paddr.sin.sin_addr, IP_NULL, IP_NULL); if (IP_NULL == src_addr) continue; zero_addr.sin.sin_addr.s_addr = src_addr->s_addr; zero_addr.sin.sin_port = ip_htons(inst->lport); break; #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 case IP_AF_INET6: src6_addr = ipnet_ip6_get_src_addr(sock->vr_index, &tp->paddr.sin6.sin6_addr, sock->ip6->iscope_id, IP_NULL, IP_NULL, sock->ip6->prefer_src_flags); if (IP_NULL == src6_addr) continue; IPNET_IP6_SET_ADDR(&zero_addr.sin6.sin6_addr, src6_addr); zero_addr.sin6.sin6_port = ip_htons(inst->lport); break; #endif /* IPCOM_USE_INET6 */ default: IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : invalid socket domain."IP_LF); continue; } break; } } } else { ipsctp_list_for_each(&inst->laddr_list, laddrs, Ipsctp_laddr_entry) { if (sock->ipcom.domain == laddrs->laddr.sa.sa_family) { sctp_addr = &laddrs->laddr; break; } } } } else { /* here include listen TCB */ tcb = (Ipsctp_tcb *)ipcom_list_first(&inst->assoc_list); if (IP_NULL == tcb) { ret_val = IPNET_ERRNO(ENOTCONN); goto set_addr; } if ((IPSCTP_STATE_LISTEN != tcb->state) && (tcb->state < IPSCTP_STATE_ESTABLISHED)) return IPNET_ERRNO(ENOTCONN); addr_port = tcb->rport; ipsctp_list_for_each(&tcb->transport_list, tp, Ipsctp_transport) { if ((sock->ipcom.domain == tp->paddr.sa.sa_family) && (IP_BIT_ISFALSE(tp->state, IPSCTP_ADDR_UNCONFIRMED)) && (IP_BIT_ISSET(tp->state, IPSCTP_ADDR_REACHABLE))) { sctp_addr = &tp->paddr; break; } } } set_addr: #ifdef IPCOM_USE_INET if (IP_AF_INET == sock->ipcom.domain) { name_in->sin_addr.s_addr = sctp_addr->sin.sin_addr.s_addr; name_in->sin_port = ip_htons(addr_port); } #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 if (IP_AF_INET6 == sock->ipcom.domain) { name_in6->sin6_addr = sctp_addr->sin6.sin6_addr; name_in6->sin6_port = ip_htons(addr_port); } #endif /* IPCOM_USE_INET6 */ return ret_val; } /* *=========================================================================== * ipsctp_sock_free *=========================================================================== * Description: Frees all resources allocated by the SCTP module for this socket. * Parameters: sock - The SCTP socket to free resouces for. * Returns: None * */ IP_PUBLIC void ipsctp_sock_free(Ipsctp_socket *sock) { Ipsctp_laddr_entry *laddr_cur = IP_NULL; Ipsctp_laddr_entry *laddr_tmp = IP_NULL; Ipsctp_instance *inst = IP_NULL; if ((!sock)||(!sock->sctp_inst)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument when free socket."); return; } inst = sock->sctp_inst; /* Connection closed or in listen state, free all resouces held by the socket */ if (ipsctp_sock_state(sock, LISTENING)) { Ipsctp_socket *backlog_sock = IP_NULL; /* Listen socket, close all sockets in the backlog */ while (inst->backlog_pending && (IP_SOCKERR != inst->backlog_pending[0])) { backlog_sock = ipstack->sockets[inst->backlog_pending[0]]; /* send ABORT instead of graceful shutdown */ if (backlog_sock) { IPSCTP_DBG2(IPSCTP_DBG_SOCK, "IPSCTP : abort pending socket %u, reference count %u."IP_LF, inst->backlog_pending[0], ipcom_atomic_get(&backlog_sock->ref_count)); ipsctp_drop_sock(backlog_sock); } } while (inst->backlog_done && (IP_SOCKERR != inst->backlog_done[0])) { backlog_sock = ipstack->sockets[inst->backlog_done[0]]; /* send ABORT instead of graceful shutdown */ if (backlog_sock) { IPSCTP_DBG2(IPSCTP_DBG_SOCK, "IPSCTP : abort done socket %u, reference count %u."IP_LF, inst->backlog_done[0], ipcom_atomic_get(&backlog_sock->ref_count)); ipsctp_drop_sock(backlog_sock); } } ip_assert(inst->backlog_used == 0); #ifdef IPCOM_FORWARDER_NAE ipcom_shm_free(inst->backlog_pending); #else ipcom_free(inst->backlog_pending); #endif } /* free local address list */ ipsctp_list_for_each_safe(&inst->laddr_list, laddr_cur, laddr_tmp, Ipsctp_laddr_entry) { ipcom_list_remove(&laddr_cur->next); #ifdef IPCOM_FORWARDER_NAE ipcom_shm_free(laddr_cur); #else ipcom_free(laddr_cur); #endif } /* if no TCB, also need cleanup. */ ipsctp_sock_cleanup(sock); ipsctp_timer_remove(inst, IP_NULL, IP_NULL, IPSCTP_TIMO_SECRET); if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_FLAG_LINGER)) { ipsctp_timer_remove(inst, IP_NULL, IP_NULL, IPSCTP_TIMO_LINGER); } if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_FLAG_SENDALL)) { ipsctp_timer_remove(inst, IP_NULL, IP_NULL, IPSCTP_TIMO_SENDALL); } ipsctp_cleanup_receive_buffer(sock); /* destory the lock here */ ipcom_spinlock_delete(&inst->tcb_lock); sock->sctp_inst = IP_NULL; return; } /* *=========================================================================== * ipsctp_close_association *=========================================================================== * Description: Close a association of SCTP socket. * Parameters: sock - The SCTP control block. * Returns: None. * */ IP_PUBLIC void ipsctp_close_association(Ipsctp_tcb *tcb) { Ipsctp_instance *inst = IP_NULL; if ((!tcb) || (!(inst = tcb->instance))) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return; } /* do abort process in ipsctp_close * NOTE : before check if LISTEN TCB */ if (0 == inst->linger_time) return; /* if the listen TCB, just free TCB, no SHUTDOWN packet sent */ if (IPSCTP_IS_LISTEN_TCB(tcb)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : it is a listen TCB, free it."IP_LF); /* free all the resource, and destory socket, * for decrease the socket reference count */ (void)ipsctp_change_state(tcb, IPSCTP_STATE_CLOSED); return; } switch (tcb->state) { /* just close, no need wait. CASE : * INIT --> * <-- INIT-ACK * ------------------------ * close then client socket * * in ipsctp_handle_init_ack will return if not COOKIE_WAIT, * SO here we just close the association. * * test on FreeBSD and Linux, they will send ABORT to the peer. */ case IPSCTP_STATE_COOKIE_WAIT: case IPSCTP_STATE_COOKIE_ECHOED: (void)ipsctp_change_state(tcb, IPSCTP_STATE_CLOSED); return; case IPSCTP_STATE_SHUTDOWN_PENDING: case IPSCTP_STATE_SHUTDOWN_RECEIVED: case IPSCTP_STATE_SHUTDOWN_SENT: case IPSCTP_STATE_SHUTDOWN_ACK_SENT: IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : in %s state when shutdown."IP_LF, ipsctp_state_to_str(tcb->state)); return; case IPSCTP_STATE_ESTABLISHED: break; case IPSCTP_STATE_LISTEN: case IPSCTP_STATE_CLOSED: default: return; } if (IP_BIT_ISSET(tcb->tcb_flags, IPSCTP_TCBC_FLAG_ABORT)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : TCB abort when shutdown."IP_LF); return; } /* if the waiting send queue is not null */ if (IP_FALSE == ipsctp_is_output_queue_empty(tcb)) { if (IPSCTP_STATE_SHUTDOWN_PENDING == tcb->state) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : already in SHUTDOWN-PENDING state.\n"); return; } IP_STACK(sock_data_avail)(tcb->sock, 0, IP_SHUT_WR); /* set to shutdown pending state */ (void)ipsctp_change_state(tcb, IPSCTP_STATE_SHUTDOWN_PENDING); } else { /* NOTE : if SHUTDOWN-ACK, go through. */ if (IPSCTP_STATE_SHUTDOWN_SENT == tcb->state) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : already in SHUTDOWN-SENT state.\n"); return; } /* stop timers */ ipsctp_stop_timers_for_shutdown(tcb); /* send SHUTDOWN to peer endpoint */ if (ipsctp_send_shutdown(tcb, tcb->primary_transport) < 0) { /* output error information, T2 timer will do the following. */ IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : send SHUTDOWN error!\n"); } (void)ipsctp_change_state(tcb, IPSCTP_STATE_SHUTDOWN_SENT); /* start T2 timer */ (void)ipsctp_timer_add(IP_NULL, tcb, tcb->primary_transport, IPSCTP_TIMO_T2); /* start T5 timer */ (void)ipsctp_timer_add(IP_NULL, tcb, tcb->primary_transport, IPSCTP_TIMO_T5); } ipsctp_tx_segments(tcb); return; } /* *=========================================================================== * ipsctp_sock_init *=========================================================================== * Description: Initiazlies the socket for SCTP use. * Parameters: sock - The socket on which SCTP should be used. * Returns: 0 = success * <0 = socket error code */ IP_PUBLIC int ipsctp_sock_init(Ipsctp_socket *sock) { Ipsctp_instance *inst = IP_NULL; int i = 0; if ((IP_NULL == sock) || (IP_NULL == (inst = sock->sctp_inst))) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."); return IPNET_ERRNO(EINVAL); } /* Init socket and instance data structure */ inst->send_lowat = 1L; inst->recv_lowat = 1L; inst->sock = sock; inst->lport = 0; inst->linger_time = IPCOM_ADDR_INFINITE; ipcom_list_init(&inst->assoc_list); ipcom_list_init(&inst->laddr_list); ipcom_list_init(&inst->recv_list); ipcom_list_init(&inst->sendall_pkt); ipcom_list_init(&inst->timers); if (ipcom_spinlock_create(&inst->tcb_lock) != IPCOM_SUCCESS) { IPCOM_LOG0(CRIT, "Failed to initialize SCTP TCB Cache Lock."); return IPNET_ERRNO(ENOMEM); } /* set TCP/UDP bit flag */ switch (IPSCTP_SOCK_TYPE(sock)) { case IP_SOCK_STREAM: IP_BIT_SET(inst->inst_flags, IPSCTP_SOCKET_TCP); break; #ifdef IPSCTP_INCLUDE_UDP_LIKE case IP_SOCK_SEQPACKET: IP_BIT_SET(inst->inst_flags, IPSCTP_SOCKET_UDP); break; case IP_SOCK_DGRAM: #endif default: return IPNET_ERRNO(ESOCKTNOSUPPORT); } IP_BIT_SET(inst->inst_flags, IPSCTP_SOCKET_FLAG_USER_REF); /* Initialize Initiation Message option parameters */ inst->initmsg.sinit_num_ostreams = (Ip_u16)ipcom_sysvar_get_as_int0("ipsctp.Default.NumOutStreams", IPSCTP_DEFAULT_NUM_OUT_STREAMS_DEFAULT); inst->initmsg.sinit_max_instreams = (Ip_u16)ipcom_sysvar_get_as_int0("ipsctp.Default.NumInStreams", IPSCTP_DEFAULT_NUM_IN_STREAMS_DEFAULT); inst->initmsg.sinit_max_attempts = (Ip_u16)ipcom_sysvar_get_as_int0("ipsctp.Max.Init.Retransmits", IPSCTP_MAX_INIT_RETRANSMITS_DEFAULT); inst->initmsg.sinit_max_init_timeo = (Ip_u16)ipcom_sysvar_get_as_int0("ipsctp.Rto.Max", IPSCTP_RTO_MAX_DEFAULT); /* Initialize Retransmission Timeout parameters */ inst->rtoinfo.srto_initial = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.Rto.Initial", IPSCTP_RTO_INITIAL_DEFAULT); inst->rtoinfo.srto_max = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.Rto.Max", IPSCTP_RTO_MAX_DEFAULT); inst->rtoinfo.srto_min = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.Rto.Min", IPSCTP_RTO_MIN_DEFAULT); /* Initialize Peer Address parameters */ inst->paddrparam.spp_hbinterval = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.HB.Interval", IPSCTP_HB_INTERVAL_DEFAULT); inst->paddrparam.spp_pathmaxrxt = (Ip_u16)ipcom_sysvar_get_as_int0("ipsctp.Max.RetrPerPath", IPSCTP_MAX_RETR_PER_PATH_DEFAULT); inst->paddrparam.spp_pathmtu = 0; inst->paddrparam.spp_flags = 0; #ifdef IPCOM_USE_INET if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V4)) inst->paddrparam.spp_ipv4_tos = sock->ip4->type_of_service; #endif #ifdef IPCOM_USE_INET6 if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V6)) inst->paddrparam.spp_ipv6_flowlabel = sock->ip6->flowinfo; #endif /* Initialize Notification Interest Options parameters */ inst->feature_flags = 0; /* Initialize Association parameters */ inst->assocparams.sasoc_asocmaxrxt = (Ip_u16)ipcom_sysvar_get_as_int0("ipsctp.Max.RetrPerAssoc", IPSCTP_MAX_RETR_PER_ASSOC_DEFAULT); inst->assocparams.sasoc_cookie_life = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.Valid.Cookie.Life", IPSCTP_VALID_COOKIE_LIFE_DEFAULT); /* Initialize default send parameters */ ipsctp_memzero(&inst->sndrcvinfo, IPSCTP_SIZE_SNDRCVINFO); inst->shut_guard_time = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.Default.ShutdownGuardTime", IPSCTP_DEFAULT_SHUTDOWN_GUARD_TIME_DEFAULT); inst->secret_time = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.Default.SecretLifeTime", IPSCTP_DEFAULT_SECRET_LIFE_TIME_DEFAULT); inst->current_secret_number = 0; inst->last_secret_number = 0; for (i = 0; i < IPSCTP_NUMBER_OF_SECRETS; i++) inst->secret_key[0][i] = ipcom_random(); ipcom_microtime(&inst->secret_change_time); ipsctp_timer_add(inst, IP_NULL, IP_NULL, IPSCTP_TIMO_SECRET); inst->frag_point = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.Max.Segment", IPSCTP_MAX_SEGMENT_DEFAULT); inst->max_burst = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.Max.Burst", IPSCTP_MAX_BURST_DEFAULT); inst->sack_info.sack_delay = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.Sack.DelayedTime", IPSCTP_SACK_DELAYED_TIME_DEFAULT); inst->sack_info.sack_freq = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.Sack.Freq", IPSCTP_SACK_FREQ_DEFAULT); inst->congestion_control_module = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.Cc.Mod", IPSCTP_CC_RFC2581); inst->pd_point = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.PartialDelivery.Point", (sock->recv_max_bytes >> 1)); inst->sws_receiver = IPSCTP_SWS_RECEIVER_DEF; inst->sws_sender = IPSCTP_SWS_SENDER_DEF; if (ipsctp_style(sock, TCP)) { inst->get_tcb_info = ipsctp_usr_sock_get_tcb_info_tcp; } else { inst->get_tcb_info = ipsctp_usr_sock_get_tcb_info_udp; } return 0; } /* *=========================================================================== * ipsctp_sock_lookup_da_udp *=========================================================================== * Description: Look up TCB based on local address and local port. * Parameters: sock - The SCTP socket descriptor. * dst_addr - The IP address in network order of the * Returns: NOT NULL - matched TCB, * NULL - no matched TCB. * */ IP_STATIC Ipsctp_tcb * ipsctp_sock_lookup_da_udp(Ipsctp_socket *sock, struct Ip_sockaddr *dst_addr) { Ipsctp_socket *cur_sock = IP_NULL; Ipsctp_instance *tmp_inst = IP_NULL; Ipsctp_instance *inst = IP_NULL; Ipsctp_transport *tp = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; Ip_u16 rport = 0; Ip_u32 i = 0; union Ip_sockaddr_union *sa = (union Ip_sockaddr_union *)dst_addr; if ((IP_NULL == ipnet->sockets) || (IP_NULL == dst_addr) || (IP_NULL == sock)) return IP_NULL; switch (dst_addr->sa_family) { #ifdef IPCOM_USE_INET case IP_AF_INET: rport = ip_ntohs(sa->sin.sin_port); break; #endif #ifdef IPCOM_USE_INET6 case IP_AF_INET6: rport = ip_ntohs(sa->sin6.sin6_port); break; #endif default: IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : return with error af."IP_LF); return IP_NULL; } inst = sock->sctp_inst; /* search in all socket arrary */ for (i = 0; i < ipnet_conf_max_sockets; i++) { if (IP_NULL == (cur_sock = ipnet->sockets[i])) continue; /* itself, continue */ if (sock == cur_sock) continue; /* check if SCTP socket */ if (IP_NULL == (tmp_inst = cur_sock->sctp_inst)) continue; /* NOTE : no check for local address */ if (inst->lport != tmp_inst->lport) continue; /* search in socket association list */ ipsctp_list_for_each(&tmp_inst->assoc_list, tcb, Ipsctp_tcb) { if (rport != tcb->rport) continue; /* NOT include listen TCB */ if (IPSCTP_IS_LISTEN_TCB(tcb)) continue; /* search in this association transport list */ ipsctp_list_for_each(&tcb->transport_list, tp, Ipsctp_transport) { #ifdef IPSCTP_DEBUG IPSCTP_DBG2(IPSCTP_DBG_SOCK, "IPSCTP : lookup transport address %s/%u UDP."IP_LF, ipsctp_addr_ntop(&tp->paddr, ipnet->log_buf, 256), tcb->rport); #endif if (tp->paddr.sa.sa_family != dst_addr->sa_family) continue; switch (dst_addr->sa_family) { #ifdef IPCOM_USE_INET case IP_AF_INET: if (sa->sin.sin_addr.s_addr != tp->paddr.sin.sin_addr.s_addr) continue; break; #endif #ifdef IPCOM_USE_INET6 case IP_AF_INET6: if (IP_FALSE == IP_IN6_ARE_ADDR_EQUAL(&sa->sin6.sin6_addr, &tp->paddr.sin6.sin6_addr)) continue; break; #endif default: continue; } return tcb; } } } return IP_NULL; } /* *=========================================================================== * ipsctp_bind *=========================================================================== * Description: Bind address to socket. * * int bind(int sd, struct sockaddr *addr, socklen_t addrlen); * * sd: the socket descriptor returned by socket() call. * addr: the address structure (either struct sockaddr_in or struct * sockaddr_in6 defined in [RFC3493]). * addrlen: the size of the address structure. * * Parameters: sock - The socket to bind. * name - The bind address. * namelen - Bind address length. * Returns: 0 = success, <0 = error code * */ IP_PUBLIC int ipsctp_bind(Ipsctp_socket *sock, IP_CONST struct Ip_sockaddr *name, Ip_socklen_t namelen) { Ipsctp_instance *inst = IP_NULL; int ret = 0; #if defined(IPCOM_USE_INET) && defined(IPCOM_USE_INET6) Ipsctp_addr bindall_saddr; #endif Ipsctp_addr saddr; if ((IP_NULL == sock) || (namelen < sizeof(struct Ip_sockaddr)) || (IP_NULL == (inst = sock->sctp_inst))) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } IPSCTP_SOCKADDR_CHECK(name); #ifdef IPSCTP_ALWAYS_BOUNDALL #ifdef IPCOM_USE_INET if (name->sa_family == IP_AF_INET) { ((struct Ip_sockaddr_in *)name)->sin_addr.s_addr = 0; } #endif #ifdef IPCOM_USE_INET6 if (name->sa_family == IP_AF_INET6) { struct Ip_sockaddr_in6 *name_in6 = (struct Ip_sockaddr_in6 *)name; ipcom_memset(&name_in6->sin6_addr, 0, sizeof(name_in6->sin6_addr)); } #endif #endif /* close by peer, refuse for rebind. */ if (ipsctp_sock_state(sock, CLOSED)) return IPNET_ERRNO(ESHUTDOWN); /* * return for the second bind, if bound all flag is set; * this case will happen with not specify bind address. */ if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUNDALL)) return 0; /* set the bind port and instance flags */ inst->lport = sock->sport; if (IP_TRUE == ipsctp_is_any((Ipsctp_addr *)name)) IP_BIT_SET(inst->inst_flags, IPSCTP_SOCKET_BOUNDALL); (void)ipsctp_memzero(&saddr, sizeof(saddr)); (void)ipcom_memcpy((void *)&saddr.sa, (void *)name, IPCOM_SA_LEN_GET(name)); switch (name->sa_family) { #ifdef IPCOM_USE_INET case IP_AF_INET: if (0 == saddr.sin.sin_port) saddr.sin.sin_port = ip_htons(inst->lport); #ifdef IPCOM_USE_INET6 /* first bind V4 and socket is V6 */ if ((IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUNDALL)) && (IP_AF_INET6 == sock->ipcom.domain)) { (void)ipsctp_memzero(&bindall_saddr, sizeof(bindall_saddr)); IPCOM_SA_LEN_SET(&bindall_saddr.sa, sizeof(struct Ip_sockaddr_in6)); bindall_saddr.sin6.sin6_family = IP_AF_INET6; bindall_saddr.sin6.sin6_port = ip_htons(inst->lport); ret = ipsctp_add_laddr(inst, &bindall_saddr, IPSCTP_LADDR_NORMAL); } #endif break; #endif #ifdef IPCOM_USE_INET6 case IP_AF_INET6: if (0 == saddr.sin6.sin6_port) saddr.sin6.sin6_port = ip_htons(inst->lport); #ifdef IPCOM_USE_INET if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUNDALL)) { (void)ipsctp_memzero(&bindall_saddr, sizeof(bindall_saddr)); IPCOM_SA_LEN_SET(&bindall_saddr.sa, sizeof(struct Ip_sockaddr_in)); bindall_saddr.sin.sin_family = IP_AF_INET; bindall_saddr.sin.sin_port = ip_htons(inst->lport); ret = ipsctp_add_laddr(inst, &bindall_saddr, IPSCTP_LADDR_NORMAL); } #endif break; #endif default: return IPNET_ERRNO(EINVAL); } /* add the bind address to the list, if bind all flag set * the IP address is "0.0.0.0" */ ret = ipsctp_add_laddr(inst, &saddr, IPSCTP_LADDR_NORMAL); #ifdef IPSCTP_DEBUG IPSCTP_DBG2(IPSCTP_DBG_SOCK, "IPSCTP : bind address %s/%u successful."IP_LF, ipsctp_addr_ntop(&saddr, ipnet->log_buf, 256), inst->lport); #endif return ret; } /* *=========================================================================== * ipsctp_connect *=========================================================================== * Description: The SCTP transport layer connect. * * int connect(int sd, const struct sockaddr *addr, socklen_t addrlen); * * sd - the socket descriptor of the endpoint. * addr - the peer's address. * addrlen - the size of the address. * * Parameters: sock - The socket to connect. * to - The coneect peer's address. * Returns: 0 = success, <0 = error code * */ IP_PUBLIC int ipsctp_connect(Ipsctp_socket *sock, IP_CONST struct Ip_sockaddr *to) { Ipsctp_instance *inst = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; int ret = 0; if ((!sock) || (!(inst = sock->sctp_inst))) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } IPSCTP_SOCKADDR_CHECK(to); /* it's a listen socket, if passive open just return. NOTE : for accept */ if (IP_NULL != inst->parent) return 0; #ifdef IPSCTP_DEBUG IPSCTP_DBG3(IPSCTP_DBG_SOCK, "IPSCTP : connect %s with state(0x%X),style(%d)."IP_LF, ipsctp_addr_ntop((Ipsctp_addr *)to, ipnet->log_buf, 256), sock->sctp_inst->inst_flags, ipsctp_style(sock, TCP)); #endif if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_FLAG_PEELOFF)) return IPNET_ERRNO(EISCONN); /* check the socket flags(for close state) */ /* close by peer, refuse for relisten */ if (ipsctp_sock_state(sock, CLOSED)) return IPNET_ERRNO(ECONNREFUSED); /* check the socket flags(for listening state) */ if (ipsctp_sock_state(sock, LISTENING) && ipsctp_style(sock, TCP)) return IPNET_ERRNO(EISCONN); /* check whether we already have a TCB with the same addresss */ if (IP_NULL != ipsctp_sock_lookup_da(sock, (struct Ip_sockaddr *)to)) return IPNET_ERRNO(EALREADY); /* CHECK if a socket that matches an existing peeled-off association */ /* NOTE : not include TCP and UDP, if UDP, bind with a same local port * and different IP address, then connect to the same destination, * it's OK. if needed in ipsctp_usr_sock_get_tcb_info_udp? */ if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_LISTENING) && ipsctp_sock_lookup_da_udp(sock, (struct Ip_sockaddr *)to)) return IPNET_ERRNO(EADDRNOTAVAIL); /* allocate the TCB(association) */ ret = ipsctp_alloc_tcb(inst, (Ipsctp_addr *)to, 0, /* alloc a new v-tag */ &tcb, IP_NULL); if ((ret < 0) || (IP_NULL == tcb)) return ret; /* for RTO calculate */ (void)ipcom_microtime(&tcb->time_entered); ip_assert(IP_NULL != ipcom_list_first(&inst->assoc_list)); /* set the connecting flag */ IP_BIT_SET(sock->flags, IPSCTP_STACK_CONST(SOCKET_FLAG_CONNECTING)); IP_BIT_SET(tcb->tcb_flags, IPSCTP_TCBC_FLAG_CONNECTING); if (ipsctp_style(sock, TCP)) IP_BIT_SET(inst->inst_flags, IPSCTP_SOCKET_CONNECTED); /* send initialize packet to the peer */ ret = ipsctp_send_init(tcb); if (IPNET_ERRNO(ENOBUFS) == ret) { /* TODO: schedule connect retry */ } if (IPNET_ERRNO(EFAULT) == ret) { return ret; } /* change the tcb state */ (void)ipsctp_change_state(tcb, IPSCTP_STATE_COOKIE_WAIT); if (IP_BIT_ISFALSE(tcb->tcb_flags, IPSCTP_TCBC_FLAG_CONNECTED)) { if (tcb->tcb_errno) return -tcb->tcb_errno; } /* block if no NON-BLOCKING flag set */ return IPNET_ERRNO(EINPROGRESS); } /* *=========================================================================== * ipsctp_listen *=========================================================================== * Description: Puts a SCTP socket into listen state. * * int listen(int sd, int backlog); * sd: the socket descriptor of the SCTP endpoint. * backlog: this specifies the max number of outstanding associations * allowed in the socket's accept queue. These are the associations * that have finished the four-way initiation handshake (see Section * 5 of [RFC4960]) and are in the ESTABLISHED state. Note, a backlog * of '0' indicates that the caller no longer wishes to receive new * associations. * * Parameters: sock - The socket on which to start listening for incoming * connection requests. * backlog - The maximum number of pending connections to * keep track of. (use 0 to get default) * Returns: 0 = success, <0 = error code. * */ IP_PUBLIC int ipsctp_listen(Ipsctp_socket *sock, int backlog) { union Ip_sockaddr_union sa; Ipsctp_instance *inst = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; int ret = 0; if ((IP_NULL == sock) || (backlog < 0) || (IP_NULL == (inst = sock->sctp_inst))) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* check the socket flags(for close state) */ /* close by peer, refuse for relisten */ if (ipsctp_sock_state(sock, CLOSED)) return IPNET_ERRNO(ESHUTDOWN); /* check the socket flags(for connect state) */ if (ipsctp_sock_state(sock, CONNECTED) && ipsctp_style(sock, TCP)) return IPNET_ERRNO(EISCONN); if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_FLAG_PEELOFF)) return IPNET_ERRNO(EISCONN); if (IP_BIT_ISFALSE(sock->flags, IPSCTP_STACK_CONST(SOCKET_FLAG_BOUND))) { Ipnet_sock_ops *ops; /* Not bound, bind to an ephemeral port and the unspecifed address */ ipsctp_memzero(&sa, sizeof(sa)); sa.sa.sa_family = (Ip_sa_family_t)sock->ipcom.domain; IPCOM_SA_LEN_SET(&sa.sa, sizeof(sa)); ops = ipnet_sock_get_ops(sock); ret = ops->bind(sock, &sa.sa, sizeof(sa), IP_TRUE); if (ret < 0) return ret; } /* set to the default backlog number */ if (0 == backlog) backlog = 5/*ipsctp_conf_default_backlog*/; /* just return if new backlog size same with the old backlog size */ if ((IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_LISTENING)) && (backlog == inst->backlog_len)) return 0; ret = ipsctp_resize_backlog(inst, backlog); if (ret < 0) return ret; /* just return if TCB has been allocated before */ if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_LISTENING)) return 0; /* make sure no TCB now, UDP-like allow it, same with FreeBSD and Linux */ /* ip_assert(0 == ipsctp_list_size(&inst->assoc_list)); */ /* set instance flag */ IP_BIT_SET(inst->inst_flags, IPSCTP_SOCKET_LISTENING); /* allocate the listening TCB(association) */ ret = ipsctp_alloc_dummy_tcb(inst, &tcb); if ((ret < 0) || (IP_NULL == tcb)) return ret; return 0; } /* *=========================================================================== * ipsctp_accept *=========================================================================== * Description: Accepts a connection attempt. * * new_sd = accept(int sd, struct sockaddr *addr, socklen_t *addrlen); * * new_sd - the socket descriptor for the newly formed association. * sd - the listening socket descriptor. * addr - on return, will contain the primary address of the peer * endpoint. * addrlen - on return, will contain the size of addr. * * Parameters: listen_sock - The listening socket to accept a socket from. * addr - The address of the connecting node will be copied to * this buffer if not equal to IP_NULL. * addrlen - Holds the length of the addr buffer and will contain * the length of the address copied to addr upon return. * Returns: >= 0 = the socket descriptor of the connection. <0 = error code. * */ IP_PUBLIC int ipsctp_accept(Ipsctp_socket *listen_sock, struct Ip_sockaddr *addr, Ip_socklen_t *addrlen) { Ipsctp_instance *inst = IP_NULL; Ipsctp_socket *sock = IP_NULL; int ret = 0; if ((IP_NULL == listen_sock) || (IP_NULL == (inst = listen_sock->sctp_inst))) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EBADF); } #ifdef IPSCTP_INCLUDE_UDP_LIKE if (ipsctp_style(listen_sock, UDP)) return IPNET_ERRNO(EOPNOTSUPP); #endif if (!ipsctp_sock_state(listen_sock, LISTENING)) return IPNET_ERRNO(EINVAL); /* A ABORT was sent to the socket in the backlog after it * reached established state */ if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_CONNABORTED)) { IP_BIT_CLR(inst->inst_flags, IPSCTP_SOCKET_CONNABORTED); return IPNET_ERRNO(ECONNABORTED); } /* No socket ready at the moment, no-blocking case */ if (inst->backlog_done[0] == IP_SOCKERR) { if (IP_BIT_ISFALSE(listen_sock->flags, IPNET_SOCKET_FLAG_NONBLOCKING)) return IPNET_ERRNO(EWOULDBLOCK); else return IPNET_ERRNO(EAGAIN); } /* Remove the oldest socket from the backlog and return it */ ip_assert(inst->backlog_done[0] >= 0); ip_assert(inst->backlog_done[0] < IP_STACK(conf_max_sockets)); sock = ipstack->sockets[inst->backlog_done[0]]; ip_assert(sock != IP_NULL); /* clear new socket accepting flag */ IP_BIT_CLR(sock->sctp_inst->inst_flags, IPSCTP_SOCKET_ACCEPTING); if (addr != IP_NULL) { /* here we get the primary peer address */ ret = IP_STACK(sys_getname)(sock->ipcom.fd, addr, addrlen, IP_FALSE); if (ret < 0) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : get peer address failed."IP_LF); return ret; } } ipcom_enter_critical_region(); ipsctp_server_remove_from_backlog(listen_sock, sock, IP_TRUE); ipcom_exit_critical_region(); ip_assert(inst->backlog_used < inst->backlog_len); /* The user is given a reference to the new socket */ ipcom_atomic_inc(&sock->ref_count); IP_BIT_SET(sock->sctp_inst->inst_flags, IPSCTP_SOCKET_FLAG_USER_REF); IPSCTP_SOCK_CHECK(sock, listen_sock); return sock->ipcom.fd; } /* *=========================================================================== * ipsctp_shutdown *=========================================================================== * Description: Shutdown the read and/or write side of a SCTP socket. * * int shutdown(int sd, int how); * sd - the socket descriptor of the association to be closed. * how - Specifies the type of shutdown. The values are as follows: * SHUT_RD - Disables further receive operations. No SCTP protocol * action is taken. * SHUT_WR - Disables further send operations, and initiates the * SCTP shutdown sequence. * SHUT_RDWR - Disables further send and receive operations and * initiates the SCTP shutdown sequence. * * Parameters: sock - The SCTP socket to shutdown. * Returns: None. * */ IP_PUBLIC void ipsctp_shutdown(Ipsctp_socket *sock, int how) { Ipsctp_instance *inst = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; if ((!sock)||(!(inst = sock->sctp_inst))) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return; } /* only TCP can do this operation */ if (ipsctp_style(sock, UDP)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : shutdown UDP socket."IP_LF); return; } if (IP_SHUT_RD == how) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : shutdown RD mode."IP_LF); return; } /* ------- flowing is for IP_SHUT_WR ------- */ /* if no TCB just return, only socket needed free. * e.g. : just create sock then free(in ipsctp_close) */ tcb = (Ipsctp_tcb *)ipcom_list_first(&inst->assoc_list); if (IP_NULL == tcb) return; ipsctp_close_association(tcb); return; } /* *=========================================================================== * ipsctp_close *=========================================================================== * Description: Closes a SCTP connection. * Parameters: sock - The SCTP socket to close. * Returns: 0 = success, <0 = error code * */ IP_PUBLIC int ipsctp_close(Ipsctp_socket *sock) { Ipsctp_instance *inst = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; Ipsctp_tcb *tcb_t = IP_NULL; if ((!sock)||(!(inst = sock->sctp_inst))) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* ONLY one time */ if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_CLOSED)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : socket already closed."IP_LF); return 0; } IP_BIT_SET(inst->inst_flags, IPSCTP_SOCKET_CLOSED); if (IP_BIT_ISSET(sock->flags, IP_STACK_CONST(SOCKET_FLAG_CLOSED))) IP_BIT_CLR(inst->inst_flags, IPSCTP_SOCKET_FLAG_USER_REF); /* do abort process */ if (0 == inst->linger_time) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : close(abort) for linger."IP_LF); /* list remove will be done in ipsctp_free_tcb. */ ipsctp_list_for_each_safe(&inst->assoc_list, tcb, tcb_t, Ipsctp_tcb) { /* if the listen TCB, just free */ if (IPSCTP_IS_LISTEN_TCB(tcb)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : free listen TCB for close."IP_LF); (void)ipsctp_change_state(tcb, IPSCTP_STATE_CLOSED); continue; } /* abort TCB */ ipsctp_abort_assoc_tcb_with_err(tcb, IPSCTP_INVALID_NOTIFY_ERR, IPSCTP_ECAUSE_USER_INITIATED_ABORT, IPSCTP_FROM_IPSCTP + SCTP_LOC_02); } } #ifdef IPSCTP_INCLUDE_UDP_LIKE else if (ipsctp_style(sock, UDP)) { ipsctp_list_for_each_safe(&inst->assoc_list, tcb, tcb_t, Ipsctp_tcb) { /* close the association */ ipsctp_close_association(tcb); /* If TCB has established, update round trip time for this route */ /* TODO(td): may be I only need get the route entry from transport */ if (tcb->state >= IPSCTP_STATE_ESTABLISHED) { /* get the route and update */; } } } #endif /* if no TCB just return, only socket needed free. * e.g. : just create sock then free */ if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_KCLOSE_PENDING) || (0 == ipsctp_list_size(&inst->assoc_list))) { IP_BIT_CLR(inst->inst_flags, IPSCTP_SOCKET_KCLOSE_PENDING); /* ensure the close operation */ if (ipcom_atomic_get(&sock->ref_count) > 1) ipsctp_sock_kclose(sock); else IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : close, ref_count so smaill."IP_LF); } /* startup linger timer if needed */ if ((0 != inst->linger_time) && (IPSCTP_INVALID_LINGER_TIME != inst->linger_time) && (IP_BIT_ISFALSE(inst->inst_flags, IPSCTP_SOCKET_FLAG_LINGER))) { IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : start linger timer(%u second)."IP_LF, inst->linger_time); (void)ipsctp_timer_add(inst, IP_NULL, IP_NULL, IPSCTP_TIMO_LINGER); } return 0; } /* *=========================================================================== * ipsctp_sock_lookup_sa *=========================================================================== * Description: Look up TCB based on local address and local port. * Parameters: vr - The virtual router the socket must be bound to. * sa_family - adrress family. * local_addr - The IP address in network order of the local * address to match. * local_port - The source port in HOST byte order. * Returns: NOT NULL - matched socket, * NULL - no matched socket. * */ IP_PUBLIC Ipsctp_socket * ipsctp_sock_lookup_sa(Ip_u16 vr, int sa_family, IP_CONST void *local_addr, Ip_u16 local_port) { Ipsctp_socket *cur_sock = IP_NULL; Ipsctp_laddr_entry *laddrs = IP_NULL; Ipsctp_instance *inst = IP_NULL; Ip_u32 i = 0; if ((IP_NULL == ipnet->sockets) || (IP_NULL == local_addr)) return IP_NULL; /* search in all socket arrary */ for (i = 0; i < ipnet_conf_max_sockets; i++) { if (IP_NULL == (cur_sock = ipnet->sockets[i])) continue; if (IP_NULL == (inst = cur_sock->sctp_inst)) continue; if (vr != cur_sock->vr_index) continue; /* if 0, no need check local port */ if (local_port && local_port != inst->lport) continue; /* search in this association local bound address list if BOUNDALL the compared address is 0.0.0.0 */ ipsctp_list_for_each(&inst->laddr_list, laddrs, Ipsctp_laddr_entry) { if (laddrs->laddr.sa.sa_family != sa_family) continue; switch (sa_family) { #ifdef IPCOM_USE_INET case IP_AF_INET: if (*(Ip_in_addr_t *)local_addr != laddrs->laddr.sin.sin_addr.s_addr) continue; break; #endif #ifdef IPCOM_USE_INET6 case IP_AF_INET6: if (IP_FALSE == IP_IN6_ARE_ADDR_EQUAL(local_addr, &laddrs->laddr.sin6.sin6_addr)) continue; break; #endif default: continue; } return cur_sock; } } return IP_NULL; } /* *=========================================================================== * ipsctp_sock_lookup_da *=========================================================================== * Description: Look up TCB based on dst_addr. * Parameters: sock - The SCTP socket descriptor. * dst_addr - The IP address in network order of the * Returns: NOT NULL - matched TCB, * NULL - no matched TCB. * */ IP_PUBLIC Ipsctp_tcb * ipsctp_sock_lookup_da(Ipsctp_socket *sock, struct Ip_sockaddr *dst_addr) { Ipsctp_instance *inst = IP_NULL; Ipsctp_transport *tp = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; Ip_u16 rport = 0; union Ip_sockaddr_union *sa = (union Ip_sockaddr_union *)dst_addr; if ((IP_NULL == dst_addr) || (IP_NULL == sock) || (IP_NULL == (inst = sock->sctp_inst))) return IP_NULL; switch (dst_addr->sa_family) { #ifdef IPCOM_USE_INET case IP_AF_INET: rport = ip_ntohs(sa->sin.sin_port); break; #endif #ifdef IPCOM_USE_INET6 case IP_AF_INET6: rport = ip_ntohs(sa->sin6.sin6_port); break; #endif default: IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : return with error af."IP_LF); return IP_NULL; } #ifdef IPSCTP_DEBUG IPSCTP_DBG2(IPSCTP_DBG_SOCK, "IPSCTP : lookup destination address %s/%u."IP_LF, ipsctp_addr_ntop((Ipsctp_addr *)dst_addr, ipnet->log_buf, 256), rport); #endif /* search in socket association list */ ipsctp_list_for_each(&inst->assoc_list, tcb, Ipsctp_tcb) { if (rport != tcb->rport) continue; /* NOT include listen TCB */ if (IPSCTP_IS_LISTEN_TCB(tcb)) continue; /* search in this association transport list */ ipsctp_list_for_each(&tcb->transport_list, tp, Ipsctp_transport) { #ifdef IPSCTP_DEBUG IPSCTP_DBG2(IPSCTP_DBG_SOCK, "IPSCTP : lookup transport address %s/%u."IP_LF, ipsctp_addr_ntop(&tp->paddr, ipnet->log_buf, 256), tcb->rport); #endif if (tp->paddr.sa.sa_family != dst_addr->sa_family) continue; switch (dst_addr->sa_family) { #ifdef IPCOM_USE_INET case IP_AF_INET: if (sa->sin.sin_addr.s_addr != tp->paddr.sin.sin_addr.s_addr) continue; break; #endif #ifdef IPCOM_USE_INET6 case IP_AF_INET6: if (IP_FALSE == IP_IN6_ARE_ADDR_EQUAL(&sa->sin6.sin6_addr, &tp->paddr.sin6.sin6_addr)) continue; break; #endif default: continue; } return tcb; } } return IP_NULL; } #if 0 /* *=========================================================================== * ipsctp_sockopt_check_rw *=========================================================================== * Description: check socket option is read or write. * Parameters: optname - the option name. * Returns: IPSCTP_SOCKOPT_R = read only, * IPSCTP_SOCKOPT_R = write only. * IPSCTP_SOCKOPT_E = neither read-only or write-only. * */ IP_STATIC int ipsctp_sockopt_check_rw(int optname) { switch (optname) { /* these options are defined by read-only */ case IPSCTP_GET_ASSOC_ID_LIST: case IPSCTP_GET_ASSOC_NUMBER: case IPSCTP_LOCAL_AUTH_CHUNKS: case IPSCTP_PEER_AUTH_CHUNKS: case IPSCTP_GET_PEER_ADDR_INFO: case IPSCTP_STATUS: return IPSCTP_SOCKOPT_R; /* these options are defined by write-only */ case IPSCTP_SET_PEER_PRIMARY_ADDR: case IPSCTP_AUTH_CHUNK: case IPSCTP_AUTH_KEY: case IPSCTP_AUTH_DEACTIVATE_KEY: case IPSCTP_AUTH_DELETE_KEY: return IPSCTP_SOCKOPT_W; default: break; } return IPSCTP_SOCKOPT_E; } #endif /* *=========================================================================== * ipsctp_get_sa_family_size *=========================================================================== * Description: Get address family length. * Parameters: inst - the socket instance. * sa_family - domain type. * Returns: >0 = address family length, <0 error code. * */ IP_STATIC int ipsctp_get_sa_family_size(Ipsctp_instance *inst, int sa_family) { #ifdef IPCOM_USE_INET6 if ((inst && IP_BIT_ISSET(inst->feature_flags, IPSCTP_INST_FLAG_WANT_MAPPED_V4)) || (IP_AF_INET6 == sa_family)) return sizeof(struct Ip_sockaddr_in6); else #endif #ifdef IPCOM_USE_INET if (IP_AF_INET == sa_family) return sizeof(struct Ip_sockaddr_in); else #endif IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : Invalid address family(%u)."IP_LF, sa_family); return IPNET_ERRNO(EINVAL); } /* *=========================================================================== * ipsctp_getsockopt_rtoinfo *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer to store the value of the option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_getsockopt_rtoinfo(Ipsctp_socket *sock, void *optval, Ip_socklen_t *optlen) { Ipsctp_instance *inst = IP_NULL; struct Ipsctp_rtoinfo *rto_info = (struct Ipsctp_rtoinfo *)optval; Ipsctp_tcb *tcb = IP_NULL; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_rtoinfo) > *optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* check instance point */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* find tcb with sctp_rtoinfo.srto_assoc_id */ IPSCTP_SOCK_GETCB_BY_ASID(rto_info->srto_assoc_id); if (!tcb && rto_info->srto_assoc_id && ipsctp_style(sock, UDP)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF); return IPNET_ERRNO(EINVAL); } /* get the values from instance or tcb */ if (tcb) { rto_info->srto_initial = tcb->initial_rto; rto_info->srto_max = tcb->max_rto; rto_info->srto_min = tcb->min_rto; } else { IPSCTP_RTOINFO_CPY(rto_info, &inst->rtoinfo); } *optlen = sizeof(struct Ipsctp_rtoinfo); #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Get socket route information option OK."IP_LF); #endif return 0; } /* *=========================================================================== * ipsctp_getsockopt_associnfo *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer to store the value of the option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_getsockopt_associnfo(Ipsctp_socket *sock, void *optval, Ip_socklen_t *optlen) { Ipsctp_instance *inst = IP_NULL; struct Ipsctp_assocparams *params = (struct Ipsctp_assocparams *)optval; Ipsctp_tcb *tcb = IP_NULL; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_assocparams) > *optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* check instance pointer */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* find tcb with sctp_assocparams.sasoc_assoc_id */ IPSCTP_SOCK_GETCB_BY_ASID(params->sasoc_assoc_id); if (!tcb && params->sasoc_assoc_id && ipsctp_style(sock, UDP)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF); return IPNET_ERRNO(EINVAL); } /* get the values from instance or tcb */ if (tcb) { params->sasoc_asocmaxrxt = tcb->max_send_times; params->sasoc_peer_rwnd = tcb->peers_rwnd; params->sasoc_local_rwnd = ipsctp_calc_rwnd(tcb); params->sasoc_cookie_life = tcb->cookie_life; /* get the peer transports count */ params->sasoc_number_peer_destinations = (Ip_u16)ipsctp_list_size(&tcb->transport_list); } else { params->sasoc_asocmaxrxt = inst->assocparams.sasoc_asocmaxrxt; params->sasoc_cookie_life = inst->assocparams.sasoc_cookie_life; params->sasoc_peer_rwnd = inst->sock->recv_max_bytes; params->sasoc_local_rwnd = inst->sock->recv_max_bytes; params->sasoc_number_peer_destinations = 0; } *optlen = sizeof(struct Ipsctp_assocparams); #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Get socket association option OK."IP_LF); #endif return 0; } /* *=========================================================================== * ipsctp_getsockopt_default_send_param *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer to store the value of the option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_getsockopt_default_send_param(Ipsctp_socket *sock, void *optval, Ip_socklen_t *optlen) { struct Ipsctp_sndrcvinfo *sr_info = (struct Ipsctp_sndrcvinfo *)optval; Ipsctp_instance *inst = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_sndrcvinfo) > *optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* check instance point */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* find tcb with sctp_sndrcvinfo.sinfo_assoc_id */ IPSCTP_SOCK_GETCB_BY_ASID(sr_info->sinfo_assoc_id); if (!tcb && sr_info->sinfo_assoc_id && ipsctp_style(sock, UDP)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF); return IPNET_ERRNO(EINVAL); } /* get the values from instance or tcb */ if (tcb) IPSCTP_SNDRCV_CPY(sr_info, &tcb->def_send); else IPSCTP_SNDRCV_CPY(sr_info, &inst->sndrcvinfo); *optlen = sizeof(struct Ipsctp_sndrcvinfo); #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Get socket default send param option OK."IP_LF); #endif return 0; } /* *=========================================================================== * ipsctp_getsockopt_peer_addr_info *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer to store the value of the option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_getsockopt_peer_addr_info(Ipsctp_socket *sock, void *optval, Ip_socklen_t *optlen) { struct Ipsctp_paddrinfo *addr_info = (struct Ipsctp_paddrinfo *)optval; Ipsctp_instance *inst = IP_NULL; Ipsctp_transport *tp = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_paddrinfo) > *optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."); return IPNET_ERRNO(EINVAL); } /* check instance pointer */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* if addr_info.spinfo_address is valid, then * find the transport with it. */ tcb = ipsctp_tcb_lookup_by_instance(inst, 0, (IP_CONST Ipsctp_addr *)&addr_info->spinfo_address, &tp); if (IP_NULL == tp) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No transport match."IP_LF); return IPNET_ERRNO(EINVAL); } /* set the value */ addr_info->spinfo_state = tp->state; addr_info->spinfo_cwnd = tp->cwnd; addr_info->spinfo_srtt = tp->srtt; addr_info->spinfo_rto = tp->rto; addr_info->spinfo_mtu = tp->pmtu; if (tcb) addr_info->spinfo_assoc_id = tcb->my_vtag; *optlen = sizeof(struct Ipsctp_paddrinfo); #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Get socket peer address information option OK."IP_LF); #endif return 0; } /* *=========================================================================== * ipsctp_getsockopt_primary_addr *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer to store the value of the option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_getsockopt_primary_addr(Ipsctp_socket *sock, void *optval, Ip_socklen_t *optlen) { struct Ipsctp_setprim *prim_info = (struct Ipsctp_setprim *)optval; Ipsctp_instance *inst = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_setprim) > *optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* check instance pointer */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* find tcb with sctp_setprim.ssp_assoc_id */ IPSCTP_SOCK_GETCB_BY_ASID(prim_info->ssp_assoc_id); if (IP_NULL == tcb) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF); return IPNET_ERRNO(EINVAL); } if ((IP_NULL == tcb->primary_transport) || (*optlen < IPCOM_SA_LEN_GET(&tcb->primary_transport->paddr.sa))) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No primary transport or address length error."IP_LF); return IPNET_ERRNO(EINVAL); } /* copy the primary destination address from tcb */ (void)ipcom_memcpy((void *)&prim_info->ssp_addr, (void *)&tcb->primary_transport->paddr, IPCOM_SA_LEN_GET(&tcb->primary_transport->paddr.sa)); *optlen = sizeof(struct Ipsctp_setprim); #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Get socket primary address option OK."IP_LF); #endif return 0; } /* *=========================================================================== * ipsctp_getsockopt_sctp_status *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer to store the value of the option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_getsockopt_sctp_status(Ipsctp_socket *sock, void *optval, Ip_socklen_t *optlen) { struct Ipsctp_status *status = (struct Ipsctp_status *)optval; Ipsctp_instance *inst = IP_NULL; Ipsctp_transport *tp = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_status) > *optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* check instance pointer */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* find tcb with sctp_status.sstat_assoc_id */ IPSCTP_SOCK_GETCB_BY_ASID(status->sstat_assoc_id); if (IP_NULL == tcb) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF); return IPNET_ERRNO(EINVAL); } /* set status value to the return buffer */ status->sstat_state = tcb->state; status->sstat_rwnd = ipsctp_calc_rwnd(tcb); status->sstat_unackdata = (Ip_u16)ipsctp_list_size(&tcb->transmitted_chunk_list); status->sstat_penddata = (Ip_u16)IPSCTP_RECVBUF_QUEUE_CNT(tcb); status->sstat_instrms = tcb->streamincnt; status->sstat_outstrms = tcb->streamoutcnt; status->sstat_fragmentation_point = 0; /* TODO(td): */ /* get and check primary transport */ tp = tcb->primary_transport; if (IP_NULL == tp) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No primary transport."IP_LF); return IPNET_ERRNO(EINVAL); } switch (tp->paddr.sa.sa_family) { #ifdef IPCOM_USE_INET case IP_AF_INET: (void)ipcom_memcpy((void *)&status->sstat_primary.spinfo_address, (void *)&tp->paddr, sizeof(struct Ip_sockaddr_in)); break; #endif #ifdef IPCOM_USE_INET6 case IP_AF_INET6: (void)ipcom_memcpy((void *)&status->sstat_primary.spinfo_address, (void *)&tp->paddr, sizeof(struct Ip_sockaddr_in6)); break; #endif default: IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : invalid transport af."IP_LF); break; } status->sstat_primary.spinfo_state = tp->state; status->sstat_primary.spinfo_cwnd = tp->cwnd; status->sstat_primary.spinfo_srtt = tp->srtt; status->sstat_primary.spinfo_rto = tp->rto; status->sstat_primary.spinfo_mtu = tp->pmtu; *optlen = sizeof(struct Ipsctp_status); #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Get sctp status option OK."IP_LF); #endif return 0; } /* *=========================================================================== * ipsctp_getsockopt_context *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer to store the value of the option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_getsockopt_context(Ipsctp_socket *sock, void *optval, Ip_socklen_t *optlen) { struct Ipsctp_assoc_value *as_val = (struct Ipsctp_assoc_value *)optval; Ipsctp_instance *inst = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_assoc_value) > *optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* check instance pointer */ IPSCTP_SOCK_INST_CHECK(sock, inst); if (as_val->assoc_id) { /* find tcb with sctp_assoc_value.assoc_id */ IPSCTP_SOCK_GETCB_BY_ASID(as_val->assoc_id); if (IP_NULL == tcb) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF); return IPNET_ERRNO(EINVAL); } as_val->assoc_value = tcb->context; } else as_val->assoc_value = inst->context; *optlen = sizeof(struct Ipsctp_assoc_value); #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Get socket context option OK."IP_LF); #endif return 0; } #if 0 /* *=========================================================================== * ipsctp_getsockopt_active_key *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer to store the value of the option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_getsockopt_active_key(Ipsctp_socket *sock, void *optval, Ip_socklen_t *optlen) { struct Ipsctp_authkeyid *keyid = (struct Ipsctp_authkeyid *)optval; Ipsctp_instance *inst = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_authkeyid) > *optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* check instance pointer */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* find tcb with sctp_authkeyid.scact_assoc_id */ IPSCTP_SOCK_GETCB_BY_ASID(keyid->scact_assoc_id); if (!tcb && keyid->scact_assoc_id && ipsctp_style(sock, UDP)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF); return IPNET_ERRNO(EINVAL); } /* TODO(td): here need lock the data */ /* get the values to instance or tcb */ if (tcb) keyid->scact_keynumber = 0; else keyid->scact_keynumber = 0; *optlen = sizeof(struct Ipsctp_authkeyid); #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Get socket active key option OK."IP_LF); #endif return 0; } /* *=========================================================================== * ipsctp_getsockopt_peer_auth_chunks *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer to store the value of the option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_getsockopt_peer_auth_chunks(Ipsctp_socket *sock, void *optval, Ip_socklen_t *optlen) { struct Ipsctp_authkeyid *keyid = (struct Ipsctp_authkeyid *)optval; Ipsctp_instance *inst = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_authkeyid) > *optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* check instance pointer */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* find tcb with sctp_authkeyid.scact_assoc_id */ IPSCTP_SOCK_GETCB_BY_ASID(keyid->scact_assoc_id); if (!tcb && keyid->scact_assoc_id && ipsctp_style(sock, UDP)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF); return IPNET_ERRNO(EINVAL); } *optlen = sizeof(struct Ipsctp_authkeyid); #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Get socket peer auth chunks option OK."IP_LF); #endif return 0; } /* *=========================================================================== * ipsctp_getsockopt_local_auth_chunks *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer to store the value of the option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_getsockopt_local_auth_chunks(Ipsctp_socket *sock, void *optval, Ip_socklen_t *optlen) { struct Ipsctp_authkeyid *keyid = (struct Ipsctp_authkeyid *)optval; Ipsctp_instance *inst = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_authkeyid) > *optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* check instance pointer */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* find tcb with sctp_authkeyid.scact_assoc_id */ IPSCTP_SOCK_GETCB_BY_ASID(keyid->scact_assoc_id); if (!tcb && keyid->scact_assoc_id && ipsctp_style(sock, UDP)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF); return IPNET_ERRNO(EINVAL); } *optlen = sizeof(struct Ipsctp_authkeyid); #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Get socket local auth chunks option OK."IP_LF); #endif return 0; } #endif /* *=========================================================================== * ipsctp_getsockopt_peer_addr_params *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer to store the value of the option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_getsockopt_peer_addr_params(Ipsctp_socket *sock, void *optval, Ip_socklen_t *optlen) { struct Ipsctp_paddrparams *params = (struct Ipsctp_paddrparams *)optval; Ipsctp_instance *inst = IP_NULL; Ipsctp_transport *tp = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_paddrparams) > *optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* check instance pointer */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* find tcb with sctp_paddrparams.spp_assoc_id */ IPSCTP_SOCK_GETCB_BY_ASID(params->spp_assoc_id); if (!tcb && params->spp_assoc_id && ipsctp_style(sock, UDP)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF); return IPNET_ERRNO(EINVAL); } /* if params.spp_address is valid, then * find the transport with it. */ if (ipsctp_is_addr_valid((Ipsctp_addr *)¶ms->spp_address)) { IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : lookup transport address %s."IP_LF, ipsctp_addr_ntop((Ipsctp_addr *)¶ms->spp_address, ipnet->log_buf, 256)); tp = ipsctp_transport_lookup(tcb, (IP_CONST Ipsctp_addr *)¶ms->spp_address); } /* get peer address parameters and set to the return buffer */ if (tp) { /* get from transport */ params->spp_hbinterval = tp->hbinterval; params->spp_pathmaxrxt = (Ip_u16)tp->err_thresh; params->spp_pathmtu = tp->pmtu; } else if (tcb) { /* get from association */ params->spp_hbinterval = tcb->hb_interval; params->spp_pathmaxrxt = tcb->max_transport_retrans; params->spp_pathmtu = tcb->pmtu; } else { /* get from instance */ params->spp_hbinterval = inst->paddrparam.spp_hbinterval; params->spp_pathmaxrxt = inst->paddrparam.spp_pathmaxrxt; params->spp_pathmtu = inst->paddrparam.spp_pathmtu; } *optlen = sizeof(struct Ipsctp_paddrparams); #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Get socket peer address params option OK."IP_LF); #endif return 0; } /* *=========================================================================== * ipsctp_getsockopt_events *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer to store the value of the option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_getsockopt_events(Ipsctp_socket *sock, void *optval, Ip_socklen_t *optlen) { #define IPSCTP_EVET_GET(evt_name, evt_flag) \ do { \ if (IP_BIT_ISSET(inst->feature_flags, evt_flag)) \ evt_sub->Ipsctp_ ## evt_name = 1; \ else \ evt_sub->Ipsctp_ ## evt_name = 0; \ } while(0) struct Ipsctp_event_subscribe *evt_sub = (struct Ipsctp_event_subscribe *)optval; Ipsctp_instance *inst = IP_NULL; if ((IP_NULL == sock) || (IP_NULL == evt_sub) || (sizeof(struct Ipsctp_event_subscribe) > *optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."); return IPNET_ERRNO(EINVAL); } /* check instance pointer */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* get the status value from instance */ IPSCTP_EVET_GET(data_io_event, IPSCTP_EVNT_FLAGS_DATA_IO); IPSCTP_EVET_GET(association_event, IPSCTP_EVNT_FLAGS_ASSOCIATION); IPSCTP_EVET_GET(address_event, IPSCTP_EVNT_FLAGS_ADDRESS); IPSCTP_EVET_GET(send_failure_event, IPSCTP_EVNT_FLAGS_SEND_FAILURE); IPSCTP_EVET_GET(peer_error_event, IPSCTP_EVNT_FLAGS_PEER_ERROR); IPSCTP_EVET_GET(shutdown_event, IPSCTP_EVNT_FLAGS_SHUTDOWN); IPSCTP_EVET_GET(partial_delivery_event, IPSCTP_EVNT_FLAGS_PARTIAL_DELIVERY); IPSCTP_EVET_GET(adaptation_layer_event, IPSCTP_EVNT_FLAGS_ADAPTATION_LAYER); IPSCTP_EVET_GET(authentication_event, IPSCTP_EVNT_FLAGS_AUTHENTICATION); *optlen = sizeof(struct Ipsctp_event_subscribe); #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Get socket events option OK."IP_LF); #endif #undef IPSCTP_EVET_GET return 0; } /* *=========================================================================== * ipsctp_getsockopt_initmsg *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer to store the value of the option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_getsockopt_initmsg(Ipsctp_socket *sock, void *optval, Ip_socklen_t *optlen) { struct Ipsctp_initmsg *init_msg = (struct Ipsctp_initmsg *)optval; Ipsctp_instance *inst = IP_NULL; if ((IP_NULL == sock) || (IP_NULL == init_msg) || (sizeof(struct Ipsctp_initmsg) > *optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."); return IPNET_ERRNO(EINVAL); } /* check instance pointer */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* get the init message from instance */ (void)ipcom_memcpy(init_msg, &inst->initmsg, sizeof(inst->initmsg)); *optlen = sizeof(struct Ipsctp_initmsg); #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Get init message option OK."IP_LF); #endif return 0; } /* *=========================================================================== * ipsctp_getsockopt_maxseg *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer to store the value of the option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_getsockopt_maxseg(Ipsctp_socket *sock, void *optval, Ip_socklen_t *optlen) { struct Ipsctp_assoc_value *as_val = (struct Ipsctp_assoc_value *)optval; Ipsctp_instance *inst = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; Ip_u32 hdr_len = 0; if ((IP_NULL == sock) || (IP_NULL == as_val) || (sizeof(struct Ipsctp_assoc_value) > *optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."); return IPNET_ERRNO(EINVAL); } /* check instance pointer */ IPSCTP_SOCK_INST_CHECK(sock, inst); #ifdef IPCOM_USE_INET if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V4)) { hdr_len = sizeof(Ipsctp_data_chunk_hdr) + sizeof(Ipsctp_sctp_hdr) + sizeof(Ipnet_pkt_ip); } #endif #ifdef IPCOM_USE_INET6 if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V6)) { hdr_len = sizeof(Ipsctp_data_chunk_hdr) + sizeof(Ipsctp_sctp_hdr) + sizeof(Ipnet_pkt_ip6); } #endif /* find tcb with sctp_assoc_value.assoc_id */ IPSCTP_SOCK_GETCB_BY_ASID(as_val->assoc_id); /* get the values to instance or tcb */ if (IP_NULL != tcb) { if (tcb->frag_point > hdr_len) as_val->assoc_value = tcb->frag_point - hdr_len; else as_val->assoc_value = 0; } else { if (inst->frag_point > hdr_len) as_val->assoc_value = inst->frag_point - hdr_len; else as_val->assoc_value = 0; } *optlen = sizeof(struct Ipsctp_assoc_value); #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Get max segment option OK."IP_LF); #endif return 0; } /* *=========================================================================== * ipsctp_getsockopt_delay_ack *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer to store the value of the option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_getsockopt_delay_ack(Ipsctp_socket *sock, void *optval, Ip_socklen_t *optlen) { struct Ipsctp_sack_info *ssi = (struct Ipsctp_sack_info *)optval; Ipsctp_instance *inst = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_sack_info) > *optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* check instance pointer */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* find tcb with Ipsctp_sack_info.sack_assoc_id */ IPSCTP_SOCK_GETCB_BY_ASID(ssi->sack_assoc_id); /* set the values to instance or tcb */ if (IP_NULL != tcb) { ssi->sack_delay = tcb->delayed_ack; ssi->sack_freq = tcb->sack_freq; } else { ssi->sack_delay = inst->sack_info.sack_delay; ssi->sack_freq = inst->sack_info.sack_freq; } *optlen = sizeof(struct Ipsctp_sack_info); #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Get socket delay ack option OK."IP_LF); #endif return 0; } /* *=========================================================================== * ipsctp_getsockopt_max_burst *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer to store the value of the option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_getsockopt_max_burst(Ipsctp_socket *sock, void *optval, Ip_socklen_t *optlen) { struct Ipsctp_assoc_value *as_val = (struct Ipsctp_assoc_value *)optval; Ipsctp_instance *inst = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_assoc_value) > *optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* check instance pointer */ IPSCTP_SOCK_INST_CHECK(sock, inst); if (as_val->assoc_id) { /* find tcb with sctp_assoc_value.assoc_id */ IPSCTP_SOCK_GETCB_BY_ASID(as_val->assoc_id); if (IP_NULL == tcb) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF); return IPNET_ERRNO(EINVAL); } as_val->assoc_value = tcb->max_burst; } else as_val->assoc_value = inst->max_burst; *optlen = sizeof(struct Ipsctp_assoc_value); #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Get socket context option OK."IP_LF); #endif return 0; } /* *=========================================================================== * ipsctp_getsockopt_peer_addrs_size *=========================================================================== * Description: get all peer address's length * Parameters: sock - the set socket descriptor. * optval - the buffer to store the value of the option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_getsockopt_peer_addrs_size(Ipsctp_socket *sock, void *optval, Ip_socklen_t *optlen) { Ipsctp_instance *inst = IP_NULL; Ipsctp_transport *tp = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; Ip_u32 as_id = IP_NULL; Ip_u32 tp_size = 0; int sa_len = 0; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(Ip_u32) > *optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } as_id = *((Ip_u32 *)optval); /* check instance pointer */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* find tcb with association id */ IPSCTP_SOCK_GETCB_BY_ASID(as_id); if (IP_NULL == tcb) goto no_paddrs; /* search in transport address list */ ipsctp_list_for_each(&tcb->transport_list, tp, Ipsctp_transport) { sa_len = ipsctp_get_sa_family_size(inst, tp->paddr.sa.sa_family); if (0 > sa_len) return IPNET_ERRNO(EINVAL); tp_size += sa_len; } no_paddrs: *((Ip_u32 *)optval) = tp_size; *optlen = sizeof(Ip_u32); #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Get socket peer address size option OK."IP_LF); #endif return 0; } /* *=========================================================================== * ipsctp_getsockopt_peer_addrs *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer to store the value of the option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_getsockopt_peer_addrs(Ipsctp_socket *sock, void *optval, Ip_socklen_t *optlen) { struct Ipsctp_getaddrs *paddr = (struct Ipsctp_getaddrs *)optval; Ipsctp_instance *inst = IP_NULL; Ipsctp_transport *tp = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; void *pbuf = IP_NULL; int total = 0; int sa_len = 0; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_getaddrs) > *optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* check instance pointer */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* find tcb with association id */ IPSCTP_SOCK_GETCB_BY_ASID(paddr->sget_assoc_id); if (IP_NULL == tcb) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF); return IPNET_ERRNO(EINVAL); } total = (int)(*optlen - sizeof(struct Ipsctp_getaddrs)); *optlen = sizeof(struct Ipsctp_getaddrs); pbuf = (void *)&paddr->sget_addrs[0]; /* copy the transport address list */ ipsctp_list_for_each(&tcb->transport_list, tp, Ipsctp_transport) { sa_len = ipsctp_get_sa_family_size(inst, tp->paddr.sa.sa_family); if (0 > sa_len) return IPNET_ERRNO(EINVAL); if (total < sa_len) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No enough memory."IP_LF); return IPNET_ERRNO(EINVAL); } #ifdef IPCOM_USE_INET6 if ((IP_BIT_ISSET(inst->feature_flags, IPSCTP_INST_FLAG_WANT_MAPPED_V4)) && (IP_AF_INET == tp->paddr.sa.sa_family)) /* TODO(td) : if change V4&V6. */ ipsctp_sin_v4_map_v6((void *)&tp->paddr.sa, pbuf); else #endif ipcom_memcpy((void *)pbuf, (void *)&tp->paddr, sa_len); *optlen += (Ip_socklen_t)sa_len; pbuf = (Ip_u8 *)pbuf + sa_len; total -= sa_len; } #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Get socket peer address option OK."IP_LF); #endif return 0; } /* *=========================================================================== * ipsctp_getsockopt_local_addrs_size *=========================================================================== * Description: get all local address's length * Parameters: sock - the set socket descriptor. * optval - the buffer to store the value of the option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_getsockopt_local_addrs_size(Ipsctp_socket *sock, void *optval, Ip_socklen_t *optlen) { Ipsctp_laddr_entry *laddrs = IP_NULL; Ipsctp_instance *inst = IP_NULL; Ip_u32 la_size = 0; int sa_len = 0; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(Ip_u32) > *optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } if (IP_BIT_ISFALSE(sock->flags, IPNET_SOCKET_FLAG_BOUND)) goto valid_ret; /* check instance pointer */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* loop for all the address entry if BOUNDALL case * NOTE : maybe not the same address list as the association setup */ if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUNDALL)) { Ipnet_netif *netif = IP_NULL; Ip_u32 i = 0; /* * IPNET_NETIF_FOR_EACH_ON_VR sets netif to NULL after the for loop, * this is a performance optimized defination */ /* coverity[result_independent_of_operands] */ IPNET_NETIF_FOR_EACH_ON_VR(netif, sock->vr_index, i) { if (IP_BIT_ISSET(netif->ipcom.flags, IP_IFF_LOOPBACK)) continue; #ifdef IPCOM_USE_INET if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V4)) { Ipnet_ip4_addr_entry *addr4 = IP_NULL; for (addr4 = netif->inet4_addr_list; addr4; addr4 = addr4->next) { /* only for unicast case */ if (IPNET_ADDR_TYPE_UNICAST != addr4->type) continue; sa_len = ipsctp_get_sa_family_size(inst, IP_AF_INET); if (0 > sa_len) return IPNET_ERRNO(EINVAL); la_size += sa_len; } } #endif #ifdef IPCOM_USE_INET6 if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V6)) { Ipnet_ip6_addr_entry *addr6 = IP_NULL; for (addr6 = netif->inet6_addr_list; addr6; addr6 = addr6->next) { /* only for unicast case */ if (IPNET_ADDR_TYPE_UNICAST != addr6->type) continue; sa_len = ipsctp_get_sa_family_size(inst, IP_AF_INET6); if (0 > sa_len) return IPNET_ERRNO(EINVAL); la_size += sa_len; } } #endif } } else { ipsctp_list_for_each(&inst->laddr_list, laddrs, Ipsctp_laddr_entry) { sa_len = ipsctp_get_sa_family_size(inst, laddrs->laddr.sa.sa_family); if (0 > sa_len) return IPNET_ERRNO(EINVAL); la_size += sa_len; } } #if 0 /* maybe only bind 127.0.0.1 */ ip_assert(0 != la_size); #endif valid_ret: *((Ip_u32 *)optval) = la_size; *optlen = sizeof(Ip_u32); #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Get socket local address size option OK."IP_LF); #endif return 0; } /* *=========================================================================== * ipsctp_getsockopt_local_addrs *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer to store the value of the option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_getsockopt_local_addrs(Ipsctp_socket *sock, void *optval, Ip_socklen_t *optlen) { struct Ipsctp_getaddrs *paddr = (struct Ipsctp_getaddrs *)optval; Ipsctp_instance *inst = IP_NULL; void *pbuf = IP_NULL; int total = 0; int sa_len = 0; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_getaddrs) > *optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* check instance pointer */ IPSCTP_SOCK_INST_CHECK(sock, inst); total = (int)(*optlen - sizeof(struct Ipsctp_getaddrs)); /* get from ipsctp_getsockopt_local_addrs_size */ *optlen = sizeof(struct Ipsctp_getaddrs); pbuf = (void *)&paddr->sget_addrs[0]; /* loop for all the address entry if BOUNDALL case * NOTE : maybe not the same address list as the association setup */ if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUNDALL)) { Ipsctp_af_ops *af_ops = IP_NULL; Ipnet_netif *netif = IP_NULL; Ip_u32 i = 0; Ipsctp_addr addr_tmp; /* * IPNET_NETIF_FOR_EACH_ON_VR sets netif to NULL after the for loop, * this is a performance optimized defination */ /* coverity[result_independent_of_operands] */ IPNET_NETIF_FOR_EACH_ON_VR(netif, sock->vr_index, i) { if (IP_BIT_ISSET(netif->ipcom.flags, IP_IFF_LOOPBACK)) continue; #ifdef IPCOM_USE_INET if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V4)) { Ipnet_ip4_addr_entry *addr4 = IP_NULL; af_ops = ipsctp_get_af_ops(IP_AF_INET); if (IP_NULL == af_ops) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No address family operation."IP_LF); return IPNET_ERRNO(EINVAL); } for (addr4 = netif->inet4_addr_list; addr4; addr4 = addr4->next) { /* only for unicast case */ if (IPNET_ADDR_TYPE_UNICAST != addr4->type) continue; sa_len = ipsctp_get_sa_family_size(inst, IP_AF_INET); if (0 > sa_len) return IPNET_ERRNO(EINVAL); if (total < sa_len) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No enough memory."IP_LF); return IPNET_ERRNO(EINVAL); } af_ops->load_soaddr((void *)&addr4->ipaddr_n, inst->lport, &addr_tmp); #ifdef IPCOM_USE_INET6 if (IP_BIT_ISSET(inst->feature_flags, IPSCTP_INST_FLAG_WANT_MAPPED_V4)) ipsctp_sin_v4_map_v6((void *)&addr_tmp.sa, pbuf); else #endif ipcom_memcpy((void *)pbuf, (void *)&addr_tmp.sa, sa_len); *optlen += (Ip_socklen_t)sa_len; pbuf = (Ip_u8 *)pbuf + sa_len; total -= sa_len; } } #endif #ifdef IPCOM_USE_INET6 if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V6)) { Ipnet_ip6_addr_entry *addr6 = IP_NULL; sa_len = sizeof(struct Ip_sockaddr_in6); af_ops = ipsctp_get_af_ops(IP_AF_INET6); if (IP_NULL == af_ops) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No address family operation."IP_LF); return IPNET_ERRNO(EINVAL); } for (addr6 = netif->inet6_addr_list; addr6; addr6 = addr6->next) { /* only for unicast case */ if (IPNET_ADDR_TYPE_UNICAST != addr6->type) continue; if (total < sa_len) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No enough memory."IP_LF); return IPNET_ERRNO(EINVAL); } af_ops->load_soaddr((void *)&addr6->addr, inst->lport, &addr_tmp); ipcom_memcpy((void *)pbuf, (void *)&addr_tmp.sin6, sizeof(struct Ip_sockaddr_in6)); *optlen += (Ip_socklen_t)sa_len; pbuf = (Ip_u8 *)pbuf + sa_len; total -= sa_len; } } #endif } } else { Ipsctp_laddr_entry *laddrs = IP_NULL; /* copy all the local address */ ipsctp_list_for_each(&inst->laddr_list, laddrs, Ipsctp_laddr_entry) { sa_len = ipsctp_get_sa_family_size(inst, laddrs->laddr.sa.sa_family); if (0 > sa_len) return IPNET_ERRNO(EINVAL); if (total < sa_len) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No enough memory."IP_LF); return IPNET_ERRNO(EINVAL); } #ifdef IPCOM_USE_INET6 if ((IP_BIT_ISSET(inst->feature_flags, IPSCTP_INST_FLAG_WANT_MAPPED_V4)) && (IP_AF_INET == laddrs->laddr.sa.sa_family)) /* TODO(td) : if change V4&V6. */ ipsctp_sin_v4_map_v6((void *)&laddrs->laddr.sa, pbuf); else #endif ipcom_memcpy((void *)pbuf, (void *)&laddrs->laddr.sa, sa_len); *optlen += (Ip_socklen_t)sa_len; pbuf = (Ip_u8 *)pbuf + sa_len; total -= sa_len; } } #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Get socket peer address option OK."IP_LF); #endif return 0; } /* *=========================================================================== * ipsctp_getsockopt_addrs_len *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer to store the value of the option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_getsockopt_addrs_len(Ipsctp_socket *sock, void *optval, Ip_socklen_t *optlen) { struct Ipsctp_assoc_value *as_val = (struct Ipsctp_assoc_value *)optval; int addrs_len = 0; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_assoc_value) > *optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } addrs_len = ipsctp_get_sa_family_size(IP_NULL, as_val->assoc_value); if (addrs_len < 0) return IPNET_ERRNO(EINVAL); as_val->assoc_value = addrs_len; *optlen = sizeof(struct Ipsctp_assoc_value); #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Get socket peer address length option OK."IP_LF); #endif return 0; } /* *=========================================================================== * ipsctp_getsockopt_peeloff *=========================================================================== * Description: Branch off an association from the listen socket. * Parameters: listen_sock - the socket descriptor. * optval - the buffer to store the value of the option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_getsockopt_peeloff(Ipsctp_socket *listen_sock, void *optval, Ip_socklen_t *optlen) { struct Ipsctp_peeloff *po = (struct Ipsctp_peeloff *)optval; Ipsctp_chunk_recv *ch_recv = IP_NULL; Ipsctp_chunk_recv *ch_recv_t = IP_NULL; Ipsctp_instance *listen_inst = IP_NULL; Ipsctp_instance *inst = IP_NULL; Ipsctp_transport *tp = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; Ip_u32 i = 0; Ipsctp_chunk_send *ch_send = IP_NULL; Ipsctp_chunk_send *ch_send_t = IP_NULL; Ip_fd fd = IP_INVALID_SOCKET; Ipsctp_socket *sock = IP_NULL; Ipsctp_laddr_entry *laddr_cur = IP_NULL; Ipcom_list rcv_list; struct Ip_sockaddr name; int ret = 0; Ipsctp_timer *cur_tmr = IP_NULL; Ipsctp_timer *cur_tmr_t = IP_NULL; if ((IP_NULL == listen_sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_peeloff) > *optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } listen_inst = inst = listen_sock->sctp_inst; /* 1,peeloff only branch from listen socket. * 2,peeloff can not branch again from peeloffed socket. */ if ((0 == po->assoc_id) || (IP_BIT_ISFALSE(inst->inst_flags, IPSCTP_SOCKET_LISTENING)) || (IP_BIT_ISSET(listen_inst->inst_flags, IPSCTP_SOCKET_FLAG_PEELOFF))) return IPNET_ERRNO(EINVAL); /* find tcb with sctp_peeloff.assoc_id */ IPSCTP_SOCK_GETCB_BY_ASID(po->assoc_id); if ((IP_NULL == tcb) || (listen_sock != tcb->sock)) { IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : No TCB for peel off(0x%x)."IP_LF, po->assoc_id); return IPNET_ERRNO(ENOTCONN); } /* client state */ if (IPSCTP_STATE_ESTABLISHED > tcb->state) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : association not open for peeloff."IP_LF); return IPNET_ERRNO(ENOTCONN); } /* 9.2 draft-ietf-tsvwg-sctpsocket-19 * The application uses sctp_peeloff() call to branch off an association * into a separate socket (Note the semantics are somewhat changed from * the traditional one-to-one style accept() call). Note that the new * socket is a one-to-one style socket. Thus it will be confined to * operations allowed for a one-to-one style socket. * NOTE : !! FreeBSD and Linux does not do like this */ fd = IP_STACK(sys_socket)(listen_sock->ipcom.domain, IP_SOCK_STREAM, listen_sock->proto, IP_FALSE); if (IP_INVALID_SOCKET == fd) { IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : peeloff new socket for listen(%u) error."IP_LF, listen_sock->ipcom.fd); goto errout; } po->fd = fd; sock = ipstack->sockets[fd]; inst = sock->sctp_inst; ip_assert(sock != IP_NULL); ip_assert(sock->sctp_inst != IP_NULL); /* Copy some flags from the listen socket */ sock->flags |= listen_sock->flags & ( IPSCTP_STACK_CONST(SOCKET_FLAG_NONBLOCKING) | IPSCTP_STACK_CONST(SOCKET_FLAG_REUSEADDR) | IPSCTP_STACK_CONST(SOCKET_FLAG_REUSEPORT) | IPSCTP_STACK_CONST(SOCKET_FLAG_DONTROUTE) | IPSCTP_STACK_CONST(SOCKET_FLAG_RECV_IF) | IPSCTP_STACK_CONST(SOCKET_FLAG_TCP_NODELAY)); #ifdef IPNET sock->vr_index = listen_sock->vr_index; sock->flags |= listen_sock->flags & IPSCTP_STACK_CONST(SOCKET_FLAG_RECVPKTINFO); #endif sock->dport = listen_sock->dport; sock->recv_max_bytes = listen_sock->recv_max_bytes; sock->send_max_bytes = listen_sock->send_max_bytes; sock->keep_alive_probe_delay = listen_sock->keep_alive_probe_delay; /* set new socket accepting flag */ IP_BIT_SET(sock->sctp_inst->inst_flags, IPSCTP_SOCKET_ACCEPTING); sock->sctp_inst->inst_flags |= IPSCTP_SOCKET_FLAG_PEELOFF; sock->sctp_inst->linger_time = listen_sock->sctp_inst->linger_time; sock->sctp_inst->feature_flags = listen_sock->sctp_inst->feature_flags; sock->sctp_inst->frag_point = listen_sock->sctp_inst->frag_point; sock->sctp_inst->pd_point = listen_sock->sctp_inst->pd_point; sock->sctp_inst->context = listen_sock->sctp_inst->context; sock->sctp_inst->congestion_control_module = listen_sock->sctp_inst->congestion_control_module; sock->sctp_inst->autoclose_time = listen_sock->sctp_inst->autoclose_time; ipsctp_list_for_each(&listen_sock->sctp_inst->laddr_list, laddr_cur, Ipsctp_laddr_entry) { ret = ipsctp_add_laddr(sock->sctp_inst, &laddr_cur->laddr, IPSCTP_LADDR_NORMAL); if (0 != ret) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : copy local address to peeloff socket error."IP_LF); } if (IP_BIT_ISSET(listen_sock->sctp_inst->inst_flags, IPSCTP_SOCKET_BOUNDALL)) { IP_BIT_SET(sock->sctp_inst->inst_flags, IPSCTP_SOCKET_BOUNDALL); } sock->sctp_inst->lport = listen_sock->sctp_inst->lport; #ifdef IPCOM_USE_INET if (sock->ipcom.domain == IP_AF_INET) { struct Ip_sockaddr_in *name_in = (struct Ip_sockaddr_in *)&name; sock->ip4->saddr_n = listen_sock->ip4->saddr_n; #ifdef IPNET sock->ip4->flags = listen_sock->ip4->flags; sock->ip4->daddr_n = listen_sock->ip4->daddr_n; #else sock->d->ip4.daddr_n = listen_sock->d->ip4.daddr_n; #endif name_in->sin_family = IP_AF_INET; name_in->sin_port = ip_htons(sock->sctp_inst->lport); } else #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 if (sock->ipcom.domain == IP_AF_INET6) { struct Ip_sockaddr_in6 *name_in6 = (struct Ip_sockaddr_in6 *)&name; sock->ip6->saddr = listen_sock->ip6->saddr; #ifdef IPNET sock->ip6->flags = listen_sock->ip6->flags; sock->ip6->daddr = listen_sock->ip6->daddr; #else sock->d->ip6.daddr = sock->d->ip6.daddr; #endif name_in6->sin6_family = IP_AF_INET6; name_in6->sin6_flowinfo = 0; name_in6->sin6_port = ip_htons(sock->sctp_inst->lport); name_in6->sin6_scope_id = 0; } else #endif /* IPCOM_USE_INET6 */ { /* Unsupported domain */ IP_PANIC(); goto errout; } /* Only need to add the reference count, for the release use. * for Ipnet_bound_socket is a local structure. * Only need (domain/proto/vr/local_port) */ ret = ipnet_sock_bind_to_port(sock, &name, IP_FALSE); if (ret < 0) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : bind port to peeloff socket error."IP_LF); goto errout; } /* NOTE : must set BOUND and CONNECTED flag */ IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_BOUND | IPNET_SOCKET_FLAG_CONNECTED); /* The owner of the new socket must be the same as the owner of the listen socket */ sock->ipcom.pid = listen_sock->ipcom.pid; if ((IP_BIT_ISSET(tcb->instance->inst_flags, IPSCTP_SOCKET_FLAG_AUTOCLOSE))) { IP_BIT_CLR(sock->sctp_inst->inst_flags, IPSCTP_SOCKET_FLAG_AUTOCLOSE); sock->sctp_inst->autoclose_time = 0; (void)ipsctp_timer_remove(tcb->instance, tcb, IP_NULL, IPSCTP_TIMO_AUTOCLOSE); } /* change the timer instance pointer if needed. */ tcb->sock = sock; if (tcb->dack_timer.inst) { tcb->dack_timer.inst = sock->sctp_inst; } if (tcb->asconf_timer.inst) { tcb->asconf_timer.inst = sock->sctp_inst; } if (tcb->shut_guard_timer.inst) { tcb->shut_guard_timer.inst = sock->sctp_inst; } if (tcb->autoclose_timer.inst) { tcb->autoclose_timer.inst = sock->sctp_inst; } if (tcb->delayed_event_timer.inst) { tcb->delayed_event_timer.inst = sock->sctp_inst; } if (tcb->delete_prim_timer.inst) { tcb->delete_prim_timer.inst = sock->sctp_inst; } ipsctp_list_for_each(&tcb->transport_list, tp, Ipsctp_transport) { if (tp->rxt_timer.inst) { tp->rxt_timer.inst = sock->sctp_inst; } if (tp->hb_timer.inst) { tp->hb_timer.inst = sock->sctp_inst; } if (tp->fr_timer.inst) { tp->fr_timer.inst = sock->sctp_inst; } } #ifndef IPCOM_FORWARDER_NAE ipcom_list_insert_first(&ipsctp->active_head, &inst->active); /* peel off all the timers, Ipcom_list MUST be the first elem */ ipsctp_list_for_each_safe(&listen_inst->timers, cur_tmr, cur_tmr_t, Ipsctp_timer) { if (inst == cur_tmr->inst) { ipcom_list_remove(&cur_tmr->list); ipcom_list_insert_last(&inst->timers, &cur_tmr->list); } } #endif /* change the list */ IPSCTP_DATA_TCBLOCK(listen_inst->tcb_lock); IPSCTP_DATA_LOCK(listen_sock->rcv_lock); ipcom_list_remove(&tcb->next); IPSCTP_DATA_UNLOCK(listen_sock->rcv_lock); IPSCTP_DATA_TCBUNLOCK(listen_inst->tcb_lock); if (ipcom_atomic_sub_and_return(&listen_sock->ref_count, 1) < 1) { ip_assert(IP_FALSE); IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : UDP ref_count < 1."IP_LF); } /* ?? */ IPSCTP_DATA_TCBLOCK(inst->tcb_lock); IPSCTP_DATA_LOCK(sock->rcv_lock); ipcom_list_insert_last(&sock->sctp_inst->assoc_list, &tcb->next); IPSCTP_DATA_UNLOCK(sock->rcv_lock); IPSCTP_DATA_TCBUNLOCK(inst->tcb_lock); /* given a reference to the new socket */ ipcom_atomic_inc(&sock->ref_count); ipcom_list_init(&rcv_list); /* decrease the listen socket send buffer and increase the new socket send buffer. * NOTE : it has ensure the snd_bytes < send_max_bytes */ ipsctp_list_for_each_safe(&tcb->transmitted_chunk_list, ch_send, ch_send_t, Ipsctp_chunk_send) { IPSCTP_ATOMIC_ZERO_SUB(listen_sock->snd_bytes, ch_send->length); (void)ipcom_atomic_add_and_return(&sock->snd_bytes, ch_send->length); } for (i = 0; i < tcb->streamoutcnt; i++) { ipsctp_list_for_each_safe(&tcb->streams_out[i].chunk_list, ch_send, ch_send_t, Ipsctp_chunk_send) { IPSCTP_ATOMIC_ZERO_SUB(listen_sock->snd_bytes, ch_send->length); ipcom_atomic_add(&sock->snd_bytes, ch_send->length); } } IPSCTP_DATA_LOCK(listen_sock->rcv_lock); /* NOTE : no IPSCTP_USE_MULTIBUF define */ ipsctp_list_for_each_safe(&listen_inst->recv_list, ch_recv, ch_recv_t, Ipsctp_chunk_recv) { if (tcb == ch_recv->chunk.tcb) { ipcom_list_remove(&ch_recv->chunk.chunk_next); IPSCTP_RECVBUF_CNT_SUB(listen_sock, IP_NULL, 1); IPSCTP_RECVBUF_LEN_SUB(listen_sock, IP_NULL, ch_recv->length); IPSCTP_RECVBUF_QUEUE_LEN(tcb) -= ch_recv->length; IPSCTP_RECVBUF_QUEUE_CNT(tcb)--; /* add to new sock length */ ipcom_list_insert_last(&rcv_list, &ch_recv->chunk.chunk_next); } } IPSCTP_DATA_UNLOCK(listen_sock->rcv_lock); /* for the new length */ tcb->instance = sock->sctp_inst; /* if the data come between the two LOCK, it will correctly append to * the new socket, becase we have set this. * NOTE : ?? need test ?? */ IPSCTP_DATA_LOCK(sock->rcv_lock); ipsctp_list_for_each_safe(&rcv_list, ch_recv, ch_recv_t, Ipsctp_chunk_recv) { #if defined(IPSCTP_DEBUG) IPSCTP_DBG4(IPSCTP_DBG_ALL, "IPSCTP : chunk type %u, chunk flag %u, chunk length %u, pkt length %u."IP_LF, ch_recv->chunk.chunk_type, ch_recv->chunk.flag, ch_recv->chunk.chunk_end - ch_recv->chunk.chunk_start, ch_recv->chunk.pkt->end - ch_recv->chunk.pkt->start); ipsctp_print_hex((void *)&ch_recv->chunk.pkt->data[ch_recv->chunk.pkt->start], "chunk packet:\n", ch_recv->chunk.pkt->end - ch_recv->chunk.pkt->start); #endif ipcom_list_remove(&ch_recv->chunk.chunk_next); /* add to new sock length */ ipcom_list_insert_last(&inst->recv_list, &ch_recv->chunk.chunk_next); IPSCTP_RECVBUF_CNT(sock, tcb)++; IPSCTP_RECVBUF_LEN_ADD(tcb, ch_recv->length); IPSCTP_RECVBUF_QUEUE_LEN(tcb) += ch_recv->length; IPSCTP_RECVBUF_QUEUE_CNT(tcb)++; } IPSCTP_DATA_UNLOCK(sock->rcv_lock); #if defined(IPSCTP_DEBUG) IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : Get socket peeloff option OK, new fd = %u."IP_LF, fd); #endif return 0; errout: if (sock) ipsctp_drop_sock(sock); po->fd = fd; return IPNET_ERRNO(ECONNREFUSED); } /* *=========================================================================== * ipsctp_setsockopt *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * 7.2. setsockopt(), getsockopt() * * Applications use setsockopt() and getsockopt() to set or retrieve * socket options. Socket options are used to change the default * behavior of sockets calls. They are described in Section 8 * * The syntax is: * * ret = getsockopt(int sd, int level, int optname, void *optval, * socklen_t *optlen); * ret = setsockopt(int sd, int level, int optname, const void *optval, * socklen_t optlen); * * * sd - the socket descriptor. * level - set to IPPROTO_SCTP for all SCTP options. * optname - the option name. * optval - the buffer to store the value of the option. * optlen - the size of the buffer (or the length of the option * returned). * * All socket options set on a 1-to-1 listening sockets also apply all * accepted sockets. All socket options set on a 1-to-many socket using * the assoc_id 0 applies for all future assocs on the socket. * Parameters: sock - the socket descriptor. * level - set to IPPROTO_SCTP for all SCTP options. * optname - the option name. * optval - the buffer to store the value of the option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_PUBLIC int ipsctp_getsockopt(Ipsctp_socket *sock, int level, int optname, void *optval, Ip_socklen_t *optlen) { Ipsctp_instance *inst = IP_NULL; int ret = 0; int val = 0; if ((IP_NULL == sock) || (IP_NULL == (inst = sock->sctp_inst))) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } #if defined(IPSCTP_DEBUG) IPSCTP_DBG3(IPSCTP_DBG_SOCK, "IPSCTP : Get socket option(sock %u, level 0x%x, optname 0x%x)."IP_LF, sock->ipcom.fd, level, optname); #endif if (IP_SOL_SOCKET == level) { /* Handle some socket options that is put at the generic socket level, but really depends on the transport layer */ switch (optname) { case IP_SO_ACCEPTCONN: if (IPSCTP_IS_LISTEN_INSTANCE(inst)) val = 1; break; case IP_SO_RCVLOWAT: val = (int) inst->recv_lowat; break; case IP_SO_SNDLOWAT: val = (int) inst->send_lowat; break; case IP_SO_LINGER: { struct Ip_linger *l = optval; if (*optlen < sizeof(struct Ip_linger)) return IPNET_ERRNO(EINVAL); *optlen = sizeof(struct Ip_linger); if (IPSCTP_INVALID_LINGER_TIME != inst->linger_time) { l->l_onoff = 1; l->l_linger = inst->linger_time; } else { l->l_onoff = 0; l->l_linger = 0; } return 0; } default: /* Let the caller handle this option */ return IPNET_ERRNO(EOPNOTSUPP); } goto set_int_and_return; } /* if level is not SCTP, return to upper layer to handle */ if (IP_IPPROTO_SCTP != level) return IPNET_ERRNO(EOPNOTSUPP); /* process optname cases */ switch (optname) { case IPSCTP_RTOINFO: ret = ipsctp_getsockopt_rtoinfo(sock, optval, optlen); break; case IPSCTP_ASSOCINFO: ret = ipsctp_getsockopt_associnfo(sock, optval, optlen); break; case IPSCTP_DEFAULT_SEND_PARAM: ret = ipsctp_getsockopt_default_send_param(sock, optval, optlen); break; case IPSCTP_GET_PEER_ADDR_INFO: ret = ipsctp_getsockopt_peer_addr_info(sock, optval, optlen); break; case IPSCTP_PRIMARY_ADDR: ret = ipsctp_getsockopt_primary_addr(sock, optval, optlen); break; case IPSCTP_PEER_ADDR_PARAMS: ret = ipsctp_getsockopt_peer_addr_params(sock, optval, optlen); break; case IPSCTP_STATUS: ret = ipsctp_getsockopt_sctp_status(sock, optval, optlen); break; case IPSCTP_CONTEXT: ret = ipsctp_getsockopt_context(sock, optval, optlen); break; #if 0 case IPSCTP_AUTH_ACTIVE_KEY: ret = ipsctp_getsockopt_active_key(sock, optval, optlen); break; case IPSCTP_PEER_AUTH_CHUNKS: ret = ipsctp_getsockopt_peer_auth_chunks(sock, optval, optlen); break; case IPSCTP_LOCAL_AUTH_CHUNKS: ret = ipsctp_getsockopt_local_auth_chunks(sock, optval, optlen); break; #endif case IPSCTP_EVENTS: ret = ipsctp_getsockopt_events(sock, optval, optlen); break; case IPSCTP_INITMSG: ret = ipsctp_getsockopt_initmsg(sock, optval, optlen); break; case IPSCTP_MAXSEG: ret = ipsctp_getsockopt_maxseg(sock, optval, optlen); break; case IPSCTP_DELAYED_SACK: ret = ipsctp_getsockopt_delay_ack(sock, optval, optlen); break; case IPSCTP_MAX_BURST: ret = ipsctp_getsockopt_max_burst(sock, optval, optlen); break; case IPSCTP_GET_PEER_ADDRS: ret = ipsctp_getsockopt_peer_addrs(sock, optval, optlen); break; case IPSCTP_GET_LOCAL_ADDRS: ret = ipsctp_getsockopt_local_addrs(sock, optval, optlen); break; case IPSCTP_GET_PEER_ADDRS_SIZE: ret = ipsctp_getsockopt_peer_addrs_size(sock, optval, optlen); break; case IPSCTP_GET_LOCAL_ADDRS_SIZE: ret = ipsctp_getsockopt_local_addrs_size(sock, optval, optlen); break; case IPSCTP_GET_ADDR_LEN: ret = ipsctp_getsockopt_addrs_len(sock, optval, optlen); break; case IPSCTP_GET_PEELOFF: ret = ipsctp_getsockopt_peeloff(sock, optval, optlen); break; case IPSCTP_NODELAY: if (IP_BIT_ISSET(inst->feature_flags , IPSCTP_INST_FLAGS_NODELAY)) val = 1; else val = 0; goto set_int_and_return; /* This boolean flag is used to enable or disable explicit end of record * (EOR) marking. When this option is enabled, a user may make multiple * send system calls to send a record and must indicate that they are * finished sending a particular record by including on the send the * SCTP_EOR flag. If this boolean flag is disabled then each individual * send system call is considered to have a SCTP_EOR indicator set on it * implicitly without the user having to explicitly add this flag. */ case IPSCTP_EXPLICIT_EOR: if (IP_BIT_ISSET(inst->feature_flags, IPSCTP_INST_FLAG_EXPLICIT_EOR)) val = 1; else val = 0; goto set_int_and_return; /* This option will enable or disable the use of the extended version of * the sctp_sndrcvinfo structure. If this option is disabled, then the * normal sctp_sndrcvinfo structure is returned in all receive message * calls. If this option is enabled then the sctp_extrcvinfo structure * is returned in all receive message calls. */ case IPSCTP_USE_EXT_RCVINFO: if (IP_BIT_ISSET(inst->feature_flags, IPSCTP_INST_FLAG_EXT_RCVINFO)) val = 1; else val = 0; goto set_int_and_return; /* This socket option is a boolean flag which turns on or off mapped V4 * addresses. If this option is turned on and the socket is type * PF_INET6, then IPv4 addresses will be mapped to V6 representation. * If this option is turned off, then no mapping will be done of V4 * addresses and a user will receive both PF_INET6 and PF_INET type * addresses on the socket. */ case IPSCTP_I_WANT_MAPPED_V4_ADDR: if (IP_BIT_ISSET(inst->feature_flags, IPSCTP_INST_FLAG_WANT_MAPPED_V4)) val = 1; else val = 0; goto set_int_and_return; /* This option is a on/off flag and is passed an integer where a non- * zero is on and a zero is off. If enabled no SCTP message * fragmentation will be performed. Instead if a message being sent * exceeds the current PMTU size, the message will NOT be sent and * instead a error will be indicated to the user. */ case IPSCTP_DISABLE_FRAGMENTS: if (IP_BIT_ISSET(inst->feature_flags, IPSCTP_INST_FLAGS_NO_FRAGMENT)) val = 1; else val = 0; goto set_int_and_return; /* pls refer to IPSCTP_FRAGMENT_INTERLEAVE's comment */ case IPSCTP_FRAGMENT_INTERLEAVE: if (IP_BIT_ISSET(inst->feature_flags, IPSCTP_INST_FLAG_FRAG_INTERLEAVE)) { if (IP_BIT_ISSET(inst->feature_flags, IPSCTP_INST_FLAG_INTERLEAVE_STRMS)) val = IPSCTP_FRAG_LEVEL_2; else val = IPSCTP_FRAG_LEVEL_1; } else val = IPSCTP_FRAG_LEVEL_0; goto set_int_and_return; /* This option will set or get the SCTP partial delivery point. This * point is the size of a message where the partial delivery API will be * invoked to help free up rwnd space for the peer. Setting this to a * lower value will cause partial deliveries to happen more often. The * calls argument is an integer that sets or gets the partial delivery * point. Note also that the call will fail if the user attempts to set * this value larger than the socket receive buffer size. * Note that any single message having a length smaller than or equal to * the SCTP partial delivery point will be delivered in one single read * call as long as the user provided buffer is large enough to hold the * message. */ case IPSCTP_PARTIAL_DELIVERY_POINT: val = inst->pd_point; goto set_int_and_return; /* This socket option is applicable to the one-to-many style socket * only. When set it will cause associations that are idle for more * than the specified number of seconds to automatically close using the * graceful shutdown procedure. An association being idle is defined as * an association that has NOT sent or received user data. The special * value of '0' indicates that no automatic close of any associations * should be performed, this is the default value. The option expects * an integer defining the number of seconds of idle time before an * association is closed. * An application using this option should enable receiving the * association change notification. This is the only mechanism an * application is informed about the closing of an association. After * an association is closed, the association ID assigned to it can be * reused. An application should be aware of this to avoid the possible * problem of sending data to an incorrect peer end point. */ case IPSCTP_AUTOCLOSE: if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_FLAG_AUTOCLOSE)) val = inst->autoclose_time; else val = 0; goto set_int_and_return; default: #if 0 if (IPSCTP_SOCKOPT_W == ipsctp_sockopt_check_rw(optname)) { ret = 0; break; } #endif return IPNET_ERRNO(ENOPROTOOPT); } return ret; set_int_and_return: if (*optlen < sizeof(int)) return IPNET_ERRNO(EINVAL); *(int*) optval = val; *optlen = sizeof(int); return 0; } /* *=========================================================================== * ipsctp_set_peer_addr_params *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * 8.1.13. Peer Address Parameters (SCTP_PEER_ADDR_PARAMS) * * Applications can enable or disable heartbeats for any peer address of * an association, modify an address's heartbeat interval, force a * heartbeat to be sent immediately, and adjust the address's maximum * number of retransmissions sent before an address is considered * unreachable. The following structure is used to access and modify an * address's parameters: * * struct sctp_paddrparams { * sctp_assoc_t spp_assoc_id; * struct sockaddr_storage spp_address; * uint32_t spp_hbinterval; * uint16_t spp_pathmaxrxt; * uint32_t spp_pathmtu; * uint32_t spp_flags; * uint32_t spp_ipv6_flowlabel; * uint8_t spp_ipv4_tos; * }; * * spp_assoc_id - (one-to-many style socket) This is filled in the * application, and identifies the association for this query. * spp_address - This specifies which address is of interest. * spp_hbinterval - This contains the value of the heartbeat interval, * in milliseconds. Note that unless the spp_flag is set to * SPP_HB_ENABLE the value of this field is ignored. Note also that * a value of zero indicates the current setting should be left * unchanged. To set an actual value of zero the use of the flag * SPP_HB_TIME_IS_ZERO should be used. * spp_pathmaxrxt - This contains the maximum number of retransmissions * before this address shall be considered unreachable. Note that a * value of zero indicates the current setting should be left * unchanged. * spp_pathmtu - When Path MTU discovery is disabled the value * specified here will be the "fixed" path mtu (i.e. the value of the * spp_flags field must include the flag SPP_PMTUD_DISABLE for this * field to have any effect). Note that if the spp_address field is * empty then all destinations for this association will have this * fixed path mtu set upon them. If an address is specified, then * only that address will be effected. Note also that this option * cannot be set on the endpoint, but must be set on each individual * association. Also, when disabling PMTU discovery, the * implementation may disallow this behavior if the "fixed" path mtu * is below the constant value SCTP_SMALLEST_PMTU. * spp_ipv6_flowlabel- This field is used in conjunction with the * SPP_IPV6_FLOWLABEL flag. * spp_ipv4_tos- This field is used in conjunction with the * SPP_IPV4_TOS flag. * spp_flags- These flags are used to control various features on an * association. The flag field is a bit mask which may contain zero * or more of the following options: * SPP_HB_ENABLE - Enable heartbeats on the specified address. Note * that if the address field is empty all addresses for the * association have heartbeats enabled upon them. * SPP_HB_DISABLE - Disable heartbeats on the specified address. * Note that if the address field is empty all addresses for the * association will have their heartbeats disabled. Note also * that SPP_HB_ENABLE and SPP_HB_DISABLE are mutually exclusive, * only one of these two should be specified. Enabling both * fields will have undetermined results. * SPP_HB_DEMAND - Request a user initiated heartbeat to be made * immediately. * SPP_HB_TIME_IS_ZERO - Specify's that the time for heartbeat delay * is to be set to the value of 0 milliseconds. * SPP_PMTUD_ENABLE - This field will enable PMTU discovery upon the * specified address. Note that if the address field is empty * then all addresses on the association are effected. * SPP_PMTUD_DISABLE - This field will disable PMTU discovery upon * the specified address. Note that if the address field is empty * then all addresses on the association are effected. Not also * that SPP_PMTUD_ENABLE and SPP_PMTUD_DISABLE are mutually * exclusive. Enabling both will have undetermined results. * SPP_IPV6_FLOWLABEL - Setting this flag enables setting of the * IPV6 flowlabel value associated with either the association or * the specific address. If the address field is filled in, then * the specific destination address has this value set upon it. * If the association is specified, but not the address, then the * flowlabel value is set for any future destination addresses * that may be added. The value is obtained in the * spp_ipv6_flowlabel field. * Upon retrieval, this flag will be set to indicate that the * spp_ipv6_flowlabel field has a valid value returned. If a * specific destination addresses is set (in the spp_address * field) when called then the value returned is that of the * address. If just an association is specified (and no address) * then the association default flowlabel is returned. If neither * an association nor an destination is specified, then the * sockets default flowlabel is returned. For non IPv6 sockets, * then this flag will be left cleared. * SPP_IPV4_TOS - Setting this flag enables setting of the IPV4 tos * value associated with either the association or specific * address. If the address field is filled in, then the specific * destination address has this value set upon it. If the * association is specified, but not the address, then the tos * value is set for any future destination addresses that may be * added. The value is obtained in the spp_ipv4_tos field. * * Upon retrieval, this flag will be set to indicate that the * spp_ipv4_tos field has a valid value returned. If a specific * destination addresses is set when called (in the spp_address * field) then that specific destination addresses tos value is * returned. If just an association is specified then the * association default tos is returned. If neither an association * nor an destination is specified, then the sockets default tos * is returned. For non IPv4 sockets, then this flag will be left * cleared. * * To read or modify these parameters, the application should call * sctp_opt_info() with the SCTP_PEER_ADDR_PARAMS option. * Parameters: sock - the set socket descriptor. * params - the set parameters pointer. * tcb - which TCB need be setted. * tp - which transport need be setted. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_apply_peer_addr_params(Ipsctp_socket *sock, struct Ipsctp_paddrparams *params, Ipsctp_tcb *tcb, Ipsctp_transport *tp) { Ipsctp_instance *inst = IP_NULL; int ret = 0; if ((IP_NULL == sock) || (IP_NULL == params) || (IP_NULL == (inst = sock->sctp_inst))) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* apply for the demand heatbeat interval, make sure. */ if (params->spp_flags & IPSPP_HB_DEMAND && tp && tcb) { ret = ipsctp_send_heartbeat(tcb, tp); IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : send heartbeat %d."IP_LF, ret); } if (params->spp_flags & IPSPP_HB_ENABLE) { if (params->spp_flags & IPSPP_HB_TIME_IS_ZERO) params->spp_hbinterval = 0; if ((params->spp_flags & IPSPP_HB_TIME_IS_ZERO) || (params->spp_hbinterval)) { if (tp) { tp->hbinterval = params->spp_hbinterval; tp->state &= ~IPSCTP_ADDR_NOHB; } else if (tcb) /* TODO : set each TP? */ tcb->hb_interval = params->spp_hbinterval; else inst->paddrparam.spp_hbinterval = params->spp_hbinterval; } } if ((params->spp_flags & IPSPP_HB_DISABLE) && tp) tp->state &= ~IPSCTP_ADDR_NOHB; #if 0 if ((params->spp_flags & IPSPP_PMTUD_DISABLE) && params->spp_pathmtu) { if (tp) tp->pmtu = params->spp_pathmtu; /* TODO(td): apply the path MTU value */ else if (tcb) { IPSCTP_DATA_TCBLOCK(tcb->instance->tcb_lock); tcb->pmtu = params->spp_pathmtu; /* TODO(td): apply the path MTU value */ IPSCTP_DATA_TCBUNLOCK(tcb->instance->tcb_lock); } else inst->paddrparam.spp_pathmtu = params->spp_pathmtu; } if ((params->spp_flags & IPSPP_PMTUD_ENABLE) && params->spp_pathmaxrxt) { ret = 0; } #endif if (params->spp_pathmaxrxt) { /* TODO : value validate; set each TP? */ if (tp) { tp->err_thresh = params->spp_pathmaxrxt; } else { if (tcb) tcb->max_transport_retrans = params->spp_pathmaxrxt; else inst->paddrparam.spp_pathmaxrxt = params->spp_pathmaxrxt; } } return ret; } /* *=========================================================================== * ipsctp_setsockopt_rtoinfo *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer stored the value of this option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_setsockopt_rtoinfo(Ipsctp_socket *sock, IP_CONST void *optval, Ip_socklen_t optlen) { Ipsctp_instance *inst = IP_NULL; struct Ipsctp_rtoinfo *rto_info = (struct Ipsctp_rtoinfo *)optval; Ipsctp_tcb *tcb = IP_NULL; Ip_u32 new_init, new_min, new_max; if ((IP_NULL == sock) || (IP_NULL == optval) || (IP_NULL == (inst = sock->sctp_inst)) || (sizeof(struct Ipsctp_rtoinfo) != optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* find tcb with sctp_rtoinfo.srto_assoc_id */ IPSCTP_SOCK_GETCB_BY_ASID(rto_info->srto_assoc_id); if (!tcb && rto_info->srto_assoc_id && ipsctp_style(sock, UDP)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF); return IPNET_ERRNO(EINVAL); } /* set the values to instance or tcb */ new_init = new_min = new_max = 0; if (tcb) { if (rto_info->srto_initial) new_init = rto_info->srto_initial; else new_init = tcb->initial_rto; if (rto_info->srto_max) new_max = rto_info->srto_max; else new_max = tcb->max_rto; if (rto_info->srto_min) new_min = rto_info->srto_min; else new_min = tcb->min_rto; if ((new_min > new_init) || (new_init > new_max)) goto print_err_info; tcb->initial_rto = new_init; tcb->max_rto = new_max; tcb->min_rto = new_min; } else { if (rto_info->srto_initial) new_init = rto_info->srto_initial; else new_init = inst->rtoinfo.srto_initial; if (rto_info->srto_max) new_max = rto_info->srto_max; else new_max = inst->rtoinfo.srto_max; if (rto_info->srto_min) new_min = rto_info->srto_min; else new_min = inst->rtoinfo.srto_min; if ((new_min > new_init) || (new_init > new_max)) goto print_err_info; inst->rtoinfo.srto_initial = new_init; inst->rtoinfo.srto_max = new_max; inst->rtoinfo.srto_min = new_min; } #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Set socket route information option OK."IP_LF); #endif return 0; print_err_info: /* here, print error infor */ IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Set socket route information option ERROR."IP_LF); return IPNET_ERRNO(EINVAL); } /* *=========================================================================== * ipsctp_setsockopt_associnfo *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer stored the value of this option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_setsockopt_associnfo(Ipsctp_socket *sock, IP_CONST void *optval, Ip_socklen_t optlen) { Ipsctp_instance *inst = IP_NULL; struct Ipsctp_assocparams *params = (struct Ipsctp_assocparams *)optval; Ipsctp_tcb *tcb = IP_NULL; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_assocparams) != optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* check the validity of the setting value */ /* check instance point */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* find tcb with sctp_rtoinfo.srto_assoc_id */ IPSCTP_SOCK_GETCB_BY_ASID(params->sasoc_assoc_id); if (!tcb && params->sasoc_assoc_id && ipsctp_style(sock, UDP)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF); return IPNET_ERRNO(EINVAL); } /* set the values to instance or tcb */ if (tcb) { if (params->sasoc_asocmaxrxt) tcb->max_send_times = params->sasoc_asocmaxrxt; if (params->sasoc_cookie_life) tcb->cookie_life = params->sasoc_cookie_life; params->sasoc_number_peer_destinations = (Ip_u16)ipsctp_list_size(&tcb->transport_list); } else { if (params->sasoc_asocmaxrxt) inst->assocparams.sasoc_asocmaxrxt = params->sasoc_asocmaxrxt; if (params->sasoc_cookie_life) inst->assocparams.sasoc_cookie_life = params->sasoc_cookie_life; } #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Set socket association option OK."IP_LF); #endif return 0; } /* *=========================================================================== * ipsctp_setsockopt_default_send_param *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer stored the value of this option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_setsockopt_default_send_param(Ipsctp_socket *sock, IP_CONST void *optval, Ip_socklen_t optlen) { struct Ipsctp_sndrcvinfo *sr_info = (struct Ipsctp_sndrcvinfo *)optval; Ipsctp_instance *inst = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_sndrcvinfo) != optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* check instance point */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* find tcb with sctp_sndrcvinfo.sinfo_assoc_id */ IPSCTP_SOCK_GETCB_BY_ASID(sr_info->sinfo_assoc_id); if (!tcb && sr_info->sinfo_assoc_id && ipsctp_style(sock, UDP)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF); return IPNET_ERRNO(EINVAL); } /* set the values to instance or tcb */ if (tcb) IPSCTP_SNDRCV_CPY(&tcb->def_send, sr_info); else IPSCTP_SNDRCV_CPY(&inst->sndrcvinfo, sr_info); #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Set socket default address param option OK."IP_LF); #endif return 0; } /* *=========================================================================== * ipsctp_setsockopt_primary_addr *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer stored the value of this option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_setsockopt_primary_addr(Ipsctp_socket *sock, IP_CONST void *optval, Ip_socklen_t optlen) { struct Ipsctp_setprim *prim_info = (struct Ipsctp_setprim *)optval; Ipsctp_instance *inst = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; Ipsctp_transport *tp = IP_NULL; int ret = 0; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_setprim) != optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* check instance point */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* find tcb with sctp_setprim.ssp_assoc_id */ IPSCTP_SOCK_GETCB_BY_ASID(prim_info->ssp_assoc_id); if (IP_NULL == tcb) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF); return IPNET_ERRNO(EINVAL); } /* get the transport by the special address */ if (ipsctp_is_addr_valid((Ipsctp_addr*)&prim_info->ssp_addr)) tp = ipsctp_transport_lookup(tcb, (IP_CONST Ipsctp_addr *)&prim_info->ssp_addr); /* set the primary destination */ ret = ipsctp_set_primary_addr(tcb, (Ipsctp_addr*)&prim_info->ssp_addr, tp, IP_TRUE, IP_FALSE ); #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Set socket primary address option OK."IP_LF); #endif return ret; } /* *=========================================================================== * ipsctp_setsockopt_peer_addr_params *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer stored the value of this option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_setsockopt_peer_addr_params(Ipsctp_socket *sock, IP_CONST void *optval, Ip_socklen_t optlen) { struct Ipsctp_paddrparams *addr_parms = (struct Ipsctp_paddrparams *)optval; Ipsctp_transport *tp = IP_NULL; Ipsctp_instance *inst = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; int ret = 0; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_paddrparams) != optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* check instance point */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* find tcb with sctp_paddrparams.spp_assoc_id, tcb can be NULL */ IPSCTP_SOCK_GETCB_BY_ASID(addr_parms->spp_assoc_id); if ((IP_NULL == tcb) && (addr_parms->spp_assoc_id)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF); return IPNET_ERRNO(EINVAL); } /* if addr_parms.spp_address is valid, then * find the transport with it. */ if ((IP_NULL != tcb) && (ipsctp_is_addr_valid((Ipsctp_addr*)&addr_parms->spp_address))) { tp = ipsctp_transport_lookup(tcb, (IP_CONST Ipsctp_addr *)&addr_parms->spp_address); } if ((addr_parms->spp_flags & IPSPP_HB_DEMAND) && (!tp || !tcb)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : NULL pointer for IPSPP_HB_DEMAND."IP_LF); return IPNET_ERRNO(EINVAL); } ret = ipsctp_apply_peer_addr_params(sock, addr_parms, tcb, tp); #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Set socket peer address params option OK."IP_LF); #endif return ret; } /* *=========================================================================== * ipsctp_setsockopt_context *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer stored the value of this option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_setsockopt_context(Ipsctp_socket *sock, IP_CONST void *optval, Ip_socklen_t optlen) { struct Ipsctp_assoc_value *as_val = (struct Ipsctp_assoc_value *)optval; Ipsctp_instance *inst = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_assoc_value) != optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* check instance point */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* set the values to instance or tcb */ if (as_val->assoc_id) { /* find tcb with sctp_assoc_value.assoc_id */ IPSCTP_SOCK_GETCB_BY_ASID(as_val->assoc_id); if (IP_NULL == tcb) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF); return IPNET_ERRNO(EINVAL); } tcb->context = as_val->assoc_value; } else inst->context = as_val->assoc_value; #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Set socket context option OK."IP_LF); #endif return 0; } #if 0 /* *=========================================================================== * ipsctp_setsockopt_active_key *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer stored the value of this option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_setsockopt_active_key(Ipsctp_socket *sock, IP_CONST void *optval, Ip_socklen_t optlen) { struct Ipsctp_authkeyid *keyid = (struct Ipsctp_authkeyid *)optval; Ipsctp_instance *inst = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_authkeyid) != optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* check instance point */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* find tcb with sctp_authkeyid.scact_assoc_id */ IPSCTP_SOCK_GETCB_BY_ASID(keyid->scact_assoc_id); if (!tcb && keyid->scact_assoc_id && ipsctp_style(sock, UDP)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF); return IPNET_ERRNO(EINVAL); } /* set the values to instance or tcb */ #if 0 if (tcb) tcb-> = keyid->scact_keynumber; else inst-> = keyid->scact_keynumber; #endif #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Set socket active key option OK."IP_LF); #endif return 0; } #endif /* *=========================================================================== * ipsctp_setsockopt_events *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer stored the value of this option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_setsockopt_events(Ipsctp_socket *sock, IP_CONST void *optval, Ip_socklen_t optlen) { #define IPSCTP_EVET_SET(evt_name, evt_flag) \ do { \ if (evt_sub->Ipsctp_ ## evt_name) \ IP_BIT_SET(inst->feature_flags , evt_flag); \ else \ IP_BIT_CLR(inst->feature_flags , evt_flag); \ } while(0) struct Ipsctp_event_subscribe *evt_sub = (struct Ipsctp_event_subscribe *)optval; Ipsctp_instance *inst = IP_NULL; if ((IP_NULL == sock) || (IP_NULL == evt_sub) || (sizeof(struct Ipsctp_event_subscribe) != optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."); return IPNET_ERRNO(EINVAL); } /* check instance point */ inst = sock->sctp_inst; if (IP_NULL == inst) return IPNET_ERRNO(EINVAL); /* set the bit value to instance or tcb */ IPSCTP_EVET_SET(data_io_event, IPSCTP_EVNT_FLAGS_DATA_IO); IPSCTP_EVET_SET(association_event, IPSCTP_EVNT_FLAGS_ASSOCIATION); IPSCTP_EVET_SET(address_event, IPSCTP_EVNT_FLAGS_ADDRESS); IPSCTP_EVET_SET(send_failure_event, IPSCTP_EVNT_FLAGS_SEND_FAILURE); IPSCTP_EVET_SET(peer_error_event, IPSCTP_EVNT_FLAGS_PEER_ERROR); IPSCTP_EVET_SET(shutdown_event, IPSCTP_EVNT_FLAGS_SHUTDOWN); IPSCTP_EVET_SET(partial_delivery_event, IPSCTP_EVNT_FLAGS_PARTIAL_DELIVERY); IPSCTP_EVET_SET(adaptation_layer_event, IPSCTP_EVNT_FLAGS_ADAPTATION_LAYER); IPSCTP_EVET_SET(authentication_event, IPSCTP_EVNT_FLAGS_AUTHENTICATION); #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Set socket events option OK."IP_LF); #endif #undef IPSCTP_EVET_SET return 0; } /* *=========================================================================== * ipsctp_setsockopt_initmsg *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer stored the value of this option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_setsockopt_initmsg(Ipsctp_socket *sock, IP_CONST void *optval, Ip_socklen_t optlen) { struct Ipsctp_initmsg *init_msg = (struct Ipsctp_initmsg *)optval; Ipsctp_instance *inst = IP_NULL; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_initmsg) != optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."); return IPNET_ERRNO(EINVAL); } /* check instance point */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* set the init message from instance */ if (init_msg->sinit_num_ostreams) { if (init_msg->sinit_num_ostreams > IPSCTP_MAX_STREAMS) inst->initmsg.sinit_num_ostreams = IPSCTP_MAX_STREAMS; else inst->initmsg.sinit_num_ostreams = init_msg->sinit_num_ostreams; } if (init_msg->sinit_max_instreams) { if (init_msg->sinit_max_instreams > IPSCTP_MAX_STREAMS) inst->initmsg.sinit_max_instreams = IPSCTP_MAX_STREAMS; else inst->initmsg.sinit_max_instreams = init_msg->sinit_max_instreams; } if (init_msg->sinit_max_attempts) inst->initmsg.sinit_max_attempts = init_msg->sinit_max_attempts; if (init_msg->sinit_max_init_timeo) inst->initmsg.sinit_max_init_timeo = init_msg->sinit_max_init_timeo; #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Set init message option OK."IP_LF); #endif return 0; } /* *=========================================================================== * ipsctp_setsockopt_maxseg *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer stored the value of this option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_setsockopt_maxseg(Ipsctp_socket *sock, IP_CONST void *optval, Ip_socklen_t optlen) { struct Ipsctp_assoc_value *as_val = (struct Ipsctp_assoc_value *)optval; Ipsctp_instance *inst = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; Ip_u32 hdr_len = 0; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_assoc_value) != optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* check instance point */ IPSCTP_SOCK_INST_CHECK(sock, inst); #ifdef IPCOM_USE_INET if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V4)) { hdr_len = sizeof(Ipsctp_data_chunk_hdr) + sizeof(Ipsctp_sctp_hdr) + sizeof(Ipnet_pkt_ip); } #endif #ifdef IPCOM_USE_INET6 if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V6)) { hdr_len = sizeof(Ipsctp_data_chunk_hdr) + sizeof(Ipsctp_sctp_hdr) + sizeof(Ipnet_pkt_ip6); } #endif /* find tcb with sctp_assoc_value.assoc_id */ IPSCTP_SOCK_GETCB_BY_ASID(as_val->assoc_id); /* set the values to instance or tcb */ if (IP_NULL != tcb) { if (as_val->assoc_value) tcb->frag_point = as_val->assoc_value + hdr_len; else tcb->frag_point = 0; if (tcb->frag_point != 0 && ((tcb->frag_point & ~0x3) < tcb->mss)) { tcb->mss = tcb->frag_point & ~0x3; } } else { if (as_val->assoc_value) inst->frag_point = as_val->assoc_value + hdr_len; else inst->frag_point = 0; } #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Set socket max segment option OK."IP_LF); #endif return 0; } /* *=========================================================================== * ipsctp_setsockopt_delay_ack *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer stored the value of this option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_setsockopt_delay_ack(Ipsctp_socket *sock, IP_CONST void *optval, Ip_socklen_t optlen) { struct Ipsctp_sack_info *ssi = (struct Ipsctp_sack_info *)optval; Ipsctp_instance *inst = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_sack_info) != optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* check instance point */ IPSCTP_SOCK_INST_CHECK(sock, inst); if (ssi->sack_delay && ssi->sack_delay > IPSCTP_MAX_SACK_DELAY) ssi->sack_delay = IPSCTP_MAX_SACK_DELAY; /* find tcb with Ipsctp_sack_info.sack_assoc_id */ IPSCTP_SOCK_GETCB_BY_ASID(ssi->sack_assoc_id); /* set the values to instance or tcb */ if (IP_NULL != tcb) { if (ssi->sack_delay) tcb->delayed_ack = ssi->sack_delay; if (ssi->sack_freq) tcb->sack_freq = ssi->sack_freq; } else { if (ssi->sack_delay) inst->sack_info.sack_delay = ssi->sack_delay; if (ssi->sack_freq) inst->sack_info.sack_freq = ssi->sack_freq; } #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Set socket delay ack option OK."IP_LF); #endif return 0; } /* *=========================================================================== * ipsctp_setsockopt_max_burst *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer stored the value of this option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_setsockopt_max_burst(Ipsctp_socket *sock, IP_CONST void *optval, Ip_socklen_t optlen) { struct Ipsctp_assoc_value *as_val = (struct Ipsctp_assoc_value *)optval; Ipsctp_instance *inst = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; if ((IP_NULL == sock) || (IP_NULL == optval) || (sizeof(struct Ipsctp_assoc_value) != optlen)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* check instance point */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* set the values to instance or tcb */ if (as_val->assoc_id) { /* find tcb with sctp_assoc_value.assoc_id */ IPSCTP_SOCK_GETCB_BY_ASID(as_val->assoc_id); if (IP_NULL == tcb) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF); return IPNET_ERRNO(EINVAL); } tcb->max_burst = as_val->assoc_value; } else inst->max_burst = as_val->assoc_value; #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Set socket context option OK."IP_LF); #endif return 0; } /* *=========================================================================== * ipsctp_sock_bindx_add *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer stored the value of this option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_sock_bindx_add(Ipsctp_socket *sock, IP_CONST void *optval, Ip_socklen_t optlen) { struct Ipsctp_getaddrs *gaddrs = (struct Ipsctp_getaddrs *)optval; Ipsctp_socket *ret_sock = IP_NULL; Ipsctp_instance *inst = IP_NULL; struct Ip_sockaddr *name = IP_NULL; Ip_u32 gaddrs_len = 0; Ip_u16 *port = IP_NULL; void *local_addr = IP_NULL; int ret = 0; #if defined(IPCOM_USE_INET6) && defined(IPCOM_USE_INET) struct Ip_sockaddr_in sin; #endif if ((IP_NULL == sock) || (IP_NULL == optval)) goto inval_out; /* check instance point */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* if already connected(include accept, not include listen), * just return. do not support dynamic */ if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_CONNECTED)) return IPNET_ERRNO(EOPNOTSUPP); if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUNDALL)) goto inval_out; name = gaddrs->sget_addrs; switch (name->sa_family) { #ifdef IPCOM_USE_INET case IP_AF_INET: if (IPCOM_SA_LEN_GET(name) != sizeof(struct Ip_sockaddr_in)) goto inval_out; gaddrs_len = sizeof(struct Ipsctp_getaddrs) - sizeof(struct Ip_sockaddr) + sizeof(struct Ip_sockaddr_in); if (IPSCTP_IS_V6ONLY(inst)) goto inval_out; local_addr = (void *)&((struct Ip_sockaddr_in *)name)->sin_addr; port = &((struct Ip_sockaddr_in *)name)->sin_port; break; #endif #ifdef IPCOM_USE_INET6 case IP_AF_INET6: if (IPCOM_SA_LEN_GET(name) != sizeof(struct Ip_sockaddr_in6)) goto inval_out; gaddrs_len = sizeof(struct Ipsctp_getaddrs) - sizeof(struct Ip_sockaddr) + sizeof(struct Ip_sockaddr_in6); local_addr = (void *)&((struct Ip_sockaddr_in6 *)name)->sin6_addr; port = &((struct Ip_sockaddr_in6 *)name)->sin6_port; /* change IPV4 maped address to IPV4 address */ if (IP_IN6_IS_ADDR_V4MAPPED(&((struct Ip_sockaddr_in6 *)name)->sin6_addr)) { if (IPSCTP_IS_V6ONLY(inst)) goto inval_out; #ifdef IPCOM_USE_INET { struct Ip_sockaddr_in6 *sin6 = (struct Ip_sockaddr_in6 *)name; IPCOM_SA_LEN_SET(&sin, sizeof(struct Ip_sockaddr_in)); sin.sin_family = IP_AF_INET; sin.sin_port = sin6->sin6_port; sin.sin_addr.s_addr = sin6->sin6_addr.in6.addr32[3]; name = (struct Ip_sockaddr *)&sin; local_addr = (void *)&sin.sin_addr.s_addr; port = &sin.sin_port; } #endif } break; #endif /* invalid address family specified */ default: goto inval_out; } if (gaddrs_len > optlen) goto inval_out; /* if the first bind, do user socket bind */ if (IP_BIT_ISFALSE(sock->flags, IPNET_SOCKET_FLAG_BOUND)) { Ipnet_sock_ops *ops = ipnet_sock_get_ops(sock); IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : first bind for bindx."IP_LF); return ops->bind(sock, name, IPCOM_SA_LEN_GET(name), IP_TRUE); } /* else add to local address list */ if (gaddrs->sget_assoc_id) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : sget_assoc_id is not zero."IP_LF); return 0; } /* check the bind-port if not the first time */ if (*port && ip_ntohs(*port) != inst->lport) goto inval_out; /* update local port number, if user does not specify, * used in ipsctp_add_laddr */ if (0 == *port) *port = ip_htons(inst->lport); ret_sock = ipsctp_sock_lookup_sa(sock->vr_index, name->sa_family, local_addr, ip_ntohs(*port)); if (ret_sock && ret_sock->sctp_inst == sock->sctp_inst) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : already set the same address in same socket."IP_LF); return 0; } if (IP_NULL == ret_sock) { enum Ipnet_addr_type addr_type = IPNET_ADDR_TYPE_NOT_LOCAL; /* check if this address on the curent system */ switch (name->sa_family) { #ifdef IPCOM_USE_INET case IP_AF_INET: addr_type = ipnet_ip4_get_addr_type(*(Ip_u32 *)local_addr, sock->vr_index, IP_NULL); break; #endif #ifdef IPCOM_USE_INET6 case IP_AF_INET6: addr_type = ipnet_ip6_get_addr_type((IP_CONST struct Ip_in6_addr *)local_addr, sock->vr_index, IP_NULL); break; #endif /* invalid address family specified */ default: goto inval_out; } if (IPNET_ADDR_TYPE_NOT_LOCAL == addr_type) return IPNET_ERRNO(EADDRNOTAVAIL); ret = ipsctp_add_laddr(inst, (Ipsctp_addr *)name, IPSCTP_LADDR_NORMAL); if (0 != ret) return ret; } else { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : already set the same address."IP_LF); return IPNET_ERRNO(EADDRINUSE); } #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Bindx add OK."IP_LF); #endif return 0; inval_out: IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* *=========================================================================== * ipsctp_sock_bindx_del *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer stored the value of this option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_sock_bindx_del(Ipsctp_socket *sock, IP_CONST void *optval, Ip_socklen_t optlen) { struct Ipsctp_getaddrs *gaddrs = (struct Ipsctp_getaddrs *)optval; Ipsctp_laddr_entry *laddrs_cur = IP_NULL; Ipsctp_laddr_entry *laddrs_tmp = IP_NULL; Ipsctp_instance *inst = IP_NULL; struct Ip_sockaddr *name = IP_NULL; Ip_u32 gaddrs_len = 0; Ip_u16 *port = IP_NULL; void *local_addr = IP_NULL; #if defined(IPCOM_USE_INET6) && defined(IPCOM_USE_INET) struct Ip_sockaddr_in sin; #endif if ((IP_NULL == sock) || (IP_NULL == optval)) goto inval_out; /* check instance point */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* if already connected(include accept, not include listen), * just return. do not support dynamic */ if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_CONNECTED)) return IPNET_ERRNO(EOPNOTSUPP); if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUNDALL)) goto inval_out; name = gaddrs->sget_addrs; switch (name->sa_family) { #ifdef IPCOM_USE_INET case IP_AF_INET: if (IPCOM_SA_LEN_GET(name) != sizeof(struct Ip_sockaddr_in)) goto inval_out; gaddrs_len = sizeof(struct Ipsctp_getaddrs) - sizeof(struct Ip_sockaddr) + sizeof(struct Ip_sockaddr_in); if (IPSCTP_IS_V6ONLY(inst)) goto inval_out; local_addr = (void *)&((struct Ip_sockaddr_in *)name)->sin_addr; port = &((struct Ip_sockaddr_in *)name)->sin_port; break; #endif #ifdef IPCOM_USE_INET6 case IP_AF_INET6: if (IPCOM_SA_LEN_GET(name) != sizeof(struct Ip_sockaddr_in6)) goto inval_out; gaddrs_len = sizeof(struct Ipsctp_getaddrs) - sizeof(struct Ip_sockaddr) + sizeof(struct Ip_sockaddr_in6); local_addr = (void *)&((struct Ip_sockaddr_in6 *)name)->sin6_addr; port = &((struct Ip_sockaddr_in6 *)name)->sin6_port; /* change IPV4 maped address to IPV4 address */ if (IP_IN6_IS_ADDR_V4MAPPED(&((struct Ip_sockaddr_in6 *)name)->sin6_addr)) { if (IPSCTP_IS_V6ONLY(inst)) goto inval_out; #ifdef IPCOM_USE_INET { struct Ip_sockaddr_in6 *sin6 = (struct Ip_sockaddr_in6 *)name; IPCOM_SA_LEN_SET(&sin, sizeof(struct Ip_sockaddr_in)); sin.sin_family = IP_AF_INET; sin.sin_port = sin6->sin6_port; sin.sin_addr.s_addr = sin6->sin6_addr.in6.addr32[3]; name = (struct Ip_sockaddr *)&sin; local_addr = (void *)&sin.sin_addr.s_addr; port = &sin.sin_port; } #endif } break; #endif /* invalid address family specified */ default: goto inval_out; } if (gaddrs_len > optlen) goto inval_out; if (gaddrs->sget_assoc_id) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : sget_assoc_id is not zero."IP_LF); return 0; } /* check the bind-port if not the first time */ if (*port && ip_ntohs(*port) != inst->lport) goto inval_out; /* update local port number, if user does not specify, * used in ipsctp_add_laddr */ if (0 == *port) *port = ip_htons(inst->lport); if (ipsctp_list_size(&inst->laddr_list) < 2) goto inval_out; /* search in this association local bound address list */ ipsctp_list_for_each_safe(&inst->laddr_list, laddrs_cur, laddrs_tmp, Ipsctp_laddr_entry) { if (laddrs_cur->laddr.sa.sa_family != name->sa_family) continue; switch (name->sa_family) { #ifdef IPCOM_USE_INET case IP_AF_INET: if (*(Ip_in_addr_t *)local_addr == laddrs_cur->laddr.sin.sin_addr.s_addr) { ipcom_list_remove((Ipcom_list *)laddrs_cur); #ifdef IPCOM_FORWARDER_NAE ipcom_shm_free(laddrs_cur); #else ipcom_free(laddrs_cur); #endif continue; } break; #endif #ifdef IPCOM_USE_INET6 case IP_AF_INET6: if (IP_TRUE == IP_IN6_ARE_ADDR_EQUAL(local_addr, &laddrs_cur->laddr.sin6.sin6_addr)) { ipcom_list_remove((Ipcom_list *)laddrs_cur); #ifdef IPCOM_FORWARDER_NAE ipcom_shm_free(laddrs_cur); #else ipcom_free(laddrs_cur); #endif continue; } break; #endif default: continue; } } #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Bindx delete OK."IP_LF); #endif return 0; inval_out: IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* *=========================================================================== * ipsctp_sock_connectx_find *=========================================================================== * Description: Find the matched TCB with the peer address. * Parameters: addrs - the connected peer addresses. * addrcnt - how many connected peer addresses in 'addrs'. * max_addrs_len - the max memony length of the peer address. * cnt_v4 - the number of V4 address in 'addrs'. * cnt_v6 - the number of V6 address in 'addrs'. * cnt_err - the number of error address 'addrs'. * Returns: 0 = success, <0 error code. * */ IP_STATIC Ipsctp_tcb * ipsctp_sock_connectx_find(Ipsctp_socket *sock, struct Ip_sockaddr *addrs, Ip_u32 addrcnt, Ip_u32 max_addrs_len, Ip_u32 *cnt_v4, Ip_u32 *cnt_v6, Ip_u32 *cnt_err) { Ipsctp_tcb *tcb = IP_NULL; struct Ip_sockaddr *sa = IP_NULL; Ip_u32 i = 0; Ip_u32 sa_len = 0; Ip_u32 addrs_len = 0; if ((IP_NULL == sock) || (IP_NULL == addrs) || (IP_NULL == cnt_v4) || (IP_NULL == cnt_v6) || (IP_NULL == cnt_err) || (0 == addrcnt) || (0 == max_addrs_len)) { IPSCTP_DBG2(IPSCTP_DBG_SOCK, "IPSCTP : cnt/max = %u/%u."IP_LF, addrcnt, max_addrs_len); goto cnt_err_out; } *cnt_v4 = 0; *cnt_v6 = 0; *cnt_err = 0; sa = addrs; addrs_len = 0; sa_len = 0; for (i = 0; i < addrcnt; i++) { switch (sa->sa_family) { #ifdef IPCOM_USE_INET case IP_AF_INET: sa_len = sizeof(struct Ip_sockaddr_in); break; #endif #ifdef IPCOM_USE_INET6 case IP_AF_INET6: #ifdef IPCOM_USE_INET if (IP_AF_INET == sock->ipcom.domain) goto cnt_err_out; #endif /* Must not IPV4 maped address */ if (IP_IN6_IS_ADDR_V4MAPPED(&((struct Ip_sockaddr_in6 *)sa)->sin6_addr)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : IPV4 address."IP_LF); goto cnt_err_out; } sa_len = sizeof(struct Ip_sockaddr_in6); break; #endif /* invalid address family specified */ default: IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid af."IP_LF); goto cnt_err_out; } if (IPCOM_SA_LEN_GET(sa) != sa_len) { IPSCTP_DBG2(IPSCTP_DBG_SOCK, "IPSCTP : get/len = %u/%u."IP_LF, IPCOM_SA_LEN_GET(sa), sa_len); goto cnt_err_out; } addrs_len += sa_len; if (addrs_len > max_addrs_len) { IPSCTP_DBG2(IPSCTP_DBG_SOCK, "IPSCTP : len/max = %u/%u."IP_LF, addrs_len, max_addrs_len); goto cnt_err_out; } tcb = ipsctp_sock_lookup_da(sock, sa); if (IP_NULL != tcb) return tcb; sa = (struct Ip_sockaddr *)((Ip_caddr_t)sa + sa_len); } return IP_NULL; cnt_err_out: IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); if (IP_NULL != cnt_err) *cnt_err = 1; return IP_NULL; } /* *=========================================================================== * ipsctp_sock_connectx_add *=========================================================================== * Description: Add the connected peer address to TCB transport. * Parameters: tcb - The SCTP association control block. * addrs - the connected peer addresses. * addrcnt - how many connected peer addresses in 'addrs'. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_sock_connectx_add(Ipsctp_tcb *tcb, struct Ip_sockaddr *addrs, int addrcnt) { struct Ip_sockaddr *sa = IP_NULL; Ip_u32 i = 0; Ip_u32 sa_len = 0; if (0 == addrcnt) return 0; if ((IP_NULL == addrs) || (IP_NULL == tcb) || (addrcnt < 0)) goto inval_out; sa = addrs; sa_len = 0; for (i = 0; i < addrcnt; i++) { switch (sa->sa_family) { #ifdef IPCOM_USE_INET case IP_AF_INET: sa_len = sizeof(struct Ip_sockaddr_in); break; #endif #ifdef IPCOM_USE_INET6 case IP_AF_INET6: sa_len = sizeof(struct Ip_sockaddr_in6); break; #endif /* invalid address family specified */ default: goto inval_out; } if (IPCOM_SA_LEN_GET(sa) != sa_len) goto inval_out; if (0 != ipsctp_add_remote_addr(tcb, (Ipsctp_addr *)sa, IPSCTP_ADDR_IS_CONFIRMED, IP_NULL)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : add remote address error."IP_LF); return IPNET_ERRNO(ENOBUFS); } sa = (struct Ip_sockaddr *)((Ip_caddr_t)sa + sa_len); } return 0; inval_out: IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* *=========================================================================== * ipsctp_sock_connectx *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer stored the value of this option. * optlen - the size of the buffer. * is_delay - the INIT packet send delay flag. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_sock_connectx(Ipsctp_socket *sock, IP_CONST void *optval, Ip_socklen_t optlen, Ip_bool is_delay) { Ipsctp_instance *inst = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; Ipsctp_tcb *new_tcb = IP_NULL; Ip_u32 addrs_cnt = 0; struct Ip_sockaddr *second_sa = IP_NULL; struct Ip_sockaddr *sa = IP_NULL; Ip_u32 v4_cnt = 0; Ip_u32 v6_cnt = 0; Ip_u32 err_cnt = 0; int ret = 0; Ipsctp_addr bind_any; #ifdef IPCOM_USE_INET6 struct Ip_sockaddr_in6 *in6_to = IP_NULL; #endif #ifdef IPCOM_USE_INET struct Ip_sockaddr_in *in_to = IP_NULL; #endif if ((IP_NULL == sock) || (IP_NULL == optval)) goto inval_out; /* check instance point */ IPSCTP_SOCK_INST_CHECK(sock, inst); /* check the socket flags(for listening state) */ if (ipsctp_sock_state(sock, LISTENING) && ipsctp_style(sock, TCP)) return IPNET_ERRNO(EISCONN); /* it's a listen socket, if passive open just return. */ if (IP_NULL != inst->parent) return 0; if (ipsctp_style(sock, TCP) && (IP_BIT_ISSET(sock->flags, IPSCTP_STACK_CONST(SOCKET_FLAG_CONNECTED)))) return IPNET_ERRNO(EISCONN); addrs_cnt = *((Ip_u32 *)optval); sa = (struct Ip_sockaddr *)((Ip_u32 *)optval + 1); /* check if TCB exist for connectx addresses */ tcb = ipsctp_sock_connectx_find(sock, sa, addrs_cnt, (Ip_u32)(optlen - sizeof(Ip_u32)), &v4_cnt, &v6_cnt, &err_cnt); if ((IP_NULL != tcb) || (0 < err_cnt)) { if (0 == err_cnt) return IPNET_ERRNO(EALREADY); IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Not NULL."IP_LF); goto inval_out; } /* more check for IPV6 socket */ #ifdef IPCOM_USE_INET6 if (IP_BIT_ISFALSE(inst->inst_flags, IPSCTP_SOCKET_BOUND_V6) && (v6_cnt > 0)) { IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : v6_cnt = %u."IP_LF, v6_cnt); goto inval_out; } if (IPSCTP_IS_V6ONLY(inst) && (v4_cnt > 0)) { IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : v4_cnt = %u."IP_LF, v4_cnt); goto inval_out; } #endif /* do bindall operation as default */ if (IP_BIT_ISFALSE(sock->flags, IPNET_SOCKET_FLAG_BOUND)) { Ipnet_sock_ops *ops = ipnet_sock_get_ops(sock); ipcom_memset((void *)&bind_any, 0, sizeof(bind_any)); switch (sock->ipcom.domain) { #ifdef IPCOM_USE_INET case IP_AF_INET: bind_any.sin.sin_family = IP_AF_INET; bind_any.sin.sin_addr.s_addr = ip_htonl(IP_INADDR_ANY); IPCOM_SA_LEN_SET(&bind_any.sa, sizeof(struct Ip_sockaddr_in)); break; #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 case IP_AF_INET6: bind_any.sin6.sin6_family = IP_AF_INET6; IPCOM_SA_LEN_SET(&bind_any.sa, sizeof(struct Ip_sockaddr_in6)); break; #endif /* IPCOM_USE_INET6 */ default: IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : domain = %u."IP_LF, sock->ipcom.domain); goto inval_out; } ret = ops->bind(sock, &bind_any.sa, IPCOM_SA_LEN_GET(&bind_any.sa), IP_TRUE); if (ret < 0) { IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : ret = %d."IP_LF, ret); return ret; } } ipnet_sock_set_unreachable(sock); /* for connect operation, use sa->sa_family instead of sock->ipcom.domain */ switch (sa->sa_family) { #ifdef IPCOM_USE_INET case IP_AF_INET: in_to = (struct Ip_sockaddr_in *)sa; second_sa = (struct Ip_sockaddr *)((Ip_caddr_t)sa + sizeof(struct Ip_sockaddr_in)); sock->ip4->daddr_n = in_to->sin_addr.s_addr; sock->dport = ip_ntohs(in_to->sin_port); break; #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 case IP_AF_INET6: in6_to = (struct Ip_sockaddr_in6 *)sa; second_sa = (struct Ip_sockaddr *)((Ip_caddr_t)sa + sizeof(struct Ip_sockaddr_in6)); /* Copy the peer information */ sock->ip6->daddr = in6_to->sin6_addr; sock->ip6->flowinfo = ip_ntohl(in6_to->sin6_flowinfo); sock->dport = ip_ntohs(in6_to->sin6_port); if (sock->bind_to_ifindex > 0 && IPNET_IP6_IS_SCOPE_LINK_LOCAL(&in6_to->sin6_addr)) /* Socket is bound to an interface.*/ sock->ip6->iscope_id = sock->bind_to_ifindex; else sock->ip6->iscope_id = in6_to->sin6_scope_id; break; #endif /* IPCOM_USE_INET6 */ default: IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : sa_family = %u."IP_LF, sa->sa_family); goto inval_out; } ipnet_sock_set_reachable(sock); if (0 == sock->dport) { IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : sock->dport = %u."IP_LF, sock->dport); goto inval_out; } /* allocate the TCB(association) */ ret = ipsctp_alloc_tcb(inst, (Ipsctp_addr *)sa, 0, /* alloc a new v-tag */ &new_tcb, IP_NULL); if ((ret < 0) || (IP_NULL == new_tcb)) return ret; /* set the connecting flag */ IP_BIT_SET(sock->flags, IPSCTP_STACK_CONST(SOCKET_FLAG_CONNECTING)); /* if more than one connectx address, then and the other addresses to remote transport address list */ if (addrs_cnt > 1) { ret = ipsctp_sock_connectx_add(new_tcb, second_sa, addrs_cnt - 1); if (0 < ret) { ret = IPNET_ERRNO(ENOBUFS); goto free_out; } } if (IP_TRUE == is_delay) { IP_BIT_SET(new_tcb->tcb_flags, IPSCTP_TCBC_FLAG_DELAY_CONNECT); ipsctp_timer_add(IP_NULL, new_tcb, new_tcb->primary_transport, IPSCTP_TIMO_T1); } else { /* send initialize packet to the peer */ ret = ipsctp_send_init(new_tcb); if (IPNET_ERRNO(ENOBUFS) == ret) { /* TODO: schedule connect retry */ } /* for RTO calculate */ (void)ipcom_microtime(&new_tcb->time_entered); } /* change the tcb state */ (void)ipsctp_change_state(new_tcb, IPSCTP_STATE_COOKIE_WAIT); if (ipsctp_style(sock, TCP)) IP_BIT_SET(inst->inst_flags, IPSCTP_SOCKET_CONNECTED); #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Connectx OK."IP_LF); #endif /* blocking if set NON-BLOCKING flag ONLY for connectx */ return (is_delay ? 0 : IPNET_ERRNO(EINPROGRESS)); inval_out: IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); free_out: IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : free out for error."IP_LF); (void)ipsctp_free_tcb(new_tcb); return ret; } /* *=========================================================================== * ipsctp_sock_connectx_complete *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * Parameters: sock - the set socket descriptor. * optval - the buffer stored the value of this option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_STATIC int ipsctp_sock_connectx_complete(Ipsctp_socket *sock, IP_CONST void *optval, Ip_socklen_t optlen) { Ipsctp_instance *inst = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; if ((IP_NULL == sock) || (IP_NULL == optval)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } /* check instance point */ IPSCTP_SOCK_INST_CHECK(sock, inst); if (ipsctp_style(sock, TCP) && (IP_BIT_ISSET(sock->flags, IPSCTP_STACK_CONST(SOCKET_FLAG_CONNECTED)))) tcb = (Ipsctp_tcb *)ipcom_list_first(&inst->assoc_list); else tcb = ipsctp_sock_lookup_da(sock, (struct Ip_sockaddr *)optval); if (IP_NULL == tcb) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No match TCB."IP_LF); return IPNET_ERRNO(ENOENT); } if (IP_BIT_ISSET(tcb->tcb_flags, IPSCTP_TCBC_FLAG_DELAY_CONNECT)) { IP_BIT_CLR(tcb->tcb_flags, IPSCTP_TCBC_FLAG_DELAY_CONNECT); /* for RTO calculate */ (void)ipcom_microtime(&tcb->time_entered); (void)ipsctp_send_init(tcb); } else { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Operation already in progress."IP_LF); return IPNET_ERRNO(EALREADY); } #if defined(IPSCTP_DEBUG) IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Connectx complete OK."IP_LF); #endif return 0; } /* *=========================================================================== * ipsctp_setsockopt *=========================================================================== * Description: base on draft-ietf-tsvwg-sctpsocket-17 * 7.2. setsockopt(), getsockopt() * * Applications use setsockopt() and getsockopt() to set or retrieve * socket options. Socket options are used to change the default * behavior of sockets calls. They are described in Section 8 * * The syntax is: * * ret = getsockopt(int sd, int level, int optname, void *optval, * socklen_t *optlen); * ret = setsockopt(int sd, int level, int optname, const void *optval, * socklen_t optlen); * * * sd - the socket descriptor. * level - set to IPPROTO_SCTP for all SCTP options. * optname - the option name. * optval - the buffer to store the value of the option. * optlen - the size of the buffer (or the length of the option * returned). * * All socket options set on a 1-to-1 listening sockets also apply all * accepted sockets. All socket options set on a 1-to-many socket using * the assoc_id 0 applies for all future assocs on the socket. * Parameters: sock - the socket descriptor. * level - set to IPPROTO_SCTP for all SCTP options. * optname - the option name. * optval - the buffer stored the value of this option. * optlen - the size of the buffer. * Returns: 0 = success, <0 error code. * */ IP_PUBLIC int ipsctp_setsockopt(Ipsctp_socket *sock, int level, int optname, IP_CONST void *optval, Ip_socklen_t optlen) { Ipsctp_instance *inst = IP_NULL; int ret = 0; int val = 0; if ((IP_NULL == sock) || (IP_NULL == (inst = sock->sctp_inst))) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF); return IPNET_ERRNO(EINVAL); } #if defined(IPSCTP_DEBUG) IPSCTP_DBG4(IPSCTP_DBG_SOCK, "IPSCTP : Set socket option(sock %u, level 0x%x, optname 0x%x, optlen %u)."IP_LF, sock->ipcom.fd, level, optname, optlen); #endif if (IP_SOL_SOCKET == level) { switch (optname) { case IP_SO_RCVLOWAT: if (optlen != sizeof(int)) return IPNET_ERRNO(EINVAL); val = *(int*) optval; if (val <= 0 || val > sock->recv_max_bytes) return IPNET_ERRNO(EINVAL); inst->recv_lowat = (Ip_u32) val; break; case IP_SO_SNDLOWAT: if (optlen != sizeof(int)) return IPNET_ERRNO(EINVAL); val = *(int*) optval; if (val <= 0 || val > sock->send_max_bytes) return IPNET_ERRNO(EINVAL); inst->send_lowat = (Ip_u32) val; break; case IP_SO_LINGER: { IP_CONST struct Ip_linger *l = optval; if (optlen != sizeof(struct Ip_linger)) return IPNET_ERRNO(EINVAL); if (l->l_onoff) inst->linger_time = l->l_linger; else inst->linger_time = IPSCTP_INVALID_LINGER_TIME; break; } default: /* Let the caller handle this option */ return IPNET_ERRNO(EOPNOTSUPP); } return 0; } /* if level is not SCTP, return to upper layer to handle */ if (IP_IPPROTO_SCTP != level) return IPNET_ERRNO(EOPNOTSUPP); /* process optname cases */ switch (optname) { case IPSCTP_RTOINFO: ret = ipsctp_setsockopt_rtoinfo(sock, optval, optlen); break; case IPSCTP_ASSOCINFO: ret = ipsctp_setsockopt_associnfo(sock, optval, optlen); break; case IPSCTP_DEFAULT_SEND_PARAM: ret = ipsctp_setsockopt_default_send_param(sock, optval, optlen); break; case IPSCTP_PRIMARY_ADDR: ret = ipsctp_setsockopt_primary_addr(sock, optval, optlen); break; case IPSCTP_PEER_ADDR_PARAMS: ret = ipsctp_setsockopt_peer_addr_params(sock, optval, optlen); break; case IPSCTP_CONTEXT: ret = ipsctp_setsockopt_context(sock, optval, optlen); break; #if 0 case IPSCTP_AUTH_ACTIVE_KEY: ret = ipsctp_setsockopt_active_key(sock, optval, optlen); break; #endif case IPSCTP_EVENTS: ret = ipsctp_setsockopt_events(sock, optval, optlen); break; case IPSCTP_INITMSG: ret = ipsctp_setsockopt_initmsg(sock, optval, optlen); break; case IPSCTP_MAXSEG: ret = ipsctp_setsockopt_maxseg(sock, optval, optlen); break; case IPSCTP_DELAYED_SACK: ret = ipsctp_setsockopt_delay_ack(sock, optval, optlen); break; case IPSCTP_MAX_BURST: ret = ipsctp_setsockopt_max_burst(sock, optval, optlen); break; case IPSCTP_BINDX_ADD_ADDR: ret = ipsctp_sock_bindx_add(sock, optval, optlen); break; case IPSCTP_BINDX_REM_ADDR: ret = ipsctp_sock_bindx_del(sock, optval, optlen); break; case IPSCTP_CONNECTX: ret = ipsctp_sock_connectx(sock, optval, optlen, IP_FALSE); break; case IPSCTP_CONNECTX_DELAY: ret = ipsctp_sock_connectx(sock, optval, optlen, IP_TRUE); break; case IPSCTP_CONNECTX_COMPLETE: ret = ipsctp_sock_connectx_complete(sock, optval, optlen); break; case IPSCTP_NODELAY: val = *(int*)optval; if (val) { IP_BIT_SET(inst->feature_flags , IPSCTP_INST_FLAGS_NODELAY); IP_BIT_SET(sock->flags, IPSCTP_STACK_CONST(SOCKET_FLAG_TCP_NODELAY)); } else { IP_BIT_CLR(inst->feature_flags , IPSCTP_INST_FLAGS_NODELAY); IP_BIT_CLR(sock->flags, IPSCTP_STACK_CONST(SOCKET_FLAG_TCP_NODELAY)); } break; /* This boolean flag is used to enable or disable explicit end of record * (EOR) marking. When this option is enabled, a user may make multiple * send system calls to send a record and must indicate that they are * finished sending a particular record by including on the send the * SCTP_EOR flag. If this boolean flag is disabled then each individual * send system call is considered to have a SCTP_EOR indicator set on it * implicitly without the user having to explicitly add this flag. */ case IPSCTP_EXPLICIT_EOR: val = *(int*)optval; if (val) IP_BIT_SET(inst->feature_flags, IPSCTP_INST_FLAG_EXPLICIT_EOR); else IP_BIT_CLR(inst->feature_flags, IPSCTP_INST_FLAG_EXPLICIT_EOR); break; /* This option will enable or disable the use of the extended version of * the sctp_sndrcvinfo structure. If this option is disabled, then the * normal sctp_sndrcvinfo structure is returned in all receive message * calls. If this option is enabled then the sctp_extrcvinfo structure * is returned in all receive message calls. */ case IPSCTP_USE_EXT_RCVINFO: val = *(int*)optval; if (val) IP_BIT_SET(inst->feature_flags, IPSCTP_INST_FLAG_EXT_RCVINFO); else IP_BIT_CLR(inst->feature_flags, IPSCTP_INST_FLAG_EXT_RCVINFO); break; /* This socket option is a boolean flag which turns on or off mapped V4 * addresses. If this option is turned on and the socket is type * PF_INET6, then IPv4 addresses will be mapped to V6 representation. * If this option is turned off, then no mapping will be done of V4 * addresses and a user will receive both PF_INET6 and PF_INET type * addresses on the socket. */ case IPSCTP_I_WANT_MAPPED_V4_ADDR: if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V6) && IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V4)) { val = *(int*)optval; if (val) IP_BIT_SET(inst->feature_flags, IPSCTP_INST_FLAG_WANT_MAPPED_V4); else IP_BIT_CLR(inst->feature_flags, IPSCTP_INST_FLAG_WANT_MAPPED_V4); } else ret = IPNET_ERRNO(EINVAL); break; /* This option is a on/off flag and is passed an integer where a non- * zero is on and a zero is off. If enabled no SCTP message * fragmentation will be performed. Instead if a message being sent * exceeds the current PMTU size, the message will NOT be sent and * instead a error will be indicated to the user. */ case IPSCTP_DISABLE_FRAGMENTS: val = *(int*)optval; if (val) IP_BIT_SET(inst->feature_flags, IPSCTP_INST_FLAGS_NO_FRAGMENT); else IP_BIT_CLR(inst->feature_flags, IPSCTP_INST_FLAGS_NO_FRAGMENT); break; /* pls refer to IPSCTP_FRAGMENT_INTERLEAVE's comment */ case IPSCTP_FRAGMENT_INTERLEAVE: val = *(int*)optval; if (IPSCTP_FRAG_LEVEL_2 == val) { IP_BIT_SET(inst->feature_flags, IPSCTP_INST_FLAG_FRAG_INTERLEAVE); IP_BIT_SET(inst->feature_flags, IPSCTP_INST_FLAG_INTERLEAVE_STRMS); } else if (IPSCTP_FRAG_LEVEL_1 == val) { IP_BIT_SET(inst->feature_flags, IPSCTP_INST_FLAG_FRAG_INTERLEAVE); IP_BIT_CLR(inst->feature_flags, IPSCTP_INST_FLAG_INTERLEAVE_STRMS); } else if (IPSCTP_FRAG_LEVEL_0 == val) { IP_BIT_CLR(inst->feature_flags, IPSCTP_INST_FLAG_FRAG_INTERLEAVE); IP_BIT_CLR(inst->feature_flags, IPSCTP_INST_FLAG_INTERLEAVE_STRMS); } else ret = IPNET_ERRNO(EINVAL); break; /* This option will set or get the SCTP partial delivery point. This * point is the size of a message where the partial delivery API will be * invoked to help free up rwnd space for the peer. Setting this to a * lower value will cause partial deliveries to happen more often. The * calls argument is an integer that sets or gets the partial delivery * point. Note also that the call will fail if the user attempts to set * this value larger than the socket receive buffer size. * Note that any single message having a length smaller than or equal to * the SCTP partial delivery point will be delivered in one single read * call as long as the user provided buffer is large enough to hold the * message. */ case IPSCTP_PARTIAL_DELIVERY_POINT: if (*(Ip_u32 *)optval >= sock->send_max_bytes) ret = IPNET_ERRNO(EINVAL); else inst->pd_point = *(Ip_u32 *)optval; break; /* This socket option is applicable to the one-to-many style socket * only. When set it will cause associations that are idle for more * than the specified number of seconds to automatically close using the * graceful shutdown procedure. An association being idle is defined as * an association that has NOT sent or received user data. The special * value of '0' indicates that no automatic close of any associations * should be performed, this is the default value. The option expects * an integer defining the number of seconds of idle time before an * association is closed. * An application using this option should enable receiving the * association change notification. This is the only mechanism an * application is informed about the closing of an association. After * an association is closed, the association ID assigned to it can be * reused. An application should be aware of this to avoid the possible * problem of sending data to an incorrect peer end point. */ case IPSCTP_AUTOCLOSE: if (ipsctp_style(sock, UDP)) { inst->autoclose_time = *(Ip_u32 *)optval; IP_BIT_SET(inst->inst_flags, IPSCTP_SOCKET_FLAG_AUTOCLOSE); } else ret = IPNET_ERRNO(EINVAL); break; default: #if 0 if (IPSCTP_SOCKOPT_R == ipsctp_sockopt_check_rw(optname)) { ret = 0; break; } #endif return IPNET_ERRNO(ENOPROTOOPT); } return ret; } /* *=========================================================================== * ipsctp_ioctl *=========================================================================== * Description: Handler for SCTP specific IOCTL * Parameters: sock - The socket affected by the ioctl * request - The ioctl request being issued * argp - The argument associated with the request * Returns: 0 = success * <0 = socket error code * */ IP_PUBLIC int ipsctp_ioctl(Ipsctp_socket *sock, unsigned long request, void *argp) { int *iargp = argp; if ((!sock)||(!sock->sctp_inst)||(IP_NULL == iargp)) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."); return IPNET_ERRNO(EINVAL); } switch (request) { case IP_SIOCATMARK: *iargp = 0; break; default: return IPNET_ERRNO(EOPNOTSUPP); } return 0; } /* *=========================================================================== * ipsctp_get_netstat_info *=========================================================================== * Description: Returns the SCTP netstat information for the socket. * Parameters: sock - the socket descriptor. * Returns: The SCTP netstat information pointer. * NOTE : THE CALLER NEED FREE THE MEMORY, * in ipnet_netstat_print_entries. * */ IP_PUBLIC void * ipsctp_get_netstat_info(Ipsctp_socket *sock) { Ipsctp_netstat_tcbinfo *tcbinfo = IP_NULL; Ipsctp_netstatinfo *netstatinfo = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; Ipsctp_laddr_entry *laddr = IP_NULL; Ipsctp_transport *tp = IP_NULL; Ipsctp_instance *inst = IP_NULL; Ip_u32 cur_addr_num = 0; Ip_u32 cur_tcb_num = 0; Ip_u32 i = 0; if ((IP_NULL == sock) || (IP_IPPROTO_SCTP != sock->proto)) return IP_NULL; inst = sock->sctp_inst; netstatinfo = (Ipsctp_netstatinfo *)ipcom_malloc(sizeof(Ipsctp_netstatinfo)); if (IP_NULL == netstatinfo) return IP_NULL; (void)ipcom_memset(netstatinfo, 0, sizeof(Ipsctp_netstatinfo)); netstatinfo->tcb_count = ipsctp_list_size(&inst->assoc_list); if (0 == netstatinfo->tcb_count) { ipcom_free(netstatinfo); return IP_NULL; } /* malloc all the TCB display memory for the special socket */ netstatinfo->tcb = (Ipsctp_netstat_tcbinfo *)ipcom_malloc(netstatinfo->tcb_count * sizeof(Ipsctp_netstat_tcbinfo)); if (IP_NULL == netstatinfo->tcb) { ipcom_free(netstatinfo); return IP_NULL; } (void)ipcom_memset(netstatinfo->tcb, 0, netstatinfo->tcb_count * sizeof(Ipsctp_netstat_tcbinfo)); /* copy local address */ if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUNDALL)) { netstatinfo->laddr_count = 1; } else { netstatinfo->laddr_count = ipsctp_list_size(&inst->laddr_list); } /* copy the local address list for netstat display */ if (netstatinfo->laddr_count > 0) { netstatinfo->laddr = (union Ip_sockaddr_union *)ipcom_malloc(netstatinfo->laddr_count * sizeof(union Ip_sockaddr_union)); if (IP_NULL == netstatinfo->laddr) goto free_ret; (void)ipcom_memset(netstatinfo->laddr, 0, netstatinfo->laddr_count * sizeof(union Ip_sockaddr_union)); cur_addr_num = 0; if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUNDALL)) { ipsctp_list_for_each(&inst->laddr_list, laddr, Ipsctp_laddr_entry) { if (sock->ipcom.domain == laddr->laddr.sa.sa_family) { ipcom_memcpy((void *)&netstatinfo->laddr[cur_addr_num++], (void *)&laddr->laddr, sizeof(union Ip_sockaddr_union)); break; /* only one */ } } } else { ipsctp_list_for_each(&inst->laddr_list, laddr, Ipsctp_laddr_entry) { ipcom_memcpy((void *)&netstatinfo->laddr[cur_addr_num++], (void *)&laddr->laddr, sizeof(union Ip_sockaddr_union)); } } } cur_tcb_num = 0; /* loop all the association to store in netstatinfo structure */ ipsctp_list_for_each(&inst->assoc_list, tcb, Ipsctp_tcb) { tcbinfo = &netstatinfo->tcb[cur_tcb_num]; cur_tcb_num++; tcbinfo->state = (int)tcb->state; /* copy transport address */ tcbinfo->raddr_count = ipsctp_list_size(&tcb->transport_list); if (tcbinfo->raddr_count > 0) { tcbinfo->raddr = (union Ip_sockaddr_union *)ipcom_malloc(tcbinfo->raddr_count * sizeof(union Ip_sockaddr_union)); if (IP_NULL == tcbinfo->raddr) goto free_ret; (void)ipcom_memset(tcbinfo->raddr, 0, tcbinfo->raddr_count * sizeof(union Ip_sockaddr_union)); cur_addr_num = 0; ipsctp_list_for_each(&tcb->transport_list, tp, Ipsctp_transport) { ipcom_memcpy((void *)&tcbinfo->raddr[cur_addr_num++], (void *)&tp->paddr, sizeof(union Ip_sockaddr_union)); } } } return netstatinfo; free_ret: /* free all the memory if not complete the operation */ for (i = 0; i < cur_tcb_num; i++) { if (netstatinfo->tcb[i].raddr_count > 0) { ipcom_free(netstatinfo->tcb[i].raddr); } } if (netstatinfo->laddr_count > 0) { ipcom_free(netstatinfo->laddr); } ipcom_free(netstatinfo->tcb); ipcom_free(netstatinfo); return IP_NULL; } /* *=========================================================================== * ipsctp_sys_send_init *=========================================================================== * Description: send INIT packet. * Parameters: fd - socket descriptor. * vtag - association verification tag. * Returns: None. * */ IP_PUBLIC void ipsctp_sys_send_init(Ip_fd fd, Ip_u32 vtag) { Ipsctp_socket *sock = IP_NULL; Ipsctp_tcb *tcb = IP_NULL; int ret = 0; if ((fd < IPNET_SOCKET_STARTFD) || (fd >= ipnet_conf_max_sockets) || (IP_NULL == (sock = ipnet->sockets[fd])) || (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_CLOSED))) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : invalid parameter for sending INIT."IP_LF); return; } /* get TCB by the specified association ID */ tcb = ipsctp_tcb_lookup_by_instance(sock->sctp_inst, vtag, IP_NULL, IP_NULL); if (IP_NULL == tcb) { IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : send init but no TCB."IP_LF); return; } /* for RTO calculate */ (void)ipcom_microtime(&tcb->time_entered); /* set the connecting flag */ IP_BIT_SET(sock->flags, IPSCTP_STACK_CONST(SOCKET_FLAG_CONNECTING)); IP_BIT_SET(tcb->tcb_flags, IPSCTP_TCBC_FLAG_CONNECTING); /* change the tcb state, NOTE : must first change state, ipsctp_output * will use this state(UDP will often use). */ (void)ipsctp_change_state(tcb, IPSCTP_STATE_COOKIE_WAIT); /* send initialize packet to the peer */ ret = ipsctp_send_init(tcb); if (ret < 0) { ipcom_printf("IPSCTP : send init error(%d)."IP_LF, ret); IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : send init error(%d)."IP_LF, ret); return; } IPSCTP_DBGSTAT_INC(IPSCTP_DBGSTAT_SYS_SEND_INIT); return; } /* **************************************************************************** * 10 PUBLIC FUNCTIONS **************************************************************************** */