docs: def from_bytes(cls, bitstream):
          '''
          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 source and destination ports
          (packet.source_port,
           packet.destination_port) = bitstream.readlist('2*uint:16')
  
          # Store the length
          length = bitstream.read('uint:16')
          if length < 8:
              raise ValueError('Invalid UDP length')
  
          # Read the checksum
          packet.checksum = bitstream.read('uint:16')
  
          # And the rest is payload
          payload_bytes = length - 8
          packet.payload = bitstream.read('bytes:%d' % payload_bytes)
  
          # LISP-specific handling
          if packet.source_port == 4341 or packet.destination_port == 4341:
              # Payload is a LISP data packet
              from pylisp.packet.lisp.data import DataPacket
              packet.payload = DataPacket.from_bytes(packet.payload)
          elif packet.source_port == 4342 or packet.destination_port == 4342:
              # Payload is a LISP control message
              from pylisp.packet.lisp.control.base import ControlMessage
              packet.payload = ControlMessage.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