diff --git a/sys/netinet/ip_icmp.h b/sys/netinet/ip_icmp.h index e69bfe1f9a9d..a04643294ae2 100644 --- a/sys/netinet/ip_icmp.h +++ b/sys/netinet/ip_icmp.h @@ -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. diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c index 5f43234af59e..1b23abca7f10 100644 --- a/sys/netinet/sctp_usrreq.c +++ b/sys/netinet/sctp_usrreq.c @@ -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 */