1163 lines
36 KiB
C
1163 lines
36 KiB
C
/* ipsctp_ip4.c - IPV4 Address Related Routines in IPSCTP */
|
|
|
|
/*
|
|
* Copyright (c) 2008, 2018 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
|
|
--------------------
|
|
01b,31oct18,lan fix TOS value isn't applied in an IP Header of SCTP INIT ACK
|
|
Packet. (VXW6-86973)
|
|
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 <ipnet_h.h>
|
|
|
|
#include "ipsctp_h.h"
|
|
|
|
#ifdef IPCOM_WV_INSTRUMENTATION
|
|
#include <ipcom_windview.h>
|
|
#endif
|
|
|
|
#ifdef IPCOM_USE_INET
|
|
/*
|
|
****************************************************************************
|
|
* 4 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 TYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
IP_STATIC Ip_bool ipsctp_v4_cmp_addr(const Ipsctp_addr *addr1, const Ipsctp_addr *addr2);
|
|
IP_STATIC Ip_bool ipsctp_v4_cmp_addr_key(IP_CONST Ipsctp_addr *addr, IP_CONST union Ip_in_addr_union *key, Ip_u16 domain);
|
|
IP_STATIC void ipsctp_load_in_addr(IP_CONST void *addr, union Ip_in_addr_union * in_address);
|
|
IP_STATIC void ipsctp_v4_transmit(IP_CONST void *dst, IP_CONST void *src, Ipcom_pkt *pkt);
|
|
IP_STATIC void ipsctp_v4_load_sctp_addr(IP_CONST void *addr, IP_CONST Ip_u16 port, Ipsctp_addr *sctp_addr);
|
|
IP_STATIC void *ipsctp_get_in_addr(IP_CONST Ipsctp_addr *addr);
|
|
IP_STATIC Ip_u32 ipsctp_get_scope_id(IP_CONST Ipsctp_addr *addr);
|
|
IP_STATIC void ipsctp_v4_get_mtu_and_mss(Ipsctp_tcb *tcb, \
|
|
Ipsctp_transport *transport, Ipnet_route_entry **rt_ret);
|
|
IP_STATIC void ipsctp_v4_addr_set_port(Ipsctp_addr *sctp_addr, IP_CONST Ip_u16 port);
|
|
IP_STATIC int ipsctp_v4_src_addr_selection(Ipsctp_tcb *tcb, Ipnet_route_entry *rt,\
|
|
Ip_u16 lport, Ipsctp_transport *transport);
|
|
IP_STATIC void ipsctp_v4_rotate_src_addr(Ipsctp_tcb *tcb, Ipsctp_transport *transport);
|
|
IP_STATIC void ipsctp_v4_set_addr_for_socket(Ipsctp_transport *transport, Ipsctp_socket *sock);
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
#if 0
|
|
IP_STATIC Ipsctp_af_ops sctp_af_inet =
|
|
{
|
|
.sa_family = IP_AF_INET,
|
|
.cmp_addr = ipsctp_v4_cmp_addr,
|
|
.load_in_addr = ipsctp_load_in_addr,
|
|
.cmp_addr_key = ipsctp_v4_cmp_addr_key,
|
|
.ip_transmit = ipsctp_v4_transmit,
|
|
.load_soaddr = ipsctp_v4_load_sockaddr,
|
|
};
|
|
#endif
|
|
IP_STATIC Ipsctp_af_ops sctp_af_inet;
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 LOCAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_v4_init()
|
|
*===========================================================================
|
|
* Description: This routine initialize IPV4 related operations in SCTP.
|
|
* Parameters:
|
|
* Returns: void
|
|
*
|
|
*/
|
|
IP_PUBLIC void
|
|
ipsctp_v4_init()
|
|
{
|
|
|
|
ipcom_list_init(&sctp_af_inet.list);
|
|
sctp_af_inet.cmp_addr = ipsctp_v4_cmp_addr;
|
|
sctp_af_inet.cmp_addr_key = ipsctp_v4_cmp_addr_key;
|
|
sctp_af_inet.load_soaddr = ipsctp_v4_load_sctp_addr;
|
|
sctp_af_inet.load_in_addr = ipsctp_load_in_addr;
|
|
sctp_af_inet.get_in_addr = ipsctp_get_in_addr;
|
|
sctp_af_inet.get_scope_id = ipsctp_get_scope_id;
|
|
sctp_af_inet.ip_transmit = ipsctp_v4_transmit;
|
|
sctp_af_inet.get_mtu_and_mss = ipsctp_v4_get_mtu_and_mss;
|
|
sctp_af_inet.addr_set_port = ipsctp_v4_addr_set_port;
|
|
sctp_af_inet.src_addr_selection = ipsctp_v4_src_addr_selection;
|
|
sctp_af_inet.rotate_src_addr = ipsctp_v4_rotate_src_addr;
|
|
sctp_af_inet.set_addr_for_socket = ipsctp_v4_set_addr_for_socket;
|
|
sctp_af_inet.sa_family = IP_AF_INET;
|
|
ipsctp_register_af(&sctp_af_inet);
|
|
return;
|
|
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_v4_cmp_addr()
|
|
*===========================================================================
|
|
* Description: This routine compares two IPV4 SCTP address.
|
|
* Parameters:
|
|
* Returns: IP_TRUE if equal, otherwise IP_FALSE.
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipsctp_v4_cmp_addr(const Ipsctp_addr *addr1,
|
|
const Ipsctp_addr *addr2)
|
|
{
|
|
if (addr1->sa.sa_family == addr2->sa.sa_family
|
|
&& addr1->sin.sin_port == addr2->sin.sin_port
|
|
&& addr1->sin.sin_addr.s_addr == addr2->sin.sin_addr.s_addr)
|
|
return IP_TRUE;
|
|
|
|
return IP_FALSE;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_v4_cmp_addr_key()
|
|
*===========================================================================
|
|
* Description: This routine compares lookup key with sctp address.
|
|
* Parameters:
|
|
* Returns: IP_TRUE if equal, otherwise IP_FALSE.
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipsctp_v4_cmp_addr_key(const Ipsctp_addr *addr,
|
|
const union Ip_in_addr_union *key,
|
|
Ip_u16 domain
|
|
)
|
|
{
|
|
if ((domain == IP_AF_INET)
|
|
&&(addr->sa.sa_family == IP_AF_INET)
|
|
&&(key->in.s_addr == addr->sin.sin_addr.s_addr))
|
|
{
|
|
return IP_TRUE;
|
|
}
|
|
return IP_FALSE;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_load_in_addr()
|
|
*===========================================================================
|
|
* Description: This routine get ipv4 address from input buffer.
|
|
* Parameters:
|
|
* Returns: void.
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipsctp_load_in_addr(IP_CONST void *addr, union Ip_in_addr_union * in_address)
|
|
{
|
|
if (addr)
|
|
in_address->in.s_addr = IP_GET_32ON16((Ip_u32 *)addr);
|
|
else
|
|
in_address->in.s_addr = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_ipv4_transmit()
|
|
*===========================================================================
|
|
* Description: This routine fills out Ipnet_ip4_output_param and transmit
|
|
* ipv4 packet.
|
|
* Parameters:
|
|
* Returns: void.
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipsctp_v4_transmit(IP_CONST void *dst,
|
|
IP_CONST void *src,
|
|
Ipcom_pkt *pkt)
|
|
{
|
|
Ipnet_socket * sock;
|
|
Ip_u8 tos = 0;
|
|
|
|
if (pkt->fd > 0)
|
|
{
|
|
IPNET_FD_TO_SOCKET(pkt->fd, sock);
|
|
tos = sock->ip4->type_of_service;
|
|
}
|
|
|
|
(void)ipnet_ip4_tx(tos,
|
|
255u,
|
|
IP_IPPROTO_SCTP,
|
|
src,
|
|
dst,
|
|
0,
|
|
pkt);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_v4_load_sockaddr()
|
|
*===========================================================================
|
|
* Description: This routine make an Ipsctp_addr from a void addr buffer
|
|
* Parameters:
|
|
* Returns: void.
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipsctp_v4_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_in));
|
|
sctp_addr->sin.sin_family = IP_AF_INET;
|
|
sctp_addr->sin.sin_port = ip_htons(port);
|
|
sctp_addr->sin.sin_addr.s_addr = *(Ip_u32 *)addr;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_get_in_addr()
|
|
*===========================================================================
|
|
* Description: This routine gets in_addr from Ipsctp_addr
|
|
* Parameters:
|
|
* Returns: void *.
|
|
*
|
|
*/
|
|
IP_STATIC void *
|
|
ipsctp_get_in_addr(IP_CONST Ipsctp_addr *addr)
|
|
{
|
|
return (void *)&(addr->sin.sin_addr.s_addr);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_get_scope_id()
|
|
*===========================================================================
|
|
* Description: This routine gets scope id from Ipsctp_addr
|
|
* Parameters:
|
|
* Returns: Ip_u32.
|
|
*
|
|
*/
|
|
IP_STATIC Ip_u32
|
|
ipsctp_get_scope_id(IP_CONST Ipsctp_addr *addr)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_v4_get_mtu_and_mss()
|
|
*===========================================================================
|
|
* Description: This routine gets transport mtu and updates the transport
|
|
* mtu & mss if necessary.
|
|
* Parameters:
|
|
* Returns: void.
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipsctp_v4_get_mtu_and_mss(struct Ipsctp_tcb_struct *tcb,
|
|
struct Ipsctp_transport_struct *transport,
|
|
Ipnet_route_entry **rt_ret)
|
|
{
|
|
Ipnet_route_entry *rt;
|
|
int ret;
|
|
Ip_u32 mtu;
|
|
Ip_u32 mss;
|
|
IP_CONST void *saddr = IP_NULL;
|
|
Ipnet_flow_spec flow_spec;
|
|
|
|
/* Determine source address to use in policy lookup */
|
|
if (IP_BIT_ISTRUE(transport->state, IPSCTP_ADDR_SRC_SELECTED)
|
|
&& IP_BIT_ISFALSE(transport->state, IPSCTP_ADDR_NOT_IN_RT))
|
|
saddr = &transport->saddr.sin.sin_addr;
|
|
else
|
|
saddr = &ip_inaddr_any;
|
|
|
|
ipnet_flow_spec_from_info(&flow_spec,
|
|
tcb->sock->vr_index,
|
|
saddr,
|
|
&transport->paddr.sin.sin_addr,
|
|
0,
|
|
0,
|
|
IP_NULL,
|
|
IP_NULL,
|
|
ipnet_ip4_flow_spec_from_info);
|
|
|
|
/* Look up the route */
|
|
ret = ipnet_route_lookup_ecmp(IP_AF_INET,
|
|
tcb->sock->vr_index,
|
|
0,
|
|
&transport->paddr.sin.sin_addr,
|
|
0,
|
|
0,
|
|
IP_NULL,
|
|
&flow_spec,
|
|
&rt);
|
|
|
|
if (ret < 0) /* no route entry*/
|
|
mtu = ipnet_conf_ip4_min_mtu;
|
|
else
|
|
/* Note: if the path mtu has changed, IPNET should notify SCTP via callback.
|
|
So we don't need pmtu timer here. */
|
|
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_in src;
|
|
struct Ip_sockaddr_in dst;
|
|
|
|
src.sin_family = IP_AF_INET;
|
|
src.sin_addr.s_addr = transport->paddr.sin.sin_addr.s_addr;
|
|
src.sin_port = ip_htons(tcb->instance->lport);
|
|
dst.sin_addr.s_addr = transport->saddr.sin.sin_addr.s_addr;
|
|
dst.sin_port = ip_htons(tcb->rport);
|
|
|
|
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/* - sizeof(Ipsctp_data_chunk_hdr)*/;
|
|
|
|
mss = IP_MIN((Ip_u32)IPSCTP_SENDBUF_MAX(tcb), mss);
|
|
|
|
transport->mss = mss & ~0x3;
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG6(IPSCTP_DBG_MTU,
|
|
"IPSCTP: transport's MTU changed (dst:%s, src:%s, pmtu:%u, "\
|
|
"mss:%u, state:0x%x, now:%u)."IP_LF,
|
|
ipsctp_addr_ntop(&transport->paddr, ipnet->log_buf, 128),
|
|
ipsctp_addr_ntop(&transport->saddr, &ipnet->log_buf[128], 128),
|
|
transport->pmtu,
|
|
transport->mss,
|
|
transport->state,
|
|
ipstack->msec_now
|
|
);
|
|
#endif
|
|
|
|
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 defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG6(IPSCTP_DBG_MTU,
|
|
"IPSCTP: tcb's MTU changed (vtag: %u, lport: %u, rport: %u, "\
|
|
"pmtu: %u, mss: %u, now: %u)."IP_LF,
|
|
tcb->my_vtag,
|
|
tcb->instance->lport,
|
|
tcb->rport,
|
|
tcb->pmtu,
|
|
tcb->mss,
|
|
ipstack->msec_now
|
|
);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (rt_ret)
|
|
{
|
|
*rt_ret = rt;
|
|
}
|
|
return ;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_icmp4_cb
|
|
*===========================================================================
|
|
* Description: Process the incoming ICMP packet, change path MTU and record
|
|
* other ICMP errors.
|
|
* Parameters: pkt - the ICMP packet, must not be modified or freed by
|
|
* this function
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC void
|
|
ipsctp_icmp4_cb(IP_CONST Ipcom_pkt *pkt)
|
|
{
|
|
Ipnet_pkt_icmp *icmp_hdr;
|
|
Ipnet_pkt_ip *failing_ip_pkt;
|
|
Ipsctp_sctp_hdr *sctp_hdr;
|
|
int icmp_len;
|
|
Ipsctp_tcb *tcb;
|
|
Ipsctp_transport *transport;
|
|
struct Ip_in_addr src;
|
|
struct Ip_in_addr dst;
|
|
Ipsctp_af_ops *af_ops;
|
|
Ip_bool has_init = IP_FALSE;
|
|
Ip_u16 sctp_len;
|
|
|
|
icmp_len = ipcom_pkt_get_length(pkt);
|
|
|
|
if (icmp_len < (8 /*IPNET_ICMP_HDR_SIZE*/ + IPNET_IP_HDR_SIZE + IPSCTP_SIZE_SCTP_HDR))
|
|
{
|
|
IPNET_STATS(icmp4_input_hdrsize2++);
|
|
IPCOM_MIB2(icmpInErrors++);
|
|
IPCOM_MIB2_SYSWI_U32_ADD(v4, icmpStatsInErrors, 1);
|
|
return;
|
|
}
|
|
|
|
/* get icmp header */
|
|
icmp_hdr = ipcom_pkt_get_data(pkt, 0);
|
|
|
|
if (icmp_hdr->type != 3 /*IPNET_ICMP4_TYPE_DST_UNREACHABLE*/
|
|
|| (icmp_hdr->code != 4 /*IPNET_ICMP4_CODE_DST_NEEDFRAG*/
|
|
&& icmp_hdr->code != 2 /*IPNET_ICMP4_CODE_DST_UNREACH_PROTO*/)
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
failing_ip_pkt = (Ipnet_pkt_ip *) icmp_hdr->data.failing_pkt.ip;
|
|
|
|
sctp_hdr = (Ipsctp_sctp_hdr *)((Ip_u8 *)failing_ip_pkt + IPNET_IP4_GET_HDR_OCTET_LEN(failing_ip_pkt));
|
|
sctp_len = ip_ntohs(failing_ip_pkt->len) - IPNET_IP4_GET_HDR_OCTET_LEN(failing_ip_pkt);
|
|
|
|
dst.s_addr = IPNET_IP4_GET_IPADDR(failing_ip_pkt->dst);
|
|
src.s_addr = IPNET_IP4_GET_IPADDR(failing_ip_pkt->src);
|
|
|
|
/* get address family specific operations */
|
|
af_ops = ipsctp_get_af_ops(IP_AF_INET);
|
|
|
|
tcb = ipsctp_tcb_lookup(ip_ntohl(sctp_hdr->vtag),
|
|
pkt->vr_index,
|
|
IP_IPPROTO_SCTP,
|
|
&src, ip_ntohs(sctp_hdr->sport),
|
|
&dst, ip_ntohs(sctp_hdr->dport),
|
|
af_ops,
|
|
&transport
|
|
);
|
|
if (tcb == NULL || transport == NULL)
|
|
return;
|
|
|
|
/* 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;
|
|
|
|
/* Check if the init chunk length is valid */
|
|
if ((sctp_len - IPSCTP_SIZE_SCTP_HDR) < sizeof(Ipsctp_init_chunk_hdr))
|
|
{
|
|
return;
|
|
}
|
|
|
|
init_chunk = (Ipsctp_init_chunk_hdr *)((Ip_u8 *)sctp_hdr + IPSCTP_SIZE_SCTP_HDR);
|
|
if ((init_chunk->hdr.type != IPSCTP_CHUNK_TYPE_INIT)
|
|
|| (init_chunk->fixed_params.init_tag != tcb->my_vtag))
|
|
{
|
|
return;
|
|
}
|
|
has_init = IP_TRUE;
|
|
|
|
}
|
|
else if (ip_ntohl(sctp_hdr->vtag) != tcb->my_vtag)
|
|
return;
|
|
|
|
transport->err_cnt++;
|
|
|
|
switch(icmp_hdr->code)
|
|
{
|
|
case 4 /*IPNET_ICMP4_CODE_DST_NEEDFRAG*/:
|
|
ipsctp_notify_cb(tcb, transport, IPSCTP_NOTIFY_EVENT_PATH_MTU);
|
|
break;
|
|
|
|
case 2 /*IPNET_ICMP4_CODE_DST_UNREACH_PROTO*/:
|
|
/*
|
|
* 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 INITchunk and the association is in the COOKIE-WAIT state,
|
|
* handle the ICMP message like an ABORT.
|
|
*/
|
|
if (!has_init || IPSCTP_GET_STATE(tcb) == IPSCTP_STATE_COOKIE_WAIT)
|
|
{
|
|
ipsctp_handle_abort_for_icmp(tcb);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* TODO, report icmp error to application, see iptcp_icmp4_report */
|
|
/* TODO, process the ICMP errors */
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_v4_addr_set_port()
|
|
*===========================================================================
|
|
* Description: This routine set port of Ipsctp_addr.
|
|
* Parameters:
|
|
* Returns: void.
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipsctp_v4_addr_set_port(Ipsctp_addr *sctp_addr, IP_CONST Ip_u16 port)
|
|
{
|
|
sctp_addr->sin.sin_port = ip_htons(port);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_v4_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_v4_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 /* selected source address */
|
|
)
|
|
{
|
|
|
|
Ipsctp_laddr_entry *addr;
|
|
Ip_u8 match_len = 0;
|
|
Ip_u8 new_match_len = 0;
|
|
IP_CONST struct Ip_in_addr *in_srcaddr = 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 */
|
|
{
|
|
in_srcaddr =
|
|
ipnet_ip4_get_src_addr(tcb->sock->vr_index,
|
|
&dstaddr->sin.sin_addr,
|
|
rt,
|
|
IP_NULL);
|
|
if (in_srcaddr)
|
|
{
|
|
goto src_selected;
|
|
}
|
|
|
|
}
|
|
|
|
/* 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_ip4_addr_entry *inet4_addr;
|
|
/* iterate all ipv4 addresses of route entry's outgoing interface */
|
|
for (inet4_addr = rt->netif->inet4_addr_list;
|
|
inet4_addr;
|
|
inet4_addr = inet4_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_INET)
|
|
&& (inet4_addr->ipaddr_n == addr->laddr.sin.sin_addr.s_addr))
|
|
{
|
|
/* find one match address, get its match length */
|
|
new_match_len = (Ip_u8)ipsctp_addr_match_len(
|
|
&dstaddr->sin.sin_addr,
|
|
&addr->laddr.sin.sin_addr,
|
|
sizeof(struct Ip_in_addr)/sizeof(Ip_u32));
|
|
|
|
if (new_match_len > match_len)
|
|
{
|
|
match_len = new_match_len;
|
|
in_srcaddr = &addr->laddr.sin.sin_addr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (in_srcaddr)
|
|
{
|
|
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_INET)
|
|
{
|
|
in_srcaddr = &addr->laddr.sin.sin_addr;
|
|
IP_BIT_SET(transport->state, IPSCTP_ADDR_NOT_IN_RT);
|
|
break;
|
|
}
|
|
}
|
|
|
|
src_selected:
|
|
|
|
if (in_srcaddr)
|
|
{
|
|
srcaddr->sin.sin_addr.s_addr
|
|
= in_srcaddr->s_addr;
|
|
srcaddr->sin.sin_port = ip_htons(lport);
|
|
IP_BIT_SET(transport->state, IPSCTP_ADDR_SRC_SELECTED);
|
|
ret = 0;
|
|
}
|
|
else
|
|
{
|
|
/* report the error */
|
|
IPCOM_LOG0(ERR, "ipsctp_v4_src_addr_selection: failed to select source address when BOUNDALL\n");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_v4_set_addr_for_socket()
|
|
*===========================================================================
|
|
* Description: This routine set the src and dst address in socket.
|
|
* Parameters:
|
|
* Returns: void.
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipsctp_v4_set_addr_for_socket(Ipsctp_transport *transport,
|
|
Ipsctp_socket *sock)
|
|
{
|
|
sock->ip4->saddr_n = transport->saddr.sin.sin_addr.s_addr;
|
|
sock->ip4->daddr_n = transport->paddr.sin.sin_addr.s_addr;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_v4_rotate_src_addr()
|
|
*===========================================================================
|
|
* Description: This routine rotates the source address for transport.
|
|
* Parameters:
|
|
* Returns: void.
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipsctp_v4_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_ip4_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 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, tcb->sock->vr_index, i)
|
|
{
|
|
/* Don't add loopback address */
|
|
if IP_BIT_ISSET(netif->ipcom.flags, IP_IFF_LOOPBACK)
|
|
{
|
|
continue;
|
|
}
|
|
/* IPV4 is allowed */
|
|
if (tcb->instance->inst_flags & IPSCTP_SOCKET_BOUND_V4)
|
|
{
|
|
struct Ipnet_ip4_addr_entry_struct *addr_entry;
|
|
for (addr_entry = netif->inet4_addr_list;
|
|
IP_NULL != addr_entry;
|
|
addr_entry = addr_entry->next)
|
|
{
|
|
/* Don't add any kind of addresses except unicast */
|
|
if (addr_entry->type != IPNET_ADDR_TYPE_UNICAST)
|
|
{
|
|
/* Not in scope , unspecified */
|
|
continue;
|
|
}
|
|
|
|
if (found_match)
|
|
{
|
|
found_addr = addr_entry;
|
|
break;
|
|
}
|
|
|
|
if (addr_entry->ipaddr_n == transport->saddr.sin.sin_addr.s_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)
|
|
{
|
|
transport->saddr.sin.sin_addr.s_addr = found_addr->ipaddr_n;
|
|
#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_INET
|
|
&& addr->laddr.sin.sin_addr.s_addr == transport->saddr.sin.sin_addr.s_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_INET)
|
|
{
|
|
transport->saddr.sin.sin_addr.s_addr = addr_next->laddr.sin.sin_addr.s_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
|
|
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_INET */
|
|
#ifdef IPCOM_FORWARDER_NAE
|
|
|
|
IP_PUBLIC Ip_bool
|
|
ipsctp_v4v6_cmp_addr(const Ipsctp_addr *addr1,
|
|
const Ipsctp_addr *addr2,
|
|
Ip_sa_family_t address_family);
|
|
|
|
IP_PUBLIC void *
|
|
ipsctp_v4v6_get_in_addr(IP_CONST Ipsctp_addr *addr,Ip_sa_family_t address_family);
|
|
|
|
|
|
IP_PUBLIC Ip_bool
|
|
ipsctp_v4v6_cmp_addr_key(const Ipsctp_addr *addr,
|
|
const union Ip_in_addr_union *key,
|
|
Ip_u16 domain,
|
|
Ip_sa_family_t address_family
|
|
);
|
|
|
|
IP_PUBLIC Ip_u32
|
|
ipsctp_v4v6_get_scope_id(IP_CONST Ipsctp_addr *addr,Ip_sa_family_t address_family);
|
|
|
|
IP_PUBLIC void
|
|
ipsctp_v4v6_get_mtu_and_mss(struct Ipsctp_tcb_struct *tcb,
|
|
struct Ipsctp_transport_struct *transport,
|
|
Ipnet_route_entry **rt_ret,
|
|
Ip_sa_family_t address_family);
|
|
|
|
|
|
IP_PUBLIC void
|
|
ipsctp_v4v6_rotate_src_addr(Ipsctp_tcb *tcb,
|
|
Ipsctp_transport *transport,
|
|
Ip_sa_family_t address_family);
|
|
|
|
IP_PUBLIC void
|
|
ipsctp_v4v6_set_addr_for_socket(Ipsctp_transport *transport,
|
|
Ipsctp_socket *sock,
|
|
Ip_sa_family_t address_family);
|
|
|
|
IP_PUBLIC void
|
|
ipsctp_v4v6_transmit(IP_CONST void *dst,
|
|
IP_CONST void *src,
|
|
Ipcom_pkt *pkt,
|
|
Ip_sa_family_t address_family);
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
IP_PUBLIC Ip_bool
|
|
ipsctp_v4v6_cmp_addr(const Ipsctp_addr *addr1,
|
|
const Ipsctp_addr *addr2,
|
|
Ip_sa_family_t address_family)
|
|
{
|
|
switch(address_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
return ipsctp_v4_cmp_addr(addr1,addr2);
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
return ipsctp_v6_cmp_addr(addr1,addr2);
|
|
break;
|
|
#endif
|
|
default:
|
|
return IP_FALSE;
|
|
}
|
|
}
|
|
|
|
IP_PUBLIC void *
|
|
ipsctp_v4v6_get_in_addr(IP_CONST Ipsctp_addr *addr,Ip_sa_family_t address_family)
|
|
{
|
|
switch(address_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
return ipsctp_get_in_addr(addr);
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
return ipsctp_get_in6_addr(addr);
|
|
break;
|
|
#endif
|
|
default:
|
|
return IP_NULL;
|
|
}
|
|
|
|
}
|
|
|
|
IP_PUBLIC Ip_bool
|
|
ipsctp_v4v6_cmp_addr_key(const Ipsctp_addr *addr,
|
|
const union Ip_in_addr_union *key,
|
|
Ip_u16 domain,
|
|
Ip_sa_family_t address_family)
|
|
{
|
|
switch(address_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
return ipsctp_v4_cmp_addr_key(addr,key,domain);
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
return ipsctp_v6_cmp_addr_key(addr,key,domain);
|
|
break;
|
|
#endif
|
|
default:
|
|
return IP_FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
IP_PUBLIC Ip_u32
|
|
ipsctp_v4v6_get_scope_id(IP_CONST Ipsctp_addr *addr,Ip_sa_family_t address_family)
|
|
{
|
|
switch(address_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
return ipsctp_get_scope_id(addr);
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
return ipsctp_get6_scope_id(addr);
|
|
break;
|
|
#endif
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
IP_PUBLIC void
|
|
ipsctp_v4v6_get_mtu_and_mss(struct Ipsctp_tcb_struct *tcb,
|
|
struct Ipsctp_transport_struct *transport,
|
|
Ipnet_route_entry **rt_ret,
|
|
Ip_sa_family_t address_family)
|
|
{
|
|
switch(address_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
return ipsctp_v4_get_mtu_and_mss(tcb,transport,rt_ret);
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
return ipsctp_v6_get_mtu_and_mss(tcb,transport,rt_ret);
|
|
break;
|
|
#endif
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
IP_PUBLIC void
|
|
ipsctp_v4v6_rotate_src_addr(Ipsctp_tcb *tcb,
|
|
Ipsctp_transport *transport,
|
|
Ip_sa_family_t address_family)
|
|
{
|
|
switch(address_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
return ipsctp_v4_rotate_src_addr(tcb,transport);
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
return ipsctp_v6_rotate_src_addr(tcb,transport);
|
|
break;
|
|
#endif
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
IP_PUBLIC void
|
|
ipsctp_v4v6_set_addr_for_socket(Ipsctp_transport *transport,
|
|
Ipsctp_socket *sock,
|
|
Ip_sa_family_t address_family)
|
|
{
|
|
switch(address_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
return ipsctp_v4_set_addr_for_socket(transport,sock);
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
return ipsctp_v6_set_addr_for_socket(transport,sock);
|
|
break;
|
|
#endif
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
IP_PUBLIC void
|
|
ipsctp_v4v6_transmit(IP_CONST void *dst,
|
|
IP_CONST void *src,
|
|
Ipcom_pkt *pkt,
|
|
Ip_sa_family_t address_family)
|
|
{
|
|
switch(address_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
return ipsctp_v4_transmit(dst,src,pkt);
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
return ipsctp_v6_transmit(dst,src,pkt);
|
|
break;
|
|
#endif
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
#endif
|