March 20, 2011

Heuristic decode of RTP packets in practice

It is not possible to a distinguish RTP packet reliably without additional analysis of signaling.

Different ways are used to understand if some packet is RTP or not.
I've made investigation of the X company's IP sniffer module and the JNetPcap library.

You can find here methods used in these software. All these methods don't give a 100% exact decision.
You can find RTP header description in specification of RTP protocol.

X company's IP sniffer module

Analysis is done inside method IsValid() of RtpPacket class.
A decision is taken on base check of the three parameters: RTP version, payload type and header length.
  • The payload type is checked if it belongs to a valid range of values from 34 (H263) to 96 (DynMin).
  • The header length check is a little more complex.
    IPP calculates the RTP header length on base value the CSRC count (field CC) and padding (field P). During this step IPP supposes that a current packet is a RTP packet.
    Then it calculates the data length as a difference between the total packet length and the calculated header length - DataLen = TotalPacketLen - HeaderLen.
    After that it compares calculated data length with total packet length.
    If data length is bigger than total length then it is not RTP.
  • RTP version must be 2.

Source code:

bool RtpPacket::IsValid()
{
    //check for correct version
   if (version != 2) return false;
  
   //check for a known payload type
   if (payloadType > rtpPayloadH263 && payloadType < rtpPayloadDynMin) return false;

   if (dataLen >= totalLength) return false;

   return true;
}

 JNetPcap

Analysis is done inside function validate_rtp() of packet_protocol.cpp file (line 143 for version 1.3.b3).
The decision is taken by using more parameters. However I am not sure that probability of the recognition is higher. Let's consider the algorithm.
  • It checks the RTP version. The version must be 2 also.
  • The CSRC count check is empty. The JNetPcap check only low boundary. It must be greater than 15. But CSRC count is 4 bits field and it can't be more 15.
  • The payload check is much simpler. It also checks only low boundary. It must be greater than 25.
  • Sequence number is checked for zero.
  • SSRC is checked for zero.
  • Timestamp is checked for zero.
  • All other fields if any are also checked for zero.
  • If a packet has extension then extension length is also checked for out of bounds.

JNetPcap source code:

int validate_rtp(scan_t *scan) {
    if ((scan->buf_len - scan->offset) < sizeof(rtp_t)) {
        return INVALID;
    }
    register rtp_t *rtp = (rtp_t *)(scan->buf + scan->offset);
    if (rtp->rtp_ver != 2 ||
            rtp->rtp_cc > 15 ||
            rtp->rtp_type > 25 ||
            rtp->rtp_seq == 0 ||
            rtp->rtp_ts == 0 ||
            rtp->rtp_ssrc == 0
            ) {
                TRACE("INVALID header flad");
                CALL(debug_rtp(rtp));
                EXIT();
                return INVALID;
    }
    uint32_t *c = (uint32_t *)(rtp + 1);
    for (int i = 0; i < rtp->rtp_cc; i ++) {
        TRACE("CSRC[%d]=0x%x", i, BIG_ENDIAN32(c[i]));
        if (BIG_ENDIAN32(c[i]) == 0) {
            TRACE("INVALID CSRC entry is 0");
            EXIT();
            return INVALID;
        }
        /*
         * Check for any duplicates CSRC ids within the table. Normally there
         * can't be any duplicates.
         */
        for (int j = i + 1; j < rtp->rtp_cc; j ++) {
            if (BIG_ENDIAN32(c[i]) == BIG_ENDIAN32(c[j])) {
                TRACE("INVALID duplicates CSRC entries");
                EXIT();
                return INVALID;
            }
        }
    }
    if (rtp->rtp_ext) {
        rtpx_t * rtpx = (rtpx_t *)(
                    scan->buf +
                    scan->offset +
                    RTP_LENGTH +
                    (rtp->rtp_cc * 4));
        register int xlen = BIG_ENDIAN16(rtpx->rtpx_len) * 4;
        if ((!SCAN_IS_FRAGMENT(scan) &&
                (scan->offset + xlen > scan->wire_len))    ||
                (xlen > 1500) ) {
                    TRACE("INVALID rtpx_len > %d bytes (wire_len) in extension header",
                            scan->wire_len);
                    EXIT();
                    return INVALID;
        }
    }
    TRACE("OK");
    EXIT();
    CALL(debug_rtp(rtp));
    return RTP_ID;
}

No comments:

Post a Comment