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)
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)
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
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
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)