929 lines
31 KiB
C
929 lines
31 KiB
C
/* 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 <ipcom_clib.h>
|
|
#include <ipcom_err.h>
|
|
#include <ipcom_inet.h>
|
|
#include <ipcom_os.h>
|
|
#include <ipcom_slab.h>
|
|
#include <ipcom_syslog.h>
|
|
#include <ipcom_sysvar.h>
|
|
|
|
#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*/
|