docs: def send_payload(self, payload=(), payload_type=None, retry=True,
                       delay_xmit=None, needskeepalive=False, timeout=None):
          """Send payload over the IPMI Session
  
          :param needskeepalive: If the payload is expected not to count as
                                 'active' by the BMC, set this to True
                                 to avoid Session considering the
                                 job done because of this payload.
                                 Notably, 0-length SOL packets
                                 are prone to confusion.
          :param timeout: Specify a custom timeout for long-running request
          """
          if payload and self.lastpayload:
              # we already have a packet outgoing, make this
              # a pending payload
              # this way a simplistic BMC won't get confused
              # and we also avoid having to do more complicated
              # retry mechanism where each payload is
              # retried separately
              self.pendingpayloads.append((payload, payload_type, retry))
              return
          if payload_type is None:
              payload_type = self.last_payload_type
          if not payload:
              payload = self.lastpayload
          message = bytearray(b'\x06\x00\xff\x07')  # constant IPMI RMCP header
          if retry:
              self.lastpayload = payload
              self.last_payload_type = payload_type
          if not isinstance(payload, bytearray):
              payload = bytearray(payload)
          message.append(self.authtype)
          baretype = payload_type
          if self.integrityalgo:
              payload_type |= 0b01000000
          if self.confalgo:
              payload_type |= 0b10000000
          if self.ipmiversion == 2.0:
              message.append(payload_type)
              if baretype == 2:
                  # TODO(jbjohnso): OEM payload types
                  raise NotImplementedError("OEM Payloads")
              elif baretype not in constants.payload_types.values():
                  raise NotImplementedError(
                      "Unrecognized payload type %d" % baretype)
              message += struct.pack("<I", self.sessionid)
          message += struct.pack("<I", self.sequencenumber)
          if self.ipmiversion == 1.5:
              message += struct.pack("<I", self.sessionid)
              if not self.authtype == 0:
                  message += self._ipmi15authcode(payload)
              message.append(len(payload))
              message += payload
              # Guessing the ipmi spec means the whole
              totlen = 34 + len(message)
              # packet and assume no tag in old 1.5 world
              if totlen in (56, 84, 112, 128, 156):
                  message.append(0)  # Legacy pad as mandated by ipmi spec
          elif self.ipmiversion == 2.0:
              psize = len(payload)
              if self.confalgo:
                  pad = (psize + 1) % 16  # pad has to cope with one byte
                  # field like the _aespad function
                  if pad:  # if no pad needed, then we take no more action
                      pad = 16 - pad
                  # new payload size grew according to pad
                  newpsize = psize + pad + 17
                  # size, plus pad length, plus 16 byte IV
                  # (Table 13-20)
                  message.append(newpsize & 0xff)
                  message.append(newpsize >> 8)
                  iv = os.urandom(16)
                  message += iv
                  payloadtocrypt = bytes(payload + _aespad(payload))
                  crypter = Cipher(
                      algorithm=algorithms.AES(self.aeskey),
                      mode=modes.CBC(iv),
                      backend=self._crypto_backend
                  )
                  encryptor = crypter.encryptor()
                  message += encryptor.update(payloadtocrypt
                                              ) + encryptor.finalize()
              else:  # no confidetiality algorithm
                  message.append(psize & 0xff)
                  message.append(psize >> 8)
                  message += payload
              if self.integrityalgo:  # see table 13-8,
                  # RMCP+ packet format
                  # TODO(jbjohnso): SHA256 which is now
                  # allowed
                  neededpad = (len(message) - 2) % 4
                  if neededpad:
                      neededpad = 4 - neededpad
                  message += b'\xff' * neededpad
                  message.append(neededpad)
                  message.append(7)  # reserved, 7 is the required value for the
                  # specification followed
                  message += hmac.new(self.k1,
                                      bytes(message[4:]),
                                      hashlib.sha1).digest()[:12]  # SHA1-96
                  # per RFC2404 truncates to 96 bits
          self.netpacket = message
          # advance idle timer since we don't need keepalive while sending
          # packets out naturally
          with util.protect(KEEPALIVE_SESSIONS):
              if (self in Session.keepalive_sessions and not needskeepalive and
                      not self._customkeepalives):
                  Session.keepalive_sessions[self]['timeout'] = \
                      _monotonic_time() + MAX_IDLE - (random.random() * 4.9)
              self._xmit_packet(retry, delay_xmit=delay_xmit, timeout=timeout)