[Snort-devel] Snort whit SS7/Sigtran

Joshua Kinard kumba at gentoo.org
Wed Feb 14 04:10:14 EST 2018


On 2/13/2018 1:49 PM, Joel Esler (jesler) via Snort-devel wrote:
> Adding the snort-team on this one.
> 
> *--*
> *Joel Esler *| *Talos:* Manager | jesler at cisco.com <mailto:jesler at cisco.com>
> 
> 
>> On Feb 13, 2018, at 8:27 AM, ALEJANDRO CORLETTI ESTRADA
>> <alejandro.corlettiestrada.ext at telefonica.com
>> <mailto:alejandro.corlettiestrada.ext at telefonica.com>> wrote:
>>
>> Hello,
>>  
>> My name is Alejandro Corletti Estrada, I am a  Engineering Doctor, I have
>> been working in networks and security for many years (I have written 3 books
>> in Spanish:  "Seguridad por Niveles",  "Seguridad en Redes" y
>> "Ciberseguridad, una Estrategia Informático/Militar  - "Security by Levels",
>> "Security in Networks" and "Cybersecurity, a Computer / Military
>> Strategy").    With Snort I have been working for almost 20 years, in 2001 I
>> published an article called "Nessus Methodology - Snort" and another one "
>> Level of immaturity of the NIDS "(both in Spanish), were very successful in
>> the Hispanic world.
>>  
>> Currently I am in the "Corporate Audit of Security in Networks and Systems"
>> of the Telefónica Group.
>>  
>> I am writing to you, because there is a critical safety problem with the
>> Signaling System 7 (SS7), more particularly in the "Sigtran" stack, which is
>> responsible for transporting SS7 over IP. I have been studying the subject
>> for months and now (with my team) we are analyzing traffic patterns of this
>> family of protocols and looking for anomalous traffic in them. We are using,
>> as we did before, Wireshark in combination with Snort.
>>  
>> Within the known vulnerabilities (which are several), to work with Snort
>> there are two fundamental problems:
>>  
>> 1. The *SCTP* protocol (Stream Control Transmission Protocol) that replaces
>> TCP in Sigtran.
>> 2. _Absence of rules for SS7_ (this includes several protocols: MAP, TCAP,
>> ISUP, CAMEL, etc ...)
>>  
>> I would like to open with you a new "work team" or committee that is
>> responsible for this issue, because I believe that Snort could be the
>> solution to this International problem. Currently, all the telephone
>> operators in the world are suffering, and we at this moment (with my team)
>> can do much about it, if we count on your support, because we have access to
>> all the nodes and network elements that operate ss7 / Sigtran and access to
>> this traffic.
>>  
>> Within this line of action there would be two aspects on which I would like
>> to join efforts with the entire Snort community:
>>  
>> 1. Create the necessary library to be able to call rules with "SCTP" protocol
>> (instead of ip, tcp, icmp .... etc) this would be very important.
>> 2. Begin to SS7 develop rules for detecting anomalous traffic patterns.
>>  
>> Please tell me what steps or actions I should take to move forward on this topic.
>>  
>> With all sincerity, I can assure you that I am one of the few people who know
>> this topic in depth (for in the 90's I was teaching the SS7 courses, before
>> its integration with Sigtran), I have studied it in detail and I have access
>> to all the traffic that is needed.
>>  
>> This project can be an international initiative to which undoubtedly, as it
>> becomes public, all the telephone operators in the world will be added.
>>  
>> Regards and I am waiting for your response
>> Alejandro Corletti Estrada.
>>  
>> PS: my English level is not very good (sorry)

[snip]

I got bored a couple of years ago and hacked together a basic SCTP support for
Snort-2.9.5.x to 2.9.6.x.  It has enough logic to parse "alert sctp ..." rule
formats, including using ports and IPv4/IPv6 addresses; should handle chopping
up SCTP packets and placing the Snort offset pointers at the right spots; has a
few protocol decoder rules; and includes a couple of new rule options to
constrain a rule to only look at specific SCTP chunk types and their associated
fields.  Can use stock Snort rule options alongside the new ones.

I never took it beyond that point, so it doesn't support any stream reassembly
beyond what Snort's existing UDP reassembler does (because I duped the code and
search/replaced "udp" with "sctp" as appropriate).  Snort's internals for
stream reassembly are terribly documented in 2.9.x.  I think this is one of the
end-goals of Snort++, which has *much* cleaner code (but I am not a fan of the
all-Lua approach).

It doesn't even try to handle SCTP's multi-homing capability, since additional
association pairs are set up in the middle of the handshake using optional
parameters to the INIT chunk type.  Snort's only able to work out an address
pairing by what's available in the OSI layer 3 header.  Pretty much everything
else, if it's in the Layer 4 payload, is game for the fast-pattern matcher.
Anything more SCTP-specific is going to require lots of additional code, such
as new static or dynamic preproc modules, probably a few more rule keywords,
etc.  Things I have no free time for.

SCTP is the superior transport protocol in my opinion.  Multi-homing,
multi-streaming, message-level framing, sensible extensibility via new chunk
types, built-in DDoS protection, etc.  It's the only one of IANA's four general
purpose transport protocols that is both artistic and functional at the same
time.  If only we could get rid of TCP...

I've attached the last patch I cut from a private git repo about two or three
years ago, in case anyone wants to use it and make magic happen.  I think it'll
apply against vanilla Snort-2.9.6, but try 2.9.5 or 2.9.7-alpha if not and then
work up from there.

Patch also includes the gcc compiler branch-optimizations "likely()" and
"unlikely()", from the Linux kernel's "compiler.h" header, I think from the 3.x
branch.  The function to deal with SCTP's padding alignment requirement is from
Wireshark, along with the checksumming code for CRC32 and Adler32.

I did not get around to drafting TeX syntax up in the manual for the new rule
options, so one will need to read the source code to work out how I designed
those to work.  The top of each *.c file for the new options includes a quick
documentation comment.

Can't really contribute anything beyond this right now.  But would be
interested to know if this has any practical use to anyone.

--------

Basic configuration of "stream_sctp":

preprocessor stream5_global:			\
	track_tcp yes,				\
	track_udp yes,				\
	track_sctp yes,				\
	track_icmp yes,				\
	max_tcp 1048576,			\
	max_udp 1048576,			\
	max_sctp 1048576,			\
	max_icmp 1048576

preprocessor stream5_sctp:			\
	timeout 600

--------

Here's a few sample rules that detect traffic in some test PCAPs I dredged up
for testing.  Should work against any basic SCTP traffic, though:

config classification: sctp,SCTP Packet,1

alert sctp any any -> any any (msg:"SCTP DECODE TEST 1"; sctp_chunk_type:DATA;
sid:17000132; rev:1; classtype:sctp;)

alert sctp any any -> any any (msg:"SCTP DECODE TEST 2"; flow:established;
sctp_chunk_field:SACK,cuml_tsn_ack,>3000; sid:17000133; rev:1; classtype:sctp;)

alert sctp any any -> any any (msg:"SCTP DECODE TEST 3"; flow:established;
sctp_chunk_type:ABORT,ERROR; sctp_cause_code:12; sid:17000134; rev:1;
classtype:sctp;)

--------

 doc/README.UNSOCK                                         |   12
 doc/README.csv                                            |    5
 preproc_rules/decoder.rules                               |   60
 preproc_rules/preprocessor.rules                          |    8
 src/active.c                                              |    7
 src/compiler.h                                            |   43
 src/decode.c                                              |  804 +++++++++
 src/decode.h                                              |  472 +++++
 src/detection-plugins/Makefile.am                         |    7
 src/detection-plugins/Makefile.in                         |   19
 src/detection-plugins/detection_options.c                 |  169 +
 src/detection-plugins/sp_clientserver.c                   |    9
 src/detection-plugins/sp_sctp_cause_code.c                |  340 ++++
 src/detection-plugins/sp_sctp_cause_code.h                |   39
 src/detection-plugins/sp_sctp_chunk_field.c               |  681 ++++++++
 src/detection-plugins/sp_sctp_chunk_field.h               |  129 +
 src/detection-plugins/sp_sctp_chunk_flags.c               |  481 +++++
 src/detection-plugins/sp_sctp_chunk_flags.h               |   81
 src/detection-plugins/sp_sctp_chunk_type.c                |  500 +++++
 src/detection-plugins/sp_sctp_chunk_type.h                |   66
 src/detection-plugins/sp_sctp_num_chunks.c                |  296 +++
 src/detection-plugins/sp_sctp_num_chunks.h                |   47
 src/dynamic-output/plugins/output_lib.h                   |    1
 src/dynamic-output/plugins/output_plugin.c                |    1
 src/dynamic-plugins/sf_engine/sf_snort_detection_engine.c |    3
 src/dynamic-plugins/sf_engine/sf_snort_packet.h           |  219 ++
 src/fpcreate.c                                            |  116 +
 src/fpcreate.h                                            |    9
 src/fpdetect.c                                            |  102 +
 src/output-plugins/spo_alert_sf_socket.c                  |    7
 src/output-plugins/spo_alert_syslog.c                     |    3
 src/output-plugins/spo_alert_unixsock.c                   |    7
 src/output-plugins/spo_csv.c                              |   21
 src/output-plugins/spo_database.c                         |   35
 src/output-plugins/spo_log_ascii.c                        |    5
 src/parser.c                                              |  167 +
 src/plugbase.c                                            |   16
 src/plugin_enum.h                                         |    4
 src/preprocessors/Stream5/Makefile.am                     |    3
 src/preprocessors/Stream5/Makefile.in                     |   29
 src/preprocessors/Stream5/snort_stream5_icmp.c            |    9
 src/preprocessors/Stream5/snort_stream5_sctp.c            |  847 ++++++++++
 src/preprocessors/Stream5/snort_stream5_sctp.h            |  287 +++
 src/preprocessors/Stream5/snort_stream5_session.c         |   66
 src/preprocessors/Stream5/snort_stream5_session.h         |    2
 src/preprocessors/Stream5/stream5_common.c                |   63
 src/preprocessors/Stream5/stream5_common.h                |   58
 src/preprocessors/Stream5/stream5_ha.c                    |   10
 src/preprocessors/perf-base.c                             |  191 +-
 src/preprocessors/perf-base.h                             |   28
 src/preprocessors/perf-flow.c                             |  178 +-
 src/preprocessors/perf-flow.h                             |   20
 src/preprocessors/perf.c                                  |    2
 src/preprocessors/portscan.c                              |  186 ++
 src/preprocessors/portscan.h                              |    7
 src/preprocessors/spp_frag3.c                             |    9
 src/preprocessors/spp_sfportscan.c                        |   76
 src/preprocessors/spp_stream5.c                           |  610 ++++++-
 src/preprocessors/stream_api.h                            |   19
 src/preprocessors/stream_expect.c                         |    2
 src/profiler.c                                            |    3
 src/rule_option_types.h                                   |    5
 src/sf_protocols.h                                        |    2
 src/sfutil/Makefile.am                                    |    1
 src/sfutil/Makefile.in                                    |    6
 src/sfutil/adler32.c                                      |   59
 src/sfutil/adler32.h                                      |   41
 src/sfutil/crc32.h                                        |  130 +
 src/sfutil/sfportobject.c                                 |    4
 src/sfutil/sfportobject.h                                 |    3
 src/snort.c                                               |    2
 src/snort.h                                               |   27
 src/target-based/sftarget_protocol_reference.c            |    6
 src/target-based/sftarget_protocol_reference.h            |    1
 src/util.c                                                |  117 +
 src/util.h                                                |   20
 82 files changed, 8019 insertions(+), 349 deletions(-)

-- 
Joshua Kinard
Gentoo/MIPS
kumba at gentoo.org
6144R/F5C6C943 2015-04-27
177C 1972 1FB8 F254 BAD0 3E72 5C63 F4E3 F5C6 C943

"The past tempts us, the present confuses us, the future frightens us.  And our
lives slip away, moment by moment, lost in that vast, terrible in-between."

--Emperor Turhan, Centauri Republic
-------------- next part --------------
diff --git a/doc/README.UNSOCK b/doc/README.UNSOCK
index 4bcb48d..9960012 100644
--- a/doc/README.UNSOCK
+++ b/doc/README.UNSOCK
@@ -99,6 +99,9 @@ main (void)
                     case IPPROTO_UDP:
                       p->udph = (UDPHdr *) (alert.pkt + alert.transhdr);
                       break;
+                    case IPPROTO_SCTP:
+                      p->sctph = (SCTPHdr *) (alert.pkt + alert.transhdr);
+                      break;
                     case IPPROTO_ICMP:
                       p->icmph = (ICMPHdr *) (alert.pkt + alert.transhdr);
                       break;
@@ -115,7 +118,7 @@ main (void)
 
       printf ("%s [%d]\n", alert.alertmsg, alert.event.event_id);
       if (!(alert.val & NOPACKET_STRUCT))
-        if (p->iph && (p->tcph || p->udph || p->icmph))
+        if (p->iph && (p->tcph || p->udph || p->sctph || p->icmph))
           {
             switch (p->iph->ip_proto)
               {
@@ -133,6 +136,13 @@ main (void)
                 printf ("to: %s:%d\n", inet_ntoa (p->iph->ip_dst),
                         ntohs (p->udph->uh_dport));
                 break;
+              case IPPROTO_SCTP:
+                printf ("SCTP from: %s:%d ",
+                        inet_ntoa (p->iph->ip_src),
+                        ntohs (p->sctph->sh_sport));
+                printf ("to: %s:%d\n", inet_ntoa (p->iph->ip_dst),
+                        ntohs (p->sctph->sh_dport));
+                break;
               case IPPROTO_ICMP:
                 printf ("ICMP type: %d code: %d from: %s ",
                         p->icmph->type,
diff --git a/doc/README.csv b/doc/README.csv
index a6cb3cc..614d49d 100644
--- a/doc/README.csv
+++ b/doc/README.csv
@@ -36,11 +36,12 @@ WEB-MISC phf access,TCP,02/23-11:06:59.600820 ,192.168.0.1,1021,192.168.0.2,80
 Possible Field Names:
 ----
 
-The following field names are available (As of 01/13/2004)
+The following field names are available (As of 01/23/2014)
 
 timestamp, sig_generator, sig_id, sig_rev, msg, proto, src, srcport, dst,
 dstport, ethsrc, ethdst, ethlen, tcpflags, tcpseq, tcpack, tcpln, tcpwindow, ttl,
-tos, id, dgmlen, iplen, icmptype, icmpcode, icmpid, icmpseq, and default
+tos, id, dgmlen, iplen, icmptype, icmpcode, icmpid, icmpseq, udplength, sctplen,
+sctpvtag, sctpcsum, and default
 
 By specifying "default" as a field name, a default set of field names
 is used.  `grep DEFAULT_CSV spo_csv.h` for the default set of fields
diff --git a/preproc_rules/decoder.rules b/preproc_rules/decoder.rules
index 78fa711..c6c14b7 100644
--- a/preproc_rules/decoder.rules
+++ b/preproc_rules/decoder.rules
@@ -150,3 +150,63 @@ alert ( msg:"DECODE_ERSPAN2_DGRAM_LT_HDR_STR"; sid:463; gid:116; rev:1; metadata
 alert ( msg:"DECODE_ERSPAN3_DGRAM_LT_HDR_STR"; sid:464; gid:116; rev:1; metadata:rule-type decode; classtype:protocol-command-decode; )
 alert ( msg:"DECODE_AUTH_HDR_TRUNC"; sid:465; gid:116; rev:1; metadata:rule-type decode; classtype:misc-activity; )
 alert ( msg:"DECODE_AUTH_HDR_BAD_LEN"; sid:466; gid:116; rev:1; metadata:rule-type decode; classtype:misc-activity; )
+alert ( msg:"DECODE_SCTP_DGRAM_LT_SCTPHDR"; sid:320; gid:116; rev:1; metadata:rule-type decode; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_CSUM_IS_ADLER32"; sid:321; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_CHUNK_ZERO_LENGTH"; sid:322; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.2; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_CHUNK_LEN_GT_DGRAM"; sid:323; gid:116; rev:1; metadata:rule-type decode; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_DATA_C_BAD_LEN"; sid:324; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_DATA_C_RSVD_NZERO"; sid:325; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_INIT_C_BAD_LEN"; sid:326; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.2; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_INIT_C_FLAGS_NZERO"; sid:327; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.2; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_INIT_C_ITAG_ZERO"; sid:328; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.2; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_INIT_C_VTAG_NZERO"; sid:329; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_INIT_C_NOUTSTR_ZERO"; sid:330; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.2; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_INIT_C_NINSTR_ZERO"; sid:331; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.2; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_INIT_ACK_C_BAD_LEN"; sid:332; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.3; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_INIT_ACK_C_FLAGS_NZERO"; sid:333; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.3; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_INIT_ACK_C_ITAG_ZERO"; sid:334; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.3; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_INIT_ACK_C_NOUTSTR_ZERO"; sid:335; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.3; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_INIT_ACK_C_NINSTR_ZERO"; sid:336; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.3; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_SACK_C_BAD_LEN"; sid:337; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.4; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_SACK_C_FLAGS_NZERO"; sid:338; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.4; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_HEARTBEAT_C_BAD_LEN"; sid:339; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.5; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_HEARTBEAT_C_FLAGS_NZERO"; sid:340; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.5; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_HEARTBEAT_ACK_C_BAD_LEN"; sid:341; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.6; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_HEARTBEAT_ACK_C_FLAGS_NZERO"; sid:342; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.6; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_ABORT_C_BAD_LEN"; sid:343; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.7; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_ABORT_C_RSVD_NZERO"; sid:344; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.7; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_SHUTDOWN_C_BAD_LEN"; sid:345; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.8; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_SHUTDOWN_C_FLAGS_NZERO"; sid:346; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.8; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_SHUTDOWN_ACK_C_BAD_LEN"; sid:347; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.9; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_SHUTDOWN_ACK_C_FLAGS_NZERO"; sid:348; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.9; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_ERROR_C_BAD_LEN"; sid:349; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.10; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_ERROR_C_FLAGS_NZERO"; sid:350; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.10; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_COOKIE_ECHO_C_BAD_LEN"; sid:351; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.11; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_COOKIE_ECHO_C_FLAGS_NZERO"; sid:352; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.11; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_COOKIE_ACK_C_BAD_LEN"; sid:353; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.12; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_COOKIE_ACK_C_FLAGS_NZERO"; sid:354; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.12; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_ECNE_C_BAD_LEN"; sid:355; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#appendix-A; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_ECNE_C_FLAGS_NZERO"; sid:356; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#appendix-A; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_CWR_C_BAD_LEN"; sid:357; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#appendix-A; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_CWR_C_FLAGS_NZERO"; sid:358; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#appendix-A; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_SHUTDOWN_COMPLETE_C_BAD_LEN"; sid:359; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.13; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_SHUTDOWN_COMPLETE_C_RSVD_NZERO"; sid:360; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.13; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_AUTH_C_BAD_LEN"; sid:361; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4895#section-5.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_AUTH_C_FLAGS_NZERO"; sid:362; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4895#section-5.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_NR_SACK_C_BAD_LEN"; sid:363; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/draft-tuexen-tsvwg-sctp-multipath-07#section-4.2; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_NR_SACK_C_FLAGS_NZERO"; sid:364; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/draft-tuexen-tsvwg-sctp-multipath-07#section-4.2; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_ASCONF_ACK_C_BAD_LEN"; sid:365; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc5061#section-4.1.2; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_ASCONF_ACK_C_FLAGS_NZERO"; sid:366; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc5061#section-4.1.2; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_PKTDROP_C_BAD_LEN"; sid:367; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/draft-stewart-sctp-pktdrprep-16#section-4.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_PKTDROP_C_FLAGS_NZERO"; sid:368; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/draft-stewart-sctp-pktdrprep-16#section-4.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_PKTDROP_C_RSVD_NZERO"; sid:369; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/draft-stewart-sctp-pktdrprep-16#section-4.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_RECONFIG_C_BAD_LEN"; sid:370; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc6525#section-3.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_RECONFIG_C_FLAGS_NZERO"; sid:371; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc6525#section-3.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_RECONFIG_C_GT_2_PARAMS"; sid:372; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc6525#section-3.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_RECONFIG_C_INVALID_PARAMS"; sid:373; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc6525#section-3.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_PAD_C_BAD_LEN"; sid:374; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4820#section-3; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_PAD_C_FLAGS_NZERO"; sid:375; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4820#section-3; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_FORWARD_TSN_C_BAD_LEN"; sid:376; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc3758#section-3.2; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_FORWARD_TSN_C_FLAGS_NZERO"; sid:377; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc3758#section-3.2; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_ASCONF_C_BAD_LEN"; sid:378; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc5061#section-4.1.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_ASCONF_C_FLAGS_NZERO"; sid:379; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc5061#section-4.1.1; classtype:protocol-command-decode; )
diff --git a/preproc_rules/preprocessor.rules b/preproc_rules/preprocessor.rules
index 4663f40..a0ad89b 100644
--- a/preproc_rules/preprocessor.rules
+++ b/preproc_rules/preprocessor.rules
@@ -83,6 +83,14 @@ alert ( msg: "PSNG_UDP_FILTERED_DISTRIBUTED_PORTSCAN"; sid: 24; gid: 122; rev: 1
 alert ( msg: "PSNG_ICMP_PORTSWEEP"; sid: 25; gid: 122; rev: 1; metadata: rule-type preproc ; classtype:attempted-recon; )
 alert ( msg: "PSNG_ICMP_PORTSWEEP_FILTERED"; sid: 26; gid: 122; rev: 1; metadata: rule-type preproc ; classtype:attempted-recon; )
 alert ( msg: "PSNG_OPEN_PORT"; sid: 27; gid: 122; rev: 1; metadata: rule-type preproc ; classtype:attempted-recon; )
+alert ( msg: "PSNG_SCTP_PORTSCAN"; sid: 28; gid: 122; rev: 1; metadata: rule-type preproc ; classtype:attempted-recon; )
+alert ( msg: "PSNG_SCTP_DECOY_PORTSCAN"; sid: 29; gid: 122; rev: 1; metadata: rule-type preproc ; classtype:attempted-recon; )
+alert ( msg: "PSNG_SCTP_PORTSWEEP"; sid: 30; gid: 122; rev: 1; metadata: rule-type preproc ; classtype:attempted-recon; )
+alert ( msg: "PSNG_SCTP_DISTRIBUTED_PORTSCAN"; sid: 31; gid: 122; rev: 1; metadata: rule-type preproc ; classtype:attempted-recon; )
+alert ( msg: "PSNG_SCTP_FILTERED_PORTSCAN"; sid: 32; gid: 122; rev: 1; metadata: rule-type preproc ; classtype:attempted-recon; )
+alert ( msg: "PSNG_SCTP_FILTERED_DECOY_PORTSCAN"; sid: 33; gid: 122; rev: 1; metadata: rule-type preproc ; classtype:attempted-recon; )
+alert ( msg: "PSNG_SCTP_PORTSWEEP_FILTERED"; sid: 34; gid: 122; rev: 1; metadata: rule-type preproc ; classtype:attempted-recon; )
+alert ( msg: "PSNG_SCTP_FILTERED_DISTRIBUTED_PORTSCAN"; sid: 35; gid: 122; rev: 1; metadata: rule-type preproc ; classtype:attempted-recon; )
 alert ( msg: "FRAG3_IPOPTIONS"; sid: 1; gid: 123; rev: 1; metadata: rule-type preproc ; classtype:protocol-command-decode; )
 alert ( msg: "FRAG3_TEARDROP"; sid: 2; gid: 123; rev: 1; metadata: rule-type preproc ; reference:cve,1999-0015; reference:bugtraq,124; classtype:attempted-dos; )
 alert ( msg: "FRAG3_SHORT_FRAG"; sid: 3; gid: 123; rev: 1; metadata: rule-type preproc ; classtype:protocol-command-decode; )
diff --git a/src/active.c b/src/active.c
index a625dbb..c89ccc8 100644
--- a/src/active.c
+++ b/src/active.c
@@ -360,9 +360,10 @@ int Active_IsRSTCandidate(const Packet* p)
 
 int Active_IsUNRCandidate(const Packet* p)
 {
-    // FIXTHIS allow unr to tcp/udp/icmp4/icmp6 only or for all
+    // FIXTHIS allow unr to tcp/udp/sctp/icmp4/icmp6 only or for all
     switch ( GetInnerProto(p) ) {
     case PROTO_UDP:
+    case PROTO_SCTP:
     case PROTO_TCP:
     case PROTO_ICMP4:
     case PROTO_ICMP6:
@@ -459,6 +460,7 @@ int Active_ForceDropAction(Packet *p)
     {
         case IPPROTO_TCP:
         case IPPROTO_UDP:
+        case IPPROTO_SCTP:
             Active_DropSession(p);
             _Active_ForceIgnoreSession(p);
     }
@@ -481,8 +483,9 @@ static inline int _Active_DoReset(Packet *p)
                 Active_QueueReject();
             break;
 
-        // FIXTHIS send unr to udp/icmp4/icmp6 only or for all non-tcp?
+        // FIXTHIS send unr to udp/sctp/icmp4/icmp6 only or for all non-tcp?
         case IPPROTO_UDP:
+        case IPPROTO_SCTP:
         case IPPROTO_ICMP:
         case IPPROTO_ICMPV6:
             if ( Active_IsUNRCandidate(p) )
diff --git a/src/compiler.h b/src/compiler.h
new file mode 100644
index 0000000..3ae50a0
--- /dev/null
+++ b/src/compiler.h
@@ -0,0 +1,43 @@
+/*
+** Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+** Borrowed from the Linux Kernel (include/linux/compiler.h)
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License Version 2 as
+** published by the Free Software Foundation.  You may not use, modify or
+** distribute this program under any other version of the GNU General
+** Public License.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/* $Id$ */
+#ifndef __COMPILER_H__
+#define __COMPILER_H__
+
+/*
+ * If using gcc, these macros help with branch prediction by allowing one
+ * to specify the likely branch that a conditional will take.  It's
+ * expensive if the branch is wrong, however, but can allow a coder with
+ * better knowledge than the compiler to optimize the code paths.  Use
+ * very sparingly, and only when you know with a reasonable certainty
+ * that you can outsmart gcc.
+ *
+ * If non-GNU, these macros do nothing.
+ */
+#ifdef __GNUC__
+  #define likely(x)      __builtin_expect(!!(x), 1)
+  #define unlikely(x)    __builtin_expect(!!(x), 0)
+#else
+  #define likely(x)      x
+  #define unlikely(x)    x
+ #endif
+
+#endif /* __COMPILER_H__ */
diff --git a/src/decode.c b/src/decode.c
index 38fe267..516d99a 100644
--- a/src/decode.c
+++ b/src/decode.c
@@ -43,6 +43,7 @@
 #include "snort_debug.h"
 #include "util.h"
 #include "detect.h"
+#include "compiler.h"
 #include "checksum.h"
 #include "log.h"
 #include "generators.h"
@@ -58,6 +59,8 @@
 #include "mempool.h"
 #include "spp_normalize.h"
 #include "sfdaq.h"
+#include "sfutil/adler32.h"
+#include "sfutil/crc32.h"
 
 extern tSfActionQueueId decoderActionQ;
 extern MemPool decoderAlertMemPool;
@@ -221,6 +224,16 @@ static inline void execIcmpChksmDrop (void *data)
     }
 }
 
+static inline void execSctpChksmDrop (void *data)
+{
+    if( ScInlineMode() && ScSctpChecksumDrops() )
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_DECODE,
+            "Dropping bad packet (SCTP checksum)\n"););
+        Active_DropPacket((Packet*)data);
+    }
+}
+
 void execDecoderEvent(void *data)
 {
     MemBucket *alertBucket = (MemBucket *)data;
@@ -2199,6 +2212,11 @@ static inline void DecodeIPv4Proto(const uint8_t proto,
             DecodeUDP(pkt, len, p);
             return;
 
+        case IPPROTO_SCTP:
+            pc.sctp++;
+            DecodeSCTP(pkt, len, p);
+            return;
+
         case IPPROTO_ICMP:
             pc.icmp++;
             DecodeICMP(pkt, len, p);
@@ -2773,7 +2791,7 @@ void DecodeICMP(const uint8_t * pkt, const uint32_t len, Packet * p)
     ICMP4MiscTests(p);
 
     p->proto_bits |= PROTO_BIT__ICMP;
-    p->proto_bits &= ~(PROTO_BIT__UDP | PROTO_BIT__TCP);
+    p->proto_bits &= ~(PROTO_BIT__UDP | PROTO_BIT__TCP | PROTO_BIT__SCTP);
 }
 
 /*
@@ -2909,6 +2927,15 @@ void DecodeICMPEmbeddedIP(const uint8_t *pkt, const uint32_t len, Packet *p)
 
             break;
 
+        case IPPROTO_SCTP:
+            p->orig_sctph = (SCTPHdr *)(pkt + hlen);
+
+            /* stuff more data into the printout data struct */
+            p->orig_sp = ntohs(p->orig_sctph->sh_sport);
+            p->orig_dp = ntohs(p->orig_sctph->sh_dport);
+
+            break;
+
         case IPPROTO_ICMP:
             p->orig_icmph = (ICMPHdr *)(pkt + hlen);
             break;
@@ -2917,6 +2944,756 @@ void DecodeICMPEmbeddedIP(const uint8_t *pkt, const uint32_t len, Packet *p)
     return;
 }
 
+/**
+ * SctpCrc32Chksum - Generate a crc32 checksum of an SCTP packet.
+ * @buf: Pointer to the current SCTP packet.
+ * @len: length from here to the end of the packet.
+ *
+ * Returns a uint32_t value of a crc32 checksum of an SCTP packet.
+ */
+uint32_t SctpCrc32Chksum(const unsigned char* buf, unsigned int len)
+{
+  uint16_t i;
+  uint32_t crc32 = CRC32C_PRELOAD, result;
+
+  /* Checksum the common header EXCEPT the checksum field */
+  for (i = 0; i < (SCTP_COMMON_HEADER_LEN - 4); i++)
+      CRC32C(crc32, buf[i]);
+
+  /* Checksum four '0' bytes as the checksum value */
+  CRC32C(crc32, 0);
+  CRC32C(crc32, 0);
+  CRC32C(crc32, 0);
+  CRC32C(crc32, 0);
+
+  /* Checksum the remainder of the packet */
+  for (i = SCTP_COMMON_HEADER_LEN; i < len; i++)
+      CRC32C(crc32, buf[i]);
+
+  result = CRC32C_SWAP(crc32);
+
+  return (~result);
+}
+
+/**
+ * SctpAdler32Chksum - Generate an adler32 checksum of an SCTP packet.
+ * @buf: Pointer to the current SCTP packet.
+ * @len: length from here to the end of the packet.
+ *
+ * Returns a uint32_t value of an adler32 checksum of an SCTP packet.
+ */
+static unsigned int
+SctpAdler32Chksum(const unsigned char* buf, unsigned int len)
+{
+  uint32_t result = 1L;
+
+  /* Checksum four '0' bytes as the checksum value */
+  result = update_adler32(result, buf, (SCTP_COMMON_HEADER_LEN - 4));
+
+  /* handle four 0 bytes as checksum */
+  result = update_adler32(result, (unsigned char *)"\0\0\0\0", 4);
+
+  /* Checksum the remainder of the packet */
+  result = update_adler32(result, (buf + SCTP_COMMON_HEADER_LEN),
+                          (len - SCTP_COMMON_HEADER_LEN));
+
+  return result;
+}
+
+/**
+ * SctpChunkAnomaly - Generate a decoder event on an SCTP protocol anomaly.
+ * @p: Pointer to the current Packet structure containing packet data.
+ * @debug_msg: string to pass to DebugMessage().
+ * @de_code: uint16_t value of the decoder event to pass to DecoderEvent().
+ * @de_str: string of the decoder event to pass to DecoderEvent().
+ *
+ * Will flag a DecoderEvent of the supplied type and exit the DecodeSCTP
+ * function.
+ */
+static inline void SctpChunkAnomaly(Packet *p, const char *debug_msg,
+    const uint16_t de_code, char *de_str, const bool disc)
+{
+    DEBUG_WRAP(DebugMessage(DEBUG_DECODE, "%s!\n", debug_msg));
+    DecoderEvent(p, de_code, de_str, 1, 1);
+    if (disc)
+    {
+        p->sctph = NULL;
+        pc.discards++;
+        pc.sctpdisc++;
+    }
+
+    return;
+}
+
+/**
+ * SctpGetNextChunk - Gets the next chunk from the packet.
+ * @c: Pointer to the current SctpChunk structure containing chunk data.
+ * @rlen: uint32_t value of the remaining payload length.
+ * @clen: uint16_t value of the current chunk's length.
+ *
+ * Returns the new remaining length in the packet and points c to the
+ * next chunk in the packet.  If there are no more chunks left, it will
+ * return 0 and c will be set to NULL.
+ */
+inline uint32_t SctpGetNextChunk(SctpChunk *chunk, uint32_t rlen, uint16_t clen)
+{
+    if (rlen > 0)
+    {
+        /*
+         * If remaining_len is > 0, see if it is > SCTP_MAX_PADDING_LEN.  If
+         * so, we probably have another chunk to process.  Else, force
+         * remaining_len to 0, because it is most likely padding bytes.  Per
+         * RFC4960, all SCTP chunks MUST be padded to a 4-byte boundary, but
+         * this padding value MUST NOT be included in the chunk length field
+         * AND this padding MUST NOT exceed 3 bytes.
+         */
+        if (rlen > SCTP_MAX_PADDING_LEN)
+            chunk = (SctpChunk *)((unsigned char *)chunk + clen);
+        else
+        {
+            rlen = 0;
+            chunk = NULL;
+        }
+    }
+
+    return rlen;
+}
+
+/**
+ * SctpGetNextCause - Gets the next cause from the chunk.
+ * @c: Pointer to the current SctpCause structure containing cause data.
+ * @rclen: uint16_t value of the remaining chunk length.
+ * @clen: uint16_t value of the current cause's length.
+ *
+ * Returns the new remaining length in the chunk and points c to the
+ * next cause in the chunk.  If there are no more causes left, it will
+ * return 0 and c will be set to NULL.
+ */
+inline uint16_t SctpGetNextCause(SctpCause *cause, uint16_t rclen, uint16_t clen)
+{
+    if (rclen > 0)
+    {
+        /* Same as in SctpGetNextChunk(). */
+        if (rclen > SCTP_MAX_PADDING_LEN)
+            cause = (SctpCause *)((unsigned char *)cause + clen);
+        else
+        {
+            rclen = 0;
+            cause = NULL;
+        }
+    }
+
+    return rclen;
+}
+
+/**
+ * SctpGetNextParam - Gets the next parameter from the chunk.
+ * @p: Pointer to the current SctpParam structure containing param data.
+ * @rclen: uint16_t value of the remaining chunk length.
+ * @plen: uint16_t value of the current param's length.
+ *
+ * Returns the new remaining length in the chunk and points p to the
+ * next param in the chunk.  If there are no more params left, it will
+ * return 0 and p will be set to NULL.
+ */
+inline uint16_t SctpGetNextParam(SctpParam *param, uint16_t rclen, uint16_t plen)
+{
+    if (rclen > 0)
+    {
+        /* Same as in SctpGetNextChunk(). */
+        if (rclen > SCTP_MAX_PADDING_LEN)
+            param = (SctpParam *)((unsigned char *)param + plen);
+        else
+        {
+            rclen = 0;
+            param = NULL;
+        }
+    }
+
+    return rclen;
+}
+
+
+/*
+ * Used often by SctpChunkTests.  Better to make it a macro.  Why a macro
+ * and not an inlined function?  Because if inline, we have to pass quite a
+ * few arguments and fuss around with snprintf().  Macros can avoid
+ * this and only needs two arguments.
+ */
+
+/* Check for a bad chunk length. */
+#define CHECK_CHUNK_LENGTH(c,s)                                                     \
+    if (clen < SCTP_##c##_C_LEN)                                                    \
+    {                                                                               \
+        SctpChunkAnomaly(p, "SCTP " #c " Chunk is smaller than " #s " bytes",       \
+                         DECODE_SCTP_##c##_C_BAD_LEN,                               \
+                         DECODE_SCTP_##c##_C_BAD_LEN_STR, true);                    \
+        break;                                                                      \
+    }
+
+/* Make sure the <CHUNKT_TYPE> chunk's flags field is 0. */
+#define CHECK_CHUNK_FLAGS_ARE_ZERO(c)                                               \
+    if (cflags > 0)                                                                 \
+    {                                                                               \
+        SctpChunkAnomaly(p, "SCTP " #c " chunk flags field "                        \
+                         "is non-zero", DECODE_SCTP_##c##_C_FLAGS_NZERO,            \
+                         DECODE_SCTP_##c##_C_FLAGS_NZERO_STR, true);                \
+        break;                                                                      \
+    }
+
+/**
+ * SctpChunkTests - Tests each chunk for various anomalies.
+ * @p: Pointer to Packet structure containing packet data.
+ * @sctp_chunk: Pointer to an SctpChunk structure containing chunk data.
+ *
+ * Tests each chunk for various anomalies as defined by the various RFCs.
+ * Returns void.
+ */
+void SctpChunkTests(Packet *p, const SctpChunk *sctp_chunk)
+{
+    uint8_t ctype = sctp_chunk->type;
+    uint8_t cflags = sctp_chunk->flags;
+    uint16_t clen = ntohs(sctp_chunk->length);
+    uint16_t remaining_chunk_len = clen;
+    uint16_t num_params = 0;
+    uint32_t tmp = 0x00000000;
+    SctpParam *param = NULL;
+
+    /* Check for zero-length chunk (minimum should ALWAYs be 4 bytes. */
+    if (unlikely(clen == 0))
+    {
+        SctpChunkAnomaly(p, "SCTP Chunk Length is 0 bytes",
+                         DECODE_SCTP_CHUNK_ZERO_LENGTH,
+                         DECODE_SCTP_CHUNK_ZERO_LENGTH_STR, true);
+        return;
+    }
+
+	/* Depending on the chunk type, check for certain anomalies, per the RFCs. */
+    switch (ctype)
+    {
+        /* DATA Chunk */
+        case SCTP_DATA_C:
+            CHECK_CHUNK_LENGTH(DATA, SCTP_DATA_C_LEN)
+
+            /*
+             * Make sure the DATA chunk's reserved area in flags is 0.
+             * Note: this includes in the 'I' flag defined in
+             * RFC7053, so bitwise-AND 0xf0, not 0xf8.
+             */
+            /*
+             * FIXME: Create bitfield or macros to check the DATA flags, as
+             * well any flags on the other chunks.  Not for here, of course,
+             * but for future SCTP rule options.
+             */
+            if (unlikely(cflags & 0xf0) > 0)
+            {
+                SctpChunkAnomaly(p, "SCTP DATA chunk flags reserved field "
+                                 "is non-zero", DECODE_SCTP_DATA_C_RSVD_NZERO,
+                                 DECODE_SCTP_DATA_C_RSVD_NZERO_STR, true);
+                break;
+            }
+            break;
+
+        /* INIT Chunk */
+        case SCTP_INIT_C:
+            CHECK_CHUNK_LENGTH(INIT, SCTP_INIT_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(INIT)
+
+            /*
+             * Per RFC4960:
+             *   - The initiate tag should not be 0 (error).
+             *   - For an INIT chunk, the verification tag MUST be zero.
+             *   - The value '0' cannot be used in either the # of outbound
+             *     streams or # of inbound streams fields.
+             */
+            if (unlikely(ntohl(sctp_chunk->init.initiate_tag)) == 0)
+            {
+                SctpChunkAnomaly(p, "SCTP INIT Chunk and initiate tag is == 0",
+                                 DECODE_SCTP_INIT_C_ITAG_ZERO,
+                                 DECODE_SCTP_INIT_C_ITAG_ZERO_STR, true);
+                break;
+            }
+            else if (unlikely(ntohl(p->sctph->sh_vtag)) != 0)
+            {
+                SctpChunkAnomaly(p, "SCTP INIT Chunk and verification tag is > 0",
+                                 DECODE_SCTP_INIT_C_VTAG_NZERO,
+                                 DECODE_SCTP_INIT_C_VTAG_NZERO_STR, true);
+                break;
+            }
+            else if (unlikely(ntohs(sctp_chunk->init.num_out_streams)) == 0)
+            {
+                SctpChunkAnomaly(p, "SCTP INIT Chunk # of outbound streams"
+                                 "is zero", DECODE_SCTP_INIT_C_NOUTSTR_ZERO,
+                                 DECODE_SCTP_INIT_C_NOUTSTR_ZERO_STR, true);
+                break;
+            }
+            else if (unlikely(ntohs(sctp_chunk->init.num_in_streams)) == 0)
+            {
+                SctpChunkAnomaly(p, "SCTP INIT Chunk # of inbound streams"
+                                 "is zero", DECODE_SCTP_INIT_C_NINSTR_ZERO,
+                                 DECODE_SCTP_INIT_C_NINSTR_ZERO_STR, true);
+                break;
+            }
+            break;
+
+        /* INIT ACK Chunk */
+        case SCTP_INIT_ACK_C:
+            CHECK_CHUNK_LENGTH(INIT_ACK, SCTP_INIT_ACK_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(INIT_ACK)
+
+            /*
+             * Per RFC4960:
+             *   - The initiate tag MUST NOT be 0.
+             *   - The value '0' cannot be used in either the # of outbound
+             *     streams or # of inbound streams fields.
+             */
+            if (unlikely(ntohl(sctp_chunk->init_ack.initiate_tag)) == 0)
+            {
+                SctpChunkAnomaly(p, "SCTP INIT_ACK Chunk and initiate tag is == 0",
+                                 DECODE_SCTP_INIT_ACK_C_ITAG_ZERO,
+                                 DECODE_SCTP_INIT_ACK_C_ITAG_ZERO_STR, true);
+                break;
+            }
+            else if (unlikely(ntohs(sctp_chunk->init_ack.num_out_streams)) == 0)
+            {
+                SctpChunkAnomaly(p, "SCTP INIT_ACK Chunk # of outbound streams"
+                                 "is zero", DECODE_SCTP_INIT_ACK_C_NOUTSTR_ZERO,
+                                 DECODE_SCTP_INIT_ACK_C_NOUTSTR_ZERO_STR, true);
+                break;
+            }
+            else if (unlikely(ntohs(sctp_chunk->init_ack.num_in_streams)) == 0)
+            {
+                SctpChunkAnomaly(p, "SCTP INIT_ACK Chunk # of inbound streams"
+                                 "is zero", DECODE_SCTP_INIT_ACK_C_NINSTR_ZERO,
+                                 DECODE_SCTP_INIT_ACK_C_NINSTR_ZERO_STR, true);
+                break;
+            }
+            break;
+
+        /* SACK Chunk */
+        case SCTP_SACK_C:
+            CHECK_CHUNK_LENGTH(SACK, SCTP_SACK_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(SACK)
+
+            break;
+
+        /* HEARTBEAT Chunk */
+        case SCTP_HEARTBEAT_C:
+            CHECK_CHUNK_LENGTH(HEARTBEAT, SCTP_HEARTBEAT_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(HEARTBEAT)
+
+            break;
+
+        /* HEARTBEAT_ACK Chunk */
+        case SCTP_HEARTBEAT_ACK_C:
+            CHECK_CHUNK_LENGTH(HEARTBEAT_ACK, SCTP_HEARTBEAT_ACK_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(HEARTBEAT_ACK)
+
+            break;
+
+        /* ABORT Chunk */
+        case SCTP_ABORT_C:
+            CHECK_CHUNK_LENGTH(ABORT, SCTP_ABORT_C_LEN)
+
+            /* Make sure the ABORT chunk's reserved area in flags is 0. */
+            if (unlikely(cflags & 0xfe) > 0)
+            {
+                SctpChunkAnomaly(p, "SCTP ABORT chunk flags reserved field "
+                                 "is non-zero", DECODE_SCTP_ABORT_C_RSVD_NZERO,
+                                 DECODE_SCTP_ABORT_C_RSVD_NZERO_STR, true);
+                break;
+            }
+
+            /* We do not check the Cause(es). */
+            /* XXX: Yet? */
+            break;
+
+        /* SHUTDOWN Chunk */
+        case SCTP_SHUTDOWN_C:
+            CHECK_CHUNK_LENGTH(SHUTDOWN, SCTP_SHUTDOWN_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(SHUTDOWN)
+
+            break;
+
+        /* SHUTDOWN_ACK Chunk */
+        case SCTP_SHUTDOWN_ACK_C:
+            CHECK_CHUNK_LENGTH(SHUTDOWN_ACK, SCTP_SHUTDOWN_ACK_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(SHUTDOWN_ACK)
+
+            break;
+
+        /* ERROR Chunk */
+        case SCTP_ERROR_C:
+            CHECK_CHUNK_LENGTH(ERROR, SCTP_ERROR_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(ERROR)
+
+            /* We do not check the Cause(es). */
+            /* XXX: Yet? */
+            break;
+
+        /* COOKIE_ECHO Chunk */
+        case SCTP_COOKIE_ECHO_C:
+            CHECK_CHUNK_LENGTH(COOKIE_ECHO, SCTP_COOKIE_ECHO_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(COOKIE_ECHO)
+
+            break;
+
+        /* COOKIE_ACK Chunk */
+        case SCTP_COOKIE_ACK_C:
+            CHECK_CHUNK_LENGTH(COOKIE_ACK, SCTP_COOKIE_ACK_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(COOKIE_ACK)
+
+            break;
+
+        /* ECNE Chunk */
+        case SCTP_ECNE_C:
+            CHECK_CHUNK_LENGTH(ECNE, SCTP_ECNE_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(ECNE)
+
+            break;
+
+        /* CWR Chunk */
+        case SCTP_CWR_C:
+            CHECK_CHUNK_LENGTH(CWR, SCTP_CWR_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(CWR)
+
+            break;
+
+        /* SHUTDOWN_COMPLETE Chunk */
+        case SCTP_SHUTDOWN_COMPLETE_C:
+            CHECK_CHUNK_LENGTH(SHUTDOWN_COMPLETE, SCTP_SHUTDOWN_COMPLETE_C_LEN)
+
+            /* Make sure the SHUTDOWN_COMPLETE chunk's reserved area in flags is 0. */
+            if (unlikely(cflags & 0xfe) > 0)
+            {
+                SctpChunkAnomaly(p, "SCTP SHUTDOWN_COMPLETE chunk flags reserved field "
+                                 "is non-zero", DECODE_SCTP_SHUTDOWN_COMPLETE_C_RSVD_NZERO,
+                                 DECODE_SCTP_SHUTDOWN_COMPLETE_C_RSVD_NZERO_STR, true);
+                break;
+            }
+            break;
+
+        /* AUTH Chunk */
+        case SCTP_AUTH_C:
+            CHECK_CHUNK_LENGTH(AUTH, SCTP_AUTH_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(AUTH)
+
+            break;
+
+        /* NR-SACK Chunk */
+        case SCTP_NR_SACK_C:
+            CHECK_CHUNK_LENGTH(SACK, SCTP_NR_SACK_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(NR_SACK)
+
+            break;
+
+        /* ASCONF_ACK Chunk */
+        case SCTP_ASCONF_ACK_C:
+            CHECK_CHUNK_LENGTH(ASCONF_ACK, SCTP_ASCONF_ACK_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(ASCONF_ACK)
+
+            break;
+
+        /* PKTDROP Chunk */
+        case SCTP_PKTDROP_C:
+            CHECK_CHUNK_LENGTH(PKTDROP, SCTP_PKTDROP_C_LEN)
+
+            /* Make sure the PKTDROP chunk's reserved area in flags is 0. */
+            if (unlikely(cflags & 0xf0) > 0)
+            {
+                SctpChunkAnomaly(p, "SCTP PKTDROP chunk flags reserved field "
+                                 "is non-zero", DECODE_SCTP_PKTDROP_C_FLAGS_NZERO,
+                                 DECODE_SCTP_PKTDROP_C_FLAGS_NZERO_STR, true);
+                break;
+            }
+            else if (unlikely(ntohs(sctp_chunk->pktdrop.reserved)) != 0)
+            {
+                SctpChunkAnomaly(p, "SCTP PKTDROP Chunk reserved field "
+                                 "is non-zero", DECODE_SCTP_PKTDROP_C_RSVD_NZERO,
+                                 DECODE_SCTP_PKTDROP_C_RSVD_NZERO_STR, true);
+                break;
+            }
+            break;
+
+        /* RE-CONFIG Chunk */
+        case SCTP_RECONFIG_C:
+            CHECK_CHUNK_LENGTH(RECONFIG, SCTP_RECONFIG_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(RECONFIG)
+
+            /*
+             * Count the number of parameters.  Per RFC6525, Section 3.1,
+             * a RE-CONFIG chunk has at least one parameter, but no more
+             * than two parameters.
+             */
+            param = (SctpParam *)sctp_chunk->reconfig.params;
+            remaining_chunk_len -= SCTP_RECONFIG_C_LEN;
+            while (likely(param != NULL) && (remaining_chunk_len != 0))
+            {
+                uint16_t param_len = SCTP_ADD_PADDING(ntohs(param->length));
+                remaining_chunk_len -= param_len;
+                switch (num_params)
+                {
+                    case 0:
+                        tmp = (uint32_t)ntohs(param->type);
+                        break;
+
+                    case 1:
+                        tmp += (uint32_t)ntohs(param->type);
+                        break;
+
+                    default:
+                        SctpChunkAnomaly(p, "SCTP RE-CONFIG Chunk has >2 parameters",
+                                         DECODE_SCTP_RECONFIG_C_GT_2_PARAMS,
+                                         DECODE_SCTP_RECONFIG_C_GT_2_PARAMS_STR, true);
+                        break;
+                }
+                num_params++;
+                remaining_chunk_len = SctpGetNextParam(param, remaining_chunk_len,
+                                                       param_len);
+            }
+
+            /*
+             * If just one parameter is present, make sure it's one of the
+             * allowed types.  Else, if two parameters are present, then
+             * make sure it's one of the allowed combinations.
+             */
+            if (num_params == 1)
+            {
+                switch (tmp)
+                {
+                    case SCTP_OUT_SSN_RST_REQ_P:
+                    case SCTP_IN_SSN_RST_REQ_P:
+                    case SCTP_SSNTSN_RST_REQ_P:
+                    case SCTP_RECONFIG_RESP_P:
+                    case SCTP_ADD_OUT_STRM_REQ_P:
+                    case SCTP_ADD_IN_STRM_REQ_P:
+                        break;
+
+                    default:
+                        SctpChunkAnomaly(p, "SCTP RE-CONFIG Chunk has invalid parameters",
+                                         DECODE_SCTP_RECONFIG_C_INVALID_PARAMS,
+                                         DECODE_SCTP_RECONFIG_C_INVALID_PARAMS_STR, false);
+                        break;
+                }
+            }
+            else if (num_params == 2)
+            {
+                switch (tmp)
+                {
+                    case (SCTP_OUT_SSN_RST_REQ_P + SCTP_IN_SSN_RST_REQ_P):
+                    case (SCTP_ADD_OUT_STRM_REQ_P + SCTP_ADD_IN_STRM_REQ_P):
+                    case (SCTP_RECONFIG_RESP_P + SCTP_OUT_SSN_RST_REQ_P):
+                    case (SCTP_RECONFIG_RESP_P + SCTP_RECONFIG_RESP_P):
+                        break;
+
+                    default:
+                        SctpChunkAnomaly(p, "SCTP RE-CONFIG Chunk has invalid parameters",
+                                         DECODE_SCTP_RECONFIG_C_INVALID_PARAMS,
+                                         DECODE_SCTP_RECONFIG_C_INVALID_PARAMS_STR, false);
+                        break;
+                }
+            }
+
+            break;
+
+        /* PAD Chunk */
+        case SCTP_PAD_C:
+            CHECK_CHUNK_LENGTH(PAD, SCTP_PAD_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(PAD)
+
+            break;
+
+        /* FORWARD_TSN Chunk */
+        case SCTP_FORWARD_TSN_C:
+            CHECK_CHUNK_LENGTH(FORWARD_TSN, SCTP_FORWARD_TSN_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(FORWARD_TSN)
+
+            break;
+
+        /* ASCONF Chunk */
+        case SCTP_ASCONF_C:
+            CHECK_CHUNK_LENGTH(ASCONF, SCTP_ASCONF_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(ASCONF)
+
+            break;
+
+        /* XXX: default catch-all for unknown chunk types. */
+    }
+}
+
+
+/**
+ * DecodeSCTP - Decode the SCTP transport layer.
+ * @pkt: Pointer to the packet data.
+ * @len: length from here to the end of the packet.
+ * @p: Pointer to a Packet structure containing decoded data.
+ *
+ * Decodes the SCTP transport layer protocol and all of its
+ * chunks/params/causes for use by other functions of Snort.
+ * Returns void.
+ */
+ void DecodeSCTP(const uint8_t *pkt, const uint32_t len, Packet *p)
+ {
+    if(unlikely(len < SCTP_COMMON_HEADER_LEN))
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_DECODE,
+            "Truncated SCTP common header (%d bytes)\n", len));
+
+        DecoderEvent(p, DECODE_SCTP_DGRAM_LT_SCTPHDR,
+                        DECODE_SCTP_DGRAM_LT_SCTPHDR_STR, 1, 1);
+
+        p->sctph = NULL;
+        pc.discards++;
+        pc.sctpdisc++;
+
+        return;
+    }
+
+	/* Overlay the SCTP header on top of the data. */
+    p->sctph = (SCTPHdr *) pkt;
+
+    /*
+     * Checksum code moved in front of the other decoder alerts.  If it's a
+     * bad checksum (maybe due to encrypted ESP traffic), the other alerts
+     * could be false positives.
+     */
+    if (ScSctpChecksums())
+    {
+        uint32_t csum_pkt = ntohl(p->sctph->sh_csum);
+        uint32_t csum_calc;
+        uint32_t csum_valid = 0;
+
+        /*
+         * RFC2960, SCTP's original RFC, origially used the Adler32 algorithm
+         * to generate the checksum.  However, on small packets, this was
+         * inadequate and RFC3309 updated RFC2960 to use the CRC32 algorithm
+         * for the checksum instead.  Both have now been superceded by RFC4960.
+         *
+         * Start by generating only the CRC32 checksum.  If that is invalid,
+         * then we will compute the Adler32 checksum and re-compare.  We also
+         * flag a decoder event, but continue processing the packet as an
+         * adler32 checksum isn't a fatal condition.
+         */
+        uint32_t csum_crc32 = SctpCrc32Chksum((unsigned char *)p->sctph, len);
+
+        if (unlikely((csum_crc32 == 0) || (csum_crc32 != csum_pkt)))
+        {
+            /* Try Adler32. */
+            uint32_t csum_adler32 = SctpAdler32Chksum((unsigned char *)p->sctph, len);
+
+            if ((csum_adler32 != 0) || (csum_adler32 == csum_pkt))
+            {
+                csum_valid = 1;
+                csum_calc = csum_adler32;
+
+                DEBUG_WRAP(DebugMessage(DEBUG_DECODE,
+                    "Checksum type is Adler32!\n"));
+                
+                DecoderEvent(p, DECODE_SCTP_CSUM_IS_ADLER32,
+                                DECODE_SCTP_CSUM_IS_ADLER32_STR, 1, 1);
+            }
+        }
+        else
+        {
+            csum_valid = 1;
+            csum_calc = csum_crc32;
+        }
+
+        if (unlikely(!csum_valid))
+        {
+            /*
+             * Don't drop the packet if this is encapuslated in Teredo or ESP.
+             * Just get rid of the SCTP header and stop decoding.
+             */
+            if (p->packet_flags & PKT_UNSURE_ENCAP)
+            {
+                p->sctph = NULL;
+                pc.discards++;
+                pc.sctpdisc++;
+
+                return;
+            }
+
+            p->error_flags |= PKT_ERR_CKSUM_SCTP;
+            DEBUG_WRAP(DebugMessage(DEBUG_DECODE, "Bad SCTP checksum\n",
+                                    "0x%x versus 0x%x\n", csum_calc,
+                                    csum_pkt););
+
+            if (ScIdsMode())
+                queueExecDrop(execSctpChksmDrop, p);
+        }
+        else
+            DEBUG_WRAP(DebugMessage(DEBUG_DECODE,"SCTP Checksum: OK\n"););
+    }
+
+    /* Fill in the printout data structs */
+    p->sp = ntohs(p->sctph->sh_sport);
+    p->dp = ntohs(p->sctph->sh_dport);
+
+    DEBUG_WRAP(DebugMessage(DEBUG_DECODE, "SCTP common header starts at: %p\n", p->sctph););
+
+    PushLayer(PROTO_SCTP, p, pkt, SCTP_COMMON_HEADER_LEN);
+    p->data = (uint8_t *) (pkt + SCTP_COMMON_HEADER_LEN);
+    p->dsize = (uint16_t)(len - SCTP_COMMON_HEADER_LEN);
+    p->proto_bits |= PROTO_BIT__SCTP;
+
+    /* Drop packet if we ignore this port */
+    if (ScIgnoreSctpPort(p->sp) || ScIgnoreSctpPort(p->dp))
+    {
+        /* Ignore all preprocessors for this packet */
+        p->packet_flags |= PKT_IGNORE;
+        return;
+    }
+
+    /* Now we check various features of the chunks. */
+    SctpChunk *chunk_data = (SctpChunk *)p->sctph->chunks;
+    uint16_t chunk_length = 0;
+    uint32_t remaining_len = (len - SCTP_COMMON_HEADER_LEN);
+
+    /*
+     * Loop through the remainder of the SCTP payload and check all chunks for
+     * decoder anomalies, based on RFC4960's guidelines. Chunk bundling is
+     * explained in RFC4960, Section 6.10.
+     */
+    while (likely(chunk_data != NULL) && (remaining_len != 0))
+    {
+        SctpChunkTests(p, chunk_data);
+        chunk_length = SCTP_ADD_PADDING(ntohs(chunk_data->length));
+        
+        /*
+         * If the chunk length is somehow greater than the length of the
+         * remainder of the packet, something isn't right.  Flag a decoder
+         * alert.
+         */
+        if (remaining_len >= chunk_length)
+            remaining_len -= chunk_length;
+        else
+        {
+            DEBUG_WRAP(DebugMessage(DEBUG_DECODE,
+                "SCTP chunk length (%d bytes) is > remaining packet "
+                "length (%d bytes)\n", chunk_length, remaining_len));
+
+            DecoderEvent(p, DECODE_SCTP_CHUNK_LEN_GT_DGRAM,
+                            DECODE_SCTP_CHUNK_LEN_GT_DGRAM_STR, 1, 1);
+
+            p->sctph = NULL;
+            pc.discards++;
+            pc.sctpdisc++;
+
+            return;
+        }
+
+        /* Get the next chunk. */
+        remaining_len = SctpGetNextChunk(chunk_data, remaining_len, chunk_length);
+        
+        /* Increment the chunk count.  Used by various SCTP rule options. */
+        p->sctp_chunk_count++;
+
+        /* Tally up the total length of the SCTP chunks. THIS INCLUDES PADDING BYTES! */
+        p->sctp_chunks_len += chunk_length;
+    }
+ }
+
 /*
  * Function: DecodeIPV6(uint8_t *, uint32_t)
  *
@@ -3605,6 +4382,11 @@ void DecodeIPV6Extensions(uint8_t next, const uint8_t *pkt, uint32_t len, Packet
             CheckIPv6ExtensionOrder(p);
             DecodeUDP(pkt, len, p);
             return;
+        case IPPROTO_SCTP:
+            pc.sctp6++;
+            CheckIPv6ExtensionOrder(p);
+            DecodeSCTP(pkt, len, p);
+            return;
         case IPPROTO_ICMPV6:
             pc.icmp6++;
             CheckIPv6ExtensionOrder(p);
@@ -4071,7 +4853,7 @@ void DecodeICMP6(const uint8_t *pkt, const uint32_t len, Packet *p)
     }
 
     p->proto_bits |= PROTO_BIT__ICMP;
-    p->proto_bits &= ~(PROTO_BIT__UDP | PROTO_BIT__TCP);
+    p->proto_bits &= ~(PROTO_BIT__UDP | PROTO_BIT__TCP | PROTO_BIT__SCTP);
 }
 
 /*
@@ -4169,6 +4951,15 @@ void DecodeICMPEmbeddedIP6(const uint8_t *pkt, const uint32_t len, Packet *p)
 
             break;
 
+        case IPPROTO_SCTP: /* decode the interesting part of the header */
+            p->orig_sctph = (SCTPHdr *)(pkt + IP6_HDR_LEN);
+
+            /* stuff more data into the printout data struct */
+            p->orig_sp = ntohs(p->orig_sctph->sh_sport);
+            p->orig_dp = ntohs(p->orig_sctph->sh_dport);
+
+            break;
+
         case IPPROTO_ICMP:
             p->orig_icmph = (ICMPHdr *)(pkt + IP6_HDR_LEN);
             break;
@@ -4366,6 +5157,12 @@ void DecodeESP(const uint8_t *pkt, uint32_t len, Packet *p)
             p->packet_flags &= ~PKT_UNSURE_ENCAP;
             break;
 
+        case IPPROTO_SCTP:
+            pc.sctp++;
+            DecodeSCTP(esp_payload, len, p);
+            p->packet_flags &= ~PKT_UNSURE_ENCAP;
+            break;
+
         case IPPROTO_ICMP:
             pc.icmp++;
             DecodeICMP(esp_payload, len, p);
@@ -5264,6 +6061,9 @@ void DecodeTCP(const uint8_t * pkt, const uint32_t len, Packet * p)
             if (p->packet_flags & PKT_UNSURE_ENCAP)
             {
                 p->tcph = NULL;
+                pc.discards++;
+                pc.tdisc++;
+
                 return;
             }
 
diff --git a/src/decode.h b/src/decode.h
index f87080f..44905a4 100644
--- a/src/decode.h
+++ b/src/decode.h
@@ -388,6 +388,132 @@ struct enc_header {
 #define DHCP_CLIENT_PORT    68
 #define DHCP_SERVER_PORT    67
 
+
+/*
+ * SCTP Definitions
+ */
+
+/* SCTP Chunk types. */
+#define SCTP_DATA_C                 0x00    /* DATA chunk (RFC4960) */
+#define SCTP_INIT_C                 0x01    /* INIT chunk (RFC4960) */
+#define SCTP_INIT_ACK_C             0x02    /* INIT-ACK chunk (RFC4960) */
+#define SCTP_SACK_C                 0x03    /* SACK chunk (RFC4960) */
+#define SCTP_HEARTBEAT_C            0x04    /* HEARTBEAT chunk (RFC4960) */
+#define SCTP_HEARTBEAT_ACK_C        0x05    /* HEARTBEAT-ACK chunk (RFC4960) */
+#define SCTP_ABORT_C                0x06    /* ABORT chunk (RFC4960) */
+#define SCTP_SHUTDOWN_C             0x07    /* SHUTDOWN chunk (RFC4960) */
+#define SCTP_SHUTDOWN_ACK_C         0x08    /* SHUTDOWN-ACK chunk (RFC4960) */
+#define SCTP_ERROR_C                0x09    /* ERROR chunk (RFC4960) */
+#define SCTP_COOKIE_ECHO_C          0x0a    /* COOKIE-ECHO chunk (RFC4960) */
+#define SCTP_COOKIE_ACK_C           0x0b    /* COOKIE-ACK chunk (RFC4960) */
+#define SCTP_ECNE_C                 0x0c    /* ECNE (Explicit Congestion Notification) chunk (RFC4960) */
+#define SCTP_CWR_C                  0x0d    /* CWR (Congestion Window Reduced) chunk (RFC4960) */
+#define SCTP_SHUTDOWN_COMPLETE_C    0x0e    /* SHUTDOWN-COMPLETE chunk (RFC4960) */
+#define SCTP_AUTH_C                 0x0f    /* AUTH chunk (RFC4895) */
+#define SCTP_NR_SACK_C              0x10    /* NR-SACK chunk (draft-tuexen-tsvwg-sctp-multipath-07) */
+#define SCTP_ASCONF_ACK_C           0x80    /* ASCONF-ACK chunk (RFC5061) */
+#define SCTP_PKTDROP_C              0x81    /* PKTDROP chunk (draft-stewart-sctp-pktdrprep-16) */
+#define SCTP_RECONFIG_C             0x82    /* RE-CONFIG chunk (RFC6525) */
+#define SCTP_PAD_C                  0x84    /* PAD chunk (RFC4820) */
+#define SCTP_FORWARD_TSN_C          0xc0    /* FORWARD-TSN chunk (RFC3758) */
+#define SCTP_ASCONF_C               0xc1    /* ASCONF chunk (RFC5061) */
+
+/* SCTP header lengths (minimum, anyways). */
+#define SCTP_COMMON_HEADER_LEN      12      /* SCTP common header is 12 bytes */
+#define SCTP_CAUSE_HEADER_LEN        4      /* SCTP cause header length is 4 bytes */
+#define SCTP_PARAM_HEADER_LEN        4      /* SCTP parameter header length is 4 bytes */
+#define SCTP_MAX_PADDING_LEN         3      /* Maximum amount of padding allowed to align data. */
+#define SCTP_DATA_C_LEN             17      /* Minimum size of a DATA (0x00) chunk */
+#define SCTP_INIT_C_LEN             20      /* Minimum size of an INIT (0x01) chunk */
+#define SCTP_INIT_ACK_C_LEN         20      /* Same as INIT, except type is 0x02) */
+#define SCTP_SACK_C_LEN             16      /* Minimum size of a SACK (0x03) chunk */
+#define SCTP_HEARTBEAT_C_LEN         8      /* Minimum size of a HEARTBEAT (0x04) chunk */
+#define SCTP_HEARTBEAT_ACK_C_LEN     8      /* Minimum size of a HEARTBEAT ACK (0x05) chunk */
+#define SCTP_ABORT_C_LEN             4      /* Minimum size of an ABORT (0x06) chunk */
+#define SCTP_SHUTDOWN_C_LEN          8      /* Minimum size of a SHUTDOWN (0x07) chunk */
+#define SCTP_SHUTDOWN_ACK_C_LEN      4      /* Minimum size of a SHUTDOWN ACK (0x08) chunk */
+#define SCTP_ERROR_C_LEN             8      /* Minimum size of an ERROR (0x09) chunk */
+#define SCTP_COOKIE_ECHO_C_LEN       4      /* Minimum size of a COOKIE ECHO (0x0a) chunk */
+#define SCTP_COOKIE_ACK_C_LEN        4      /* Minimum size of a COOKIE ACK (0x0b) chunk */
+#define SCTP_ECNE_C_LEN              8      /* Minimum size of an ECNE (0x0c) chunk */
+#define SCTP_CWR_C_LEN               8      /* Minimum size of a CWR (0x0d) chunk */
+#define SCTP_SHUTDOWN_COMPLETE_C_LEN 4      /* Minimum size of a SHUTDOWN COMPLETE (0x0e) chunk */
+#define SCTP_AUTH_C_LEN              4      /* Minimum size of an AUTH (0x0f) chunk */
+#define SCTP_NR_SACK_C_LEN          20      /* Minimum size of an NR-SACK (0x10) chunk */
+#define SCTP_ASCONF_ACK_C_LEN        8      /* Minimum size of an ASCONF ACK (0x80) chunk */
+#define SCTP_PKTDROP_C_LEN          16      /* Minimum size of a PKTDROP (0x81) chunk */
+#define SCTP_RECONFIG_C_LEN          4      /* Minimim size of a RE-CONFIG (0x82) chunk */
+#define SCTP_PAD_C_LEN               4      /* Minimum size of a PAD (0x84) chunk */
+#define SCTP_FORWARD_TSN_C_LEN      12      /* Minimum size of a FORWARD TSN (0xc0) chunk */
+#define SCTP_ASCONF_C_LEN           12      /* Minimum size of an ASCONF (0xc1) chunk */
+
+/* SCTP Parameter Types. */
+#define SCTP_HEARTBEAT_INFO_P       0x0001  /* Heartbeat Info */
+    /* 0x0002 - 0x0004 Unassigned */
+#define SCTP_IPV4_ADDR_P            0x0005  /* IPv4 Address */
+#define SCTP_IPV6_ADDR_P            0x0006  /* IPv6 Address */
+#define SCTP_STATE_COOKIE_P         0x0007  /* State Cookie */
+#define SCTP_UNKNOWN_P              0x0008  /* Unrecognized Parameters */
+#define SCTP_COOKIE_PRESERVE_P      0x0009  /* Cookie Preservative */
+#define SCTP_HOSTNAME_ADDR_P        0x000b  /* Host Name Address */
+#define SCTP_SUPP_ADDRTYPES_P       0x000c  /* Supported Address Types */
+#define SCTP_OUT_SSN_RST_REQ_P      0x000d  /* Outgoing SSN Reset Request (RFC6525) */
+#define SCTP_IN_SSN_RST_REQ_P       0x000e  /* Incoming SSN Reset Request (RFC6525) */
+#define SCTP_SSNTSN_RST_REQ_P       0x000f  /* SSN/TSN Reset Request (RFC6525) */
+#define SCTP_RECONFIG_RESP_P        0x0010  /* Re-configuration Response (RFC6525) */
+#define SCTP_ADD_OUT_STRM_REQ_P     0x0011  /* Add Outgoing Streams Request (RFC6525) */
+#define SCTP_ADD_IN_STRM_REQ_P      0x0012  /* Add Incoming Streams Request (RFC6525) */
+    /* 0x0013 = 0x7fff Unassigned */
+#define SCTP_ECN_CAPABLE_P          0x8000  /* ECN Capable (Reserved) */
+#define SCTP_NONCE_SUPPORTED_P      0x8001  /* Nonce-Supported */
+#define SCTP_RANDOM_P               0x8002  /* Random */
+#define SCTP_CHUNK_LIST_P           0x8003  /* Chunk List */
+#define SCTP_REQ_HMAC_ALGO_P        0x8004  /* Requested HMAC Algorithm Parameter */
+#define SCTP_PADDING_P              0x8005  /* Padding */
+#define SCTP_SUPP_EXTEN_P           0x8008  /* Supported Extensions */
+    /* 0x8009 - 0xbfff Unassigned */
+#define SCTP_SUPP_FWD_TSN_P         0xc000  /* Forward TSN Supported */
+#define SCTP_ADD_IP_P               0xc001  /* Add IP */
+#define SCTP_DEL_IP_P               0xc002  /* Delete IP */
+#define SCTP_SUPP_ERR_CAUSE_P       0xc003  /* Error Cause Indication */
+#define SCTP_PRIMARY_ADDR_P         0xc004  /* Set Primary Address */
+#define SCTP_SUCCESS_P              0xc005  /* Success Indication */
+#define SCTP_ADAPT_LAYER_P          0xc006  /* Adaptation Layer Indication */
+
+/* SCTP Cause codes (for ERROR/ABORT chunks) */
+#define SCTP_INV_STREAM_ID_CC       0x0001  /* Invalid Stream Identifier */
+#define SCTP_MISSING_MAND_PARAM_CC  0x0002  /* Missing Mandatory Parameter */
+#define SCTP_STALE_COOKIE_ERR_CC    0x0003  /* Stale Cookie Error */
+#define SCTP_OUT_OF_RESOURCE_CC     0x0004  /* Out of Resource */
+#define SCTP_UNRESOLVABLE_ADDR_CC   0x0005  /* Unresolvable Address */
+#define SCTP_UNKNOWN_CHUNK_CC       0x0006  /* Unrecognized Chunk Type */
+#define SCTP_INV_MAND_PARAM_CC      0x0007  /* Invalid Mandatory Parameter */
+#define SCTP_UNKNOWN_PARAM_CC       0x0008  /* Unrecognized Parameters */
+#define SCTP_NO_USER_DATA_CC        0x0009  /* No User Data */
+#define SCTP_COOKIE_RECV_SHUTDWN_CC 0x000a  /* Cookie Received While Shutting Down */
+#define SCTP_RSTRT_ASSOC_NW_ADDR_CC 0x000b  /* Restart of an Association with New Addresses */
+#define SCTP_USER_ABORT_CC          0x000c  /* User Initiated Abort (RFC4460) */
+#define SCTP_PROTO_VIOLATION_CC     0x000d  /* Protocol Violation (RFC4460) */
+    /* 0x000e - 0x009f Unassigned */
+#define SCTP_REQ_DEL_LAST_IPADDR_CC 0x00a0  /* Request to Delete Last Remaining IP Address (RFC5061) */
+#define SCTP_OP_DENY_RSRC_SHORT_CC  0x00a1  /* Operation Refused Due to Resource Shortage (RFC5061) */
+#define SCTP_REQ_DEL_SRC_IPADDR_CC  0x00a2  /* Request to Delete Source IP Address (RFC5061) */
+#define SCTP_ASSOC_ABRT_ILL_AACK_CC 0x00a3  /* Association Aborted Due to Illegal ASCONF-ACK (RFC5061) */
+#define SCTP_REQ_DENY_NO_AUTH_CC    0x00a4  /* Request Refused - No Authorization (RFC5061) */
+    /* 0x00a5 - 0x0104 Unassigned */
+#define SCTP_UNSUPP_HMAC_ID_CC      0x0105  /* Unsupported HMAC Identifier (RFC4895) */
+    /* 0x0106 - 0xffff Unassigned */
+
+/* SCTP HMAC Algorithm bits. */
+#define SCTP_SHA1_HMAC              0x0001  /* HMAC SHA-1 */
+#define SCTP_SHA256_HMAC            0x0003  /* HMAC SHA-256 */
+
+/* Misc SCTP Bits.  Borrowed from Wireshark's epan/dissectors/packet-sctp.c. */
+#define SCTP_ADD_PADDING(x) ((((x) + 3) >> 2) << 2)
+
+/* End SCTP definitions */
+
+
 #ifndef NO_NON_ETHER_DECODER
 /* Start Token Ring */
 #define TR_ALEN             6        /* octets in an Ethernet header */
@@ -670,8 +796,9 @@ typedef enum {
 #define PKT_ERR_CKSUM_UDP    0x04
 #define PKT_ERR_CKSUM_ICMP   0x08
 #define PKT_ERR_CKSUM_IGMP   0x10
-#define PKT_ERR_CKSUM_ANY    0x1F
-#define PKT_ERR_BAD_TTL      0x20
+#define PKT_ERR_CKSUM_SCTP   0x20
+#define PKT_ERR_CKSUM_ANY    0x3F
+#define PKT_ERR_BAD_TTL      0x40
 
 /*  D A T A  S T R U C T U R E S  *********************************************/
 typedef int (*LogFunction)(void *ssnptr, uint8_t **buf, uint32_t *len, uint32_t *type);
@@ -1498,6 +1625,327 @@ typedef struct _EtherARP
 }         EtherARP;
 
 
+/*
+ * SCTP data structures
+ */
+#ifdef _MSC_VER
+  /* Visual C++ pragma to disable warning
+   * messages about nonstandard bit field type
+   */
+  #pragma warning( disable : 4214 )
+#endif
+
+#pragma pack(push)  /* push current alignment to stack */
+#pragma pack(1)     /* set alignment to 1 byte boundary */
+
+typedef struct _SCTPHdr
+{
+    uint16_t sh_sport;        /* Source port */
+    uint16_t sh_dport;        /* Destination port */
+    uint32_t sh_vtag;         /* Verification tag */
+    uint32_t sh_csum;         /* Checksum (CRC32c/Adler32) */
+    uint8_t chunks[0];        /* Start of SCTP Chunks */
+} SCTPHdr;
+
+/* Sctp Cause Header (for the ABORT/ERROR chunks). */
+typedef struct _SctpCause
+{
+    uint16_t code;
+    uint16_t length;
+    uint8_t info[0];
+} SctpCause;
+
+/* Sctp Chunk Type Definitions. */
+typedef struct _SctpDataChunk
+{
+    uint32_t tsn;
+    uint16_t stream_id;
+    uint16_t stream_seq;
+    uint32_t payload_id;
+    uint8_t data[0];
+} SctpDataChunk;
+
+typedef struct _SctpInitChunk
+{
+    uint32_t initiate_tag;
+    uint32_t a_rwnd;
+    uint16_t num_out_streams;
+    uint16_t num_in_streams;
+    uint32_t tsn;
+    uint8_t params[0];
+} SctpInitChunk;
+
+typedef struct _SctpInitAckChunk
+{
+    uint32_t initiate_tag;
+    uint32_t a_rwnd;
+    uint16_t num_out_streams;
+    uint16_t num_in_streams;
+    uint32_t tsn;
+    uint8_t params[0];
+} SctpInitAckChunk;
+
+typedef struct _SctpSackChunk
+{
+    uint32_t cuml_tsn_ack;
+    uint32_t a_rwnd;
+    uint16_t num_gap_acks;
+    uint16_t num_dupe_tsns;
+    uint8_t data[0];
+} SctpSackChunk;
+
+typedef struct _SctpHeartbeatChunk
+{
+    uint8_t info[0];
+} SctpHeartbeatChunk;
+
+typedef struct _SctpHeartbeatAckChunk
+{
+    uint8_t info[0];
+} SctpHeartbeatAckChunk;
+
+typedef struct _SctpAbortChunk
+{
+    uint8_t causes[0];
+} SctpAbortChunk;
+
+typedef struct _SctpShutdownChunk
+{
+    uint32_t tsn_ack;
+} SctpShutdownChunk;
+
+typedef struct _SctpErrorChunk
+{
+    uint8_t causes[0];
+} SctpErrorChunk;
+
+typedef struct _SctpCookieEchoChunk
+{
+    char *cookie;
+} SctpCookieEchoChunk;
+
+typedef struct _SctpEcneChunk
+{
+    uint32_t lowest_tsn;
+} SctpEcneChunk;
+
+typedef struct _SctpCwrChunk
+{
+    uint32_t lowest_tsn;
+} SctpCwrChunk;
+
+typedef struct _SctpAuthChunk
+{
+    uint16_t skey_id;
+    uint16_t hmac_id;
+    uint8_t hmac[0];
+    uint8_t params[0];
+} SctpAuthChunk;
+
+typedef struct _SctpNrSackChunk
+{
+    uint32_t cuml_tsn_ack;
+    uint32_t a_rwnd;
+    uint16_t num_r_gap_acks;
+    uint16_t num_nr_gap_acks;
+    uint16_t num_dupe_tsns;
+    uint16_t reserved;
+    uint8_t data[0];
+} SctpNrSackChunk;
+
+typedef struct _SctpAsconfAckChunk
+{
+    uint32_t seq;
+    uint8_t params[0];
+} SctpAsconfAckChunk;
+
+typedef struct _SctpPktdropChunk
+{
+    uint32_t lbmr;
+    uint32_t queue_size;
+    uint16_t trunc_length;
+    uint16_t reserved;
+    uint8_t dropped_pkt[0];
+} SctpPktdropChunk;
+
+typedef struct _SctpReConfigChunk
+{
+    uint8_t params[0];
+} SctpReConfigChunk;
+
+typedef struct _SctpPadChunk
+{
+    uint8_t padding[0];
+} SctpPadChunk;
+
+typedef struct _SctpForwardTsnChunk
+{
+    uint32_t new_tsn;
+    uint8_t streams[0];
+} SctpForwardTsnChunk;
+
+typedef struct _SctpAsconfChunk
+{
+    uint32_t seq;
+    /* One IPv4 or IPv6 addr param is mandatory, but start at params[0]. */
+    uint8_t params[0];
+} SctpAsconfChunk;
+
+
+/* Sctp Chunk structure. */
+typedef struct _SctpChunk
+{
+    uint8_t type;      /* chunk type */
+    uint8_t flags;     /* chunk flags */
+    uint16_t length;   /* chunk length */
+
+    union
+    {
+        uint8_t value[0]; /* Access a chunk generically. */
+        SctpDataChunk data;
+        SctpInitChunk init;
+        SctpInitAckChunk init_ack;
+        SctpSackChunk sack;
+        SctpHeartbeatChunk heartbeat;
+        SctpHeartbeatAckChunk heartbeat_ack;
+        SctpAbortChunk abort;
+        SctpShutdownChunk shutdown;
+        /* No struct for shutdown_ack. */
+        SctpErrorChunk error;
+        SctpCookieEchoChunk cookie_echo;
+        /* No struct for cookie_ack. */
+        SctpEcneChunk ecne;
+        SctpEcneChunk cwr;
+        /* No struct for shutdown_complete. */
+        SctpAuthChunk auth;
+        SctpNrSackChunk nr_sack;
+        SctpAsconfAckChunk asconf_ack;
+        SctpPktdropChunk pktdrop;
+        SctpReConfigChunk reconfig;
+        SctpPadChunk pad;
+        SctpForwardTsnChunk forward_tsn;
+        SctpAsconfChunk asconf;
+    };
+} SctpChunk;
+
+
+/* Sctp Param Type Definitions. */
+typedef struct _SctpHeartbeatInfoParam
+{
+    uint8_t value[0];
+} SctpHeartbeatInfoParam;
+
+typedef struct _SctpIPv4Param
+{
+    uint32_t addr;
+    uint32_t pad1;
+    uint32_t pad2;
+    uint32_t pad3;
+} SctpIPv4Param;
+
+typedef struct _SctpIPv6Param
+{
+    struct in6_addr addr;
+} SctpIPv6Param;
+
+typedef struct _SctpStateCookieParam
+{
+    uint8_t value[0];
+} SctpStateCookieParam;
+
+typedef struct _SctpUnrecogParam
+{
+    uint8_t value[0];
+} SctpUnrecogParam;
+
+typedef struct _SctpCookiePreservParam
+{
+    uint32_t lifespan_incr;
+} SctpCookiePreservParam;
+
+typedef struct _SctpHostAddrParam
+{
+    uint8_t value[0];
+} SctpHostAddrParam;
+
+typedef struct _SctpSuppAddrTypeParam
+{
+    uint8_t value[0];
+} SctpSuppAddrTypeParam;
+
+typedef struct _SctpOutSsnRstReqParam
+{
+    uint32_t req_seq;
+    uint32_t resp_seq;
+    uint32_t last_tsn;
+    uint8_t stream_nums[0];
+} SctpOutSsnRstReqParam;
+
+typedef struct _SctpInSsnRstReqParam
+{
+    uint32_t req_seq;
+    uint8_t stream_nums[0];
+} SctpInSsnRstReqParam;
+
+typedef struct _SctpSsnTsnRstReqParam
+{
+    uint32_t req_seq;
+} SctpSsnTsnRstReqParam;
+
+//typedef struct _SctpReconfigRespParam
+//{
+//    uint32_t resp_seq;
+//    uint32_t result;
+//    uint32_t send /* XXX: There are 2 optional params that can appear -- how to handle? */
+
+typedef struct _SctpParam
+{
+    uint16_t type;
+    uint16_t length;
+
+    union
+    {
+        uint8_t value[0];                      /* Access a param generically */
+        SctpHeartbeatInfoParam heartbeat_info; /* 0x0001 */
+        SctpIPv4Param ipv4;                    /* 0x0005 */
+        SctpIPv6Param ipv6;                    /* 0x0006 */
+        SctpStateCookieParam state_cookie;     /* 0x0007 */
+        SctpUnrecogParam unknown;              /* 0x0008 */
+        SctpCookiePreservParam cookie_preserv; /* 0x0009 */
+        SctpHostAddrParam hostname;            /* 0x000b */
+        SctpSuppAddrTypeParam addr_type;       /* 0x000c */
+        SctpOutSsnRstReqParam out_ssn_rst_req; /* 0x000d */
+        SctpInSsnRstReqParam in_ssn_rst_req;   /* 0x000e */
+        SctpSsnTsnRstReqParam ssn_tsn_rst_req; /* 0x000f */
+        /* XXX: Add more as time permits */
+    };
+} SctpParam;
+
+
+
+#pragma pack(pop)   /* restore original alignment from stack */
+
+
+#ifdef _MSC_VER
+  /* Visual C++ pragma to enable warning messages
+   * about nonstandard bit field type
+   */
+  #pragma warning( default : 4214 )
+#endif
+/* End SCTP data structures */
+
+
+/*
+ * SCTP prototypes for utility functions used in other modules.
+ */
+
+inline uint32_t SctpGetNextChunk(SctpChunk *, uint32_t, uint16_t);
+inline uint16_t SctpGetNextCause(SctpCause *, uint16_t, uint16_t);
+inline uint16_t SctpGetNextParam(SctpParam *, uint16_t, uint16_t);
+
+/* End SCTP prototypes */
+
+
 #ifndef NO_NON_ETHER_DECODER
 typedef struct _EtherEapol
 {
@@ -1635,6 +2083,7 @@ typedef struct _Packet
     const UDPHdr *udph, *orig_udph;
     const UDPHdr *inner_udph;   /* if Teredo + UDP, this will be the inner UDP header */
     const UDPHdr *outer_udph;   /* if Teredo + UDP, this will be the outer UDP header */
+    const SCTPHdr *sctph, *orig_sctph;
     const ICMPHdr *icmph, *orig_icmph;
 
     const uint8_t *data;        /* packet payload pointer */
@@ -1663,6 +2112,8 @@ typedef struct _Packet
     uint32_t preprocessor_bits; /* flags for preprocessors to check */
     uint32_t preproc_reassembly_pkt_bits;
 
+    uint32_t sctp_chunk_count;  /* Counter for # of SCTP chunks in an SCTP packet. */
+    uint32_t sctp_chunks_len;   /* Total length of all SCTP chunks in a pkt + padding. */
     uint32_t packet_flags;      /* special flags for the packet */
 
     uint32_t xtradata_mask;
@@ -1683,10 +2134,10 @@ typedef struct _Packet
     uint16_t tcp_options_len;
 
     //vvv-----------------------------
-    uint16_t sp;                /* source port (TCP/UDP) */
-    uint16_t dp;                /* dest port (TCP/UDP) */
-    uint16_t orig_sp;           /* source port (TCP/UDP) of original datagram */
-    uint16_t orig_dp;           /* dest port (TCP/UDP) of original datagram */
+    uint16_t sp;                /* source port (TCP/UDP/SCTP) */
+    uint16_t dp;                /* dest port (TCP/UDP/SCTP) */
+    uint16_t orig_sp;           /* source port (TCP/UDP/SCTP) of original datagram */
+    uint16_t orig_dp;           /* dest port (TCP/UDP/SCTP) of original datagram */
     //^^^-----------------------------
     // and so on ...
 
@@ -1776,15 +2227,17 @@ typedef struct _Packet
 #define PROTO_BIT__ARP      0x0002
 #define PROTO_BIT__TCP      0x0004
 #define PROTO_BIT__UDP      0x0008
-#define PROTO_BIT__ICMP     0x0010
-#define PROTO_BIT__TEREDO   0x0020
-#define PROTO_BIT__GTP      0x0040
+#define PROTO_BIT__SCTP     0x0010
+#define PROTO_BIT__ICMP     0x0020
+#define PROTO_BIT__TEREDO   0x0040
+#define PROTO_BIT__GTP      0x0080
 #define PROTO_BIT__OTHER    0x8000
 #define PROTO_BIT__ALL      0xffff
 
 #define IsIP(p) (IPH_IS_VALID(p))
 #define IsTCP(p) (IsIP(p) && p->tcph)
 #define IsUDP(p) (IsIP(p) && p->udph)
+#define IsSCTP(p) (IsIP(p) && p->sctph)
 #define IsICMP(p) (IsIP(p) && p->icmph)
 #define GET_PKT_SEQ(p) (ntohl(p->tcph->th_seq))
 
@@ -1854,6 +2307,7 @@ void DecodeIP(const uint8_t *, const uint32_t, Packet *);
 void DecodeIPV6(const uint8_t *, uint32_t, Packet *);
 void DecodeTCP(const uint8_t *, const uint32_t, Packet *);
 void DecodeUDP(const uint8_t *, const uint32_t, Packet *);
+void DecodeSCTP(const uint8_t *, const uint32_t, Packet *);
 void DecodeICMP(const uint8_t *, const uint32_t, Packet *);
 void DecodeICMP6(const uint8_t *, const uint32_t, Packet *);
 void DecodeICMPEmbeddedIP(const uint8_t *, const uint32_t, Packet *);
diff --git a/src/detection-plugins/Makefile.am b/src/detection-plugins/Makefile.am
index 40b1ea3..a9c81c3 100644
--- a/src/detection-plugins/Makefile.am
+++ b/src/detection-plugins/Makefile.am
@@ -51,7 +51,12 @@ sp_urilen_check.c sp_urilen_check.h \
 sp_file_data.c sp_file_data.h \
 sp_base64_decode.c sp_base64_decode.h \
 sp_base64_data.c sp_base64_data.h \
-sp_pkt_data.c sp_pkt_data.h
+sp_pkt_data.c sp_pkt_data.h \
+sp_sctp_chunk_type.c sp_sctp_chunk_type.h \
+sp_sctp_chunk_flags.c sp_sctp_chunk_flags.h \
+sp_sctp_chunk_field.c sp_sctp_chunk_field.h \
+sp_sctp_num_chunks.c sp_sctp_num_chunks.h \
+sp_sctp_cause_code.c sp_sctp_cause_code.h
 
 if BUILD_REACT
 libspd_a_SOURCES += sp_react.c sp_react.h
diff --git a/src/detection-plugins/Makefile.in b/src/detection-plugins/Makefile.in
index 24d031b..21c5e5b 100644
--- a/src/detection-plugins/Makefile.in
+++ b/src/detection-plugins/Makefile.in
@@ -124,7 +124,12 @@ am__libspd_a_SOURCES_DIST = detection_options.c detection_options.h \
 	sp_file_data.h sp_base64_decode.c sp_base64_decode.h \
 	sp_base64_data.c sp_base64_data.h sp_pkt_data.c sp_pkt_data.h \
 	sp_react.c sp_react.h sp_respond3.c sp_respond.h \
-	sp_file_type.c sp_file_type.h sp_appid.c sp_appid.h
+	sp_file_type.c sp_file_type.h sp_appid.c sp_appid.h \
+	sp_sctp_chunk_type.c sp_sctp_chunk_type.h \
+	sp_sctp_chunk_flags.c sp_sctp_chunk_flags.h \
+	sp_sctp_chunk_field.c sp_sctp_chunk_field.h \
+	sp_sctp_num_chunks.c sp_sctp_num_chunks.h \
+	sp_sctp_cause_code.c sp_sctp_cause_code.h
 @BUILD_REACT_TRUE at am__objects_1 = sp_react.$(OBJEXT)
 @BUILD_RESPOND3_TRUE at am__objects_2 = sp_respond3.$(OBJEXT)
 @FEAT_FILE_INSPECT_TRUE at am__objects_3 = sp_file_type.$(OBJEXT)
@@ -147,8 +152,11 @@ am_libspd_a_OBJECTS = detection_options.$(OBJEXT) sp_asn1.$(OBJEXT) \
 	sp_tcp_win_check.$(OBJEXT) sp_ttl_check.$(OBJEXT) \
 	sp_urilen_check.$(OBJEXT) sp_file_data.$(OBJEXT) \
 	sp_base64_decode.$(OBJEXT) sp_base64_data.$(OBJEXT) \
-	sp_pkt_data.$(OBJEXT) $(am__objects_1) $(am__objects_2) \
-	$(am__objects_3) $(am__objects_4)
+	sp_pkt_data.$(OBJEXT) sp_sctp_chunk_type.$(OBJEXT) \
+	sp_sctp_chunk_flags.$(OBJEXT) sp_sctp_chunk_field.$(OBJEXT) \
+	sp_sctp_num_chunks.$(OBJEXT) sp_sctp_cause_code.$(OBJEXT) \
+	$(am__objects_1) $(am__objects_2) $(am__objects_3) \
+	$(am__objects_4)
 nodist_libspd_a_OBJECTS = sf_snort_plugin_hdropts.$(OBJEXT)
 libspd_a_OBJECTS = $(am_libspd_a_OBJECTS) $(nodist_libspd_a_OBJECTS)
 AM_V_P = $(am__v_P_ at AM_V@)
@@ -382,6 +390,11 @@ libspd_a_SOURCES = detection_options.c detection_options.h sp_asn1.c \
 	sp_urilen_check.c sp_urilen_check.h sp_file_data.c \
 	sp_file_data.h sp_base64_decode.c sp_base64_decode.h \
 	sp_base64_data.c sp_base64_data.h sp_pkt_data.c sp_pkt_data.h \
+	sp_sctp_chunk_type.c sp_sctp_chunk_type.h \
+	sp_sctp_chunk_flags.c sp_sctp_chunk_flags.h \
+	sp_sctp_chunk_field.c sp_sctp_chunk_field.h \
+	sp_sctp_num_chunks.c sp_sctp_num_chunks.h \
+	sp_sctp_cause_code.c sp_sctp_cause_code.h \
 	$(am__append_1) $(am__append_2) $(am__append_3) \
 	$(am__append_4)
 copy_files = \
diff --git a/src/detection-plugins/detection_options.c b/src/detection-plugins/detection_options.c
index 862f3ed..905e11f 100644
--- a/src/detection-plugins/detection_options.c
+++ b/src/detection-plugins/detection_options.c
@@ -70,6 +70,11 @@
 #include "sp_isdataat.h"
 #include "sp_pattern_match.h"
 #include "sp_pcre.h"
+#include "sp_sctp_chunk_type.h"
+#include "sp_sctp_chunk_flags.h"
+#include "sp_sctp_chunk_field.h"
+#include "sp_sctp_num_chunks.h"
+#include "sp_sctp_cause_code.h"
 #ifdef ENABLE_REACT
 #include "sp_react.h"
 #endif
@@ -234,6 +239,21 @@ uint32_t detection_option_hash_func(SFHASHFCN *p, unsigned char *k, int n)
         case RULE_OPTION_TYPE_URILEN:
             hash = UriLenCheckHash(key->option_data);
             break;
+        case RULE_OPTION_TYPE_SCTP_CHUNK_TYPE:
+            hash = SctpChunkTypeHash(key->option_data);
+            break;
+        case RULE_OPTION_TYPE_SCTP_CHUNK_FLAGS:
+            hash = SctpChunkFlagsHash(key->option_data);
+            break;
+        case RULE_OPTION_TYPE_SCTP_CHUNK_FIELD:
+            hash = SctpChunkFieldHash(key->option_data);
+            break;
+        case RULE_OPTION_TYPE_SCTP_NUM_CHUNKS:
+            hash = SctpNumChunksHash(key->option_data);
+            break;
+        case RULE_OPTION_TYPE_SCTP_CAUSE_CODE:
+            hash = SctpCauseCodeHash(key->option_data);
+            break;
         case RULE_OPTION_TYPE_HDR_OPT_CHECK:
             hash = HdrOptCheckHash(key->option_data);
             break;
@@ -396,6 +416,21 @@ int detection_option_key_compare_func(const void *k1, const void *k2, size_t n)
         case RULE_OPTION_TYPE_URILEN:
             ret = UriLenCheckCompare(key1->option_data, key2->option_data);
             break;
+        case RULE_OPTION_TYPE_SCTP_CHUNK_TYPE:
+            ret = SctpChunkTypeCompare(key1->option_data, key2->option_data);
+            break;
+        case RULE_OPTION_TYPE_SCTP_CHUNK_FLAGS:
+            ret = SctpChunkFlagsCompare(key1->option_data, key2->option_data);
+            break;
+        case RULE_OPTION_TYPE_SCTP_CHUNK_FIELD:
+            ret = SctpChunkFieldCompare(key1->option_data, key2->option_data);
+            break;
+        case RULE_OPTION_TYPE_SCTP_NUM_CHUNKS:
+            ret = SctpNumChunksCompare(key1->option_data, key2->option_data);
+            break;
+        case RULE_OPTION_TYPE_SCTP_CAUSE_CODE:
+            ret = SctpCauseCodeCompare(key1->option_data, key2->option_data);
+            break;
         case RULE_OPTION_TYPE_HDR_OPT_CHECK:
             ret = HdrOptCheckCompare(key1->option_data, key2->option_data);
             break;
@@ -427,133 +462,90 @@ int detection_hash_free_func(void *option_key, void *data)
     switch (key->option_type)
     {
         /* Call free function specific to the key type */
-        case RULE_OPTION_TYPE_ASN1:
-            free(key->option_data);
+        case RULE_OPTION_TYPE_CONTENT:
+        case RULE_OPTION_TYPE_CONTENT_URI:
+            PatternMatchFree(key->option_data);
             break;
+
+        case RULE_OPTION_TYPE_ASN1:
         case RULE_OPTION_TYPE_BYTE_TEST:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_BYTE_JUMP:
-            free(key->option_data);
-            break;
-        case RULE_OPTION_TYPE_BYTE_EXTRACT:
-            ByteExtractFree(key->option_data);
-            break;
         case RULE_OPTION_TYPE_FLOW:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_CVS:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_DSIZE:
-            free(key->option_data);
-            break;
-        case RULE_OPTION_TYPE_FLOWBIT:
-            FlowBitsFree(key->option_data);
-            break;
-        case RULE_OPTION_TYPE_FTPBOUNCE:
-            /* Data is NULL, nothing to free */
-            break;
         case RULE_OPTION_TYPE_FILE_DATA:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_BASE64_DECODE:
-            free(key->option_data);
-            break;
-        case RULE_OPTION_TYPE_BASE64_DATA:
-            break;
-        case RULE_OPTION_TYPE_PKT_DATA:
-            break;
         case RULE_OPTION_TYPE_ICMP_CODE:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_ICMP_ID:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_ICMP_SEQ:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_ICMP_TYPE:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_IP_FRAGBITS:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_IP_FRAG_OFFSET:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_IP_ID:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_IP_OPTION:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_IP_PROTO:
-            free(key->option_data);
-            break;
-        case RULE_OPTION_TYPE_IP_SAME:
-            /* Data is NULL, nothing to free */
-            break;
         case RULE_OPTION_TYPE_IP_TOS:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_IS_DATA_AT:
-            free(key->option_data);
-            break;
-        case RULE_OPTION_TYPE_CONTENT:
-        case RULE_OPTION_TYPE_CONTENT_URI:
-            PatternMatchFree(key->option_data);
-            break;
-        case RULE_OPTION_TYPE_PCRE:
-            PcreFree(key->option_data);
-            break;
-#ifdef ENABLE_REACT
-        case RULE_OPTION_TYPE_REACT:
-            ReactFree(key->option_data);
-            break;
-#endif
 #ifdef ENABLE_RESPOND
         case RULE_OPTION_TYPE_RESPOND:
-            free(key->option_data);
-            break;
 #endif
         case RULE_OPTION_TYPE_RPC_CHECK:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_SESSION:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_TCP_ACK:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_TCP_FLAG:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_TCP_SEQ:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_TCP_WIN:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_TTL:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_URILEN:
+        case RULE_OPTION_TYPE_SCTP_CHUNK_TYPE:
+        case RULE_OPTION_TYPE_SCTP_CHUNK_FLAGS:
+        case RULE_OPTION_TYPE_SCTP_CHUNK_FIELD:
+        case RULE_OPTION_TYPE_SCTP_NUM_CHUNKS:
+        case RULE_OPTION_TYPE_SCTP_CAUSE_CODE:
             free(key->option_data);
             break;
+
+        case RULE_OPTION_TYPE_FTPBOUNCE:
+        case RULE_OPTION_TYPE_BASE64_DATA:
+        case RULE_OPTION_TYPE_PKT_DATA:
+        case RULE_OPTION_TYPE_IP_SAME:
+            break;
+
+        case RULE_OPTION_TYPE_BYTE_EXTRACT:
+            ByteExtractFree(key->option_data);
+            break;
+
+        case RULE_OPTION_TYPE_FLOWBIT:
+            FlowBitsFree(key->option_data);
+            break;
+
+        case RULE_OPTION_TYPE_PCRE:
+            PcreFree(key->option_data);
+            break;
+
+#ifdef ENABLE_REACT
+        case RULE_OPTION_TYPE_REACT:
+            ReactFree(key->option_data);
+            break;
+#endif
+
         case RULE_OPTION_TYPE_HDR_OPT_CHECK:
             break;
+
 #if defined(FEAT_FILE_INSPECT)
         case RULE_OPTION_TYPE_FILE_TYPE:
             FileTypeFree(key->option_data);
             break;
 #endif
+
         case RULE_OPTION_TYPE_PREPROCESSOR:
             PreprocessorRuleOptionsFreeFunc(key->option_data);
             break;
+
         case RULE_OPTION_TYPE_DYNAMIC:
             fpDynamicDataFree(key->option_data);
             break;
+
         case RULE_OPTION_TYPE_LEAF_NODE:
             break;
 #if defined(FEAT_OPEN_APPID)
@@ -807,6 +799,14 @@ char *option_type_str[] =
     "RULE_OPTION_TYPE_TCP_WIN",
     "RULE_OPTION_TYPE_TTL",
     "RULE_OPTION_TYPE_URILEN",
+    "RULE_OPTION_TYPE_SCTP_CHUNK_TYPE",
+    "RULE_OPTION_TYPE_SCTP_CHUNK_FLAGS",
+    "RULE_OPTION_TYPE_SCTP_CHUNK_FIELD",
+    "RULE_OPTION_TYPE_SCTP_NUM_CHUNKS",
+    "RULE_OPTION_TYPE_SCTP_CAUSE_CODE",
+    "RULE_OPTION_TYPE_HDR_OPT_CHECK",
+    "RULE_OPTION_TYPE_PREPROCESSOR",
+    "RULE_OPTION_TYPE_DYNAMIC",
     "RULE_OPTION_TYPE_HDR_OPT_CHECK",
     "RULE_OPTION_TYPE_PREPROCESSOR",
     "RULE_OPTION_TYPE_DYNAMIC"
@@ -1146,6 +1146,11 @@ int detection_option_node_evaluate(detection_option_tree_node_t *node, detection
             case RULE_OPTION_TYPE_TCP_WIN:
             case RULE_OPTION_TYPE_TTL:
             case RULE_OPTION_TYPE_URILEN:
+            case RULE_OPTION_TYPE_SCTP_CHUNK_TYPE:
+            case RULE_OPTION_TYPE_SCTP_CHUNK_FLAGS:
+            case RULE_OPTION_TYPE_SCTP_CHUNK_FIELD:
+            case RULE_OPTION_TYPE_SCTP_NUM_CHUNKS:
+            case RULE_OPTION_TYPE_SCTP_CAUSE_CODE:
             case RULE_OPTION_TYPE_HDR_OPT_CHECK:
 #if defined(FEAT_FILE_INSPECT)
             case RULE_OPTION_TYPE_FILE_TYPE:
diff --git a/src/detection-plugins/sp_clientserver.c b/src/detection-plugins/sp_clientserver.c
index b3074ff..47b03be 100644
--- a/src/detection-plugins/sp_clientserver.c
+++ b/src/detection-plugins/sp_clientserver.c
@@ -236,6 +236,15 @@ void FlowInit(struct _SnortConfig *sc, char *data, OptTreeNode *otn, int protoco
         }
     }
 
+    if(protocol == IPPROTO_SCTP)
+    {
+        if (!stream_api || (stream_api->version != STREAM_API_VERSION5))
+        {
+            FatalError("%s(%d): Cannot check flow connection "
+                   "for SCTP traffic\n", file_name, file_line);
+        }
+    }
+
     if (protocol == IPPROTO_ICMP)
     {
         if ((csd->only_reassembled != ONLY_FRAG) && (csd->ignore_reassembled != IGNORE_FRAG))
diff --git a/src/detection-plugins/sp_sctp_cause_code.c b/src/detection-plugins/sp_sctp_cause_code.c
new file mode 100644
index 0000000..797d0c4
--- /dev/null
+++ b/src/detection-plugins/sp_sctp_cause_code.c
@@ -0,0 +1,340 @@
+/*
+** Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+** Author: Joshua Kinard
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License Version 2 as
+** published by the Free Software Foundation.  You may not use, modify or
+** distribute this program under any other version of the GNU General
+** Public License.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "sf_types.h"
+#include "rules.h"
+#include "treenodes.h"
+#include "decode.h"
+#include "plugbase.h"
+#include "parser.h"
+#include "util.h"
+#include "snort_debug.h"
+#include "plugin_enum.h"
+#include "compiler.h"
+#include "sp_sctp_cause_code.h"
+#include "sp_sctp_chunk_type.h"
+
+#include "snort.h"
+#include "profiler.h"
+#ifdef PERF_PROFILING
+PreprocStats sctpCauseCodePerfStats;
+extern PreprocStats ruleOTNEvalPerfStats;
+#endif
+
+#include "sfhashfcn.h"
+#include "detection_options.h"
+
+
+/*
+ * Quick documentation:
+ *   sctp_cause_code:<VALUE>;
+ *   (Requires sctp_chunk_type specified beforehand)
+ *
+ * Ex:
+ *   sctp_chunk_type:ABORT; sctp_cause_code:12;
+ *   sctp_chunk_type:ERROR; sctp_cause_code:6;
+ */
+
+
+/**
+ * SctpCauseCodeHash - Hash function for sctp_cause_code.
+ * @data: pointer to SctpCauseCodeData.
+ *
+ * Returns a uint32_t representing the hash.
+ */
+uint32_t SctpCauseCodeHash(void *data)
+{
+    uint32_t a, b, c;
+    SctpCauseCodeData *sccd = (SctpCauseCodeData *)data;
+
+    a = sccd->cause_code;
+    b = RULE_OPTION_TYPE_SCTP_CAUSE_CODE;
+    c = 0;
+    final(a, b, c);
+
+    return c;
+}
+
+/**
+ * SctpCauseCodeCompare - Comparator function for sctp_cause_code.
+ * @l: pointer to SctpCauseCodeData (left side).
+ * @r: pointer to SctpCauseCodeData (right side).
+ *
+ * Returns the comparison result of the two data structures.
+ */
+int SctpCauseCodeCompare(void *l, void *r)
+{
+    SctpCauseCodeData *left = (SctpCauseCodeData *)l;
+    SctpCauseCodeData *right = (SctpCauseCodeData *)r;
+
+    if (!left)
+        return (!right ? DETECTION_OPTION_EQUAL : 
+                         DETECTION_OPTION_NOT_EQUAL);
+    
+    if (!right)
+        return (!left ? DETECTION_OPTION_EQUAL : 
+                        DETECTION_OPTION_NOT_EQUAL);
+
+    if (left->cause_code != right->cause_code)
+        return DETECTION_OPTION_NOT_EQUAL;
+
+    return DETECTION_OPTION_EQUAL;
+}
+
+/**
+ * SctpCauseCodeSetup - Setup function for sctp_cause_code.
+ *
+ * Links the 'sctp_cause_code' keyword to its initialization function.
+ * Returns void.
+ */
+void SctpCauseCodeSetup(void)
+{
+    /* Map the keyword to an initialization/processing function */
+    RegisterRuleOption("sctp_cause_code", SctpCauseCodeInit, NULL, OPT_TYPE_DETECTION, NULL);
+    
+#ifdef PERF_PROFILING
+    RegisterPreprocessorProfile("sctp_cause_code", &sctpCauseCodePerfStats, 3, &ruleOTNEvalPerfStats);
+#endif
+
+    DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Plugin: SctpCauseCode Initialized\n"););
+}
+
+/**
+ * SctpCauseCodeInit - Initialization function for sctp_cause_code.
+ * @sc: pointer to _SnortConfig structure.
+ * @data: pointer to SctpCauseCodeData (as char *).
+ * @otn: pointer to OptTreeNode structure.
+ * @protocol: integer value representing the transport-layer protocol.
+ *
+ * Attaches the rule option data to the appropriate data structure and links
+ * in the evaluator function to the function pointer list.  Returns void.
+ */
+void SctpCauseCodeInit(struct _SnortConfig *sc, char *data, OptTreeNode *otn, int protocol)
+{
+    OptFpList *fpl;
+    SctpCauseCodeData *sccd;
+    void *sccd_dup;
+
+    /* This keyword only applies to the SCTP Protocol. */
+    if (protocol != IPPROTO_SCTP)
+        FatalError("Error at %s:%d: 'sctp_cause_code' can only be used with "
+                   "the SCTP protocol.\n", file_name, file_line);
+
+    /* This keyword requires 'sctp_chunk_type' to be used first. */
+    if (otn->ds_list[PLUGIN_SCTP_CHUNK_TYPE] == NULL)
+        FatalError("%s(%d): 'sctp_chunk_type' must be used before "
+                   "'sctp_cause_code'.", file_name, file_line);
+
+    /* Check for multiple uses of this keyword in a rule. */
+    if (otn->ds_list[PLUGIN_SCTP_CAUSE_CODE])
+        FatalError("%s(%d): 'sctp_cause_code' may only be used once per "
+                   "rule.\n", file_name, file_line);
+
+    /* Allocate the data structure and attach it to the otn->ds_list. */
+    otn->ds_list[PLUGIN_SCTP_CAUSE_CODE] = (SctpCauseCodeData *)
+            SnortAlloc(sizeof(SctpCauseCodeData));
+
+    /* Parse the keyword's parameters and configure the data structure. */
+    SctpCauseCodeParse(data, otn);
+    sccd = otn->ds_list[PLUGIN_SCTP_CAUSE_CODE];
+
+    /* Check for a duplicate entry in the otn. */
+    if (add_detection_option(sc, RULE_OPTION_TYPE_SCTP_CAUSE_CODE,
+                             (void *)sccd, &sccd_dup) == DETECTION_OPTION_EQUAL)
+    {
+        otn->ds_list[PLUGIN_SCTP_CAUSE_CODE] = (SctpCauseCodeData *)sccd_dup;
+        free(sccd);
+    }
+
+    /*
+     * Finally, attach the option's detection function to the rule's detection
+     * function pointer list
+     */
+    fpl = AddOptFuncToList(SctpCauseCodeEval, otn);
+    fpl->type = RULE_OPTION_TYPE_SCTP_CAUSE_CODE;
+    fpl->context = otn->ds_list[PLUGIN_SCTP_CAUSE_CODE];
+}
+
+/**
+ * SctpCauseCodeParse - Parsing function for sctp_cause_code.
+ * @data: pointer to SctpCauseCodeData (as char *).
+ * @otn: pointer to OptTreeNode structure.
+ *
+ * Parses the rule option's parameter list and fills in the SctpCauseCodeData
+ * structure.  Returns void.
+ */
+void SctpCauseCodeParse(char *data, OptTreeNode *otn)
+{
+    char *end;
+    uint16_t val = 0;
+    SctpChunkTypeData *sctd;
+    SctpCauseCodeData *sccd;
+
+    /* Get both the chunk type and cause code data sets. */
+    sctd = otn->ds_list[PLUGIN_SCTP_CHUNK_TYPE];
+    sccd = otn->ds_list[PLUGIN_SCTP_CAUSE_CODE];
+
+    if ((sctd->parse_count == 1) || (sctd->parse_count == 2))
+    {
+        if (sctd->abort || sctd->error)
+        {
+            val = strtoul(data, &end, 10);
+            if ((val >= 0) && (val <= UINT16_MAX))
+                sccd->cause_code = (uint16_t)val;
+            else
+                FatalError("Error at %s:%d: Invalid 'sctp_num_chunks' "
+                           "argument.\n", file_name, file_line);
+        }
+        else
+            FatalError("Error at %s:%d: When using 'sctp_cause_code', the "
+                       "'sctp_chunk_type' keyword can only contain 'ABORT' "
+                       "or 'ERROR'.\n", file_name, file_line);
+    }
+    else
+        FatalError("Error at %s:%d: The number of specified chunks to "
+                   "'sctp_chunk_type' exceeds the number of chunk types in "
+                   "SCTP that support cause codes.\n", file_name, file_line);
+
+    return;
+}
+
+/**
+ * SctpCauseCodeEval - Evaluation function for sctp_cause_code.
+ * @data: pointer to SctpCauseCodeData (as char *).
+ * @p: pointer to Packet structure.
+ *
+ * Evaluates the rule option against the packet pointed to by 'p'.  Returns
+ * either DETECTION_OPTION_MATCH or DETECTION_OPTION_NO_MATCH.
+ */
+int SctpCauseCodeEval(void *data, Packet *p)
+{
+    SctpCauseCodeData *sccd = (SctpCauseCodeData *)data;
+    int rval = DETECTION_OPTION_NO_MATCH;
+    PROFILE_VARS;
+
+    if(!p->sctph || unlikely(!sccd))
+        return rval;
+
+    PREPROC_PROFILE_START(sctpCauseCodePerfStats);
+
+    SctpChunk *c = (SctpChunk *)p->sctph->chunks;
+    uint32_t remaining_len = p->dsize;
+
+    /*
+     * Loop through the chunks in the packet and for each DATA chunk, check
+     * check the pp_id field against the supplied value for a match.
+     */
+    while (likely(c != NULL) && 
+           (remaining_len != 0) && 
+           (rval == DETECTION_OPTION_NO_MATCH))
+    {
+        uint16_t chunk_length = SCTP_ADD_PADDING(ntohs(c->length));
+        uint16_t remaining_chunk_len = chunk_length;
+		SctpCause *cause = NULL;
+        remaining_len -= chunk_length;
+        
+        /*
+         * Although we have already validated which chunks we want to check
+         * via 'sctp_chunk_type', we still have to check the chunk types
+         * here because of chunk bundling.
+         */
+        switch (c->type)
+        {
+            case SCTP_ABORT_C:
+                cause = (SctpCause *)c->abort.causes;
+                remaining_chunk_len -= SCTP_ABORT_C_LEN;
+                break;
+
+            case SCTP_ERROR_C:
+                cause = (SctpCause *)c->error.causes;
+                remaining_chunk_len -= SCTP_ERROR_C_LEN;
+                break;
+
+            case SCTP_DATA_C:
+            case SCTP_INIT_C:
+            case SCTP_INIT_ACK_C:
+            case SCTP_SACK_C:
+            case SCTP_HEARTBEAT_C:
+            case SCTP_HEARTBEAT_ACK_C:
+            case SCTP_SHUTDOWN_C:
+            case SCTP_SHUTDOWN_ACK_C:
+            case SCTP_COOKIE_ECHO_C:
+            case SCTP_COOKIE_ACK_C:
+            case SCTP_ECNE_C:
+            case SCTP_CWR_C:
+            case SCTP_SHUTDOWN_COMPLETE_C:
+            case SCTP_AUTH_C:
+            case SCTP_NR_SACK_C:
+            case SCTP_ASCONF_ACK_C:
+            case SCTP_PKTDROP_C:
+            case SCTP_RECONFIG_C:
+            case SCTP_PAD_C:
+            case SCTP_FORWARD_TSN_C:
+            case SCTP_ASCONF_C:
+                break;
+
+            default:
+                DEBUG_WRAP(DebugMessage(
+                    DEBUG_PLUGIN, "Plugin (sctp_cause_code): Encountered "
+                                  "an unknown SCTP Chunk Type 0x%2x!\n",
+                                  c->type));
+                break;
+        }
+
+        /*
+         * If we encountered either an ABORT chunk or ERROR chunk, then
+         * cause_data might not be NULL and the below loop, which mimics the
+         * chunk-counting loop in src/decode.c::DecodeSCTP(), will walk the
+         * causes and look for a matching cause code.
+         *
+         * Note that ABORT can have zero or more causes and ERROR will have
+         * at least one cause.
+         */
+        while (likely(cause != NULL) &&
+               (remaining_chunk_len != 0) &&
+               (rval == DETECTION_OPTION_NO_MATCH))
+        {
+            uint16_t cause_len = SCTP_ADD_PADDING(ntohs(cause->length));
+            remaining_chunk_len -= cause_len;
+            
+            if (ntohs(cause->code) == sccd->cause_code)
+                rval = DETECTION_OPTION_MATCH;
+
+            remaining_chunk_len = SctpGetNextCause(cause, remaining_chunk_len,
+                                                   cause_len);
+        }
+
+        remaining_len = SctpGetNextChunk(c, remaining_len, chunk_length);
+    }
+
+    /* If the test isn't successful, return DETECTION_OPTION_NO_MATCH */
+    PREPROC_PROFILE_END(sctpCauseCodePerfStats);
+    return rval;
+}
diff --git a/src/detection-plugins/sp_sctp_cause_code.h b/src/detection-plugins/sp_sctp_cause_code.h
new file mode 100644
index 0000000..0bf634c
--- /dev/null
+++ b/src/detection-plugins/sp_sctp_cause_code.h
@@ -0,0 +1,39 @@
+/*
+** Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+** Author: Joshua Kinard
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License Version 2 as
+** published by the Free Software Foundation.  You may not use, modify or
+** distribute this program under any other version of the GNU General
+** Public License.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+/* $Id$ */
+#ifndef __SP_SCTP_CAUSE_CODE_H__
+#define __SP_SCTP_CAUSE_CODE_H__
+
+/* SctpCauseCode data structure. */
+typedef struct _SctpCauseCodeData
+{
+    uint16_t cause_code;
+} SctpCauseCodeData;
+
+/* Prototypes. */
+uint32_t SctpCauseCodeHash(void *);
+int SctpCauseCodeCompare(void *, void *);
+void SctpCauseCodeSetup(void);
+void SctpCauseCodeInit(struct _SnortConfig *, char *, OptTreeNode *, int);
+void SctpCauseCodeParse(char *, OptTreeNode *);
+int SctpCauseCodeEval(void *, Packet *);
+
+#endif  /* __SP_SCTP_CAUSE_CODE_H__ */
diff --git a/src/detection-plugins/sp_sctp_chunk_field.c b/src/detection-plugins/sp_sctp_chunk_field.c
new file mode 100644
index 0000000..6955dca
--- /dev/null
+++ b/src/detection-plugins/sp_sctp_chunk_field.c
@@ -0,0 +1,681 @@
+/*
+** Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+** Author: Joshua Kinard
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License Version 2 as
+** published by the Free Software Foundation.  You may not use, modify or
+** distribute this program under any other version of the GNU General
+** Public License.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "sf_types.h"
+#include "rules.h"
+#include "treenodes.h"
+#include "decode.h"
+#include "plugbase.h"
+#include "parser.h"
+#include "util.h"
+#include "snort_debug.h"
+#include "plugin_enum.h"
+#include "compiler.h"
+#include "sp_sctp_chunk_field.h"
+
+#include "snort.h"
+#include "profiler.h"
+#ifdef PERF_PROFILING
+PreprocStats sctpChunkFieldPerfStats;
+extern PreprocStats ruleOTNEvalPerfStats;
+#endif
+
+#include "sfhashfcn.h"
+#include "detection_options.h"
+
+
+/*
+ * Quick documentation:
+ *   sctp_chunk_field:<CHUNK_TYPE>,<FIELD_NAME>,[<|>]<NUMBER>;
+ *   sctp_chunk_field:<CHUNK_TYPE>,<FIELD_NAME>,<MIN><><MAX>;
+ *
+ * Ex:
+ *   sctp_chunk_field:DATA,tsn,42;
+ *   sctp_chunk_field:FORWARD-TSN,new_tsn,>31337;
+ *   sctp_chunk_field:INIT-ACK,num_out_streams,10<>20;
+ *
+ * Available fields:
+ *   DATA                  INIT                       INIT-ACK
+ *       "tsn"                 "initiate_tag"             "initiate_tag"
+ *       "stream_id"           "a_rwnd"                   "a_rwnd"
+ *       "stream_seq"          "num_out_streams"          "num_out_streams"
+ *       "payload_id"          "num_in_streams"           "num_in_streams"
+ *                             "tsn"                      "tsn"
+ *
+ *   SACK                  SHUTDOWN                   ECNE
+ *       "cuml_tsn_ack"        "tsn_ack"                  "lowest_tsn"
+ *       "a_rwnd"
+ *       "num_gap_acks"
+ *       "num_dupe_tsns"
+ *
+ *   CWR                   AUTH                       ASCONF-ACK
+ *       "lowest_tsn"          "skey_id"                  "seq"
+ *                             "hmac_id"
+ *
+ *   PKTDROP               FORWARD-TSN                ASCONF
+ *       "lbmr"              "new_tsn"                "seq"
+ *       "queue_size"
+ *       "trunc_length"
+ *       "reserved"
+ */
+
+/**
+ * SctpChunkFieldHash - Hash function for sctp_chunk_field.
+ * @data: pointer to SctpChunkFieldData.
+ *
+ * Returns a uint32_t representing the hash.
+ */
+uint32_t SctpChunkFieldHash(void *data)
+{
+    uint32_t a, b, c;
+    SctpChunkFieldData *scfd = (SctpChunkFieldData *)data;
+
+    a = scfd->field;
+    b = scfd->operator;
+    c = scfd->value_min;
+    mix(a, b, c);
+    a += scfd->value_max;
+    b += RULE_OPTION_TYPE_SCTP_CHUNK_FIELD;
+    final(a, b, c);
+
+    return c;
+}
+
+/**
+ * SctpChunkFieldCompare - Comparator function for sctp_chunk_field.
+ * @l: pointer to SctpChunkFieldData (left side).
+ * @r: pointer to SctpChunkFieldData (right side).
+ *
+ * Returns the comparison result of the two data structures.
+ */
+int SctpChunkFieldCompare(void *l, void *r)
+{
+    SctpChunkFieldData *left = (SctpChunkFieldData *)l;
+    SctpChunkFieldData *right = (SctpChunkFieldData *)r;
+
+    if (!left)
+        return (!right ? DETECTION_OPTION_EQUAL : 
+                         DETECTION_OPTION_NOT_EQUAL);
+    
+    if (!right)
+        return (!left ? DETECTION_OPTION_EQUAL : 
+                        DETECTION_OPTION_NOT_EQUAL);
+
+    if (left->field != right->field)         return DETECTION_OPTION_NOT_EQUAL;
+    if (left->operator != right->operator)   return DETECTION_OPTION_NOT_EQUAL;
+    if (left->value_min != right->value_min) return DETECTION_OPTION_NOT_EQUAL;
+    if (left->value_max != right->value_max) return DETECTION_OPTION_NOT_EQUAL;
+
+    return DETECTION_OPTION_EQUAL;
+}
+
+/**
+ * SctpChunkFieldSetup - Setup function for sctp_chunk_field.
+ *
+ * Links the 'sctp_chunk_field' keyword to its initialization function.
+ * Returns void.
+ */
+void SctpChunkFieldSetup(void)
+{
+    /* Map the keyword to an initialization/processing function */
+    RegisterRuleOption("sctp_chunk_field", SctpChunkFieldInit, NULL, OPT_TYPE_DETECTION, NULL);
+    
+#ifdef PERF_PROFILING
+    RegisterPreprocessorProfile("sctp_chunk_field", &sctpChunkFieldPerfStats, 3, &ruleOTNEvalPerfStats);
+#endif
+
+    DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Plugin: SctpChunkField Initialized\n"););
+}
+
+/**
+ * SctpChunkFieldInit - Initialization function for sctp_chunk_field.
+ * @sc: pointer to _SnortConfig structure.
+ * @data: pointer to SctpChunkFieldData (as char *).
+ * @otn: pointer to OptTreeNode structure.
+ * @protocol: integer value representing the transport-layer protocol.
+ *
+ * Attaches the rule option data to the appropriate data structure and links
+ * in the evaluator function to the function pointer list.  Returns void.
+ */
+void SctpChunkFieldInit(struct _SnortConfig *sc, char *data, OptTreeNode *otn, int protocol)
+{
+    OptFpList *fpl;
+    SctpChunkFieldData *scfd;
+    void *scfd_dup;
+
+    /* This keyword only applies to the SCTP Protocol. */
+    if (protocol != IPPROTO_SCTP)
+        FatalError("Error at %s:%d: 'sctp_chunk_field' can only be used with "
+                   "the SCTP protocol.\n", file_name, file_line);
+
+    /* Allocate the data structure and attach it to the otn->ds_list. */
+    scfd = (SctpChunkFieldData *)SnortAlloc(sizeof(SctpChunkFieldData));
+
+    /* Parse the keyword's parameters and configure the data structure. */
+    SctpChunkFieldParse(data, scfd, otn);
+
+    /* Check for a duplicate entry in the otn. */
+    if (add_detection_option(sc, RULE_OPTION_TYPE_SCTP_CHUNK_FIELD,
+                             (void *)scfd, &scfd_dup) == DETECTION_OPTION_EQUAL)
+    {
+        free(scfd);
+        scfd = (SctpChunkFieldData *)scfd_dup;
+    }
+
+    /*
+     * Finally, attach the option's detection function to the rule's detection
+     * function pointer list
+     */
+    fpl = AddOptFuncToList(SctpChunkFieldEval, otn);
+    fpl->type = RULE_OPTION_TYPE_SCTP_CHUNK_FIELD;
+    fpl->context = (void *)scfd;
+}
+
+/**
+ * SctpChunkFieldParse - Parsing function for sctp_chunk_field.
+ * @data: pointer to SctpChunkFieldData (as char *).
+ * @otn: pointer to OptTreeNode structure.
+ *
+ * Parses the rule option's parameter list and fills in the SctpChunkFieldData
+ * structure.  Returns void.
+ */
+void SctpChunkFieldParse(char *data, SctpChunkFieldData *scfd, OptTreeNode *otn)
+{
+    char *str, *strtmp, *tok, *end;
+    char *chunk;
+    uint32_t parse_status = SCTP_CHUNK_FIELD_PARSE_NONE;
+
+    /* Covert to a string and make a copy of the pointer. */
+    str = SnortStrdup(data);
+    strtmp = str;
+
+    /* Skip any leading whitespace. */
+    while(isspace((int)*strtmp)) strtmp++;
+
+    /* Parse. */
+    tok = strtok(strtmp, ",");
+    while (tok)
+    {
+        /* Continue to skip any whitespace. */
+        while(isspace((int)*tok)) tok++;
+
+        /* Parse the first field, which should be the chunk type. */
+        if (parse_status == SCTP_CHUNK_FIELD_PARSE_NONE)
+        {
+            /*
+             * Parse the first operand: the chunk type.  This is left-shifted
+             * by 8 to store it in the upper-half of the scfd->field variable.
+             * The lower 8 bits will be used for storing the particular field
+             * ID that we wish to check.
+             */
+            if      (!strcasecmp("DATA", tok))              scfd->field = (SCTP_DATA_C << 8);
+            else if (!strcasecmp("INIT", tok))              scfd->field = (SCTP_INIT_C << 8);
+            else if (!strcasecmp("INIT-ACK", tok))          scfd->field = (SCTP_INIT_ACK_C << 8);
+            else if (!strcasecmp("SACK", tok))              scfd->field = (SCTP_SACK_C << 8);
+            else if (!strcasecmp("HEARTBEAT", tok))         scfd->field = (SCTP_HEARTBEAT_C << 8);
+            else if (!strcasecmp("HEARTBEAT-ACK", tok))     scfd->field = (SCTP_HEARTBEAT_ACK_C << 8);
+            else if (!strcasecmp("ABORT", tok))             scfd->field = (SCTP_ABORT_C << 8);
+            else if (!strcasecmp("SHUTDOWN", tok))          scfd->field = (SCTP_SHUTDOWN_C << 8);
+            else if (!strcasecmp("SHUTDOWN-ACK", tok))      scfd->field = (SCTP_SHUTDOWN_ACK_C << 8);
+            else if (!strcasecmp("ERROR", tok))             scfd->field = (SCTP_ERROR_C << 8);
+            else if (!strcasecmp("COOKIE-ECHO", tok))       scfd->field = (SCTP_COOKIE_ECHO_C << 8);
+            else if (!strcasecmp("COOKIE-ACK", tok))        scfd->field = (SCTP_COOKIE_ACK_C << 8);
+            else if (!strcasecmp("ECNE", tok))              scfd->field = (SCTP_ECNE_C << 8);
+            else if (!strcasecmp("CWR", tok))               scfd->field = (SCTP_CWR_C << 8);
+            else if (!strcasecmp("SHUTDOWN-COMPLETE", tok)) scfd->field = (SCTP_SHUTDOWN_COMPLETE_C << 8);
+            else if (!strcasecmp("AUTH", tok))              scfd->field = (SCTP_AUTH_C << 8);
+            else if (!strcasecmp("NR-SACK", tok))           scfd->field = (SCTP_NR_SACK_C << 8);
+            else if (!strcasecmp("ASCONF-ACK", tok))        scfd->field = (SCTP_ASCONF_ACK_C << 8);
+            else if (!strcasecmp("PKTDROP", tok))           scfd->field = (SCTP_PKTDROP_C << 8);
+            else if (!strcasecmp("RE-CONFIG", tok))         scfd->field = (SCTP_RECONFIG_C << 8);
+            else if (!strcasecmp("PAD", tok))               scfd->field = (SCTP_PAD_C << 8);
+            else if (!strcasecmp("FORWARD-TSN", tok))       scfd->field = (SCTP_FORWARD_TSN_C << 8);
+            else if (!strcasecmp("ASCONF", tok))            scfd->field = (SCTP_ASCONF_C << 8);
+            else
+                FatalError("Error at %s:%d: %s is not a recognized argument to "
+                           "'sctp_chunk_field'.\n", file_name, file_line, tok);
+
+            /*
+             * Now check the scfd->field value for chunks that don't have any
+             * numeric fields to check (yet).  We do it this way because one day,
+             * they may get a numeric field of some kind.
+             */
+            switch (scfd->field >> 8)
+            {
+                case SCTP_HEARTBEAT_C:
+                case SCTP_HEARTBEAT_ACK_C:
+                case SCTP_ABORT_C:
+                case SCTP_SHUTDOWN_ACK_C:
+                case SCTP_ERROR_C:
+                case SCTP_COOKIE_ECHO_C:
+                case SCTP_COOKIE_ACK_C:
+                case SCTP_SHUTDOWN_COMPLETE_C:
+                case SCTP_RECONFIG_C:
+                case SCTP_PAD_C:
+                    FatalError("Error at %s:%d: %s does not have any numeric "
+                               "fields to check for.", file_name, file_line, tok);
+                    break;
+            }
+            
+            parse_status = SCTP_CHUNK_FIELD_PARSE_TYPE;
+            chunk = tok;
+            tok = strtok(NULL, ",");
+        }
+        
+        /*
+         * If the type is parsed, move on to parse the particular field to
+         * check in the selected chunk type.
+         */
+        else if (parse_status == SCTP_CHUNK_FIELD_PARSE_TYPE)
+        {
+            switch (scfd->field >> 8)
+            {
+                case SCTP_DATA_C:
+                    if      (!strcasecmp("tsn", tok))             scfd->field += SCTP_DATA_TSN;
+                    else if (!strcasecmp("stream_id", tok))       scfd->field += SCTP_DATA_STREAM_ID;
+                    else if (!strcasecmp("stream_seq", tok))      scfd->field += SCTP_DATA_STREAM_SEQ;
+                    else if (!strcasecmp("payload_id", tok))      scfd->field += SCTP_DATA_PAYLOAD_ID;
+                    else goto field_error;
+                    break;
+
+                case SCTP_INIT_C:
+                    if      (!strcasecmp("initiate_tag", tok))    scfd->field += SCTP_INIT_INITIATE_TAG;
+                    else if (!strcasecmp("a_rwnd", tok))          scfd->field += SCTP_INIT_A_RWND;
+                    else if (!strcasecmp("num_out_streams", tok)) scfd->field += SCTP_INIT_NUM_OUT_STRMS;
+                    else if (!strcasecmp("num_in_streams", tok))  scfd->field += SCTP_INIT_NUM_IN_STRMS;
+                    else if (!strcasecmp("tsn", tok))             scfd->field += SCTP_INIT_TSN;
+                    else goto field_error;
+                    break;
+
+                case SCTP_INIT_ACK_C:
+                    if      (!strcasecmp("initiate_tag", tok))    scfd->field += SCTP_INIT_ACK_INITIATE_TAG;
+                    else if (!strcasecmp("a_rwnd", tok))          scfd->field += SCTP_INIT_ACK_A_RWND;
+                    else if (!strcasecmp("num_out_streams", tok)) scfd->field += SCTP_INIT_ACK_NUM_OUT_STRMS;
+                    else if (!strcasecmp("num_in_streams", tok))  scfd->field += SCTP_INIT_ACK_NUM_IN_STRMS;
+                    else if (!strcasecmp("tsn", tok))             scfd->field += SCTP_INIT_ACK_TSN;
+                    else goto field_error;
+                    break;
+
+                case SCTP_SACK_C:
+                    if      (!strcasecmp("cuml_tsn_ack", tok))    scfd->field += SCTP_SACK_CUML_TSN_ACK;
+                    else if (!strcasecmp("a_rwnd", tok))          scfd->field += SCTP_SACK_A_RWND;
+                    else if (!strcasecmp("num_gap_acks", tok))    scfd->field += SCTP_SACK_NUM_GAP_ACKS;
+                    else if (!strcasecmp("num_dupe_tsns", tok))   scfd->field += SCTP_SACK_NUM_DUPE_TSNS;
+                    else goto field_error;
+                    break;
+
+                case SCTP_SHUTDOWN_C:
+                    if      (!strcasecmp("tsn_ack", tok))         scfd->field += SCTP_SHUTDOWN_TSN_ACK;
+                    else goto field_error;
+                    break;
+
+                case SCTP_ECNE_C:
+                    if      (!strcasecmp("lowest_tsn", tok))      scfd->field += SCTP_ECNE_LOWEST_TSN;
+                    else goto field_error;
+                    break;
+
+                case SCTP_CWR_C:
+                    if      (!strcasecmp("lowest_tsn", tok))      scfd->field += SCTP_CWR_LOWEST_TSN;
+                    else goto field_error;
+                    break;
+
+                case SCTP_AUTH_C:
+                    if      (!strcasecmp("skey_id", tok))         scfd->field += SCTP_AUTH_SKEY_ID;
+                    else if (!strcasecmp("hmac_id", tok))         scfd->field += SCTP_AUTH_HMAC_ID;
+                    else goto field_error;
+                    break;
+
+                case SCTP_NR_SACK_C:
+                    if      (!strcasecmp("cuml_tsn_ack", tok))    scfd->field += SCTP_NR_SACK_CUML_TSN_ACK;
+                    else if (!strcasecmp("a_rwnd", tok))          scfd->field += SCTP_NR_SACK_A_RWND;
+                    else if (!strcasecmp("num_r_gap_acks", tok))  scfd->field += SCTP_NR_SACK_NUM_R_GAP_ACKS;
+                    else if (!strcasecmp("num_nr_gap_acks", tok)) scfd->field += SCTP_NR_SACK_NUM_NR_GAP_ACKS;
+                    else if (!strcasecmp("num_dupe_tsns", tok))   scfd->field += SCTP_NR_SACK_NUM_DUPE_TSNS;
+                    else goto field_error;
+                    break;
+
+                case SCTP_ASCONF_ACK_C:
+                    if      (!strcasecmp("seq", tok))             scfd->field += SCTP_ASCONF_ACK_SEQ;
+                    else goto field_error;
+                    break;
+
+                case SCTP_PKTDROP_C:
+                    if      (!strcasecmp("lbmr", tok))            scfd->field += SCTP_PKTDROP_LBMR;
+                    else if (!strcasecmp("queue_size", tok))      scfd->field += SCTP_PKTDROP_QUEUE_SIZE;
+                    else if (!strcasecmp("trunc_length", tok))    scfd->field += SCTP_PKTDROP_TRUNC_LENGTH;
+                    else if (!strcasecmp("reserved", tok))        scfd->field += SCTP_PKTDROP_RESERVED;
+                    else goto field_error;
+                    break;
+
+                case SCTP_FORWARD_TSN_C:
+                    if      (!strcasecmp("new_tsn", tok))         scfd->field += SCTP_FORWARD_TSN_NEW_TSN;
+                    else goto field_error;
+                    break;
+
+                case SCTP_ASCONF_C:
+                    if      (!strcasecmp("seq", tok))             scfd->field += SCTP_ASCONF_SEQ;
+                    else goto field_error;
+                    break;
+
+                default:
+                    FatalError("Error at %s:%d: Unknown chunk type 0x%2x or "
+                               "chunk does not have any numeric fields to "
+                               "check for.", file_name, file_line,
+                               (scfd->field >> 8));
+                    break;
+            }
+
+            parse_status = SCTP_CHUNK_FIELD_PARSE_FIELD;
+            tok = strtok(NULL, ",");
+        }
+
+        /*
+         * If the field is parsed, move on to parse the value.  The value can
+         * be a static value, or a ranged value (<, >, and <>).
+         */
+        else if (parse_status == SCTP_CHUNK_FIELD_PARSE_FIELD)
+        {
+            UNumericRange rng;
+            char *toktmp = tok;
+            uint32_t num = 0;
+            int rval = -3;
+
+            /* Parse out the operands of the range. */
+            rval = SnortStrToUNumRng(toktmp, "<>", &rng);
+            if (rval == SNORT_STR2RNG_SUCCESS)
+            {
+                scfd->value_min = rng.min;
+                scfd->value_max = rng.max;
+                scfd->operator = SCTP_CHUNK_FIELD_OP_RANGE;
+            }
+
+            /* Not a ranged value. */
+            else if (rval == SNORT_STR2RNG_NOTRNG)
+            {
+                if (*toktmp == '>')
+                {
+                    toktmp++;
+                    scfd->operator = SCTP_CHUNK_FIELD_OP_GT;
+                }
+                else if(*toktmp == '<')
+                {
+                    toktmp++;
+                    scfd->operator = SCTP_CHUNK_FIELD_OP_LT;
+                }
+                else
+                    scfd->operator = SCTP_CHUNK_FIELD_OP_EQUAL;
+            }
+
+            /* Error? */
+            else if (rval == SNORT_STR2RNG_ERROR)
+                goto value_error;
+
+            /* Parse the remainder and store it as the minimum value. */
+            if (scfd->operator != SCTP_CHUNK_FIELD_OP_RANGE)
+            {
+                num = strtoul(toktmp, &end, 10);
+                if (*end)
+                    goto value_error;
+
+                scfd->value_min = num;
+            }
+
+            parse_status = SCTP_CHUNK_FIELD_PARSE_VALUE;
+            tok = NULL;
+        }
+
+    }
+
+    return;
+
+field_error:
+    FatalError("Error %s:%d: The %s chunk does not have a field by the "
+                "name of %s!\n", file_name, file_line, chunk, tok);
+
+    return;
+    
+value_error:
+    FatalError("Error at %s:%d: Invalid value passed to "
+               "'sctp_chunk_field'!\n", file_name, file_line);
+
+    return;
+}
+
+/**
+ * SctpChunkFieldEval - Evaluation function for sctp_chunk_field.
+ * @data: pointer to SctpChunkFieldData (as char *).
+ * @p: pointer to Packet structure.
+ *
+ * Evaluates the rule option against the packet pointed to by 'p'.  Returns
+ * either DETECTION_OPTION_MATCH or DETECTION_OPTION_NO_MATCH.
+ */
+int SctpChunkFieldEval(void *data, Packet *p)
+{
+    SctpChunkFieldData *scfd = (SctpChunkFieldData *)data;
+    int rval = DETECTION_OPTION_NO_MATCH;
+    PROFILE_VARS;
+
+    if(!p->sctph || unlikely(!scfd))
+        return rval;
+
+    PREPROC_PROFILE_START(sctpChunkFieldPerfStats);
+
+    SctpChunk *c = (SctpChunk *)p->sctph->chunks;
+    uint32_t remaining_len = p->dsize;
+    uint32_t chunk_type = (scfd->field >> 8);
+    uint32_t field = (scfd->field & 0x00ff);
+    uint32_t val = 0;
+
+    /*
+     * Loop through the chunks in the packet and for each chunk, check
+     * check the specified field against the supplied value(s) for a match.
+     */
+    while (likely(c != NULL) &&
+           (remaining_len != 0) &&
+           (rval == DETECTION_OPTION_NO_MATCH))
+    {
+        uint16_t chunk_length = SCTP_ADD_PADDING(ntohs(c->length));
+        remaining_len -= chunk_length;
+        
+        /*
+         * Continue the while loop if the chunk type in the packet does
+         * not match the chunk type selected by the rule keyword.
+         */
+        if (c->type != chunk_type)
+            goto next_chunk;
+
+        /* Fetch the specific field value from the specified chunk. */
+        switch (c->type)
+        {
+            case SCTP_DATA_C:
+                switch (field)
+                {
+                    case SCTP_DATA_TSN:                val = ntohl(c->data.tsn);                           break;
+                    case SCTP_DATA_STREAM_ID:          val = (uint32_t)ntohs(c->data.stream_id);           break;
+                    case SCTP_DATA_STREAM_SEQ:         val = (uint32_t)ntohs(c->data.stream_seq);          break;
+                    case SCTP_DATA_PAYLOAD_ID:         val = ntohl(c->data.payload_id);                    break;
+                }
+                break;
+                
+            case SCTP_INIT_C:
+                switch (field)
+                {
+                    case SCTP_INIT_INITIATE_TAG:       val = ntohl(c->init.initiate_tag);                  break;
+                    case SCTP_INIT_A_RWND:             val = ntohl(c->init.a_rwnd);                        break;
+                    case SCTP_INIT_NUM_OUT_STRMS:      val = (uint32_t)ntohs(c->init.num_out_streams);     break;
+                    case SCTP_INIT_NUM_IN_STRMS:       val = (uint32_t)ntohs(c->init.num_in_streams);      break;
+                    case SCTP_INIT_TSN:                val = ntohl(c->init.tsn);                           break;
+                }
+                break;
+
+            case SCTP_INIT_ACK_C:
+                switch (field)
+                {
+                    case SCTP_INIT_ACK_INITIATE_TAG:   val = ntohl(c->init_ack.initiate_tag);              break;
+                    case SCTP_INIT_ACK_A_RWND:         val = ntohl(c->init_ack.a_rwnd);                    break;
+                    case SCTP_INIT_ACK_NUM_OUT_STRMS:  val = (uint32_t)ntohs(c->init_ack.num_out_streams); break;
+                    case SCTP_INIT_ACK_NUM_IN_STRMS:   val = (uint32_t)ntohs(c->init_ack.num_in_streams);  break;
+                    case SCTP_INIT_ACK_TSN:            val = ntohl(c->init_ack.tsn);                       break;
+                }
+                break;
+
+            case SCTP_SACK_C:
+                switch (field)
+                {
+                    case SCTP_SACK_CUML_TSN_ACK:       val = ntohl(c->sack.cuml_tsn_ack);                  break;
+                    case SCTP_SACK_A_RWND:             val = ntohl(c->sack.a_rwnd);                        break;
+                    case SCTP_SACK_NUM_GAP_ACKS:       val = (uint32_t)ntohs(c->sack.num_gap_acks);        break;
+                    case SCTP_SACK_NUM_DUPE_TSNS:      val = (uint32_t)ntohs(c->sack.num_dupe_tsns);       break;
+                }
+                break;
+
+            case SCTP_SHUTDOWN_C:
+                if (field == SCTP_SHUTDOWN_TSN_ACK)
+                    val = ntohl(c->shutdown.tsn_ack);
+                break;
+
+            case SCTP_ECNE_C:
+                if (field == SCTP_ECNE_LOWEST_TSN)
+                    val = ntohl(c->ecne.lowest_tsn);
+                break;
+
+            case SCTP_CWR_C:
+                if (field == SCTP_CWR_LOWEST_TSN)
+                    val = ntohl(c->cwr.lowest_tsn);
+                break;
+
+            case SCTP_AUTH_C:
+                switch (field)
+                {
+                    case SCTP_AUTH_SKEY_ID:            val = (uint32_t)ntohs(c->auth.skey_id);             break;
+                    case SCTP_AUTH_HMAC_ID:            val = (uint32_t)ntohs(c->auth.skey_id);             break;
+                }
+                break;
+
+            case SCTP_NR_SACK_C:
+                switch (field)
+                {
+                    case SCTP_NR_SACK_CUML_TSN_ACK:    val = ntohl(c->nr_sack.cuml_tsn_ack);               break;
+                    case SCTP_NR_SACK_A_RWND:          val = ntohl(c->nr_sack.a_rwnd);                     break;
+                    case SCTP_NR_SACK_NUM_R_GAP_ACKS:  val = (uint32_t)ntohs(c->nr_sack.num_r_gap_acks);   break;
+                    case SCTP_NR_SACK_NUM_NR_GAP_ACKS: val = (uint32_t)ntohs(c->nr_sack.num_nr_gap_acks);  break;
+                    case SCTP_NR_SACK_NUM_DUPE_TSNS:   val = (uint32_t)ntohs(c->nr_sack.num_dupe_tsns);    break;
+                }
+                break;
+
+            case SCTP_ASCONF_ACK_C:
+                if (field == SCTP_ASCONF_ACK_SEQ)
+                    val = ntohl(c->asconf_ack.seq);
+                break;
+
+            case SCTP_PKTDROP_C:
+                switch (field)
+                {
+                    case SCTP_PKTDROP_LBMR:            val = ntohl(c->pktdrop.lbmr);                       break;
+                    case SCTP_PKTDROP_QUEUE_SIZE:      val = ntohl(c->pktdrop.queue_size);                 break;
+                    case SCTP_PKTDROP_TRUNC_LENGTH:    val = (uint32_t)ntohs(c->pktdrop.trunc_length);     break;
+                    case SCTP_PKTDROP_RESERVED:        val = (uint32_t)ntohs(c->pktdrop.reserved);         break;
+                }
+                break;
+
+            case SCTP_FORWARD_TSN_C:
+                if (field == SCTP_FORWARD_TSN_NEW_TSN)
+                    val = ntohl(c->forward_tsn.new_tsn);
+                break;
+
+            case SCTP_ASCONF_C:
+                if (field == SCTP_ASCONF_SEQ)
+                    val = ntohl(c->asconf.seq);
+                break;
+
+            case SCTP_HEARTBEAT_C:
+            case SCTP_HEARTBEAT_ACK_C:
+            case SCTP_ABORT_C:
+            case SCTP_SHUTDOWN_ACK_C:
+            case SCTP_ERROR_C:
+            case SCTP_COOKIE_ECHO_C:
+            case SCTP_COOKIE_ACK_C:
+            case SCTP_SHUTDOWN_COMPLETE_C:
+            case SCTP_RECONFIG_C:
+            case SCTP_PAD_C:
+                DEBUG_WRAP(DebugMessage(
+                    DEBUG_PLUGIN, "Plugin (sctp_chunk_field): Chunk Type 0x%2x "
+                                  "does not have any numeric fields to compare "
+                                  "against.\n", c->type));
+                break;
+
+            default:
+                DEBUG_WRAP(DebugMessage(
+                    DEBUG_PLUGIN, "Plugin (sctp_chunk_field): Encountered "
+                                  "an unknown SCTP Chunk Type 0x%2x!\n",
+                                  c->type));
+                break;
+        }
+
+        /*
+         * Now compare the value from the field against the user-specified
+         * value, based on the set operator.
+         */
+        switch (scfd->operator)
+        {
+            case SCTP_CHUNK_FIELD_OP_EQUAL:
+                if (scfd->value_min == val)
+                    rval = DETECTION_OPTION_MATCH;
+                break;
+
+            case SCTP_CHUNK_FIELD_OP_LT:
+                if (scfd->value_min > val)
+                    rval = DETECTION_OPTION_MATCH;
+                break;
+
+            case SCTP_CHUNK_FIELD_OP_GT:
+                if (scfd->value_min < val)
+                    rval = DETECTION_OPTION_MATCH;
+                break;
+
+            case SCTP_CHUNK_FIELD_OP_RANGE:
+                if ((scfd->value_min <= val) &&
+                    (scfd->value_max >= val))
+                    rval = DETECTION_OPTION_MATCH;
+                break;
+
+            default:
+                break;
+        }
+        break;
+
+next_chunk:
+        remaining_len = SctpGetNextChunk(c, remaining_len, chunk_length);
+    }
+
+    /* If the test isn't successful, return DETECTION_OPTION_NO_MATCH */
+    PREPROC_PROFILE_END(sctpChunkFieldPerfStats);
+    return rval;
+}
diff --git a/src/detection-plugins/sp_sctp_chunk_field.h b/src/detection-plugins/sp_sctp_chunk_field.h
new file mode 100644
index 0000000..9ba2c1a
--- /dev/null
+++ b/src/detection-plugins/sp_sctp_chunk_field.h
@@ -0,0 +1,129 @@
+/*
+** Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+** Author: Joshua Kinard
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License Version 2 as
+** published by the Free Software Foundation.  You may not use, modify or
+** distribute this program under any other version of the GNU General
+** Public License.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+/* $Id$ */
+#ifndef __SP_SCTP_CHUNK_FIELD_H__
+#define __SP_SCTP_CHUNK_FIELD_H__
+
+/* These are used to know what phase of the parsing that we are on. */
+#define SCTP_CHUNK_FIELD_PARSE_NONE         0x00
+#define SCTP_CHUNK_FIELD_PARSE_TYPE         0x01
+#define SCTP_CHUNK_FIELD_PARSE_FIELD        0x02
+#define SCTP_CHUNK_FIELD_PARSE_VALUE        0x03
+
+/* Operator definitions for sctp_payload_id. */
+#define SCTP_CHUNK_FIELD_OP_EQUAL           0x01
+#define SCTP_CHUNK_FIELD_OP_LT              0x02
+#define SCTP_CHUNK_FIELD_OP_GT              0x03
+#define SCTP_CHUNK_FIELD_OP_RANGE           0x04
+
+/* Field definitions for DATA. */
+#define SCTP_DATA_TSN                       0x01
+#define SCTP_DATA_STREAM_ID                 0x02
+#define SCTP_DATA_STREAM_SEQ                0x03
+#define SCTP_DATA_PAYLOAD_ID                0x04
+
+/* Field definitions for INIT. */
+#define SCTP_INIT_INITIATE_TAG              0x01
+#define SCTP_INIT_A_RWND                    0x02
+#define SCTP_INIT_NUM_OUT_STRMS             0x03
+#define SCTP_INIT_NUM_IN_STRMS              0x04
+#define SCTP_INIT_TSN                       0x05
+
+/* Field definitions for INIT-ACK. */
+#define SCTP_INIT_ACK_INITIATE_TAG          0x01
+#define SCTP_INIT_ACK_A_RWND                0x02
+#define SCTP_INIT_ACK_NUM_OUT_STRMS         0x03
+#define SCTP_INIT_ACK_NUM_IN_STRMS          0x04
+#define SCTP_INIT_ACK_TSN                   0x05
+
+/* Field definitions for SACK. */
+#define SCTP_SACK_CUML_TSN_ACK              0x01
+#define SCTP_SACK_A_RWND                    0x02
+#define SCTP_SACK_NUM_GAP_ACKS              0x03
+#define SCTP_SACK_NUM_DUPE_TSNS             0x04
+
+/* No field definitions for HEARTBEAT. */
+/* No field definitions for HEARTBEAT-ACK. */
+/* No field definitions for ABORT. */
+
+/* Field definitions for SHUTDOWN. */
+#define SCTP_SHUTDOWN_TSN_ACK               0x01
+
+/* No field definitions for SHUTDOWN-ACK. */
+/* No field definitions for ERROR. */
+/* No field definitions for COOKIE-ECHO. */
+/* No field definitions for COOKIE-ACK. */
+
+/* Field definitions for ECNE. */
+#define SCTP_ECNE_LOWEST_TSN                0x01
+
+/* Field definitions for CWR. */
+#define SCTP_CWR_LOWEST_TSN                 0x01
+
+/* No field definitions for SHUTDOWN-COMPLETE. */
+
+/* Field definitions for AUTH. */
+#define SCTP_AUTH_SKEY_ID                   0x01
+#define SCTP_AUTH_HMAC_ID                   0x02
+
+/* Field definitions for NR-SACK. */
+#define SCTP_NR_SACK_CUML_TSN_ACK           0x01
+#define SCTP_NR_SACK_A_RWND                 0x02
+#define SCTP_NR_SACK_NUM_R_GAP_ACKS         0x03
+#define SCTP_NR_SACK_NUM_NR_GAP_ACKS        0x04
+#define SCTP_NR_SACK_NUM_DUPE_TSNS          0x05
+
+/* Field definitions for ASCONF-ACK. */
+#define SCTP_ASCONF_ACK_SEQ                 0x01
+
+/* Field definitions for PKTDROP. */
+#define SCTP_PKTDROP_LBMR                   0x01
+#define SCTP_PKTDROP_QUEUE_SIZE             0x02
+#define SCTP_PKTDROP_TRUNC_LENGTH           0x03
+#define SCTP_PKTDROP_RESERVED               0x04
+
+/* No field definitions for RE-CONFIG. */
+/* No field definitions for PAD. */
+
+/* Field definitions for FORWARD-TSN. */
+#define SCTP_FORWARD_TSN_NEW_TSN            0x01
+
+/* Field definitions for ASCONF. */
+#define SCTP_ASCONF_SEQ                     0x01
+
+/* SctpChunkField data structure. */
+typedef struct _SctpChunkFieldData
+{
+    uint16_t field;
+    uint8_t operator;
+    uint32_t value_min;
+    uint32_t value_max;
+} SctpChunkFieldData;
+
+/* Prototypes. */
+uint32_t SctpChunkFieldHash(void *);
+int SctpChunkFieldCompare(void *, void *);
+void SctpChunkFieldSetup(void);
+void SctpChunkFieldInit(struct _SnortConfig *, char *, OptTreeNode *, int);
+void SctpChunkFieldParse(char *, SctpChunkFieldData *, OptTreeNode *);
+int SctpChunkFieldEval(void *, Packet *);
+
+#endif  /* __SP_SCTP_CHUNK_FIELD_H__ */
diff --git a/src/detection-plugins/sp_sctp_chunk_flags.c b/src/detection-plugins/sp_sctp_chunk_flags.c
new file mode 100644
index 0000000..758da36
--- /dev/null
+++ b/src/detection-plugins/sp_sctp_chunk_flags.c
@@ -0,0 +1,481 @@
+/*
+** Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+** Author: Joshua Kinard
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License Version 2 as
+** published by the Free Software Foundation.  You may not use, modify or
+** distribute this program under any other version of the GNU General
+** Public License.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "sf_types.h"
+#include "rules.h"
+#include "treenodes.h"
+#include "decode.h"
+#include "plugbase.h"
+#include "parser.h"
+#include "util.h"
+#include "snort_debug.h"
+#include "plugin_enum.h"
+#include "compiler.h"
+#include "sp_sctp_chunk_flags.h"
+#include "sp_sctp_chunk_type.h"
+
+#include "snort.h"
+#include "profiler.h"
+#ifdef PERF_PROFILING
+PreprocStats sctpChunkFlagsPerfStats;
+extern PreprocStats ruleOTNEvalPerfStats;
+#endif
+
+#include "sfhashfcn.h"
+#include "detection_options.h"
+
+
+/*
+ * Quick documentation:
+ *   sctp_chunk_flags:<OP><FLAG_BITS>[,<FLAG_MASK>];
+ *   (Requires sctp_chunk_type specified beforehand)
+ *
+ * Ex:
+ *   sctp_chunk_type:DATA; sctp_chunk_flags:*IBE;
+ *   sctp_chunk_type:ABORT; sctp_chunk_flags:!T;
+ *   sctp_chunk_type:PKTDROP; sctp_chunk_flags:*TB;
+ */
+
+
+/**
+ * SctpChunkFlagsHash - Hash function for sctp_chunk_flags.
+ * @data: pointer to SctpChunkFlagsData.
+ *
+ * Returns a uint32_t representing the hash.
+ */
+uint32_t SctpChunkFlagsHash(void *data)
+{
+    uint32_t a, b, c;
+    SctpChunkFlagsData *scfd = (SctpChunkFlagsData *)data;
+
+    a = scfd->mode;
+    b = scfd->flags || (scfd->mask << 8);
+    c = RULE_OPTION_TYPE_SCTP_CHUNK_FLAGS;
+    final(a, b, c);
+
+    return c;
+}
+
+/**
+ * SctpChunkFlagsCompare - Comparator function for sctp_chunk_flags.
+ * @l: pointer to SctpChunkFlagsData (left side).
+ * @r: pointer to SctpChunkFlagsData (right side).
+ *
+ * Returns the comparison result of the two data structures.
+ */
+int SctpChunkFlagsCompare(void *l, void *r)
+{
+    SctpChunkFlagsData *left = (SctpChunkFlagsData *)l;
+    SctpChunkFlagsData *right = (SctpChunkFlagsData *)r;
+
+    if (!left)
+        return (!right ? DETECTION_OPTION_EQUAL : 
+                         DETECTION_OPTION_NOT_EQUAL);
+    
+    if (!right)
+        return (!left ? DETECTION_OPTION_EQUAL : 
+                        DETECTION_OPTION_NOT_EQUAL);
+
+    if (left->mode != right->mode)   return DETECTION_OPTION_NOT_EQUAL;
+    if (left->flags != right->flags) return DETECTION_OPTION_NOT_EQUAL;
+    if (left->mask != right->mask)   return DETECTION_OPTION_NOT_EQUAL;
+
+    return DETECTION_OPTION_EQUAL;
+}
+
+/**
+ * SctpChunkFlagsSetup - Setup function for sctp_chunk_flags.
+ *
+ * Links the 'sctp_chunk_flags' keyword to its initialization function.
+ * Returns void.
+ */
+void SctpChunkFlagsSetup(void)
+{
+    /* Map the keyword to an initialization/processing function */
+    RegisterRuleOption("sctp_chunk_flags", SctpChunkFlagsInit, NULL, OPT_TYPE_DETECTION, NULL);
+    
+#ifdef PERF_PROFILING
+    RegisterPreprocessorProfile("sctp_chunk_flags", &sctpChunkFlagsPerfStats, 3, &ruleOTNEvalPerfStats);
+#endif
+
+    DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Plugin: SctpChunkFlags Initialized\n"););
+}
+
+/**
+ * SctpChunkFlagsInit - Initialization function for sctp_chunk_flags.
+ * @sc: pointer to _SnortConfig structure.
+ * @data: pointer to SctpChunkFlagsData (as char *).
+ * @otn: pointer to OptTreeNode structure.
+ * @protocol: integer value representing the transport-layer protocol.
+ *
+ * Attaches the rule option data to the appropriate data structure and links
+ * in the evaluator function to the function pointer list.  Returns void.
+ */
+void SctpChunkFlagsInit(struct _SnortConfig *sc, char *data, OptTreeNode *otn, int protocol)
+{
+    OptFpList *fpl;
+    SctpChunkFlagsData *scfd;
+    void *scfd_dup;
+
+    /* This keyword only applies to the SCTP Protocol. */
+    if (protocol != IPPROTO_SCTP)
+        FatalError("Error at %s:%d: 'sctp_chunk_flags' can only be used with "
+                   "the SCTP protocol.\n", file_name, file_line);
+
+    /* This keyword requires 'sctp_chunk_type' to be used first. */
+    if (otn->ds_list[PLUGIN_SCTP_CHUNK_TYPE] == NULL)
+        FatalError("%s(%d): 'sctp_chunk_type' must be used before "
+                   "'sctp_chunk_flags'.", file_name, file_line);
+
+    /* Check for multiple uses of this keyword in a rule. */
+    if (otn->ds_list[PLUGIN_SCTP_CHUNK_FLAGS])
+        FatalError("%s(%d): 'sctp_chunk_flags' may only be used once per "
+                   "rule.\n", file_name, file_line);
+
+    /* Allocate the data structure and attach it to the otn->ds_list. */
+    otn->ds_list[PLUGIN_SCTP_CHUNK_FLAGS] = (SctpChunkFlagsData *)
+            SnortAlloc(sizeof(SctpChunkFlagsData));
+
+    /* Parse the keyword's parameters and configure the data structure. */
+    SctpChunkFlagsParse(data, otn);
+    scfd = otn->ds_list[PLUGIN_SCTP_CHUNK_FLAGS];
+
+    /* Check for a duplicate entry in the otn. */
+    if (add_detection_option(sc, RULE_OPTION_TYPE_SCTP_CHUNK_FLAGS,
+                             (void *)scfd, &scfd_dup) == DETECTION_OPTION_EQUAL)
+    {
+        otn->ds_list[PLUGIN_SCTP_CHUNK_FLAGS] = (SctpChunkFlagsData *)scfd_dup;
+        free(scfd);
+    }
+
+    /*
+     * Finally, attach the option's detection function to the rule's detection
+     * function pointer list
+     */
+    fpl = AddOptFuncToList(SctpChunkFlagsEval, otn);
+    fpl->type = RULE_OPTION_TYPE_SCTP_CHUNK_FLAGS;
+    fpl->context = otn->ds_list[PLUGIN_SCTP_CHUNK_FLAGS];
+}
+
+/**
+ * SctpChunkFlagsParse - Parsing function for sctp_chunk_flags.
+ * @data: pointer to SctpChunkFlagsData (as char *).
+ * @otn: pointer to OptTreeNode structure.
+ *
+ * Parses the rule option's parameter list and fills in the SctpChunkFlagsData
+ * structure.  Returns void.
+ */
+void SctpChunkFlagsParse(char *data, OptTreeNode *otn)
+{
+    char *params, *end = NULL;
+    uint32_t comma_set = 0;
+    SctpChunkTypeData *sctd;
+    SctpChunkFlagsData *scfd;
+
+    /* Get both the chunk flags and chunk type data sets. */
+    sctd = otn->ds_list[PLUGIN_SCTP_CHUNK_TYPE];
+    scfd = otn->ds_list[PLUGIN_SCTP_CHUNK_FLAGS];
+
+    params = SnortStrdup(data);
+    while (isspace((u_char)*params)) params++;     /* nuke leading whitespace */
+    if (strlen(params) == 0)
+        FatalError("Error at %s:%d: Missing argument to the 'sctp_chunk_flags' "
+                   "keyword\n", file_name, file_line);
+
+    /* Parse each flag bit.  Loop once to validate, then again to parse. */
+    if (sctd->parse_count == 1)
+    {
+        end = params + strlen(params);
+
+        while ((params < end) && (comma_set == 0))
+        {
+            /* Parse operators or the '0' bit. */
+            switch (*params)
+            {
+            case '+':
+                scfd->mode = SCTP_FLAGS_ALL;
+                params++;
+                break;
+
+            case '*':
+                scfd->mode = SCTP_FLAGS_ANY;
+                params++;
+                break;
+
+            case '!':
+                scfd->mode = SCTP_FLAGS_NOT;
+                params++;
+                break;
+
+            case '0':
+                scfd->flags = 0;
+                params++;
+                continue;
+                break;
+
+            case ',':
+                comma_set = 1;
+                params++;
+                continue;
+                break;
+            }
+
+            /* Parse - Certain flag bits only work with certain chunk types. */
+            if (sctd->data)
+                switch (*params)
+                {
+                    _SCTP_CHECK_CHUNK_FLAGS('i', 'I', flags, DATA, I);
+                    _SCTP_CHECK_CHUNK_FLAGS('u', 'U', flags, DATA, U);
+                    _SCTP_CHECK_CHUNK_FLAGS('b', 'B', flags, DATA, B);
+                    _SCTP_CHECK_CHUNK_FLAGS('e', 'E', flags, DATA, E);
+
+                default:
+                    FatalError("Error at %s:%d: Valid flags for DATA chunks "
+                               "are IUBE.\n", file_name, file_line);
+                    break;
+                }
+
+            else if (sctd->abort)
+                switch (*params)
+                {
+                    _SCTP_CHECK_CHUNK_FLAGS('t', 'T', flags, ABORT, T);
+
+                default:
+                    FatalError("Error at %s:%d: Valid flags for ABORT chunks "
+                               "are T.\n", file_name, file_line);
+                    break;
+                }
+
+            else if (sctd->shutdown_complete)
+                switch (*params)
+                {
+                    _SCTP_CHECK_CHUNK_FLAGS('t', 'T', flags, SHUTDOWN_COMPLETE, T);
+
+                default:
+                    FatalError("Error at %s:%d: Valid flags for "
+                               "SHUTDOWN_COMPLETE chunks are T.\n",
+                               file_name, file_line);
+                    break;
+                }
+
+            else if (sctd->pktdrop)
+                switch (*params)
+                {
+                    _SCTP_CHECK_CHUNK_FLAGS('c', 'C', flags, PKTDROP, C);
+                    _SCTP_CHECK_CHUNK_FLAGS('t', 'T', flags, PKTDROP, T);
+                    _SCTP_CHECK_CHUNK_FLAGS('b', 'B', flags, PKTDROP, B);
+                    _SCTP_CHECK_CHUNK_FLAGS('m', 'M', flags, PKTDROP, M);
+
+                default:
+                    FatalError("Error at %s:%d: Valid flags for PKTDROP chunks "
+                               "are CTBM.\n", file_name, file_line);
+                    break;
+                }
+            else
+                FatalError("Error at %s:%d: The specified chunk type does not "
+                           "take any flags.\n", file_name, file_line);
+
+            params++;
+        }
+
+        /* Skip any whitespace between the <flags> and <mask> fields. */
+        while (isspace((u_char)*params)) params++;
+
+        /* Parse the mask field, if set. */
+        while ((params < end) && (comma_set == 1))
+        {
+            if (sctd->data)
+                switch (*params)
+                {
+                    _SCTP_CHECK_CHUNK_FLAGS('i', 'I', mask, DATA, I);
+                    _SCTP_CHECK_CHUNK_FLAGS('u', 'U', mask, DATA, U);
+                    _SCTP_CHECK_CHUNK_FLAGS('b', 'B', mask, DATA, B);
+                    _SCTP_CHECK_CHUNK_FLAGS('e', 'E', mask, DATA, E);
+
+                default:
+                    FatalError("Error at %s:%d: Valid masks for DATA chunks "
+                               "are IUBE.\n", file_name, file_line);
+                    break;
+                }
+
+            else if (sctd->abort)
+                switch (*params)
+                {
+                    _SCTP_CHECK_CHUNK_FLAGS('t', 'T', mask, ABORT, T);
+
+                default:
+                    FatalError("Error at %s:%d: Valid masks for ABORT chunks "
+                               "are T.\n", file_name, file_line);
+                    break;
+                }
+
+            else if (sctd->shutdown_complete)
+                switch (*params)
+                {
+                    _SCTP_CHECK_CHUNK_FLAGS('t', 'T', mask, SHUTDOWN_COMPLETE, T);
+
+                default:
+                    FatalError("Error at %s:%d: Valid masks for "
+                               "SHUTDOWN_COMPLETE chunks are T.\n",
+                               file_name, file_line);
+                    break;
+                }
+
+            else if (sctd->pktdrop)
+                switch (*params)
+                {
+                    _SCTP_CHECK_CHUNK_FLAGS('c', 'C', mask, PKTDROP, C);
+                    _SCTP_CHECK_CHUNK_FLAGS('t', 'T', mask, PKTDROP, T);
+                    _SCTP_CHECK_CHUNK_FLAGS('b', 'B', mask, PKTDROP, B);
+                    _SCTP_CHECK_CHUNK_FLAGS('m', 'M', mask, PKTDROP, M);
+
+                default:
+                    FatalError("Error at %s:%d: Valid masks for PKTDROP chunks "
+                               "are CTBM.\n", file_name, file_line);
+                    break;
+                }
+            else
+                FatalError("Error at %s:%d: The specified chunk type does not "
+                           "take any masks.\n", file_name, file_line);
+
+            params++;
+        }
+    }
+    else
+        FatalError("Error at %s:%d: When using 'sctp_chunk_flags', the "
+                   "'sctp_chunk_type' keyword can only be set to a single "
+                   "parameter representing the chunk whose flags field "
+                   "you wish to check.\n",  file_name, file_line);
+}
+
+/**
+ * SctpChunkFlagsEval - Evaluation function for sctp_chunk_flags.
+ * @data: pointer to SctpChunkFlagsData (as char *).
+ * @p: pointer to Packet structure.
+ *
+ * Evaluates the rule option against the packet pointed to by 'p'.  Returns
+ * either DETECTION_OPTION_MATCH or DETECTION_OPTION_NO_MATCH.
+ */
+int SctpChunkFlagsEval(void *data, Packet *p)
+{
+    SctpChunkFlagsData *scfd = (SctpChunkFlagsData *)data;
+    int rval = DETECTION_OPTION_NO_MATCH;
+    PROFILE_VARS;
+
+    if(!p->sctph || unlikely(!scfd))
+        return rval;
+
+    PREPROC_PROFILE_START(sctpChunkFlagsPerfStats);
+
+    SctpChunk *c = (SctpChunk *)p->sctph->chunks;
+    uint32_t remaining_len = p->dsize;
+
+    /*
+     * Loop through the chunks in the packet and for each chunk, check
+     * check the chunk flags against the supplied value for a match.
+     */
+    while (likely(c != NULL) &&
+           (remaining_len != 0) &&
+           (rval == DETECTION_OPTION_NO_MATCH))
+    {
+        uint8_t chunk_flags = c->flags & (0xff ^ scfd->mask);
+        uint16_t chunk_length = SCTP_ADD_PADDING(ntohs(c->length));
+        remaining_len -= chunk_length;
+        
+        /*
+         * Although we have already validated which chunks we want to check
+         * via 'sctp_chunk_type', we still have to check the flags for
+         * specific types because of chunk bundling.
+         */
+        switch (c->type)
+        {
+        case SCTP_DATA_C:
+        case SCTP_ABORT_C:
+        case SCTP_SHUTDOWN_COMPLETE_C:
+        case SCTP_PKTDROP_C:
+            switch (scfd->mode)
+            {
+            case SCTP_FLAGS_NORMAL:
+                if (scfd->flags == chunk_flags)
+                    rval = DETECTION_OPTION_MATCH;
+                break;
+
+            case SCTP_FLAGS_ALL:
+                if ((scfd->flags & chunk_flags) == scfd->flags)
+                    rval = DETECTION_OPTION_MATCH;
+                break;
+
+            case SCTP_FLAGS_ANY:
+                if ((scfd->flags & chunk_flags) != 0)
+                    rval = DETECTION_OPTION_MATCH;
+                break;
+
+            case SCTP_FLAGS_NOT:
+                if ((scfd->flags & chunk_flags) == 0)
+                    rval = DETECTION_OPTION_MATCH;
+                break;
+            }
+            break;
+
+        case SCTP_INIT_C:
+        case SCTP_INIT_ACK_C:
+        case SCTP_SACK_C:
+        case SCTP_HEARTBEAT_C:
+        case SCTP_HEARTBEAT_ACK_C:
+        case SCTP_SHUTDOWN_C:
+        case SCTP_SHUTDOWN_ACK_C:
+        case SCTP_ERROR_C:
+        case SCTP_COOKIE_ECHO_C:
+        case SCTP_COOKIE_ACK_C:
+        case SCTP_ECNE_C:
+        case SCTP_CWR_C:
+        case SCTP_AUTH_C:
+        case SCTP_NR_SACK_C:
+        case SCTP_ASCONF_ACK_C:
+        case SCTP_RECONFIG_C:
+        case SCTP_PAD_C:
+        case SCTP_FORWARD_TSN_C:
+        case SCTP_ASCONF_C:
+            break;
+
+        default:
+            DEBUG_WRAP(DebugMessage(
+                DEBUG_PLUGIN, "Plugin (sctp_chunk_flags): Encountered "
+                              "unknown SCTP Chunk Type 0x%2x!\n", c->type));
+            break;
+        }
+
+        remaining_len = SctpGetNextChunk(c, remaining_len, chunk_length);
+    }
+
+    /* If the test isn't successful, return DETECTION_OPTION_NO_MATCH */
+    PREPROC_PROFILE_END(sctpChunkFlagsPerfStats);
+    return rval;
+}
diff --git a/src/detection-plugins/sp_sctp_chunk_flags.h b/src/detection-plugins/sp_sctp_chunk_flags.h
new file mode 100644
index 0000000..795f17b
--- /dev/null
+++ b/src/detection-plugins/sp_sctp_chunk_flags.h
@@ -0,0 +1,81 @@
+/*
+** Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+** Author: Joshua Kinard
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License Version 2 as
+** published by the Free Software Foundation.  You may not use, modify or
+** distribute this program under any other version of the GNU General
+** Public License.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+/* $Id$ */
+#ifndef __SP_SCTP_CHUNK_FLAGS_H__
+#define __SP_SCTP_CHUNK_FLAGS_H__
+
+/* From linux-kernel/include/linux/bitops.h */
+/* Renamed to avoid a conflict w/ BIT() in decode.h */
+#define BIT2(nr) (1UL << (nr))
+
+/* Flag Definitions for DATA. */
+#define SCTP_DATA_C_FLAGS_I                 BIT2(3)  /* SACK Immediate */
+#define SCTP_DATA_C_FLAGS_U                 BIT2(2)  /* Unordered */
+#define SCTP_DATA_C_FLAGS_B                 BIT2(1)  /* Beginning Fragment */
+#define SCTP_DATA_C_FLAGS_E                 BIT2(0)  /* Ending Fragment */
+#define SCTP_DATA_C_FLAGS_MASK              0xffff
+
+/* Flag Definitions for ABORT. */
+#define SCTP_ABORT_C_FLAGS_T                BIT2(0)  /* VTag Reflected */
+#define SCTP_ABORT_C_FLAGS_MASK             0x0f
+
+/* Flag Definitions for SHUTDOWN-COMPLETE. */
+#define SCTP_SHUTDOWN_COMPLETE_C_FLAGS_T    BIT2(0)  /* VTag Reflected */
+#define SCTP_SHUTDOWN_COMPLETE_C_FLAGS_MASK 0x0f
+
+/* Flag Definitions for PKTDROP. */
+#define SCTP_PKTDROP_C_FLAGS_C              BIT2(3)  /* Count Transform */
+#define SCTP_PKTDROP_C_FLAGS_T              BIT2(2)  /* Truncated */
+#define SCTP_PKTDROP_C_FLAGS_B              BIT2(1)  /* Bad CRC32c */
+#define SCTP_PKTDROP_C_FLAGS_M              BIT2(0)  /* Middle/VTag Reflected */
+#define SCTP_PKTDROP_C_FLAGS_MASK           0xffff
+
+/* Flag Mode Definitions. */
+#define SCTP_FLAGS_NORMAL                   0x00
+#define SCTP_FLAGS_ALL                      0x01
+#define SCTP_FLAGS_ANY                      0x02
+#define SCTP_FLAGS_NOT                      0x04
+
+/* Macros to duplicate common bits of code and make things look cleaner :) */
+#define _SCTP_CHECK_CHUNK_FLAGS(lower,upper,field,chunk,bit)           \
+                case lower:                                            \
+                case upper:                                            \
+                    scfd->field |= SCTP_##chunk##_C_FLAGS_##bit;       \
+                    break
+
+
+/* SctpChunkFlags data structure. */
+typedef struct _SctpChunkFlagsData
+{
+    u_char mode;
+    u_char flags;
+    u_char mask;
+} SctpChunkFlagsData;
+
+/* Prototypes. */
+uint32_t SctpChunkFlagsHash(void *);
+int SctpChunkFlagsCompare(void *, void *);
+void SctpChunkFlagsSetup(void);
+void SctpChunkFlagsInit(struct _SnortConfig *, char *, OptTreeNode *, int);
+void SctpChunkFlagsParse(char *, OptTreeNode *);
+int SctpChunkFlagsEval(void *, Packet *);
+
+#endif  /* __SP_SCTP_CHUNK_FLAGS_H__ */
diff --git a/src/detection-plugins/sp_sctp_chunk_type.c b/src/detection-plugins/sp_sctp_chunk_type.c
new file mode 100644
index 0000000..31583aa
--- /dev/null
+++ b/src/detection-plugins/sp_sctp_chunk_type.c
@@ -0,0 +1,500 @@
+/*
+** Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+** Author: Joshua Kinard
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License Version 2 as
+** published by the Free Software Foundation.  You may not use, modify or
+** distribute this program under any other version of the GNU General
+** Public License.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "sf_types.h"
+#include "rules.h"
+#include "treenodes.h"
+#include "decode.h"
+#include "plugbase.h"
+#include "parser.h"
+#include "util.h"
+#include "snort_debug.h"
+#include "plugin_enum.h"
+#include "compiler.h"
+#include "sp_sctp_chunk_type.h"
+
+#include "snort.h"
+#include "profiler.h"
+#ifdef PERF_PROFILING
+PreprocStats sctpChunkTypePerfStats;
+extern PreprocStats ruleOTNEvalPerfStats;
+#endif
+
+#include "sfhashfcn.h"
+#include "detection_options.h"
+
+
+/*
+ * Quick documentation:
+ *   sctp_chunk_type:<CHUNK_TYPE1>[,<CHUNK_TYPE2>][,CHUNK_TYPE3>][,...];
+ *
+ * Ex:
+ *   sctp_chunk_type:DATA;
+ *   sctp_chunk_type:INIT,FORWARD-TSN,PAD;
+ */
+
+
+/**
+ * SctpChunkTypeHash - Hash function for sctp_chunk_type.
+ * @data: pointer to SctpChunkFlagsData.
+ *
+ * Returns a uint32_t representing the hash.
+ */
+uint32_t SctpChunkTypeHash(void *data)
+{
+    uint32_t a, b, c;
+    SctpChunkTypeData *sctd = (SctpChunkTypeData *)data;
+
+    a = sctd->data;
+    b = sctd->init;
+    c = sctd->init_ack;
+    mix(a, b, c);
+    a += sctd->sack;
+    b += sctd->heartbeat;
+    c += sctd->heartbeat_ack;
+    mix(a, b, c);
+    a += sctd->abort;
+    b += sctd->shutdown;
+    c += sctd->shutdown_ack;
+    mix(a, b, c);
+    a += sctd->error;
+    b += sctd->cookie_echo;
+    c += sctd->cookie_ack;
+    mix(a, b, c);
+    a += sctd->ecne;
+    b += sctd->cwr;
+    c += sctd->shutdown_complete;
+    mix(a, b, c);
+    a += sctd->auth;
+    b += sctd->nr_sack;
+    c += sctd->asconf_ack;
+    mix(a, b, c);
+    a += sctd->pktdrop;
+    b += sctd->reconfig;
+    c += sctd->pad;
+    mix(a, b, c);
+    a += sctd->forward_tsn;
+    b += sctd->asconf;
+    c += RULE_OPTION_TYPE_SCTP_CHUNK_TYPE;
+    final(a, b, c);
+
+    return c;
+}
+
+/**
+ * SctpChunkTypeCompare - Comparator function for sctp_chunk_type.
+ * @l: pointer to SctpChunkTypeData (left side).
+ * @r: pointer to SctpChunkTypeData (right side).
+ *
+ * Returns the comparison result of the two data structures.
+ */
+int SctpChunkTypeCompare(void *l, void *r)
+{
+    SctpChunkTypeData *left = (SctpChunkTypeData *)l;
+    SctpChunkTypeData *right = (SctpChunkTypeData *)r;
+
+    if (!left)
+        return (!right ? DETECTION_OPTION_EQUAL : 
+                         DETECTION_OPTION_NOT_EQUAL);
+    
+    if (!right)
+        return (!left ? DETECTION_OPTION_EQUAL : 
+                        DETECTION_OPTION_NOT_EQUAL);
+
+    if (left->data != right->data)                           return DETECTION_OPTION_NOT_EQUAL;
+    if (left->init != right->init)                           return DETECTION_OPTION_NOT_EQUAL;
+    if (left->init_ack != right->init_ack)                   return DETECTION_OPTION_NOT_EQUAL;
+    if (left->sack != right->sack)                           return DETECTION_OPTION_NOT_EQUAL;
+    if (left->heartbeat != right->heartbeat)                 return DETECTION_OPTION_NOT_EQUAL;
+    if (left->heartbeat_ack != right->heartbeat_ack)         return DETECTION_OPTION_NOT_EQUAL;
+    if (left->abort != right->abort)                         return DETECTION_OPTION_NOT_EQUAL;
+    if (left->shutdown != right->shutdown)                   return DETECTION_OPTION_NOT_EQUAL;
+    if (left->shutdown_ack != right->shutdown_ack)           return DETECTION_OPTION_NOT_EQUAL;
+    if (left->error != right->error)                         return DETECTION_OPTION_NOT_EQUAL;
+    if (left->cookie_echo != right->cookie_echo)             return DETECTION_OPTION_NOT_EQUAL;
+    if (left->cookie_ack != right->cookie_ack)               return DETECTION_OPTION_NOT_EQUAL;
+    if (left->ecne != right->ecne)                           return DETECTION_OPTION_NOT_EQUAL;
+    if (left->cwr != right->cwr)                             return DETECTION_OPTION_NOT_EQUAL;
+    if (left->shutdown_complete != right->shutdown_complete) return DETECTION_OPTION_NOT_EQUAL;
+    if (left->auth != right->auth)                           return DETECTION_OPTION_NOT_EQUAL;
+    if (left->nr_sack != right->nr_sack)                     return DETECTION_OPTION_NOT_EQUAL;
+    if (left->asconf_ack != right->asconf_ack)               return DETECTION_OPTION_NOT_EQUAL;
+    if (left->pktdrop != right->pktdrop)                     return DETECTION_OPTION_NOT_EQUAL;
+    if (left->reconfig != right->reconfig)                   return DETECTION_OPTION_NOT_EQUAL;
+    if (left->pad != right->pad)                             return DETECTION_OPTION_NOT_EQUAL;
+    if (left->forward_tsn != right->forward_tsn)             return DETECTION_OPTION_NOT_EQUAL;
+    if (left->asconf != right->asconf)                       return DETECTION_OPTION_NOT_EQUAL;
+
+    return DETECTION_OPTION_EQUAL;
+}
+
+/**
+ * SctpChunkTypeSetup - Setup function for sctp_chunk_type.
+ *
+ * Links the 'sctp_chunk_type' keyword to its initialization function.
+ * Returns void.
+ */
+void SctpChunkTypeSetup(void)
+{
+    /* Map the keyword to an initialization/processing function */
+    RegisterRuleOption("sctp_chunk_type", SctpChunkTypeInit, NULL, OPT_TYPE_DETECTION, NULL);
+    
+#ifdef PERF_PROFILING
+    RegisterPreprocessorProfile("sctp_chunk_type", &sctpChunkTypePerfStats, 3, &ruleOTNEvalPerfStats);
+#endif
+
+    DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Plugin: SctpChunkType Initialized\n"););
+}
+
+/**
+ * SctpChunkTypeInit - Initialization function for sctp_chunk_type.
+ * @sc: pointer to _SnortConfig structure.
+ * @data: pointer to SctpChunkTypeData (as char *).
+ * @otn: pointer to OptTreeNode structure.
+ * @protocol: integer value representing the transport-layer protocol.
+ *
+ * Attaches the rule option data to the appropriate data structure and links
+ * in the evaluator function to the function pointer list.  Returns void.
+ */
+void SctpChunkTypeInit(struct _SnortConfig *sc, char *data, OptTreeNode *otn, int protocol)
+{
+    OptFpList *fpl;
+    SctpChunkTypeData *sctd;
+    void *sctd_dup;
+
+    /* This keyword only applies to the SCTP Protocol. */
+    if (protocol != IPPROTO_SCTP)
+        FatalError("Error at %s:%d: 'sctp_chunk_types' can only be used with "
+                   "the SCTP protocol.\n", file_name, file_line);
+
+    /* Check for multiple uses of this keyword in a rule. */
+    if (otn->ds_list[PLUGIN_SCTP_CHUNK_TYPE])
+        FatalError("%s(%d): 'sctp_chunk_type' may only be used once per "
+                   "rule.\n", file_name, file_line);
+
+    /* Allocate the data structure and attach it to the otn->ds_list. */
+    otn->ds_list[PLUGIN_SCTP_CHUNK_TYPE] = (SctpChunkTypeData *)
+            SnortAlloc(sizeof(SctpChunkTypeData));
+
+    /* Parse the keyword's parameters and configure the data structure. */
+    SctpChunkTypeParse(data, otn);
+    sctd = otn->ds_list[PLUGIN_SCTP_CHUNK_TYPE];
+
+    /* Check for a duplicate entry in the otn. */
+    if (add_detection_option(sc, RULE_OPTION_TYPE_SCTP_CHUNK_TYPE,
+                             (void *)sctd, &sctd_dup) == DETECTION_OPTION_EQUAL)
+    {
+        otn->ds_list[PLUGIN_SCTP_CHUNK_TYPE] = (SctpChunkTypeData *)sctd_dup;
+        free(sctd);
+    }
+
+    /*
+     * Finally, attach the option's detection function to the rule's detection
+     * function pointer list
+     */
+    fpl = AddOptFuncToList(SctpChunkTypeEval, otn);
+    fpl->type = RULE_OPTION_TYPE_SCTP_CHUNK_TYPE;
+    fpl->context = otn->ds_list[PLUGIN_SCTP_CHUNK_TYPE];
+}
+
+/**
+ * SctpChunkTypeParse - Parsing function for sctp_chunk_type.
+ * @data: char pointer to SctpChunkTypeData.
+ * @otn: pointer to the OptTreeNode structure.
+ *
+ * Parses the rule option's parameter list and fills in the SctpChunkTypeData
+ * structure.  Returns void.
+ */
+void SctpChunkTypeParse(char *data, OptTreeNode *otn)
+{
+    char *tok, *params;
+    SctpChunkTypeData *sctd;
+
+    /* Set the sctd pointer to make it easier to reference the option's
+       particular data struct */
+    sctd = otn->ds_list[PLUGIN_SCTP_CHUNK_TYPE];
+
+    params = SnortStrdup(data);
+    while(isspace((int)*params)) params++;     /* nuke leading whitespace */
+    
+    /* Make sure at least one value was supplied. */
+    tok = strtok(params, ",");
+    if (!tok)
+        FatalError("Error at %s:%d: Missing argument to the 'sctp_chunk_type' "
+                   "keyword\n", file_name, file_line);
+
+    /* Negation - Only handled on the first token. */
+    if (tok[0] == '!')
+    {
+        sctd->neg = 1;
+        tok++;
+    }
+
+    /* Parse each token. */
+    while (tok)
+    {
+        /* Limit the number of tokens to be (SCTP_MAX_NUM_CHUNKS - 1). */
+        if (sctd->parse_count >= SCTP_MAX_NUM_CHUNKS)
+            FatalError("Error at %s:%d: Number of parameters exceeds "
+                       "SCTP_MAX_NUM_CHUNKS (%d)!\n", file_name,
+                       file_line, SCTP_MAX_NUM_CHUNKS);
+
+        /*
+         * For each token, See what the specified chunk type is and set its
+         * appropriate member in the sctd structure to be 'true'.
+         */
+        if      (!strcasecmp("DATA", tok))              sctd->data = 1;
+        else if (!strcasecmp("INIT", tok))              sctd->init = 1;
+        else if (!strcasecmp("INIT-ACK", tok))          sctd->init_ack = 1;
+        else if (!strcasecmp("SACK", tok))              sctd->sack = 1;
+        else if (!strcasecmp("HEARTBEAT", tok))         sctd->heartbeat = 1;
+        else if (!strcasecmp("HEARTBEAT-ACK", tok))     sctd->heartbeat_ack = 1;
+        else if (!strcasecmp("ABORT", tok))             sctd->abort = 1;
+        else if (!strcasecmp("SHUTDOWN", tok))          sctd->shutdown = 1;
+        else if (!strcasecmp("SHUTDOWN-ACK", tok))      sctd->shutdown_ack = 1;
+        else if (!strcasecmp("ERROR", tok))             sctd->error = 1;
+        else if (!strcasecmp("COOKIE-ECHO", tok))       sctd->cookie_echo = 1;
+        else if (!strcasecmp("COOKIE-ACK", tok))        sctd->cookie_ack = 1;
+        else if (!strcasecmp("ECNE", tok))              sctd->ecne = 1;
+        else if (!strcasecmp("CWR", tok))               sctd->cwr = 1;
+        else if (!strcasecmp("SHUTDOWN-COMPLETE", tok)) sctd->shutdown_complete = 1;
+        else if (!strcasecmp("AUTH", tok))              sctd->auth = 1;
+        else if (!strcasecmp("NR-SACK", tok))           sctd->nr_sack = 1;
+        else if (!strcasecmp("ASCONF-ACK", tok))        sctd->asconf_ack = 1;
+        else if (!strcasecmp("PKTDROP", tok))           sctd->pktdrop = 1;
+        else if (!strcasecmp("RE-CONFIG", tok))         sctd->reconfig = 1;
+        else if (!strcasecmp("PAD", tok))               sctd->pad = 1;
+        else if (!strcasecmp("FORWARD-TSN", tok))       sctd->forward_tsn = 1;
+        else if (!strcasecmp("ASCONF", tok))            sctd->asconf = 1;
+        else
+            FatalError("Error at %s:%d: %s is not a recognized argument to "
+                       "'sctp_chunk_type'.\n", file_name, file_line, tok);
+
+        sctd->parse_count++;
+        tok = strtok(NULL, ",");
+    }
+}
+
+
+/**
+ * SctpChunkTypeEval - Evaluation function for sctp_chunk_type.
+ * @data: void pointer to SctpChunkTypeData.
+ * @p: pointer to the Packet structure.
+ *
+ * Evaluates the rule option against the packet pointed to by 'p'.  Returns
+ * either DETECTION_OPTION_MATCH or DETECTION_OPTION_NO_MATCH.
+ */
+int SctpChunkTypeEval(void *data, Packet *p)
+{
+    SctpChunkTypeData *sctd = (SctpChunkTypeData *)data;
+    int rval;
+    PROFILE_VARS;
+
+    if(!p->sctph || unlikely(!sctd))
+        return DETECTION_OPTION_NO_MATCH;
+
+    PREPROC_PROFILE_START(sctpChunkTypePerfStats);
+
+    SctpChunk *c = (SctpChunk *)p->sctph->chunks;
+    uint32_t remaining_len = p->dsize;
+
+    /*
+     * Pre-assignment for rval.  Yes, it's inverted.  Don't try to think
+     * about it, because you'll only wind up getting hurt.  There is probably
+     * a better way to do this.
+     */
+    rval = (sctd->neg ? DETECTION_OPTION_MATCH :
+                        DETECTION_OPTION_NO_MATCH);
+
+    /*
+     * For each chunk, look at its type field and find the corresponding
+     * member in the SctpChunkTypeData struct.  If it is true, then see
+     * whether we have to deal with negation.
+     */
+    while (likely(c != NULL) && (remaining_len != 0))
+    {
+        uint16_t chunk_length = SCTP_ADD_PADDING(ntohs(c->length));
+        remaining_len -= chunk_length;
+        
+        switch (c->type)
+        {
+            case SCTP_DATA_C:
+                if (sctd->data)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_INIT_C:
+                if (sctd->init)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_INIT_ACK_C:
+                if (sctd->init_ack)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_SACK_C:
+                if (sctd->sack)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_HEARTBEAT_C:
+                if (sctd->heartbeat)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_HEARTBEAT_ACK_C:
+                if (sctd->heartbeat_ack)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_ABORT_C:
+                if (sctd->abort)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_SHUTDOWN_C:
+                if (sctd->shutdown)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_SHUTDOWN_ACK_C:
+                if (sctd->shutdown_ack)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_ERROR_C:
+                if (sctd->error)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_COOKIE_ECHO_C:
+                if (sctd->cookie_echo)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_COOKIE_ACK_C:
+                if (sctd->cookie_ack)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_ECNE_C:
+                if (sctd->ecne)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_CWR_C:
+                if (sctd->cwr)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_SHUTDOWN_COMPLETE_C:
+                if (sctd->shutdown_complete)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_AUTH_C:
+                if (sctd->auth)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_NR_SACK_C:
+                if (sctd->nr_sack)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_ASCONF_ACK_C:
+                if (sctd->asconf_ack)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_PKTDROP_C:
+                if (sctd->pktdrop)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_RECONFIG_C:
+                if (sctd->reconfig)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_PAD_C:
+                if (sctd->pad)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_FORWARD_TSN_C:
+                if (sctd->forward_tsn)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_ASCONF_C:
+                if (sctd->asconf)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            default:
+                DEBUG_WRAP(DebugMessage(
+                    DEBUG_PLUGIN, "Plugin (sctp_chunk_type): Encountered "
+                                  "unknown SCTP Chunk Type 0x%2x!\n",
+                                  c->type));
+                break;
+        }
+
+        remaining_len = SctpGetNextChunk(c, remaining_len, chunk_length);
+    }
+
+    /* If the test isn't successful, return DETECTION_OPTION_NO_MATCH */
+    PREPROC_PROFILE_END(sctpChunkTypePerfStats);
+    return rval;
+}
diff --git a/src/detection-plugins/sp_sctp_chunk_type.h b/src/detection-plugins/sp_sctp_chunk_type.h
new file mode 100644
index 0000000..dd394c6
--- /dev/null
+++ b/src/detection-plugins/sp_sctp_chunk_type.h
@@ -0,0 +1,66 @@
+/*
+** Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+** Author: Joshua Kinard
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License Version 2 as
+** published by the Free Software Foundation.  You may not use, modify or
+** distribute this program under any other version of the GNU General
+** Public License.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+/* $Id$ */
+#ifndef __SP_SCTP_CHUNK_TYPE_H__
+#define __SP_SCTP_CHUNK_TYPE_H__
+
+/* Current number of chunks, defined by approved RFC's  and drafts */
+#define SCTP_MAX_NUM_CHUNKS         23
+
+/* SctpChunkType data structure. */
+typedef struct _SctpChunkTypeData
+{
+    uint8_t parse_count;
+    uint8_t neg;
+    uint8_t data;
+    uint8_t init;
+    uint8_t init_ack;
+    uint8_t sack;
+    uint8_t heartbeat;
+    uint8_t heartbeat_ack;
+    uint8_t abort;
+    uint8_t shutdown;
+    uint8_t shutdown_ack;
+    uint8_t error;
+    uint8_t cookie_echo;
+    uint8_t cookie_ack;
+    uint8_t ecne;
+    uint8_t cwr;
+    uint8_t shutdown_complete;
+    uint8_t auth;
+    uint8_t nr_sack;
+    uint8_t asconf_ack;
+    uint8_t pktdrop;
+    uint8_t reconfig;
+    uint8_t pad;
+    uint8_t forward_tsn;
+    uint8_t asconf;
+} SctpChunkTypeData;
+
+/* Prototypes. */
+uint32_t SctpChunkTypeHash(void *);
+int SctpChunkTypeCompare(void *, void *);
+void SctpChunkTypeSetup(void);
+void SctpChunkTypeInit(struct _SnortConfig *, char *, OptTreeNode *, int);
+void SctpChunkTypeParse(char *, OptTreeNode *);
+int SctpChunkTypeEval(void *, Packet *);
+
+#endif  /* __SP_SCTP_CHUNK_TYPE_H__ */
diff --git a/src/detection-plugins/sp_sctp_num_chunks.c b/src/detection-plugins/sp_sctp_num_chunks.c
new file mode 100644
index 0000000..22bf5fe
--- /dev/null
+++ b/src/detection-plugins/sp_sctp_num_chunks.c
@@ -0,0 +1,296 @@
+/*
+** Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+** Author: Joshua Kinard
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License Version 2 as
+** published by the Free Software Foundation.  You may not use, modify or
+** distribute this program under any other version of the GNU General
+** Public License.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "sf_types.h"
+#include "rules.h"
+#include "treenodes.h"
+#include "decode.h"
+#include "plugbase.h"
+#include "parser.h"
+#include "util.h"
+#include "snort_debug.h"
+#include "plugin_enum.h"
+#include "compiler.h"
+#include "sp_sctp_num_chunks.h"
+
+#include "snort.h"
+#include "profiler.h"
+#ifdef PERF_PROFILING
+PreprocStats sctpNumChunksPerfStats;
+extern PreprocStats ruleOTNEvalPerfStats;
+#endif
+
+#include "sfhashfcn.h"
+#include "detection_options.h"
+
+
+/*
+ * Quick documentation:
+ *   sctp_num_chunks:min<>max;
+ *   sctp_num_chunks:[<|>]<number>;
+ *
+ * Ex:
+ *   sctp_num_chunks:42;
+ *   sctp_num_chunks:<42;
+ *   sctp_num_chunks:42<>44;
+ */
+
+
+/**
+ * SctpNumChunksHash - Hash function for sctp_num_chunks.
+ * @data: pointer to SctpNumChunksData.
+ *
+ * Returns a uint32_t representing the hash.
+ */
+uint32_t SctpNumChunksHash(void *data)
+{
+    uint32_t a, b, c;
+    SctpNumChunksData *sncd = (SctpNumChunksData *)data;
+
+    a = sncd->num_chunks_min;
+    b = sncd->num_chunks_max;
+    c = (uint32_t)sncd->operator;
+    mix(a, b, c);
+
+    a += RULE_OPTION_TYPE_SCTP_NUM_CHUNKS;
+    final(a, b, c);
+
+    return c;
+}
+
+/**
+ * SctpNumChunksCompare - Comparator function for sctp_num_chunks.
+ * @l: pointer to SctpNumChunksData (left side).
+ * @r: pointer to SctpNumChunksData (right side).
+ *
+ * Returns the comparison result of the two data structures.
+ */
+int SctpNumChunksCompare(void *l, void *r)
+{
+    SctpNumChunksData *left = (SctpNumChunksData *)l;
+    SctpNumChunksData *right = (SctpNumChunksData *)r;
+
+    if (!left)
+        return (!right ? DETECTION_OPTION_EQUAL : 
+                         DETECTION_OPTION_NOT_EQUAL);
+    
+    if (!right)
+        return (!left ? DETECTION_OPTION_EQUAL : 
+                        DETECTION_OPTION_NOT_EQUAL);
+
+    if (left->operator != right->operator)             return DETECTION_OPTION_NOT_EQUAL;
+    if (left->num_chunks_min != right->num_chunks_min) return DETECTION_OPTION_NOT_EQUAL;
+    if (left->num_chunks_max != right->num_chunks_max) return DETECTION_OPTION_NOT_EQUAL;
+
+    return DETECTION_OPTION_EQUAL;
+}
+
+/**
+ * SctpNumChunksSetup - Setup function for sctp_num_chunks.
+ *
+ * Links the 'sctp_num_chunks' keyword to its initialization function.
+ * Returns void.
+ */
+void SctpNumChunksSetup(void)
+{
+    /* Map the keyword to an initialization/processing function */
+    RegisterRuleOption("sctp_num_chunks", SctpNumChunksInit, NULL, OPT_TYPE_DETECTION, NULL);
+    
+#ifdef PERF_PROFILING
+    RegisterPreprocessorProfile("sctp_num_chunks", &sctpNumChunksPerfStats, 3, &ruleOTNEvalPerfStats);
+#endif
+
+    DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Plugin: SctpNumChunks Initialized\n"););
+}
+
+/**
+ * SctpNumChunksInit - Initialization function for sctp_num_chunks.
+ * @sc: pointer to _SnortConfig structure.
+ * @data: pointer to SctpNumChunksData.
+ * @otn: pointer to OptTreeNode structure.
+ * @protocol: integer value representing the transport-layer protocol.
+ *
+ * Attaches the rule option data to the appropriate data structure and links
+ * in the evaluator function to the function pointer list.  Returns void.
+ */
+void SctpNumChunksInit(struct _SnortConfig *sc, char *data, OptTreeNode *otn, int protocol)
+{
+    OptFpList *fpl;
+    SctpNumChunksData *sncd;
+    void *sncd_dup;
+
+    /* This keyword only applies to the SCTP Protocol. */
+    if (protocol != IPPROTO_SCTP)
+        FatalError("Error at %s:%d: 'sctp_num_chunks' can only be used with "
+                   "the SCTP protocol.\n", file_name, file_line);
+
+    /* Check for multiple uses of this keyword in a rule. */
+    if (otn->ds_list[PLUGIN_SCTP_NUM_CHUNKS])
+        FatalError("Error at %s:%d: 'sctp_num_chunks' may only be used once per "
+                   "rule.\n", file_name, file_line);
+
+    /* Allocate the data structure and attach it to the otn->ds_list. */
+    otn->ds_list[PLUGIN_SCTP_NUM_CHUNKS] = (SctpNumChunksData *)
+            SnortAlloc(sizeof(SctpNumChunksData));
+
+    /* Parse the keyword's parameters and configure the data structure. */
+    SctpNumChunksParse(data, otn);
+    sncd = otn->ds_list[PLUGIN_SCTP_NUM_CHUNKS];
+
+    /* Check for a duplicate entry in the otn. */
+    if (add_detection_option(sc, RULE_OPTION_TYPE_SCTP_NUM_CHUNKS,
+                             (void *)sncd, &sncd_dup) == DETECTION_OPTION_EQUAL)
+    {
+        otn->ds_list[PLUGIN_SCTP_NUM_CHUNKS] = (SctpNumChunksData *)sncd_dup;
+        free(sncd);
+    }
+
+    /*
+     * Finally, attach the option's detection function to the rule's detection
+     * function pointer list
+     */
+    fpl = AddOptFuncToList(SctpNumChunksEval, otn);
+    fpl->type = RULE_OPTION_TYPE_SCTP_NUM_CHUNKS;
+    fpl->context = (void *)otn->ds_list[PLUGIN_SCTP_NUM_CHUNKS];
+}
+
+/**
+ * SctpNumChunksParse - Parsing function for sctp_num_chunks.
+ * @data: pointer to SctpNumChunksData.
+ * @otn: pointer to OptTreeNode structure.
+ *
+ * Parses the rule option's parameter list and fills in the SctpNumChunksData
+ * structure.  Returns void.
+ */
+void SctpNumChunksParse(char *data, OptTreeNode *otn)
+{
+    char *tok = data, *end;
+    int num = 0, rval = 0;
+    UNumericRange rng;
+    SctpNumChunksData *sncd;
+
+    /* Get the sctp_num_chunks data set. */
+    sncd = otn->ds_list[PLUGIN_SCTP_NUM_CHUNKS];
+
+    while(isspace((int)*tok)) tok++;
+
+    /* Parse out the operands of the range. */
+    rval = SnortStrToUNumRng(tok, "<>", &rng);
+    if (rval == SNORT_STR2RNG_SUCCESS)
+    {
+        sncd->num_chunks_min = rng.min;
+        sncd->num_chunks_max = rng.max;
+        sncd->operator = SCTP_NUM_CHUNKS_RANGE;
+    }
+
+    /* Not a ranged value. */
+    else if (rval == SNORT_STR2RNG_NOTRNG)
+    {
+        if (*tok == '>')
+        {
+            tok++;
+            sncd->operator = SCTP_NUM_CHUNKS_GT;
+        }
+        else if(*tok == '<')
+        {
+            tok++;
+            sncd->operator = SCTP_NUM_CHUNKS_LT;
+        }
+        else
+            sncd->operator = SCTP_NUM_CHUNKS_EQUAL;
+    }
+
+    /* Error. */
+    else if (rval == SNORT_STR2RNG_ERROR)
+        goto error;  /* Muah ha!  goto! */
+
+    /* Parse the remainder and store it as the minimum value. */
+    num = strtol(tok, &end, 10);
+    if (*end)
+        goto error;
+
+    sncd->num_chunks_min = num;
+
+    return;
+
+error:
+    FatalError("Error at %s:%d: Invalid 'sctp_num_chunks' argument.\n",
+               file_name, file_line);
+    return;
+}
+
+/**
+ * SctpNumChunksEval - Evaluation function for sctp_num_chunks.
+ * @data: pointer to SctpNumChunksData (as char *).
+ * @p: pointer to Packet structure.
+ *
+ * Evaluates the rule option against the packet pointed to by 'p'.  Returns
+ * either DETECTION_OPTION_MATCH or DETECTION_OPTION_NO_MATCH.
+ */
+int SctpNumChunksEval(void *data, Packet *p)
+{
+    SctpNumChunksData *sncd = (SctpNumChunksData *)data;
+    int rval = DETECTION_OPTION_NO_MATCH;
+    PROFILE_VARS;
+
+    if(!p->sctph || unlikely(!sncd))
+        return rval;
+
+    PREPROC_PROFILE_START(sctpNumChunksPerfStats);
+
+    /* The chunk count is maintained in Packet *p. */
+    switch (sncd->operator)
+    {
+        case SCTP_NUM_CHUNKS_EQUAL:
+            if (sncd->num_chunks_min == p->sctp_chunk_count)
+                rval = DETECTION_OPTION_MATCH;
+            break;
+
+        case SCTP_NUM_CHUNKS_GT:
+            if (sncd->num_chunks_min < p->sctp_chunk_count)
+                rval = DETECTION_OPTION_MATCH;
+            break;
+
+        case SCTP_NUM_CHUNKS_LT:
+            if (sncd->num_chunks_min > p->sctp_chunk_count)
+                rval = DETECTION_OPTION_MATCH;
+            break;
+
+        case SCTP_NUM_CHUNKS_RANGE:
+            if ((sncd->num_chunks_min <= p->sctp_chunk_count) &&
+                (sncd->num_chunks_max >= p->sctp_chunk_count))
+                rval = DETECTION_OPTION_MATCH;
+            break;
+    }
+
+    /* If the test isn't successful, return DETECTION_OPTION_NO_MATCH */
+    PREPROC_PROFILE_END(sctpNumChunksPerfStats);
+    return rval;
+}
diff --git a/src/detection-plugins/sp_sctp_num_chunks.h b/src/detection-plugins/sp_sctp_num_chunks.h
new file mode 100644
index 0000000..07dc7b6
--- /dev/null
+++ b/src/detection-plugins/sp_sctp_num_chunks.h
@@ -0,0 +1,47 @@
+/*
+** Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+** Author: Joshua Kinard
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License Version 2 as
+** published by the Free Software Foundation.  You may not use, modify or
+** distribute this program under any other version of the GNU General
+** Public License.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+/* $Id$ */
+#ifndef __SP_SCTP_NUM_CHUNKS_H__
+#define __SP_SCTP_NUM_CHUNKS_H__
+
+/* Operator definitions for sctp_payload_id. */
+#define SCTP_NUM_CHUNKS_EQUAL               0x01
+#define SCTP_NUM_CHUNKS_LT                  0x02
+#define SCTP_NUM_CHUNKS_GT                  0x03
+#define SCTP_NUM_CHUNKS_RANGE               0x04
+
+/* SctpPayloadId data structure. */
+typedef struct _SctpNumChunksData
+{
+    uint32_t num_chunks_min;
+    uint32_t num_chunks_max;
+    uint8_t operator;
+} SctpNumChunksData;
+
+/* Prototypes. */
+uint32_t SctpNumChunksHash(void *);
+int SctpNumChunksCompare(void *, void *);
+void SctpNumChunksSetup(void);
+void SctpNumChunksInit(struct _SnortConfig *, char *, OptTreeNode *, int);
+void SctpNumChunksParse(char *, OptTreeNode *);
+int SctpNumChunksEval(void *, Packet *);
+
+#endif  /* __SP_SCTP_NUM_CHUNKS_H__ */
diff --git a/src/dynamic-output/plugins/output_lib.h b/src/dynamic-output/plugins/output_lib.h
index f287af8..ea59a17 100644
--- a/src/dynamic-output/plugins/output_lib.h
+++ b/src/dynamic-output/plugins/output_lib.h
@@ -97,6 +97,7 @@ typedef struct _DynamicOutputData
     LogFunc logIPHeader;
     LogFunc logTCPHeader;
     LogFunc logUDPHeader;
+    LogFunc logSCTPHeader;
     LogFunc logICMPHeader;
     LogFunc logArpHeader;
 
diff --git a/src/dynamic-output/plugins/output_plugin.c b/src/dynamic-output/plugins/output_plugin.c
index ae5814a..f67f742 100644
--- a/src/dynamic-output/plugins/output_plugin.c
+++ b/src/dynamic-output/plugins/output_plugin.c
@@ -130,6 +130,7 @@ int initOutputPlugin(void *outputInit)
     outputData.logIPHeader = (LogFunc) &LogIPHeader;
     outputData.logTCPHeader = (LogFunc)&LogTCPHeader;
     outputData.logUDPHeader = (LogFunc)&LogUDPHeader;
+    outputData.logSCTPHeader = (LogFunc)&LogSCTPHeader;
     outputData.logICMPHeader = (LogFunc)&LogICMPHeader;
 
     outputData.textLog_Init = (TextLog_InitFunc)&TextLog_Init;
diff --git a/src/dynamic-plugins/sf_engine/sf_snort_detection_engine.c b/src/dynamic-plugins/sf_engine/sf_snort_detection_engine.c
index bd80ec8..ab6cf1f 100644
--- a/src/dynamic-plugins/sf_engine/sf_snort_detection_engine.c
+++ b/src/dynamic-plugins/sf_engine/sf_snort_detection_engine.c
@@ -1192,6 +1192,7 @@ static void FreeOneRule(void *data)
 
 #define TCP_STRING "tcp"
 #define UDP_STRING "udp"
+#define SCTP_STRING "sctp"
 #define ICMP_STRING "icmp"
 #define IP_STRING "ip"
 char *GetProtoString(int protocol)
@@ -1202,6 +1203,8 @@ char *GetProtoString(int protocol)
         return TCP_STRING;
     case IPPROTO_UDP:
         return UDP_STRING;
+    case IPPROTO_SCTP:
+        return SCTP_STRING;
     case IPPROTO_ICMP:
         return ICMP_STRING;
     default:
diff --git a/src/dynamic-plugins/sf_engine/sf_snort_packet.h b/src/dynamic-plugins/sf_engine/sf_snort_packet.h
index 4a9f761..5c3e78a 100644
--- a/src/dynamic-plugins/sf_engine/sf_snort_packet.h
+++ b/src/dynamic-plugins/sf_engine/sf_snort_packet.h
@@ -392,6 +392,218 @@ typedef struct _ICMP6
 
 struct _SFSnortPacket;
 
+/* Snort redefines much of decoder.h here for unknown reasons. */
+typedef struct _SCTPIpParam2
+{
+    u_int16_t ptype;
+	u_int16_t plen;
+	union
+	{
+		struct _ipv4
+		{
+			u_int32_t address;
+			u_int32_t padding1;
+			u_int32_t padding2;
+			u_int32_t padding3;
+		} ipv4;
+		struct in6_addr ipv6;
+	} address;
+} SCTPIpParam2;
+
+typedef struct _SCTPHeader
+{
+    u_int16_t source_port;          /* source port */
+    u_int16_t destination_port;     /* destination port */
+    u_int32_t verification_tag;     /* verification tag */
+    u_int32_t checksum;             /* checksum */
+	u_int8_t chunk_type;            /* chunk type */
+	u_int8_t chunk_flags;           /* chunk flags */
+	u_int16_t chunk_length;         /* chunk length */
+    void *chunks;                   /* chunk(s) data pointer */
+} SCTPHeader;
+
+typedef union _SCTPChunkData
+{
+    struct data_chunk
+    {
+        u_int32_t tsn;
+        u_int16_t stream_id;
+        u_int16_t stream_seq;
+        u_int32_t pp_id;
+        void *data;
+    } data;
+
+    struct init_chunk
+    {
+        u_int32_t init_tag;
+        u_int32_t a_rwnd;
+        u_int16_t num_out_streams;
+        u_int16_t num_in_streams;
+        u_int32_t tsn;
+        void *parameters;
+    } init;
+
+    struct init_ack_chunk
+    {
+        u_int32_t init_tag;
+        u_int32_t a_rwnd;
+        u_int16_t num_out_streams;
+        u_int16_t num_in_streams;
+        u_int32_t tsn;
+        void *parameters;
+    } init_ack;
+
+    struct sack_chunk
+    {
+        u_int32_t tsn_ack;
+        u_int32_t a_rwnd;
+        u_int16_t n_gap_acks;
+        u_int16_t n_dupe_tsns;
+        void *data;
+    } sack;
+
+    struct heartbeat_chunk
+    {
+        void *parameters;
+    } heartbeat;
+
+    struct heartbeat_ack_chunk
+    {
+        void *parameters;
+    } heartbeat_ack;
+
+    struct abort_chunk
+    {
+        void *causes;
+    } abort;
+
+    struct shutdown_chunk
+    {
+        u_int32_t tsn_ack;
+    } shutdown;
+
+    /* No struct for shutdown_ack. */
+
+    struct error_chunk
+    {
+        void *errors;
+    } error;
+
+    struct cookie_echo_chunk
+    {
+        char *cookie;
+    } cookie_echo;
+
+    /* No struct for cookie_ack. */
+
+    /* No struct for ecne (reserved). */
+
+    /* No struct for cwr (reserved). */
+
+    /* No struct for shutdown_complete. */
+
+    struct auth_chunk
+    {
+        u_int16_t skey_id;
+        u_int16_t hmac_id;
+        void *hmac;
+    } auth;
+
+    struct nr_sack_chunk
+    {
+        u_int32_t cuml_tsn_ack;
+        u_int32_t a_rwnd;
+        u_int16_t num_r_gap_acks;
+        u_int16_t num_nr_gap_acks;
+        u_int16_t num_dupe_tsns;
+        u_int16_t reserved;
+        void *data;
+    } nr_sack;
+
+    struct asconf_ack_chunk
+    {
+        u_int32_t seq;
+        void *parameters;
+    } asconf_ack;
+
+    struct pktdrop_chunk
+    {
+        u_int32_t lbmr;
+        u_int32_t queue_size;
+        u_int16_t trunc_length;
+        u_int16_t reserved;
+        void *dropped_pkt;
+    } pktdrop;
+
+    struct reconfig_chunk
+    {
+        void *params;
+    } reconfig;
+
+    struct pad_chunk
+    {
+        void *data;
+    } pad;
+
+    struct forward_tsn_chunk
+    {
+        u_int32_t new_tsn;
+        void *streams;
+    } forward_tsn;
+
+    struct asconf_chunk
+    {
+        u_int32_t seq;
+        SCTPIpParam2 *addr;
+        void *parameters;
+    } asconf;
+} SCTPChunkData;
+
+/* IPHeader access calls */
+sfip_t *    ip4_ret_src(const struct _SFSnortPacket *);
+sfip_t *    ip4_ret_dst(const struct _SFSnortPacket *);
+uint16_t   ip4_ret_tos(const struct _SFSnortPacket *);
+uint8_t    ip4_ret_ttl(const struct _SFSnortPacket *);
+uint16_t   ip4_ret_len(const struct _SFSnortPacket *);
+uint32_t   ip4_ret_id(const struct _SFSnortPacket *);
+uint8_t    ip4_ret_proto(const struct _SFSnortPacket *);
+uint16_t   ip4_ret_off(const struct _SFSnortPacket *);
+uint8_t    ip4_ret_ver(const struct _SFSnortPacket *);
+uint8_t    ip4_ret_hlen(const struct _SFSnortPacket *);
+
+sfip_t *    orig_ip4_ret_src(const struct _SFSnortPacket *);
+sfip_t *    orig_ip4_ret_dst(const struct _SFSnortPacket *);
+uint16_t   orig_ip4_ret_tos(const struct _SFSnortPacket *);
+uint8_t    orig_ip4_ret_ttl(const struct _SFSnortPacket *);
+uint16_t   orig_ip4_ret_len(const struct _SFSnortPacket *);
+uint32_t   orig_ip4_ret_id(const struct _SFSnortPacket *);
+uint8_t    orig_ip4_ret_proto(const struct _SFSnortPacket *);
+uint16_t   orig_ip4_ret_off(const struct _SFSnortPacket *);
+uint8_t    orig_ip4_ret_ver(const struct _SFSnortPacket *);
+uint8_t    orig_ip4_ret_hlen(const struct _SFSnortPacket *);
+
+sfip_t *    ip6_ret_src(const struct _SFSnortPacket *);
+sfip_t *    ip6_ret_dst(const struct _SFSnortPacket *);
+uint16_t   ip6_ret_toc(const struct _SFSnortPacket *);
+uint8_t    ip6_ret_hops(const struct _SFSnortPacket *);
+uint16_t   ip6_ret_len(const struct _SFSnortPacket *);
+uint32_t   ip6_ret_id(const struct _SFSnortPacket *);
+uint8_t    ip6_ret_next(const struct _SFSnortPacket *);
+uint16_t   ip6_ret_off(const struct _SFSnortPacket *);
+uint8_t    ip6_ret_ver(const struct _SFSnortPacket *);
+uint8_t    ip6_ret_hlen(const struct _SFSnortPacket *);
+
+sfip_t *    orig_ip6_ret_src(const struct _SFSnortPacket *);
+sfip_t *    orig_ip6_ret_dst(const struct _SFSnortPacket *);
+uint16_t   orig_ip6_ret_toc(const struct _SFSnortPacket *);
+uint8_t    orig_ip6_ret_hops(const struct _SFSnortPacket *);
+uint16_t   orig_ip6_ret_len(const struct _SFSnortPacket *);
+uint32_t   orig_ip6_ret_id(const struct _SFSnortPacket *);
+uint8_t    orig_ip6_ret_next(const struct _SFSnortPacket *);
+uint16_t   orig_ip6_ret_off(const struct _SFSnortPacket *);
+uint8_t    orig_ip6_ret_ver(const struct _SFSnortPacket *);
+uint8_t    orig_ip6_ret_hlen(const struct _SFSnortPacket *);
+
 typedef struct _IPH_API
 {
     sfip_t *    (*iph_ret_src)(const struct _SFSnortPacket *);
@@ -485,6 +697,7 @@ typedef struct _SFSnortPacket
     const UDPHeader *inner_udph;   /* if Teredo + UDP, this will be the inner UDP header */
     const UDPHeader *outer_udph;   /* if Teredo + UDP, this will be the outer UDP header */
     const ICMPHeader *icmp_header, *orig_icmp_header;
+    const SCTPHeader *sctp_header, *orig_sctp_header;
 
     const uint8_t *payload;
     const uint8_t *ip_payload;
@@ -618,13 +831,15 @@ typedef struct _SFSnortPacket
 #define PROTO_BIT__ARP      0x0002
 #define PROTO_BIT__TCP      0x0004
 #define PROTO_BIT__UDP      0x0008
-#define PROTO_BIT__ICMP     0x0010
-#define PROTO_BIT__TEREDO   0x0020
+#define PROTO_BIT__SCTP     0x0010
+#define PROTO_BIT__ICMP     0x0020
+#define PROTO_BIT__TEREDO   0x0040
 #define PROTO_BIT__ALL      0xffff
 
 #define IsIP(p) (IPH_IS_VALID(p))
 #define IsTCP(p) (IsIP(p) && p->tcp_header)
 #define IsUDP(p) (IsIP(p) && p->udp_header)
+#define IsSCTP(p) (IsIP(p) && p->sctp_header)
 #define IsICMP(p) (IsIP(p) && p->icmp_header)
 
 #define SET_IP4_VER(ip_header, value) \
diff --git a/src/fpcreate.c b/src/fpcreate.c
index bd24655..f8dce03 100644
--- a/src/fpcreate.c
+++ b/src/fpcreate.c
@@ -230,6 +230,9 @@ static srmm_table_t * ServiceMapNew(void)
     table->udp_to_srv = alloc_srvmap();
     table->udp_to_cli = alloc_srvmap();
 
+    table->sctp_to_srv = alloc_srvmap();
+    table->sctp_to_cli = alloc_srvmap();
+
     table->icmp_to_srv = alloc_srvmap();
     table->icmp_to_cli = alloc_srvmap();
 
@@ -254,6 +257,8 @@ static void ServiceMapFree(srmm_table_t *srvc_map)
     ServiceTableFree(srvc_map->tcp_to_cli);
     ServiceTableFree(srvc_map->udp_to_srv);
     ServiceTableFree(srvc_map->udp_to_cli);
+    ServiceTableFree(srvc_map->sctp_to_srv);
+    ServiceTableFree(srvc_map->sctp_to_cli);
     ServiceTableFree(srvc_map->icmp_to_srv);
     ServiceTableFree(srvc_map->icmp_to_cli);
     ServiceTableFree(srvc_map->ip_to_srv);
@@ -290,6 +295,9 @@ static srmm_table_t * ServicePortGroupMapNew(void)
     table->udp_to_srv = alloc_spgmm();
     table->udp_to_cli = alloc_spgmm();
 
+    table->sctp_to_srv = alloc_spgmm();
+    table->sctp_to_cli = alloc_spgmm();
+
     table->icmp_to_srv = alloc_spgmm();
     table->icmp_to_cli = alloc_spgmm();
 
@@ -334,6 +342,8 @@ static void ServicePortGroupMapFree(srmm_table_t *srvc_pg_map)
     ServicePortGroupTableFree(srvc_pg_map->tcp_to_cli);
     ServicePortGroupTableFree(srvc_pg_map->udp_to_srv);
     ServicePortGroupTableFree(srvc_pg_map->udp_to_cli);
+    ServicePortGroupTableFree(srvc_pg_map->sctp_to_srv);
+    ServicePortGroupTableFree(srvc_pg_map->sctp_to_cli);
     ServicePortGroupTableFree(srvc_pg_map->icmp_to_srv);
     ServicePortGroupTableFree(srvc_pg_map->icmp_to_cli);
     ServicePortGroupTableFree(srvc_pg_map->ip_to_srv);
@@ -401,6 +411,11 @@ static int ServiceMapAddOtn(srmm_table_t *srmm, int proto, char *servicename, Op
         to_srv = srmm->udp_to_srv;
         to_cli = srmm->udp_to_cli;
     }
+    else if( proto == IPPROTO_SCTP)
+    {
+        to_srv = srmm->sctp_to_srv;
+        to_cli = srmm->sctp_to_cli;
+    }
     else if( proto == IPPROTO_ICMP )
     {
         to_srv = srmm->icmp_to_srv;
@@ -466,6 +481,12 @@ int prmFindRuleGroupUdp(PORT_RULE_MAP *prm, int dport, int sport, PORT_GROUP **
     return prmFindRuleGroup( prm, dport, sport, src, dst , gen);
 }
 
+int prmFindRuleGroupSctp(PORT_RULE_MAP *prm, int dport, int sport, PORT_GROUP ** src,
+                        PORT_GROUP **dst , PORT_GROUP ** gen)
+{
+    return prmFindRuleGroup( prm, dport, sport, src, dst , gen);
+}
+
 void free_detection_option_root(void **existing_tree)
 {
     detection_option_tree_root_t *root;
@@ -1838,7 +1859,7 @@ static int fpAddPortGroupRule(SnortConfig *sc, PORT_GROUP *pg, OptTreeNode *otn,
  *
  *  Generation of PortRuleMaps and data is done differently.
  *
- *    1) Build tcp/udp/icmp/ip src and dst PORT_GROUP objects based on the PortList Objects rules.
+ *    1) Build tcp/udp/sctp/icmp/ip src and dst PORT_GROUP objects based on the PortList Objects rules.
  *
  *    2) For each protocols PortList objects walk it's ports and assign the PORT_RULE_MAP src and dst
  *         PORT_GROUP[port] array pointers to that PortList objects PORT_GROUP.
@@ -1848,7 +1869,7 @@ static int fpAddPortGroupRule(SnortConfig *sc, PORT_GROUP *pg, OptTreeNode *otn,
  *    Each PortList Object will be translated into a PORT_GROUP, than pointed to by the
  *    PORT_GROUP array in the PORT_RULE_MAP for the procotocol
  *
- *    protocol = tcp, udp, ip, icmp - one port_rule_map for each of these protocols
+ *    protocol = tcp, udp, sctp, ip, icmp - one port_rule_map for each of these protocols
  *    { create a port_rule_map
  *      dst port processing
  *          for each port-list object create a port_group object
@@ -2017,6 +2038,13 @@ static int fpCreateRuleMaps(SnortConfig *sc, rule_port_tables_t *p)
     if (fpCreateInitRuleMap(sc->prmUdpRTNX, p->udp_src, p->udp_dst, p->udp_anyany,p->udp_nocontent))
         return -1;
 
+    sc->prmSctpRTNX = prmNewMap();
+    if (sc->prmSctpRTNX == NULL)
+        return -1;
+
+    if (fpCreateInitRuleMap(sc->prmSctpRTNX, p->sctp_src, p->sctp_dst, p->sctp_anyany,p->sctp_nocontent))
+        return -1;
+
     sc->prmIpRTNX = prmNewMap();
     if (sc->prmIpRTNX == NULL)
         return 1;
@@ -2051,6 +2079,12 @@ static void fpFreeRuleMaps(SnortConfig *sc)
         sc->prmUdpRTNX = NULL;
     }
 
+    if (sc->prmSctpRTNX != NULL)
+    {
+        free(sc->prmSctpRTNX);
+        sc->prmSctpRTNX = NULL;
+    }
+
     if (sc->prmIpRTNX != NULL)
     {
         free(sc->prmIpRTNX);
@@ -2529,7 +2563,7 @@ static int fpCreatePortGroups(SnortConfig *sc, rule_port_tables_t *p)
 
     if (fpCreatePortTablePortGroups(sc, p->udp_dst, add_any_any))
     {
-        LogMessage("fpCreatePorTablePortGroups failed-udp_src\n");
+        LogMessage("fpCreatePorTablePortGroups failed-udp_dst\n");
         return -1;
     }
 
@@ -2538,7 +2572,7 @@ static int fpCreatePortGroups(SnortConfig *sc, rule_port_tables_t *p)
 
     if (fpCreatePortObject2PortGroup(sc, po2, 0))
     {
-        LogMessage("fpCreatePorTablePortGroups failed-udp_src\n");
+        LogMessage("fpCreatePorTablePortGroups failed-udp_any-any\n");
         return -1;
     }
 
@@ -2548,6 +2582,47 @@ static int fpCreatePortGroups(SnortConfig *sc, rule_port_tables_t *p)
     //LogMessage("fpcreate: calling PortObjectFree2(po2), line = %d\n",__LINE__ );
     PortObject2Free(po2);
 
+    /* SCTP */
+    po2 = PortObject2Dup(p->sctp_anyany);
+    if (po2 == NULL )
+        FatalError("Could not create a PortObject version 2 for sctp-any-any rules\n!");
+
+    if (!fpDetectSplitAnyAny(fp))
+        add_any_any = po2;
+
+    if (fpDetectGetDebugPrintRuleGroupBuildDetails(fp))
+        LogMessage("\nSCTP-SRC ");
+
+    if (fpCreatePortTablePortGroups(sc, p->sctp_src, add_any_any))
+    {
+        LogMessage("fpCreatePorTablePortGroups failed-sctp_src\n");
+        return -1;
+    }
+
+    if (fpDetectGetDebugPrintRuleGroupBuildDetails(fp))
+        LogMessage("\nSCTP-DST ");
+
+    if (fpCreatePortTablePortGroups(sc, p->sctp_dst, add_any_any))
+    {
+        LogMessage("fpCreatePorTablePortGroups failed-sctp_dst\n");
+        return -1;
+    }
+
+    if (fpDetectGetDebugPrintRuleGroupBuildDetails(fp))
+        LogMessage("\nSCTP-ANYANY ");
+
+    if (fpCreatePortObject2PortGroup(sc, po2, 0))
+    {
+        LogMessage("fpCreatePorTablePortGroups failed-sctp_any-any\n");
+        return -1;
+    }
+
+    p->sctp_anyany->data = po2->data;
+    p->sctp_anyany->data_free = fpDeletePortGroup;
+    po2->data = 0;
+    //LogMessage("fpcreate: calling PortObjectFree2(po2), line = %d\n",__LINE__ );
+    PortObject2Free(po2);
+
     /* ICMP */
     po2 = PortObject2Dup(p->icmp_anyany);
     if (po2 == NULL)
@@ -2570,7 +2645,7 @@ static int fpCreatePortGroups(SnortConfig *sc, rule_port_tables_t *p)
 
     if (fpCreatePortTablePortGroups(sc, p->icmp_dst, add_any_any))
     {
-        LogMessage("fpCreatePorTablePortGroups failed-icmp_src\n");
+        LogMessage("fpCreatePorTablePortGroups failed-icmp_dst\n");
         return -1;
     }
 
@@ -2670,7 +2745,8 @@ void fpWalkOtns(int enabled, OtnWalkFcn fcn)
                 continue;
 
             if ((rtn->proto == IPPROTO_TCP) || (rtn->proto == IPPROTO_UDP)
-                || (rtn->proto == IPPROTO_ICMP) || (rtn->proto == ETHERNET_TYPE_IP))
+                || (rtn->proto == IPPROTO_SCTP) || (rtn->proto == IPPROTO_ICMP)
+                || (rtn->proto == ETHERNET_TYPE_IP))
             {
                 //do operation
                 if ( enabled && (otn->rule_state != RULE_STATE_ENABLED) )
@@ -2707,7 +2783,8 @@ static int fpCreateServiceMaps(SnortConfig *sc)
             rtn = getRtnFromOtn(otn, policyId);
 
             if (rtn && ((rtn->proto == IPPROTO_TCP) || (rtn->proto == IPPROTO_UDP)
-                    || (rtn->proto == IPPROTO_ICMP) || (rtn->proto == ETHERNET_TYPE_IP)))
+                    || (rtn->proto == IPPROTO_SCTP) || (rtn->proto == IPPROTO_ICMP)
+                    || (rtn->proto == ETHERNET_TYPE_IP)))
             {
                 //do operation
 
@@ -2885,6 +2962,11 @@ static void fpCreateServiceMapPortGroups(SnortConfig *sc)
     fpBuildServicePortGroups(sc, sc->spgmmTable->udp_to_cli, sc->sopgTable->udp_to_cli,
                              sc->srmmTable->udp_to_cli, fp);
 
+    fpBuildServicePortGroups(sc, sc->spgmmTable->sctp_to_srv, sc->sopgTable->sctp_to_srv,
+                             sc->srmmTable->sctp_to_srv, fp);
+    fpBuildServicePortGroups(sc, sc->spgmmTable->sctp_to_cli, sc->sopgTable->sctp_to_cli,
+                             sc->srmmTable->sctp_to_cli, fp);
+
     fpBuildServicePortGroups(sc, sc->spgmmTable->icmp_to_srv, sc->sopgTable->icmp_to_srv,
                              sc->srmmTable->icmp_to_srv, fp);
     fpBuildServicePortGroups(sc, sc->spgmmTable->icmp_to_cli, sc->sopgTable->icmp_to_cli,
@@ -2925,6 +3007,14 @@ PORT_GROUP * fpGetServicePortGroupByOrdinal(sopg_table_t *sopg, int proto, int d
 
            break;
 
+       case IPPROTO_SCTP:
+           if (dir == TO_SERVER)
+               pg = sopg->sctp_to_srv[proto_ordinal];
+           else
+               pg = sopg->sctp_to_cli[proto_ordinal];
+
+           break;
+
        case IPPROTO_ICMP:
            if (dir == TO_SERVER)
                pg = sopg->icmp_to_srv[proto_ordinal];
@@ -3004,6 +3094,9 @@ static void fpPrintServiceRuleMaps(srmm_table_t *service_map)
     fpPrintServiceRuleMapTable( service_map->udp_to_srv,  "udp to server" );
     fpPrintServiceRuleMapTable( service_map->udp_to_cli,  "udp to client" );
 
+    fpPrintServiceRuleMapTable( service_map->sctp_to_srv,  "sctp to server" );
+    fpPrintServiceRuleMapTable( service_map->sctp_to_cli,  "sctp to client" );
+
     fpPrintServiceRuleMapTable( service_map->icmp_to_srv, "icmp to server" );
     fpPrintServiceRuleMapTable( service_map->icmp_to_cli, "icmp to client" );
 
@@ -3031,6 +3124,11 @@ void fpPrintServicePortGroupSummary(srmm_table_t *srvc_pg_map)
     if(srvc_pg_map->udp_to_cli->count)
     LogMessage("| udp to cient   : %d services\n",srvc_pg_map->udp_to_cli->count);
 
+    if(srvc_pg_map->sctp_to_srv->count)
+    LogMessage("| sctp to server  : %d services\n",srvc_pg_map->sctp_to_srv->count);
+    if(srvc_pg_map->sctp_to_cli->count)
+    LogMessage("| sctp to cient   : %d services\n",srvc_pg_map->sctp_to_cli->count);
+
     if(srvc_pg_map->icmp_to_srv->count)
     LogMessage("| icmp to server : %d services\n",srvc_pg_map->icmp_to_srv->count);
     if(srvc_pg_map->icmp_to_cli->count)
@@ -3223,6 +3321,10 @@ void fpShowEventStats(SnortConfig *sc)
     prmShowEventStats(sc->prmUdpRTNX);
 
     LogMessage("\n");
+    LogMessage("** SCTP Event Stats --\n");
+    prmShowEventStats(sc->prmSctpRTNX);
+
+    LogMessage("\n");
     LogMessage("** ICMP Event Stats --\n");
     prmShowEventStats(sc->prmIcmpRTNX);
 
diff --git a/src/fpcreate.h b/src/fpcreate.h
index 9c67777..0f565e6 100644
--- a/src/fpcreate.h
+++ b/src/fpcreate.h
@@ -46,7 +46,7 @@ struct _SnortConfig;
 
 /*
  *  Max Number of Protocols Supported by Rules in fpcreate.c
- *  for tcp,udp,icmp,ip ... this is an array dimesnion used to
+ *  for tcp,udp,sctp,icmp,ip ... this is an array dimesnion used to
  *  map protocol-ordinals to port_groups ...
  */
 /* This is now defined in sftarget_protocol_refererence.h"
@@ -117,6 +117,9 @@ typedef struct
   SFGHASH * udp_to_srv;
   SFGHASH * udp_to_cli;
 
+  SFGHASH * sctp_to_srv;
+  SFGHASH * sctp_to_cli;
+
   SFGHASH * icmp_to_srv;
   SFGHASH * icmp_to_cli;
 
@@ -136,6 +139,9 @@ typedef struct
   PORT_GROUP *udp_to_srv[MAX_PROTOCOL_ORDINAL];
   PORT_GROUP *udp_to_cli[MAX_PROTOCOL_ORDINAL];
 
+  PORT_GROUP *sctp_to_srv[MAX_PROTOCOL_ORDINAL];
+  PORT_GROUP *sctp_to_cli[MAX_PROTOCOL_ORDINAL];
+
   PORT_GROUP *icmp_to_srv[MAX_PROTOCOL_ORDINAL];
   PORT_GROUP *icmp_to_cli[MAX_PROTOCOL_ORDINAL];
 
@@ -168,6 +174,7 @@ void FastPatternConfigFree(FastPatternConfig *);
 */
 int prmFindRuleGroupTcp(PORT_RULE_MAP *, int, int, PORT_GROUP **, PORT_GROUP **, PORT_GROUP **);
 int prmFindRuleGroupUdp(PORT_RULE_MAP *, int, int, PORT_GROUP **, PORT_GROUP **, PORT_GROUP **);
+int prmFindRuleGroupSctp(PORT_RULE_MAP *, int, int, PORT_GROUP **, PORT_GROUP **, PORT_GROUP **);
 int prmFindRuleGroupIp(PORT_RULE_MAP *, int, PORT_GROUP **, PORT_GROUP **);
 int prmFindRuleGroupIcmp(PORT_RULE_MAP *, int, PORT_GROUP **, PORT_GROUP **);
 
diff --git a/src/fpdetect.c b/src/fpdetect.c
index ba8d898..24b3fcf 100644
--- a/src/fpdetect.c
+++ b/src/fpdetect.c
@@ -104,6 +104,7 @@ static inline int fpEvalHeaderIp(Packet *p, int ip_proto, OTNX_MATCH_DATA *);
 static inline int fpEvalHeaderIcmp(Packet *p, OTNX_MATCH_DATA *);
 static inline int fpEvalHeaderTcp(Packet *p, OTNX_MATCH_DATA *);
 static inline int fpEvalHeaderUdp(Packet *p, OTNX_MATCH_DATA *);
+static inline int fpEvalHeaderSctp(Packet *p, OTNX_MATCH_DATA *);
 static inline int fpEvalHeaderSW(PORT_GROUP *port_group, Packet *p,
                                  int check_ports, char ip_rule, OTNX_MATCH_DATA *);
 static int rule_tree_match (void* id, void * tree, int index, void * data, void *neg_list );
@@ -1031,6 +1032,7 @@ void printRuleFmt1( SnortConfig *sc, OptTreeNode * otn )
 
     if(      rtn->proto== IPPROTO_TCP     )LogMessage("tcp  ");
     else if( rtn->proto== IPPROTO_UDP     )LogMessage("udp  ");
+    else if( rtn->proto== IPPROTO_SCTP    )LogMessage("sctp ");
     else if( rtn->proto== IPPROTO_ICMP    )LogMessage("icmp ");
     else if( rtn->proto== ETHERNET_TYPE_IP)LogMessage("ip   ");
 
@@ -1063,7 +1065,7 @@ void printRuleFmt1( SnortConfig *sc, OptTreeNode * otn )
 **  FORMAL INPUTS
 **    PORT_GROUP * - the port group to inspect
 **    Packet *     - the packet to inspect
-**    int          - whether src/dst ports should be checked (udp/tcp or icmp)
+**    int          - whether src/dst ports should be checked (udp/tcp/sctp or icmp)
 **    char         - whether the rule is an IP rule (change the packet payload pointer)
 **
 **  FORMAL OUTPUTS
@@ -1556,6 +1558,84 @@ static inline int fpEvalHeaderTcp(Packet *p, OTNX_MATCH_DATA *omd)
 }
 
 /*
+** fpEvalHeaderSctp::
+*/
+static inline int fpEvalHeaderSctp(Packet *p, OTNX_MATCH_DATA *omd)
+{
+    PORT_GROUP *src = NULL, *dst = NULL, *gen = NULL;
+
+#ifdef TARGET_BASED
+    if (IsAdaptiveConfigured(getRuntimePolicy()))
+    {
+        /* Check for a service/protocol ordinal for this packet */
+        int16_t proto_ordinal = GetProtocolReference(p);
+
+        DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "proto_ordinal=%d\n", proto_ordinal););
+
+        if (proto_ordinal > 0)
+        {
+            /* Grab the generic group -- the any-any rules */
+            prmFindGenericRuleGroup(snort_conf->prmTcpRTNX, &gen);
+
+            /* TODO:  To From Server ?, else we apply  */
+            dst = fpGetServicePortGroupByOrdinal(snort_conf->sopgTable, IPPROTO_SCTP,
+                                                 TO_SERVER, proto_ordinal);
+            src = fpGetServicePortGroupByOrdinal(snort_conf->sopgTable, IPPROTO_SCTP,
+                                                 TO_CLIENT, proto_ordinal);
+
+            DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE,
+                        "fpEvalHeaderSctpp:targetbased-ordinal-lookup: "
+                        "sport=%d, dport=%d, proto_ordinal=%d, src:%x, "
+                        "dst:%x, gen:%x\n",p->sp,p->dp,proto_ordinal,src,dst,gen););
+        }
+    }
+
+    if ((src == NULL) && (dst == NULL))
+    {
+        /* we did not have a target based port group, use ports */
+        if (!prmFindRuleGroupSctp(snort_conf->prmSctpRTNX, p->dp, p->sp, &src, &dst, &gen))
+            return 0;
+
+        DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE,
+                    "fpEvalHeaderSctp: sport=%d, dport=%d, "
+                    "src:%x, dst:%x, gen:%x\n",p->sp,p->dp,src,dst,gen););
+    }
+#else
+    if (!prmFindRuleGroupSctp(snort_conf->prmSctpRTNX, p->dp, p->sp, &src, &dst, &gen))
+        return 0;
+#endif
+
+    if (fpDetectGetDebugPrintNcRules(snort_conf->fast_pattern_config))
+    {
+        LogMessage(
+            "fpEvalHeaderSctp: sport=%d, dport=%d, src:%p, dst:%p, gen:%p\n",
+             p->sp, p->dp, (void*)src, (void*)dst, (void*)gen);
+    }
+
+    InitMatchInfo(omd);
+
+    if (dst != NULL)
+    {
+        if (fpEvalHeaderSW(dst, p, 1, 0, omd))
+            return 1;
+    }
+
+    if (src != NULL)
+    {
+        if (fpEvalHeaderSW(src, p, 1, 0, omd))
+            return 1;
+    }
+
+    if (gen != NULL)
+    {
+        if (fpEvalHeaderSW(gen, p, 1, 0, omd))
+            return 1;
+    }
+
+    return fpFinalSelectEvent(omd, p);
+}
+
+/*
 **  fpEvalHeaderICMP::
 */
 static inline int fpEvalHeaderIcmp(Packet *p, OTNX_MATCH_DATA *omd)
@@ -1626,10 +1706,10 @@ static inline int fpEvalHeaderIp(Packet *p, int ip_proto, OTNX_MATCH_DATA *omd)
 **
 **  DESCRIPTION
 **    This function is the interface to the Detect() routine.  Here
-**    the IP protocol is processed.  If it is TCP, UDP, or ICMP, we
-**    process the both that particular ruleset and the IP ruleset
+**    the IP protocol is processed.  If it is TCP, UDP, SCTP, or ICMP,
+**    we process the both that particular ruleset and the IP ruleset
 **    with in the fpEvalHeader for that protocol.  If the protocol
-**    is not TCP, UDP, or ICMP, we just process the packet against
+**    is not TCP, UDP, SCTP, or ICMP, we just process the packet against
 **    the IP rules at the end of the fpEvalPacket routine.  Since
 **    we are using a setwise methodology for snort rules, both the
 **    network layer rules and the transport layer rules are done
@@ -1707,6 +1787,18 @@ int fpEvalPacket(Packet *p)
 
             return fpEvalHeaderUdp(p, omd);
 
+        case IPPROTO_SCTP:
+            DEBUG_WRAP(DebugMessage(DEBUG_DETECT,
+                        "Detecting on SctpList\n"););
+
+            if(p->sctph == NULL)
+            {
+                ip_proto = -1;
+                break;
+            }
+
+            return fpEvalHeaderSctp(p, omd);
+
         case IPPROTO_ICMPV6:
         case IPPROTO_ICMP:
             DEBUG_WRAP(DebugMessage(DEBUG_DETECT,
@@ -1725,7 +1817,7 @@ int fpEvalPacket(Packet *p)
     }
 
     /*
-    **  No Match on TCP/UDP, Do IP
+    **  No Match on TCP/UDP/SCTP, Do IP
     */
     return fpEvalHeaderIp(p, ip_proto, omd);
 }
diff --git a/src/generators.h b/src/generators.h
index 554efbf..b5db790 100644
--- a/src/generators.h
+++ b/src/generators.h
@@ -229,6 +229,68 @@ enum {
     DECODE_INDEX_MAX
 };
 
+/* SCTP starts at 320 to leave room for other enums */
+#define     DECODE_SCTP_DGRAM_LT_SCTPHDR                320
+#define     DECODE_SCTP_CSUM_IS_ADLER32                 321
+#define     DECODE_SCTP_CHUNK_ZERO_LENGTH               322
+#define     DECODE_SCTP_CHUNK_LEN_GT_DGRAM              323
+#define     DECODE_SCTP_DATA_C_BAD_LEN                  324
+#define     DECODE_SCTP_DATA_C_RSVD_NZERO               325
+#define     DECODE_SCTP_INIT_C_BAD_LEN                  326
+#define     DECODE_SCTP_INIT_C_FLAGS_NZERO              327
+#define     DECODE_SCTP_INIT_C_ITAG_ZERO                328
+#define     DECODE_SCTP_INIT_C_VTAG_NZERO               329
+#define     DECODE_SCTP_INIT_C_NOUTSTR_ZERO             330
+#define     DECODE_SCTP_INIT_C_NINSTR_ZERO              331
+#define     DECODE_SCTP_INIT_ACK_C_BAD_LEN              332
+#define     DECODE_SCTP_INIT_ACK_C_FLAGS_NZERO          333
+#define     DECODE_SCTP_INIT_ACK_C_ITAG_ZERO            334
+#define     DECODE_SCTP_INIT_ACK_C_NOUTSTR_ZERO         335
+#define     DECODE_SCTP_INIT_ACK_C_NINSTR_ZERO          336
+#define     DECODE_SCTP_SACK_C_BAD_LEN                  337
+#define     DECODE_SCTP_SACK_C_FLAGS_NZERO              338
+#define     DECODE_SCTP_HEARTBEAT_C_BAD_LEN             339
+#define     DECODE_SCTP_HEARTBEAT_C_FLAGS_NZERO         340
+#define     DECODE_SCTP_HEARTBEAT_ACK_C_BAD_LEN         341
+#define     DECODE_SCTP_HEARTBEAT_ACK_C_FLAGS_NZERO     342
+#define     DECODE_SCTP_ABORT_C_BAD_LEN                 343
+#define     DECODE_SCTP_ABORT_C_RSVD_NZERO              344
+#define     DECODE_SCTP_SHUTDOWN_C_BAD_LEN              345
+#define     DECODE_SCTP_SHUTDOWN_C_FLAGS_NZERO          346
+#define     DECODE_SCTP_SHUTDOWN_ACK_C_BAD_LEN          347
+#define     DECODE_SCTP_SHUTDOWN_ACK_C_FLAGS_NZERO      348
+#define     DECODE_SCTP_ERROR_C_BAD_LEN                 349
+#define     DECODE_SCTP_ERROR_C_FLAGS_NZERO             350
+#define     DECODE_SCTP_COOKIE_ECHO_C_BAD_LEN           351
+#define     DECODE_SCTP_COOKIE_ECHO_C_FLAGS_NZERO       352
+#define     DECODE_SCTP_COOKIE_ACK_C_BAD_LEN            353
+#define     DECODE_SCTP_COOKIE_ACK_C_FLAGS_NZERO        354
+#define     DECODE_SCTP_ECNE_C_BAD_LEN                  355
+#define     DECODE_SCTP_ECNE_C_FLAGS_NZERO              356
+#define     DECODE_SCTP_CWR_C_BAD_LEN                   357
+#define     DECODE_SCTP_CWR_C_FLAGS_NZERO               358
+#define     DECODE_SCTP_SHUTDOWN_COMPLETE_C_BAD_LEN     359
+#define     DECODE_SCTP_SHUTDOWN_COMPLETE_C_RSVD_NZERO  360
+#define     DECODE_SCTP_AUTH_C_BAD_LEN                  361
+#define     DECODE_SCTP_AUTH_C_FLAGS_NZERO              362
+#define     DECODE_SCTP_NR_SACK_C_BAD_LEN               363
+#define     DECODE_SCTP_NR_SACK_C_FLAGS_NZERO           364
+#define     DECODE_SCTP_ASCONF_ACK_C_BAD_LEN            365
+#define     DECODE_SCTP_ASCONF_ACK_C_FLAGS_NZERO        366
+#define     DECODE_SCTP_PKTDROP_C_BAD_LEN               367
+#define     DECODE_SCTP_PKTDROP_C_FLAGS_NZERO           368
+#define     DECODE_SCTP_PKTDROP_C_RSVD_NZERO            369
+#define     DECODE_SCTP_RECONFIG_C_BAD_LEN              370
+#define     DECODE_SCTP_RECONFIG_C_FLAGS_NZERO          371
+#define     DECODE_SCTP_RECONFIG_C_GT_2_PARAMS          372
+#define     DECODE_SCTP_RECONFIG_C_INVALID_PARAMS       373
+#define     DECODE_SCTP_PAD_C_BAD_LEN                   374
+#define     DECODE_SCTP_PAD_C_FLAGS_NZERO               375
+#define     DECODE_SCTP_FORWARD_TSN_C_BAD_LEN           376
+#define     DECODE_SCTP_FORWARD_TSN_C_FLAGS_NZERO       377
+#define     DECODE_SCTP_ASCONF_C_BAD_LEN                378
+#define     DECODE_SCTP_ASCONF_C_FLAGS_NZERO            379
+
 
 //-----------------------------------------------------
 /*
@@ -328,6 +390,15 @@ enum {
 
 #define     PSNG_OPEN_PORT                         27
 
+#define     PSNG_SCTP_PORTSCAN                     28
+#define     PSNG_SCTP_DECOY_PORTSCAN               29
+#define     PSNG_SCTP_PORTSWEEP                    30
+#define     PSNG_SCTP_DISTRIBUTED_PORTSCAN         31
+#define     PSNG_SCTP_FILTERED_PORTSCAN            32
+#define     PSNG_SCTP_FILTERED_DECOY_PORTSCAN      33
+#define     PSNG_SCTP_PORTSWEEP_FILTERED           34
+#define     PSNG_SCTP_FILTERED_DISTRIBUTED_PORTSCAN     35
+
 #define GENERATOR_SPP_FRAG3                       123
 #define     FRAG3_IPOPTIONS                         1
 #define     FRAG3_TEARDROP                          2
@@ -753,6 +824,68 @@ enum {
 #define DECODE_AUTH_HDR_TRUNC_STR "(snort_decoder) WARNING: truncated authentication header"
 #define DECODE_AUTH_HDR_BAD_LEN_STR "(snort_decoder) WARNING: authentication header bad length"
 
+/* SCTP Decoder strings */
+#define DECODE_SCTP_DGRAM_LT_SCTPHDR_STR "(snort_decoder) WARNING: SCTP packet len is smaller than 12 bytes!"
+#define DECODE_SCTP_CSUM_IS_ADLER32_STR "(snort_decoder) WARNING: SCTP Checksum is Adler32, not CRC32c!"
+#define DECODE_SCTP_CHUNK_ZERO_LENGTH_STR "(snort_decoder) WARNING: SCTP chunk length is zero bytes!"
+#define DECODE_SCTP_CHUNK_LEN_GT_DGRAM_STR "(snort_decoder) WARNING: SCTP chunk length is greater than remainder of the packet!"
+#define DECODE_SCTP_DATA_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP DATA chunk length is less than 17 bytes!"
+#define DECODE_SCTP_DATA_C_RSVD_NZERO_STR "(snort_decoder) WARNING: SCTP DATA chunk reserved flags field is non-zero!"
+#define DECODE_SCTP_INIT_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP INIT chunk length is less than 20 bytes!"
+#define DECODE_SCTP_INIT_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP INIT chunk flags field is non-zero!"
+#define DECODE_SCTP_INIT_C_ITAG_ZERO_STR "(snort_decoder) WARNING: SCTP INIT chunk and initiate tag is == 0!"
+#define DECODE_SCTP_INIT_C_VTAG_NZERO_STR "(snort_decoder) WARNING: SCTP INIT chunk and verification tag is > 0!"
+#define DECODE_SCTP_INIT_C_NOUTSTR_ZERO_STR "(snort_decoder) WARNING: SCTP INIT chunk # of outbound streams is zero!"
+#define DECODE_SCTP_INIT_C_NINSTR_ZERO_STR "(snort_decoder) WARNING: SCTP INIT chunk # of inbound streams is zero!"
+#define DECODE_SCTP_INIT_ACK_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP INIT_ACK chunk length is less than 20 bytes!"
+#define DECODE_SCTP_INIT_ACK_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP INIT_ACK chunk flags field is non-zero!"
+#define DECODE_SCTP_INIT_ACK_C_ITAG_ZERO_STR "(snort_decoder) WARNING: SCTP INIT_ACK chunk and initiate tag is == 0!"
+#define DECODE_SCTP_INIT_ACK_C_NOUTSTR_ZERO_STR "(snort_decoder) WARNING: SCTP INIT_ACK chunk # of outbound streams is zero!"
+#define DECODE_SCTP_INIT_ACK_C_NINSTR_ZERO_STR "(snort_decoder) WARNING: SCTP INIT_ACK chunk # of inbound streams is zero!"
+#define DECODE_SCTP_SACK_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP SACK chunk length is less than 16 bytes!"
+#define DECODE_SCTP_SACK_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP SACK chunk flags field is non-zero!"
+#define DECODE_SCTP_HEARTBEAT_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP HEARTBEAT chunk length is less than 8 bytes!"
+#define DECODE_SCTP_HEARTBEAT_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP HEARTBEAT chunk flags field is non-zero!"
+#define DECODE_SCTP_HEARTBEAT_ACK_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP HEARTBEAT_ACK chunk length is less than 8 bytes!"
+#define DECODE_SCTP_HEARTBEAT_ACK_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP HEARTBEAT_ACK chunk flags field is non-zero!"
+#define DECODE_SCTP_ABORT_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP ABORT chunk length is less than 4 bytes!"
+#define DECODE_SCTP_ABORT_C_RSVD_NZERO_STR "(snort_decoder) WARNING: SCTP ABORT chunk reserved flags field is non-zero!"
+#define DECODE_SCTP_SHUTDOWN_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP SHUTDOWN chunk length is less than 8 bytes!"
+#define DECODE_SCTP_SHUTDOWN_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP SHUTDOWN chunk flags field is non-zero!"
+#define DECODE_SCTP_SHUTDOWN_ACK_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP SHUTDOWN_ACK chunk length is less than 4 bytes!"
+#define DECODE_SCTP_SHUTDOWN_ACK_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP SHUTDOWN_ACK chunk flags field is non-zero!"
+#define DECODE_SCTP_ERROR_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP ERROR chunk length is less than 8 bytes!"
+#define DECODE_SCTP_ERROR_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP ERROR chunk flags field is non-zero!"
+#define DECODE_SCTP_COOKIE_ECHO_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP COOKIE_ECHO chunk length is less than 4 bytes!"
+#define DECODE_SCTP_COOKIE_ECHO_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP COOKIE_ECHO chunk flags field is non-zero!"
+#define DECODE_SCTP_COOKIE_ACK_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP COOKIE_ACK chunk length is less than 4 bytes!"
+#define DECODE_SCTP_COOKIE_ACK_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP COOKIE_ACK chunk flags field is non-zero!"
+#define DECODE_SCTP_ECNE_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP ECNE chunk length is less than 8 bytes!"
+#define DECODE_SCTP_ECNE_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP ECNE chunk flags field is non-zero!"
+#define DECODE_SCTP_CWR_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP CWR chunk length is less than 8 bytes!"
+#define DECODE_SCTP_CWR_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP CWR chunk flags field is non-zero!"
+#define DECODE_SCTP_SHUTDOWN_COMPLETE_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP SHUTDOWN_COMPLETE chunk length is less than 4 bytes!"
+#define DECODE_SCTP_SHUTDOWN_COMPLETE_C_RSVD_NZERO_STR "(snort_decoder) WARNING: SCTP SHUTDOWN_COMPLETE chunk reserved flags field is non-zero!"
+#define DECODE_SCTP_AUTH_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP AUTH chunk length is less than 4 bytes!"
+#define DECODE_SCTP_AUTH_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP AUTH chunk flags field is non-zero!"
+#define DECODE_SCTP_NR_SACK_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP NR-SACK chunk length is less than 20 bytes!"
+#define DECODE_SCTP_NR_SACK_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP NR-SACK chunk flags field is non-zero!"
+#define DECODE_SCTP_ASCONF_ACK_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP ASCONF_ACK chunk length is less than 8 bytes!"
+#define DECODE_SCTP_ASCONF_ACK_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP ASCONF_ACK chunk flags field is non-zero!"
+#define DECODE_SCTP_PKTDROP_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP PKTDROP chunk length is less than 8 bytes!"
+#define DECODE_SCTP_PKTDROP_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP PKTDROP chunk flags field is non-zero!"
+#define DECODE_SCTP_PKTDROP_C_RSVD_NZERO_STR "(snort_decoder) WARNING: SCTP PKTDROP reserved field is non-zero!"
+#define DECODE_SCTP_RECONFIG_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP RECONFIG chunk length is less than 4 bytes!"
+#define DECODE_SCTP_RECONFIG_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP RECONFIG chunk flags field is non-zero!"
+#define DECODE_SCTP_RECONFIG_C_GT_2_PARAMS_STR "(snort_decoder) WARNING: SCTP RECONFIG chunk has >2 params!"
+#define DECODE_SCTP_RECONFIG_C_INVALID_PARAMS_STR "(snort_decoder) WARNING: SCTP RECONFIG chunk has invalid params!"
+#define DECODE_SCTP_PAD_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP PAD chunk length is less than 4 bytes!"
+#define DECODE_SCTP_PAD_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP PAD chunk flags field is non-zero!"
+#define DECODE_SCTP_FORWARD_TSN_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP FORWARD_TSN chunk length is less than 12 bytes!"
+#define DECODE_SCTP_FORWARD_TSN_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP FORWARD_TSN chunk flags field is non-zero!"
+#define DECODE_SCTP_ASCONF_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP ASCONF chunk length is less than 12 bytes!"
+#define DECODE_SCTP_ASCONF_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP ASCONF chunk flags field is non-zero!"
+
 /*  RPC decode preprocessor strings */
 #define RPC_FRAG_TRAFFIC_STR "(spp_rpc_decode) Fragmented RPC Records"
 #define RPC_MULTIPLE_RECORD_STR "(spp_rpc_decode) Multiple RPC Records"
@@ -787,6 +920,15 @@ enum {
 #define PSNG_UDP_FILTERED_DISTRIBUTED_PORTSCAN_STR "(portscan) UDP Filtered Distributed Portscan"
 #define PSNG_UDP_PORTSWEEP_FILTERED_STR "(portscan) UDP Filtered Portsweep"
 
+#define PSNG_SCTP_PORTSCAN_STR "(portscan) SCTP Portscan"
+#define PSNG_SCTP_DECOY_PORTSCAN_STR "(portscan) SCTP Decoy Portscan"
+#define PSNG_SCTP_PORTSWEEP_STR "(portscan) SCTP Portsweep"
+#define PSNG_SCTP_DISTRIBUTED_PORTSCAN_STR "(portscan) SCPT Distributed Portscan"
+#define PSNG_SCTP_FILTERED_PORTSCAN_STR "(portscan) SCTP Filtered Portscan"
+#define PSNG_SCTP_FILTERED_DECOY_PORTSCAN_STR "(portscan) SCPT Filtered Decoy Portscan"
+#define PSNG_SCTP_FILTERED_DISTRIBUTED_PORTSCAN_STR "(portscan) SCTP Filtered Distributed Portscan"
+#define PSNG_SCTP_PORTSWEEP_FILTERED_STR "(portscan) SCTP Filtered Portsweep"
+
 #define PSNG_ICMP_PORTSWEEP_STR "(portscan) ICMP Sweep"
 #define PSNG_ICMP_PORTSWEEP_FILTERED_STR "(portscan) ICMP Filtered Sweep"
 
diff --git a/src/log.c b/src/log.c
index 4ed8a8b..ddf6202 100644
--- a/src/log.c
+++ b/src/log.c
@@ -520,6 +520,20 @@ void PrintIPPkt(FILE * fp, int type, Packet * p)
 
                 break;
 
+            case IPPROTO_SCTP:
+                if(p->sctph != NULL)
+                {
+                    PrintSCTPHeader(fp, p);
+                }
+                else
+                {
+                    PrintNetData(fp, (u_char *)
+                            (u_char *)p->iph + (GET_IPH_HLEN(p) << 2),
+                            GET_IP_PAYLEN(p), NULL);
+                }
+
+                break;
+
             case IPPROTO_ICMP:
                 if(p->icmph != NULL)
                 {
@@ -1200,6 +1214,32 @@ void PrintUDPHeader(FILE * fp, Packet * p)
 
 /****************************************************************************
  *
+ * Function: PrintSCTPHeader(FILE *)
+ *
+ * Purpose: Dump the SCTP common header to the specified file stream
+ *
+ * Arguments: fp => file stream
+ *
+ * Returns: void function
+ *
+ ***************************************************************************/
+void PrintSCTPHeader(FILE * fp, Packet * p)
+{
+
+    if(p->sctph == NULL)
+    {
+        fprintf(fp, "SCTP common header truncated\n");
+        return;
+    }
+    /* not much to do here... */
+    fprintf(fp, "Vtag: 0x%.8X  Csum: 0x%.8X\n",
+            ntohl(p->sctph->sh_vtag), ntohl(p->sctph->sh_csum));
+}
+
+
+
+/****************************************************************************
+ *
  * Function: PrintICMPHeader(FILE *)
  *
  * Purpose: Print ICMP header
@@ -1482,6 +1522,7 @@ void PrintICMPEmbeddedIP(FILE *fp, Packet *p)
     orig_p->iph = p->orig_iph;
     orig_p->tcph = p->orig_tcph;
     orig_p->udph = p->orig_udph;
+    orig_p->sctph = p->orig_sctph;
     orig_p->sp = p->orig_sp;
     orig_p->dp = p->orig_dp;
     orig_p->icmph = p->orig_icmph;
@@ -1511,6 +1552,13 @@ void PrintICMPEmbeddedIP(FILE *fp, Packet *p)
                             ntohs(orig_p->udph->uh_chk));
                 break;
 
+            case IPPROTO_SCTP:
+                if(orig_p->sctph != NULL)
+                    fprintf(fp, "Vtag: %d  Csum: %d\n",
+                            ntohs(orig_p->sctph->sh_vtag),
+                            ntohs(orig_p->sctph->sh_csum));
+                break;
+
             case IPPROTO_ICMP:
                 if(orig_p->icmph != NULL)
                     PrintEmbeddedICMPHeader(fp, orig_p->icmph);
@@ -2298,7 +2346,8 @@ void PrintIpAddrs(FILE *fp, Packet *p)
 
     if (p->frag_flag
             || ((GET_IPH_PROTO(p) != IPPROTO_TCP)
-                && (GET_IPH_PROTO(p) != IPPROTO_UDP)))
+                && (GET_IPH_PROTO(p) != IPPROTO_UDP)
+                && (GET_IPH_PROTO(p) != IPPROTO_SCTP)))
     {
         char *ip_fmt = "%s -> %s";
 
diff --git a/src/log.h b/src/log.h
index bff69c9..bce775c 100644
--- a/src/log.h
+++ b/src/log.h
@@ -63,6 +63,7 @@ void PrintICMPHeader(FILE *, Packet *);
 void PrintICMPEmbeddedIP(FILE *, Packet *);
 void PrintEmbeddedICMPHeader(FILE *, const ICMPHdr *);
 void PrintUDPHeader(FILE *, Packet *);
+void PrintSCTPHeader(FILE *, Packet *);
 void PrintPriorityData(FILE *, int);
 void PrintXrefs(FILE *, int);
 void CreateTCPFlagString(Packet *, char *);
diff --git a/src/log_text.c b/src/log_text.c
index 2a2b8c2..109669e 100644
--- a/src/log_text.c
+++ b/src/log_text.c
@@ -537,7 +537,8 @@ void LogIpAddrs(TextLog *log, Packet *p)
 
     if (p->frag_flag
             || ((GET_IPH_PROTO(p) != IPPROTO_TCP)
-                && (GET_IPH_PROTO(p) != IPPROTO_UDP)))
+                && (GET_IPH_PROTO(p) != IPPROTO_UDP)
+                && (GET_IPH_PROTO(p) != IPPROTO_SCTP)))
     {
         char *ip_fmt = "%s -> %s";
 
@@ -911,6 +912,33 @@ void LogUDPHeader(TextLog* log, Packet* p)
     TextLog_Print(log, "Len: %d\n", ntohs(p->udph->uh_len) - UDP_HEADER_LEN);
 }
 
+/*-------------------------------------------------------------------
+ * SCTP stuff cloned from log.c
+ *-------------------------------------------------------------------
+ */
+/*--------------------------------------------------------------------
+ * Function: LogSCTPHeader(TextLog* )
+ *
+ * Purpose: Dump the SCTP header to the given TextLog
+ *
+ * Arguments: log => pointer to TextLog
+ *
+ * Returns: void function
+ *--------------------------------------------------------------------
+ */
+void LogSCTPHeader(TextLog* log, Packet* p)
+{
+
+    if(p->sctph == NULL)
+    {
+        TextLog_Print(log, "SCTP common header truncated\n");
+        return;
+    }
+    /* not much to do here... */
+    TextLog_Print(log, "Vtag: 0x%.8X  Csum: 0x%.8X\n",
+            ntohl(p->sctph->sh_vtag), ntohl(p->sctph->sh_csum));
+}
+
 /*--------------------------------------------------------------------
  * ICMP stuff cloned from log.c
  *--------------------------------------------------------------------
@@ -1034,6 +1062,13 @@ static void LogICMPEmbeddedIP(TextLog* log, Packet *p)
                             ntohs(orig_p->udph->uh_chk));
                 break;
 
+            case IPPROTO_SCTP:
+                if(orig_p->sctph != NULL)
+                    TextLog_Print(log, "Vtag: %d  Csum: %d\n",
+                            ntohs(orig_p->sctph->sh_vtag),
+                            ntohs(orig_p->sctph->sh_csum));
+                break;
+
             case IPPROTO_ICMP:
                 if(orig_p->icmph != NULL)
                     LogEmbeddedICMPHeader(log, orig_p->icmph);
@@ -1680,6 +1715,18 @@ void LogIPPkt(TextLog* log, int type, Packet * p)
 
                 break;
 
+            case IPPROTO_SCTP:
+                if ( p->sctph != NULL )
+                {
+                    LogSCTPHeader(log, p);
+                }
+                else
+                {
+                    LogNetData(log, DATA_PTR(p), DATA_LEN(p), NULL);
+                }
+
+                break;
+
             case IPPROTO_ICMP:
                 if ( p->icmph != NULL )
                 {
diff --git a/src/log_text.h b/src/log_text.h
index a0ffa6a..e39f09a 100644
--- a/src/log_text.h
+++ b/src/log_text.h
@@ -48,6 +48,7 @@ void LogIpAddrs(TextLog*, Packet*);
 void LogIPHeader(TextLog*, Packet*);
 void LogTCPHeader(TextLog*, Packet*);
 void LogUDPHeader(TextLog*, Packet*);
+void LogSCTPHeader(TextLog*, Packet*);
 void LogICMPHeader(TextLog*, Packet*);
 void LogArpHeader(TextLog*, Packet*);
 
diff --git a/src/output-plugins/spo_alert_full.c b/src/output-plugins/spo_alert_full.c
index 2d2740c..c0d3f7a 100644
--- a/src/output-plugins/spo_alert_full.c
+++ b/src/output-plugins/spo_alert_full.c
@@ -193,6 +193,10 @@ static void AlertFull(Packet *p, char *msg, void *arg, Event *event)
                    LogUDPHeader(data->log, p);
                     break;
 
+                case IPPROTO_SCTP:
+                   LogSCTPHeader(data->log, p);
+                    break;
+
                 case IPPROTO_ICMP:
                    LogICMPHeader(data->log, p);
                     break;
diff --git a/src/output-plugins/spo_alert_sf_socket.c b/src/output-plugins/spo_alert_sf_socket.c
index ad7b76b..7414fad 100644
--- a/src/output-plugins/spo_alert_sf_socket.c
+++ b/src/output-plugins/spo_alert_sf_socket.c
@@ -345,7 +345,9 @@ void AlertSFSocket(Packet *packet, char *msg, void *arg, Event *event)
     sar.dest_ip = ntohl(GET_DST_IP(packet)->ip32[0]);
     sar.protocol = GET_IPH_PROTO(packet);
 
-    if(sar.protocol == IPPROTO_UDP || sar.protocol == IPPROTO_TCP)
+    if(sar.protocol == IPPROTO_UDP
+        || sar.protocol == IPPROTO_TCP
+        || sar.protocol == IPPROTO_SCTP)
     {
         sar.sport = packet->sp;
         sar.dport = packet->dp;
@@ -463,7 +465,8 @@ static OptTreeNode *OptTreeNode_Search(uint32_t gid, uint32_t sid)
         if (rtn)
         {
             if ((rtn->proto == IPPROTO_TCP) || (rtn->proto == IPPROTO_UDP)
-                    || (rtn->proto == IPPROTO_ICMP) || (rtn->proto == ETHERNET_TYPE_IP))
+                    || (rtn->proto == IPPROTO_SCTP) || (rtn->proto == IPPROTO_ICMP)
+                    || (rtn->proto == ETHERNET_TYPE_IP))
             {
                 if (otn->sigInfo.id == sid)
                 {
diff --git a/src/output-plugins/spo_alert_syslog.c b/src/output-plugins/spo_alert_syslog.c
index c4d6294..716f886 100644
--- a/src/output-plugins/spo_alert_syslog.c
+++ b/src/output-plugins/spo_alert_syslog.c
@@ -575,7 +575,8 @@ static void AlertSyslog(Packet *p, char *msg, void *arg, Event *event)
 
         if (p->frag_flag
                 || ((GET_IPH_PROTO(p) != IPPROTO_TCP)
-                    && (GET_IPH_PROTO(p) != IPPROTO_UDP)))
+                    && (GET_IPH_PROTO(p) != IPPROTO_UDP)
+                    && (GET_IPH_PROTO(p) != IPPROTO_SCTP)))
         {
             char *ip_fmt = "%s -> %s";
 
diff --git a/src/output-plugins/spo_alert_unixsock.c b/src/output-plugins/spo_alert_unixsock.c
index a445fe9..beaeab4 100644
--- a/src/output-plugins/spo_alert_unixsock.c
+++ b/src/output-plugins/spo_alert_unixsock.c
@@ -234,6 +234,13 @@ static void AlertUnixSock(Packet *p, char *msg, void *arg, Event *event)
                         }
                         break;
 
+                    case IPPROTO_SCTP:
+                        if (p->sctph)
+                        {
+                            alertpkt.transhdr=(char *)p->sctph-(char *)p->pkt;
+                        }
+                        break;
+
                     case IPPROTO_ICMP:
                        if (p->icmph)
                        {
diff --git a/src/output-plugins/spo_csv.c b/src/output-plugins/spo_csv.c
index 529d576..f23059a 100644
--- a/src/output-plugins/spo_csv.c
+++ b/src/output-plugins/spo_csv.c
@@ -63,6 +63,7 @@
 #include "mstring.h"
 #include "util.h"
 #include "log.h"
+#include "compiler.h"
 
 #include "snort.h"
 
@@ -329,6 +330,9 @@ static void RealAlertCSV(Packet * p, char *msg, char **args,
                     case IPPROTO_UDP:
                         TextLog_Puts(log, "UDP");
                         break;
+                    case IPPROTO_SCTP:
+                        TextLog_Puts(log, "SCTP");
+                        break;
                     case IPPROTO_TCP:
                         TextLog_Puts(log, "TCP");
                         break;
@@ -387,6 +391,7 @@ static void RealAlertCSV(Packet * p, char *msg, char **args,
                 switch (GET_IPH_PROTO(p))
                 {
                     case IPPROTO_UDP:
+                    case IPPROTO_SCTP:
                     case IPPROTO_TCP:
                         TextLog_Print(log, "%d", p->sp);
                         break;
@@ -402,6 +407,7 @@ static void RealAlertCSV(Packet * p, char *msg, char **args,
                 switch (GET_IPH_PROTO(p))
                 {
                     case IPPROTO_UDP:
+                    case IPPROTO_SCTP:
                     case IPPROTO_TCP:
                         TextLog_Print(log, "%d", p->dp);
                         break;
@@ -499,6 +505,21 @@ static void RealAlertCSV(Packet * p, char *msg, char **args,
                 TextLog_Print(log, "%s", tcpFlags);
             }
         }
+        else if (!strcasecmp("sctplen", type))
+        {
+            if (likely(p->sctph != NULL))
+                TextLog_Print(log, "%d", (SCTP_COMMON_HEADER_LEN + p->dsize));
+        }
+        else if (!strcasecmp("sctpvtag", type))
+        {
+            if (likely(p->sctph != NULL))
+                TextLog_Print(log, "0x%.8X", ntohl(p->sctph->sh_vtag));
+        }
+        else if (!strcasecmp("sctpcsum", type))
+        {
+            if (likely(p->sctph != NULL))
+                TextLog_Print(log, "0x%.8X", ntohl(p->sctph->sh_csum));
+        }
 
         if (num < numargs - 1)
             TextLog_Putc(log, ',');
diff --git a/src/output-plugins/spo_database.c b/src/output-plugins/spo_database.c
index a998bc1..df31930 100644
--- a/src/output-plugins/spo_database.c
+++ b/src/output-plugins/spo_database.c
@@ -1887,6 +1887,41 @@ static void Database(Packet *p, char *msg, void *arg, Event *event)
                         goto bad_query;
                 }
             }
+            else if(GET_IPH_PROTO(p) == IPPROTO_SCTP && p->sctph)
+            {
+                query = NewQueryNode(query, 0);
+                /*** Build the query for the SCTP Header ***/
+                if(data->detail)
+                {
+                    ret = SnortSnprintf(query->val, MAX_QUERY_LENGTH,
+                                        "INSERT INTO "
+                                        "sctphdr (sid, cid, sctp_sport, sctp_dport, sctp_vtag, sctp_csum) "
+                                        "VALUES (%u, %u, %u, %u, %lu, %lu)",
+                                        data->shared->sid,
+                                        data->shared->cid,
+                                        ntohs(p->sctph->sh_sport),
+                                        ntohs(p->sctph->sh_dport),
+                                        (u_long)ntohl(p->sctph->sh_vtag),
+                                        (u_long)ntohl(p->sctph->sh_csum));
+
+                    if (ret != SNORT_SNPRINTF_SUCCESS)
+                        goto bad_query;
+                }
+                else
+                {
+                    ret = SnortSnprintf(query->val, MAX_QUERY_LENGTH,
+                                        "INSERT INTO "
+                                        "sctphdr (sid, cid, sctp_sport, sctp_dport) "
+                                        "VALUES (%u, %u, %u, %u)",
+                                        data->shared->sid,
+                                        data->shared->cid,
+                                        ntohs(p->sctph->sh_sport),
+                                        ntohs(p->sctph->sh_dport));
+
+                    if (ret != SNORT_SNPRINTF_SUCCESS)
+                        goto bad_query;
+                }
+            }
         }
 
         /*** Build the query for the IP Header ***/
diff --git a/src/output-plugins/spo_log_ascii.c b/src/output-plugins/spo_log_ascii.c
index 4dbbeea..29619fd 100644
--- a/src/output-plugins/spo_log_ascii.c
+++ b/src/output-plugins/spo_log_ascii.c
@@ -285,8 +285,9 @@ static FILE *OpenLogFile(int mode, Packet * p)
     DEBUG_WRAP(DebugMessage(DEBUG_FLOW, "Directory Created!\n"););
 
     /* build the log filename */
-    if(GET_IPH_PROTO(p) == IPPROTO_TCP ||
-            GET_IPH_PROTO(p) == IPPROTO_UDP)
+    if(GET_IPH_PROTO(p) == IPPROTO_TCP
+        || GET_IPH_PROTO(p) == IPPROTO_UDP
+        || GET_IPH_PROTO(p) == IPPROTO_SCTP)
     {
         if(p->frag_flag)
         {
diff --git a/src/parser.c b/src/parser.c
index eba5376..9e25feb 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -135,6 +135,7 @@
 #define RULE_PROTO_OPT__IP    "ip"
 #define RULE_PROTO_OPT__TCP   "tcp"
 #define RULE_PROTO_OPT__UDP   "udp"
+#define RULE_PROTO_OPT__SCTP  "sctp"
 #define RULE_PROTO_OPT__ICMP  "icmp"
 
 #define RULE_DIR_OPT__DIRECTIONAL    "->"
@@ -262,6 +263,8 @@
 #define CHECKSUM_MODE_OPT__NO_TCP   "notcp"
 #define CHECKSUM_MODE_OPT__UDP      "udp"
 #define CHECKSUM_MODE_OPT__NO_UDP   "noudp"
+#define CHECKSUM_MODE_OPT__SCTP     "sctp"
+#define CHECKSUM_MODE_OPT__NO_SCTP  "nosctp"
 #define CHECKSUM_MODE_OPT__ICMP     "icmp"
 #define CHECKSUM_MODE_OPT__NO_ICMP  "noicmp"
 
@@ -451,6 +454,7 @@ static port_list_t port_list;
 
 static rule_count_t tcpCnt;
 static rule_count_t udpCnt;
+static rule_count_t sctpCnt;
 static rule_count_t icmpCnt;
 static rule_count_t ipCnt;
 
@@ -800,7 +804,7 @@ static void PortTablesFinish(rule_port_tables_t *, FastPatternConfig *);
 static rule_port_tables_t * PortTablesNew(void);
 static int FinishPortListRule(rule_port_tables_t *, RuleTreeNode *, OptTreeNode *,
                               int, port_entry_t *, FastPatternConfig *);
-static PortObject * ParsePortListTcpUdpPort(PortVarTable *, PortTable *, char *);
+static PortObject * ParsePortListProtosWithPorts(PortVarTable *, PortTable *, char *);
 static int ParsePortList(RuleTreeNode *, PortVarTable *, PortTable *, char *, int, int);
 static void ParseRule(SnortConfig *, SnortPolicy *, char *, RuleType, ListHead *);
 static void TransferOutputConfigs(OutputConfig *, OutputConfig **);
@@ -1195,6 +1199,7 @@ static void DumpRuleChains(RuleListNode *rule_lists)
         DumpChain(rule->name, rule->mode, ETHERNET_TYPE_IP);
         DumpChain(rule->name, rule->mode, IPPROTO_TCP);
         DumpChain(rule->name, rule->mode, IPPROTO_UDP);
+        DumpChain(rule->name, rule->mode, IPPROTO_SCTP);
         DumpChain(rule->name, rule->mode, IPPROTO_ICMP);
 
         rule = rule->next;
@@ -1262,7 +1267,7 @@ static void DumpList(IpAddrNode *idx, int negated)
 /*
  * Finish adding the rule to the port tables
  *
- * 1) find the table this rule should belong to (src/dst/any-any tcp,udp,icmp,ip or nocontent)
+ * 1) find the table this rule should belong to (src/dst/any-any tcp,udp,sctp,icmp,ip or nocontent)
  * 2) find an index for the sid:gid pair
  * 3) add all no content rules to a single no content port object, the ports are irrelevant so
  *    make it a any-any port object.
@@ -1301,6 +1306,13 @@ static int FinishPortListRule(rule_port_tables_t *port_tables, RuleTreeNode *rtn
         aaObject = port_tables->udp_anyany;
         prc = &udpCnt;
     }
+    else if (proto == IPPROTO_SCTP)
+    {
+        dstTable = port_tables->sctp_dst;
+        srcTable = port_tables->sctp_src;
+        aaObject = port_tables->sctp_anyany;
+        prc = &sctpCnt;
+    }
     else if (proto == IPPROTO_ICMP)
     {
         dstTable = port_tables->icmp_dst;
@@ -1389,8 +1401,8 @@ static int FinishPortListRule(rule_port_tables_t *port_tables, RuleTreeNode *rtn
         {
             /* Add the IP rules to the higher level app protocol groups, if they apply
              * to those protocols.  All IP rules should have any-any port descriptors
-             * and fall into this test.  IP rules that are not tcp/udp/icmp go only into the
-             * IP table */
+             * and fall into this test.  IP rules that are not tcp/udp/sctp/icmp go only
+             * into the IP table */
             DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,
                                     "Finishing IP any-any rule %u:%u\n",
                                     otn->sigInfo.generator,otn->sigInfo.id););
@@ -1407,6 +1419,11 @@ static int FinishPortListRule(rule_port_tables_t *port_tables, RuleTreeNode *rtn
                     udpCnt.aa++;
                     break;
 
+                case IPPROTO_SCTP:
+                    PortObjectAddRule(port_tables->sctp_anyany, rim_index);
+                    sctpCnt.aa++;
+                    break;
+
                 case IPPROTO_ICMP:
                     PortObjectAddRule(port_tables->icmp_anyany, rim_index);
                     icmpCnt.aa++;
@@ -1419,6 +1436,9 @@ static int FinishPortListRule(rule_port_tables_t *port_tables, RuleTreeNode *rtn
                     PortObjectAddRule(port_tables->udp_anyany, rim_index);
                     udpCnt.aa++;
 
+                    PortObjectAddRule(port_tables->sctp_anyany, rim_index);
+                    sctpCnt.aa++;
+
                     PortObjectAddRule(port_tables->icmp_anyany, rim_index);
                     icmpCnt.aa++;
 
@@ -1434,7 +1454,7 @@ static int FinishPortListRule(rule_port_tables_t *port_tables, RuleTreeNode *rtn
         }
         else
         {
-            /* For other protocols-tcp/udp/icmp add to the any any group */
+            /* For other protocols-tcp/udp/sctp/icmp add to the any any group */
             PortObjectAddRule(aaObject, rim_index);
             prc->aa++;
         }
@@ -1540,8 +1560,8 @@ static int FinishPortListRule(rule_port_tables_t *port_tables, RuleTreeNode *rtn
 *  to build PORT_GROUP objects. Those are generated after the otn processing.
 *
 */
-static PortObject * ParsePortListTcpUdpPort(PortVarTable *pvt,
-                                            PortTable *noname, char *port_str)
+static PortObject * ParsePortListProtosWithPorts(PortVarTable *pvt,
+                                                PortTable *noname, char *port_str)
 {
     PortObject * portobject;
     //PortObject * pox;
@@ -1690,18 +1710,18 @@ int GetOtnIcmpType(OptTreeNode * otn )
  *   Process the rule, add it to the appropriate PortObject
  *   and add the PortObject to the rtn.
  *
- *   TCP/UDP rules use ports/portlists, icmp uses the icmp type field and ip uses the protocol
+ *   TCP/UDP/SCTP rules use ports/portlists, icmp uses the icmp type field and ip uses the protocol
  *   field as a dst port for the purposes of looking up a rule group as packets are being
  *   processed.
  *
- *   TCP/UDP- use src/dst ports
- *   ICMP   - use icmp type as dst port,src=-1
- *   IP     - use protocol as dst port,src=-1
+ *   TCP/UDP/SCTP - use src/dst ports
+ *   ICMP         - use icmp type as dst port, src=-1
+ *   IP           - use protocol as dst port, src=-1
  *
- *   rtn - proto_node
- *   port_str - port list string or port var name
- *   proto - protocol
- *   dst_flag - dst or src port flag, true = dst, false = src
+ *   rtn          - proto_node
+ *   port_str     - port list string or port var name
+ *   proto        - protocol
+ *   dst_flag     - dst or src port flag, true = dst, false = src
  *
  */
 static int ParsePortList(RuleTreeNode *rtn, PortVarTable *pvt, PortTable *noname,
@@ -1710,9 +1730,9 @@ static int ParsePortList(RuleTreeNode *rtn, PortVarTable *pvt, PortTable *noname
     PortObject *portobject = NULL;  /* src or dst */
 
     /* Get the protocol specific port object */
-    if( proto == IPPROTO_TCP || proto == IPPROTO_UDP )
+    if( proto == IPPROTO_TCP || proto == IPPROTO_UDP || proto == IPPROTO_SCTP )
     {
-        portobject = ParsePortListTcpUdpPort(pvt, noname, port_str);
+        portobject = ParsePortListProtosWithPorts(pvt, noname, port_str);
     }
     else /* ICMP, IP  - no real ports just Type and Protocol */
     {
@@ -3235,6 +3255,10 @@ static int GetRuleProtocol(char *proto_str)
     {
         return IPPROTO_UDP;
     }
+    else if (strcasecmp(proto_str, RULE_PROTO_OPT__SCTP) == 0)
+    {
+        return IPPROTO_SCTP;
+    }
     else if (strcasecmp(proto_str, RULE_PROTO_OPT__ICMP) == 0)
     {
         return IPPROTO_ICMP;
@@ -4415,6 +4439,7 @@ static void LinkDynamicRules(SnortConfig *sc)
 
     SetLinks(sc, IPPROTO_TCP);
     SetLinks(sc, IPPROTO_UDP);
+    SetLinks(sc, IPPROTO_SCTP);
     SetLinks(sc, IPPROTO_ICMP);
 }
 
@@ -4717,7 +4742,8 @@ int CheckRuleStates(SnortConfig *sc)
             }
 
             if ((rtn->proto == IPPROTO_TCP) || (rtn->proto == IPPROTO_UDP) ||
-                (rtn->proto == IPPROTO_ICMP) || (rtn->proto == ETHERNET_TYPE_IP))
+                (rtn->proto == IPPROTO_ICMP) || (rtn->proto == IPPROTO_SCTP) ||
+                (rtn->proto == ETHERNET_TYPE_IP))
             {
                 //do operation
                 if ( otn->sigInfo.shared )
@@ -4822,7 +4848,7 @@ void SetRuleStates(SnortConfig *sc)
         otn->rule_state = rule_state->state;
     }
 
-    /* Check TCP/UDP/ICMP/IP in one iteration for all rulelists and for all policies*/
+    /* Check TCP/UDP/SCTP/ICMP/IP in one iteration for all rulelists and for all policies*/
 #if 1
     CheckRuleStates(sc);
 #else
@@ -5764,6 +5790,7 @@ static void InitParser(void)
 
     memset(&tcpCnt, 0, sizeof(tcpCnt));
     memset(&udpCnt, 0, sizeof(udpCnt));
+    memset(&sctpCnt, 0, sizeof(sctpCnt));
     memset(&ipCnt, 0, sizeof(ipCnt));
     memset(&icmpCnt, 0, sizeof(icmpCnt));
 
@@ -5850,12 +5877,12 @@ void ParseRules(SnortConfig *sc)
     PortTablesFinish(sc->port_tables, sc->fast_pattern_config);
 
     LogMessage("+-------------------[Rule Port Counts]---------------------------------------\n");
-    LogMessage("|%8s%8s%8s%8s%8s\n", " ", "tcp", "udp", "icmp", "ip");
-    LogMessage("|%8s%8u%8u%8u%8u\n", "src", tcpCnt.src, udpCnt.src, icmpCnt.src, ipCnt.src);
-    LogMessage("|%8s%8u%8u%8u%8u\n", "dst", tcpCnt.dst, udpCnt.dst, icmpCnt.dst, ipCnt.dst);
-    LogMessage("|%8s%8u%8u%8u%8u\n", "any", tcpCnt.aa, udpCnt.aa, icmpCnt.aa, ipCnt.aa);
-    LogMessage("|%8s%8u%8u%8u%8u\n", "nc", tcpCnt.nc, udpCnt.nc, icmpCnt.nc, ipCnt.nc);
-    LogMessage("|%8s%8u%8u%8u%8u\n", "s+d", tcpCnt.sd, udpCnt.sd, icmpCnt.sd, ipCnt.sd);
+    LogMessage("|%8s%8s%8s%8s%8s%8s\n", " ", "tcp", "udp", "sctp", "icmp", "ip");
+    LogMessage("|%8s%8u%8u%8u%8u%8u\n", "src", tcpCnt.src, udpCnt.src, sctpCnt.src, icmpCnt.src, ipCnt.src);
+    LogMessage("|%8s%8u%8u%8u%8u%8u\n", "dst", tcpCnt.dst, udpCnt.dst, sctpCnt.dst, icmpCnt.dst, ipCnt.dst);
+    LogMessage("|%8s%8u%8u%8u%8u%8u\n", "any", tcpCnt.aa, udpCnt.aa, sctpCnt.aa, icmpCnt.aa, ipCnt.aa);
+    LogMessage("|%8s%8u%8u%8u%8u%8u\n", "nc", tcpCnt.nc, udpCnt.nc, sctpCnt.nc, icmpCnt.nc, ipCnt.nc);
+    LogMessage("|%8s%8u%8u%8u%8u%8u\n", "s+d", tcpCnt.sd, udpCnt.sd, sctpCnt.sd, icmpCnt.sd, ipCnt.sd);
     LogMessage("+----------------------------------------------------------------------------\n");
 
     ///print_rule_index_map( ruleIndexMap );
@@ -6405,6 +6432,18 @@ static int GetChecksumFlags(char *args)
             negative_flags |= CHECKSUM_FLAG__UDP;
             got_negative_flag = 1;
         }
+        else if (strcasecmp(toks[i], CHECKSUM_MODE_OPT__SCTP) == 0)
+        {
+            positive_flags |= CHECKSUM_FLAG__SCTP;
+            negative_flags &= ~CHECKSUM_FLAG__SCTP;
+            got_positive_flag = 1;
+        }
+        else if (strcasecmp(toks[i], CHECKSUM_MODE_OPT__NO_SCTP) == 0)
+        {
+            positive_flags &= ~CHECKSUM_FLAG__SCTP;
+            negative_flags |= CHECKSUM_FLAG__SCTP;
+            got_negative_flag = 1;
+        }
         else if (strcasecmp(toks[i], CHECKSUM_MODE_OPT__ICMP) == 0)
         {
             positive_flags |= CHECKSUM_FLAG__ICMP;
@@ -7425,7 +7464,7 @@ void ConfigFlowbitsSize(SnortConfig *sc, char *args)
 /****************************************************************************
  *
  * Purpose: Parses a protocol plus a list of ports.
- *          The protocol should be "udp" or "tcp".
+ *          The protocol should be "tcp", "udp", or "sctp".
  *          The ports list should be a list of numbers or pairs of numbers.
  *          Each element of the list is separated by a space character.
  *          Each pair of numbers is separated by a colon character.
@@ -7462,7 +7501,7 @@ void ConfigIgnorePorts(SnortConfig *sc, char *args)
 
     protocol = GetRuleProtocol(toks[0]);
 
-    if ( !(protocol == IPPROTO_TCP || protocol == IPPROTO_UDP) )
+    if ( !(protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || protocol == IPPROTO_SCTP) )
     {
         ParseError("Invalid protocol: %s.", toks[0]);
     }
@@ -7474,10 +7513,20 @@ void ConfigIgnorePorts(SnortConfig *sc, char *args)
 
         for ( p = lo_port; p <= hi_port; p++ )
         {
-            if (protocol == IPPROTO_TCP)
-                sc->ignore_ports[p] |= PROTO_BIT__TCP;
-            else if (protocol == IPPROTO_UDP)
-                sc->ignore_ports[p] |= PROTO_BIT__UDP;
+            switch (protocol)
+            {
+                case IPPROTO_TCP:
+                    sc->ignore_ports[p] |= PROTO_BIT__TCP;
+                    break;
+
+                case IPPROTO_UDP:
+                    sc->ignore_ports[p] |= PROTO_BIT__UDP;
+                    break;
+
+                case IPPROTO_SCTP:
+                    sc->ignore_ports[p] |= PROTO_BIT__SCTP;
+                    break;
+            }
         }
     }
 
@@ -9242,6 +9291,9 @@ static void ParseRule(SnortConfig *sc, SnortPolicy *p, char *args,
             case IPPROTO_UDP:
                 sc->ip_proto_array[IPPROTO_UDP] = 1;
                 break;
+            case IPPROTO_SCTP:
+                sc->ip_proto_array[IPPROTO_SCTP] = 1;
+                break;
             case IPPROTO_ICMP:
                 sc->ip_proto_array[IPPROTO_ICMP] = 1;
                 sc->ip_proto_array[IPPROTO_ICMPV6] = 1;
@@ -11285,22 +11337,27 @@ static rule_port_tables_t * PortTablesNew(void)
     /* No content rule objects */
     rpt->tcp_nocontent = PortObjectNew();
     if (rpt->tcp_nocontent == NULL)
-        ParseError("ParseRulesFile nocontent PortObjectNew() failed\n");
+        ParseError("ParseRulesFile tcp nocontent PortObjectNew() failed\n");
     PortObjectAddPortAny(rpt->tcp_nocontent);
 
     rpt->udp_nocontent = PortObjectNew();
     if (rpt->udp_nocontent == NULL)
-        ParseError("ParseRulesFile nocontent PortObjectNew() failed\n");
+        ParseError("ParseRulesFile udp nocontent PortObjectNew() failed\n");
     PortObjectAddPortAny(rpt->udp_nocontent);
 
+    rpt->sctp_nocontent = PortObjectNew();
+    if (rpt->sctp_nocontent == NULL)
+        FatalError("ParseRulesFile sctp nocontent PortObjectNew() failed\n");
+    PortObjectAddPortAny(rpt->sctp_nocontent);
+
     rpt->icmp_nocontent = PortObjectNew();
     if (rpt->icmp_nocontent == NULL)
-        ParseError("ParseRulesFile nocontent PortObjectNew() failed\n");
+        ParseError("ParseRulesFile icmp nocontent PortObjectNew() failed\n");
     PortObjectAddPortAny(rpt->icmp_nocontent);
 
     rpt->ip_nocontent = PortObjectNew();
     if (rpt->ip_nocontent == NULL)
-        ParseError("ParseRulesFile nocontent PortObjectNew() failed\n");
+        ParseError("ParseRulesFile ip nocontent PortObjectNew() failed\n");
     PortObjectAddPortAny(rpt->ip_nocontent);
 
     /* Create the Any-Any Port Objects for each protocol */
@@ -11314,6 +11371,11 @@ static rule_port_tables_t * PortTablesNew(void)
         ParseError("ParseRulesFile udp any-any PortObjectNew() failed\n");
     PortObjectAddPortAny(rpt->udp_anyany);
 
+    rpt->sctp_anyany = PortObjectNew();
+    if (rpt->sctp_anyany == NULL)
+        FatalError("ParseRulesFile sctp any-any PortObjectNew() failed\n");
+    PortObjectAddPortAny(rpt->sctp_anyany);
+
     rpt->icmp_anyany = PortObjectNew();
     if (rpt->icmp_anyany == NULL)
         ParseError("ParseRulesFile icmp any-any PortObjectNew() failed\n");
@@ -11342,6 +11404,15 @@ static rule_port_tables_t * PortTablesNew(void)
     if (rpt->udp_dst == NULL)
         ParseError("ParseRulesFile udp-dst PortTableNew() failed\n");
 
+    /* Create the sctp Rules PortTables */
+    rpt->sctp_src = PortTableNew();
+    if (rpt->sctp_src == NULL)
+        FatalError("ParseRulesFile sctp-src PortTableNew() failed\n");
+
+    rpt->sctp_dst = PortTableNew();
+    if (rpt->sctp_dst == NULL)
+        FatalError("ParseRulesFile sctp-dst PortTableNew() failed\n");
+
     /* Create the icmp Rules PortTables */
     rpt->icmp_src = PortTableNew();
     if (rpt->icmp_src == NULL)
@@ -11368,6 +11439,8 @@ static rule_port_tables_t * PortTablesNew(void)
     rpt->tcp_dst->pt_lrc = DEFAULT_LARGE_RULE_GROUP;
     rpt->udp_src->pt_lrc = DEFAULT_LARGE_RULE_GROUP;
     rpt->udp_dst->pt_lrc = DEFAULT_LARGE_RULE_GROUP;
+    rpt->sctp_src->pt_lrc = DEFAULT_LARGE_RULE_GROUP;
+    rpt->sctp_dst->pt_lrc = DEFAULT_LARGE_RULE_GROUP;
     rpt->icmp_src->pt_lrc= DEFAULT_LARGE_RULE_GROUP;
     rpt->icmp_dst->pt_lrc= DEFAULT_LARGE_RULE_GROUP;
     rpt->ip_src->pt_lrc  = DEFAULT_LARGE_RULE_GROUP;
@@ -11400,6 +11473,17 @@ static void PortTablesFinish(rule_port_tables_t *port_tables, FastPatternConfig
     finish_portlist_table(fp, "udp src", port_tables->udp_src);
     finish_portlist_table(fp, "udp dst", port_tables->udp_dst);
 
+    /* SCTP-SRC */
+    if (fpDetectGetDebugPrintRuleGroupsCompiled(fp))
+    {
+        LogMessage("*** SCTP-Any-Any Port List\n");
+        PortObjectPrintEx(port_tables->sctp_anyany,
+                          rule_index_map_print_index);
+    }
+
+    finish_portlist_table(fp, "sctp src", port_tables->sctp_src);
+    finish_portlist_table(fp, "sctp dst", port_tables->sctp_dst);
+
     /* ICMP-SRC */
     if (fpDetectGetDebugPrintRuleGroupsCompiled(fp))
     {
@@ -11424,10 +11508,12 @@ static void PortTablesFinish(rule_port_tables_t *port_tables, FastPatternConfig
 
     RuleListSortUniq(port_tables->tcp_anyany->rule_list);
     RuleListSortUniq(port_tables->udp_anyany->rule_list);
+    RuleListSortUniq(port_tables->sctp_anyany->rule_list);
     RuleListSortUniq(port_tables->icmp_anyany->rule_list);
     RuleListSortUniq(port_tables->ip_anyany->rule_list);
     RuleListSortUniq(port_tables->tcp_nocontent->rule_list);
     RuleListSortUniq(port_tables->udp_nocontent->rule_list);
+    RuleListSortUniq(port_tables->sctp_nocontent->rule_list);
     RuleListSortUniq(port_tables->icmp_nocontent->rule_list);
     RuleListSortUniq(port_tables->ip_nocontent->rule_list);
 }
@@ -11485,6 +11571,10 @@ void PortTablesFree(rule_port_tables_t *port_tables)
         PortTableFree(port_tables->udp_src);
     if (port_tables->udp_dst)
         PortTableFree(port_tables->udp_dst);
+    if (port_tables->sctp_src)
+        PortTableFree(port_tables->sctp_src);
+    if (port_tables->sctp_dst)
+        PortTableFree(port_tables->sctp_dst);
     if (port_tables->icmp_src)
         PortTableFree(port_tables->icmp_src);
     if (port_tables->icmp_dst)
@@ -11498,6 +11588,8 @@ void PortTablesFree(rule_port_tables_t *port_tables)
         PortObjectFree(port_tables->tcp_anyany);
     if (port_tables->udp_anyany)
         PortObjectFree(port_tables->udp_anyany);
+    if (port_tables->sctp_anyany)
+        PortObjectFree(port_tables->sctp_anyany);
     if (port_tables->icmp_anyany)
         PortObjectFree(port_tables->icmp_anyany);
     if (port_tables->ip_anyany)
@@ -11507,6 +11599,8 @@ void PortTablesFree(rule_port_tables_t *port_tables)
         PortObjectFree(port_tables->tcp_nocontent);
     if (port_tables->udp_nocontent)
         PortObjectFree(port_tables->udp_nocontent);
+    if (port_tables->sctp_nocontent)
+        PortObjectFree(port_tables->sctp_nocontent);
     if (port_tables->icmp_nocontent)
         PortObjectFree(port_tables->icmp_nocontent);
     if (port_tables->ip_nocontent)
@@ -12041,7 +12135,8 @@ static void IntegrityCheckRules(SnortConfig *sc)
             }
 
             if ((rtn->proto == IPPROTO_TCP) || (rtn->proto == IPPROTO_UDP)
-                    || (rtn->proto == IPPROTO_ICMP) || (rtn->proto == ETHERNET_TYPE_IP))
+                    || (rtn->proto == IPPROTO_SCTP) || (rtn->proto == IPPROTO_ICMP)
+                    || (rtn->proto == ETHERNET_TYPE_IP))
             {
                 //do operation
                 ofl_idx = otn->opt_func;
diff --git a/src/plugbase.c b/src/plugbase.c
index a55b66f..0ba748f 100644
--- a/src/plugbase.c
+++ b/src/plugbase.c
@@ -94,6 +94,11 @@
 #include "detection-plugins/sp_base64_data.h"
 #include "detection-plugins/sp_pkt_data.h"
 #include "detection-plugins/sp_asn1.h"
+#include "detection-plugins/sp_sctp_chunk_type.h"
+#include "detection-plugins/sp_sctp_chunk_flags.h"
+#include "detection-plugins/sp_sctp_chunk_field.h"
+#include "detection-plugins/sp_sctp_num_chunks.h"
+#include "detection-plugins/sp_sctp_cause_code.h"
 #ifdef ENABLE_REACT
 #include "detection-plugins/sp_react.h"
 #endif
@@ -214,6 +219,11 @@ void RegisterRuleOptions(void)
 #if defined(FEAT_OPEN_APPID)
     SetupAppId();
 #endif /* defined(FEAT_OPEN_APPID) */
+    SctpChunkTypeSetup();
+    SctpChunkFlagsSetup();
+    SctpChunkFieldSetup();
+    SctpNumChunksSetup();
+    SctpCauseCodeSetup();
 }
 
 /****************************************************************************
@@ -397,7 +407,7 @@ void DumpRuleOptions(void)
 
     while (node != NULL)
     {
-        LogMessage("%-13s:      %p\n", node->keyword, (void *)node->vfunc);
+        LogMessage("%-16s:      %p\n", node->keyword, (void *)node->vfunc);
         node = node->next;
     }
 
@@ -878,7 +888,7 @@ void DumpPreprocessors(void)
 
     while (node != NULL)
     {
-        LogMessage("%-13s:       %p\n", node->keyword, node->config_vfunc);
+        LogMessage("%-20s:       %p\n", node->keyword, node->config_vfunc);
         node = node->next;
     }
 
@@ -1729,7 +1739,7 @@ void DumpOutputPlugins(void)
     LogMessage("-------------------------------------------------\n");
     while(idx != NULL)
     {
-        LogMessage("%-13s:       %p\n", idx->keyword, idx->config_vfunc);
+        LogMessage("%-19s:       %p\n", idx->keyword, idx->config_vfunc);
         idx = idx->next;
     }
     LogMessage("-------------------------------------------------\n\n");
diff --git a/src/plugin_enum.h b/src/plugin_enum.h
index 62ade01..51da623 100644
--- a/src/plugin_enum.h
+++ b/src/plugin_enum.h
@@ -66,6 +66,10 @@ enum {
 #if defined(FEAT_OPEN_APPID)
     PLUGIN_APPID,
 #endif /* defined(FEAT_OPEN_APPID) */
+    PLUGIN_SCTP_CHUNK_TYPE,
+    PLUGIN_SCTP_CHUNK_FLAGS,
+    PLUGIN_SCTP_NUM_CHUNKS,
+    PLUGIN_SCTP_CAUSE_CODE,
     PLUGIN_MAX  /* sentinel value */
 };
 
diff --git a/src/preprocessors/Stream5/Makefile.am b/src/preprocessors/Stream5/Makefile.am
index ac4ff90..751fa19 100644
--- a/src/preprocessors/Stream5/Makefile.am
+++ b/src/preprocessors/Stream5/Makefile.am
@@ -8,6 +8,8 @@ snort_stream5_tcp.c \
 snort_stream5_tcp.h \
 snort_stream5_udp.c \
 snort_stream5_udp.h \
+snort_stream5_sctp.c \
+snort_stream5_sctp.h \
 snort_stream5_icmp.c \
 snort_stream5_icmp.h \
 snort_stream5_ip.c \
@@ -22,6 +24,7 @@ stream5_common.h
 libstream5_a_LIBADD = \
 snort_stream5_tcp.o \
 snort_stream5_udp.o \
+snort_stream5_sctp.o \
 snort_stream5_icmp.o \
 snort_stream5_ip.o \
 snort_stream5_session.o \
diff --git a/src/preprocessors/Stream5/Makefile.in b/src/preprocessors/Stream5/Makefile.in
index 51af565..9e2f6fa 100644
--- a/src/preprocessors/Stream5/Makefile.in
+++ b/src/preprocessors/Stream5/Makefile.in
@@ -103,20 +103,22 @@ am__v_AR_0 = @echo "  AR      " $@;
 am__v_AR_1 = 
 libstream5_a_AR = $(AR) $(ARFLAGS)
 libstream5_a_DEPENDENCIES = snort_stream5_tcp.o snort_stream5_udp.o \
-	snort_stream5_icmp.o snort_stream5_ip.o \
+	snort_stream5_sctp.o snort_stream5_icmp.o snort_stream5_ip.o \
 	snort_stream5_session.o stream5_paf.o stream5_common.o \
 	$(am__append_2)
 am__libstream5_a_SOURCES_DIST = snort_stream5_tcp.c \
 	snort_stream5_tcp.h snort_stream5_udp.c snort_stream5_udp.h \
-	snort_stream5_icmp.c snort_stream5_icmp.h snort_stream5_ip.c \
-	snort_stream5_ip.h snort_stream5_session.c \
-	snort_stream5_session.h stream5_paf.c stream5_paf.h \
-	stream5_common.c stream5_common.h stream5_ha.c stream5_ha.h
+	snort_stream5_sctp.c snort_stream5_sctp.h snort_stream5_icmp.c \
+	snort_stream5_icmp.h snort_stream5_ip.c snort_stream5_ip.h \
+	snort_stream5_session.c snort_stream5_session.h stream5_paf.c \
+	stream5_paf.h stream5_common.c stream5_common.h stream5_ha.c \
+	stream5_ha.h
 @BUILD_HA_TRUE at am__objects_1 = stream5_ha.$(OBJEXT)
 am_libstream5_a_OBJECTS = snort_stream5_tcp.$(OBJEXT) \
-	snort_stream5_udp.$(OBJEXT) snort_stream5_icmp.$(OBJEXT) \
-	snort_stream5_ip.$(OBJEXT) snort_stream5_session.$(OBJEXT) \
-	stream5_paf.$(OBJEXT) stream5_common.$(OBJEXT) \
+	snort_stream5_udp.$(OBJEXT) snort_stream5_sctp.$(OBJEXT) \
+	snort_stream5_icmp.$(OBJEXT) snort_stream5_ip.$(OBJEXT) \
+	snort_stream5_session.$(OBJEXT) stream5_paf.$(OBJEXT) \
+	stream5_common.$(OBJEXT) \
 	$(am__objects_1)
 libstream5_a_OBJECTS = $(am_libstream5_a_OBJECTS)
 AM_V_P = $(am__v_P_ at AM_V@)
@@ -318,13 +320,14 @@ top_srcdir = @top_srcdir@
 AUTOMAKE_OPTIONS = foreign no-dependencies
 noinst_LIBRARIES = libstream5.a
 libstream5_a_SOURCES = snort_stream5_tcp.c snort_stream5_tcp.h \
-	snort_stream5_udp.c snort_stream5_udp.h snort_stream5_icmp.c \
-	snort_stream5_icmp.h snort_stream5_ip.c snort_stream5_ip.h \
-	snort_stream5_session.c snort_stream5_session.h stream5_paf.c \
-	stream5_paf.h stream5_common.c stream5_common.h \
+	snort_stream5_udp.c snort_stream5_udp.h snort_stream5_sctp.c \
+	snort_stream5_sctp.h snort_stream5_icmp.c snort_stream5_icmp.h \
+	snort_stream5_ip.c snort_stream5_ip.h snort_stream5_session.c \
+	snort_stream5_session.h stream5_paf.c stream5_paf.h stream5_common.c \
+	stream5_common.h \
 	$(am__append_1)
 libstream5_a_LIBADD = snort_stream5_tcp.o snort_stream5_udp.o \
-	snort_stream5_icmp.o snort_stream5_ip.o \
+	snort_stream5_sctp.o snort_stream5_icmp.o snort_stream5_ip.o \
 	snort_stream5_session.o stream5_paf.o stream5_common.o \
 	$(am__append_2)
 all: all-am
diff --git a/src/preprocessors/Stream5/snort_stream5_icmp.c b/src/preprocessors/Stream5/snort_stream5_icmp.c
index a258356..db72017 100644
--- a/src/preprocessors/Stream5/snort_stream5_icmp.c
+++ b/src/preprocessors/Stream5/snort_stream5_icmp.c
@@ -35,6 +35,7 @@
 
 #include "snort_stream5_tcp.h"
 #include "snort_stream5_udp.h"
+#include "snort_stream5_sctp.h"
 #include "snort_stream5_icmp.h"
 
 #include "parser.h"
@@ -309,9 +310,9 @@ static int ProcessIcmpUnreach(Packet *p)
     if (!p->orig_iph)
         return 0;
 
-    /* Get TCP/UDP/ICMP session from original protocol/port info
+    /* Get TCP/UDP/SCTP/ICMP session from original protocol/port info
      * embedded in the ICMP Unreach message.  This is already decoded
-     * in p->orig_foo.  TCP/UDP ports are decoded as p->orig_sp/dp.
+     * in p->orig_foo.  TCP/UDP/SCTP ports are decoded as p->orig_sp/dp.
      */
     skey.protocol = GET_ORIG_IPH_PROTO(p);
     sport = p->orig_sp;
@@ -365,6 +366,10 @@ static int ProcessIcmpUnreach(Packet *p)
         /* Lookup a UDP session */
         ssn = GetLWUdpSession(&skey);
         break;
+    case IPPROTO_SCTP:
+        /* Lookup an SCTP session */
+        ssn = GetLWSctpSession(&skey);
+        break;
     case IPPROTO_ICMP:
         /* Lookup a ICMP session */
         ssn = GetLWSessionFromKey(icmp_lws_cache, &skey);
diff --git a/src/preprocessors/Stream5/snort_stream5_sctp.c b/src/preprocessors/Stream5/snort_stream5_sctp.c
new file mode 100644
index 0000000..7ddfe1d
--- /dev/null
+++ b/src/preprocessors/Stream5/snort_stream5_sctp.c
@@ -0,0 +1,847 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+ * Copyright (C) 2005-2013 Sourcefire, Inc.
+ * Author: Joshua Kinard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.  You may not use, modify or
+ * distribute this program under any other version of the GNU General
+ * Public License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ ****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "sf_types.h"
+#include "snort_debug.h"
+#include "detect.h"
+#include "plugbase.h"
+#include "mstring.h"
+#include "sfxhash.h"
+#include "util.h"
+#include "decode.h"
+#include "compiler.h"
+
+#include "stream5_common.h"
+#include "stream_api.h"
+#include "snort_stream5_session.h"
+#include "stream_expect.h"
+#include "snort_stream5_sctp.h"
+
+#include "plugin_enum.h"
+#include "rules.h"
+#include "treenodes.h"
+#include "snort.h"
+#include "active.h"
+
+#include "portscan.h" /* To know when to create sessions for all SCTP */
+
+#include "dynamic-plugins/sp_dynamic.h"
+
+#include "profiler.h"
+#include "sfPolicy.h"
+#include "stream5_ha.h"
+
+#ifdef PERF_PROFILING
+PreprocStats s5SctpPerfStats;
+#endif
+
+/*  M A C R O S  **************************************************/
+/* actions */
+#define ACTION_NOTHING                  0x00000000
+
+/* sender/responder ip/port dereference */
+#define sctp_sender_ip lwSsn->client_ip
+#define sctp_sender_port lwSsn->client_port
+#define sctp_responder_ip lwSsn->server_ip
+#define sctp_responder_port lwSsn->server_port
+
+/*  D A T A  S T R U C T U R E S  ***********************************/
+typedef struct _SctpSession
+{
+    Stream5LWSession *lwSsn;
+
+    struct timeval ssn_time;
+
+    //uint8_t    c_ttl;
+    //uint8_t    s_ttl;
+
+} SctpSession;
+
+
+/*  G L O B A L S  **************************************************/
+Stream5SessionCache *sctp_lws_cache = NULL;
+static MemPool sctp_session_mempool;
+
+/*  P R O T O T Y P E S  ********************************************/
+static void Stream5ParseSctpArgs(Stream5SctpConfig *, char *, Stream5SctpPolicy *);
+static void Stream5PrintSctpConfig(Stream5SctpPolicy *);
+static int ProcessSctp(Stream5LWSession *, Packet *, Stream5SctpPolicy *, SFXHASH_NODE *);
+
+//-------------------------------------------------------------------------
+// sctp ha stuff
+// TBD there may be some refactoring possible once tcp, udp, sctp, and icmp
+// are complete
+
+static Stream5LWSession *Stream5SCTPCreateSession(const SessionKey *key)
+{
+    setRuntimePolicy(getDefaultPolicy());
+
+    return NewLWSession(sctp_lws_cache, NULL, key, NULL);
+}
+
+static int Stream5SCTPDeleteSession(const SessionKey *key)
+{
+    Stream5LWSession *lwssn = GetLWSessionFromKey(sctp_lws_cache, key);
+
+    if ( lwssn && !Stream5SetRuntimeConfiguration(lwssn, lwssn->protocol) )
+        DeleteLWSession(sctp_lws_cache, lwssn, "ha sync");
+
+    return 0;
+}
+
+#ifdef ENABLE_HA
+
+static HA_Api ha_sctp_api = {
+    /*.get_lws = */ GetLWSctpSession,
+    /*.create_session = */ Stream5SCTPCreateSession,
+    /*.deactivate_session = */ NULL,
+    /*.delete_session = */ Stream5SCTPDeleteSession,
+};
+
+#endif
+
+//-------------------------------------------------------------------------
+
+void Stream5InitSctp(Stream5GlobalConfig *gconfig)
+{
+    if (unlikely(gconfig == NULL))
+        return;
+
+    /* Now SCTP */
+    if ((sctp_lws_cache == NULL) && (gconfig->track_sctp_sessions))
+    {
+        sctp_lws_cache = InitLWSessionCache(gconfig->max_sctp_sessions, gconfig->sctp_cache_pruning_timeout,
+                                            gconfig->sctp_cache_nominal_timeout, 5, 0, &SctpSessionCleanup);
+
+        if(unlikely(!sctp_lws_cache))
+        {
+            FatalError("Unable to init stream5 SCTP session cache, no SCTP "
+                       "stream inspection!\n");
+        }
+
+        if (unlikely(mempool_init(&sctp_session_mempool,
+                    gconfig->max_sctp_sessions, sizeof(SctpSession)) != 0))
+        {
+            FatalError("%s(%d) Could not initialize sctp session memory pool.\n",
+                    __FILE__, __LINE__);
+        }
+    }
+#ifdef ENABLE_HA
+    ha_set_api(IPPROTO_SCTP, &ha_sctp_api);
+#endif
+}
+
+void Stream5SctpPolicyInit(Stream5SctpConfig *config, char *args)
+{
+    Stream5SctpPolicy *s5SctpPolicy;
+
+    if (unlikely(config == NULL))
+        return;
+
+    s5SctpPolicy = (Stream5SctpPolicy *)SnortAlloc(sizeof(Stream5SctpPolicy));
+
+    Stream5ParseSctpArgs(config, args, s5SctpPolicy);
+
+    config->num_policies++;
+
+    /* Now add this context to the internal list */
+    if (config->policy_list == NULL)
+    {
+        config->policy_list =
+            (Stream5SctpPolicy **)SnortAlloc(sizeof(Stream5SctpPolicy *));
+    }
+    else
+    {
+        Stream5SctpPolicy **tmpPolicyList =
+            (Stream5SctpPolicy **)SnortAlloc(sizeof(Stream5SctpPolicy *) * config->num_policies);
+
+        memcpy(tmpPolicyList, config->policy_list,
+               sizeof(Stream5SctpPolicy *) * (config->num_policies - 1));
+
+        free(config->policy_list);
+
+        config->policy_list = tmpPolicyList;
+    }
+
+    config->policy_list[config->num_policies - 1] = s5SctpPolicy;
+
+    Stream5PrintSctpConfig(s5SctpPolicy);
+
+#ifdef REG_TEST
+    LogMessage("    SCTP Session Size: %lu\n",sizeof(SctpSession));
+#endif
+}
+
+static void Stream5ParseSctpArgs(Stream5SctpConfig *config, char *args, Stream5SctpPolicy *s5SctpPolicy)
+{
+    char **toks;
+    int num_toks;
+    int i;
+    char *index;
+    char **stoks = NULL;
+    int s_toks;
+    char *endPtr = NULL;
+
+    if (s5SctpPolicy == NULL)
+        return;
+
+    s5SctpPolicy->session_timeout = S5_DEFAULT_SSN_TIMEOUT;
+    s5SctpPolicy->flags = 0;
+
+    if(args != NULL && strlen(args) != 0)
+    {
+        toks = mSplit(args, ",", 6, &num_toks, 0);
+
+        i=0;
+
+        while(i < num_toks)
+        {
+            index = toks[i];
+
+            while(isspace((int)*index)) index++;
+
+            stoks = mSplit(index, " ", 3, &s_toks, 0);
+
+            if (s_toks == 0)
+            {
+                FatalError("%s(%d) => Missing parameter in Stream5 SCTP config.\n",
+                    file_name, file_line);
+            }
+
+            if(!strcasecmp(stoks[0], "timeout"))
+            {
+                if(stoks[1])
+                {
+                    s5SctpPolicy->session_timeout = strtoul(stoks[1], &endPtr, 10);
+                }
+
+                if (!stoks[1] || (endPtr == &stoks[1][0]))
+                {
+                    FatalError("%s(%d) => Invalid timeout in config file.  Integer parameter required.\n",
+                            file_name, file_line);
+                }
+
+                if ((s5SctpPolicy->session_timeout > S5_MAX_SSN_TIMEOUT) ||
+                    (s5SctpPolicy->session_timeout < S5_MIN_SSN_TIMEOUT))
+                {
+                    FatalError("%s(%d) => Invalid timeout in config file.  "
+                        "Must be between %d and %d\n",
+                        file_name, file_line,
+                        S5_MIN_SSN_TIMEOUT, S5_MAX_SSN_TIMEOUT);
+                }
+
+                if (s_toks > 2)
+                {
+                    FatalError("%s(%d) => Invalid Stream5 SCTP Policy option.  Missing comma?\n",
+                        file_name, file_line);
+                }
+            }
+            else if (!strcasecmp(stoks[0], "ignore_any_rules"))
+            {
+                s5SctpPolicy->flags |= STREAM5_CONFIG_IGNORE_ANY;
+
+                if (s_toks > 1)
+                {
+                    FatalError("%s(%d) => Invalid Stream5 SCTP Policy option.  Missing comma?\n",
+                        file_name, file_line);
+                }
+            }
+            else
+            {
+                FatalError("%s(%d) => Invalid Stream5 SCTP Policy option\n",
+                            file_name, file_line);
+            }
+
+            mSplitFree(&stoks, s_toks);
+            i++;
+        }
+
+        mSplitFree(&toks, num_toks);
+    }
+
+    if (s5SctpPolicy->bound_addrs == NULL)
+    {
+        if (config->default_policy != NULL)
+        {
+            FatalError("%s(%d) => Default Stream5 SCTP Policy already set. "
+                       "This policy must be bound to a specific host or "
+                       "network.\n", file_name, file_line);
+        }
+
+        config->default_policy = s5SctpPolicy;
+    }
+    else
+    {
+        if (s5SctpPolicy->flags & STREAM5_CONFIG_IGNORE_ANY)
+        {
+            FatalError("%s(%d) => \"ignore_any_rules\" option can be used only"
+                       " with Default Stream5 SCTP Policy\n", file_name, file_line);
+        }
+    }
+}
+
+static void Stream5PrintSctpConfig(Stream5SctpPolicy *s5SctpPolicy)
+{
+    LogMessage("Stream5 SCTP Policy config:\n");
+    LogMessage("    Timeout: %d seconds\n", s5SctpPolicy->session_timeout);
+    if (s5SctpPolicy->flags)
+    {
+        LogMessage("    Options:\n");
+        if (s5SctpPolicy->flags & STREAM5_CONFIG_IGNORE_ANY)
+        {
+            LogMessage("        Ignore Any -> Any Rules: YES\n");
+        }
+    }
+    //IpAddrSetPrint("    Bound Addresses:", s5SctpPolicy->bound_addrs);
+}
+
+
+int Stream5VerifySctpConfig(struct _SnortConfig *sc, Stream5SctpConfig *config, tSfPolicyId policyId)
+{
+    if (config == NULL)
+        return -1;
+
+    if (!sctp_lws_cache)
+        return -1;
+
+    if (config->num_policies == 0)
+        return -1;
+
+    /* Post-process SCTP rules to establish SCTP ports to inspect. */
+    setPortFilterList(sc, config->port_filter, IPPROTO_SCTP,
+            (config->default_policy->flags & STREAM5_CONFIG_IGNORE_ANY), policyId);
+
+    //printf ("SCTP Ports with Inspection/Monitoring\n");
+    //s5PrintPortFilter(config->port_filter);
+    return 0;
+}
+
+#ifdef DEBUG_STREAM5
+static void PrintSctpSession(SctpSession *us)
+{
+    LogMessage("SctpSession:\n");
+    LogMessage("    ssn_time:           %lu\n", us->ssn_time.tv_sec);
+    LogMessage("    sender IP:          0x%08X\n", us->sctp_sender_ip);
+    LogMessage("    responder IP:          0x%08X\n", us->sctp_responder_ip);
+    LogMessage("    sender port:        %d\n", us->sctp_sender_port);
+    LogMessage("    responder port:        %d\n", us->sctp_responder_port);
+
+    LogMessage("    flags:              0x%X\n", us->lwSsn->session_flags);
+}
+#endif
+
+Stream5LWSession *GetLWSctpSession(const SessionKey *key)
+{
+    return GetLWSessionFromKey(sctp_lws_cache, key);
+}
+
+void SctpSessionCleanup(Stream5LWSession *lwssn)
+{
+    SctpSession *sctpssn = NULL;
+
+    if (lwssn->ha_state.session_flags & SSNFLAG_PRUNED)
+    {
+        CloseStreamSession(&sfBase, SESSION_CLOSED_PRUNED);
+    }
+    else if (lwssn->ha_state.session_flags & SSNFLAG_TIMEDOUT)
+    {
+        CloseStreamSession(&sfBase, SESSION_CLOSED_TIMEDOUT);
+    }
+    else
+    {
+        CloseStreamSession(&sfBase, SESSION_CLOSED_NORMALLY);
+    }
+
+    if (lwssn->proto_specific_data)
+        sctpssn = (SctpSession *)lwssn->proto_specific_data->data;
+
+    if (!sctpssn)
+    {
+        /* Huh? */
+        return;
+    }
+
+    /* Cleanup the proto specific data */
+    mempool_free(&sctp_session_mempool, lwssn->proto_specific_data);
+    lwssn->proto_specific_data = NULL;
+    lwssn->session_state = STREAM5_STATE_NONE;
+    lwssn->ha_state.session_flags = SSNFLAG_NONE;
+    lwssn->expire_time = 0;
+    lwssn->ha_state.ignore_direction = 0;
+
+    Stream5ResetFlowBits(lwssn);
+    FreeLWApplicationData(lwssn);
+
+    s5stats.sctp_sessions_released++;
+
+    RemoveSCTPSession(&sfBase);
+}
+
+uint32_t Stream5GetSctpPrunes(void)
+{
+    return sctp_lws_cache ? sctp_lws_cache->prunes : s5stats.sctp_prunes;
+}
+
+void Stream5ResetSctpPrunes(void)
+{
+    if ( sctp_lws_cache )
+        sctp_lws_cache->prunes = 0;
+}
+
+void Stream5ResetSctp(void)
+{
+    PurgeLWSessionCache(sctp_lws_cache);
+    mempool_clean(&sctp_session_mempool);
+}
+
+void Stream5CleanSctp(void)
+{
+    if ( sctp_lws_cache )
+        s5stats.sctp_prunes = sctp_lws_cache->prunes;
+
+    /* Clean up hash table -- delete all sessions */
+    DeleteLWSessionCache(sctp_lws_cache);
+    sctp_lws_cache = NULL;
+
+    mempool_destroy(&sctp_session_mempool);
+}
+
+static int NewSctpSession(Packet *p,
+                         Stream5LWSession *lwssn,
+                         Stream5SctpPolicy *s5SctpPolicy)
+{
+    SctpSession *tmp;
+    MemBucket *tmpBucket;
+    /******************************************************************
+     * create new sessions
+     *****************************************************************/
+    tmpBucket = mempool_alloc(&sctp_session_mempool);
+    if (tmpBucket == NULL)
+        return -1;
+
+    tmp = tmpBucket->data;
+    DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
+                "Creating new session tracker!\n"););
+
+    tmp->ssn_time.tv_sec = p->pkth->ts.tv_sec;
+    tmp->ssn_time.tv_usec = p->pkth->ts.tv_usec;
+    lwssn->ha_state.session_flags |= SSNFLAG_SEEN_SENDER;
+
+    DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
+                "adding SctpSession to lightweight session\n"););
+    lwssn->proto_specific_data = tmpBucket;
+    lwssn->protocol = GET_IPH_PROTO(p);
+    lwssn->ha_state.direction = FROM_SENDER;
+    tmp->lwSsn = lwssn;
+
+#ifdef DEBUG_STREAM5
+    PrintSctpSession(tmp);
+#endif
+    Stream5SetExpire(p, lwssn, s5SctpPolicy->session_timeout);
+
+    s5stats.sctp_sessions_created++;
+
+    AddSCTPSession(&sfBase);
+    if (perfmon_config && (perfmon_config->perf_flags & SFPERF_FLOWIP))
+        UpdateFlowIPState(&sfFlow, IP_ARG(lwssn->client_ip), IP_ARG(lwssn->server_ip), SFS_STATE_SCTP_CREATED);
+
+    return 0;
+}
+
+//-------------------------------------------------------------------------
+/*
+ * Main entry point for SCTP
+ */
+int Stream5ProcessSctp(Packet *p, Stream5LWSession *lwssn,
+                      Stream5SctpPolicy *s5SctpPolicy, SessionKey *skey)
+{
+    SFXHASH_NODE *hash_node = NULL;
+    PROFILE_VARS;
+
+// XXX-IPv6 Stream5ProcessSCTP debugging
+
+    PREPROC_PROFILE_START(s5SctpPerfStats);
+
+    if (s5SctpPolicy == NULL)
+    {
+        int policyIndex;
+
+        /* Find an Sctp policy for this packet */
+        for (policyIndex = 0; policyIndex < s5_sctp_eval_config->num_policies; policyIndex++)
+        {
+            s5SctpPolicy = s5_sctp_eval_config->policy_list[policyIndex];
+
+            if (s5SctpPolicy->bound_addrs == NULL)
+                continue;
+
+            /*
+             * Does this policy handle packets to this IP address?
+             */
+            if(sfvar_ip_in(s5SctpPolicy->bound_addrs, GET_DST_IP(p)))
+            {
+                DEBUG_WRAP(DebugMessage(DEBUG_STREAM,
+                                        "[Stream5] Found sctp policy in IpAddrSet\n"););
+                break;
+            }
+        }
+
+        if (policyIndex == s5_sctp_eval_config->num_policies)
+            s5SctpPolicy = s5_sctp_eval_config->default_policy;
+
+        if (!s5SctpPolicy)
+        {
+            DEBUG_WRAP(DebugMessage(DEBUG_STREAM,
+                                    "[Stream5] Could not find Sctp Policy context "
+                                    "for IP %s\n", inet_ntoa(GET_DST_ADDR(p))););
+            PREPROC_PROFILE_END(s5SctpPerfStats);
+            return 0;
+        }
+
+        /* If this is an existing LWSession that didn't have its policy set, set it now to save time in the future. */
+        if (lwssn != NULL && lwssn->policy == NULL)
+            lwssn->policy = s5SctpPolicy;
+    }
+
+    /* SCTP Sessions required */
+    if (lwssn == NULL)
+    {
+        if ((isPacketFilterDiscard(p, s5SctpPolicy->flags & STREAM5_CONFIG_IGNORE_ANY) == PORT_MONITOR_PACKET_DISCARD)
+                && !StreamExpectIsExpected(p, &hash_node))
+        {
+            //ignore the packet
+            UpdateFilteredPacketStats(&sfBase, IPPROTO_SCTP);
+            PREPROC_PROFILE_END(s5SctpPerfStats);
+            return 0;
+        }
+        /* Create a new session, mark SENDER seen */
+        lwssn = NewLWSession(sctp_lws_cache, p, skey, (void *)s5SctpPolicy);
+        s5stats.total_sctp_sessions++;
+    }
+    else
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
+            "Stream5: Retrieved existing session object.\n"););
+    }
+
+    if (!lwssn)
+    {
+        LogMessage("Stream5: Failed to retrieve session object.  Out of memory?\n");
+        PREPROC_PROFILE_END(s5SctpPerfStats);
+        return -1;
+    }
+
+    p->ssnptr = lwssn;
+
+    /*
+     * Check if the session is expired.
+     * Should be done before we do something with the packet...
+     * ie, Insert a packet, or handle state change SYN, FIN, RST, etc.
+     */
+    if ((lwssn->session_state & STREAM5_STATE_TIMEDOUT) ||
+        Stream5Expire(p, lwssn))
+    {
+        lwssn->ha_state.session_flags |= SSNFLAG_TIMEDOUT;
+
+        /* Session is timed out */
+        DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
+                    "Stream5 SCTP session timedout!\n"););
+
+#ifdef ENABLE_HA
+        /* Notify the HA peer of the session cleanup/reset by way of a deletion notification. */
+        PREPROC_PROFILE_TMPEND(s5SctpPerfStats);
+        Stream5HANotifyDeletion(lwssn);
+        PREPROC_PROFILE_TMPSTART(s5SctpPerfStats);
+        lwssn->ha_flags = (HA_FLAG_NEW | HA_FLAG_MODIFIED | HA_FLAG_MAJOR_CHANGE);
+#endif
+
+        /* Clean it up */
+        SctpSessionCleanup(lwssn);
+
+        ProcessSctp(lwssn, p, s5SctpPolicy, hash_node);
+    }
+    else
+    {
+        ProcessSctp(lwssn, p, s5SctpPolicy, hash_node);
+        DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
+                    "Finished Stream5 SCTP cleanly!\n"
+                    "---------------------------------------------------\n"););
+    }
+    MarkupPacketFlags(p, lwssn);
+    Stream5SetExpire(p, lwssn, s5SctpPolicy->session_timeout);
+
+    PREPROC_PROFILE_END(s5SctpPerfStats);
+
+    return 0;
+}
+
+static int ProcessSctp(Stream5LWSession *lwssn, Packet *p,
+        Stream5SctpPolicy *s5SctpPolicy, SFXHASH_NODE *hash_node)
+{
+    char ignore;
+    SctpSession *sctpssn = NULL;
+
+    if (lwssn->protocol != IPPROTO_SCTP)
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
+                    "Lightweight session not SCTP on SCTP packet\n"););
+        return ACTION_NOTHING;
+    }
+
+    if (lwssn->ha_state.session_flags & (SSNFLAG_DROP_CLIENT|SSNFLAG_DROP_SERVER))
+    {
+        /* Got a packet on a session that was dropped (by a rule). */
+        GetLWPacketDirection(p, lwssn);
+
+        /* Drop this packet */
+        if (((p->packet_flags & PKT_FROM_SERVER) &&
+             (lwssn->ha_state.session_flags & SSNFLAG_DROP_SERVER)) ||
+            ((p->packet_flags & PKT_FROM_CLIENT) &&
+             (lwssn->ha_state.session_flags & SSNFLAG_DROP_CLIENT)))
+        {
+            DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
+                        "Blocking %s packet as session was blocked\n",
+                        p->packet_flags & PKT_FROM_SERVER ?
+                        "server" : "client"););
+            DisableDetect(p);
+            /* Still want to add this number of bytes to totals */
+            SetPreprocBit(p, PP_PERFMONITOR);
+
+            if ( lwssn->ha_state.session_flags & SSNFLAG_FORCE_BLOCK )
+                Active_ForceDropPacket();
+            else
+                Active_DropPacket(p);
+
+#ifdef ACTIVE_RESPONSE
+            Stream5ActiveResponse(p, lwssn);
+#endif
+            return ACTION_NOTHING;
+        }
+    }
+
+    if (lwssn->proto_specific_data != NULL)
+        sctpssn = (SctpSession *)lwssn->proto_specific_data->data;
+
+    if (sctpssn == NULL)
+    {
+        lwssn->ha_state.direction = FROM_SENDER;
+        IP_COPY_VALUE(lwssn->client_ip, GET_SRC_IP(p));
+        lwssn->client_port = p->sctph->sh_sport;
+        IP_COPY_VALUE(lwssn->server_ip, GET_DST_IP(p));
+        lwssn->server_port = p->sctph->sh_dport;
+
+        if (NewSctpSession(p, lwssn, s5SctpPolicy) == -1)
+            return ACTION_NOTHING;
+        sctpssn = (SctpSession *)lwssn->proto_specific_data->data;
+
+        /* Check if the session is to be ignored */
+        if (hash_node)
+            ignore = StreamExpectProcessNode(p, lwssn, hash_node);
+        else
+            ignore = StreamExpectCheck(p, lwssn);
+        if (ignore)
+        {
+            /* Set the directions to ignore... */
+            lwssn->ha_state.ignore_direction = ignore;
+            DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
+                        "Stream5: Ignoring packet from %d. "
+                        "Marking session marked as ignore.\n",
+                        p->packet_flags & PKT_FROM_CLIENT? "sender" : "responder"););
+            Stream5DisableInspection(lwssn, p);
+            return ACTION_NOTHING;
+        }
+    }
+
+    /* figure out direction of this packet */
+    GetLWPacketDirection(p, lwssn);
+
+    if (((p->packet_flags & PKT_FROM_SERVER) && (lwssn->ha_state.ignore_direction & SSN_DIR_FROM_CLIENT)) ||
+        ((p->packet_flags & PKT_FROM_CLIENT) && (lwssn->ha_state.ignore_direction & SSN_DIR_FROM_SERVER)))
+    {
+        Stream5DisableInspection(lwssn, p);
+        DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
+                    "Stream5 Ignoring packet from %d. "
+                    "Session marked as ignore\n",
+                    p->packet_flags & PKT_FROM_CLIENT? "sender" : "responder"););
+        return ACTION_NOTHING;
+    }
+
+    /* if both seen, mark established */
+    if(p->packet_flags & PKT_FROM_SERVER)
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
+                    "Stream5: Updating on packet from responder\n"););
+        lwssn->ha_state.session_flags |= SSNFLAG_SEEN_RESPONDER;
+#ifdef ACTIVE_RESPONSE
+        SetTTL(lwssn, p, 0);
+#endif
+    }
+    else
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
+                    "Stream5: Updating on packet from client\n"););
+        /* if we got here we had to see an INIT chunk already... */
+        lwssn->ha_state.session_flags |= SSNFLAG_SEEN_SENDER;
+#ifdef ACTIVE_RESPONSE
+        SetTTL(lwssn, p, 1);
+#endif
+    }
+
+    if (!(lwssn->ha_state.session_flags & SSNFLAG_ESTABLISHED))
+    {
+        if ((lwssn->ha_state.session_flags & SSNFLAG_SEEN_SENDER) &&
+            (lwssn->ha_state.session_flags & SSNFLAG_SEEN_RESPONDER))
+        {
+            lwssn->ha_state.session_flags |= SSNFLAG_ESTABLISHED;
+        }
+    }
+
+    return ACTION_NOTHING;
+}
+
+void SctpUpdateDirection(Stream5LWSession *ssn, char dir,
+                        snort_ip_p ip, uint16_t port)
+{
+    SctpSession *sctpssn = (SctpSession *)ssn->proto_specific_data->data;
+    snort_ip tmpIp;
+    uint16_t tmpPort;
+
+    if (IP_EQUALITY(&sctpssn->sctp_sender_ip, ip) && (sctpssn->sctp_sender_port == port))
+    {
+        if ((dir == SSN_DIR_FROM_SENDER) && (ssn->ha_state.direction == SSN_DIR_FROM_SENDER))
+        {
+            /* Direction already set as SENDER */
+            return;
+        }
+    }
+    else if (IP_EQUALITY(&sctpssn->sctp_responder_ip, ip) && (sctpssn->sctp_responder_port == port))
+    {
+        if ((dir == SSN_DIR_FROM_RESPONDER) && (ssn->ha_state.direction == SSN_DIR_FROM_RESPONDER))
+        {
+            /* Direction already set as RESPONDER */
+            return;
+        }
+    }
+
+    /* Swap them -- leave ssn->ha_state.direction the same */
+    tmpIp = sctpssn->sctp_sender_ip;
+    tmpPort = sctpssn->sctp_sender_port;
+    sctpssn->sctp_sender_ip = sctpssn->sctp_responder_ip;
+    sctpssn->sctp_sender_port = sctpssn->sctp_responder_port;
+    sctpssn->sctp_responder_ip = tmpIp;
+    sctpssn->sctp_responder_port = tmpPort;
+}
+
+void s5SctpSetPortFilterStatus(struct _SnortConfig *sc, unsigned short port, uint16_t status, tSfPolicyId policyId, int parsing)
+{
+    Stream5Config *config;
+    Stream5SctpConfig *sctp_config;
+
+#ifdef SNORT_RELOAD
+    tSfPolicyUserContextId s5_swap_config;
+    if (parsing && ((s5_swap_config = (tSfPolicyUserContextId)GetReloadStreamConfig(sc)) != NULL))
+        config = (Stream5Config *)sfPolicyUserDataGet(s5_swap_config, policyId);
+    else
+#endif
+    config = (Stream5Config *)sfPolicyUserDataGet(s5_config, policyId);
+
+    if (config == NULL)
+        return;
+
+    sctp_config = config->sctp_config;
+    if (sctp_config == NULL)
+        return;
+
+    sctp_config->port_filter[port] |= status;
+}
+
+void s5SctpUnsetPortFilterStatus(struct _SnortConfig *sc, unsigned short port, uint16_t status, tSfPolicyId policyId, int parsing)
+{
+    Stream5Config *config;
+    Stream5SctpConfig *sctp_config;
+
+#ifdef SNORT_RELOAD
+    tSfPolicyUserContextId s5_swap_config;
+    if (parsing && ((s5_swap_config = (tSfPolicyUserContextId)GetReloadStreamConfig(sc)) != NULL))
+        config = (Stream5Config *)sfPolicyUserDataGet(s5_swap_config, policyId);
+    else
+#endif
+    config = (Stream5Config *)sfPolicyUserDataGet(s5_config, policyId);
+
+    if (config == NULL)
+        return;
+
+    sctp_config = config->sctp_config;
+    if (sctp_config == NULL)
+        return;
+
+    sctp_config->port_filter[port] &= ~status;
+}
+
+int s5SctpGetPortFilterStatus(struct _SnortConfig *sc, unsigned short port, tSfPolicyId policyId, int parsing)
+{
+    Stream5Config *config;
+    Stream5SctpConfig *sctp_config;
+
+#ifdef SNORT_RELOAD
+    tSfPolicyUserContextId s5_swap_config;
+    if (parsing && ((s5_swap_config = (tSfPolicyUserContextId)GetReloadStreamConfig(sc)) != NULL))
+        config = (Stream5Config *)sfPolicyUserDataGet(s5_swap_config, policyId);
+    else
+#endif
+    config = (Stream5Config *)sfPolicyUserDataGet(s5_config, policyId);
+
+    if (config == NULL)
+        return PORT_MONITOR_NONE;
+
+    sctp_config = config->sctp_config;
+    if (sctp_config == NULL)
+        return PORT_MONITOR_NONE;
+
+    return (int)sctp_config->port_filter[port];
+}
+
+void Stream5SctpConfigFree(Stream5SctpConfig *config)
+{
+    int i;
+
+    if (config == NULL)
+        return;
+
+    /* Cleanup SCTP Policies and the list */
+    for (i = 0; i < config->num_policies; i++)
+    {
+        Stream5SctpPolicy *policy = config->policy_list[i];
+
+        if (policy->bound_addrs != NULL)
+            sfvar_free(policy->bound_addrs);
+        free(policy);
+    }
+
+    free(config->policy_list);
+    free(config);
+}
+
diff --git a/src/preprocessors/Stream5/snort_stream5_sctp.h b/src/preprocessors/Stream5/snort_stream5_sctp.h
new file mode 100644
index 0000000..dbdba04
--- /dev/null
+++ b/src/preprocessors/Stream5/snort_stream5_sctp.h
@@ -0,0 +1,287 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+ * Copyright (C) 2005-2013 Sourcefire, Inc.
+ * Author: Joshua Kinard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.  You may not use, modify or
+ * distribute this program under any other version of the GNU General
+ * Public License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ ****************************************************************************/
+
+#ifndef SNORT_STREAM5_SCTP_H_
+#define SNORT_STREAM5_SCTP_H_
+
+#include "ipv6_port.h"
+#include "stream5_common.h"
+#include "stream5_paf.h"
+#include "sfPolicy.h"
+
+/* XXX: Not implemented yet! */
+/**
+ * struct SctpDataBlock
+ * @sip: Snort IP structure for source.
+ * @dip: Snort IP structure for destination.
+ * @vtag: Verification tag from the SCTP common header.
+ * @tsn: Transmission Sequence Number from the DATA chunk.
+ * @length: Chunk length.
+ * @sid: Stream ID.
+ * @ssn: Stream Sequence Number.
+ * @flags: DATA chunk flags field (IUBE).
+ * @pad: Padding to align on a 32-bit boundary.
+ */
+/*
+typedef struct _SctpDataBlock
+{
+    snort_ip     sip;
+    snort_ip     dip;
+    uint32_t     vtag;
+    uint32_t     tsn;
+    uint16_t     length;
+    uint16_t     sid;
+    uint16_t     ssn;
+    uint8_t      flags;
+    uint8_t      pad;
+} SctpDataBlock;
+*/
+
+/* XXX: Not implemented yet! */
+/**
+ * struct SctpStreamSegment
+ * @pkth: Pointer to packet header from DAQ.
+ * @pktOrig: Pointer to original Packet structure.
+ * @pkt: Pointer to Packet structure (modified?).
+ * @data: Pointer to data (?).
+ * @payload: Pointer to packet payload.
+ * @prev: Pointer to previous SctpStreamMessage.
+ * @next: Pointer to next SctpStreamMessage.
+ * @vtag: Verification tag.
+ * @tsn: Transmission Sequence Number.
+ * @length: Chunk length.
+ * @sid: Stream ID.
+ * @ssn: Stream Sequence Number.
+ * @cflags: Chunk flags.
+ * @msgflags: SctpStreamMessage flags.
+ */
+/*
+typedef struct _SctpStreamMessage
+{
+    DAQ_PktHdr_t pkth;
+    uint8_t      *pktOrig;
+    uint8_t      *pkt;
+    uint8_t      *data;
+    uint8_t      *payload;
+    struct       _SctpStreamMessage *prev;
+    struct       _SctpStreamMessage *next;
+    uint32_t     vtag;
+    uint32_t     tsn;
+    uint32_t     caplen;
+    uint16_t     length;
+    uint16_t     sid;
+    uint16_t     ssn;
+    uint8_t      cflags;
+    uint8_t      msgflags;
+} SctpStreamMessage;
+*/
+
+
+#define SCTP_S5_HMAC_SHA1           0x0001        /* HMAC SHA-1 */
+#define SCTP_S5_HMAC_SHA256         0x0002        /* HMAC SHA-256 */
+
+#define SCTP_S5_EXT_ASCONF          0x0001        /* ASCONF chunk supported. */
+#define SCTP_S5_EXT_ASCONF_ACK      0x0002        /* ASCONF-ACK chunk supported. */
+#define SCTP_S5_EXT_AUTH            0x0004        /* AUTH chunk supported. */
+#define SCTP_S5_EXT_FWD_TSN         0x0008        /* FORWARD-TSN chunk supported. */
+#define SCTP_S5_EXT_PKTDROP         0x0010        /* PKTDROP chunk supported. */
+#define SCTP_S5_EXT_RECONFIG        0x0020        /* RE-CONFIG chunk supported. */
+
+#define SCTP_S5_FLAG_ECN            0x0001        /* ECN parameter (0x8000). */
+#define SCTP_S5_FLAG_NONCE          0x0002        /* Nonce-Supported parameter (0x8001). */
+#define SCTP_S5_FLAG_FWD_TSN        0x0004        /* FORWARD-TSN parameter (0xc000). */
+
+#define SCTP_S5_ADDR_IPV4           0x0001        /* IPv4 addresses supported. */
+#define SCTP_S5_ADDR_IPV6           0x0002        /* IPv6 addresses supported. */
+#define SCTP_S5_ADDR_DNS            0x0004        /* Hostnames supported. */
+
+#define SCTP_S5_AUTH_DATA           0x00000001    /* Authenticated DATA chunk */
+#define SCTP_S5_AUTH_SACK           0x00000002    /* Authenticated SACK chunk */
+#define SCTP_S5_AUTH_HEARTBEAT      0x00000004    /* Authenticated HEARTBEAT chunk */
+#define SCTP_S5_AUTH_HEARTBEAT_ACK  0x00000008    /* Authenticated HEARTBEAT-ACK chunk */
+#define SCTP_S5_AUTH_ABORT          0x00000010    /* Authenticated ABORT chunk */
+#define SCTP_S5_AUTH_SHUTDOWN       0x00000020    /* Authenticated SHUTDOWN chunk */
+#define SCTP_S5_AUTH_SHUTDOWN_ACK   0x00000040    /* Authenticated SHUTDOWN-ACK chunk */
+#define SCTP_S5_AUTH_ERROR          0x00000080    /* Authenticated ERROR chunk */
+#define SCTP_S5_AUTH_ECNE           0x00000100    /* Authenticated ECNE chunk */
+#define SCTP_S5_AUTH_CWR            0x00000200    /* Authenticated CWR chunk */
+#define SCTP_S5_AUTH_ASCONF_ACK     0x00000400    /* Authenticated ASCONF-ACK chunk */
+#define SCTP_S5_AUTH_PKTDROP        0x00000800    /* Authenticated PKTDROP chunk */
+#define SCTP_S5_AUTH_RECONFIG       0x00001000    /* Authenticated RE-CONFIG chunk */
+#define SCTP_S5_AUTH_PAD            0x00002000    /* Authenticated PAD chunk */
+#define SCTP_S5_AUTH_FORWARD_TSN    0x00004000    /* Authenticated FORWARD-TSN chunk */
+#define SCTP_S5_AUTH_ASCONF         0x00008000    /* Authenticated ASCONF chunk */
+
+/* XXX: Not implemented yet! */
+/**
+ * struct SctpPeerProperties
+ * @extensions: uint16_t flag variable of supported SCTP extensions.
+ * @flags: uint16_t flag variable of miscellaneous SCTP bits (not extensions).
+ * @addrtypes: uint16_t flag variable of the supported address types.
+ * @cookie_len: uint16_t value to hold the size of the state cookie.
+ * @cookie: pointer to a MemBucket to hold the state cookie.
+ */
+/*
+typedef struct _SctpPeerBits
+{
+    uint32_t auth_chunks;
+    uint16_t extensions;
+    uint16_t flags;
+    uint16_t addr_types;
+    uint16_t hmac_types;
+    uint16_t cookie_len;
+    uint8_t pad;
+    uint8_t *cookie;
+} SctpPeerBits;
+*/
+
+/* XXX: Not implemented yet! */
+/**
+ * struct SctpPeers
+ * @client_bits: pointer to client-specific SctpPeerBits structure.
+ * @server_bits: pointer to server-specific SctpPeerBits structure.
+ */
+/*
+typedef struct _SctpPeers
+{
+    SctpPeerBits *client_bits;
+    SctpPeerBits *server_bits;
+} SctpPeers;
+*/
+
+/* XXX: Not implemented yet! */
+/**
+ * struct SctpStreamTracker
+ * @flush_mgr: Pointer to FlushMgr structure.
+ * @paf_state: Pointer to PAF_State structure (DO NOT MODIFY!)
+ * @alerts[]: Array of Stream5AlertInfo structures.
+ * @sctp_policy: Pointer to Stream5SctpPolicy structure.
+ * @msghead: Pointer to first queued message.
+ * @msgtail: Pointer to last queued message.
+ * @msgnext: Pointer to next queued message (to flush?).
+ * @vtag: Verification tag.
+ * @itag: Initiate tag from the INIT or INIT-ACK.
+ * @init_tsn: Initial TSN for this side of the conversation.
+ * @l_unsack_tsn: [Local]: UnSACK'ed TSN.
+ * @l_next_tsn: [Local]: Next expected TSN.
+ * @r_next_tsn: [Remote]: Next expected TSN.
+ * @msghead_tsn: TSN of first queued message.
+ * @msg_count: Number of currently queued messages.
+ * @msg_bytes_total: Total amount of message bytes queued.
+ * @msg_flush_count: Number of flushed queued messages.
+ * @macaddr: MAC Address.
+ * @flags: Bitmap flags (TF_*).
+ * @state: SCTP Stream State.
+ * @alert_count: Number of alerts stored (up to MAX_SESSION_ALERTS). 
+ * @pad: Padding to align on a 32-bit boundary.
+ */
+/*
+typedef struct _SctpStreamTracker
+{
+    FlushMgr  flush_mgr;
+    PAF_State paf_state;
+    Stream5AlertInfo alerts[MAX_SESSION_ALERTS];
+    Stream5SctpPolicy *sctp_policy;
+    SctpStreamMessage *msghead;
+    SctpStreamMessage *msgtail;
+    SctpStreamMessage *msgnext;
+    SctpPeerBits *peerbits;
+    uint64_t macaddr;
+    uint32_t vtag;
+    uint32_t itag;
+    uint32_t a_rwnd;
+    uint32_t init_tsn;
+    uint32_t l_unsack_tsn;
+    uint32_t l_next_tsn;
+    uint32_t r_next_tsn;
+    uint32_t msghead_tsn;
+    uint32_t msg_count;
+    uint32_t msg_bytes_total;
+    uint32_t msg_flush_count;
+    uint16_t nr_out_streams;
+    uint16_t nr_in_streams;
+    uint8_t  flags;
+    uint8_t  state;
+    uint8_t  alert_count;
+    uint8_t  pad;
+} SctpStreamTracker;
+*/
+
+/* XXX: Not implemented yet! */
+/**
+ * struct SctpAssociation
+ * @client: Pointer to SctpStreamTracker structure for the client.
+ * @server: Pointer to SctpStreamTracker structure for the server.
+ * @lwssn: Lightweight session data.
+ * @ssn_time: [DEBUG]: Session time.
+ */
+/*
+typedef struct _SctpAssociation
+{
+    SctpStreamTracker *client;
+    SctpStreamTracker *server;
+    Stream5LWSession *lwssn;
+#ifdef DEBUG
+    struct timeval ssn_time;
+#endif
+} SctpAssociation;
+*/
+
+
+/* Prototypes. */
+void Stream5CleanSctp(void);
+void Stream5ResetSctp(void);
+void Stream5InitSctp(Stream5GlobalConfig *);
+void Stream5SctpPolicyInit(Stream5SctpConfig *, char *);
+int Stream5VerifySctpConfig(struct _SnortConfig *, Stream5SctpConfig *, tSfPolicyId);
+int Stream5ProcessSctp(Packet *, Stream5LWSession *, Stream5SctpPolicy *, SessionKey *);
+void SctpUpdateDirection(Stream5LWSession *ssn, char dir,
+                        snort_ip_p ip, uint16_t port);
+Stream5LWSession *GetLWSctpSession(const SessionKey *key);
+void s5SctpSetPortFilterStatus(
+        struct _SnortConfig *sc,
+        unsigned short port,
+        uint16_t status,
+        tSfPolicyId policyId,
+        int parsing
+        );
+void s5SctpUnsetPortFilterStatus(
+        struct _SnortConfig *sc,
+        unsigned short port,
+        uint16_t status,
+        tSfPolicyId policyId,
+        int parsing
+        );
+int s5SctpGetPortFilterStatus(
+        struct _SnortConfig *sc,
+        unsigned short port,
+        tSfPolicyId policyId,
+        int parsing
+        );
+void Stream5SctpConfigFree(Stream5SctpConfig *);
+
+uint32_t Stream5GetSctpPrunes(void);
+void Stream5ResetSctpPrunes(void);
+void SctpSessionCleanup(Stream5LWSession *lwssn);
+
+#endif /* SNORT_STREAM5_SCTP_H_ */
diff --git a/src/preprocessors/Stream5/snort_stream5_session.c b/src/preprocessors/Stream5/snort_stream5_session.c
index 9b756fd..fa83430 100644
--- a/src/preprocessors/Stream5/snort_stream5_session.c
+++ b/src/preprocessors/Stream5/snort_stream5_session.c
@@ -4,6 +4,7 @@
 ** Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
 ** Copyright (C) 2005-2013 Sourcefire, Inc.
 ** AUTHOR: Steven Sturges <ssturges at sourcefire.com>
+** SCTP bits: Joshua Kinard
 **
 ** This program is free software; you can redistribute it and/or modify
 ** it under the terms of the GNU General Public License Version 2 as
@@ -107,7 +108,7 @@ int GetLWSessionKeyFromIpPort(
                     uint16_t srcPort,
                     snort_ip_p dstIP,
                     uint16_t dstPort,
-                    char proto,
+                    uint8_t proto,
                     uint16_t vlan,
                     uint32_t mplsId,
                     uint16_t addressSpaceId,
@@ -133,6 +134,7 @@ int GetLWSessionKeyFromIpPort(
         {
             case IPPROTO_TCP:
             case IPPROTO_UDP:
+            case IPPROTO_SCTP:
                 sport = srcPort;
                 dport = dstPort;
                 break;
@@ -210,6 +212,7 @@ int GetLWSessionKeyFromIpPort(
         {
             case IPPROTO_TCP:
             case IPPROTO_UDP:
+            case IPPROTO_SCTP:
                 sport = srcPort;
                 dport = dstPort;
                 break;
@@ -310,7 +313,7 @@ int GetLWSessionKeyFromIpPort(
 
 int GetLWSessionKey(Packet *p, SessionKey *key)
 {
-    char proto = GET_IPH_PROTO(p);
+    uint8_t proto = GET_IPH_PROTO(p);
     uint32_t mplsId = 0;
     uint16_t vlanId = 0;
     uint16_t sport = p->sp;
@@ -365,6 +368,17 @@ void GetLWPacketDirection(Packet *p, Stream5LWSession *ssn)
                     p->packet_flags |= PKT_FROM_SERVER;
                 }
             }
+            else if (GET_IPH_PROTO(p) == IPPROTO_SCTP)
+            {
+                if (p->sctph->sh_sport == ssn->client_port)
+                {
+                    p->packet_flags |= PKT_FROM_CLIENT;
+                }
+                else
+                {
+                    p->packet_flags |= PKT_FROM_SERVER;
+                }
+            }
             else
             {
                 p->packet_flags |= PKT_FROM_CLIENT;
@@ -394,6 +408,17 @@ void GetLWPacketDirection(Packet *p, Stream5LWSession *ssn)
                     p->packet_flags |= PKT_FROM_CLIENT;
                 }
             }
+            else if (GET_IPH_PROTO(p) == IPPROTO_SCTP)
+            {
+                if (p->sctph->sh_dport == ssn->client_port)
+                {
+                    p->packet_flags |= PKT_FROM_SERVER;
+                }
+                else
+                {
+                    p->packet_flags |= PKT_FROM_CLIENT;
+                }
+            }
             else
             {
                 p->packet_flags |= PKT_FROM_SERVER;
@@ -426,6 +451,17 @@ void GetLWPacketDirection(Packet *p, Stream5LWSession *ssn)
                     p->packet_flags |= PKT_FROM_SERVER;
                 }
             }
+            else if (GET_IPH_PROTO(p) == IPPROTO_SCTP)
+            {
+                if (p->sctph->sh_sport == ssn->client_port)
+                {
+                    p->packet_flags |= PKT_FROM_CLIENT;
+                }
+                else
+                {
+                    p->packet_flags |= PKT_FROM_SERVER;
+                }
+            }
             else
             {
                 p->packet_flags |= PKT_FROM_CLIENT;
@@ -455,6 +491,17 @@ void GetLWPacketDirection(Packet *p, Stream5LWSession *ssn)
                     p->packet_flags |= PKT_FROM_CLIENT;
                 }
             }
+            else if (GET_IPH_PROTO(p) == IPPROTO_SCTP)
+            {
+                if (p->sctph->sh_dport == ssn->client_port)
+                {
+                    p->packet_flags |= PKT_FROM_SERVER;
+                }
+                else
+                {
+                    p->packet_flags |= PKT_FROM_CLIENT;
+                }
+            }
             else
             {
                 p->packet_flags |= PKT_FROM_SERVER;
@@ -572,6 +619,7 @@ int DeleteLWSession(Stream5SessionCache *sessionCache,
     Stream5GlobalConfig *save_global_eval_config = s5_global_eval_config;
     Stream5TcpConfig *save_tcp_eval_config = s5_tcp_eval_config;
     Stream5UdpConfig *save_udp_eval_config = s5_udp_eval_config;
+    Stream5SctpConfig *save_sctp_eval_config = s5_sctp_eval_config;
     Stream5IcmpConfig *save_icmp_eval_config = s5_icmp_eval_config;
 
     int ret;
@@ -600,7 +648,7 @@ int DeleteLWSession(Stream5SessionCache *sessionCache,
 #endif
 
     /*
-     * Call callback to cleanup the protocol (TCP/UDP/ICMP)
+     * Call callback to cleanup the protocol (TCP/UDP/SCTP/ICMP)
      * specific session details
      */
     if (sessionCache->cleanup_fcn)
@@ -644,6 +692,7 @@ int DeleteLWSession(Stream5SessionCache *sessionCache,
     s5_global_eval_config = save_global_eval_config;
     s5_tcp_eval_config = save_tcp_eval_config;
     s5_udp_eval_config = save_udp_eval_config;
+    s5_sctp_eval_config = save_sctp_eval_config;
     s5_icmp_eval_config = save_icmp_eval_config;
 
     return ret;
@@ -1223,6 +1272,14 @@ Stream5SetRuntimeConfiguration(
             }
             s5_udp_eval_config = pPolicyConfig->udp_config;
             break;
+        case IPPROTO_SCTP:
+            if (!s5_global_eval_config->track_sctp_sessions || !pPolicyConfig->sctp_config)
+            {
+                s5_sctp_eval_config = NULL;
+                return -1;
+            }
+            s5_sctp_eval_config = pPolicyConfig->sctp_config;
+            break;
         case IPPROTO_ICMP:
             if (s5_global_eval_config->track_icmp_sessions && pPolicyConfig->icmp_config)
             {
@@ -1291,7 +1348,7 @@ static void checkCacheFlowTimeout(uint32_t flowCount, time_t cur_time, Stream5Se
     cache->nextTimeoutEvalNode = hnode_prev;
 }
 
-extern Stream5SessionCache *tcp_lws_cache, *udp_lws_cache;
+extern Stream5SessionCache *tcp_lws_cache, *udp_lws_cache, *sctp_lws_cache;
 
 /*get next flow from session cache. */
 void checkLWSessionTimeout(uint32_t flowCount, time_t cur_time)
@@ -1299,6 +1356,7 @@ void checkLWSessionTimeout(uint32_t flowCount, time_t cur_time)
     Active_Suspend();
     checkCacheFlowTimeout(flowCount, cur_time, tcp_lws_cache);
     checkCacheFlowTimeout(flowCount, cur_time, udp_lws_cache);
+    checkCacheFlowTimeout(flowCount, cur_time, sctp_lws_cache);
     //icmp_lws_cache does not need cleaning
     Active_Resume();
 
diff --git a/src/preprocessors/Stream5/snort_stream5_session.h b/src/preprocessors/Stream5/snort_stream5_session.h
index b31be42..e2993b1 100644
--- a/src/preprocessors/Stream5/snort_stream5_session.h
+++ b/src/preprocessors/Stream5/snort_stream5_session.h
@@ -70,7 +70,7 @@ int GetLWSessionKeyFromIpPort(
                     uint16_t srcPort,
                     snort_ip_p dstIP,
                     uint16_t dstPort,
-                    char proto,
+                    uint8_t proto,
                     uint16_t vlan,
                     uint32_t mplsId,
                     uint16_t addressSpaceId,
diff --git a/src/preprocessors/Stream5/stream5_common.c b/src/preprocessors/Stream5/stream5_common.c
index bf277b9..c1f94f9 100644
--- a/src/preprocessors/Stream5/stream5_common.c
+++ b/src/preprocessors/Stream5/stream5_common.c
@@ -40,6 +40,7 @@
 #include "sp_dynamic.h"
 #include "snort_stream5_tcp.h"
 #include "snort_stream5_udp.h"
+#include "snort_stream5_sctp.h"
 #include "snort_stream5_icmp.h"
 #include "snort_stream5_ip.h"
 #include "parser.h"
@@ -90,6 +91,9 @@ int Stream5ExpireSession(Stream5LWSession *lwssn)
         case IPPROTO_UDP:
             s5stats.udp_timeouts++;
             break;
+        case IPPROTO_SCTP:
+            s5stats.sctp_timeouts++;
+            break;
         case IPPROTO_ICMP:
             s5stats.icmp_timeouts++;
             break;
@@ -220,6 +224,8 @@ static inline RuleTreeNode * protocolRuleList(RuleListNode *rule, int protocol)
             return rule->RuleList->TcpList;
         case IPPROTO_UDP:
             return rule->RuleList->UdpList;
+        case IPPROTO_SCTP:
+            return rule->RuleList->SctpList;
         case IPPROTO_ICMP:
             break;
         default:
@@ -230,15 +236,17 @@ static inline RuleTreeNode * protocolRuleList(RuleListNode *rule, int protocol)
 #endif
 static inline char * getProtocolName (int protocol)
 {
-    static char *protocolName[] = {"TCP", "UDP", "ICMP"};
+    static char *protocolName[] = {"TCP", "UDP", "SCTP", "ICMP"};
     switch (protocol)
     {
         case IPPROTO_TCP:
             return protocolName[0];
         case IPPROTO_UDP:
             return protocolName[1];
-        case IPPROTO_ICMP:
+        case IPPROTO_SCTP:
             return protocolName[2];
+        case IPPROTO_ICMP:
+            return protocolName[3];
             break;
         default:
             break;
@@ -283,7 +291,7 @@ void setPortFilterList(
     OptTreeNode *otn;
     int inspectSrc, inspectDst;
     char any_any_flow = 0;
-    IgnoredRuleList *pIgnoredRuleList = NULL;     ///list of ignored rules
+    IgnoredRuleList *pIgnoredRuleList = NULL;     /* List of ignored rules */
     char *protocolName;
     SFGHASH_NODE *hashNode;
     int flowBitIsSet = 0;
@@ -294,7 +302,9 @@ void setPortFilterList(
                    __FILE__, __LINE__);
     }
 
-    if ((protocol == IPPROTO_TCP) && (ignoreAnyAnyRules == 0))
+    if (((protocol == IPPROTO_TCP) ||
+         (protocol == IPPROTO_SCTP)) &&
+        (ignoreAnyAnyRules == 0))
     {
         int j;
         for (j=0; j<MAX_PORTS; j++)
@@ -306,14 +316,13 @@ void setPortFilterList(
 
     protocolName = getProtocolName(protocol);
 
-    /* Post-process TCP rules to establish TCP ports to inspect. */
+    /* Post-process TCP/SCTP rules to establish TCP/SCTP ports to inspect. */
     for (hashNode = sfghash_findfirst(sc->otn_map);
          hashNode;
          hashNode = sfghash_findnext(sc->otn_map))
     {
         otn = (OptTreeNode *)hashNode->data;
         flowBitIsSet = Stream5OtnHasFlowOrFlowbit(otn);
-
         rtn = getRtnFromOtn(otn, policyId);
 
         if (!rtn)
@@ -323,29 +332,28 @@ void setPortFilterList(
 
         if (rtn->proto == protocol)
         {
-            //do operation
+            /* Do operation */
             inspectSrc = inspectDst = 0;
             if (PortObjectHasAny(rtn->src_portobject))
-            {
                 inspectSrc = -1;
-            }
             else
             {
                 port_array = PortObjectCharPortArray(port_array, rtn->src_portobject, &num_ports);
                 if (port_array && num_ports != 0)
                 {
                     inspectSrc = 1;
-                    for (i=0;i<SFPO_MAX_PORTS;i++)
+                    for (i = 0; i < SFPO_MAX_PORTS; i++)
                     {
                         if (port_array[i])
                         {
                             portList[i] |= PORT_MONITOR_INSPECT;
+
                             /* port specific rule */
-                                /* Look for an OTN with flow or flowbits keyword */
-                                if (flowBitIsSet)
-                                {
-                                    portList[i] |= PORT_MONITOR_SESSION;
-                                }
+                            /* Look for an OTN with flow or flowbits keyword */
+                            if (flowBitIsSet)
+                            {
+                                portList[i] |= PORT_MONITOR_SESSION;
+                            }
                         }
                     }
                 }
@@ -355,6 +363,7 @@ void setPortFilterList(
                     port_array = NULL;
                 }
             }
+
             if (PortObjectHasAny(rtn->dst_portobject))
             {
                 inspectDst = -1;
@@ -365,16 +374,17 @@ void setPortFilterList(
                 if (port_array && num_ports != 0)
                 {
                     inspectDst = 1;
-                    for (i=0;i<SFPO_MAX_PORTS;i++)
+                    for (i = 0; i < SFPO_MAX_PORTS; i++)
                     {
                         if (port_array[i])
                         {
                             portList[i] |= PORT_MONITOR_INSPECT;
+
                             /* port specific rule */
-                                if (flowBitIsSet)
-                                {
-                                    portList[i] |= PORT_MONITOR_SESSION;
-                                }
+                            if (flowBitIsSet)
+                            {
+                                portList[i] |= PORT_MONITOR_SESSION;
+                            }
                         }
                     }
                 }
@@ -396,13 +406,14 @@ void setPortFilterList(
         }
     }
 
-    /* If portscan is tracking TCP/UDP, need to create
+    /* If portscan is tracking TCP/UDP/SCTP, need to create
      * sessions for all ports */
     if (((protocol == IPPROTO_UDP) && (ps_get_protocols(sc, policyId) & PS_PROTO_UDP))
-     || ((protocol == IPPROTO_TCP) && (ps_get_protocols(sc, policyId) & PS_PROTO_TCP)))
+     || ((protocol == IPPROTO_TCP) && (ps_get_protocols(sc, policyId) & PS_PROTO_TCP))
+     || ((protocol == IPPROTO_SCTP) && (ps_get_protocols(sc, policyId) & PS_PROTO_SCTP)))
     {
         int j;
-        for (j=0; j<MAX_PORTS; j++)
+        for (j = 0; j < MAX_PORTS; j++)
         {
             portList[j] |= PORT_MONITOR_SESSION;
         }
@@ -605,6 +616,12 @@ void Stream5FreeConfig(Stream5Config *config)
         config->udp_config = NULL;
     }
 
+    if (config->sctp_config != NULL)
+    {
+        Stream5SctpConfigFree(config->sctp_config);
+        config->sctp_config = NULL;
+    }
+
     if (config->icmp_config != NULL)
     {
         Stream5IcmpConfigFree(config->icmp_config);
diff --git a/src/preprocessors/Stream5/stream5_common.h b/src/preprocessors/Stream5/stream5_common.h
index fc77003..b5583fe 100644
--- a/src/preprocessors/Stream5/stream5_common.h
+++ b/src/preprocessors/Stream5/stream5_common.h
@@ -136,6 +136,19 @@
 /* Control Socket types */
 #define CS_TYPE_DEBUG_S5_HA     ((PP_STREAM5 << 7) + 0)     // 0x680 / 1664
 
+/* SCTP States. */
+/* XXX: Not implemented yet */
+#define STREAM5_SCTP_STATE_NONE                 0x0000
+#define STREAM5_SCTP_STATE_COOKIE_WAIT          0x0001
+#define STREAM5_SCTP_STATE_COOKIE_SENT          0x0002   /* Not in RFC4690 */
+#define STREAM5_SCTP_STATE_COOKIE_ECHOED        0x0004
+#define STREAM5_SCTP_STATE_ESTABLISHED          0x0008
+#define STREAM5_SCTP_STATE_SHUTDOWN_PENDING     0x0010
+#define STREAM5_SCTP_STATE_SHUTDOWN_SENT        0x0020
+#define STREAM5_SCTP_STATE_SHUTDOWN_RECEIVED    0x0040
+#define STREAM5_SCTP_STATE_SHUTDOWN_ACK_SENT    0x0080
+#define STREAM5_SCTP_STATE_CLOSED               0x0100
+
 /*  D A T A   S T R U C T U R E S  **********************************/
 typedef StreamSessionKey SessionKey;
 
@@ -216,6 +229,7 @@ typedef struct _Stream5GlobalConfig
     char       disabled;
     char       track_tcp_sessions;
     char       track_udp_sessions;
+    char       track_sctp_sessions;
     char       track_icmp_sessions;
     char       track_ip_sessions;
 #ifdef ENABLE_HA
@@ -223,12 +237,15 @@ typedef struct _Stream5GlobalConfig
 #endif
     uint32_t   max_tcp_sessions;
     uint32_t   max_udp_sessions;
+    uint32_t   max_sctp_sessions;
     uint32_t   max_icmp_sessions;
     uint32_t   max_ip_sessions;
     uint16_t   tcp_cache_pruning_timeout;
     uint16_t   tcp_cache_nominal_timeout;
     uint16_t   udp_cache_pruning_timeout;
     uint16_t   udp_cache_nominal_timeout;
+    uint16_t   sctp_cache_pruning_timeout;
+    uint16_t   sctp_cache_nominal_timeout;
     uint32_t   memcap;
     uint32_t   prune_log_max;
     uint32_t   flags;
@@ -342,6 +359,34 @@ typedef struct _Stream5UdpConfig
 
 } Stream5UdpConfig;
 
+typedef struct _Stream5SctpPolicy
+{
+    uint32_t   session_timeout;
+    uint32_t   max_queued_bytes;  /* XXX: Not used yet */
+    uint32_t   max_queued_chunks; /* XXX: Not used yet */
+    uint32_t   hs_timeout;        /* XXX: Not used yet */
+    uint16_t   flags;
+    IpAddrSet   *bound_addrs;     /* XXX: Not used yet - SCTP multi-homes! */
+    FlushConfig flush_config[MAX_PORTS];
+#ifdef TARGET_BASED
+    FlushConfig flush_config_protocol[MAX_PROTOCOL_ORDINAL];
+#endif
+/* Needed?  Beats the frak out of me. */
+#ifndef DYNAMIC_RANDOM_FLUSH_POINTS
+    FlushPointList flush_point_list;
+#endif
+} Stream5SctpPolicy;
+
+typedef struct _Stream5SctpConfig
+{
+    Stream5SctpPolicy *default_policy;
+    Stream5SctpPolicy **policy_list;
+    uint8_t num_policies;
+    uint8_t dummy;  /* For alignment */
+    uint16_t port_filter[MAX_PORTS + 1];
+
+} Stream5SctpConfig;
+
 typedef struct _Stream5IcmpPolicy
 {
     uint32_t   session_timeout;
@@ -387,6 +432,7 @@ typedef struct _Stream5Config
     Stream5GlobalConfig *global_config;
     Stream5TcpConfig *tcp_config;
     Stream5UdpConfig *udp_config;
+    Stream5SctpConfig *sctp_config;
     Stream5IcmpConfig *icmp_config;
     Stream5IpConfig *ip_config;
 #ifdef ENABLE_HA
@@ -401,7 +447,7 @@ typedef struct _Stream5Config
 
 } Stream5Config;
 
-/**Common statistics for tcp and udp packets, maintained by port filtering.
+/* Common statistics for tcp, udp, and sctp packets, maintained by port filtering.
  */
 typedef struct {
     /**packets filtered without further processing by any preprocessor or
@@ -422,10 +468,12 @@ typedef struct _Stream5Stats
 {
     uint32_t   total_tcp_sessions;
     uint32_t   total_udp_sessions;
+    uint32_t   total_sctp_sessions;
     uint32_t   total_icmp_sessions;
     uint32_t   total_ip_sessions;
     uint32_t   tcp_prunes;
     uint32_t   udp_prunes;
+    uint32_t   sctp_prunes;
     uint32_t   icmp_prunes;
     uint32_t   ip_prunes;
     uint32_t   tcp_timeouts;
@@ -442,6 +490,12 @@ typedef struct _Stream5Stats
     uint32_t   udp_sessions_created;
     uint32_t   udp_sessions_released;
     uint32_t   udp_discards;
+    uint32_t   sctp_timeouts;
+    uint32_t   sctp_sessions_created;
+    uint32_t   sctp_sessions_released;
+    uint32_t   sctp_streammsgs_created;
+    uint32_t   sctp_streammsgs_released;
+    uint32_t   sctp_discards;
     uint32_t   icmp_timeouts;
     uint32_t   icmp_sessions_created;
     uint32_t   icmp_sessions_released;
@@ -450,6 +504,7 @@ typedef struct _Stream5Stats
     uint32_t   internalEvents;
     tPortFilterStats  tcp_port_filter;
     tPortFilterStats  udp_port_filter;
+    tPortFilterStats  sctp_port_filter;
 } Stream5Stats;
 
 /**Whether incoming packets should be ignored or processed.
@@ -527,6 +582,7 @@ extern uint32_t mem_in_use;
 extern Stream5GlobalConfig *s5_global_eval_config;
 extern Stream5TcpConfig *s5_tcp_eval_config;
 extern Stream5UdpConfig *s5_udp_eval_config;
+extern Stream5SctpConfig *s5_sctp_eval_config;
 extern Stream5IcmpConfig *s5_icmp_eval_config;
 extern Stream5IpConfig *s5_ip_eval_config;
 extern tSfPolicyUserContextId s5_config;
diff --git a/src/preprocessors/Stream5/stream5_ha.c b/src/preprocessors/Stream5/stream5_ha.c
index 861ef66..57062be 100644
--- a/src/preprocessors/Stream5/stream5_ha.c
+++ b/src/preprocessors/Stream5/stream5_ha.c
@@ -304,6 +304,7 @@ static int Stream5DebugHA(uint16_t type, const uint8_t *data, uint32_t length, v
 
 static const HA_Api *s_tcp = NULL;
 static const HA_Api *s_udp = NULL;
+static const HA_Api *s_sctp = NULL;
 static const HA_Api *s_ip = NULL;
 
 int ha_set_api(unsigned proto, const HA_Api *api)
@@ -316,6 +317,9 @@ int ha_set_api(unsigned proto, const HA_Api *api)
         case IPPROTO_UDP:
             s_udp = api;
             break;
+        case IPPROTO_SCTP:
+            s_sctp = api;
+            break;
         case IPPROTO_IP:
             s_ip = api;
             break;
@@ -333,6 +337,8 @@ static inline const HA_Api *ha_get_api(unsigned proto)
             return s_tcp;
         case IPPROTO_UDP:
             return s_udp;
+        case IPPROTO_SCTP:
+            return s_sctp;
         case IPPROTO_ICMP:
         case IPPROTO_IP:
         default:
@@ -754,7 +760,7 @@ void Stream5HAConfigFree(Stream5HAConfig *config)
 // This MUST have the exact same logic as GetLWSessionKeyFromIpPort()
 // TBD any alternative to this approach?
 static inline bool IsClientLower(const sfip_t *cltIP, uint16_t cltPort,
-                                 const sfip_t *srvIP, uint16_t srvPort, char proto)
+                                 const sfip_t *srvIP, uint16_t srvPort, uint8_t proto)
 {
     if (IS_IP4(cltIP))
     {
@@ -768,6 +774,7 @@ static inline bool IsClientLower(const sfip_t *cltIP, uint16_t cltPort,
         {
             case IPPROTO_TCP:
             case IPPROTO_UDP:
+            case IPPROTO_SCTP:
                 if (cltPort < srvPort)
                     return true;
         }
@@ -783,6 +790,7 @@ static inline bool IsClientLower(const sfip_t *cltIP, uint16_t cltPort,
     {
         case IPPROTO_TCP:
         case IPPROTO_UDP:
+        case IPPROTO_SCTP:
             if (cltPort < srvPort)
                 return true;
     }
diff --git a/src/preprocessors/perf-base.c b/src/preprocessors/perf-base.c
index 7a19fb8..383650e 100644
--- a/src/preprocessors/perf-base.c
+++ b/src/preprocessors/perf-base.c
@@ -7,6 +7,7 @@
 ** Copyright (C) 2002-2013 Sourcefire, Inc.
 ** Dan Roelker <droelker at sourcefire.com>
 ** Marc Norton <mnorton at sourcefire.com>
+** SCTP bits: Joshua Kinard
 **
 ** This program is free software; you can redistribute it and/or modify
 ** it under the terms of the GNU General Public License Version 2 as
@@ -41,6 +42,9 @@
 **              and the permonitor command has 'reset' and 'accrue' commands instead.(MAN)
 **    10.4.06 : Added UDP Session Stats (SAS)
 **    4.3.07  : Added stats for TCP sessions (SAS)
+**    8.27.11 : Added stats for SCTP sessions (JK)
+**    5.19.13 : Updated SCTP support for 2.9.4.6 (JK)
+**    1.22.14 : Updated SCTP support for 2.9.5.6 (JK)
 */
 
 #include <time.h>
@@ -188,6 +192,9 @@ int InitBaseStats(SFBASE *sfBase)
     sfBase->iNewUDPSessions = 0;
     sfBase->iDeletedUDPSessions = 0;
 
+    sfBase->iNewSCTPSessions = 0;
+    sfBase->iDeletedSCTPSessions = 0;
+
     //sfBase->iAttributeHosts = 0;
     //sfBase->iAttributeReloads = 0;
     sfBase->total_mpls_packets = 0;
@@ -197,6 +204,7 @@ int InitBaseStats(SFBASE *sfBase)
 
     sfBase->total_tcp_filtered_packets = 0;
     sfBase->total_udp_filtered_packets = 0;
+    sfBase->total_sctp_filtered_packets = 0;
 
     sfBase->frag3_mem_in_use = 0;
     sfBase->stream5_mem_in_use = 0;
@@ -372,7 +380,7 @@ void UpdateStreamReassStats(SFBASE *sfBase, int len)
 }
 
 /**API to update stats for packets discarded due to
- * TCP/UDP port/service based filtering.
+ * TCP/UDP/SCTP port/service based filtering.
  *
  * @param sfBase - pointer to accumulated stats
  */
@@ -386,6 +394,9 @@ void UpdateFilteredPacketStats(SFBASE *sfBase, unsigned int proto)
         case IPPROTO_UDP:
             sfBase->total_udp_filtered_packets++;
             break;
+        case IPPROTO_SCTP:
+            sfBase->total_sctp_filtered_packets++;
+            break;
         default:
             //coding error
             ;
@@ -518,6 +529,51 @@ int RemoveUDPSession(SFBASE *sfBase)
 
 /*
 **  NAME
+**    AddSCTPSession
+**
+**  DESCRIPTION
+**    Add a session count
+**
+**  FORMAL INPUTS
+**    SFBASE * - ptr to update.
+**
+**  FORMAL OUTPUTS
+**    int - 0 is successful
+*/
+int AddSCTPSession(SFBASE *sfBase)
+{
+    sfBase->iTotalSCTPSessions++;
+    sfBase->iNewSCTPSessions++;
+
+    if(sfBase->iTotalSCTPSessions > sfBase->iMaxSCTPSessions)
+        sfBase->iMaxSCTPSessions = sfBase->iTotalSCTPSessions;
+
+    return 0;
+}
+
+/*
+**  NAME
+**    RemoveSCTPSession
+**
+**  DESCRIPTION
+**    Add a session count
+**
+**  FORMAL INPUTS
+**    SFBASE * - ptr to update.
+**
+**  FORMAL OUTPUTS
+**    int - 0 is successful
+*/
+
+int RemoveSCTPSession(SFBASE *sfBase)
+{
+    sfBase->iTotalSCTPSessions--;
+    sfBase->iDeletedSCTPSessions++;
+    return 0;
+}
+
+/*
+**  NAME
 **    ProcessBaseStats
 **
 **  DESCRIPTION
@@ -704,6 +760,17 @@ static void GetEventsPerSecond(SFBASE *sfBase, SFBASE_STATS *sfBaseStats,
     sfBase->iNewUDPSessions = 0;
     sfBase->iDeletedUDPSessions = 0;
 
+    sfBaseStats->total_sctp_sessions = sfBase->iTotalSCTPSessions;
+    sfBaseStats->max_sctp_sessions = sfBase->iMaxSCTPSessions;
+    sfBaseStats->deleted_sctp_sessions_per_second =
+        (double)(sfBase->iDeletedSCTPSessions) / Systimes->realtime;
+
+    sfBaseStats->new_sctp_sessions_per_second =
+        (double)(sfBase->iNewSCTPSessions) / Systimes->realtime;
+
+    sfBase->iNewSCTPSessions = 0;
+    sfBase->iDeletedSCTPSessions = 0;
+
     sfBase->iMaxSessionsInterval = sfBase->iTotalSessions;
     sfBase->iMidStreamSessions = 0;
     sfBase->iClosedSessions = 0;
@@ -981,6 +1048,7 @@ static int CalculateBasePerfStats(SFBASE *sfBase, SFBASE_STATS *sfBaseStats, int
 
     sfBaseStats->total_tcp_filtered_packets = sfBase->total_tcp_filtered_packets;
     sfBaseStats->total_udp_filtered_packets = sfBase->total_udp_filtered_packets;
+    sfBaseStats->total_sctp_filtered_packets = sfBase->total_sctp_filtered_packets;
 
 #ifdef NORMALIZER
     {
@@ -1234,6 +1302,10 @@ static void GetPktDropStats(SFBASE *sfBase, SFBASE_STATS *sfBaseStats)
  * max-udp-sessions
  * del-udp-sessions/sec (udp stream cache)
  * new-udp-sessions/sec (udp stream cache)
+ * sctp-sessions
+ * max-sctp-sessions
+ * del-sctp-sessions/sec (sctp stream cache)
+ * new-sctp-sessions/sec (sctp stream cache)
  * max-sessions, interval (tcp stream cache)
  * curr-tcp-sessions-initializing (tcp stream cache, of total-sessions open)
  * curr-tcp-sessions-established (tcp stream cache, of total-sessions open)
@@ -1380,7 +1452,14 @@ static void LogBasePerfStats(SFBASE_STATS *sfBaseStats,  FILE * fh )
         sfBaseStats->total_udp_sessions,
         sfBaseStats->max_udp_sessions);
 
-    size += SafeSnprintf(buff + size, sizeof(buff) - size, 
+    size += SafeSnprintf(buff + size, sizeof(buff) - size,
+        "%.3f,%.3f," CSVu64 CSVu64,
+        sfBaseStats->new_sctp_sessions_per_second,
+        sfBaseStats->deleted_sctp_sessions_per_second,
+        sfBaseStats->total_sctp_sessions,
+        sfBaseStats->max_sctp_sessions);
+
+    size += SafeSnprintf(buff + size, sizeof(buff) - size,
         CSVu64 CSVu64 CSVu64 CSVu64 "%.3f,%.3f,%.3f,%.3f,%.3f,",
         sfBaseStats->max_tcp_sessions_interval,
         sfBaseStats->curr_tcp_sessions_initializing,
@@ -1403,9 +1482,10 @@ static void LogBasePerfStats(SFBASE_STATS *sfBaseStats,  FILE * fh )
         sfBaseStats->kpackets_per_sec_mpls.realtime);
 
     size += SafeSnprintf(buff + size, sizeof(buff) - size, 
-        CSVu64 CSVu64,
+        CSVu64 CSVu64 CSVu64,
         sfBaseStats->total_tcp_filtered_packets,
-        sfBaseStats->total_udp_filtered_packets);
+        sfBaseStats->total_udp_filtered_packets,
+        sfBaseStats->total_sctp_filtered_packets);
 
 #ifdef NORMALIZER
     for ( iCtr = 0; iCtr < PERF_COUNT_MAX; iCtr++ )
@@ -1574,6 +1654,13 @@ void LogBasePerfHeader (FILE* fh)
         "max_udp_sessions");
 
     fprintf(fh,
+        ",%s,%s,%s,%s",
+        "new_sctp_sessions_per_second",
+        "deleted_sctp_sessions_per_second",
+        "total_sctp_sessions",
+        "max_sctp_sessions");
+
+    fprintf(fh,
         ",%s,%s,%s,%s,%s,%s,%s,%s,%s",
         "max_tcp_sessions_interval",
         "curr_tcp_sessions_initializing",
@@ -1596,9 +1683,10 @@ void LogBasePerfHeader (FILE* fh)
         "kpackets_per_sec_mpls.realtime");
 
     fprintf(fh,
-        ",%s,%s",
+        ",%s,%s,%s",
         "total_tcp_filtered_packets",
-        "total_udp_filtered_packets");
+        "total_udp_filtered_packets",
+        "total_sctp_filtered_packets");
 
 #ifdef NORMALIZER
     for ( iCtr = 0; iCtr < PERF_COUNT_MAX; iCtr++ )
@@ -1654,6 +1742,7 @@ static void DisplayBasePerfStatsConsole(SFBASE_STATS *sfBaseStats, int max_stats
     LogMessage("Injected:    " STDu64 "\n", sfBaseStats->total_injected_packets);
     LogMessage("Pkts Filtered TCP:     " STDu64 "\n", sfBaseStats->total_tcp_filtered_packets);
     LogMessage("Pkts Filtered UDP:     " STDu64 "\n\n", sfBaseStats->total_udp_filtered_packets);
+    LogMessage("Pkts Filtered SCTP:    " STDu64 "\n\n", sfBaseStats->total_sctp_filtered_packets);
 
     LogMessage("Mbits/Sec:   %.3f (wire)\n",
             sfBaseStats->wire_mbits_per_sec.realtime);
@@ -1733,52 +1822,58 @@ static void DisplayBasePerfStatsConsole(SFBASE_STATS *sfBaseStats, int max_stats
     /*
     **  Shows the number of snort alerts per second.
     */
-    LogMessage("Alerts/Sec             :  %.3f\n",   sfBaseStats->alerts_per_second);
+    LogMessage("Alerts/Sec              :  %.3f\n", sfBaseStats->alerts_per_second);
 
     /* Session estimation statistics */
-    LogMessage("Syns/Sec               :  %.3f\n", sfBaseStats->syns_per_second);
-    LogMessage("Syn-Acks/Sec           :  %.3f\n", sfBaseStats->synacks_per_second);
-    LogMessage("New Cached Sessions/Sec:  %.3f\n", sfBaseStats->new_sessions_per_second);
-    LogMessage("Midstream Sessions/Sec :  %.3f\n", sfBaseStats->tcp_sessions_midstream_per_second);
-    LogMessage("Cached Sessions Del/Sec:  %.3f\n", sfBaseStats->deleted_sessions_per_second);
-    LogMessage("Closed Sessions/Sec    :  %.3f\n", sfBaseStats->tcp_sessions_closed_per_second);
-    LogMessage("TimedOut Sessions/Sec  :  %.3f\n", sfBaseStats->tcp_sessions_timedout_per_second);
-    LogMessage("Pruned Sessions/Sec    :  %.3f\n", sfBaseStats->tcp_sessions_pruned_per_second);
-    LogMessage("Dropped Async Ssns/Sec :  %.3f\n", sfBaseStats->tcp_sessions_dropped_async_per_second);
-
-    LogMessage("Current Cached Sessions:  " STDu64 "\n", sfBaseStats->total_sessions);
-    LogMessage("Sessions Initializing  :  " STDu64 "\n", sfBaseStats->curr_tcp_sessions_initializing);
-    LogMessage("Sessions Established   :  " STDu64 "\n", sfBaseStats->curr_tcp_sessions_established);
-    LogMessage("Sessions Closing       :  " STDu64 "\n", sfBaseStats->curr_tcp_sessions_closing);
-    LogMessage("Max Cached Sessions    :  " STDu64 "\n", sfBaseStats->max_sessions);
-    LogMessage("Max Sessions (interval):  " STDu64 "\n", sfBaseStats->max_tcp_sessions_interval);
-
-    /* more instrumentation for stream4/frag2 */
-    LogMessage("Stream Flushes/Sec     :  %.3f\n", sfBaseStats->stream_flushes_per_second);
-    LogMessage("Stream Cache Faults/Sec:  " STDu64 "\n", sfBaseStats->stream_faults);
-    LogMessage("Stream Cache Timeouts  :  " STDu64 "\n", sfBaseStats->stream_timeouts);
-
-    LogMessage("Frag Creates()s/Sec    :  %.3f\n", sfBaseStats->frag_creates_per_second);
-    LogMessage("Frag Completes()s/Sec  :  %.3f\n", sfBaseStats->frag_completes_per_second);
-    LogMessage("Frag Inserts()s/Sec    :  %.3f\n", sfBaseStats->frag_inserts_per_second);
-    LogMessage("Frag Deletes/Sec       :  %.3f\n", sfBaseStats->frag_deletes_per_second);
-    LogMessage("Frag AutoFrees/Sec     :  %.3f\n", sfBaseStats->frag_autofrees_per_second);
-    LogMessage("Frag Flushes/Sec       :  %.3f\n", sfBaseStats->frag_flushes_per_second);
-
-    LogMessage("Current Cached Frags   :  " STDu64 "\n", sfBaseStats->current_frags);
-    LogMessage("Max Cached Frags       :  " STDu64 "\n", sfBaseStats->max_frags);
-    LogMessage("Frag Timeouts          :  " STDu64 "\n", sfBaseStats->frag_timeouts);
-    LogMessage("Frag Faults            :  " STDu64 "\n\n", sfBaseStats->frag_faults);
-
-    LogMessage("New Cached UDP Ssns/Sec:  %.3f\n", sfBaseStats->new_udp_sessions_per_second);
-    LogMessage("Cached UDP Ssns Del/Sec:  %.3f\n", sfBaseStats->deleted_udp_sessions_per_second);
-
-    LogMessage("Current Cached UDP Ssns:  " STDu64 "\n", sfBaseStats->total_udp_sessions);
-    LogMessage("Max Cached UDP Ssns    :  " STDu64 "\n\n", sfBaseStats->max_udp_sessions);
+    LogMessage("Syns/Sec                :  %.3f\n", sfBaseStats->syns_per_second);
+    LogMessage("Syn-Acks/Sec            :  %.3f\n", sfBaseStats->synacks_per_second);
+    LogMessage("New Cached Sessions/Sec :  %.3f\n", sfBaseStats->new_sessions_per_second);
+    LogMessage("Midstream Sessions/Sec  :  %.3f\n", sfBaseStats->tcp_sessions_midstream_per_second);
+    LogMessage("Cached Sessions Del/Sec :  %.3f\n", sfBaseStats->deleted_sessions_per_second);
+    LogMessage("Closed Sessions/Sec     :  %.3f\n", sfBaseStats->tcp_sessions_closed_per_second);
+    LogMessage("TimedOut Sessions/Sec   :  %.3f\n", sfBaseStats->tcp_sessions_timedout_per_second);
+    LogMessage("Pruned Sessions/Sec     :  %.3f\n", sfBaseStats->tcp_sessions_pruned_per_second);
+    LogMessage("Dropped Async Ssns/Sec  :  %.3f\n", sfBaseStats->tcp_sessions_dropped_async_per_second);
+
+    LogMessage("Current Cached Sessions :  " STDu64 "\n", sfBaseStats->total_sessions);
+    LogMessage("Sessions Initializing   :  " STDu64 "\n", sfBaseStats->curr_tcp_sessions_initializing);
+    LogMessage("Sessions Established    :  " STDu64 "\n", sfBaseStats->curr_tcp_sessions_established);
+    LogMessage("Sessions Closing        :  " STDu64 "\n", sfBaseStats->curr_tcp_sessions_closing);
+    LogMessage("Max Cached Sessions     :  " STDu64 "\n", sfBaseStats->max_sessions);
+    LogMessage("Max Sessions (interval) :  " STDu64 "\n", sfBaseStats->max_tcp_sessions_interval);
+
+    /* more instrumentation for stream5/frag3 */
+    LogMessage("Stream Flushes/Sec      :  %.3f\n", sfBaseStats->stream_flushes_per_second);
+    LogMessage("Stream Cache Faults/Sec :  " STDu64 "\n", sfBaseStats->stream_faults);
+    LogMessage("Stream Cache Timeouts   :  " STDu64 "\n", sfBaseStats->stream_timeouts);
+
+    LogMessage("Frag Creates()s/Sec     :  %.3f\n", sfBaseStats->frag_creates_per_second);
+    LogMessage("Frag Completes()s/Sec   :  %.3f\n", sfBaseStats->frag_completes_per_second);
+    LogMessage("Frag Inserts()s/Sec     :  %.3f\n", sfBaseStats->frag_inserts_per_second);
+    LogMessage("Frag Deletes/Sec        :  %.3f\n", sfBaseStats->frag_deletes_per_second);
+    LogMessage("Frag AutoFrees/Sec      :  %.3f\n", sfBaseStats->frag_autofrees_per_second);
+    LogMessage("Frag Flushes/Sec        :  %.3f\n", sfBaseStats->frag_flushes_per_second);
+
+    LogMessage("Current Cached Frags    :  " STDu64 "\n", sfBaseStats->current_frags);
+    LogMessage("Max Cached Frags        :  " STDu64 "\n", sfBaseStats->max_frags);
+    LogMessage("Frag Timeouts           :  " STDu64 "\n", sfBaseStats->frag_timeouts);
+    LogMessage("Frag Faults             :  " STDu64 "\n\n", sfBaseStats->frag_faults);
+
+    LogMessage("New Cached UDP Ssns/Sec :  %.3f\n", sfBaseStats->new_udp_sessions_per_second);
+    LogMessage("Cached UDP Ssns Del/Sec :  %.3f\n", sfBaseStats->deleted_udp_sessions_per_second);
+
+    LogMessage("Current Cached UDP Ssns :  " STDu64 "\n", sfBaseStats->total_udp_sessions);
+    LogMessage("Max Cached UDP Ssns     :  " STDu64 "\n\n", sfBaseStats->max_udp_sessions);
+
+    LogMessage("New Cached SCTP Ssns/Sec:  %.3f\n", sfBaseStats->new_sctp_sessions_per_second);
+    LogMessage("Cached SCTP Ssns Del/Sec:  %.3f\n", sfBaseStats->deleted_sctp_sessions_per_second);
+
+    LogMessage("Current Cached SCTP Ssns:  " STDu64 "\n", sfBaseStats->total_sctp_sessions);
+    LogMessage("Max Cached SCTP Ssns    :  " STDu64 "\n\n", sfBaseStats->max_sctp_sessions);
 
 #ifdef TARGET_BASED
-    LogMessage("Attribute Table Hosts  :  " STDu64 "\n", sfBaseStats->current_attribute_hosts);
-    LogMessage("Attribute Table Reloads:  " STDu64 "\n\n", sfBaseStats->attribute_table_reloads);
+    LogMessage("Attribute Table Hosts   :  " STDu64 "\n", sfBaseStats->current_attribute_hosts);
+    LogMessage("Attribute Table Reloads :  " STDu64 "\n\n", sfBaseStats->attribute_table_reloads);
 #endif
 
 #ifdef NORMALIZER
diff --git a/src/preprocessors/perf-base.h b/src/preprocessors/perf-base.h
index 3ced977..bb57ee1 100644
--- a/src/preprocessors/perf-base.h
+++ b/src/preprocessors/perf-base.h
@@ -143,6 +143,11 @@ typedef struct _SFBASE
     uint64_t   iDeletedUDPSessions;
     uint64_t   iMaxUDPSessions;
 
+    uint64_t   iTotalSCTPSessions;
+    uint64_t   iNewSCTPSessions;
+    uint64_t   iDeletedSCTPSessions;
+    uint64_t   iMaxSCTPSessions;
+
     uint64_t   iMaxSessionsInterval;
     uint64_t   iMidStreamSessions;
     uint64_t   iClosedSessions;
@@ -160,11 +165,15 @@ typedef struct _SFBASE
     uint64_t   total_blocked_mpls_packets;
     uint64_t   total_blocked_mpls_bytes;
 
-    /**TCP packets ignored due to port/service filtering.*/
+    /* TCP packets ignored due to port/service filtering. */
     uint64_t   total_tcp_filtered_packets;
-    /**UDP packets ignored due to port/service filtering.*/
+
+    /* UDP packets ignored due to port/service filtering. */
     uint64_t   total_udp_filtered_packets;
 
+    /* SCTP packets ignored due to port/service filtering. */
+    uint64_t   total_sctp_filtered_packets;
+
     uint64_t   frag3_mem_in_use;
     uint64_t   stream5_mem_in_use;
     uint64_t   total_iAlerts;
@@ -246,6 +255,11 @@ typedef struct _SFBASE_STATS {
     double   deleted_udp_sessions_per_second;
     double   new_udp_sessions_per_second;
 
+    uint64_t   total_sctp_sessions;
+    uint64_t   max_sctp_sessions;
+    double   deleted_sctp_sessions_per_second;
+    double   new_sctp_sessions_per_second;
+
     uint64_t   max_tcp_sessions_interval;
     uint64_t   curr_tcp_sessions_initializing;
     uint64_t   curr_tcp_sessions_established;
@@ -266,11 +280,15 @@ typedef struct _SFBASE_STATS {
     SYSTIMES mpls_mbits_per_sec;
     int      avg_bytes_per_mpls_packet;
 
-    /**TCP packets ignored due to port/service filtering.*/
+    /* TCP packets ignored due to port/service filtering. */
     uint64_t   total_tcp_filtered_packets;
-    /**UDP packets ignored due to port/service filtering.*/
+
+    /* UDP packets ignored due to port/service filtering. */
     uint64_t   total_udp_filtered_packets;
 
+    /* SCTP packets ignored due to port/service filtering. */
+    uint64_t   total_sctp_filtered_packets;
+
     uint64_t   frag3_mem_in_use;
     uint64_t   stream5_mem_in_use;
     double     total_alerts_per_second;
@@ -288,6 +306,8 @@ int CloseStreamSession(SFBASE *sfBase, char flags);
 int RemoveStreamSession(SFBASE *sfBase);
 int AddUDPSession(SFBASE *sfBase);
 int RemoveUDPSession(SFBASE *sfBase);
+int AddSCTPSession(SFBASE *sfBase);
+int RemoveSCTPSession(SFBASE *sfBase);
 
 void UpdateWireStats(SFBASE *sfBase, int len, int dropped, int injected);
 void UpdateMPLSStats(SFBASE *sfBase, int len, int dropped);
diff --git a/src/preprocessors/perf-flow.c b/src/preprocessors/perf-flow.c
index b21b641..e894723 100644
--- a/src/preprocessors/perf-flow.c
+++ b/src/preprocessors/perf-flow.c
@@ -8,6 +8,8 @@
 ** Copyright (C) 2002-2013 Sourcefire, Inc.
 ** Marc Norton <mnorton at sourcefire.com>
 ** Dan Roelker <droelker at sourcefire.com>
+** Joshua Kinard
+**
 **
 ** NOTES
 **   4.10.02 - Initial Checkin.  Norton
@@ -15,6 +17,8 @@
 **             easy stat printing. Roelker
 **   5.29.02 - Added ICMP traffic stats and overall protocol flow
 **             stats. Roelker
+**   8.27.11 - Added SCTP traffic stats and overall protocol flow
+**             stats. (JK)
 **
 ** This program is free software; you can redistribute it and/or modify
 ** it under the terms of the GNU General Public License Version 2 as
@@ -32,14 +36,16 @@
 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 **
 **  DESCRIPTION
-**    The following subroutines track eand analyze the traffic flow
+**    The following subroutines track and analyze the traffic flow
 **  statistics.
 **
 **   PacketLen vs Packet Count
 **   TCP-Port vs Packet Count
 **   UDP-Port vs Packet Count
+**   SCTP-Port vs Packet Count
 **   TCP High<->High Port Count
 **   UDP High<->High Port Count
+**   SCTP High<->High Port Count
 **
 **
 */
@@ -103,6 +109,8 @@ int InitFlowStats(SFFLOW *sfFlow)
         sfFlow->portTcpDst = (uint64_t*)SnortAlloc(sizeof(uint64_t) * (SF_MAX_PORT+1));
         sfFlow->portUdpSrc = (uint64_t*)SnortAlloc(sizeof(uint64_t) * (SF_MAX_PORT+1));
         sfFlow->portUdpDst = (uint64_t*)SnortAlloc(sizeof(uint64_t) * (SF_MAX_PORT+1));
+        sfFlow->portSctpSrc = (uint64_t*)SnortAlloc(sizeof(uint64_t) * (SF_MAX_PORT+1));
+        sfFlow->portSctpDst = (uint64_t*)SnortAlloc(sizeof(uint64_t) * (SF_MAX_PORT+1));
         sfFlow->typeIcmp = (uint64_t *)SnortAlloc(sizeof(uint64_t) * 256);
 
         first = 0;
@@ -114,6 +122,8 @@ int InitFlowStats(SFFLOW *sfFlow)
         memset(sfFlow->portTcpDst, 0, sizeof(uint64_t) * (SF_MAX_PORT+1));
         memset(sfFlow->portUdpSrc, 0, sizeof(uint64_t) * (SF_MAX_PORT+1));
         memset(sfFlow->portUdpDst, 0, sizeof(uint64_t) * (SF_MAX_PORT+1));
+        memset(sfFlow->portSctpSrc, 0, sizeof(uint64_t) * (SF_MAX_PORT+1));
+        memset(sfFlow->portSctpDst, 0, sizeof(uint64_t) * (SF_MAX_PORT+1));
         memset(sfFlow->typeIcmp, 0, sizeof(uint64_t) * 256);
     }
 
@@ -126,6 +136,9 @@ int InitFlowStats(SFFLOW *sfFlow)
     sfFlow->portUdpHigh=0;
     sfFlow->portUdpTotal=0;
 
+    sfFlow->portSctpHigh=0;
+    sfFlow->portSctpTotal=0;
+
     sfFlow->typeIcmpTotal = 0;
 
     return 0;
@@ -182,6 +195,18 @@ void FreeFlowStats(SFFLOW *sfFlow)
         sfFlow->portUdpDst = NULL;
     }
 
+    if (sfFlow->portSctpSrc != NULL)
+    {
+        free(sfFlow->portSctpSrc);
+        sfFlow->portSctpSrc = NULL;
+    }
+
+    if (sfFlow->portSctpDst != NULL)
+    {
+        free(sfFlow->portSctpDst);
+        sfFlow->portSctpDst = NULL;
+    }
+
     if (sfFlow->typeIcmp != NULL)
     {
         free(sfFlow->typeIcmp);
@@ -246,7 +271,7 @@ int UpdateTCPFlowStats(SFFLOW *sfFlow, int sport, int dport, int len)
 int UpdateUDPFlowStats(SFFLOW *sfFlow, int sport, int dport, int len )
 {
     /*
-     * Track how much data on each port, and hihg<-> high port data
+     * Track how much data on each port, and high<->high port data
      */
     if( sport <  1024 && dport > 1023 ) //sfFlow->maxPortToTrack )
     {
@@ -274,6 +299,37 @@ int UpdateUDPFlowStats(SFFLOW *sfFlow, int sport, int dport, int len )
     return 0;
 }
 
+int UpdateSCTPFlowStats(SFFLOW *sfFlow, int sport, int dport, int len )
+{
+    /*
+     * Track how much data on each port, and high<->high port data
+     */
+    if( sport <  1024 && dport > 1023 ) //sfFlow->maxPortToTrack )
+    {
+        sfFlow->portSctpSrc  [ sport ]+= len;
+    }
+    else if( dport < 1024 && sport > 1023 ) //sfFlow->maxPortToTrack )
+    {
+        sfFlow->portSctpDst  [ dport ]+= len;
+    }
+    else if( sport < 1023 && dport < 1023 )
+    {
+        sfFlow->portSctpSrc  [ sport ]+= len;
+        sfFlow->portSctpDst  [ dport ]+= len;
+    }
+    else if( sport > 1023 && dport > 1023 )
+    {
+        sfFlow->portSctpSrc  [ sport ]+= len;
+        sfFlow->portSctpDst  [ dport ]+= len;
+
+        sfFlow->portSctpHigh += len;
+    }
+
+    sfFlow->portSctpTotal += len;
+
+    return 0;
+}
+
 int UpdateICMPFlowStats(SFFLOW *sfFlow, int type, int len)
 {
     if(type < 256)
@@ -376,6 +432,8 @@ void UpdateFlowStats(SFFLOW *sfFlow, Packet *p)
         UpdateTCPFlowStats(sfFlow, p->sp, p->dp, len);
     else if (p->udph != NULL)
         UpdateUDPFlowStats(sfFlow, p->sp, p->dp, len);
+    else if (p->sctph != NULL)
+        UpdateSCTPFlowStats(sfFlow, p->sp, p->dp, len);
     else if (p->icmph != NULL)
         UpdateICMPFlowStats(sfFlow, p->icmph->type, len);
 
@@ -403,7 +461,7 @@ void ProcessFlowStats(SFFLOW *sfFlow, FILE *fh, int console)
     memset(&sfFlowStats, 0x00, sizeof(sfFlowStats));
 
     /*
-    **  Calculate the percentage of TCP, UDP and ICMP
+    **  Calculate the percentage of TCP, UDP, SCTP, and ICMP
     **  and other traffic that consisted in the stream.
     */
     if (sfFlow->byteTotal != 0)
@@ -412,18 +470,22 @@ void ProcessFlowStats(SFFLOW *sfFlow, FILE *fh, int console)
             (double)(sfFlow->byteTotal);
         sfFlowStats.trafficUDP = 100.0 * (double)(sfFlow->portUdpTotal) /
             (double)(sfFlow->byteTotal);
+        sfFlowStats.trafficSCTP = 100.0 * (double)(sfFlow->portSctpTotal) /
+            (double)(sfFlow->byteTotal);
         sfFlowStats.trafficICMP = 100.0 * (double)(sfFlow->typeIcmpTotal) /
             (double)(sfFlow->byteTotal);
         sfFlowStats.trafficOTHER = 100.0 *
             (double)((double)sfFlow->byteTotal -
                     ((double)sfFlow->portTcpTotal +
                      (double)sfFlow->portUdpTotal +
+                     (double)sfFlow->portSctpTotal +
                      (double)sfFlow->typeIcmpTotal)) / (double)sfFlow->byteTotal;
     }
     else
     {
         sfFlowStats.trafficTCP = 0;
         sfFlowStats.trafficUDP = 0;
+        sfFlowStats.trafficSCTP = 0;
         sfFlowStats.trafficICMP = 0;
         sfFlowStats.trafficOTHER = 0;
     }
@@ -523,6 +585,42 @@ void ProcessFlowStats(SFFLOW *sfFlow, FILE *fh, int console)
         sfFlowStats.portflowHighUDP = 0;
 
     /*
+    **  Calculate SCTP port processing based on src, dst and
+    **  total distributions.
+    */
+    for (i = 0; i < perfmon_config->flow_max_port_to_track; i++)
+    {
+        tot = sfFlow->portSctpSrc[i]+sfFlow->portSctpDst[i];
+        if(!tot)
+        {
+            sfFlowStats.portflowSCTP.totperc[i] = 0;
+            continue;
+        }
+
+        totperc= 100.0 * tot / sfFlow->portSctpTotal;
+
+        if (totperc >= 0.1)
+        {
+            srate =  100.0 * (double)(sfFlow->portSctpSrc[i]) / tot ;
+            drate =  100.0 * (double)(sfFlow->portSctpDst[i]) / tot ;
+
+            sfFlowStats.portflowSCTP.totperc[i]    = totperc;
+            sfFlowStats.portflowSCTP.sport_rate[i] = srate;
+            sfFlowStats.portflowSCTP.dport_rate[i] = drate;
+            sfFlowStats.portflowSCTPCount++;
+        }
+        else
+        {
+            sfFlowStats.portflowSCTP.totperc[i] = 0;
+        }
+    }
+
+    if (sfFlow->portSctpTotal > 0)
+        sfFlowStats.portflowHighSCTP = 100.0 * sfFlow->portSctpHigh / sfFlow->portSctpTotal;
+    else
+        sfFlowStats.portflowHighSCTP = 0;
+
+    /*
     **  Calculate ICMP statistics
     */
     for(i=0;i<256;i++)
@@ -573,6 +671,7 @@ static void DisplayFlowStats(SFFLOW_STATS *sfFlowStats)
     LogMessage("------------------\n");
     LogMessage("     TCP    %6.2f\n", sfFlowStats->trafficTCP);
     LogMessage("     UDP    %6.2f\n", sfFlowStats->trafficUDP);
+    LogMessage("    SCTP    %6.2f\n", sfFlowStats->trafficSCTP);
     LogMessage("    ICMP    %6.2f\n", sfFlowStats->trafficICMP);
     LogMessage("   Other    %6.2f\n", sfFlowStats->trafficOTHER);
 
@@ -665,6 +764,41 @@ static void DisplayFlowStats(SFFLOW_STATS *sfFlowStats)
 
     LogMessage("\n");
     LogMessage("=========================================\n");
+    LogMessage("SCTP Port Flows : %.2f%% of Total\n", sfFlowStats->trafficSCTP);
+    LogMessage("=========================================\n");
+    if (sfFlowStats->portflowSCTPCount || (sfFlowStats->portflowHighSCTP >= 0.1))
+    {
+        if (sfFlowStats->portflowSCTPCount)
+        {
+            LogMessage("Port   %%Total     %%Src     %%Dst\n");
+            LogMessage("-------------------------------\n");
+            for (i = 0; i <= SF_MAX_PORT; i++)
+            {
+                if (sfFlowStats->portflowSCTP.totperc[i])
+                {
+                    LogMessage("%4d   %6.2f   %6.2f   %6.2f\n",
+                            i, sfFlowStats->portflowSCTP.totperc[i],
+                            sfFlowStats->portflowSCTP.sport_rate[i],
+                            sfFlowStats->portflowSCTP.dport_rate[i]);
+                }
+            }
+        }
+
+        if (sfFlowStats->portflowHighSCTP >= 0.1)
+        {
+            if (sfFlowStats->portflowSCTPCount)
+                LogMessage("\n");
+
+            LogMessage("High<->High: %.2f%%\n", sfFlowStats->portflowHighSCTP);
+        }
+    }
+    else
+    {
+        LogMessage("N/A\n");
+    }
+
+    LogMessage("\n");
+    LogMessage("=========================================\n");
     LogMessage("ICMP Type Flows : %.2f%% of Total\n", sfFlowStats->trafficICMP);
     LogMessage("=========================================\n");
     if (sfFlowStats->flowICMPCount)
@@ -697,9 +831,10 @@ static void WriteFlowStats(SFFLOW_STATS *sfFlowStats, FILE *fh)
 
     fprintf(fh, "%u,", (uint32_t)sfFlowStats->time);
 
-    fprintf(fh, "%.2f,%.2f,%.2f,%.2f,",
+    fprintf(fh, "%.2f,%.2f,%.2f,%.2f,%.2f,",
             sfFlowStats->trafficTCP,
             sfFlowStats->trafficUDP,
+            sfFlowStats->trafficSCTP,
             sfFlowStats->trafficICMP,
             sfFlowStats->trafficOTHER);
 
@@ -738,6 +873,20 @@ static void WriteFlowStats(SFFLOW_STATS *sfFlowStats, FILE *fh)
 
     fprintf(fh, "%.2f,", sfFlowStats->portflowHighUDP);
 
+    fprintf(fh, "%d,", sfFlowStats->portflowSCTPCount);
+    for (i = 0; i <= SF_MAX_PORT; i++)
+    {
+        if (sfFlowStats->portflowSCTP.totperc[i])
+        {
+            fprintf(fh, "%d,%.2f,%.2f,%.2f,",
+                    i, sfFlowStats->portflowSCTP.totperc[i],
+                    sfFlowStats->portflowSCTP.sport_rate[i],
+                    sfFlowStats->portflowSCTP.dport_rate[i]);
+        }
+    }
+
+    fprintf(fh, "%.2f,", sfFlowStats->portflowHighSCTP);
+
     fprintf(fh, "%d,", sfFlowStats->flowICMPCount);
     for (i = 0; i < 256; i++)
     {
@@ -757,10 +906,11 @@ void LogFlowPerfHeader(FILE *fh)
         return;
 
     fprintf(fh,
-        "#%s,%s,%s,%s,%s,",
+        "#%s,%s,%s,%s,%s,%s,",
         "time",
         "trafficTCP",
         "trafficUDP",
+        "trafficSCTP",
         "trafficICMP",
         "trafficOTHER");
 
@@ -784,6 +934,13 @@ void LogFlowPerfHeader(FILE *fh)
         "(port,portflowUDP.totperc,portflowUDP.sport_rate,portflowUDP.dport_rate)*portflowUDPCount",
         "portflowHighUDP");
 
+    // SCTP flows
+    fprintf(fh,
+        "%s,%s,%s,",
+        "portflowSCTPCount",
+        "(port,portflowSCTP.totperc,portflowSCTP.sport_rate,portflowSCTP.dport_rate)*portflowSCTPCount",
+        "portflowHighSCTP");
+
     // ICMP flows
     fprintf(fh,
         "%s,%s,",
@@ -822,9 +979,10 @@ static void DisplayFlowIPStats(SFFLOW *sfFlow)
 
         sfip_raw_ntop(key->ipA.family, key->ipA.ip32, ipA, sizeof(ipA));
         sfip_raw_ntop(key->ipB.family, key->ipB.ip32, ipB, sizeof(ipB));
-        LogMessage("[%s <-> %s]: " STDu64 " bytes in " STDu64 " packets (%u, %u, %u)\n", ipA, ipB,
+        LogMessage("[%s <-> %s]: " STDu64 " bytes in " STDu64 " packets (%u, %u, %u, %u)\n", ipA, ipB,
                 stats->total_bytes, stats->total_packets, stats->stateChanges[SFS_STATE_TCP_ESTABLISHED],
-                stats->stateChanges[SFS_STATE_TCP_CLOSED], stats->stateChanges[SFS_STATE_UDP_CREATED]);
+                stats->stateChanges[SFS_STATE_TCP_CLOSED], stats->stateChanges[SFS_STATE_UDP_CREATED],
+                stats->stateChanges[SFS_STATE_SCTP_CREATED]);
         total += stats->total_packets;
     }
     LogMessage("Classified " STDu64 " packets.\n", total);
@@ -848,13 +1006,15 @@ static void WriteFlowIPStats(SFFLOW *sfFlow, FILE *fp)
 
         sfip_raw_ntop(key->ipA.family, key->ipA.ip32, ipA, sizeof(ipA));
         sfip_raw_ntop(key->ipB.family, key->ipB.ip32, ipB, sizeof(ipB));
-        fprintf(fp, "%s,%s," CSVu64 CSVu64 CSVu64 CSVu64 CSVu64 CSVu64 CSVu64
-                CSVu64 CSVu64 CSVu64 CSVu64 CSVu64 "%u,%u,%u\n",
+        fprintf(fp, "%s,%s," CSVu64 CSVu64 CSVu64 CSVu64 CSVu64 CSVu64 CSVu64 CSVu64 CSVu64
+                CSVu64 CSVu64 CSVu64 CSVu64 CSVu64 CSVu64 CSVu64 "%u,%u,%u\n",
                 ipA, ipB,
                 stats->trafficStats[SFS_TYPE_TCP].packets_AtoB, stats->trafficStats[SFS_TYPE_TCP].bytes_AtoB,
                 stats->trafficStats[SFS_TYPE_TCP].packets_BtoA, stats->trafficStats[SFS_TYPE_TCP].bytes_BtoA,
                 stats->trafficStats[SFS_TYPE_UDP].packets_AtoB, stats->trafficStats[SFS_TYPE_UDP].bytes_AtoB,
                 stats->trafficStats[SFS_TYPE_UDP].packets_BtoA, stats->trafficStats[SFS_TYPE_UDP].bytes_BtoA,
+                stats->trafficStats[SFS_TYPE_SCTP].packets_AtoB, stats->trafficStats[SFS_TYPE_SCTP].bytes_AtoB,
+                stats->trafficStats[SFS_TYPE_SCTP].packets_BtoA, stats->trafficStats[SFS_TYPE_SCTP].bytes_BtoA,
                 stats->trafficStats[SFS_TYPE_OTHER].packets_AtoB, stats->trafficStats[SFS_TYPE_OTHER].bytes_AtoB,
                 stats->trafficStats[SFS_TYPE_OTHER].packets_BtoA, stats->trafficStats[SFS_TYPE_OTHER].bytes_BtoA,
                 stats->stateChanges[SFS_STATE_TCP_ESTABLISHED], stats->stateChanges[SFS_STATE_TCP_CLOSED],
diff --git a/src/preprocessors/perf-flow.h b/src/preprocessors/perf-flow.h
index f6af407..a1c1501 100644
--- a/src/preprocessors/perf-flow.h
+++ b/src/preprocessors/perf-flow.h
@@ -6,6 +6,7 @@
 ** Copyright (C) 2002-2013 Sourcefire, Inc.
 ** Marc Norton <mnorton at sourcefire.com>
 ** Dan Roelker <droelker at sourcefire.com>
+** Joshua Kinard
 **
 **
 ** This program is free software; you can redistribute it and/or modify
@@ -40,15 +41,17 @@
 typedef enum {
     SFS_TYPE_TCP   = 0,
     SFS_TYPE_UDP   = 1,
-    SFS_TYPE_OTHER = 2,
-    SFS_TYPE_MAX   = 3
+    SFS_TYPE_SCTP  = 2,
+    SFS_TYPE_OTHER = 3,
+    SFS_TYPE_MAX   = 4
 } SFSType;
 
 typedef enum {
     SFS_STATE_TCP_ESTABLISHED = 0,
     SFS_STATE_TCP_CLOSED      = 1,
     SFS_STATE_UDP_CREATED     = 2,
-    SFS_STATE_MAX             = 3
+    SFS_STATE_SCTP_CREATED    = 3,
+    SFS_STATE_MAX             = 4
 } SFSState;
 
 typedef struct _portflow {
@@ -80,6 +83,8 @@ typedef struct _sfflow {
     uint64_t   *portTcpDst;
     uint64_t   *portUdpSrc;
     uint64_t   *portUdpDst;
+    uint64_t   *portSctpSrc;
+    uint64_t   *portSctpDst;
 
     uint64_t   *typeIcmp;
 
@@ -89,6 +94,9 @@ typedef struct _sfflow {
     uint64_t    portUdpHigh;
     uint64_t    portUdpTotal;
 
+    uint64_t    portSctpHigh;
+    uint64_t    portSctpTotal;
+
     uint64_t    typeIcmpTotal;
 
     SFXHASH     *ipMap;
@@ -102,6 +110,7 @@ typedef struct _sfflow_stats {
 
     double    trafficTCP;
     double    trafficUDP;
+    double    trafficSCTP;
     double    trafficICMP;
     double    trafficOTHER;
 
@@ -113,10 +122,13 @@ typedef struct _sfflow_stats {
     double    portflowHighUDP;
     int       portflowUDPCount;
 
+    PORTFLOW  portflowSCTP;
+    double    portflowHighSCTP;
+    int       portflowSCTPCount;
+
     ICMPFLOW  flowICMP;
     int       flowICMPCount;
 
-
 }  SFFLOW_STATS;
 
 /*
diff --git a/src/preprocessors/perf.c b/src/preprocessors/perf.c
index 18b2911..ed73cf1 100644
--- a/src/preprocessors/perf.c
+++ b/src/preprocessors/perf.c
@@ -586,6 +586,8 @@ static void UpdatePerfStats(SFPERF *sfPerf, Packet *p)
             type = SFS_TYPE_TCP;
         else if (p->udph != NULL)
             type = SFS_TYPE_UDP;
+        else if (p->sctph != NULL)
+            type = SFS_TYPE_SCTP;
 
         UpdateFlowIPStats(&sfFlow, GET_SRC_IP(p), GET_DST_IP(p), p->pkth->caplen, type);
     }
diff --git a/src/preprocessors/portscan.c b/src/preprocessors/portscan.c
index cd08dfc..a9c7062 100644
--- a/src/preprocessors/portscan.c
+++ b/src/preprocessors/portscan.c
@@ -54,7 +54,7 @@
 **
 **  The primary goal of this portscan detection engine is to catch nmap and
 **  variant scanners.  The engine tracks connection attempts on TCP, UDP,
-**  ICMP, and IP Protocols.  If there is a valid response, the connection
+**  ICMP, SCTP, and IP Protocols.  If there is a valid response, the connection
 **  is marked as valid.  If there is no response or a invalid response
 **  (TCP RST), then we track these attempts separately, so we know the
 **  number of invalid responses and the number of connection attempts that
@@ -188,6 +188,24 @@ static PS_ALERT_CONF g_udp_hi_sweep =     {30,3,3,10};
 static PS_ALERT_CONF g_udp_hi_dist_ps =   {200,3,200,10};
 
 /*
+**  SCTP alert configurations
+*/
+static PS_ALERT_CONF g_sctp_low_ps =       {0,5,25,5};
+static PS_ALERT_CONF g_sctp_low_decoy_ps = {0,15,50,30};
+static PS_ALERT_CONF g_sctp_low_sweep =    {0,5,5,15};
+static PS_ALERT_CONF g_sctp_low_dist_ps =  {0,15,50,15};
+
+static PS_ALERT_CONF g_sctp_med_ps =       {200,10,60,15};
+static PS_ALERT_CONF g_sctp_med_decoy_ps = {200,30,120,60};
+static PS_ALERT_CONF g_sctp_med_sweep =    {30,7,7,10};
+static PS_ALERT_CONF g_sctp_med_dist_ps =  {200,30,120,30};
+
+static PS_ALERT_CONF g_sctp_hi_ps =        {200,5,100,10};
+static PS_ALERT_CONF g_sctp_hi_decoy_ps =  {200,7,200,60};
+static PS_ALERT_CONF g_sctp_hi_sweep =     {30,3,3,10};
+static PS_ALERT_CONF g_sctp_hi_dist_ps =   {200,5,200,10};
+
+/*
 **  IP Protocol alert configurations
 */
 static PS_ALERT_CONF g_ip_low_ps =        {0,10,10,50};
@@ -429,6 +447,11 @@ static int ps_filter_ignore(PS_PKT *ps_pkt)
         if(!(portscan_eval_config->detect_scans & PS_PROTO_UDP))
             return 1;
     }
+    else if(p->sctph)
+    {
+        if(!(portscan_eval_config->detect_scans & PS_PROTO_SCTP))
+            return 1;
+    }
     else if(p->icmph)
     {
         if(p->icmph->type != ICMP_DEST_UNREACH &&
@@ -454,7 +477,7 @@ static int ps_filter_ignore(PS_PKT *ps_pkt)
     {
         reverse_pkt = 1;
     }
-    else if (p->udph && p->ssnptr &&
+    else if ((p->udph || p->sctph) && p->ssnptr &&
              stream_api && stream_api->version >= STREAM_API_VERSION5)
     {
         if (stream_api->get_packet_direction(p) & PKT_FROM_SERVER)
@@ -643,6 +666,19 @@ static int ps_get_proto(PS_PKT *ps_pkt, int *proto)
         }
     }
 
+    if (portscan_eval_config->detect_scans & PS_PROTO_SCTP)
+    {
+        if ((p->sctph != NULL)
+                || ((p->icmph != NULL) && (p->icmph->type == ICMP_DEST_UNREACH)
+                    && ((p->icmph->code == ICMP_PORT_UNREACH)
+                        || (p->icmph->code == ICMP_PKT_FILTERED))
+                    && (p->orig_sctph != NULL)))
+        {
+            *proto = PS_PROTO_SCTP;
+            return 0;
+        }
+    }
+
     if (portscan_eval_config->detect_scans & PS_PROTO_IP)
     {
         if ((IPH_IS_VALID(p) && (p->icmph == NULL))
@@ -1141,6 +1177,64 @@ static int ps_tracker_update_udp(PS_PKT *ps_pkt, PS_TRACKER *scanner,
     return 0;
 }
 
+static int ps_tracker_update_sctp(PS_PKT *ps_pkt, PS_TRACKER *scanner,
+                                 PS_TRACKER *scanned)
+{
+    Packet  *p;
+    snort_ip    cleared;
+    IP_CLEAR(cleared);
+
+    p = (Packet *)ps_pkt->pkt;
+
+    if(p->icmph)
+    {
+        if(scanned)
+        {
+            ps_proto_update(&scanned->proto,0,1,CLEARED,0,0);
+            scanned->priority_node = 1;
+        }
+
+        if(scanner)
+        {
+            ps_proto_update(&scanner->proto,0,1,CLEARED,0,0);
+            scanner->priority_node = 1;
+        }
+    }
+    else if(p->sctph)
+    {
+        if (stream_api && (stream_api->version >= STREAM_API_VERSION5) &&
+            p->ssnptr)
+        {
+            uint32_t direction = stream_api->get_packet_direction(p);
+
+            if (direction == PKT_FROM_CLIENT)
+            {
+                if(scanned)
+                {
+                    ps_proto_update(&scanned->proto,1,0,
+                                     GET_SRC_IP(p),p->dp, packet_time());
+                }
+
+                if(scanner)
+                {
+                    ps_proto_update(&scanner->proto,1,0,
+                                     GET_DST_IP(p),p->dp, packet_time());
+                }
+            }
+            else if (direction == PKT_FROM_SERVER)
+            {
+                if(scanned)
+                    ps_proto_update(&scanned->proto,-1,0,CLEARED,0,0);
+
+                if(scanner)
+                    ps_proto_update(&scanner->proto,-1,0,CLEARED,0,0);
+            }
+        }
+    }
+
+    return 0;
+}
+
 static int ps_tracker_update_icmp(PS_PKT *ps_pkt, PS_TRACKER *scanner,
                                   PS_TRACKER *scanned)
 {
@@ -1221,6 +1315,12 @@ static int ps_tracker_update(PS_PKT *ps_pkt, PS_TRACKER *scanner,
 
             break;
 
+        case PS_PROTO_SCTP:
+            if(ps_tracker_update_sctp(ps_pkt, scanner, scanned))
+                return -1;
+
+            break;
+
         case PS_PROTO_ICMP:
             if(ps_tracker_update_icmp(ps_pkt, scanner, scanned))
                 return -1;
@@ -1611,6 +1711,77 @@ static int ps_alert_udp(PS_PROTO *scanner, PS_PROTO *scanned)
     return 0;
 }
 
+static int ps_alert_sctp(PS_PROTO *scanner, PS_PROTO *scanned)
+{
+    static PS_ALERT_CONF *one_to_one;
+    static PS_ALERT_CONF *one_to_one_decoy;
+    static PS_ALERT_CONF *one_to_many;
+    static PS_ALERT_CONF *many_to_one;
+
+    /*
+    ** Set the configurations depending on the sensitivity
+    ** level.
+    */
+    switch(portscan_eval_config->sense_level)
+    {
+        case PS_SENSE_HIGH:
+            one_to_one       = &g_sctp_hi_ps;
+            one_to_one_decoy = &g_sctp_hi_decoy_ps;
+            one_to_many      = &g_sctp_hi_sweep;
+            many_to_one      = &g_sctp_hi_dist_ps;
+
+            break;
+
+        case PS_SENSE_MEDIUM:
+            one_to_one       = &g_sctp_med_ps;
+            one_to_one_decoy = &g_sctp_med_decoy_ps;
+            one_to_many      = &g_sctp_med_sweep;
+            many_to_one      = &g_sctp_med_dist_ps;
+
+            break;
+
+        case PS_SENSE_LOW:
+            one_to_one       = &g_sctp_low_ps;
+            one_to_one_decoy = &g_sctp_low_decoy_ps;
+            one_to_many      = &g_sctp_low_sweep;
+            many_to_one      = &g_sctp_low_dist_ps;
+
+            break;
+
+        default:
+            return -1;
+    }
+
+    /*
+    **  Do detection on the different portscan types.
+    */
+    if((portscan_eval_config->detect_scan_type & PS_TYPE_PORTSCAN) &&
+        ps_alert_one_to_one(scanner, scanned, one_to_one))
+    {
+        return 0;
+    }
+
+    if((portscan_eval_config->detect_scan_type & PS_TYPE_DECOYSCAN) &&
+        ps_alert_one_to_one_decoy(scanner, scanned, one_to_one_decoy))
+    {
+        return 0;
+    }
+
+    if((portscan_eval_config->detect_scan_type & PS_TYPE_PORTSWEEP) &&
+        ps_alert_one_to_many(scanner, scanned, one_to_many))
+    {
+        return 0;
+    }
+
+    if((portscan_eval_config->detect_scan_type & PS_TYPE_DISTPORTSCAN) &&
+        ps_alert_many_to_one(scanner, scanned, many_to_one))
+    {
+        return 0;
+    }
+
+    return 0;
+}
+
 static int ps_alert_icmp(PS_PROTO *scanner, PS_PROTO *scanned)
 {
     static PS_ALERT_CONF *one_to_many;
@@ -1684,6 +1855,11 @@ static int ps_tracker_alert(PS_PKT *ps_pkt, PS_TRACKER *scanner,
                          (scanned ? &scanned->proto : NULL));
             break;
 
+        case PS_PROTO_SCTP:
+            ps_alert_sctp((scanner ? &scanner->proto : NULL),
+                          (scanned ? &scanned->proto : NULL));
+            break;
+
         case PS_PROTO_ICMP:
             ps_alert_icmp((scanner ? &scanner->proto : NULL),
                           (scanned ? &scanned->proto : NULL));
@@ -1810,6 +1986,12 @@ void ps_tracker_print(PS_TRACKER* ps_tracker)
         ps_proto_print(&ps_tracker->proto);
         proto_index++;
     }
+    if(portscan_eval_config->detect_scans & PS_PROTO_SCTP)
+    {
+        printf("    ** SCTP **\n");
+        ps_proto_print(&ps_tracker->proto);
+        proto_index++;
+    }
     if(portscan_eval_config->detect_scans & PS_PROTO_IP)
     {
         printf("    ** IP **\n");
diff --git a/src/preprocessors/portscan.h b/src/preprocessors/portscan.h
index b93c655..091cbc4 100644
--- a/src/preprocessors/portscan.h
+++ b/src/preprocessors/portscan.h
@@ -104,9 +104,10 @@ typedef struct s_PS_PKT
 #define PS_PROTO_NONE        0x00
 #define PS_PROTO_TCP         0x01
 #define PS_PROTO_UDP         0x02
-#define PS_PROTO_ICMP        0x04
-#define PS_PROTO_IP          0x08
-#define PS_PROTO_ALL         0x0f
+#define PS_PROTO_SCTP        0x04
+#define PS_PROTO_ICMP        0x08
+#define PS_PROTO_IP          0x10
+#define PS_PROTO_ALL         0x1f
 
 #define PS_PROTO_OPEN_PORT   0x80
 
diff --git a/src/preprocessors/spp_frag3.c b/src/preprocessors/spp_frag3.c
index e755de4..819e50e 100644
--- a/src/preprocessors/spp_frag3.c
+++ b/src/preprocessors/spp_frag3.c
@@ -504,8 +504,8 @@ static void PrintFragKey(FRAGKEY *fkey)
 
     if(fkey)
     {
-        LogMessage("   sip: %s\n", FragIPToStr(fkey->sip, fkey->ipver));
-        LogMessage("   dip: %s\n", FragIPToStr(fkey->dip, fkey->ipver));
+        LogMessage("    sip: %s\n", FragIPToStr(fkey->sip, fkey->ipver));
+        LogMessage("    dip: %s\n", FragIPToStr(fkey->dip, fkey->ipver));
         LogMessage("     id: %d\n", fkey->id);
         LogMessage("  proto: 0x%X\n", fkey->proto);
 #ifdef MPLS
@@ -4744,6 +4744,11 @@ int fragGetApplicationProtocolId(Packet *p)
             src_port = p->sp;
             dst_port = p->dp;
             break;
+        case IPPROTO_SCTP:
+            ft->ipprotocol = protocolReferenceSCTP;
+            src_port = p->sp;
+            dst_port = p->dp;
+            break;
         case IPPROTO_ICMP:
             ft->ipprotocol = protocolReferenceICMP;
             break;
diff --git a/src/preprocessors/spp_sfportscan.c b/src/preprocessors/spp_sfportscan.c
index b844626..d9b326f 100644
--- a/src/preprocessors/spp_sfportscan.c
+++ b/src/preprocessors/spp_sfportscan.c
@@ -31,7 +31,7 @@
 **    - User Configuration:  The following is a list of parameters that can
 **      be configured through the user interface:
 **
-**      proto  { tcp udp icmp ip all }
+**      proto  { tcp udp sctp icmp ip all }
 **      scan_type { portscan portsweep decoy_portscan distributed_portscan all }
 **      sense_level { high }    # high, medium, low
 **      watch_ip { }            # list of IPs, CIDR blocks
@@ -443,6 +443,9 @@ static int MakePortscanPkt(PS_PKT *ps_pkt, PS_PROTO *proto, int proto_type,
         case PS_PROTO_UDP:
             g_tmp_pkt->ps_proto = IPPROTO_UDP;
             break;
+        case PS_PROTO_SCTP:
+            g_tmp_pkt->ps_proto = IPPROTO_SCTP;
+            break;
         case PS_PROTO_ICMP:
             g_tmp_pkt->ps_proto = IPPROTO_ICMP;
             break;
@@ -473,6 +476,7 @@ static int MakePortscanPkt(PS_PKT *ps_pkt, PS_PROTO *proto, int proto_type,
     {
         case PS_PROTO_TCP:
         case PS_PROTO_UDP:
+        case PS_PROTO_SCTP:
         case PS_PROTO_ICMP:
         case PS_PROTO_IP:
             if(MakeProtoInfo(proto, (u_char *)g_tmp_pkt->data, &ip_size))
@@ -661,6 +665,61 @@ static int PortscanAlertUdp(Packet *p, PS_PROTO *proto, int proto_type)
     return 0;
 }
 
+static int PortscanAlertSctp(Packet *p, PS_PROTO *proto, int proto_type)
+{
+    if(!proto)
+        return -1;
+
+    switch(proto->alerts)
+    {
+        case PS_ALERT_ONE_TO_ONE:
+            GeneratePSSnortEvent(p, GENERATOR_PSNG, PSNG_SCTP_PORTSCAN, 0, 0, 3,
+                    PSNG_SCTP_PORTSCAN_STR);
+            break;
+
+        case PS_ALERT_ONE_TO_ONE_DECOY:
+            GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_SCTP_DECOY_PORTSCAN, 0, 0, 3,
+                    PSNG_SCTP_DECOY_PORTSCAN_STR);
+            break;
+
+        case PS_ALERT_PORTSWEEP:
+           GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_SCTP_PORTSWEEP, 0, 0, 3,
+                    PSNG_SCTP_PORTSWEEP_STR);
+            break;
+
+        case PS_ALERT_DISTRIBUTED:
+            GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_SCTP_DISTRIBUTED_PORTSCAN,
+                    0, 0, 3, PSNG_SCTP_DISTRIBUTED_PORTSCAN_STR);
+            break;
+
+        case PS_ALERT_ONE_TO_ONE_FILTERED:
+            GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_SCTP_FILTERED_PORTSCAN,0,0,3,
+                    PSNG_SCTP_FILTERED_PORTSCAN_STR);
+            break;
+
+        case PS_ALERT_ONE_TO_ONE_DECOY_FILTERED:
+            GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_SCTP_FILTERED_DECOY_PORTSCAN,
+                    0,0,3, PSNG_SCTP_FILTERED_DECOY_PORTSCAN_STR);
+            break;
+
+        case PS_ALERT_PORTSWEEP_FILTERED:
+           GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_SCTP_PORTSWEEP_FILTERED,0,0,3,
+                    PSNG_SCTP_PORTSWEEP_FILTERED_STR);
+            break;
+
+        case PS_ALERT_DISTRIBUTED_FILTERED:
+            GeneratePSSnortEvent(p,GENERATOR_PSNG,
+                    PSNG_SCTP_FILTERED_DISTRIBUTED_PORTSCAN, 0, 0, 3,
+                    PSNG_SCTP_FILTERED_DISTRIBUTED_PORTSCAN_STR);
+            break;
+
+        default:
+            break;
+    }
+
+    return 0;
+}
+
 static int PortscanAlertIp(Packet *p, PS_PROTO *proto, int proto_type)
 {
     if(!proto)
@@ -772,6 +831,10 @@ static int PortscanAlert(PS_PKT *ps_pkt, PS_PROTO *proto, int proto_type)
                 PortscanAlertUdp(g_tmp_pkt, proto, proto_type);
                 break;
 
+            case PS_PROTO_SCTP:
+                PortscanAlertSctp(g_tmp_pkt, proto, proto_type);
+                break;
+
             case PS_PROTO_ICMP:
                 PortscanAlertIcmp(g_tmp_pkt, proto, proto_type);
                 break;
@@ -870,6 +933,8 @@ static void ParseProtos(int *protos, char **savptr)
             *protos |= PS_PROTO_TCP;
         else if(!strcasecmp(pcTok, "udp"))
             *protos |= PS_PROTO_UDP;
+        else if(!strcasecmp(pcTok, "sctp"))
+            *protos |= PS_PROTO_SCTP;
         else if(!strcasecmp(pcTok, "icmp"))
             *protos |= PS_PROTO_ICMP;
         else if(!strcasecmp(pcTok, "ip"))
@@ -1063,6 +1128,7 @@ static void PrintPortscanConf(int detect_scans, int detect_scan_type,
         SnortSnprintf(buf, STD_BUF + 1, "    Detect Protocols:  ");
         if(detect_scans & PS_PROTO_TCP)  { sfsnprintfappend(buf, STD_BUF, "TCP ");  proto_cnt++; }
         if(detect_scans & PS_PROTO_UDP)  { sfsnprintfappend(buf, STD_BUF, "UDP ");  proto_cnt++; }
+        if(detect_scans & PS_PROTO_SCTP) { sfsnprintfappend(buf, STD_BUF, "SCTP "); proto_cnt++; }
         if(detect_scans & PS_PROTO_ICMP) { sfsnprintfappend(buf, STD_BUF, "ICMP "); proto_cnt++; }
         if(detect_scans & PS_PROTO_IP)   { sfsnprintfappend(buf, STD_BUF, "IP");    proto_cnt++; }
         LogMessage("%s\n", buf);
@@ -1246,7 +1312,7 @@ void SetupSfPortscan(void)
 static void ParsePortscan(struct _SnortConfig *sc, PortscanConfig *config, char *args)
 {
     int    sense_level = PS_SENSE_LOW;
-    int    protos      = (PS_PROTO_TCP | PS_PROTO_UDP);
+    int    protos      = (PS_PROTO_TCP | PS_PROTO_UDP | PS_PROTO_SCTP);
     int    scan_types  = PS_TYPE_ALL;
     unsigned long memcap = 1048576;
     IPSET *ignore_scanners = NULL;
@@ -1447,6 +1513,12 @@ static int PortscanGetProtoBits(int detect_scans)
         proto_bits |= PROTO_BIT__UDP;
     }
 
+    if (detect_scans & PS_PROTO_SCTP)
+    {
+        proto_bits |= PROTO_BIT__ICMP;
+        proto_bits |= PROTO_BIT__SCTP;
+    }
+
     if (detect_scans & PS_PROTO_ICMP)
         proto_bits |= PROTO_BIT__ICMP;
 
diff --git a/src/preprocessors/spp_stream5.c b/src/preprocessors/spp_stream5.c
index cd56e82..bf46b9f 100644
--- a/src/preprocessors/spp_stream5.c
+++ b/src/preprocessors/spp_stream5.c
@@ -55,6 +55,7 @@
 #include "snort_stream5_session.h"
 #include "snort_stream5_tcp.h"
 #include "snort_stream5_udp.h"
+#include "snort_stream5_sctp.h"
 #include "snort_stream5_icmp.h"
 #include "snort_stream5_ip.h"
 #include "checksum.h"
@@ -84,6 +85,7 @@
 PreprocStats s5PerfStats;
 extern PreprocStats s5TcpPerfStats;
 extern PreprocStats s5UdpPerfStats;
+extern PreprocStats s5SctpPerfStats;
 extern PreprocStats s5IcmpPerfStats;
 extern PreprocStats s5IpPerfStats;
 # ifdef ENABLE_HA
@@ -94,6 +96,7 @@ extern PreprocStats s5HAPerfStats;
 extern OptTreeNode *otn_tmp;
 extern Stream5SessionCache *tcp_lws_cache;
 extern Stream5SessionCache *udp_lws_cache;
+extern Stream5SessionCache *sctp_lws_cache;
 extern Stream5SessionCache *icmp_lws_cache;
 extern Stream5SessionCache *ip_lws_cache;
 
@@ -115,12 +118,15 @@ extern FlushConfig ignore_flush_policy_protocol[MAX_PROTOCOL_ORDINAL];
 #define S5_MIN_TCP_SESSIONS 2  /* set at least 2 TCP sessions */
 #define S5_DEFAULT_MAX_TCP_SESSIONS 262144 /* 256k TCP sessions by default */
 #define S5_DEFAULT_MAX_UDP_SESSIONS 131072 /* 128k UDP sessions by default */
+#define S5_DEFAULT_MAX_SCTP_SESSIONS 262144 /* 256k SCTP sessions by default */
 #define S5_DEFAULT_MAX_ICMP_SESSIONS 65536 /* 64k ICMP sessions by default */
 #define S5_DEFAULT_MAX_IP_SESSIONS   16384 /* 16k IP sessions by default */
 #define S5_DEFAULT_TCP_CACHE_PRUNING_TIMEOUT    30          /* 30 seconds */
 #define S5_DEFAULT_TCP_CACHE_NOMINAL_TIMEOUT    (60 * 60)   /* 1 hour */
 #define S5_DEFAULT_UDP_CACHE_PRUNING_TIMEOUT    30          /* 30 seconds */
 #define S5_DEFAULT_UDP_CACHE_NOMINAL_TIMEOUT    (3 * 60)    /* 3 minutes */
+#define S5_DEFAULT_SCTP_CACHE_PRUNING_TIMEOUT   30          /* 30 seconds */
+#define S5_DEFAULT_SCTP_CACHE_NOMINAL_TIMEOUT   (60 * 60)   /* 1 hour */
 #define S5_MAX_CACHE_TIMEOUT                    (12 * 60 * 60)  /* 12 hours */
 #define S5_MIN_PRUNE_LOG_MAX     1024      /* 1k packet data stored */
 #define S5_MAX_PRUNE_LOG_MAX     S5_RIDICULOUS_HI_MEMCAP  /* 1GB packet data stored */
@@ -140,6 +146,7 @@ tSfPolicyUserContextId s5_config = NULL;
 Stream5GlobalConfig *s5_global_eval_config = NULL;
 Stream5TcpConfig *s5_tcp_eval_config = NULL;
 Stream5UdpConfig *s5_udp_eval_config = NULL;
+Stream5SctpConfig *s5_sctp_eval_config = NULL;
 Stream5IcmpConfig *s5_icmp_eval_config = NULL;
 Stream5IpConfig *s5_ip_eval_config = NULL;
 
@@ -148,7 +155,7 @@ uint32_t mem_in_use = 0;
 uint32_t firstPacketTime = 0;
 Stream5Stats s5stats;
 MemPool s5FlowMempool;
-static PoolCount s_tcp_sessions = 0, s_udp_sessions = 0;
+static PoolCount s_tcp_sessions = 0, s_udp_sessions = 0, s_sctp_sessions = 0;
 static PoolCount s_icmp_sessions = 0, s_ip_sessions = 0;
 static int s_proto_flags = 0;
 
@@ -163,6 +170,7 @@ static void Stream5GlobalInit(struct _SnortConfig *, char *);
 static void Stream5ParseGlobalArgs(Stream5GlobalConfig *, char *);
 static void Stream5PolicyInitTcp(struct _SnortConfig *, char *);
 static void Stream5PolicyInitUdp(struct _SnortConfig *, char *);
+static void Stream5PolicyInitSctp(struct _SnortConfig *, char *);
 static void Stream5PolicyInitIcmp(struct _SnortConfig *, char *);
 static void Stream5PolicyInitIp(struct _SnortConfig *, char *);
 static void Stream5CleanExit(int, void *);
@@ -181,6 +189,7 @@ static void s5InitServiceFilterStatus(struct _SnortConfig *);
 static void Stream5GlobalReload(struct _SnortConfig *, char *, void **);
 static void Stream5TcpReload(struct _SnortConfig *, char *, void **);
 static void Stream5UdpReload(struct _SnortConfig *, char *, void **);
+static void Stream5SctpReload(struct _SnortConfig *, char *, void **);
 static void Stream5IcmpReload(struct _SnortConfig *, char *, void **);
 static void Stream5IpReload(struct _SnortConfig *, char *, void **);
 static int Stream5ReloadVerify(struct _SnortConfig *, void *);
@@ -257,7 +266,7 @@ static void *Stream5GetApplicationDataFromIpPort(
                     uint16_t srcPort,
                     snort_ip_p dstIP,
                     uint16_t dstPort,
-                    char ip_protocol,
+                    uint8_t ip_protocol,
                     uint16_t vlan,
                     uint32_t mplsId,
                     uint16_t addressSpaceId,
@@ -350,7 +359,7 @@ static uint16_t s5GetPreprocessorStatusBit(void);
 
 static void s5SetPortFilterStatus(
         struct _SnortConfig *sc,
-        int protocol,
+        uint8_t protocol,
         uint16_t port,
         uint16_t status,
         tSfPolicyId policyId,
@@ -359,7 +368,7 @@ static void s5SetPortFilterStatus(
 
 static void s5UnsetPortFilterStatus(
         struct _SnortConfig *sc,
-        int protocol,
+        uint8_t protocol,
         uint16_t port,
         uint16_t status,
         tSfPolicyId policyId,
@@ -373,7 +382,7 @@ static void *Stream5GetSessionPtrFromIpPort(
                     uint16_t srcPort,
                     snort_ip_p dstIP,
                     uint16_t dstPort,
-                    char ip_protocol,
+                    uint8_t ip_protocol,
                     uint16_t vlan,
                     uint32_t mplsId,
                     uint16_t addressSpaceId);
@@ -510,6 +519,7 @@ void SetupStream5(void)
     RegisterPreprocessor("stream5_global", Stream5GlobalInit);
     RegisterPreprocessor("stream5_tcp", Stream5PolicyInitTcp);
     RegisterPreprocessor("stream5_udp", Stream5PolicyInitUdp);
+    RegisterPreprocessor("stream5_sctp", Stream5PolicyInitSctp);
     RegisterPreprocessor("stream5_icmp", Stream5PolicyInitIcmp);
     RegisterPreprocessor("stream5_ip", Stream5PolicyInitIp);
 # ifdef ENABLE_HA
@@ -523,6 +533,8 @@ void SetupStream5(void)
                          Stream5TcpReload, NULL, NULL, NULL);
     RegisterPreprocessor("stream5_udp", Stream5PolicyInitUdp,
                          Stream5UdpReload, NULL, NULL, NULL);
+    RegisterPreprocessor("stream5_sctp", Stream5PolicyInitSctp,
+                         Stream5SctpReload, NULL, NULL, NULL);
     RegisterPreprocessor("stream5_icmp", Stream5PolicyInitIcmp,
                          Stream5IcmpReload, NULL, NULL, NULL);
     RegisterPreprocessor("stream5_ip", Stream5PolicyInitIp,
@@ -553,6 +565,7 @@ static void Stream5GlobalInit(struct _SnortConfig *sc, char *args)
         RegisterPreprocessorProfile("s5", &s5PerfStats, 0, &totalPerfStats);
         RegisterPreprocessorProfile("s5tcp", &s5TcpPerfStats, 1, &s5PerfStats);
         RegisterPreprocessorProfile("s5udp", &s5UdpPerfStats, 1, &s5PerfStats);
+        RegisterPreprocessorProfile("s5sctp", &s5SctpPerfStats, 1, &s5PerfStats);
         RegisterPreprocessorProfile("s5icmp", &s5IcmpPerfStats, 1, &s5PerfStats);
         RegisterPreprocessorProfile("s5ip", &s5IpPerfStats, 1, &s5PerfStats);
 # ifdef ENABLE_HA
@@ -605,6 +618,10 @@ static void Stream5GlobalInit(struct _SnortConfig *sc, char *args)
     pCurrentPolicyConfig->global_config->max_udp_sessions = S5_DEFAULT_MAX_UDP_SESSIONS;
     pCurrentPolicyConfig->global_config->udp_cache_pruning_timeout = S5_DEFAULT_UDP_CACHE_PRUNING_TIMEOUT;
     pCurrentPolicyConfig->global_config->udp_cache_nominal_timeout = S5_DEFAULT_UDP_CACHE_NOMINAL_TIMEOUT;
+    pCurrentPolicyConfig->global_config->track_sctp_sessions = S5_TRACK_YES;
+    pCurrentPolicyConfig->global_config->max_sctp_sessions = S5_DEFAULT_MAX_SCTP_SESSIONS;
+    pCurrentPolicyConfig->global_config->sctp_cache_pruning_timeout = S5_DEFAULT_SCTP_CACHE_PRUNING_TIMEOUT;
+    pCurrentPolicyConfig->global_config->sctp_cache_nominal_timeout = S5_DEFAULT_SCTP_CACHE_NOMINAL_TIMEOUT;
     pCurrentPolicyConfig->global_config->track_icmp_sessions = S5_TRACK_NO;
     pCurrentPolicyConfig->global_config->max_icmp_sessions = S5_DEFAULT_MAX_ICMP_SESSIONS;
     pCurrentPolicyConfig->global_config->track_ip_sessions = S5_TRACK_NO;
@@ -626,11 +643,12 @@ static void Stream5GlobalInit(struct _SnortConfig *sc, char *args)
     if ((!pCurrentPolicyConfig->global_config->disabled) &&
         (pCurrentPolicyConfig->global_config->track_tcp_sessions == S5_TRACK_NO) &&
         (pCurrentPolicyConfig->global_config->track_udp_sessions == S5_TRACK_NO) &&
+        (pCurrentPolicyConfig->global_config->track_sctp_sessions == S5_TRACK_NO) &&
         (pCurrentPolicyConfig->global_config->track_icmp_sessions == S5_TRACK_NO) &&
         (pCurrentPolicyConfig->global_config->track_ip_sessions == S5_TRACK_NO))
     {
         FatalError("%s(%d) ==> Stream5 enabled, but not configured to track "
-                   "TCP, UDP, ICMP, or IP.\n", file_name, file_line);
+                   "TCP, UDP, SCTP, ICMP, or IP.\n", file_name, file_line);
     }
 
     if (policy_id != getDefaultPolicy())
@@ -639,6 +657,8 @@ static void Stream5GlobalInit(struct _SnortConfig *sc, char *args)
             pDefaultPolicyConfig->global_config->max_tcp_sessions;
         pCurrentPolicyConfig->global_config->max_udp_sessions =
             pDefaultPolicyConfig->global_config->max_udp_sessions;
+        pCurrentPolicyConfig->global_config->max_sctp_sessions =
+            pDefaultPolicyConfig->global_config->max_sctp_sessions;
         pCurrentPolicyConfig->global_config->max_icmp_sessions =
             pDefaultPolicyConfig->global_config->max_icmp_sessions;
         pCurrentPolicyConfig->global_config->max_ip_sessions =
@@ -651,6 +671,10 @@ static void Stream5GlobalInit(struct _SnortConfig *sc, char *args)
             pDefaultPolicyConfig->global_config->udp_cache_pruning_timeout;
         pCurrentPolicyConfig->global_config->udp_cache_nominal_timeout =
             pDefaultPolicyConfig->global_config->udp_cache_nominal_timeout;
+        pCurrentPolicyConfig->global_config->sctp_cache_pruning_timeout =
+            pDefaultPolicyConfig->global_config->sctp_cache_pruning_timeout;
+        pCurrentPolicyConfig->global_config->sctp_cache_nominal_timeout =
+            pDefaultPolicyConfig->global_config->sctp_cache_nominal_timeout;
         pCurrentPolicyConfig->global_config->memcap =
             pDefaultPolicyConfig->global_config->memcap;
 #ifdef ENABLE_HA
@@ -669,7 +693,8 @@ static void Stream5GlobalInit(struct _SnortConfig *sc, char *args)
     {
         uint32_t max =
             pCurrentPolicyConfig->global_config->max_tcp_sessions +
-            pCurrentPolicyConfig->global_config->max_udp_sessions;
+            pCurrentPolicyConfig->global_config->max_udp_sessions +
+            pCurrentPolicyConfig->global_config->max_sctp_sessions;
 
         max >>= 9;
         if ( !max )
@@ -692,8 +717,9 @@ static void Stream5ParseGlobalArgs(Stream5GlobalConfig *config, char *args)
     char *endPtr = NULL;
 #define MAX_TCP 0x01
 #define MAX_UDP 0x02
-#define MAX_ICMP 0x04
-#define MAX_IP 0x08
+#define MAX_SCTP 0x04
+#define MAX_ICMP 0x08
+#define MAX_IP 0x10
     char max_set = 0;
 
     if (config == NULL)
@@ -918,6 +944,95 @@ static void Stream5ParseGlobalArgs(Stream5GlobalConfig *config, char *args)
                            file_name, file_line);
             }
         }
+        else if(!strcasecmp(stoks[0], "max_sctp"))
+        {
+            if (stoks[1])
+            {
+                config->max_sctp_sessions = strtoul(stoks[1], &endPtr, 10);
+                if (config->track_sctp_sessions == S5_TRACK_YES)
+                {
+                    if ((config->max_sctp_sessions > S5_RIDICULOUS_MAX_SESSIONS) ||
+                        (config->max_sctp_sessions == 0))
+                    {
+                        FatalError("%s(%d) => 'max_sctp %d' invalid: value must be "
+                                   "between 1 and %d sessions\n",
+                                   file_name, file_line,
+                                   config->max_sctp_sessions,
+                                   S5_RIDICULOUS_MAX_SESSIONS);
+                    }
+                }
+            }
+
+            if (!stoks[1] || (endPtr == &stoks[1][0]))
+            {
+                FatalError("%s(%d) => Invalid max_sctp in config file.  Requires integer parameter.\n",
+                           file_name, file_line);
+            }
+            max_set |= MAX_SCTP;
+        }
+        else if(!strcasecmp(stoks[0], "sctp_cache_pruning_timeout"))
+        {
+            if (stoks[1])
+            {
+                unsigned long timeout = strtoul(stoks[1], &endPtr, 10);
+
+                if (config->track_sctp_sessions == S5_TRACK_YES)
+                {
+                    if ( !timeout || (timeout > S5_MAX_CACHE_TIMEOUT) )
+                    {
+                        FatalError(
+                            "%s(%d) => '%s %lu' invalid: value must be between 1 and %d seconds\n",
+                            file_name, file_line, stoks[0], timeout, S5_MAX_CACHE_TIMEOUT);
+                    }
+                }
+                config->sctp_cache_pruning_timeout = (uint16_t)timeout;
+            }
+
+            if (!stoks[1] || (endPtr == &stoks[1][0]))
+            {
+                FatalError("%s(%d) => Invalid %s in config file.  Requires integer parameter.\n",
+                           file_name, file_line, stoks[0]);
+            }
+        }
+        else if(!strcasecmp(stoks[0], "sctp_cache_nominal_timeout"))
+        {
+            if (stoks[1])
+            {
+                unsigned long timeout = strtoul(stoks[1], &endPtr, 10);
+
+                if (config->track_sctp_sessions == S5_TRACK_YES)
+                {
+                    if ( !timeout || (timeout > S5_MAX_CACHE_TIMEOUT) )
+                    {
+                        FatalError(
+                            "%s(%d) => '%s %lu' invalid: value must be between 1 and %d seconds\n",
+                            file_name, file_line, stoks[0], timeout, S5_MAX_CACHE_TIMEOUT);
+                    }
+                }
+                config->sctp_cache_nominal_timeout = (uint16_t)timeout;
+            }
+
+            if (!stoks[1] || (endPtr == &stoks[1][0]))
+            {
+                FatalError("%s(%d) => Invalid %s in config file.  Requires integer parameter.\n",
+                           file_name, file_line, stoks[0]);
+            }
+        }
+        else if(!strcasecmp(stoks[0], "track_sctp"))
+        {
+            if (stoks[1])
+            {
+                if(!strcasecmp(stoks[1], "no"))
+                    config->track_sctp_sessions = S5_TRACK_NO;
+                else
+                    config->track_sctp_sessions = S5_TRACK_YES;
+            }
+            else
+            {
+                FatalError("%s(%d) => 'track_sctp' missing option\n",
+                           file_name, file_line);
+            }
+        }
         else if(!strcasecmp(stoks[0], "max_icmp"))
         {
             if (stoks[1])
@@ -1134,6 +1249,15 @@ static void Stream5PrintGlobalConfig(Stream5GlobalConfig *config)
         LogMessage("    UDP cache pruning timeout: %u seconds\n", config->udp_cache_pruning_timeout);
         LogMessage("    UDP cache nominal timeout: %u seconds\n", config->udp_cache_nominal_timeout);
     }
+    LogMessage("    Track SCTP sessions: %s\n",
+        config->track_sctp_sessions == S5_TRACK_YES ?
+        "ACTIVE" : "INACTIVE");
+    if (config->track_sctp_sessions == S5_TRACK_YES)
+    {
+        LogMessage("    Max SCTP sessions: %u\n", config->max_sctp_sessions);
+        LogMessage("    SCTP cache pruning timeout: %u seconds\n", config->sctp_cache_pruning_timeout);
+        LogMessage("    SCTP cache nominal timeout: %u seconds\n", config->sctp_cache_nominal_timeout);
+    }
     LogMessage("    Track ICMP sessions: %s\n",
         config->track_icmp_sessions == S5_TRACK_YES ?
         "ACTIVE" : "INACTIVE");
@@ -1239,6 +1363,39 @@ static void Stream5PolicyInitUdp(struct _SnortConfig *sc, char *args)
     Stream5UdpPolicyInit(config->udp_config, args);
 }
 
+static void Stream5PolicyInitSctp(struct _SnortConfig *sc, char *args)
+{
+    tSfPolicyId policy_id = getParserPolicy(sc);
+    Stream5Config *config;
+
+    if (s5_config == NULL)
+        FatalError("Tried to config stream5 SCTP policy without global config!\n");
+
+    sfPolicyUserPolicySet (s5_config, policy_id);
+    config = (Stream5Config *)sfPolicyUserDataGetCurrent(s5_config);
+
+    if ((config == NULL) || (config->global_config == NULL))
+    {
+        FatalError("Tried to config stream5 SCTP policy without global config!\n");
+    }
+
+    if (!config->global_config->track_sctp_sessions)
+    {
+        return;
+    }
+
+    if (config->sctp_config == NULL)
+    {
+        config->sctp_config =
+            (Stream5SctpConfig *)SnortAlloc(sizeof(Stream5SctpConfig));
+
+        Stream5InitSctp(config->global_config);
+    }
+
+    /* Call the protocol specific initializer */
+    Stream5SctpPolicyInit(config->sctp_config, args);
+}
+
 static void Stream5PolicyInitIcmp(struct _SnortConfig *sc, char *args)
 {
     tSfPolicyId policy_id = getParserPolicy(sc);
@@ -1312,6 +1469,7 @@ static void Stream5Reset(int signal, void *foo)
 
     Stream5ResetTcp();
     Stream5ResetUdp();
+    Stream5ResetSctp();
     Stream5ResetIcmp();
     Stream5ResetIp();
 
@@ -1323,6 +1481,7 @@ static void Stream5ResetStats(int signal, void *foo)
     memset(&s5stats, 0, sizeof(s5stats));
     Stream5ResetTcpPrunes();
     Stream5ResetUdpPrunes();
+    Stream5ResetSctpPrunes();
     Stream5ResetIcmpPrunes();
     Stream5ResetIpPrunes();
 #ifdef ENABLE_HA
@@ -1334,12 +1493,14 @@ static void Stream5CleanExit(int signal, void *foo)
 {
     s5stats.tcp_prunes = Stream5GetTcpPrunes();
     s5stats.udp_prunes = Stream5GetUdpPrunes();
+    s5stats.sctp_prunes = Stream5GetSctpPrunes();
     s5stats.icmp_prunes = Stream5GetIcmpPrunes();
     s5stats.ip_prunes = Stream5GetIpPrunes();
 
     /* Clean up the hash tables for these */
     Stream5CleanTcp();
     Stream5CleanUdp();
+    Stream5CleanSctp();
     Stream5CleanIcmp();
     Stream5CleanIp();
 #ifdef ENABLE_HA
@@ -1367,6 +1528,7 @@ static int Stream5VerifyConfigPolicy(
 
     int tcpNotConfigured = 0;
     int udpNotConfigured = 0;
+    int sctpNotConfigured = 0;
     int icmpNotConfigured = 0;
     int ipNotConfigured = 0;
 #ifdef ENABLE_HA
@@ -1425,6 +1587,26 @@ static int Stream5VerifyConfigPolicy(
         }
     }
 
+    if (pPolicyConfig->global_config->track_sctp_sessions)
+    {
+        sctpNotConfigured =
+            !pPolicyConfig->global_config->max_sctp_sessions ||
+            Stream5VerifySctpConfig(sc, pPolicyConfig->sctp_config, policyId);
+
+        if (sctpNotConfigured)
+        {
+            ErrorMessage(
+                "WARNING: Stream5 SCTP misconfigured (policy %u).\n", policyId);
+        }
+        else
+        {
+            if ( !(s_proto_flags & PROTO_BIT__SCTP) )
+                s_sctp_sessions += pPolicyConfig->global_config->max_sctp_sessions;
+
+            proto_flags |= PROTO_BIT__SCTP;
+        }
+    }
+
     if (pPolicyConfig->global_config->track_icmp_sessions)
     {
         icmpNotConfigured =
@@ -1476,7 +1658,7 @@ static int Stream5VerifyConfigPolicy(
     }
 #endif
 
-    if ( tcpNotConfigured || udpNotConfigured || icmpNotConfigured || ipNotConfigured
+    if ( tcpNotConfigured || udpNotConfigured || sctpNotConfigured || icmpNotConfigured || ipNotConfigured
 #ifdef ENABLE_HA
          || haNotConfigured
 #endif
@@ -1505,7 +1687,7 @@ static int Stream5VerifyConfig(struct _SnortConfig *sc)
     if (s5_config == NULL)
         return 0;
 
-    s_tcp_sessions = s_udp_sessions = 0;
+    s_tcp_sessions = s_udp_sessions = s_sctp_sessions = 0;
     s_icmp_sessions = s_ip_sessions = 0;
 
     if ((rval = sfPolicyUserDataIterate (sc, s5_config, Stream5VerifyConfigPolicy)))
@@ -1513,7 +1695,7 @@ static int Stream5VerifyConfig(struct _SnortConfig *sc)
 
     defConfig = sfPolicyUserDataGet(s5_config, getDefaultPolicy());
 
-    total_sessions = s_tcp_sessions + s_udp_sessions
+    total_sessions = s_tcp_sessions + s_udp_sessions + s_sctp_sessions
                    + s_icmp_sessions + s_ip_sessions;
 
     if ( !total_sessions )
@@ -1531,6 +1713,12 @@ static int Stream5VerifyConfig(struct _SnortConfig *sc)
         LogMessage("UDP tracking disabled, no UDP sessions allocated\n");
     }
 
+    if ( (defConfig->global_config->max_sctp_sessions > 0)
+        && (s_sctp_sessions == 0) )
+    {
+        LogMessage("SCTP tracking disabled, no SCTP sessions allocated\n");
+    }
+
     if ( (defConfig->global_config->max_icmp_sessions > 0)
         && (s_icmp_sessions == 0) )
     {
@@ -1576,15 +1764,18 @@ static void Stream5PrintStats(int exiting)
     LogMessage("            Total sessions: %u\n",
             s5stats.total_tcp_sessions +
             s5stats.total_udp_sessions +
+            s5stats.total_sctp_sessions +
             s5stats.total_icmp_sessions +
             s5stats.total_ip_sessions);
     LogMessage("              TCP sessions: %u\n", s5stats.total_tcp_sessions);
     LogMessage("              UDP sessions: %u\n", s5stats.total_udp_sessions);
+    LogMessage("             SCTP sessions: %u\n", s5stats.total_sctp_sessions);
     LogMessage("             ICMP sessions: %u\n", s5stats.total_icmp_sessions);
     LogMessage("               IP sessions: %u\n", s5stats.total_ip_sessions);
 
     LogMessage("                TCP Prunes: %u\n", Stream5GetTcpPrunes());
     LogMessage("                UDP Prunes: %u\n", Stream5GetUdpPrunes());
+    LogMessage("               SCTP Prunes: %u\n", Stream5GetSctpPrunes());
     LogMessage("               ICMP Prunes: %u\n", Stream5GetIcmpPrunes());
     LogMessage("                 IP Prunes: %u\n", Stream5GetIpPrunes());
     LogMessage("TCP StreamTrackers Created: %u\n",
@@ -1599,12 +1790,16 @@ static void Stream5PrintStats(int exiting)
     LogMessage("         TCP Segments Used: %u\n", s5stats.tcp_rebuilt_seqs_used);
     LogMessage("              TCP Discards: %u\n", s5stats.tcp_discards);
     LogMessage("                  TCP Gaps: %u\n", s5stats.tcp_gaps);
-    LogMessage("      UDP Sessions Created: %u\n",
-            s5stats.udp_sessions_created);
-    LogMessage("      UDP Sessions Deleted: %u\n",
-            s5stats.udp_sessions_released);
+    LogMessage("      UDP Sessions Created: %u\n", s5stats.udp_sessions_created);
+    LogMessage("      UDP Sessions Deleted: %u\n", s5stats.udp_sessions_released);
     LogMessage("              UDP Timeouts: %u\n", s5stats.udp_timeouts);
     LogMessage("              UDP Discards: %u\n", s5stats.udp_discards);
+    LogMessage("     SCTP Sessions Created: %u\n", s5stats.sctp_sessions_created);
+    LogMessage("     SCTP Sessions Deleted: %u\n", s5stats.sctp_sessions_released);
+    LogMessage("      SCTP Messages Queued: %u\n", s5stats.sctp_streammsgs_created);
+    LogMessage("    SCTP Messages Released: %u\n", s5stats.sctp_streammsgs_released);
+    LogMessage("             SCTP Timeouts: %u\n", s5stats.sctp_timeouts);
+    LogMessage("             SCTP Discards: %u\n", s5stats.sctp_discards);
     LogMessage("                    Events: %u\n", s5stats.events);
     LogMessage("           Internal Events: %u\n", s5stats.internalEvents);
     LogMessage("           TCP Port Filter\n");
@@ -1615,6 +1810,10 @@ static void Stream5PrintStats(int exiting)
     LogMessage("                  Filtered: %u\n", s5stats.udp_port_filter.filtered);
     LogMessage("                 Inspected: %u\n", s5stats.udp_port_filter.inspected);
     LogMessage("                   Tracked: %u\n", s5stats.udp_port_filter.session_tracked);
+    LogMessage("          SCTP Port Filter\n");
+    LogMessage("                  Filtered: %u\n", s5stats.sctp_port_filter.filtered);
+    LogMessage("                 Inspected: %u\n", s5stats.sctp_port_filter.inspected);
+    LogMessage("                   Tracked: %u\n", s5stats.sctp_port_filter.session_tracked);
 #ifdef ENABLE_HA
     Stream5PrintHAStats();
 #endif
@@ -1703,7 +1902,7 @@ void Stream5Process(Packet *p, void *context)
 
     PREPROC_PROFILE_START(s5PerfStats);
 
-    /* Call individual TCP/UDP/ICMP/IP processing, per GET_IPH_PROTO(p) */
+    /* Call individual TCP/UDP/SCTP/ICMP/IP processing, per GET_IPH_PROTO(p) */
     switch(GET_IPH_PROTO(p))
     {
         case IPPROTO_TCP:
@@ -1746,6 +1945,26 @@ void Stream5Process(Packet *p, void *context)
             }
             break;
 
+        case IPPROTO_SCTP:
+            {
+                Stream5SctpPolicy *policy = NULL;
+
+                lwssn = GetLWSession(sctp_lws_cache, p, &key);
+                if (lwssn != NULL)
+                {
+                    policy = (Stream5SctpPolicy *)lwssn->policy;
+#ifdef ENABLE_HA
+                    old_ha_state = lwssn->ha_state;
+#endif
+                }
+
+                if (Stream5SetRuntimeConfiguration(lwssn, IPPROTO_SCTP) == -1)
+                    return;
+
+                Stream5ProcessSctp(p, lwssn, policy, &key);
+            }
+            break;
+
         case IPPROTO_ICMP:
             if (Stream5SetRuntimeConfiguration(NULL, IPPROTO_ICMP) != -1)
             {
@@ -1834,6 +2053,15 @@ static inline int IsEligible(Packet *p)
                  return 0;
         }
         break;
+        case IPPROTO_SCTP:
+        {
+             if(p->sctph == NULL)
+                 return 0;
+
+             if (p->error_flags & PKT_ERR_CKSUM_SCTP)
+                 return 0;
+        }
+        break;
         case IPPROTO_ICMP:
         case IPPROTO_ICMPV6:
         {
@@ -1947,6 +2175,9 @@ static inline void * Stream5GetSessionPtr(const SessionKey *key)
         case IPPROTO_UDP:
             ssn = GetLWSessionFromKey(udp_lws_cache, key);
             break;
+        case IPPROTO_SCTP:
+            ssn = GetLWSessionFromKey(sctp_lws_cache, key);
+            break;
         case IPPROTO_ICMP:
             ssn = GetLWSessionFromKey(icmp_lws_cache, key);
             if (ssn) break;
@@ -1964,7 +2195,7 @@ static void * Stream5GetSessionPtrFromIpPort(
                     uint16_t srcPort,
                     snort_ip_p dstIP,
                     uint16_t dstPort,
-                    char ip_protocol,
+                    uint8_t ip_protocol,
                     uint16_t vlan,
                     uint32_t mplsId,
                     uint16_t addressSpaceId)
@@ -2025,7 +2256,7 @@ static void * Stream5GetApplicationDataFromIpPort(
                     uint16_t srcPort,
                     snort_ip_p dstIP,
                     uint16_t dstPort,
-                    char ip_protocol,
+                    uint8_t ip_protocol,
                     uint16_t vlan,
                     uint32_t mplsId,
                     uint16_t addressSpaceID,
@@ -2060,6 +2291,10 @@ static void Stream5CheckSessionClosed(Packet* p)
             DeleteLWSession(udp_lws_cache, ssn, "closed normally");
             p->ssnptr = NULL;
             break;
+        case IPPROTO_SCTP:
+            DeleteLWSession(sctp_lws_cache, ssn, "closed normally");
+            p->ssnptr = NULL;
+            break;
         case IPPROTO_IP:
             DeleteLWSession(ip_lws_cache, ssn, "closed normally");
             p->ssnptr = NULL;
@@ -2096,7 +2331,9 @@ static int Stream5AlertFlushStream(Packet *p)
         return 0;
     }
 
-    if ((ssn->protocol != IPPROTO_TCP) ||
+
+    if (((ssn->protocol != IPPROTO_TCP) ||
+         (ssn->protocol != IPPROTO_SCTP)) ||
         (p->packet_flags & PKT_REBUILT_STREAM))
     {
         DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
@@ -2126,7 +2363,9 @@ static int Stream5ResponseFlushStream(Packet *p)
     if (Stream5SetRuntimeConfiguration(ssn, ssn->protocol) == -1)
         return 0;
 
-    if ((ssn->protocol != IPPROTO_TCP) ||
+
+    if (((ssn->protocol != IPPROTO_TCP) ||
+         (ssn->protocol != IPPROTO_SCTP)) ||
         (p->packet_flags & PKT_REBUILT_STREAM))
     {
         DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
@@ -2196,6 +2435,9 @@ static int Stream5AddSessionAlert(
 
     /* Don't need to do this for other protos because they don't
        do any reassembly. */
+    /* XXX: SCTP supports reassembly of DATA chunks, but Stream5 SCTP
+            is not complete yet.  For now, it mimics Stream5 UDP
+            functionality. */
     if ( GET_IPH_PROTO(p) != IPPROTO_TCP )
         return 0;
 
@@ -2220,6 +2462,9 @@ static int Stream5CheckSessionAlert(
 
     /* Don't need to do this for other protos because they don't
        do any reassembly. */
+    /* XXX: SCTP supports reassembly of DATA chunks, but Stream5 SCTP
+            is not complete yet.  For now, it mimics Stream5 UDP
+            functionality. */
     if ( GET_IPH_PROTO(p) != IPPROTO_TCP )
         return 0;
 
@@ -2245,6 +2490,9 @@ static int Stream5UpdateSessionAlert(
 
     /* Don't need to do this for other protos because they don't
        do any reassembly. */
+    /* XXX: SCTP supports reassembly of DATA chunks, but Stream5 SCTP
+            is not complete yet.  For now, it mimics Stream5 UDP
+            functionality. */
     if ( GET_IPH_PROTO(p) != IPPROTO_TCP )
         return 0;
 
@@ -2365,7 +2613,8 @@ static void Stream5StopInspection(
         return;
 
     /* Flush any queued data on the client and/or server */
-    if (ssn->protocol == IPPROTO_TCP)
+    if ((ssn->protocol == IPPROTO_TCP) ||
+        (ssn->protocol == IPPROTO_SCTP))
     {
         if (ssn->ha_state.ignore_direction & SSN_DIR_FROM_CLIENT)
         {
@@ -2431,6 +2680,9 @@ static void Stream5UpdateDirection(
         case IPPROTO_UDP:
             UdpUpdateDirection(ssn, dir, ip, port);
             break;
+        case IPPROTO_SCTP:
+            SctpUpdateDirection(ssn, dir, ip, port);
+            break;
         case IPPROTO_ICMP:
             //IcmpUpdateDirection(ssn, dir, ip, port);
             break;
@@ -2499,6 +2751,9 @@ static void Stream5DropPacket(Packet *p)
         case IPPROTO_UDP:
             UdpSessionCleanup(ssn);
             break;
+        case IPPROTO_SCTP:
+            SctpSessionCleanup(ssn);
+            break;
         case IPPROTO_IP:
             IpSessionCleanup(ssn);
             break;
@@ -2521,7 +2776,7 @@ static int Stream5GetRebuiltPackets(
 {
     Stream5LWSession *ssn = (Stream5LWSession*)p->ssnptr;
 
-    if (!ssn || ssn->protocol != IPPROTO_TCP)
+    if (!ssn)
         return 0;
 
     /* Only if this is a rebuilt packet */
@@ -2531,7 +2786,22 @@ static int Stream5GetRebuiltPackets(
     if (Stream5SetRuntimeConfiguration(ssn, ssn->protocol) == -1)
         return 0;
 
-    return GetTcpRebuiltPackets(p, ssn, callback, userdata);
+    switch (ssn->protocol)
+    {
+        case IPPROTO_TCP:
+            return GetTcpRebuiltPackets(p, ssn, callback, userdata);
+            break;
+
+        /* Not functional yet.
+         * case IPPROTO_SCTP:
+         *   return GetSctpRebuiltPackets(p, ssn, callback, userdata);
+         *   break;
+         */   
+
+        default:
+            return 0;
+            break;
+    }
 }
 
 static int Stream5GetStreamSegments(
@@ -2541,7 +2811,7 @@ static int Stream5GetStreamSegments(
 {
     Stream5LWSession *ssn = (Stream5LWSession*)p->ssnptr;
 
-    if ((ssn == NULL) || (ssn->protocol != IPPROTO_TCP))
+    if (!ssn)
         return -1;
 
     /* Only if this is a rebuilt packet */
@@ -2551,7 +2821,22 @@ static int Stream5GetStreamSegments(
     if (Stream5SetRuntimeConfiguration(ssn, ssn->protocol) == -1)
         return -1;
 
-    return GetTcpStreamSegments(p, ssn, callback, userdata);
+    switch (ssn->protocol)
+    {
+        case IPPROTO_TCP:
+            return GetTcpStreamSegments(p, ssn, callback, userdata);
+            break;
+
+        /* Not functional yet
+         * case IPPROTO_SCTP:
+         *   return GetSctpStreamSegments(p, ssn, callback, userdata);
+         *   break;
+         */
+        
+        default:
+            return -1;
+            break;
+    }
 }
 
 static StreamFlowData *Stream5GetFlowData(Packet *p)
@@ -2568,39 +2853,83 @@ static char Stream5GetReassemblyDirection(void *ssnptr)
 {
     Stream5LWSession *ssn = (Stream5LWSession *)ssnptr;
 
-    if (!ssn || ssn->protocol != IPPROTO_TCP)
+    if (!ssn)
         return SSN_DIR_NONE;
 
     if (Stream5SetRuntimeConfiguration(ssn, ssn->protocol) == -1)
         return SSN_DIR_NONE;
 
-    return Stream5GetReassemblyDirectionTcp(ssn);
+    switch (ssn->protocol)
+    {
+        case IPPROTO_TCP:
+            return Stream5GetReassemblyDirectionTcp(ssn);
+            break;
+
+        /* Not functional yet.   
+         * case IPPROTO_SCTP:
+         *   return Stream5GetReassemblyDirectionSctp(ssn);
+         *   break;
+         */   
+
+        default:
+            return SSN_DIR_NONE;
+            break;
+    }
 }
 
 static uint32_t Stream5GetFlushPoint(void *ssnptr, char dir)
 {
     Stream5LWSession *ssn = (Stream5LWSession *)ssnptr;
 
-    if ((ssn == NULL) || (ssn->protocol != IPPROTO_TCP))
+    if (!ssn)
         return 0;
 
     if (Stream5SetRuntimeConfiguration(ssn, ssn->protocol) == -1)
         return 0;
 
-    return Stream5GetFlushPointTcp(ssn, dir);
+    switch (ssn->protocol)
+    {
+        case IPPROTO_TCP:
+            return Stream5GetFlushPointTcp(ssn, dir);
+            break;
+
+        /* Not functional yet.   
+         * case IPPROTO_SCTP:
+         *   return Stream5GetFlushPointSctp(ssn, dir);
+         *   break;
+         */   
+
+        default:
+            return 0;
+            break;
+    }
 }
 
 static void Stream5SetFlushPoint(void *ssnptr, char dir, uint32_t flush_point)
 {
     Stream5LWSession *ssn = (Stream5LWSession *)ssnptr;
 
-    if ((ssn == NULL) || (ssn->protocol != IPPROTO_TCP))
+    if (!ssn)
         return;
 
     if (Stream5SetRuntimeConfiguration(ssn, ssn->protocol) == -1)
         return;
 
-    Stream5SetFlushPointTcp(ssn, dir, flush_point);
+    switch (ssn->protocol)
+    {
+        case IPPROTO_TCP:
+            Stream5SetFlushPointTcp(ssn, dir, flush_point);
+            break;
+
+        /* Not functional yet.   
+         * case IPPROTO_SCTP:
+         *   Stream5SetFlushPointSctp(ssn, dir, flush_point);
+         *   break;
+         */   
+
+        default:
+            break;
+    }
 }
 
 static char Stream5SetReassembly(void *ssnptr,
@@ -2610,65 +2939,140 @@ static char Stream5SetReassembly(void *ssnptr,
 {
     Stream5LWSession *ssn = (Stream5LWSession *)ssnptr;
 
-    if (!ssn || ssn->protocol != IPPROTO_TCP)
+    if (!ssn)
         return 0;
 
     if (Stream5SetRuntimeConfiguration(ssn, ssn->protocol) == -1)
         return 0;
+    
+    switch (ssn->protocol)
+    {
+        case IPPROTO_TCP:
+            return Stream5SetReassemblyTcp(ssn, flush_policy, dir, flags);
+            break;
+
+        /* Not functional yet.   
+         * case IPPROTO_SCTP:
+         *   return Stream5SetReassemblySctp(ssn, flush_policy, dir, flags);
+         *   break;
+         */   
 
-    return Stream5SetReassemblyTcp(ssn, flush_policy, dir, flags);
+        default:
+            return 0;
+            break;
+    }
 }
 
 static char Stream5GetReassemblyFlushPolicy(void *ssnptr, char dir)
 {
     Stream5LWSession *ssn = (Stream5LWSession *)ssnptr;
 
-    if (!ssn || ssn->protocol != IPPROTO_TCP)
+    if (!ssn)
         return STREAM_FLPOLICY_NONE;
 
     if (Stream5SetRuntimeConfiguration(ssn, ssn->protocol) == -1)
         return STREAM_FLPOLICY_NONE;
 
-    return Stream5GetReassemblyFlushPolicyTcp(ssn, dir);
+    switch (ssn->protocol)
+    {
+        case IPPROTO_TCP:
+            return Stream5GetReassemblyFlushPolicyTcp(ssn, dir);
+            break;
+
+        /* Not functional yet.   
+         * case IPPROTO_SCTP:
+         *   return Stream5GetReassemblyFlushPolicySctp(ssn, dir);
+         *   break;
+         */   
+
+        default:
+            return STREAM_FLPOLICY_NONE;
+            break;
+    }
 }
 
 static char Stream5IsStreamSequenced(void *ssnptr, char dir)
 {
     Stream5LWSession *ssn = (Stream5LWSession *)ssnptr;
 
-    if (!ssn || ssn->protocol != IPPROTO_TCP)
+    if (!ssn)
         return 1;
 
     if (Stream5SetRuntimeConfiguration(ssn, ssn->protocol) == -1)
         return 1;
 
-    return Stream5IsStreamSequencedTcp(ssn, dir);
+    switch (ssn->protocol)
+    {
+        case IPPROTO_TCP:
+            return Stream5IsStreamSequencedTcp(ssn, dir);
+            break;
+
+        /* Not functional yet.   
+         * case IPPROTO_SCTP:
+         *   return Stream5IsStreamSequencedSctp(ssn, dir);
+         *   break;
+         */   
+
+        default:
+            return 1;
+            break;
+    }
 }
 
 static int Stream5MissingInReassembled(void *ssnptr, char dir)
 {
     Stream5LWSession *ssn = (Stream5LWSession *)ssnptr;
 
-    if (!ssn || ssn->protocol != IPPROTO_TCP)
+    if (!ssn)
         return SSN_MISSING_NONE;
 
     if (Stream5SetRuntimeConfiguration(ssn, ssn->protocol) == -1)
         return SSN_MISSING_NONE;
 
-    return Stream5MissingInReassembledTcp(ssn, dir);
+    switch (ssn->protocol)
+    {
+        case IPPROTO_TCP:
+            return Stream5MissingInReassembledTcp(ssn, dir);
+            break;
+
+        /* Not functional yet.   
+         * case IPPROTO_SCTP:
+         *   return Stream5MissingInReassembledSctp(ssn, dir);
+         *   break;
+         */   
+
+        default:
+            return SSN_MISSING_NONE;
+            break;
+    }
 }
 
 static char Stream5PacketsMissing(void *ssnptr, char dir)
 {
     Stream5LWSession *ssn = (Stream5LWSession *)ssnptr;
 
-    if (!ssn || ssn->protocol != IPPROTO_TCP)
+    if (!ssn)
         return 1;
 
     if (Stream5SetRuntimeConfiguration(ssn, ssn->protocol) == -1)
         return 1;
 
-    return Stream5PacketsMissingTcp(ssn, dir);
+    switch (ssn->protocol)
+    {
+        case IPPROTO_TCP:
+            return Stream5PacketsMissingTcp(ssn, dir);
+            break;
+
+        /* Not functional yet.   
+         * case IPPROTO_SCTP:
+         *   return Stream5PacketsMissingSctp(ssn, dir);
+         *   break;
+         */   
+
+        default:
+            return 1;
+            break;
+    }
 }
 
 static uint16_t s5GetPreprocessorStatusBit(void)
@@ -2682,7 +3086,7 @@ static uint16_t s5GetPreprocessorStatusBit(void)
 
 static void s5SetPortFilterStatus(
         struct _SnortConfig *sc,
-        int protocol,
+        uint8_t protocol,
         uint16_t port,
         uint16_t status,
         tSfPolicyId policyId,
@@ -2694,11 +3098,18 @@ static void s5SetPortFilterStatus(
         case IPPROTO_TCP:
             s5TcpSetPortFilterStatus(sc, port, status, policyId, parsing);
             break;
+
         case IPPROTO_UDP:
             s5UdpSetPortFilterStatus(sc, port, status, policyId, parsing);
             break;
+
+        case IPPROTO_SCTP:
+            s5SctpSetPortFilterStatus(sc, port, status, policyId, parsing);
+            break;
+
         case IPPROTO_ICMP:
             break;
+
         default:
             break;
     }
@@ -2706,7 +3117,7 @@ static void s5SetPortFilterStatus(
 
 static void s5UnsetPortFilterStatus(
         struct _SnortConfig *sc,
-        int protocol,
+        uint8_t protocol,
         uint16_t port,
         uint16_t status,
         tSfPolicyId policyId,
@@ -2724,6 +3135,9 @@ static void s5UnsetPortFilterStatus(
         case IPPROTO_UDP:
             s5UdpUnsetPortFilterStatus(sc, port, status, policyId, parsing);
             break;
+        case IPPROTO_SCTP:
+            s5SctpUnsetPortFilterStatus(sc, port, status, policyId, parsing);
+            break;
         case IPPROTO_ICMP:
             break;
         default:
@@ -2795,6 +3209,12 @@ static void Stream5SetIPProtocol(Stream5LWSession *lwssn)
         lwssn->ha_flags |= HA_FLAG_MODIFIED;
 #endif
         break;
+    case IPPROTO_SCTP:
+        lwssn->ha_state.ipprotocol = protocolReferenceSCTP;
+#ifdef ENABLE_HA
+        lwssn->ha_flags |= HA_FLAG_MODIFIED;
+#endif
+        break;
     case IPPROTO_ICMP:
         lwssn->ha_state.ipprotocol = protocolReferenceICMP;
 #ifdef ENABLE_HA
@@ -2873,7 +3293,8 @@ static void s5InitServiceFilterStatus(struct _SnortConfig *sc)
         {
             RuleTreeNode *rtn = getRtnFromOtn(otn, policyId);
 
-            if (rtn && (rtn->proto == IPPROTO_TCP))
+            if (rtn && ((rtn->proto == IPPROTO_TCP) ||
+                        (rtn->proto == IPPROTO_SCTP)))
             {
                 unsigned int svc_idx;
 
@@ -3094,6 +3515,18 @@ int isPacketFilterDiscard(
 
             pPortFilterStats = &s5stats.udp_port_filter;
             break;
+
+        case IPPROTO_SCTP:
+            if ((s5_global_eval_config != NULL) &&
+                s5_global_eval_config->track_sctp_sessions)
+            {
+                action = s5SctpGetPortFilterStatus(NULL, p->sp, policy_id, 0) |
+                    s5SctpGetPortFilterStatus(NULL, p->dp, policy_id, 0);
+            }
+
+            pPortFilterStats = &s5stats.sctp_port_filter;
+            break;
+
         default:
             return PORT_MONITOR_PACKET_PROCESS;
     }
@@ -3202,6 +3635,7 @@ static void s5GetMaxSessions(struct _SnortConfig *sc, tSfPolicyId policyId, Stre
     {
         limits->tcp_session_limit = config->global_config->track_tcp_sessions ? config->global_config->max_tcp_sessions : 0;
         limits->udp_session_limit = config->global_config->track_udp_sessions ? config->global_config->max_udp_sessions : 0;
+        limits->sctp_session_limit = config->global_config->track_sctp_sessions ? config->global_config->max_sctp_sessions : 0;
         limits->icmp_session_limit = config->global_config->track_icmp_sessions ? config->global_config->max_icmp_sessions : 0;
         limits->ip_session_limit = config->global_config->track_ip_sessions ? config->global_config->max_ip_sessions : 0;
     }
@@ -3357,6 +3791,10 @@ static void Stream5GlobalReload(struct _SnortConfig *sc, char *args, void **new_
     pCurrentPolicyConfig->global_config->max_udp_sessions = S5_DEFAULT_MAX_UDP_SESSIONS;
     pCurrentPolicyConfig->global_config->udp_cache_pruning_timeout = S5_DEFAULT_UDP_CACHE_PRUNING_TIMEOUT;
     pCurrentPolicyConfig->global_config->udp_cache_nominal_timeout = S5_DEFAULT_UDP_CACHE_NOMINAL_TIMEOUT;
+    pCurrentPolicyConfig->global_config->track_sctp_sessions = S5_TRACK_YES;
+    pCurrentPolicyConfig->global_config->max_sctp_sessions = S5_DEFAULT_MAX_SCTP_SESSIONS;
+    pCurrentPolicyConfig->global_config->sctp_cache_pruning_timeout = S5_DEFAULT_SCTP_CACHE_PRUNING_TIMEOUT;
+    pCurrentPolicyConfig->global_config->sctp_cache_nominal_timeout = S5_DEFAULT_SCTP_CACHE_NOMINAL_TIMEOUT;
     pCurrentPolicyConfig->global_config->track_icmp_sessions = S5_TRACK_NO;
     pCurrentPolicyConfig->global_config->max_icmp_sessions = S5_DEFAULT_MAX_ICMP_SESSIONS;
     pCurrentPolicyConfig->global_config->track_ip_sessions = S5_TRACK_NO;
@@ -3375,11 +3813,12 @@ static void Stream5GlobalReload(struct _SnortConfig *sc, char *args, void **new_
     if ((!pCurrentPolicyConfig->global_config->disabled) &&
         (pCurrentPolicyConfig->global_config->track_tcp_sessions == S5_TRACK_NO) &&
         (pCurrentPolicyConfig->global_config->track_udp_sessions == S5_TRACK_NO) &&
+        (pCurrentPolicyConfig->global_config->track_sctp_sessions == S5_TRACK_NO) &&
         (pCurrentPolicyConfig->global_config->track_icmp_sessions == S5_TRACK_NO) &&
         (pCurrentPolicyConfig->global_config->track_ip_sessions == S5_TRACK_NO))
     {
         FatalError("%s(%d) ==> Stream5 enabled, but not configured to track "
-                   "TCP, UDP, ICMP, or IP.\n", file_name, file_line);
+                   "TCP, UDP, SCTP, ICMP, or IP.\n", file_name, file_line);
     }
 
     if (policy_id != getDefaultPolicy())
@@ -3388,6 +3827,8 @@ static void Stream5GlobalReload(struct _SnortConfig *sc, char *args, void **new_
             pDefaultPolicyConfig->global_config->max_tcp_sessions;
         pCurrentPolicyConfig->global_config->max_udp_sessions =
             pDefaultPolicyConfig->global_config->max_udp_sessions;
+        pCurrentPolicyConfig->global_config->max_sctp_sessions =
+            pDefaultPolicyConfig->global_config->max_sctp_sessions;
         pCurrentPolicyConfig->global_config->max_icmp_sessions =
             pDefaultPolicyConfig->global_config->max_icmp_sessions;
         pCurrentPolicyConfig->global_config->max_ip_sessions =
@@ -3400,6 +3841,10 @@ static void Stream5GlobalReload(struct _SnortConfig *sc, char *args, void **new_
             pDefaultPolicyConfig->global_config->udp_cache_pruning_timeout;
         pCurrentPolicyConfig->global_config->udp_cache_nominal_timeout =
             pDefaultPolicyConfig->global_config->udp_cache_nominal_timeout;
+        pCurrentPolicyConfig->global_config->sctp_cache_pruning_timeout =
+            pDefaultPolicyConfig->global_config->sctp_cache_pruning_timeout;
+        pCurrentPolicyConfig->global_config->sctp_cache_nominal_timeout =
+            pDefaultPolicyConfig->global_config->sctp_cache_nominal_timeout;
         pCurrentPolicyConfig->global_config->memcap =
             pDefaultPolicyConfig->global_config->memcap;
     }
@@ -3472,6 +3917,32 @@ static void Stream5UdpReload(struct _SnortConfig *sc, char *args, void **new_con
     *new_config = NULL;
 }
 
+static void Stream5SctpReload(struct _SnortConfig *sc, char *args, void **new_config)
+{
+    tSfPolicyUserContextId s5_swap_config;
+    tSfPolicyId policy_id = getParserPolicy(sc);
+    Stream5Config *config;
+
+    s5_swap_config = (tSfPolicyUserContextId)GetReloadStreamConfig(sc);
+    if (s5_swap_config == NULL)
+        FatalError("Tried to config stream5 SCTP policy without global config!\n");
+
+    config = (Stream5Config *)sfPolicyUserDataGet(s5_swap_config, policy_id);
+
+    if ((config == NULL) || (config->global_config == NULL))
+    {
+        FatalError("Tried to config stream5 SCTP policy without global config!\n");
+    }
+
+    if (config->sctp_config == NULL)
+        config->sctp_config = (Stream5SctpConfig *)SnortAlloc(sizeof(Stream5SctpConfig));
+
+    /* Call the protocol specific initializer */
+    Stream5SctpPolicyInit(config->sctp_config, args);
+
+    *new_config = NULL;
+}
+
 static void Stream5IcmpReload(struct _SnortConfig *sc, char *args, void **new_config)
 {
     tSfPolicyUserContextId s5_swap_config;
@@ -3580,6 +4051,7 @@ static int Stream5ReloadVerifyPolicy(
     Stream5Config *sc = (Stream5Config *)sfPolicyUserDataGet(s5_swap_config, policyId);
     int tcpNotConfigured = 0;
     int udpNotConfigured = 0;
+    int sctpNotConfigured = 0;
     int icmpNotConfigured = 0;
     int ipNotConfigured = 0;
     int proto_flags = 0;
@@ -3597,10 +4069,11 @@ static int Stream5ReloadVerifyPolicy(
 
         if ((sc->global_config->track_tcp_sessions != cc->global_config->track_tcp_sessions) ||
                 (sc->global_config->track_udp_sessions != cc->global_config->track_udp_sessions) ||
+                (sc->global_config->track_sctp_sessions != cc->global_config->track_sctp_sessions) ||
                 (sc->global_config->track_icmp_sessions != cc->global_config->track_icmp_sessions) ||
                 (sc->global_config->track_ip_sessions != cc->global_config->track_ip_sessions))
         {
-            ErrorMessage("Stream5 Reload: Changing tracking of TCP, UDP ICMP, or IP "
+            ErrorMessage("Stream5 Reload: Changing tracking of TCP, UDP, SCTP, ICMP, or IP "
                     "sessions requires a restart.\n");
             return -1;
         }
@@ -3655,6 +4128,30 @@ static int Stream5ReloadVerifyPolicy(
             return -1;
         }
 
+        if (sc->global_config->max_sctp_sessions != cc->global_config->max_sctp_sessions)
+        {
+            ErrorMessage("Stream5 Reload: Changing \"max_sctp\" requires a restart.\n");
+            Stream5FreeConfigs(s5_swap_config);
+            s5_swap_config = NULL;
+            return -1;
+        }
+
+        if (sc->global_config->sctp_cache_pruning_timeout != cc->global_config->sctp_cache_pruning_timeout)
+        {
+            ErrorMessage("Stream5 Reload: Changing \"sctp_cache_pruning_timeout\" requires a restart.\n");
+            Stream5FreeConfigs(s5_swap_config);
+            s5_swap_config = NULL;
+            return -1;
+        }
+
+        if (sc->global_config->sctp_cache_nominal_timeout != cc->global_config->sctp_cache_nominal_timeout)
+        {
+            ErrorMessage("Stream5 Reload: Changing \"sctp_cache_nominal_timeout\" requires a restart.\n");
+            Stream5FreeConfigs(s5_swap_config);
+            s5_swap_config = NULL;
+            return -1;
+        }
+
         if (cc->global_config->max_icmp_sessions != sc->global_config->max_icmp_sessions)
         {
             ErrorMessage("Stream5 Reload: Changing \"max_icmp\" requires a restart.\n");
@@ -3717,6 +4214,25 @@ static int Stream5ReloadVerifyPolicy(
         }
     }
 
+    if (sc->global_config->track_sctp_sessions)
+    {
+        sctpNotConfigured =
+            !sc->global_config->max_sctp_sessions ||
+            Stream5VerifySctpConfig(snortConf, sc->sctp_config, policyId);
+
+        if (sctpNotConfigured)
+        {
+            ErrorMessage("WARNING: Stream5 SCTP misconfigured.\n");
+        }
+        else
+        {
+            if ( !(s_proto_flags & PROTO_BIT__SCTP) )
+                s_sctp_sessions += sc->global_config->max_sctp_sessions;
+
+            proto_flags |= PROTO_BIT__SCTP;
+        }
+    }
+
     if (sc->global_config->track_icmp_sessions)
     {
         icmpNotConfigured =
diff --git a/src/preprocessors/stream_api.h b/src/preprocessors/stream_api.h
index 5ed4170..cfb5882 100644
--- a/src/preprocessors/stream_api.h
+++ b/src/preprocessors/stream_api.h
@@ -24,7 +24,7 @@
 /* stream_api.h
  *
  * Purpose: Definition of the StreamAPI.  To be used as a common interface
- *          for TCP (and later UDP & ICMP) Stream access for other
+ *          for TCP (and later UDP, SCTP, & ICMP) Stream access for other
  *          preprocessors and detection plugins.
  *
  * Arguments:
@@ -207,6 +207,7 @@ typedef struct _StreamSessionLimits
 {
     uint32_t tcp_session_limit;
     uint32_t udp_session_limit;
+    uint32_t sctp_session_limit;
     uint32_t icmp_session_limit;
     uint32_t ip_session_limit;
 } StreamSessionLimits;
@@ -579,7 +580,7 @@ typedef struct _stream_api
      *  sure to set the parsing argument to 1.
      */
     void (*set_port_filter_status)(struct _SnortConfig *sc,
-                                   int protocol, uint16_t port, uint16_t status, tSfPolicyId policyId, int parsing);
+                                   uint8_t protocol, uint16_t port, uint16_t status, tSfPolicyId policyId, int parsing);
 
     /** Unset port to maintain session state. This function can only
      *  be used with independent bits acquired from
@@ -588,7 +589,7 @@ typedef struct _stream_api
      *  parsing argument to 1.
      */
     void (*unset_port_filter_status)(struct _SnortConfig *sc,
-                                     int protocol, uint16_t port, uint16_t status, tSfPolicyId policyId, int parsing);
+                                     uint8_t protocol, uint16_t port, uint16_t status, tSfPolicyId policyId, int parsing);
 
 #ifdef ACTIVE_RESPONSE
     /* initialize response count and expiration time */
@@ -728,9 +729,9 @@ typedef struct _stream_api
      *
      * Parameters
      *     IP addr #1
-     *     Port #1 (0 for non TCP/UDP)
+     *     Port #1 (0 for non TCP/UDP/SCTP)
      *     IP addr #2
-     *     Port #2 (0 for non TCP/UDP)
+     *     Port #2 (0 for non TCP/UDP/SCTP)
      *     Protocol
      *     VLAN ID
      *     MPLS ID
@@ -740,7 +741,7 @@ typedef struct _stream_api
      * Returns
      *     Application Data reference (pointer)
      */
-    void *(*get_application_data_from_ip_port)(snort_ip_p, uint16_t, snort_ip_p, uint16_t, char, uint16_t, uint32_t, uint16_t, uint32_t);
+    void *(*get_application_data_from_ip_port)(snort_ip_p, uint16_t, snort_ip_p, uint16_t, uint8_t, uint16_t, uint32_t, uint16_t, uint32_t);
 
     /*Register callbacks for extra data logging */
     uint32_t (*reg_xtra_data_cb)(LogFunction );
@@ -767,9 +768,9 @@ typedef struct _stream_api
      *
      * Parameters
      *     IP addr #1
-     *     Port #1 (0 for non TCP/UDP)
+     *     Port #1 (0 for non TCP/UDP/SCTP)
      *     IP addr #2
-     *     Port #2 (0 for non TCP/UDP)
+     *     Port #2 (0 for non TCP/UDP/SCTP)
      *     Protocol
      *     VLAN ID
      *     MPLS ID
@@ -778,7 +779,7 @@ typedef struct _stream_api
      * Returns
      *     Stream session pointer
      */
-    void *(*get_session_ptr_from_ip_port)(snort_ip_p, uint16_t, snort_ip_p, uint16_t, char, uint16_t, uint32_t, uint16_t);
+    void *(*get_session_ptr_from_ip_port)(snort_ip_p, uint16_t, snort_ip_p, uint16_t, uint8_t, uint16_t, uint32_t, uint16_t);
 
     /** Retrieve the session key given a stream session pointer.
      *
diff --git a/src/preprocessors/stream_expect.c b/src/preprocessors/stream_expect.c
index deb72b2..9a00940 100644
--- a/src/preprocessors/stream_expect.c
+++ b/src/preprocessors/stream_expect.c
@@ -167,7 +167,7 @@ static snort_ip zeroed;
  * @param cliPort - client port number
  * @param srvIP - server IP address. All preprocessors must have consisten view of server side of a session.
  * @param srcPort - server port number
- * @param protocol - IPPROTO_TCP or IPPROTO_UDP.
+ * @param protocol - IPPROTO_TCP, IPPROTO_UDP, or IPPROTO_SCTP.
  * @param direction - direction of session. Assumed that direction value for session being expected or expected will
  * remain same across different calls to this function.
  * @param expiry - session expiry in seconds.
diff --git a/src/profiler.c b/src/profiler.c
index 1abaeb2..182d4c0 100644
--- a/src/profiler.c
+++ b/src/profiler.c
@@ -123,7 +123,8 @@ void ResetRuleProfiling(void)
                 continue;
 
             if ((rtn->proto == IPPROTO_TCP) || (rtn->proto == IPPROTO_UDP)
-                    || (rtn->proto == IPPROTO_ICMP) || (rtn->proto == ETHERNET_TYPE_IP))
+                    || (rtn->proto == IPPROTO_SCTP) || (rtn->proto == IPPROTO_ICMP)
+                    || (rtn->proto == ETHERNET_TYPE_IP))
             {
                 //do operation
                 otn->ticks = 0;
diff --git a/src/rule_option_types.h b/src/rule_option_types.h
index 9c37c26..bc0b706 100644
--- a/src/rule_option_types.h
+++ b/src/rule_option_types.h
@@ -69,6 +69,11 @@ typedef enum _option_type_t
     RULE_OPTION_TYPE_TCP_WIN,
     RULE_OPTION_TYPE_TTL,
     RULE_OPTION_TYPE_URILEN,
+    RULE_OPTION_TYPE_SCTP_CHUNK_TYPE,
+    RULE_OPTION_TYPE_SCTP_CHUNK_FLAGS,
+    RULE_OPTION_TYPE_SCTP_CHUNK_FIELD,
+    RULE_OPTION_TYPE_SCTP_NUM_CHUNKS,
+    RULE_OPTION_TYPE_SCTP_CAUSE_CODE,
     RULE_OPTION_TYPE_HDR_OPT_CHECK,
     RULE_OPTION_TYPE_PREPROCESSOR,
 #if !defined(FEAT_OPEN_APPID)
diff --git a/src/sf_protocols.h b/src/sf_protocols.h
index 735e11b..da52f7b 100644
--- a/src/sf_protocols.h
+++ b/src/sf_protocols.h
@@ -35,7 +35,7 @@ typedef enum {
     PROTO_UDP,        /* DecodeUDP */
     PROTO_TCP,        /* DecodeTCP */
                       /* DecodeTCPOptions - handled with TCP */
-
+    PROTO_SCTP,       /* DecodeSCTP */
     PROTO_IP6,        /* DecodeIPV6 */
                       /* DecodeIPV6Extensions - nothing to do here, calls below */
     PROTO_IP6_HOP_OPTS,  /* DecodeIPV6Options - ip6 hop, dst, rte, and frag exts */
diff --git a/src/sfutil/Makefile.am b/src/sfutil/Makefile.am
index 398f97f..52ed60b 100644
--- a/src/sfutil/Makefile.am
+++ b/src/sfutil/Makefile.am
@@ -54,6 +54,7 @@ libsfutil_a_SOURCES = \
     sf_seqnums.h \
     md5.c md5.h \
     sha2.c sha2.h \
+    adler32.c adler32.h \
     $(INTEL_SOFT_CPM_SOURCES)
 
 INCLUDES = @INCLUDES@
diff --git a/src/sfutil/Makefile.in b/src/sfutil/Makefile.in
index 1298db7..dca145d 100644
--- a/src/sfutil/Makefile.in
+++ b/src/sfutil/Makefile.in
@@ -117,7 +117,7 @@ am__libsfutil_a_SOURCES_DIST = sfghash.c sfghash.h sfhashfcn.c \
 	strvec.h sf_email_attach_decode.c sf_email_attach_decode.h \
 	sf_base64decode.c sf_base64decode.h Unified2_common.h \
 	sf_seqnums.h md5.c md5.h sha2.c sha2.h intel-soft-cpm.c \
-	intel-soft-cpm.h
+	intel-soft-cpm.h adler32.c adler32.h
 @HAVE_INTEL_SOFT_CPM_TRUE at am__objects_1 = intel-soft-cpm.$(OBJEXT)
 am_libsfutil_a_OBJECTS = sfghash.$(OBJEXT) sfhashfcn.$(OBJEXT) \
 	sflsq.$(OBJEXT) sfmemcap.$(OBJEXT) sfthd.$(OBJEXT) \
@@ -135,7 +135,8 @@ am_libsfutil_a_OBJECTS = sfghash.$(OBJEXT) sfhashfcn.$(OBJEXT) \
 	sfPolicyUserData.$(OBJEXT) sfActionQueue.$(OBJEXT) \
 	sfrf.$(OBJEXT) strvec.$(OBJEXT) \
 	sf_email_attach_decode.$(OBJEXT) sf_base64decode.$(OBJEXT) \
-	md5.$(OBJEXT) sha2.$(OBJEXT) $(am__objects_1)
+	md5.$(OBJEXT) sha2.$(OBJEXT) adler32.$(OBJEXT) \
+	$(am__objects_1)
 libsfutil_a_OBJECTS = $(am_libsfutil_a_OBJECTS)
 AM_V_P = $(am__v_P_ at AM_V@)
 am__v_P_ = $(am__v_P_ at AM_DEFAULT_V@)
@@ -384,6 +385,7 @@ libsfutil_a_SOURCES = \
     sf_seqnums.h \
     md5.c md5.h \
     sha2.c sha2.h \
+    adler32.h adler32.c \
     $(INTEL_SOFT_CPM_SOURCES)
 
 all: all-am
diff --git a/src/sfutil/adler32.c b/src/sfutil/adler32.c
new file mode 100644
index 0000000..3dc33c6
--- /dev/null
+++ b/src/sfutil/adler32.c
@@ -0,0 +1,59 @@
+/* adler32.c
+ * Compute the Adler32 checksum (RFC 1950)
+ * Based on code from RFC 1950 (Chapter 9. Appendix: Sample code)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/*
+ * Borrwed from Wireshark.  Hey, it's GPLv2!
+ * Original by Gerald Combs <gerald at wireshark.org>
+ * Original Copyright 1998 Gerald Combs
+ *                    2003 Tomas Kukosa
+ */
+
+#include <string.h>
+
+#include "adler32.h"
+
+#define BASE 65521 /* largest prime smaller than 65536 */
+
+/*--- update_adler32 --------------------------------------------------------*/
+unsigned long update_adler32(unsigned long adler, const unsigned char *buf, int len)
+{
+  unsigned long s1 = adler & 0xffff;
+  unsigned long s2 = (adler >> 16) & 0xffff;
+  int n;
+
+  for (n = 0; n < len; n++) {
+    s1 = (s1 + buf[n]) % BASE;
+    s2 = (s2 + s1)     % BASE;
+  }
+  return (s2 << 16) + s1;
+}
+
+/*--- adler32 ---------------------------------------------------------------*/
+unsigned long adler32_bytes(const unsigned char *buf, int len)
+{
+  return update_adler32(1L, buf, len);
+}
+
+/*--- adler32_str -----------------------------------------------------------*/
+unsigned long adler32_str(const char *buf)
+{
+  return update_adler32(1L, (const unsigned char*)buf, (int)strlen(buf));
+}
+
+/*---------------------------------------------------------------------------*/
diff --git a/src/sfutil/adler32.h b/src/sfutil/adler32.h
new file mode 100644
index 0000000..b3eba31
--- /dev/null
+++ b/src/sfutil/adler32.h
@@ -0,0 +1,41 @@
+/* adler32.h
+ * Compute the Adler32 checksum (RFC 1950)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/*
+ * Borrwed from Wireshark.  Hey, it's GPLv2!
+ * Original by Gerald Combs <gerald at wireshark.org>
+ * Original Copyright 1998 Gerald Combs
+ *                    2003 Tomas Kukosa
+ */
+
+#ifndef ADLER32_H
+#define ADLER32_H
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+unsigned long update_adler32(unsigned long adler, const unsigned char *buf, int len);
+unsigned long adler32_bytes(const unsigned char *buf, int len);
+unsigned long adler32_str(const char *buf);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* ADLER32_H */
diff --git a/src/sfutil/crc32.h b/src/sfutil/crc32.h
new file mode 100644
index 0000000..3e19174
--- /dev/null
+++ b/src/sfutil/crc32.h
@@ -0,0 +1,130 @@
+/* crc32.c
+ * CRC-32 routine
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+/*
+ * Borrwed from Wireshark.  Hey, it's GPLv2!
+ * Original by Gerald Combs <gerald at wireshark.org>
+ * Original Copyright 1998 Gerald Combs
+ *
+ * Credits:
+ * Table from Solomon Peachy
+ * Routine from Chris Waters
+ */
+
+#ifndef _CRC32_H_
+#define _CRC32_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+
+/*****************************************************************/
+/*                                                               */
+/* CRC32C LOOKUP TABLE                                           */
+/* +++================                                           */
+/* The following CRC lookup table was generated automagically    */
+/* by the Rocksoft^tm Model CRC Algorithm Table Generation       */
+/* Program V1.0 using the following model parameters:            */
+/*                                                               */
+/*    Width   : 4 bytes.                                         */
+/*    Poly    : 0x1EDC6F41L                                      */
+/*    Reverse : TRUE.                                            */
+/*                                                               */
+/* For more information on the Rocksoft^tm Model CRC Algorithm,  */
+/* see the document titled "A Painless Guide to CRC Error        */
+/* Detection Algorithms" by Ross Williams                        */
+/* (ross at guest.adelaide.edu.au.). This document is likely to be  */
+/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft".        */
+/*                                                               */
+/*****************************************************************/
+
+static const unsigned long crc32c_table[256] = {
+        0x00000000U, 0xF26B8303U, 0xE13B70F7U, 0x1350F3F4U, 0xC79A971FU,
+        0x35F1141CU, 0x26A1E7E8U, 0xD4CA64EBU, 0x8AD958CFU, 0x78B2DBCCU,
+        0x6BE22838U, 0x9989AB3BU, 0x4D43CFD0U, 0xBF284CD3U, 0xAC78BF27U,
+        0x5E133C24U, 0x105EC76FU, 0xE235446CU, 0xF165B798U, 0x030E349BU,
+        0xD7C45070U, 0x25AFD373U, 0x36FF2087U, 0xC494A384U, 0x9A879FA0U,
+        0x68EC1CA3U, 0x7BBCEF57U, 0x89D76C54U, 0x5D1D08BFU, 0xAF768BBCU,
+        0xBC267848U, 0x4E4DFB4BU, 0x20BD8EDEU, 0xD2D60DDDU, 0xC186FE29U,
+        0x33ED7D2AU, 0xE72719C1U, 0x154C9AC2U, 0x061C6936U, 0xF477EA35U,
+        0xAA64D611U, 0x580F5512U, 0x4B5FA6E6U, 0xB93425E5U, 0x6DFE410EU,
+        0x9F95C20DU, 0x8CC531F9U, 0x7EAEB2FAU, 0x30E349B1U, 0xC288CAB2U,
+        0xD1D83946U, 0x23B3BA45U, 0xF779DEAEU, 0x05125DADU, 0x1642AE59U,
+        0xE4292D5AU, 0xBA3A117EU, 0x4851927DU, 0x5B016189U, 0xA96AE28AU,
+        0x7DA08661U, 0x8FCB0562U, 0x9C9BF696U, 0x6EF07595U, 0x417B1DBCU,
+        0xB3109EBFU, 0xA0406D4BU, 0x522BEE48U, 0x86E18AA3U, 0x748A09A0U,
+        0x67DAFA54U, 0x95B17957U, 0xCBA24573U, 0x39C9C670U, 0x2A993584U,
+        0xD8F2B687U, 0x0C38D26CU, 0xFE53516FU, 0xED03A29BU, 0x1F682198U,
+        0x5125DAD3U, 0xA34E59D0U, 0xB01EAA24U, 0x42752927U, 0x96BF4DCCU,
+        0x64D4CECFU, 0x77843D3BU, 0x85EFBE38U, 0xDBFC821CU, 0x2997011FU,
+        0x3AC7F2EBU, 0xC8AC71E8U, 0x1C661503U, 0xEE0D9600U, 0xFD5D65F4U,
+        0x0F36E6F7U, 0x61C69362U, 0x93AD1061U, 0x80FDE395U, 0x72966096U,
+        0xA65C047DU, 0x5437877EU, 0x4767748AU, 0xB50CF789U, 0xEB1FCBADU,
+        0x197448AEU, 0x0A24BB5AU, 0xF84F3859U, 0x2C855CB2U, 0xDEEEDFB1U,
+        0xCDBE2C45U, 0x3FD5AF46U, 0x7198540DU, 0x83F3D70EU, 0x90A324FAU,
+        0x62C8A7F9U, 0xB602C312U, 0x44694011U, 0x5739B3E5U, 0xA55230E6U,
+        0xFB410CC2U, 0x092A8FC1U, 0x1A7A7C35U, 0xE811FF36U, 0x3CDB9BDDU,
+        0xCEB018DEU, 0xDDE0EB2AU, 0x2F8B6829U, 0x82F63B78U, 0x709DB87BU,
+        0x63CD4B8FU, 0x91A6C88CU, 0x456CAC67U, 0xB7072F64U, 0xA457DC90U,
+        0x563C5F93U, 0x082F63B7U, 0xFA44E0B4U, 0xE9141340U, 0x1B7F9043U,
+        0xCFB5F4A8U, 0x3DDE77ABU, 0x2E8E845FU, 0xDCE5075CU, 0x92A8FC17U,
+        0x60C37F14U, 0x73938CE0U, 0x81F80FE3U, 0x55326B08U, 0xA759E80BU,
+        0xB4091BFFU, 0x466298FCU, 0x1871A4D8U, 0xEA1A27DBU, 0xF94AD42FU,
+        0x0B21572CU, 0xDFEB33C7U, 0x2D80B0C4U, 0x3ED04330U, 0xCCBBC033U,
+        0xA24BB5A6U, 0x502036A5U, 0x4370C551U, 0xB11B4652U, 0x65D122B9U,
+        0x97BAA1BAU, 0x84EA524EU, 0x7681D14DU, 0x2892ED69U, 0xDAF96E6AU,
+        0xC9A99D9EU, 0x3BC21E9DU, 0xEF087A76U, 0x1D63F975U, 0x0E330A81U,
+        0xFC588982U, 0xB21572C9U, 0x407EF1CAU, 0x532E023EU, 0xA145813DU,
+        0x758FE5D6U, 0x87E466D5U, 0x94B49521U, 0x66DF1622U, 0x38CC2A06U,
+        0xCAA7A905U, 0xD9F75AF1U, 0x2B9CD9F2U, 0xFF56BD19U, 0x0D3D3E1AU,
+        0x1E6DCDEEU, 0xEC064EEDU, 0xC38D26C4U, 0x31E6A5C7U, 0x22B65633U,
+        0xD0DDD530U, 0x0417B1DBU, 0xF67C32D8U, 0xE52CC12CU, 0x1747422FU,
+        0x49547E0BU, 0xBB3FFD08U, 0xA86F0EFCU, 0x5A048DFFU, 0x8ECEE914U,
+        0x7CA56A17U, 0x6FF599E3U, 0x9D9E1AE0U, 0xD3D3E1ABU, 0x21B862A8U,
+        0x32E8915CU, 0xC083125FU, 0x144976B4U, 0xE622F5B7U, 0xF5720643U,
+        0x07198540U, 0x590AB964U, 0xAB613A67U, 0xB831C993U, 0x4A5A4A90U,
+        0x9E902E7BU, 0x6CFBAD78U, 0x7FAB5E8CU, 0x8DC0DD8FU, 0xE330A81AU,
+        0x115B2B19U, 0x020BD8EDU, 0xF0605BEEU, 0x24AA3F05U, 0xD6C1BC06U,
+        0xC5914FF2U, 0x37FACCF1U, 0x69E9F0D5U, 0x9B8273D6U, 0x88D28022U,
+        0x7AB90321U, 0xAE7367CAU, 0x5C18E4C9U, 0x4F48173DU, 0xBD23943EU,
+        0xF36E6F75U, 0x0105EC76U, 0x12551F82U, 0xE03E9C81U, 0x34F4F86AU,
+        0xC69F7B69U, 0xD5CF889DU, 0x27A40B9EU, 0x79B737BAU, 0x8BDCB4B9U,
+        0x988C474DU, 0x6AE7C44EU, 0xBE2DA0A5U, 0x4C4623A6U, 0x5F16D052U,
+        0xAD7D5351U
+};
+
+/*
+ * Byte swap fix contributed by Dave Wysochanski <davidw at netapp.com>.
+ */
+#define CRC32C_SWAP(crc32c_value)				\
+    (((crc32c_value & 0xff000000) >> 24)	|	\
+     ((crc32c_value & 0x00ff0000) >>  8)	|	\
+     ((crc32c_value & 0x0000ff00) <<  8)	|	\
+     ((crc32c_value & 0x000000ff) << 24))
+
+#define CRC32C(c,d) (c = (c >> 8) ^ crc32c_table[(c ^ (d)) & 0xFF])
+#define CRC32C_PRELOAD 0xffffffff
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _CRC32_H_ */
diff --git a/src/sfutil/sfportobject.c b/src/sfutil/sfportobject.c
index 5e52dd3..1f56e2a 100644
--- a/src/sfutil/sfportobject.c
+++ b/src/sfutil/sfportobject.c
@@ -92,9 +92,9 @@
    PortVar - has been added. These are always added to the PortVarTable.
 
    --- Loading Port lists and rules
-   PortTables - we support src and dst tables for tcp/udp/icmp/ip/arp rules.
+   PortTables - we support src and dst tables for tcp/udp/sctp/icmp/ip/arp rules.
    PortVar References - we dup the PortVar entries as needed into each table if referenced,
-   so HTTP_PORTS for tcp and udp contain different rules.  If a rule references a PortVar
+   so HTTP_PORTS for tcp, udp, & sctp contain different rules.  If a rule references a PortVar
    we look it up in the table, if its not present we dup it from the PortVarTable, otherwise
    we just add the rule index to the PortVar HTTP_PORTS in the proper table. If a PortVar
    is not used to specify a Port entry in a rule we create a temp port-object, and check if
diff --git a/src/sfutil/sfportobject.h b/src/sfutil/sfportobject.h
index 1515464..15205a5 100644
--- a/src/sfutil/sfportobject.h
+++ b/src/sfutil/sfportobject.h
@@ -166,16 +166,19 @@ typedef struct {
 
     PortTable * tcp_src, * tcp_dst;
     PortTable * udp_src, * udp_dst;
+    PortTable * sctp_src, * sctp_dst;
     PortTable * icmp_src,* icmp_dst;
     PortTable * ip_src,  * ip_dst;
     
     PortObject * tcp_anyany;
     PortObject * udp_anyany;
+    PortObject * sctp_anyany;
     PortObject * icmp_anyany;
     PortObject * ip_anyany;
     
     PortObject * tcp_nocontent; 
     PortObject * udp_nocontent; 
+    PortObject * sctp_nocontent;
     PortObject * icmp_nocontent; 
     PortObject * ip_nocontent; 
 
diff --git a/src/snort.c b/src/snort.c
index 3927c4d..f11222c 100644
--- a/src/snort.c
+++ b/src/snort.c
@@ -1935,7 +1935,7 @@ static int ShowUsage(char *program_name)
     FPUTS_BOTH ("        -H         Make hash tables deterministic.\n");
     FPUTS_BOTH ("        -i <if>    Listen on interface <if>\n");
     FPUTS_BOTH ("        -I         Add Interface name to alert output\n");
-    FPUTS_BOTH ("        -k <mode>  Checksum mode (all,noip,notcp,noudp,noicmp,none)\n");
+    FPUTS_BOTH ("        -k <mode>  Checksum mode (all,noip,notcp,noudp,nosctp,noicmp,none)\n");
     FPUTS_BOTH ("        -K <mode>  Logging mode (pcap[default],ascii,none)\n");
     FPUTS_BOTH ("        -l <ld>    Log to directory <ld>\n");
     FPUTS_BOTH ("        -L <file>  Log to this tcpdump file\n");
diff --git a/src/snort.h b/src/snort.h
index a562d7b..42c6546 100644
--- a/src/snort.h
+++ b/src/snort.h
@@ -122,7 +122,8 @@
 #define DO_IP_CHECKSUMS     0x00000001
 #define DO_TCP_CHECKSUMS    0x00000002
 #define DO_UDP_CHECKSUMS    0x00000004
-#define DO_ICMP_CHECKSUMS   0x00000008
+#define DO_SCTP_CHECKSUMS   0x00000008
+#define DO_ICMP_CHECKSUMS   0x00000010
 
 #define LOG_UNIFIED         0x00000001
 #define LOG_TCPDUMP         0x00000002
@@ -526,7 +527,8 @@ typedef enum _ChecksumFlag
     CHECKSUM_FLAG__IP   = 0x00000001,
     CHECKSUM_FLAG__TCP  = 0x00000002,
     CHECKSUM_FLAG__UDP  = 0x00000004,
-    CHECKSUM_FLAG__ICMP = 0x00000008,
+    CHECKSUM_FLAG__SCTP = 0x00000008,
+    CHECKSUM_FLAG__ICMP = 0x00000010,
     CHECKSUM_FLAG__ALL  = 0x7fffffff
 
 } ChecksumFlag;
@@ -869,7 +871,7 @@ typedef struct _SnortConfig
 #endif
 
     /* The port-rule-maps map the src-dst ports to rules for
-     * udp and tcp, for Ip we map the dst port as the protocol,
+     * udp, tcp, & sctp; for Ip we map the dst port as the protocol,
      * and for Icmp we map the dst port to the Icmp type. This
      * allows us to use the decode packet information to in O(1)
      * select a group of rules to apply to the packet.  These
@@ -880,6 +882,7 @@ typedef struct _SnortConfig
     PORT_RULE_MAP *prmIpRTNX;
     PORT_RULE_MAP *prmTcpRTNX;
     PORT_RULE_MAP *prmUdpRTNX;
+    PORT_RULE_MAP *prmSctpRTNX;
     PORT_RULE_MAP *prmIcmpRTNX;
 
 #ifdef TARGET_BASED
@@ -954,6 +957,7 @@ typedef struct _PacketCount
     uint64_t other;
     uint64_t tcp;
     uint64_t udp;
+    uint64_t sctp;
     uint64_t icmp;
     uint64_t arp;
 #ifndef NO_NON_ETHER_DECODER
@@ -968,8 +972,10 @@ typedef struct _PacketCount
     uint64_t icmp6;
     uint64_t tdisc;
     uint64_t udisc;
+    uint64_t sctpdisc;
     uint64_t tcp6;
     uint64_t udp6;
+    uint64_t sctp6;
     uint64_t teredo;
     uint64_t ipdisc;
     uint64_t icmpdisc;
@@ -1297,6 +1303,16 @@ static inline int ScUdpChecksumDrops(void)
     return snort_conf->targeted_policies[getRuntimePolicy()]->checksum_drop_flags & CHECKSUM_FLAG__UDP;
 }
 
+static inline int ScSctpChecksums(void)
+{
+    return snort_conf->targeted_policies[getDefaultPolicy()]->checksum_flags & CHECKSUM_FLAG__SCTP;
+}
+
+static inline int ScSctpChecksumDrops(void)
+{
+    return snort_conf->targeted_policies[getRuntimePolicy()]->checksum_drop_flags & CHECKSUM_FLAG__SCTP;
+}
+
 static inline int ScTcpChecksums(void)
 {
     return snort_conf->targeted_policies[getDefaultPolicy()]->checksum_flags & CHECKSUM_FLAG__TCP;
@@ -1327,6 +1343,11 @@ static inline int ScIgnoreUdpPort(uint16_t port)
     return snort_conf->ignore_ports[port] & PROTO_BIT__UDP;
 }
 
+static inline int ScIgnoreSctpPort(uint16_t port)
+{
+    return snort_conf->ignore_ports[port] & PROTO_BIT__SCTP;
+}
+
 #ifdef MPLS
 static inline long int ScMplsStackDepth(void)
 {
diff --git a/src/target-based/sftarget_protocol_reference.c b/src/target-based/sftarget_protocol_reference.c
index 843ef9d..47ab9b3 100644
--- a/src/target-based/sftarget_protocol_reference.c
+++ b/src/target-based/sftarget_protocol_reference.c
@@ -43,6 +43,7 @@
 
 int16_t protocolReferenceTCP;
 int16_t protocolReferenceUDP;
+int16_t protocolReferenceSCTP;
 int16_t protocolReferenceICMP;
 
 static SFGHASH *proto_reference_table = NULL;
@@ -57,6 +58,7 @@ static char *standard_protocols[] =
     "ip",
     "tcp",
     "udp",
+    "sctp",
     "icmp",
     /* Application Protocols */
     "http",
@@ -208,6 +210,7 @@ void InitializeProtocolReferenceTable(void)
     }
     protocolReferenceTCP = FindProtocolReference("tcp");
     protocolReferenceUDP = FindProtocolReference("udp");
+    protocolReferenceSCTP = FindProtocolReference("sctp");
     protocolReferenceICMP = FindProtocolReference("icmp");
 }
 
@@ -263,6 +266,9 @@ int16_t GetProtocolReference(Packet *p)
         case IPPROTO_UDP:
             ipprotocol = protocolReferenceUDP;
             break;
+        case IPPROTO_SCTP:
+            ipprotocol = protocolReferenceSCTP;
+            break;
         case IPPROTO_ICMP:
             ipprotocol = protocolReferenceICMP;
             break;
diff --git a/src/target-based/sftarget_protocol_reference.h b/src/target-based/sftarget_protocol_reference.h
index f0ab62e..6da5846 100644
--- a/src/target-based/sftarget_protocol_reference.h
+++ b/src/target-based/sftarget_protocol_reference.h
@@ -39,6 +39,7 @@ typedef struct _SFTargetProtocolReference
 
 extern int16_t protocolReferenceTCP;
 extern int16_t protocolReferenceUDP;
+extern int16_t protocolReferenceSCTP;
 extern int16_t protocolReferenceICMP;
 
 void InitializeProtocolReferenceTable(void);
diff --git a/src/util.c b/src/util.c
index 7f426ce..6e3cd59 100644
--- a/src/util.c
+++ b/src/util.c
@@ -1174,6 +1174,7 @@ void DropStats(int exiting)
     LogStat("ICMP", pc.icmp, total);
     LogStat("UDP", pc.udp, total);
     LogStat("TCP", pc.tcp, total);
+    LogStat("SCTP", pc.sctp, total);
 
     LogStat("IP6", pc.ipv6, total);
     LogStat("IP6 Ext", pc.ip6ext, total);
@@ -1182,6 +1183,7 @@ void DropStats(int exiting)
     LogStat("ICMP6", pc.icmp6, total);
     LogStat("UDP6", pc.udp6, total);
     LogStat("TCP6", pc.tcp6, total);
+    LogStat("SCTP6", pc.sctp6, total);
     LogStat("Teredo", pc.teredo, total);
 
     LogStat("ICMP-IP", pc.embdip, total);
@@ -1217,6 +1219,7 @@ void DropStats(int exiting)
     LogStat("IP6 Disc", pc.ipv6disc, total);
     LogStat("TCP Disc", pc.tdisc, total);
     LogStat("UDP Disc", pc.udisc, total);
+    LogStat("SCTP Disc", pc.sctpdisc, total);
     LogStat("ICMP Disc", pc.icmpdisc, total);
     LogStat("All Discard", pc.discards, total);
 
@@ -1880,6 +1883,120 @@ void * SnortAlloc2(size_t size, const char *format, ...)
 }
 
 /**
+ * SnortStrToNumRng - Parses a number (signed) range split by a delimiter.
+ * @str: char pointer to the string to be parsed.
+ * @delimiter: char pointer to the delimiter separating the two ranges.
+ * @rng: int pointer to the smaller value of the range.
+ *
+ * Returns:
+ *   SNORT_STR2RNG_SUCCESS is a range was successfully parsed.
+ *   SNORT_STR2RNG_NOTRNG if the range delimiter was not found.
+ *   SNORT_STR2RNG_ERROR if an error condition was encountered.
+ */
+int SnortStrToNumRng(char *str, const char *delimiter, NumericRange *rng)
+{
+    if (!str || !delimiter || !rng)
+        return SNORT_STR2RNG_ERROR;
+
+    if (SnortStrcasestr(str, strlen(str), delimiter))
+    {
+        char *tok, *end;
+        int num = 0;
+
+        /* Get the first token. */
+        tok = strtok(str, delimiter);
+        if (!tok)
+            return SNORT_STR2RNG_ERROR;
+
+        num = strtol(tok, &end, 10);
+        if (*end)
+            return SNORT_STR2RNG_ERROR;
+
+        rng->min = num;
+
+        /* Get the second token. */
+        tok = strtok(NULL, delimiter);
+        if (!tok)
+            return SNORT_STR2RNG_ERROR;
+
+        num = strtol(tok, &end, 10);
+        if (*end)
+            return SNORT_STR2RNG_ERROR;
+
+        rng->max = num;
+
+        /* See if min is > max.  If so, swap them. */
+        if (rng->min > rng->max)
+        {
+            int tmp = rng->min;
+            rng->min = rng->max;
+            rng->max = tmp;
+        }
+
+        return SNORT_STR2RNG_SUCCESS;
+    } else {
+        return SNORT_STR2RNG_NOTRNG;
+    }
+}
+
+/**
+ * SnortStrToUNumRng - Parses a number (signed) range split by a delimiter.
+ * @str: char pointer to the string to be parsed.
+ * @delimiter: char pointer to the delimiter separating the two ranges.
+ * @rng: int pointer to the smaller value of the range.
+ *
+ * Returns:
+ *   SNORT_STR2RNG_SUCCESS is a range was successfully parsed.
+ *   SNORT_STR2RNG_NOTRNG if the range delimiter was not found.
+ *   SNORT_STR2RNG_ERROR if an error condition was encountered.
+ */
+int SnortStrToUNumRng(char *str, const char *delimiter, UNumericRange *rng)
+{
+    if (!str || !delimiter || !rng)
+        return SNORT_STR2RNG_ERROR;
+
+    if (SnortStrcasestr(str, strlen(str), delimiter))
+    {
+        char *tok, *end;
+        uint32_t num = 0;
+
+        /* Get the first token. */
+        tok = strtok(str, delimiter);
+        if (!tok)
+            return SNORT_STR2RNG_ERROR;
+
+        num = strtoul(tok, &end, 10);
+        if (*end)
+            return SNORT_STR2RNG_ERROR;
+
+        rng->min = num;
+
+        /* Get the second token. */
+        tok = strtok(NULL, delimiter);
+        if (!tok)
+            return SNORT_STR2RNG_ERROR;
+
+        num = strtoul(tok, &end, 10);
+        if (*end)
+            return SNORT_STR2RNG_ERROR;
+
+        rng->max = num;
+
+        /* See if min is > max.  If so, swap them. */
+        if (rng->min > rng->max)
+        {
+            uint32_t tmp = rng->min;
+            rng->min = rng->max;
+            rng->max = tmp;
+        }
+
+        return SNORT_STR2RNG_SUCCESS;
+    } else {
+        return SNORT_STR2RNG_NOTRNG;
+    }
+}
+
+/**
  * Chroot and adjust the snort_conf->log_dir reference
  *
  * @param directory directory to chroot to
diff --git a/src/util.h b/src/util.h
index f166457..13a92c3 100644
--- a/src/util.h
+++ b/src/util.h
@@ -80,6 +80,10 @@
 
 #define SNORT_STRNLEN_ERROR -1
 
+#define SNORT_STR2RNG_SUCCESS 0
+#define SNORT_STR2RNG_ERROR -1
+#define SNORT_STR2RNG_NOTRNG -2
+
 #define SECONDS_PER_DAY  86400  /* number of seconds in a day  */
 #define SECONDS_PER_HOUR  3600  /* number of seconds in a hour */
 #define SECONDS_PER_MIN     60     /* number of seconds in a minute */
@@ -109,6 +113,7 @@ typedef struct _IntervalStats
     uint64_t processed, processed_total;
     uint64_t tcp, tcp_total;
     uint64_t udp, udp_total;
+    uint64_t sctp, sctp_total;
     uint64_t icmp, icmp_total;
     uint64_t arp, arp_total;
     uint64_t ipx, ipx_total;
@@ -156,6 +161,19 @@ typedef struct _IntervalStats
 
 } IntervalStats;
 
+/* Used by rule keywords that involve number ranges. */
+typedef struct _NumericRange
+{
+    int min;
+    int max;
+} NumericRange;
+
+typedef struct _UNumericRange
+{
+    uint32_t min;
+    uint32_t max;
+} UNumericRange;
+
 
 /* Public function prototypes *************************************************/
 void StoreSnortInfoStrings(void);
@@ -210,6 +228,8 @@ int CheckValueInRange(const char *value_str, char *option,
         unsigned long lo, unsigned long hi, unsigned long *value);
 
 void *SnortAlloc2(size_t, const char *, ...);
+int SnortStrToNumRng(char *, const char *, NumericRange *);
+int SnortStrToUNumRng(char *, const char *, UNumericRange *);
 char *CurrentWorkingDir(void);
 char *GetAbsolutePath(char *dir);
 char *StripPrefixDir(char *prefix, char *dir);


More information about the Snort-devel mailing list