Get Table Statistics Information In POX

Monitoring table statistics is needed to evaluate a method whether it costs too much switch's resources or make a comparison between two methods. This post is shown how to get table statistics information such as active entries in switch.

Controller can query about datapath's current state using the OFPT_STATS_REQEST message. The structure of OFPT_STATS_REQUEST message is shown as following:

struct ofp_stats_request {  
    struct ofp_header header;
    uint16_t type; /* One of the OFPST_* constants. */
    uint16_t flags; /* OFPSF_REQ_* flags (none yet defined). */
    uint8_t body[0]; /* Body of the request. */
};
OFP_ASSERT(sizeof(struct ofp_stats_request) == 12);

The switch responds with one or more OFPT_STATS_REPLY messages:

struct ofp_stats_reply {  
    struct ofp_header header;
    uint16_t type; /* One of the OFPST_* constants. */
    uint16_t flags; /* OFPSF_REPLY_* flags. */
    uint8_t body[0]; /* Body of the reply. */
};
OFP_ASSERT(sizeof(struct ofp_stats_reply) == 12);

In both the request and response, the type field specifies the kind of information being passed and determines how the body field is interpreted:

enum ofp_stats_types {  
/* Description of this OpenFlow switch.
* The request body is empty.
* The reply body is struct ofp_desc_stats. */
OFPST_DESC,  
/* Individual flow statistics.
* The request body is struct ofp_flow_stats_request.
* The reply body is an array of struct ofp_flow_stats. */
OFPST_FLOW,  
/* Aggregate flow statistics.
* The request body is struct ofp_aggregate_stats_request.
* The reply body is struct ofp_aggregate_stats_reply. */
OFPST_AGGREGATE,  
/* Flow table statistics.
* The request body is empty.
* The reply body is an array of struct ofp_table_stats. */
OFPST_TABLE,  
/* Physical port statistics.
* The request body is struct ofp_port_stats_request.
* The reply body is an array of struct ofp_port_stats. */
OFPST_PORT,  
/* Queue statistics for a port
* The request body defines the port
* The reply body is an array of struct ofp_queue_stats */
OFPST_QUEUE,  
/* Vendor extension.
* The request and reply bodies begin with a 32-bit vendor ID, which takes
* the same form as in "struct ofp_vendor_header". The request and reply
* bodies are otherwise vendor-defined. */
OFPST_VENDOR = 0xffff  
};

Since we want to get the table statistics information from switch, we need to send OFPT_STATS_REQUEST packet with type field OFPST_TABLE.

The body of the reply consists of an array of the following:

/* Body of reply to OFPST_TABLE request. */
    struct ofp_table_stats {
    uint8_t table_id; /* Identifier of table. Lower numbered tables are consulted first. */
    uint8_t pad[3]; /* Align to 32-bits. */
    char name[OFP_MAX_TABLE_NAME_LEN];
    uint32_t wildcards; /* Bitmap of OFPFW_* wildcards that are supported by the table. */
    uint32_t max_entries; /* Max number of entries supported. */
    uint32_t active_count; /* Number of active entries. */
    uint64_t lookup_count; /* Number of packets looked up in table. */
    uint64_t matched_count; /* Number of packets that hit table. */
};
OFP_ASSERT(sizeof(struct ofp_table_stats) == 64);  

According to the body of reply message, following information about flow table in switch can be obtained.

Max number of entries supported  
Number of active entries  
Number of packets looked up in table  
Number of packets that hit table  

The following code is an example for monitoring active entries in switches based on the topology mentioned in Create a custom topology in Mininet (2).

from pox.core import core  
import pox.openflow.libopenflow_01 as of  
from pox.lib.revent import *  
from pox.lib.recoco import Timer  
from collections import defaultdict  
from pox.openflow.discovery import Discovery  
from pox.lib.util import dpid_to_str  
import time

class tableStats(EventMixin):  
  def __init__(self,interval = 10):
    self.tableActiveCount = {}
    self.interval = interval
    core.openflow.addListeners(self)
  def _handle_ConnectionUp(self,event):
    print "Switch %s has connected" %event.dpid
    self.sendTableStatsRequest(event)
  def _handle_TableStatsReceived(self,event):
    sw = 's%s'%event.dpid
    self.tableActiveCount[sw] = event.stats[0].active_count
    print "TableStatsReceived"
    print self.tableActiveCount
    Timer(self.interval, self.sendTableStatsRequest,args=[event])
  def sendTableStatsRequest(self, event):
    sr = of.ofp_stats_request()
    sr.type = of.OFPST_TABLE
    event.connection.send(sr)
    print "Send table stat message to Switch %s " %event.dpid

def launch(interval = '10'):  
  interval = int(interval)
  core.registerNew(tableStats,interval)

The above codes send table statistic request once controller and switches get connected. And when controller receives TableStatsReceived event, controller handles this event and sends table statistic request again in a interval (default is 10 seconds).

This program can be run by input

sudo ./pox/pox.py tableStat --interval=1

and the output is a dictionary like

{'s3': 0, 's2': 0, 's1': 0, 's6': 0, 's5': 0, 's4': 0}

References:
OpenFlow Switch Specification Version1.0.0
POX WIKI - Requesting statistics from switches