/* ipsctp_ip6.c - IPV6 Address Related Routines in IPSCTP */ /* * Copyright (c) 2008 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. */ /* modification history -------------------- 01a,15jul08, . */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #ifndef IPCOM_KERNEL #define IPCOM_KERNEL #endif #include "ipsctp_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include #include "ipsctp_h.h" #ifdef IPCOM_USE_INET6 /* **************************************************************************** * 4 DEFINES **************************************************************************** */ /* **************************************************************************** * 5 TYPES **************************************************************************** */ /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 8 DATA **************************************************************************** */ #if 0 IP_STATIC Ipsctp_af_ops sctp_af_inet6 = { .sa_family = AF_INET6, .cmp_addr = ipsctp_v6_cmp_addr, .load_in_addr = ipsctp_load_in6_addr, .cmp_addr_key = ipsctp_v6_cmp_addr_key, .ip_transmit = ipsctp_v6_transmit, .load_soaddr = ipsctp_v6_load_sockaddr, }; #endif IP_STATIC Ipsctp_af_ops sctp_af_inet6; /* **************************************************************************** * 9 LOCAL FUNCTIONS **************************************************************************** */ IP_STATIC void ipsctp_load_in6_addr(IP_CONST void *addr, union Ip_in_addr_union * in_address); IP_STATIC void ipsctp_v6_load_sctp_addr(IP_CONST void *addr, IP_CONST Ip_u16 port, Ipsctp_addr *sctp_addr); IP_STATIC void ipsctp_v6_addr_set_port(Ipsctp_addr *sctp_addr, IP_CONST Ip_u16 port); IP_STATIC int ipsctp_v6_src_addr_selection(Ipsctp_tcb *tcb, Ipnet_route_entry *rt,\ Ip_u16 lport, Ipsctp_transport *transport); Ip_bool ipsctp_v6_cmp_addr(IP_CONST Ipsctp_addr *addr1, IP_CONST Ipsctp_addr *addr2); Ip_bool ipsctp_v6_cmp_addr_key(IP_CONST Ipsctp_addr *addr, IP_CONST union Ip_in_addr_union *key, Ip_u16 domain); void ipsctp_v6_transmit(IP_CONST void *dst, IP_CONST void *src, Ipcom_pkt *pkt); void *ipsctp_get_in6_addr(IP_CONST Ipsctp_addr *addr6); Ip_u32 ipsctp_get6_scope_id(IP_CONST Ipsctp_addr *addr6); void ipsctp_v6_get_mtu_and_mss(Ipsctp_tcb *tcb, Ipsctp_transport *transport, Ipnet_route_entry **rt_ret); void ipsctp_v6_set_addr_for_socket(Ipsctp_transport *transport,Ipsctp_socket *sock); void ipsctp_v6_rotate_src_addr(Ipsctp_tcb *tcb,Ipsctp_transport *transport); /* **************************************************************************** * 10 PUBLIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipsctp_v6_init() *=========================================================================== * Description: This routine initialize IPV6 related operations in SCTP. * Parameters: * Returns: void * */ IP_PUBLIC void ipsctp_v6_init() { ipcom_list_init(&sctp_af_inet6.list); sctp_af_inet6.cmp_addr = ipsctp_v6_cmp_addr; sctp_af_inet6.cmp_addr_key = ipsctp_v6_cmp_addr_key; sctp_af_inet6.load_soaddr = ipsctp_v6_load_sctp_addr; sctp_af_inet6.load_in_addr = ipsctp_load_in6_addr; sctp_af_inet6.get_in_addr = ipsctp_get_in6_addr; sctp_af_inet6.get_scope_id = ipsctp_get6_scope_id; sctp_af_inet6.ip_transmit = ipsctp_v6_transmit; sctp_af_inet6.get_mtu_and_mss = ipsctp_v6_get_mtu_and_mss; sctp_af_inet6.addr_set_port = ipsctp_v6_addr_set_port; sctp_af_inet6.src_addr_selection = ipsctp_v6_src_addr_selection; sctp_af_inet6.rotate_src_addr = ipsctp_v6_rotate_src_addr; sctp_af_inet6.set_addr_for_socket = ipsctp_v6_set_addr_for_socket; sctp_af_inet6.sa_family = IP_AF_INET6; ipsctp_register_af(&sctp_af_inet6); return; } /* *=========================================================================== * ipsctp_v6_cmp_addr() *=========================================================================== * Description: This routine compares two IPV6 SCTP address. * Parameters: * Returns: IP_TRUE if equal, otherwise IP_FALSE. * */ Ip_bool ipsctp_v6_cmp_addr(IP_CONST Ipsctp_addr *addr1, IP_CONST Ipsctp_addr *addr2) { if (addr1->sa.sa_family != addr2->sa.sa_family) { /* V4-Mapped-V6 */ if (addr1->sa.sa_family == IP_AF_INET && addr2->sa.sa_family == IP_AF_INET6 && IP_IN6_IS_ADDR_V4MAPPED(&addr2->sin6.sin6_addr)) { if (addr2->sin6.sin6_port == addr1->sin.sin_port && addr2->sin6.sin6_addr.in6.addr32[3] == addr1->sin.sin_addr.s_addr) return IP_TRUE; } if (addr1->sa.sa_family == IP_AF_INET6 && addr2->sa.sa_family == IP_AF_INET && IP_IN6_IS_ADDR_V4MAPPED(&addr1->sin6.sin6_addr)) { if (addr1->sin6.sin6_port == addr2->sin.sin_port && addr1->sin6.sin6_addr.in6.addr32[3] == addr2->sin.sin_addr.s_addr) return IP_TRUE; } return IP_FALSE; } /* Link-Local address */ if (IP_IN6_IS_ADDR_LINK_LOCAL(&addr1->sin6.sin6_addr)) { if (addr1->sin6.sin6_scope_id && addr2->sin6.sin6_scope_id && addr1->sin6.sin6_scope_id != addr2->sin6.sin6_scope_id) { return IP_FALSE; } } return IP_IN6_ARE_ADDR_EQUAL(&addr1->sin6.sin6_addr, &addr2->sin6.sin6_addr); } /* *=========================================================================== * ipsctp_v6_cmp_addr_key() *=========================================================================== * Description: This routine compares lookup key with sctp address. * Parameters: * Returns: IP_TRUE if equal, otherwise IP_FALSE. * */ Ip_bool ipsctp_v6_cmp_addr_key(const Ipsctp_addr *addr, const union Ip_in_addr_union *key, Ip_u16 domain) { if ((domain == addr->sa.sa_family) &&(IP_IN6_ARE_ADDR_EQUAL(&key->in6, &addr->sin6.sin6_addr))) { return IP_TRUE; } return IP_FALSE; } /* *=========================================================================== * ipsctp_load_in6_addr() *=========================================================================== * Description: This routine get ipv6 address from input buffer. * Parameters: * Returns: void. * */ IP_STATIC void ipsctp_load_in6_addr(IP_CONST void *addr, union Ip_in_addr_union * in_address) { if (addr) IPNET_IP6_SET_ADDR(&in_address->in6, addr); else ipcom_memset(&in_address->in6, 0, sizeof(struct Ip_in6_addr)); return; } /* *=========================================================================== * ipsctp_v6_transmit() *=========================================================================== * Description: This routine fills out Ipnet_ip6_output_param and transmit * ipv6 packet. * Parameters: * Returns: void. * */ void ipsctp_v6_transmit(IP_CONST void *dst, IP_CONST void *src, Ipcom_pkt *pkt) { (void)ipnet_ip6_tx(0, 255, IP_IPPROTO_SCTP, 0, src, dst, pkt->ifindex, /* zone_id, from scope_id */ 0, pkt); return; } /* *=========================================================================== * ipsctp_v6_load_sockaddr() *=========================================================================== * Description: This routine make an Ip_sockaddr from a void addr buffer * Parameters: * Returns: void. * */ IP_STATIC void ipsctp_v6_load_sctp_addr(IP_CONST void *addr, IP_CONST Ip_u16 port, Ipsctp_addr *sctp_addr) { IPCOM_SA_LEN_SET(sctp_addr, sizeof(struct Ip_sockaddr_in6)); sctp_addr->sin6.sin6_family = IP_AF_INET6; sctp_addr->sin6.sin6_port = ip_htons(port); sctp_addr->sin6.sin6_flowinfo = 0; ipcom_memcpy(&sctp_addr->sin6.sin6_addr, addr, sizeof(struct Ip_in6_addr)); sctp_addr->sin6.sin6_scope_id = 0; return; } /* *=========================================================================== * ipsctp_get_in6_addr() *=========================================================================== * Description: This routine gets in_addr from Ipsctp_addr * Parameters: * Returns: void *. * */ void * ipsctp_get_in6_addr(IP_CONST Ipsctp_addr *addr6) { return (void *)&addr6->sin6.sin6_addr; } /* *=========================================================================== * ipsctp_get6_scope_id() *=========================================================================== * Description: This routine gets scope id from Ipsctp_addr * Parameters: * Returns: Ip_u32. * */ Ip_u32 ipsctp_get6_scope_id(IP_CONST Ipsctp_addr *addr6) { return addr6->sin6.sin6_scope_id; } /* *=========================================================================== * ipsctp_v6_get_mtu_and_mss *=========================================================================== * Description: This routine gets transport mtu and updates the transport * mtu & mss if necessary. * Parameters: * Returns: void. * */ void ipsctp_v6_get_mtu_and_mss(Ipsctp_tcb *tcb, Ipsctp_transport *transport, Ipnet_route_entry **rt_ret) { Ipnet_route_entry *rt; int ret; Ip_u32 mtu; Ip_u32 mss; ret = ipnet_route_lookup(IP_AF_INET6, tcb->sock->vr_index, IPCOM_ROUTE_TABLE_DEFAULT, 0, &transport->paddr.sin6.sin6_addr, ipsctp_get6_scope_id(&transport->paddr), 0, &rt); if (ret < 0) mtu = ipnet_conf_ip6_min_mtu; else mtu = (rt->metrics.rmx_mtu == 0) ? rt->netif->ipcom.mtu : IP_MIN(rt->netif->ipcom.mtu, rt->metrics.rmx_mtu); transport->pmtu = mtu & ~0x3; #if 0 #ifdef IPIPSEC2 { struct Ip_sockaddr_in6 src; struct Ip_sockaddr_in6 dst; src.sin6_family = IP_AF_INET6; src.sin6_port = ip_htons(tcb->instance->lport); src.sin6_scope_id = ipsctp_get6_scope_id(&transport->saddr); IPNET_IP6_SET_ADDR(&src.sin6_addr, &transport->saddr.sin.sin6_addr); dst.sin6_family = IP_AF_INET6; dst.sin6_port = ip_htons(tcb->rport); dst.sin6_scope_id = ipsctp_get6_scope_id(&transport->paddr); IPNET_IP6_SET_ADDR(&dst.sin6_addr, &&transport->paddr.sin.sin6_addr); mtu -= (Ip_u32)ipipsec_get_header_space((Ip_u8)IP_IPPROTO_SCTP, (struct Ip_sockaddr *)&src, (struct Ip_sockaddr *)&dst); } #endif /* IPIPSEC2 */ #endif mss = mtu - tcb->sock->max_hdrspace; mss = IP_MIN((Ip_u32)IPSCTP_SENDBUF_MAX(tcb), mss); transport->mss = mss & ~0x3; if (IP_BIT_ISFALSE(transport->state, IPSCTP_ADDR_UNCONFIRMED) && IP_BIT_ISSET(transport->state, IPSCTP_ADDR_REACHABLE)) { if (tcb->pmtu == 0 /* first remote address for this occasion */ || tcb->pmtu > transport->pmtu) { IPSCTP_DATA_TCBLOCK(tcb->instance->tcb_lock); tcb->pmtu = transport->pmtu; /* update tcb path mtu */ IPSCTP_DATA_TCBUNLOCK(tcb->instance->tcb_lock); } if (tcb->mss == 0 || tcb->mss > transport->mss) { tcb->mss = transport->mss; if (tcb->frag_point != 0) { tcb->mss = IP_MIN(tcb->mss, tcb->frag_point & ~0x3); } } } if (rt_ret) { *rt_ret = rt; } return; } /* *=========================================================================== * ipsctp_icmp6_cb *=========================================================================== * Description: process the incoming ICMP6 packet, change path mtu and record * other ICMP errors. * * Parameters: * Returns: * */ IP_PUBLIC void ipsctp_icmp6_cb(Ipcom_pkt *pkt, Ipnet_pkt_icmp6 *icmp6, Ipnet_pkt_ip6 *failed_ip6_hdr) { Ip_sa_family_t sa_family = IP_AF_INET; Ipsctp_tcb *tcb; Ipsctp_transport *transport; Ipsctp_af_ops *af_ops; Ipsctp_sctp_hdr *sctp_hdr; Ip_bool has_init = IP_FALSE; if (failed_ip6_hdr == IP_NULL || (IPNET_ICMP6_DST_UNREACH != icmp6->icmp6_type && IPNET_ICMP6_PACKET_TOO_BIG != icmp6->icmp6_type && IPNET_ICMP6_PARAM_PROB != icmp6->icmp6_type)) { goto free_out; } sctp_hdr = (Ipsctp_sctp_hdr *)&pkt->data[pkt->start]; #ifdef IPCOM_USE_INET if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_IPV4)) { sa_family = IP_AF_INET; } #endif #ifdef IPCOM_USE_INET6 if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_IPV6)) { sa_family = IP_AF_INET6; } #endif /* get address family specific operations */ af_ops = ipsctp_get_af_ops(sa_family); tcb = ipsctp_tcb_lookup(ip_ntohl(sctp_hdr->vtag), pkt->vr_index, IP_IPPROTO_SCTP, &failed_ip6_hdr->src, ip_ntohs(sctp_hdr->sport), &failed_ip6_hdr->dst, ip_ntohs(sctp_hdr->dport), af_ops, &transport ); if (tcb == NULL || transport == NULL) { goto free_out; } /* Appendix C. ICMP Handling, RFC4960: ICMP6) An implementation MUST validate that the Verification Tag contained in the ICMP message matches the Verification Tag of the peer. If the Verification Tag is not 0 and does NOT match, discard the ICMP message. If it is 0 and the ICMP message contains enough bytes to verify that the chunk type is an INIT chunk and that the Initiate Tag matches the tag of the peer, continue with ICMP7. If the ICMP message is too short or the chunk type or the Initiate Tag does not match, silently discard the packet. ICMP7) If the ICMP message is either a v6 "Packet Too Big" or a v4 "Fragmentation Needed", an implementation MAY process this information as defined for PATH MTU discovery. */ if (ip_ntohl(sctp_hdr->vtag) == 0) { Ipsctp_init_chunk_hdr *init_chunk; if ((pkt->end - pkt->start - IPSCTP_SIZE_SCTP_HDR) < sizeof(Ipsctp_init_chunk_hdr)) { goto free_out; } init_chunk = (Ipsctp_init_chunk_hdr *)&pkt->data[pkt->start + IPSCTP_SIZE_SCTP_HDR]; if ((init_chunk->hdr.type != IPSCTP_CHUNK_TYPE_INIT) || (init_chunk->fixed_params.init_tag != tcb->my_vtag)) { goto free_out; } has_init = IP_TRUE; } else if (ip_ntohl(sctp_hdr->vtag) != tcb->my_vtag) { goto free_out; } switch (icmp6->icmp6_type) { case IPNET_ICMP6_DST_UNREACH: /* Appendix C. ICMP Handling, RFC4960: ICMP9) If the ICMPv6 code is "Destination Unreachable", the implementation MAY mark the destination into the unreachable state or alternatively increment the path error counter. */ transport->err_cnt++; break; case IPNET_ICMP6_PACKET_TOO_BIG: ipsctp_notify_cb(tcb, transport, IPSCTP_NOTIFY_EVENT_PATH_MTU); break; case IPNET_ICMP6_PARAM_PROB: /* Appendix C. ICMP Handling, RFC4960: ICMP8) If the ICMP code is an "Unrecognized Next Header Type Encountered" or a "Protocol Unreachable", an implementation MUST treat this message as an abort with the T bit set if it does not contain an INIT chunk. If it does contain an INIT chunk and the association is in the COOKIE-WAIT state, handle the ICMP message like an ABORT. */ if (icmp6->icmp6_code == IPNET_ICMP6_PARAM_PROB_NXT_HDR) { if (!has_init || (IPSCTP_GET_STATE(tcb) == IPSCTP_STATE_COOKIE_WAIT)) { ipsctp_handle_abort_for_icmp(tcb); } } default: break; } free_out: ipcom_pkt_free(pkt); return; } /* *=========================================================================== * ipsctp_v6_addr_set_port() *=========================================================================== * Description: This routine set port of Ipsctp_addr. * Parameters: * Returns: void. * */ IP_STATIC void ipsctp_v6_addr_set_port(Ipsctp_addr *sctp_addr, IP_CONST Ip_u16 port) { sctp_addr->sin6.sin6_port = ip_htons(port); return; } /* *=========================================================================== * ipsctp_src_addr_selection *=========================================================================== * Description: This routine selects a source address for given peer address. * If SCTP TCB bounds all addresses, we invoke general IPV4 or IPV6 * routine to select source address. Otherwise, for bounded local * addresses and route entry's interface addresses, select the most * matchable source address. We use ipsctp_addr_match_len to get address's * match extent. If rt is NULL, don't select the source address. * Parameters: * Returns: 0 if success, or -1 if it fails to select one. * */ IP_STATIC int ipsctp_v6_src_addr_selection(Ipsctp_tcb *tcb, /* TCB pointer */ Ipnet_route_entry *rt, /* route entry for dstaddr, NULL if no route */ Ip_u16 lport, Ipsctp_transport *transport ) { Ipsctp_laddr_entry *addr; Ip_u8 match_len = 0; Ip_u8 new_match_len = 0; Ipsctp_addr *sctp_addr = IP_NULL; int ret = -1; Ipsctp_addr *dstaddr = &transport->paddr; /* peer or dst address of transport */ Ipsctp_addr *srcaddr = &transport->saddr; /* selected source address */ *srcaddr = *dstaddr; if (IP_BIT_ISSET(tcb->instance->inst_flags, IPSCTP_SOCKET_BOUNDALL)) /* bound all addresses */ { IP_CONST struct Ip_in6_addr *in6_srcaddr = IP_NULL; in6_srcaddr = ipnet_ip6_get_src_addr(tcb->sock->vr_index, &dstaddr->sin6.sin6_addr, dstaddr->sin6.sin6_scope_id, rt, IP_NULL, 0); if (in6_srcaddr) { ipcom_memcpy(&srcaddr->sin6.sin6_addr, in6_srcaddr, sizeof(struct Ip_in6_addr)); srcaddr->sin6.sin6_port = ip_htons(lport); IP_BIT_SET(transport->state, IPSCTP_ADDR_SRC_SELECTED); return 0; } } /* Iterate all addresses of route entry's interface, and if the address is in local bounded list, find the most matchable one */ if (rt && rt->netif) { Ipnet_ip6_addr_entry *inet6_addr; /* iterate all ipv6 addresses of route entry's outgoing interface */ for (inet6_addr = rt->netif->inet6_addr_list; inet6_addr; inet6_addr = inet6_addr->next) { for(addr = (Ipsctp_laddr_entry *)ipcom_list_first(&tcb->instance->laddr_list); addr; addr = (Ipsctp_laddr_entry *)ipcom_list_next(&addr->next) ) { if ((addr->laddr.sa.sa_family == IP_AF_INET6) && IP_IN6_ARE_ADDR_EQUAL(&addr->laddr.sin6.sin6_addr, &inet6_addr->addr)) { /* find one match address, get its match length */ new_match_len = ipsctp_addr_match_len( &dstaddr->sin6.sin6_addr, &addr->laddr.sin6.sin6_addr, sizeof(struct Ip_in6_addr)/sizeof(Ip_u32)); if (new_match_len > match_len) { match_len = new_match_len; sctp_addr = &addr->laddr; } } } } if (sctp_addr) { goto src_selected; } } /* randomly select source address, and rotate the source address when re-transmitting */ for(addr = (Ipsctp_laddr_entry *)ipcom_list_first(&tcb->instance->laddr_list); addr; addr = (Ipsctp_laddr_entry *)ipcom_list_next(&addr->next) ) { if (addr->laddr.sa.sa_family == IP_AF_INET6 && addr->laddr.sin6.sin6_scope_id == dstaddr->sin6.sin6_scope_id) { sctp_addr = &addr->laddr; IP_BIT_SET(transport->state, IPSCTP_ADDR_NOT_IN_RT); break; } } src_selected: if (sctp_addr) { *srcaddr = *sctp_addr; srcaddr->sin6.sin6_port = ip_htons(lport); IP_BIT_SET(transport->state, IPSCTP_ADDR_SRC_SELECTED); ret = 0; } else { /* report the error */ IPCOM_LOG0(ERR, "ipsctp_v6_src_addr_selection: failed to select source address when BOUNDALL\n"); } return ret; } /* *=========================================================================== * ipsctp_v6_set_addr_for_socket() *=========================================================================== * Description: This routine set the src and dst address in socket. * Parameters: * Returns: void. * */ void ipsctp_v6_set_addr_for_socket(Ipsctp_transport *transport, Ipsctp_socket *sock) { sock->ip6->saddr = transport->saddr.sin6.sin6_addr; sock->ip6->daddr = transport->paddr.sin6.sin6_addr; return; } /* *=========================================================================== * ipsctp_v6_rotate_src_addr() *=========================================================================== * Description: This routine rotates the source address for transport. * Parameters: * Returns: void. * */ void ipsctp_v6_rotate_src_addr(Ipsctp_tcb *tcb, Ipsctp_transport *transport) { #if defined(IPSCTP_DEBUG) IPSCTP_DBG7(IPSCTP_DBG_PATH, "IPSCTP :transport before rotating source (dst:%s, src:%s, err_cnt:%u, "\ "tcb->oa_err_cnt:%u, state:0x%x, rto:%u, now:%u)."IP_LF, ipsctp_addr_ntop(&transport->paddr, ipnet->log_buf, 128), ipsctp_addr_ntop(&transport->saddr, &ipnet->log_buf[128], 128), transport->err_cnt, tcb->oa_err_cnt, transport->state, transport->rto, ipstack->msec_now ); #endif if (tcb->instance->inst_flags & IPSCTP_SOCKET_BOUNDALL) { Ipnet_netif *netif; struct Ipnet_ip6_addr_entry_struct *found_addr = IP_NULL; Ip_bool found_match = IP_FALSE; Ip_u32 i; retry_first: /* Search for all interfaces */ IPNET_NETIF_FOR_EACH_ON_VR(netif, tcb->sock->vr_index, i) { /* Don't add loopback address */ if IP_BIT_ISSET(netif->ipcom.flags, IP_IFF_LOOPBACK) { continue; } /* IPV6 is allowed */ if (tcb->instance->inst_flags & IPSCTP_SOCKET_BOUND_V6) { struct Ipnet_ip6_addr_entry_struct *addr_entry; for (addr_entry = netif->inet6_addr_list; IP_NULL != addr_entry; addr_entry = addr_entry->next) { if (IP_IN6_IS_ADDR_UNSPECIFIED(&addr_entry->addr)) { /* Not in scope , unspecified */ continue; } /* only for unicast case */ if (IPNET_ADDR_TYPE_UNICAST != addr_entry->type) continue; if (found_match /* and match the scope_id */ && (ipnet_ip6_get_zone(netif->ipcom.ifindex, &addr_entry->addr) == transport->paddr.sin6.sin6_scope_id)) { found_addr = addr_entry; break; } if (IP_IN6_ARE_ADDR_EQUAL(&addr_entry->addr, &transport->saddr.sin6.sin6_addr)) { found_match = IP_TRUE; } } } if (found_addr) { break; } } if (found_match && found_addr == IP_NULL) { /* retry to get the first address */ goto retry_first; } if (found_match && found_addr) { ipcom_memcpy(&transport->saddr.sin6.sin6_addr, &found_addr->addr, sizeof(struct Ip_in6_addr)); #if defined(IPSCTP_DEBUG) IPSCTP_DBG7(IPSCTP_DBG_PATH, "IPSCTP :transport after rotating source (dst:%s, src:%s, err_cnt:%u, "\ "tcb->oa_err_cnt:%u, state:0x%x, rto:%u, now:%u)."IP_LF, ipsctp_addr_ntop(&transport->paddr, ipnet->log_buf, 128), ipsctp_addr_ntop(&transport->saddr, &ipnet->log_buf[128], 128), transport->err_cnt, tcb->oa_err_cnt, transport->state, transport->rto, ipstack->msec_now ); #endif } return; } if (ipsctp_list_size(&tcb->instance->laddr_list) < 2) { return; } else { Ipsctp_laddr_entry *addr; Ipsctp_laddr_entry *addr_next; /* find next address in local address list*/ addr = (Ipsctp_laddr_entry *)ipcom_list_first(&tcb->instance->laddr_list); while(addr) { if (addr->laddr.sa.sa_family == IP_AF_INET6 && IP_IN6_ARE_ADDR_EQUAL(&addr->laddr.sin6.sin6_addr, &transport->saddr.sin6.sin6_addr)) { break; } else { addr = (Ipsctp_laddr_entry *)ipcom_list_next(&addr->next); } } if (addr == IP_NULL) { return; } /* get next address in the local list */ addr_next = (Ipsctp_laddr_entry *)ipcom_list_next(&addr->next); if (addr_next == IP_NULL) { addr_next = (Ipsctp_laddr_entry *)ipcom_list_first(&tcb->instance->laddr_list); } while (addr_next && addr_next != addr) { if (addr_next->laddr.sa.sa_family == IP_AF_INET6 && addr_next->laddr.sin6.sin6_scope_id == transport->paddr.sin6.sin6_scope_id) { ipcom_memcpy(&transport->saddr.sin6.sin6_addr, &addr_next->laddr.sin6.sin6_addr, sizeof(struct Ip_in6_addr)); transport->saddr.sin6.sin6_scope_id = addr_next->laddr.sin6.sin6_scope_id; #if defined(IPSCTP_DEBUG) IPSCTP_DBG7(IPSCTP_DBG_PATH, "IPSCTP :transport after rotating source (dst:%s, src:%s, err_cnt:%u, "\ "tcb->oa_err_cnt:%u, state:0x%x, rto:%u, now:%u)."IP_LF, ipsctp_addr_ntop(&transport->paddr, ipnet->log_buf, 128), ipsctp_addr_ntop(&transport->saddr, &ipnet->log_buf[128], 128), transport->err_cnt, tcb->oa_err_cnt, transport->state, transport->rto, ipstack->msec_now ); #endif break; } else { addr_next = (Ipsctp_laddr_entry *)ipcom_list_next(&addr_next->next); if (addr_next == IP_NULL) { addr_next = (Ipsctp_laddr_entry *)ipcom_list_first(&tcb->instance->laddr_list); } } } return; } } #endif /*IPCOM_USE_INET6*/