Dissecting OpenDPI (BGP)

A Short Introduction

Welcome back! Last week we covered the elusive LDAP, so this week is going to be a bit more shallow — we’ll cover the Boarder Gateway Protocol (BGP) as defined by RFC 4271 and RFC 2918. Don’t worry about the protocol and its purpose too much, because the DPI part is quite easy and distinctive. As always, the code presented is part of a new DPI library, which will be made available via a BSD/ISC license. Ready? Ok, here we go.

OpenDPI Does a Good Job

The code isn’t long since BGP has a well-defined static header structure to wrap its private messages.

if (packet->payload_packet_len > 18 &&
    get_u64(packet->payload, 0) == 0xffffffffffffffffULL &&
    get_u64(packet->payload, 8) == 0xffffffffffffffffULL &&

BGP starts off with 128 enabled bits. The header is at least 19 bytes long. Great work so far!

    ntohs(get_u16(packet->payload, 16)) <= packet->payload_packet_len &&
    (packet->tcp->dest == htons(179) || packet->tcp->source == htons(179)) &&

The message length is pulled from the packet and tested against the actual segment length present in the packet. It is verified that the header length is included in the message length. And then port numbers, really? Although it’s a good indicator of BGP, port numbers should be the very last resort in modern DPI code. Last bit is interesting for the message type:

    packet->payload[18] < 5) {

Message types defined are 1 to 4 plus an additional 5, so the test fails to address that zero is not a valid type and forgets about 5 completely. But I liked the effort -- the RFCs have been consulted to some degree.

Just a Little Further Please

So the goals for me would be to better visualise the BGP header (via a struct), change the program flow (more if directives don't hurt), and to properly validate the message types. All input is welcome. Use the comment functionality below...

LI_DESCRIBE_APP(bgp)
{
	struct bgp {
		uint32_t marker[4];
		uint16_t msg_length;
		uint8_t msg_type;
	} __packed *ptr = (void *)packet->app.raw;

	if (packet->app_len < sizeof(struct bgp)) {
		return (0);
	}

	if (memcmp(ptr->marker, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
	    "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", sizeof(ptr->marker))) {
		/* marker is filled with ones */
		return (0);
	}

	if (be16dec(&ptr->msg_length) < sizeof(struct bgp)) {
		/* total length must include header length */
		return (0);
	}

	switch (ptr->msg_type) {
	case 1:		/* Open		RFC 4271 */
	case 2:		/* Update	RFC 4271 */
	case 3:		/* Notification	RFC 4271 */
	case 4:		/* KeepAlive	RFC 4271 */
	case 5:		/* RouteRefresh	RFC 2918 */
		break;
	default:	/* not a known type */
		return (0);
	}

	return (1);
}

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.