Page MenuHomeFreeBSD

D13239.diff
No OneTemporary

D13239.diff

Index: head/sbin/ipfw/ipfw.8
===================================================================
--- head/sbin/ipfw/ipfw.8
+++ head/sbin/ipfw/ipfw.8
@@ -1,7 +1,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd October 2, 2017
+.Dd November 26, 2017
.Dt IPFW 8
.Os
.Sh NAME
@@ -1173,6 +1173,14 @@
.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
Index: head/sbin/ipfw/ipfw2.h
===================================================================
--- head/sbin/ipfw/ipfw2.h
+++ head/sbin/ipfw/ipfw2.h
@@ -81,6 +81,8 @@
TOK_STARTBRACE,
TOK_ENDBRACE,
+ TOK_ABORT6,
+ TOK_ABORT,
TOK_ACCEPT,
TOK_COUNT,
TOK_EACTION,
Index: head/sbin/ipfw/ipfw2.c
===================================================================
--- head/sbin/ipfw/ipfw2.c
+++ head/sbin/ipfw/ipfw2.c
@@ -244,6 +244,8 @@
};
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 @@
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 @@
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;
@@ -3752,6 +3758,16 @@
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:
Index: head/sys/netinet/ip_fw.h
===================================================================
--- head/sys/netinet/ip_fw.h
+++ head/sys/netinet/ip_fw.h
@@ -728,6 +728,8 @@
#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.
Index: head/sys/netpfil/ipfw/ip_fw2.c
===================================================================
--- head/sys/netpfil/ipfw/ip_fw2.c
+++ head/sys/netpfil/ipfw/ip_fw2.c
@@ -80,6 +80,8 @@
#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 @@
}
/*
+ * 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.
@@ -730,7 +839,71 @@
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,
@@ -770,9 +943,10 @@
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) {
@@ -784,6 +958,68 @@
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;
@@ -1203,7 +1439,18 @@
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;
@@ -1380,7 +1627,18 @@
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;
Index: head/sys/netpfil/ipfw/ip_fw_log.c
===================================================================
--- head/sys/netpfil/ipfw/ip_fw_log.c
+++ head/sys/netpfil/ipfw/ip_fw_log.c
@@ -165,6 +165,8 @@
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 @@
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);

File Metadata

Mime Type
text/plain
Expires
Mon, Mar 3, 9:26 PM (6 h, 47 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16953840
Default Alt Text
D13239.diff (11 KB)

Event Timeline