Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F111364420
D13239.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
11 KB
Referenced Files
None
Subscribers
None
D13239.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D13239: Add support for ABORT action in ipfw
Attached
Detach File
Event Timeline
Log In to Comment