docs: def from_bytes(cls, bitstream, decode_payload=True):
          '''
          Parse the given packet and update properties accordingly
          '''
          packet = cls()
  
          # Convert to ConstBitStream (if not already provided)
          if not isinstance(bitstream, ConstBitStream):
              if isinstance(bitstream, Bits):
                  bitstream = ConstBitStream(auto=bitstream)
              else:
                  bitstream = ConstBitStream(bytes=bitstream)
  
          # Read the version
          version = bitstream.read('uint:4')
          if version != packet.version:
              raise ValueError('Provided bytes do not contain an IPv4 packet')
  
          # Read the header length
          ihl = bitstream.read('uint:4')
          if ihl < 5:
              raise ValueError('Invalid IPv4 header length')
  
          # Now that we know the length of the header we store it to be able
          # to easily recalculate the header checksum later
          remaining_header_bits = (ihl * 32) - 8
          header = (BitStream('uint:4=4, uint:4=%d' % ihl) +
                    bitstream.peek(remaining_header_bits))
  
          # Read the type of service
          packet.tos = bitstream.read('uint:8')
  
          # Read the total length
          total_length = bitstream.read('uint:16')
          if total_length < ihl * 4:
              raise ValueError('Total length is shorter than the header')
  
          # Read the identification
          packet.identification = bitstream.read('uint:16')
  
          # Read the flags
          (reserved,
           packet.dont_fragment,
           packet.more_fragments) = bitstream.readlist('3*bool')
  
          if reserved:
              raise ValueError('Reserved flag must be 0')
  
          # Read the fragment offset
          packet.fragment_offset = bitstream.read('uint:13')
  
          # Read the TTL
          packet.ttl = bitstream.read('uint:8')
  
          # Read the protocol number
          packet.protocol = bitstream.read('uint:8')
  
          # Read the header checksum
          header_checksum = bitstream.read('uint:16')
  
          # Set the checksum bits in the header to 0 and re-calculate
          header[80:96] = BitStream(16)
          my_checksum = checksum.ones_complement(header.bytes)
  
          if my_checksum != header_checksum:
              raise ValueError('Header checksum does not match')
  
          # Read the source and destination addresses
          packet.source = IPv4Address(bitstream.read('uint:32'))
          packet.destination = IPv4Address(bitstream.read('uint:32'))
  
          # Read the options
          option_len = (ihl - 5) * 4
          packet.options = bitstream.read('bytes:%d' % option_len)
  
          # And the rest is payload
          payload_bytes = (total_length) - (ihl * 4)
          packet.payload = bitstream.read('bytes:%d' % payload_bytes)
  
          if decode_payload:
              payload_class = protocol_registry.get_type_class(packet.protocol)
              if payload_class:
                  packet.payload = payload_class.from_bytes(packet.payload)
  
          # There should be no remaining bits
          if bitstream.pos != bitstream.len:
              raise ValueError('Bits remaining after processing packet')
  
          # Verify that the properties make sense
          packet.sanitize()
  
          return packet