Allow the handling of ICMP messages sent in response to SCTP packets

containing an INIT chunk. These need to be handled in case the peer
does not support SCTP and returns an ICMP messages indicating destination
unreachable, protocol unreachable.

MFC after:	1 week
This commit is contained in:
Michael Tuexen 2016-04-14 19:59:21 +00:00
parent f77b842746
commit 4d6b853ad6
2 changed files with 45 additions and 32 deletions

View File

@ -139,9 +139,10 @@ struct icmp {
/* This is the minimum length required by RFC 792. */
/*
* ICMP_ADVLENPREF is the preferred number of bytes which should be contiguous.
* It currently reflects the required minimum.
* SCTP needs additional 12 bytes to be able to access the initiate tag
* in packets containing an INIT chunk.
*/
#define ICMP_ADVLENPREF(p) (8 + ((p)->icmp_ip.ip_hl << 2) + 8)
#define ICMP_ADVLENPREF(p) (8 + ((p)->icmp_ip.ip_hl << 2) + 8 + 12)
/*
* Definition of type and code field values.

View File

@ -254,48 +254,49 @@ sctp_notify(struct sctp_inpcb *inp,
void
sctp_ctlinput(int cmd, struct sockaddr *sa, void *vip)
{
struct ip *ip = vip;
struct ip *outer_ip, *inner_ip;
struct sctphdr *sh;
struct icmp *icmph;
uint32_t vrf_id;
struct icmp *icmp;
struct sctp_inpcb *inp;
struct sctp_tcb *stcb;
struct sctp_nets *net;
struct sctp_init_chunk *ch;
struct sockaddr_in to, from;
/* FIX, for non-bsd is this right? */
vrf_id = SCTP_DEFAULT_VRFID;
if (sa->sa_family != AF_INET ||
((struct sockaddr_in *)sa)->sin_addr.s_addr == INADDR_ANY) {
return;
}
if (PRC_IS_REDIRECT(cmd)) {
ip = 0;
vip = NULL;
} else if ((unsigned)cmd >= PRC_NCMDS || inetctlerrmap[cmd] == 0) {
return;
}
if (ip) {
struct sctp_inpcb *inp = NULL;
struct sctp_tcb *stcb = NULL;
struct sctp_nets *net = NULL;
struct sockaddr_in to, from;
icmph = (struct icmp *)((caddr_t)ip - (sizeof(struct icmp) -
sizeof(struct ip)));
sh = (struct sctphdr *)((caddr_t)ip + (ip->ip_hl << 2));
if (vip != NULL) {
inner_ip = (struct ip *)vip;
icmp = (struct icmp *)((caddr_t)inner_ip -
(sizeof(struct icmp) - sizeof(struct ip)));
outer_ip = (struct ip *)((caddr_t)icmp - sizeof(struct ip));
sh = (struct sctphdr *)((caddr_t)inner_ip + (inner_ip->ip_hl << 2));
bzero(&to, sizeof(to));
bzero(&from, sizeof(from));
from.sin_family = to.sin_family = AF_INET;
from.sin_len = to.sin_len = sizeof(to);
from.sin_port = sh->src_port;
from.sin_addr = ip->ip_src;
from.sin_addr = inner_ip->ip_src;
to.sin_port = sh->dest_port;
to.sin_addr = ip->ip_dst;
to.sin_addr = inner_ip->ip_dst;
/*
* 'to' holds the dest of the packet that failed to be sent.
* 'from' holds our local endpoint address. Thus we reverse
* the to and the from in the lookup.
*/
inp = NULL;
net = NULL;
stcb = sctp_findassociation_addr_sa((struct sockaddr *)&to,
(struct sockaddr *)&from,
&inp, &net, 1, vrf_id);
&inp, &net, 1,
SCTP_DEFAULT_VRFID);
if ((stcb != NULL) &&
(net != NULL) &&
(inp != NULL) &&
@ -313,19 +314,30 @@ sctp_ctlinput(int cmd, struct sockaddr *sa, void *vip)
return;
}
} else {
/*
* In this case we could check if we got an
* INIT chunk and if the initiate tag
* matches. But this is not there yet...
*/
SCTP_TCB_UNLOCK(stcb);
return;
if (ntohs(outer_ip->ip_len) >=
sizeof(struct ip) +
8 + (inner_ip->ip_hl << 2) + 20) {
/*
* In this case we can check if we
* got an INIT chunk and if the
* initiate tag matches.
*/
ch = (struct sctp_init_chunk *)(sh + 1);
if ((ch->ch.chunk_type != SCTP_INITIATION) ||
(ntohl(ch->init.initiate_tag) != stcb->asoc.my_vtag)) {
SCTP_TCB_UNLOCK(stcb);
return;
}
} else {
SCTP_TCB_UNLOCK(stcb);
return;
}
}
sctp_notify(inp, stcb, net,
icmph->icmp_type,
icmph->icmp_code,
ntohs(ip->ip_len),
ntohs(icmph->icmp_nextmtu));
icmp->icmp_type,
icmp->icmp_code,
ntohs(inner_ip->ip_len),
ntohs(icmp->icmp_nextmtu));
} else {
if ((stcb == NULL) && (inp != NULL)) {
/* reduce ref-count */