Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 7 Apr 2018 20:27:12 +0000 (UTC)
From:      Michael Tuexen <tuexen@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org
Subject:   svn commit: r332229 - in stable/11: sbin/ipfw sys/conf sys/netinet sys/netpfil/ipfw
Message-ID:  <201804072027.w37KRCNN088242@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: tuexen
Date: Sat Apr  7 20:27:11 2018
New Revision: 332229
URL: https://svnweb.freebsd.org/changeset/base/332229

Log:
  MFC r326233:
  
  Add to ipfw support for sending an SCTP packet containing an ABORT chunk.
  This is similar to the TCP case. where a TCP RST segment can be sent.
  
  There is one limitation: When sending an ABORT in response to an incoming
  packet, it should be tested if there is no ABORT chunk in the received
  packet. Currently, it is only checked if the first chunk is an ABORT
  chunk to avoid parsing the whole packet, which could result in a DOS attack.
  
  Thanks to Timo Voelker for helping me to test this patch.
  
  MFC r327200:
  
  When adding support for sending SCTP packets containing an ABORT chunk
  to ipfw in https://svnweb.freebsd.org/changeset/base/326233,
  a dependency on the SCTP stack was added to ipfw by accident.
  
  This was noted by Kevel Bowling in https://reviews.freebsd.org/D13594
  where also a solution was suggested. This patch is based on Kevin's
  suggestion, but implements the required SCTP checksum computation
  without any dependency on other SCTP sources.
  
  While there, do some cleanups and improve comments.
  
  Thanks to Kevin Kevin Bowling for reporting the issue and suggesting
  a fix.

Modified:
  stable/11/sbin/ipfw/ipfw.8
  stable/11/sbin/ipfw/ipfw2.c
  stable/11/sbin/ipfw/ipfw2.h
  stable/11/sys/conf/files
  stable/11/sys/netinet/ip_fw.h
  stable/11/sys/netinet/sctp_crc32.c
  stable/11/sys/netinet/sctp_crc32.h
  stable/11/sys/netpfil/ipfw/ip_fw2.c
  stable/11/sys/netpfil/ipfw/ip_fw_log.c
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/sbin/ipfw/ipfw.8
==============================================================================
--- stable/11/sbin/ipfw/ipfw.8	Sat Apr  7 20:23:34 2018	(r332228)
+++ stable/11/sbin/ipfw/ipfw.8	Sat Apr  7 20:27:11 2018	(r332229)
@@ -1173,6 +1173,14 @@ ipfw add reass all from any to any in
 .Ed
 .Pp
 is all you need at the beginning of your ruleset.
+.It Cm abort
+Discard packets that match this rule, and if the packet is an SCTP packet,
+try to send an SCTP packet containing an ABORT chunk.
+The search terminates.
+.It Cm abort6
+Discard packets that match this rule, and if the packet is an SCTP packet,
+try to send an SCTP packet containing an ABORT chunk.
+The search terminates.
 .El
 .Ss RULE BODY
 The body of a rule contains zero or more patterns (such as

Modified: stable/11/sbin/ipfw/ipfw2.c
==============================================================================
--- stable/11/sbin/ipfw/ipfw2.c	Sat Apr  7 20:23:34 2018	(r332228)
+++ stable/11/sbin/ipfw/ipfw2.c	Sat Apr  7 20:27:11 2018	(r332229)
@@ -244,6 +244,8 @@ static struct _s_x rule_eactions[] = {
 };
 
 static struct _s_x rule_actions[] = {
+	{ "abort6",		TOK_ABORT6 },
+	{ "abort",		TOK_ABORT },
 	{ "accept",		TOK_ACCEPT },
 	{ "pass",		TOK_ACCEPT },
 	{ "allow",		TOK_ACCEPT },
@@ -1507,6 +1509,8 @@ show_static_rule(struct cmdline_opts *co, struct forma
 		case O_REJECT:
 			if (cmd->arg1 == ICMP_REJECT_RST)
 				bprintf(bp, "reset");
+			else if (cmd->arg1 == ICMP_REJECT_ABORT)
+				bprintf(bp, "abort");
 			else if (cmd->arg1 == ICMP_UNREACH_HOST)
 				bprintf(bp, "reject");
 			else
@@ -1516,6 +1520,8 @@ show_static_rule(struct cmdline_opts *co, struct forma
 		case O_UNREACH6:
 			if (cmd->arg1 == ICMP6_UNREACH_RST)
 				bprintf(bp, "reset6");
+			else if (cmd->arg1 == ICMP6_UNREACH_ABORT)
+				bprintf(bp, "abort6");
 			else
 				print_unreach6_code(bp, cmd->arg1);
 			break;
@@ -3753,6 +3759,16 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize
 			break;
 		}
 		errx(EX_DATAERR, "Invalid state name %s", *av);
+		break;
+
+	case TOK_ABORT:
+		action->opcode = O_REJECT;
+		action->arg1 = ICMP_REJECT_ABORT;
+		break;
+
+	case TOK_ABORT6:
+		action->opcode = O_UNREACH6;
+		action->arg1 = ICMP6_UNREACH_ABORT;
 		break;
 
 	case TOK_ACCEPT:

Modified: stable/11/sbin/ipfw/ipfw2.h
==============================================================================
--- stable/11/sbin/ipfw/ipfw2.h	Sat Apr  7 20:23:34 2018	(r332228)
+++ stable/11/sbin/ipfw/ipfw2.h	Sat Apr  7 20:27:11 2018	(r332229)
@@ -81,6 +81,8 @@ enum tokens {
 	TOK_STARTBRACE,
 	TOK_ENDBRACE,
 
+	TOK_ABORT6,
+	TOK_ABORT,
 	TOK_ACCEPT,
 	TOK_COUNT,
 	TOK_EACTION,

Modified: stable/11/sys/conf/files
==============================================================================
--- stable/11/sys/conf/files	Sat Apr  7 20:23:34 2018	(r332228)
+++ stable/11/sys/conf/files	Sat Apr  7 20:27:11 2018	(r332229)
@@ -4094,7 +4094,7 @@ netinet/sctp_asconf.c		optional inet sctp | inet6 sctp
 netinet/sctp_auth.c		optional inet sctp | inet6 sctp
 netinet/sctp_bsd_addr.c		optional inet sctp | inet6 sctp
 netinet/sctp_cc_functions.c	optional inet sctp | inet6 sctp
-netinet/sctp_crc32.c		optional inet sctp | inet6 sctp
+netinet/sctp_crc32.c		optional inet | inet6
 netinet/sctp_indata.c		optional inet sctp | inet6 sctp
 netinet/sctp_input.c		optional inet sctp | inet6 sctp
 netinet/sctp_output.c		optional inet sctp | inet6 sctp

Modified: stable/11/sys/netinet/ip_fw.h
==============================================================================
--- stable/11/sys/netinet/ip_fw.h	Sat Apr  7 20:23:34 2018	(r332228)
+++ stable/11/sys/netinet/ip_fw.h	Sat Apr  7 20:27:11 2018	(r332229)
@@ -728,6 +728,8 @@ struct _ipfw_dyn_rule {
 
 #define	ICMP_REJECT_RST		0x100	/* fake ICMP code (send a TCP RST) */
 #define	ICMP6_UNREACH_RST	0x100	/* fake ICMPv6 code (send a TCP RST) */
+#define	ICMP_REJECT_ABORT	0x101	/* fake ICMP code (send an SCTP ABORT) */
+#define	ICMP6_UNREACH_ABORT	0x101	/* fake ICMPv6 code (send an SCTP ABORT) */
 
 /*
  * These are used for lookup tables.

Modified: stable/11/sys/netinet/sctp_crc32.c
==============================================================================
--- stable/11/sys/netinet/sctp_crc32.c	Sat Apr  7 20:23:34 2018	(r332228)
+++ stable/11/sys/netinet/sctp_crc32.c	Sat Apr  7 20:27:11 2018	(r332229)
@@ -33,29 +33,36 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
+#include "opt_sctp.h"
+
+#ifdef SCTP
 #include <netinet/sctp_os.h>
 #include <netinet/sctp.h>
 #include <netinet/sctp_crc32.h>
 #include <netinet/sctp_pcb.h>
+#else
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
 
+#include <netinet/sctp_crc32.h>
+#endif
 
-
 static uint32_t
 sctp_finalize_crc32c(uint32_t crc32c)
 {
 	uint32_t result;
-
 #if BYTE_ORDER == BIG_ENDIAN
 	uint8_t byte0, byte1, byte2, byte3;
-
 #endif
+
 	/* Complement the result */
 	result = ~crc32c;
 #if BYTE_ORDER == BIG_ENDIAN
 	/*
-	 * For BIG-ENDIAN.. aka Motorola byte order the result is in
-	 * little-endian form. So we must manually swap the bytes. Then we
-	 * can call htonl() which does nothing...
+	 * For BIG-ENDIAN platforms the result is in little-endian form. So
+	 * we must swap the bytes to return the result in network byte
+	 * order.
 	 */
 	byte0 = result & 0x000000ff;
 	byte1 = (result >> 8) & 0x000000ff;
@@ -64,56 +71,54 @@ sctp_finalize_crc32c(uint32_t crc32c)
 	crc32c = ((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3);
 #else
 	/*
-	 * For INTEL platforms the result comes out in network order. No
-	 * htonl is required or the swap above. So we optimize out both the
-	 * htonl and the manual swap above.
+	 * For LITTLE ENDIAN platforms the result is in already in network
+	 * byte order.
 	 */
 	crc32c = result;
 #endif
 	return (crc32c);
 }
 
+/*
+ * Compute the SCTP checksum in network byte order for a given mbuf chain m
+ * which contains an SCTP packet starting at offset.
+ * Since this function is also called by ipfw, don't assume that
+ * it is compiled on a kernel with SCTP support.
+ */
 uint32_t
 sctp_calculate_cksum(struct mbuf *m, uint32_t offset)
 {
-	/*
-	 * given a mbuf chain with a packetheader offset by 'offset'
-	 * pointing at a sctphdr (with csum set to 0) go through the chain
-	 * of SCTP_BUF_NEXT()'s and calculate the SCTP checksum. This also
-	 * has a side bonus as it will calculate the total length of the
-	 * mbuf chain. Note: if offset is greater than the total mbuf
-	 * length, checksum=1, pktlen=0 is returned (ie. no real error code)
-	 */
 	uint32_t base = 0xffffffff;
-	struct mbuf *at;
 
-	at = m;
-	/* find the correct mbuf and offset into mbuf */
-	while ((at != NULL) && (offset > (uint32_t)SCTP_BUF_LEN(at))) {
-		offset -= SCTP_BUF_LEN(at);	/* update remaining offset
-						 * left */
-		at = SCTP_BUF_NEXT(at);
-	}
-	while (at != NULL) {
-		if ((SCTP_BUF_LEN(at) - offset) > 0) {
-			base = calculate_crc32c(base,
-			    (unsigned char *)(SCTP_BUF_AT(at, offset)),
-			    (unsigned int)(SCTP_BUF_LEN(at) - offset));
+	while (offset > 0) {
+		KASSERT(m != NULL, ("sctp_calculate_cksum, offset > length of mbuf chain"));
+		if (offset < (uint32_t)m->m_len) {
+			break;
 		}
-		if (offset) {
-			/* we only offset once into the first mbuf */
-			if (offset < (uint32_t)SCTP_BUF_LEN(at))
-				offset = 0;
-			else
-				offset -= SCTP_BUF_LEN(at);
-		}
-		at = SCTP_BUF_NEXT(at);
+		offset -= m->m_len;
+		m = m->m_next;
 	}
+	if (offset > 0) {
+		base = calculate_crc32c(base,
+		    (unsigned char *)(m->m_data + offset),
+		    (unsigned int)(m->m_len - offset));
+		m = m->m_next;
+	}
+	while (m != NULL) {
+		base = calculate_crc32c(base,
+		    (unsigned char *)m->m_data,
+		    (unsigned int)m->m_len);
+		m = m->m_next;
+	}
 	base = sctp_finalize_crc32c(base);
 	return (base);
 }
 
-
+#ifdef SCTP
+/*
+ * Compute and insert the SCTP checksum in network byte order for a given
+ * mbuf chain m which contains an SCTP packet starting at offset.
+ */
 void
 sctp_delayed_cksum(struct mbuf *m, uint32_t offset)
 {
@@ -125,14 +130,15 @@ sctp_delayed_cksum(struct mbuf *m, uint32_t offset)
 	offset += offsetof(struct sctphdr, checksum);
 
 	if (offset + sizeof(uint32_t) > (uint32_t)(m->m_len)) {
-		SCTP_PRINTF("sctp_delayed_cksum(): m->len: %d,  off: %d.\n",
-		    (uint32_t)m->m_len, offset);
-		/*
-		 * XXX this shouldn't happen, but if it does, the correct
-		 * behavior may be to insert the checksum in the appropriate
-		 * next mbuf in the chain.
-		 */
+#ifdef INVARIANTS
+		panic("sctp_delayed_cksum(): m->m_len: %d, offset: %u.",
+		    m->m_len, offset);
+#else
+		SCTP_PRINTF("sctp_delayed_cksum(): m->m_len: %d, offset: %u.\n",
+		    m->m_len, offset);
+#endif
 		return;
 	}
 	*(uint32_t *)(m->m_data + offset) = checksum;
 }
+#endif

Modified: stable/11/sys/netinet/sctp_crc32.h
==============================================================================
--- stable/11/sys/netinet/sctp_crc32.h	Sat Apr  7 20:23:34 2018	(r332228)
+++ stable/11/sys/netinet/sctp_crc32.h	Sat Apr  7 20:27:11 2018	(r332229)
@@ -38,6 +38,8 @@ __FBSDID("$FreeBSD$");
 
 #if defined(_KERNEL)
 uint32_t sctp_calculate_cksum(struct mbuf *, uint32_t);
+#ifdef SCTP
 void sctp_delayed_cksum(struct mbuf *, uint32_t offset);
+#endif
 #endif				/* _KERNEL */
 #endif				/* __crc32c_h__ */

Modified: stable/11/sys/netpfil/ipfw/ip_fw2.c
==============================================================================
--- stable/11/sys/netpfil/ipfw/ip_fw2.c	Sat Apr  7 20:23:34 2018	(r332228)
+++ stable/11/sys/netpfil/ipfw/ip_fw2.c	Sat Apr  7 20:27:11 2018	(r332229)
@@ -80,6 +80,8 @@ __FBSDID("$FreeBSD$");
 #include <netinet/udp.h>
 #include <netinet/udp_var.h>
 #include <netinet/sctp.h>
+#include <netinet/sctp_crc32.h>
+#include <netinet/sctp_header.h>
 
 #include <netinet/ip6.h>
 #include <netinet/icmp6.h>
@@ -469,6 +471,113 @@ verify_path(struct in_addr src, struct ifnet *ifp, u_i
 }
 
 /*
+ * Generate an SCTP packet containing an ABORT chunk. The verification tag
+ * is given by vtag. The T-bit is set in the ABORT chunk if and only if
+ * reflected is not 0.
+ */
+
+static struct mbuf *
+ipfw_send_abort(struct mbuf *replyto, struct ipfw_flow_id *id, u_int32_t vtag,
+    int reflected)
+{
+	struct mbuf *m;
+	struct ip *ip;
+#ifdef INET6
+	struct ip6_hdr *ip6;
+#endif
+	struct sctphdr *sctp;
+	struct sctp_chunkhdr *chunk;
+	u_int16_t hlen, plen, tlen;
+
+	MGETHDR(m, M_NOWAIT, MT_DATA);
+	if (m == NULL)
+		return (NULL);
+
+	M_SETFIB(m, id->fib);
+#ifdef MAC
+	if (replyto != NULL)
+		mac_netinet_firewall_reply(replyto, m);
+	else
+		mac_netinet_firewall_send(m);
+#else
+	(void)replyto;		/* don't warn about unused arg */
+#endif
+
+	switch (id->addr_type) {
+	case 4:
+		hlen = sizeof(struct ip);
+		break;
+#ifdef INET6
+	case 6:
+		hlen = sizeof(struct ip6_hdr);
+		break;
+#endif
+	default:
+		/* XXX: log me?!? */
+		FREE_PKT(m);
+		return (NULL);
+	}
+	plen = sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr);
+	tlen = hlen + plen;
+	m->m_data += max_linkhdr;
+	m->m_flags |= M_SKIP_FIREWALL;
+	m->m_pkthdr.len = m->m_len = tlen;
+	m->m_pkthdr.rcvif = NULL;
+	bzero(m->m_data, tlen);
+
+	switch (id->addr_type) {
+	case 4:
+		ip = mtod(m, struct ip *);
+
+		ip->ip_v = 4;
+		ip->ip_hl = sizeof(struct ip) >> 2;
+		ip->ip_tos = IPTOS_LOWDELAY;
+		ip->ip_len = htons(tlen);
+		ip->ip_id = htons(0);
+		ip->ip_off = htons(0);
+		ip->ip_ttl = V_ip_defttl;
+		ip->ip_p = IPPROTO_SCTP;
+		ip->ip_sum = 0;
+		ip->ip_src.s_addr = htonl(id->dst_ip);
+		ip->ip_dst.s_addr = htonl(id->src_ip);
+
+		sctp = (struct sctphdr *)(ip + 1);
+		break;
+#ifdef INET6
+	case 6:
+		ip6 = mtod(m, struct ip6_hdr *);
+
+		ip6->ip6_vfc = IPV6_VERSION;
+		ip6->ip6_plen = htons(plen);
+		ip6->ip6_nxt = IPPROTO_SCTP;
+		ip6->ip6_hlim = IPV6_DEFHLIM;
+		ip6->ip6_src = id->dst_ip6;
+		ip6->ip6_dst = id->src_ip6;
+
+		sctp = (struct sctphdr *)(ip6 + 1);
+		break;
+#endif
+	}
+
+	sctp->src_port = htons(id->dst_port);
+	sctp->dest_port = htons(id->src_port);
+	sctp->v_tag = htonl(vtag);
+	sctp->checksum = htonl(0);
+
+	chunk = (struct sctp_chunkhdr *)(sctp + 1);
+	chunk->chunk_type = SCTP_ABORT_ASSOCIATION;
+	chunk->chunk_flags = 0;
+	if (reflected != 0) {
+		chunk->chunk_flags |= SCTP_HAD_NO_TCB;
+	}
+	chunk->chunk_length = htons(sizeof(struct sctp_chunkhdr));
+
+	sctp->checksum = sctp_calculate_cksum(m, hlen);
+
+	return (m);
+}
+
+/*
  * Generate a TCP packet, containing either a RST or a keepalive.
  * When flags & TH_RST, we are sending a RST packet, because of a
  * "reset" action matched the packet.
@@ -756,7 +865,71 @@ send_reject6(struct ip_fw_args *args, int code, u_int 
 				    NULL);
 		}
 		FREE_PKT(m);
-	} else if (code != ICMP6_UNREACH_RST) { /* Send an ICMPv6 unreach. */
+	} else if (code == ICMP6_UNREACH_ABORT &&
+	    args->f_id.proto == IPPROTO_SCTP) {
+		struct mbuf *m0;
+		struct sctphdr *sctp;
+		u_int32_t v_tag;
+		int reflected;
+
+		sctp = (struct sctphdr *)((char *)ip6 + hlen);
+		reflected = 1;
+		v_tag = ntohl(sctp->v_tag);
+		/* Investigate the first chunk header if available */
+		if (m->m_len >= hlen + sizeof(struct sctphdr) +
+		    sizeof(struct sctp_chunkhdr)) {
+			struct sctp_chunkhdr *chunk;
+
+			chunk = (struct sctp_chunkhdr *)(sctp + 1);
+			switch (chunk->chunk_type) {
+			case SCTP_INITIATION:
+				/*
+				 * Packets containing an INIT chunk MUST have
+				 * a zero v-tag.
+				 */
+				if (v_tag != 0) {
+					v_tag = 0;
+					break;
+				}
+				/* INIT chunk MUST NOT be bundled */
+				if (m->m_pkthdr.len >
+				    hlen + sizeof(struct sctphdr) +
+				    ntohs(chunk->chunk_length) + 3) {
+					break;
+				}
+				/* Use the initiate tag if available */
+				if ((m->m_len >= hlen + sizeof(struct sctphdr) +
+				    sizeof(struct sctp_chunkhdr) +
+				    offsetof(struct sctp_init, a_rwnd))) {
+					struct sctp_init *init;
+
+					init = (struct sctp_init *)(chunk + 1);
+					v_tag = ntohl(init->initiate_tag);
+					reflected = 0;
+				}
+				break;
+			case SCTP_ABORT_ASSOCIATION:
+				/*
+				 * If the packet contains an ABORT chunk, don't
+				 * reply.
+				 * XXX: We should search through all chunks,
+				 *      but don't do to avoid attacks.
+				 */
+				v_tag = 0;
+				break;
+			}
+		}
+		if (v_tag == 0) {
+			m0 = NULL;
+		} else {
+			m0 = ipfw_send_abort(args->m, &(args->f_id), v_tag,
+			    reflected);
+		}
+		if (m0 != NULL)
+			ip6_output(m0, NULL, NULL, 0, NULL, NULL, NULL);
+		FREE_PKT(m);
+	} else if (code != ICMP6_UNREACH_RST && code != ICMP6_UNREACH_ABORT) {
+		/* Send an ICMPv6 unreach. */
 #if 0
 		/*
 		 * Unlike above, the mbufs need to line up with the ip6 hdr,
@@ -796,9 +969,10 @@ send_reject(struct ip_fw_args *args, int code, int ipl
 	if (args->L3offset)
 		m_adj(m, args->L3offset);
 #endif
-	if (code != ICMP_REJECT_RST) { /* Send an ICMP unreach */
+	if (code != ICMP_REJECT_RST && code != ICMP_REJECT_ABORT) {
+		/* Send an ICMP unreach */
 		icmp_error(args->m, ICMP_UNREACH, code, 0L, 0);
-	} else if (args->f_id.proto == IPPROTO_TCP) {
+	} else if (code == ICMP_REJECT_RST && args->f_id.proto == IPPROTO_TCP) {
 		struct tcphdr *const tcp =
 		    L3HDR(struct tcphdr, mtod(args->m, struct ip *));
 		if ( (tcp->th_flags & TH_RST) == 0) {
@@ -810,6 +984,68 @@ send_reject(struct ip_fw_args *args, int code, int ipl
 				ip_output(m, NULL, NULL, 0, NULL, NULL);
 		}
 		FREE_PKT(args->m);
+	} else if (code == ICMP_REJECT_ABORT &&
+	    args->f_id.proto == IPPROTO_SCTP) {
+		struct mbuf *m;
+		struct sctphdr *sctp;
+		struct sctp_chunkhdr *chunk;
+		struct sctp_init *init;
+		u_int32_t v_tag;
+		int reflected;
+
+		sctp = L3HDR(struct sctphdr, mtod(args->m, struct ip *));
+		reflected = 1;
+		v_tag = ntohl(sctp->v_tag);
+		if (iplen >= (ip->ip_hl << 2) + sizeof(struct sctphdr) +
+		    sizeof(struct sctp_chunkhdr)) {
+			/* Look at the first chunk header if available */
+			chunk = (struct sctp_chunkhdr *)(sctp + 1);
+			switch (chunk->chunk_type) {
+			case SCTP_INITIATION:
+				/*
+				 * Packets containing an INIT chunk MUST have
+				 * a zero v-tag.
+				 */
+				if (v_tag != 0) {
+					v_tag = 0;
+					break;
+				}
+				/* INIT chunk MUST NOT be bundled */
+				if (iplen >
+				    (ip->ip_hl << 2) + sizeof(struct sctphdr) +
+				    ntohs(chunk->chunk_length) + 3) {
+					break;
+				}
+				/* Use the initiate tag if available */
+				if ((iplen >= (ip->ip_hl << 2) +
+				    sizeof(struct sctphdr) +
+				    sizeof(struct sctp_chunkhdr) +
+				    offsetof(struct sctp_init, a_rwnd))) {
+					init = (struct sctp_init *)(chunk + 1);
+					v_tag = ntohl(init->initiate_tag);
+					reflected = 0;
+				}
+				break;
+			case SCTP_ABORT_ASSOCIATION:
+				/*
+				 * If the packet contains an ABORT chunk, don't
+				 * reply.
+				 * XXX: We should search through all chunks,
+				 * but don't do to avoid attacks.
+				 */
+				v_tag = 0;
+				break;
+			}
+		}
+		if (v_tag == 0) {
+			m = NULL;
+		} else {
+			m = ipfw_send_abort(args->m, &(args->f_id), v_tag,
+			    reflected);
+		}
+		if (m != NULL)
+			ip_output(m, NULL, NULL, 0, NULL, NULL);
+		FREE_PKT(args->m);
 	} else
 		FREE_PKT(args->m);
 	args->m = NULL;
@@ -1232,7 +1468,18 @@ do {								\
 				break;
 
 			case IPPROTO_SCTP:
-				PULLUP_TO(hlen, ulp, struct sctphdr);
+				if (pktlen >= hlen + sizeof(struct sctphdr) +
+				    sizeof(struct sctp_chunkhdr) +
+				    offsetof(struct sctp_init, a_rwnd))
+					PULLUP_LEN(hlen, ulp,
+					    sizeof(struct sctphdr) +
+					    sizeof(struct sctp_chunkhdr) +
+					    offsetof(struct sctp_init, a_rwnd));
+				else if (pktlen >= hlen + sizeof(struct sctphdr))
+					PULLUP_LEN(hlen, ulp, pktlen - hlen);
+				else
+					PULLUP_LEN(hlen, ulp,
+					    sizeof(struct sctphdr));
 				src_port = SCTP(ulp)->src_port;
 				dst_port = SCTP(ulp)->dest_port;
 				break;
@@ -1410,7 +1657,18 @@ do {								\
 				break;
 
 			case IPPROTO_SCTP:
-				PULLUP_TO(hlen, ulp, struct sctphdr);
+				if (pktlen >= hlen + sizeof(struct sctphdr) +
+				    sizeof(struct sctp_chunkhdr) +
+				    offsetof(struct sctp_init, a_rwnd))
+					PULLUP_LEN(hlen, ulp,
+					    sizeof(struct sctphdr) +
+					    sizeof(struct sctp_chunkhdr) +
+					    offsetof(struct sctp_init, a_rwnd));
+				else if (pktlen >= hlen + sizeof(struct sctphdr))
+					PULLUP_LEN(hlen, ulp, pktlen - hlen);
+				else
+					PULLUP_LEN(hlen, ulp,
+					    sizeof(struct sctphdr));
 				src_port = SCTP(ulp)->src_port;
 				dst_port = SCTP(ulp)->dest_port;
 				break;

Modified: stable/11/sys/netpfil/ipfw/ip_fw_log.c
==============================================================================
--- stable/11/sys/netpfil/ipfw/ip_fw_log.c	Sat Apr  7 20:23:34 2018	(r332228)
+++ stable/11/sys/netpfil/ipfw/ip_fw_log.c	Sat Apr  7 20:27:11 2018	(r332229)
@@ -165,6 +165,8 @@ ipfw_log(struct ip_fw_chain *chain, struct ip_fw *f, u
 		case O_REJECT:
 			if (cmd->arg1==ICMP_REJECT_RST)
 				action = "Reset";
+			else if (cmd->arg1==ICMP_REJECT_ABORT)
+				action = "Abort";
 			else if (cmd->arg1==ICMP_UNREACH_HOST)
 				action = "Reject";
 			else
@@ -175,6 +177,8 @@ ipfw_log(struct ip_fw_chain *chain, struct ip_fw *f, u
 		case O_UNREACH6:
 			if (cmd->arg1==ICMP6_UNREACH_RST)
 				action = "Reset";
+			else if (cmd->arg1==ICMP6_UNREACH_ABORT)
+				action = "Abort";
 			else
 				snprintf(SNPARGS(action2, 0), "Unreach %d",
 					cmd->arg1);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201804072027.w37KRCNN088242>