svn commit: r297990 - head/sys/netinet

Michael Tuexen tuexen at FreeBSD.org
Thu Apr 14 19:59:23 UTC 2016


Author: tuexen
Date: Thu Apr 14 19:59:21 2016
New Revision: 297990
URL: https://svnweb.freebsd.org/changeset/base/297990

Log:
  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

Modified:
  head/sys/netinet/ip_icmp.h
  head/sys/netinet/sctp_usrreq.c

Modified: head/sys/netinet/ip_icmp.h
==============================================================================
--- head/sys/netinet/ip_icmp.h	Thu Apr 14 19:51:29 2016	(r297989)
+++ head/sys/netinet/ip_icmp.h	Thu Apr 14 19:59:21 2016	(r297990)
@@ -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.

Modified: head/sys/netinet/sctp_usrreq.c
==============================================================================
--- head/sys/netinet/sctp_usrreq.c	Thu Apr 14 19:51:29 2016	(r297989)
+++ head/sys/netinet/sctp_usrreq.c	Thu Apr 14 19:59:21 2016	(r297990)
@@ -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 *
 					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 */


More information about the svn-src-all mailing list