class Mu::Pcap::SCTP

Constants

CHUNK_ABORT
CHUNK_ASCONF
CHUNK_ASCONF_ACK
CHUNK_AUTH
CHUNK_CWR
CHUNK_DATA

SCTP chunk types

CHUNK_ECNE
CHUNK_ERROR
CHUNK_FORWARD_TSN
CHUNK_HEARTBEAT
CHUNK_HEARTBEAT_ACK
CHUNK_INIT
CHUNK_INIT_ACK
CHUNK_PADDING
CHUNK_SACK
CHUNK_SHUTDOWN
CHUNK_SHUTDOWN_ACK
CHUNK_SHUTDOWN_COMPLETE
PARAM_ADAPTATION_LAYER_INDICATION
PARAM_CHUNK_LIST
PARAM_ECN
PARAM_FORWARD_TSN
PARAM_HMAC_ALGORITHM
PARAM_HOST_NAME_ADDR
PARAM_IPV4

SCTP parameter types

PARAM_IPV6
PARAM_PADDING
PARAM_RANDOM
PARAM_SET_PRIMARY_ADDR
PARAM_SUPPORTED_ADDR_TYPES
PARAM_SUPPORTED_EXTENSIONS

Attributes

checksum[RW]
dst_port[RW]
src_port[RW]
verify_tag[RW]

Public Class Methods

crc32(bytes) click to toggle source
# File lib/woolen_common/pcap/mu/pcap/sctp.rb, line 283
def self.crc32 bytes
    r = 0xFFFFFFFF

    bytes.each_byte do |b|
        r ^= b

        8.times do
            r = (r >> 1) ^ (0xEDB88320 * (r & 1))
        end
    end

    return r ^ 0xFFFFFFFF
end
from_bytes(bytes) click to toggle source

Creates SCTP packet from the payload

# File lib/woolen_common/pcap/mu/pcap/sctp.rb, line 69
def self.from_bytes bytes
    # Basic packet validation
    Pcap.assert(bytes.length >= 12,
                "Truncated SCTP header: 12 > #{bytes.length}")
    Pcap.assert(bytes.length >= 16,
                "Truncated SCTP packet: got only #{bytes.length} bytes")

    # Read SCTP header
    sport, dport, vtag, cksum = bytes.unpack('nnNN')

    # Create SCTP packet and populate SCTP header fields
    sctp = SCTP.new
    sctp.src_port = sport
    sctp.dst_port = dport
    sctp.verify_tag = vtag
    sctp.checksum = cksum

    # Initialize the counter
    length = 12

    # Collect the chunks
    while length < bytes.length
        # Parse new chunk from the bytes
        chunk = Chunk.from_bytes(bytes[length..-1])

        # Get chunk size with padding
        length += chunk.padded_size

        # Add chunk to the list
        sctp << chunk
    end

    # Sync the payload
    sctp.sync_payload

    # Return the result
    return sctp
end
new() click to toggle source
Calls superclass method Mu::Pcap::Packet::new
# File lib/woolen_common/pcap/mu/pcap/sctp.rb, line 50
def initialize
    super

    @src_port = 0
    @dst_port = 0
    @verify_tag = 0
    @checksum = 0
    @payload = []
end
reorder(packets) click to toggle source

Reorders SCTP packets, if necessary

# File lib/woolen_common/pcap/mu/pcap/sctp.rb, line 112
def self.reorder packets
    # Initialize
    tsns = {}
    init_packets = {}
    init_ack_packets = {}
    reordered_packets = []

    # Iterate over each packet
    while not packets.empty?
        # Get next packet
        packet = packets.shift

        # Do not reorder non-SCTP packets
        if not sctp?(packet)
            reordered_packets << packet
        else
            # Get SCTP portion
            sctp = packet.payload.payload

            # Sanity checks and packet filtering/preprocessing
            if 0 == sctp.verify_tag and not sctp.init?
                # Non-Init packet with 0 verify tag
                raise ReorderError, "Non-Init packet with zero verify tag"
            elsif sctp.init_or_ack? and 1 < sctp.chunk_count
                # Init/InitAck packet with more with one chunk
                raise ReorderError, "Init/Ack packet with more than 1 chunk"
            elsif sctp.init?
                # Use checksum to save reverse verify tag in the Init packet
                sctp.checksum = sctp[0].init_tag

                # Save orphaned Init packets until we find the Ack
                init_packets[sctp.reverse_flow_id] = sctp

                # Add packet for further processing
                reordered_packets << packet
            elsif sctp.init_ack?
                # Lookup Init packet and construct it's flow it
                init_packet = init_packets.delete(sctp.flow_id)

                # Did we find anything?
                if init_packet
                    # Set verify tag in the Init packet
                    init_packet.verify_tag = sctp[0].init_tag

                    # Set reverse verify tag in the InitAck packet
                    sctp.checksum = init_packet.verify_tag

                    # Re-insert INIT packet keyed by its flow id
                    init_packets[init_packet.flow_id] = init_packet
                else
                    Pcap.warning("Orphaned SCTP INIT_ACK packet")
                end

                # Save InitAck packet
                init_ack_packets[sctp.flow_id] = sctp

                # Add packet for further processing
                reordered_packets << packet
            elsif sctp.has_data?
                # SCTP packet with user data; lookup Init or InitAck packet
                init_packet = init_packets[sctp.flow_id]
                init_ack_packet = init_ack_packets[sctp.flow_id]

                # It should belong to either one flow id or the other
                if init_packet
                    # Set reverse verify tag from Init packet
                    sctp.checksum = init_packet.checksum
                elsif init_ack_packet
                    # Set reverse flow id from InitAck packet
                    sctp.checksum = init_ack_packet.checksum
                else
                    # Orphaned SCTP packet -- not very good
                    Pcap.warning("Orphaned SCTP DATA packet detected")
                end

                # If we have just one chunk we are done
                if 1 == sctp.chunk_count and not tsns.member?(sctp[0].tsn)
                    # Save TSN
                    tsns[sctp[0].tsn] = sctp[0]

                    # sync the payload
                    sctp.sync_payload

                    # Add packet for further processing
                    reordered_packets << packet
                else
                    # Split each data chunk in a separate SCTP packet
                    sctp.chunk_count.times do
                        # Get next chunk
                        chunk = sctp.shift

                        # Is it data?
                        if CHUNK_DATA == chunk.type
                            # Yes, check for duplicate TSNs
                            if not tsns.member?(chunk.tsn)
                                # Not a duplicate; create new SCTP packet
                                packet_new = packet.deepdup

                                # Create new SCTP payload
                                sctp_new = SCTP.new
                                sctp_new.src_port = sctp.src_port
                                sctp_new.dst_port = sctp.dst_port
                                sctp_new.verify_tag = sctp.verify_tag
                                sctp_new.checksum = sctp.checksum

                                # Add the chunk
                                sctp_new << chunk

                                # Add SCTP payload to the new packet
                                packet_new.payload.payload = sctp_new

                                # Save TSN
                                tsns[chunk.tsn] = chunk

                                # Sync the payload
                                sctp_new.sync_payload

                                # Add packet for further processing
                                reordered_packets << packet_new
                            else
                                Pcap.warning("Duplicate chunk: #{chunk.tsn}")
                            end
                        else
                            Pcap.warning("Non-data chunk: #{chunk.type}")
                        end
                    end
                end
            else
                # Other SCTP packet; we are not interested at this time
            end
        end
    end

    # Return the result
    return reordered_packets
end
sctp?(packet) click to toggle source
# File lib/woolen_common/pcap/mu/pcap/sctp.rb, line 297
def self.sctp? packet
    return packet.is_a?(Ethernet) &&
        packet.payload.is_a?(IP) &&
        packet.payload.payload.is_a?(SCTP)
end

Public Instance Methods

<<(chunk) click to toggle source
# File lib/woolen_common/pcap/mu/pcap/sctp.rb, line 303
def << chunk
    @payload << chunk
end
==(other) click to toggle source
Calls superclass method Mu::Pcap::Packet#==
# File lib/woolen_common/pcap/mu/pcap/sctp.rb, line 332
def == other
    return super &&
        self.src_port == other.src_port &&
        self.dst_port == other.dst_port &&
        self.verify_tag == other.verify_tag &&
        self.payload.size == other.payload.size
end
[](index) click to toggle source
# File lib/woolen_common/pcap/mu/pcap/sctp.rb, line 311
def [] index
    return @payload[index]
end
chunk_count() click to toggle source
# File lib/woolen_common/pcap/mu/pcap/sctp.rb, line 315
def chunk_count
    return @payload.size
end
flow_id() click to toggle source
# File lib/woolen_common/pcap/mu/pcap/sctp.rb, line 60
def flow_id
    return [:sctp, @src_port, @dst_port, @verify_tag]
end
has_data?() click to toggle source
# File lib/woolen_common/pcap/mu/pcap/sctp.rb, line 319
def has_data?
    return @payload.any? do |chunk|
        CHUNK_DATA == chunk.type
    end
end
init?() click to toggle source
# File lib/woolen_common/pcap/mu/pcap/sctp.rb, line 340
def init?
    if CHUNK_INIT == @payload[0].type
        return true
    else
        return false
    end
end
init_ack?() click to toggle source
# File lib/woolen_common/pcap/mu/pcap/sctp.rb, line 348
def init_ack?
    if CHUNK_INIT_ACK == @payload[0].type
        return true
    else
        return false
    end
end
init_or_ack?() click to toggle source
# File lib/woolen_common/pcap/mu/pcap/sctp.rb, line 356
def init_or_ack?
    if CHUNK_INIT == @payload[0].type or CHUNK_INIT_ACK == @payload[0].type
        return true
    else
        return false
    end
end
reverse_flow_id() click to toggle source
# File lib/woolen_common/pcap/mu/pcap/sctp.rb, line 64
def reverse_flow_id
    return [:sctp, @dst_port, @src_port, @checksum]
end
shift() click to toggle source
# File lib/woolen_common/pcap/mu/pcap/sctp.rb, line 307
def shift
    return @payload.shift
end
sync_payload() click to toggle source
# File lib/woolen_common/pcap/mu/pcap/sctp.rb, line 270
def sync_payload
    # Reset raw bytes
    @payload_raw = ''

    # Iterate over each chunk
    @payload.each do |chunk|
        @payload_raw << chunk.payload_raw
    end

    # Reset raw payload if it's empty
    @payload_raw = nil if @payload_raw == ''
end
to_s() click to toggle source
# File lib/woolen_common/pcap/mu/pcap/sctp.rb, line 325
def to_s
    return "sctp(%d, %d, %d, %s)" % [@src_port,
                                     @dst_port,
                                     @verify_tag,
                                     @payload.join(", ")]
end
write(io, ip) click to toggle source
# File lib/woolen_common/pcap/mu/pcap/sctp.rb, line 249
def write io, ip
    # Give a warning if packet size exceeds maximum allowed
    if @payload_raw and @payload_raw.length + 20 > 65535
        Pcap.warning("SCTP payload is too large")
    end

    # Calculate CRC32 checksum on the packet; temporarily removed due to a
    # hack that uses checksum to link forward and reverse SCTP flow IDs.
    #header = [@src_port, @dst_port, @verify_tag, 0].pack('nnNN')
    #checksum = SCTP.crc32(header + @payload_raw)
    header = [@src_port, @dst_port, @verify_tag, @checksum].pack('nnNN')

    # Write SCTP header followed by each chunk
    io.write(header)

    # Write each chunks' data
    @payload.each do |chunk|
        chunk.write(io, ip)
    end
end