Example #1
0
    def test_tcp_with_options(self):
        """Assert that a TCP with options is correctly encoded."""
        packet = tcp()

        nop = pcs.Field("nop", 8)
        mss = pcs.TypeLengthValueField("mss", pcs.Field("t", 8, default=0x02),
                                       pcs.Field("l", 8), pcs.Field("v", 16))
        end = pcs.Field("end", 8)

        nop.value = 1
        mss.value.value = 1460  # Most common Internet MSS value.

        # Build a TCP option list which will be 32-bits aligned.
        packet.options.append(nop)
        packet.options.append(nop)
        packet.options.append(mss)
        packet.options.append(nop)
        packet.options.append(end)

        expected = "\x00\x00\x00\x00\x00\x00\x00\x00" \
            "\x00\x00\x00\x00\x00\x00\x00\x00" \
            "\x00\x00\x00\x00\x01\x01\x02\x04" \
            "\x05\xb4\x01\x00"
        got = packet.bytes

        #packet.encode()
        #hd = hexdumper()
        #print hd.dump(expected)
        #print hd.dump(got)

        # XXX: Note well: just because you added an option list,
        # doesn't mean the TCP option length is correct.

        self.assertEqual(expected, got)
Example #2
0
 def field(self):
     """ Return the complete field value as it should be appended to
         the DHCPv4 options payload. """
     return pcs.TypeLengthValueField( \
         self.fieldname(), \
         pcs.Field("t", 8, default = self.optno), \
         pcs.Field("l", 8, default = len(self.bytes)), \
         self.datafield(), \
         inclusive = False, \
         bytewise = True)
Example #3
0
File: ipv4.py Project: jeamland/PCS
    def __init__(self, bytes = None, timestamp = None, **kv):
        """ define the fields of an IPv4 packet, from RFC 791."""
        version = pcs.Field("version", 4, default=4)
        hlen = pcs.Field("hlen", 4, default=5)
        tos = pcs.Field("tos", 8)
        length = pcs.Field("length", 16, default=20)
        id = pcs.Field("id", 16)
        flags = pcs.Field("flags", 3)
        offset = pcs.Field("offset", 13, default=0)
        ttl = pcs.Field("ttl", 8, default=64)
        protocol = pcs.Field("protocol", 8, discriminator=True)
        checksum = pcs.Field("checksum", 16)
        src = pcs.Field("src", 32)
        dst = pcs.Field("dst", 32)
        options = pcs.OptionListField("options")
        pcs.Packet.__init__(self,
                            [version, hlen, tos, length, id, flags, offset,
                             ttl, protocol, checksum, src, dst, options],
                            bytes = bytes, **kv)
        # Description MUST be set after the PCS layer init
        self.description = "IPv4"

        if timestamp is None:
            self.timestamp = time.time()
        else:
            self.timestamp = timestamp

        if bytes is not None:
            hlen_bytes = self.hlen * 4
            options_len = hlen_bytes - self.sizeof()

            if hlen_bytes > len(bytes):
                raise UnpackError, \
                      "IP header is larger than input (%d > %d)" % \
                      (hlen_bytes, len(bytes))

            if options_len > 0:
                curr = self.sizeof()
                while curr < hlen_bytes:
                    option = struct.unpack('!B', bytes[curr])[0]

                    if option == IPOPT_EOL:
                        options.append(pcs.Field("end", 8, default = IPOPT_EOL))
                        curr += 1
                        continue
                    elif option == IPOPT_NOP:
                        options.append(pcs.Field("nop", 8, default = IPOPT_NOP))
                        curr += 1
                        continue

                    optlen = struct.unpack('!B', bytes[curr+1])[0]
                    if option == IPOPT_RA:
                        # The IPv4 Router Alert option (RFC 2113) is a
                        # single 16 bit value. Its existence indicates
                        # that a router must examine the packet. It is
                        # 32 bits wide including option code and length.
                        if optlen != 4:
                            raise UnpackError, \
                                  "Bad length %d for IP option %d, " \
                                  "should be %d" % (optlen, option, 4)
                        value = struct.unpack("!H", bytes[curr+2:curr+4])[0]
                        options.append(pcs.TypeLengthValueField("ra",
                                       pcs.Field("t", 8, default = option),
                                       pcs.Field("l", 8, default = optlen),
                                       pcs.Field("v", 16, default = value)))
                        curr += optlen
                    else:
                        print "warning: unknown IP option %d" % option
                        optdatalen = optlen - 2
                        options.append(pcs.TypeLengthValueField("unknown",
                                       pcs.Field("t", 8, default = option),
                                       pcs.Field("l", 8, default = optlen),
                                       pcs.Field("v", optdatalen * 8,
                                                 default = value)))
                        curr += optlen

        if (bytes is not None):
            offset = self.hlen << 2
            self.data = self.next(bytes[offset:len(bytes)],
                                  timestamp = timestamp)
            if self.data is None:
                from pcs.packets.payload import payload
                self.data = payload(bytes[offset:len(bytes)])
            #if __debug__:
            #    print "decoded IPv4 payload proto", self.protocol, "as", type(self.data)
        else:
            self.data = None
Example #4
0
    def __init__(self, bytes=None, timestamp=None, **kv):
        """initialize a TCP packet"""
        sport = pcs.Field("sport", 16)
        dport = pcs.Field("dport", 16)
        sequence = pcs.Field("sequence", 32)
        ack_number = pcs.Field("ack_number", 32)
        offset = pcs.Field("offset", 4, default=5)
        reserved = pcs.Field("reserved", 3)
        ns = pcs.Field("ns", 1)
        cwr = pcs.Field("cwr", 1)
        ece = pcs.Field("ece", 1)
        urg = pcs.Field("urg", 1)
        ack = pcs.Field("ack", 1)
        psh = pcs.Field("psh", 1)
        rst = pcs.Field("rst", 1)
        syn = pcs.Field("syn", 1)
        fin = pcs.Field("fin", 1)
        window = pcs.Field("window", 16)
        checksum = pcs.Field("checksum", 16)
        urg_pointer = pcs.Field("urg_pointer", 16)
        options = pcs.OptionListField("options")
        pcs.Packet.__init__(self, [
            sport, dport, sequence, ack_number, offset, reserved, ns, cwr, ece,
            urg, ack, psh, rst, syn, fin, window, checksum, urg_pointer,
            options
        ],
                            bytes=bytes,
                            **kv)
        self.description = "TCP"
        if timestamp is None:
            self.timestamp = time.time()
        else:
            self.timestamp = timestamp

        # Decode TCP options.
        if bytes is not None:
            data_offset = self.offset * 4  # in bytes
            options_len = data_offset - self.sizeof()

            # Sanity check that the buffer we are given is large enough
            # to contain the TCP header, or else TCP option decode will
            # fail. This usually indicates a problem below, i.e. we
            # tried to copy a segment and didn't create fields to back
            # the options, causing the data to be lost.
            # If options are present then they must fit into the 40 byte
            # option area. We will perform this check during encoding later.

            if data_offset > len(bytes):
                raise UnpackError, \
                      "TCP segment is larger than input (%d > %d)" % \
                      (data_offset, len(bytes))

            if (options_len > 0):
                curr = self.sizeof()
                while (curr < data_offset):
                    option = struct.unpack('!B', bytes[curr])[0]

                    #print "(curr = %d, data_offset = %d, option = %d)" % \
                    #	  (curr, data_offset, option)

                    # Special-case options which do not have a length field.
                    if option == 0:  # end
                        options.append(pcs.Field("end", 8, default=0))
                        curr += 1
                        #break              # immediately stop processing.
                        continue  # immediately stop processing.
                    elif option == 1:  # nop
                        options.append(pcs.Field("nop", 8, default=1))
                        curr += 1
                        continue

                    optlen = struct.unpack('!B', bytes[curr + 1])[0]
                    if (optlen < 1 or optlen > (data_offset - curr)):
                        raise UnpackError, \
                              "Bad length %d for TCP option %d" % \
                              (optlen, option)

                    # XXX we could break this out into a map.
                    # option lengths include the length of the code byte,
                    # length byte, and the option data. the fly in the
                    # buttermilk of course is that they do not 1:1 map
                    # onto TLVs, see above, but they need to if we plan
                    # to use the existing object model.
#print "\t(optlen %d)" % (optlen)

                    if option == 2:  # mss
                        # XXX This is being thrown, not sure why.
                        #if optlen != 4:
                        #    print options
                        #    raise UnpackError, \
                        #          "Bad length %d for TCP option %d, should be %d" % \
                        #          (optlen, option, 4)
                        value = struct.unpack("!H",
                                              bytes[curr + 2:curr + 4])[0]
                        # XXX does tlv encode a length in bits or bytes??
                        # 'cuz a second pass spits out 'it's optlen 16'"
                        options.append(pcs.TypeLengthValueField("mss", \
                                pcs.Field("t", 8, default = option), \
                                pcs.Field("l", 8, default = optlen), \
                                pcs.Field("v", 16, default = value)))
                        curr += optlen
                    elif option == 3:  # wscale
                        if optlen != 3:
                            raise UnpackError, \
                                  "Bad length %d for TCP option %d, should be %d" % \
                                  (optlen, option, 3)
                        value = struct.unpack("B", bytes[curr + 2:curr + 3])[0]
                        options.append(pcs.TypeLengthValueField("wscale", \
                                pcs.Field("t", 8, default = option), \
                                pcs.Field("l", 8, default = optlen), \
                                pcs.Field("v", 8, default = value)))
                        curr += optlen
                    elif option == 4:  # sackok
                        if optlen != 2:
                            raise UnpackError, \
                                  "Bad length %d for TCP option %d, should be %d" % \
                                  (optlen, option, 2)
                        options.append(pcs.TypeLengthValueField("sackok", \
                                pcs.Field("t", 8, default = option), \
                                pcs.Field("l", 8, default = optlen), \
                                pcs.Field("v", 0, default = value)))
                        curr += optlen
                    elif option == 5:  # sack
                        # this is a variable length option, the permitted
                        # range is 2 + 1..4*sizeof(sackblock) subject
                        # to any other options.
                        sacklen = optlen - 2
                        value = struct.unpack(
                            "%dB" % sacklen, bytes[curr + 2:curr + sacklen])[0]
                        options.append(pcs.TypeLengthValueField("sack", \
                                pcs.Field("t", 8, default = option), \
                                pcs.Field("l", 8, default = optlen), \
                                pcs.Field("v", sacklen * 8, default = value)))
                        curr += optlen
                    elif option == 8:  # tstamp
                        if optlen != 10:
                            raise UnpackError, \
                                  "Bad length %d for TCP option %d, should be %d" % \
                                  (optlen, option, 10)
                        value = struct.unpack("!2I",
                                              bytes[curr + 2:curr + 10])[0]
                        options.append(pcs.TypeLengthValueField("tstamp", \
                                pcs.Field("t", 8, default = option), \
                                pcs.Field("l", 8, default = optlen), \
                                pcs.Field("v", 64, default = value)))
                        curr += optlen
                    #elif option == 19:        # md5
                    #    if optlen != 18:
                    #        raise UnpackError, \
                    #              "Bad length %d for TCP option %d, should be %d" % \
                    #              (optlen, option, 18)
                    #    value = struct.unpack("16B", bytes[curr+2:curr+16])[0]
#	options.append(pcs.TypeLengthValueField("md5", \
#		       pcs.Field("t", 8, default = option), \
#		       pcs.Field("l", 8, default = optlen), \
#		       pcs.Field("v", 64, default = value)))
#    curr += optlen
                    elif option == 30:  # multipath
                        optdatalen = optlen - 2
                        value = struct.unpack("!" + str(optdatalen) + "B",
                                              bytes[curr + 2:curr + optlen])
                        myval = 0
                        for i in value:
                            myval = myval << 8 | i
                        value = myval
                        options.append(pcs.TypeLengthValueField("unknown", \
                                       pcs.Field("t", 8, default = option), \
                                       pcs.Field("l", 8, default = optlen+2), \
                                       pcs.Field("v", optdatalen * 8, default = value)))
                        curr += optlen
                    else:
                        #print "warning: unknown option %d" % option
                        optdatalen = optlen - 2
                        value = struct.unpack(
                            "!B", bytes[curr + 2:curr + optdatalen])[0]
                        options.append(pcs.TypeLengthValueField("unknown", \
                                pcs.Field("t", 8, default = option), \
                                pcs.Field("l", 8, default = optlen), \
                                pcs.Field("v", optdatalen * 8, default = value)))
                        curr += optlen

        if (bytes is not None and (self.offset * 4 < len(bytes))):
            self.data = self.next(bytes[(self.offset * 4):len(bytes)],
                                  timestamp=timestamp)
            if self.data is None:
                from pcs.packets.payload import payload
                self.data = payload(bytes[(self.offset * 4):len(bytes)])
        else:
            self.data = None
Example #5
0
 def __init__(self, bytes=None):
     f1 = pcs.Field("f1", 32)
     f2 = pcs.TypeLengthValueField("f2", pcs.Field("t", 8),
                                   pcs.Field("l", 8),
                                   pcs.StringField("v", 10 * 8))
     pcs.Packet.__init__(self, [f1, f2], bytes=None)