analysis/trace_filter.hpp (357 lines of code) (raw):
#ifndef TRACE_FILTER_HPP
#define TRACE_FILTER_HPP
#include <vector>
#include <stdint.h>
#include <cctype>
#include <regex>
#include <sstream>
#include "trace-format.h"
class TraceFilter{
public:
/**********************************
* classes for test a single field
**********************************/
class Field{
public:
uint32_t offset; // data offset in TraceFormat
uint8_t op;
Field(uint32_t _offset, std::string &_op){
offset = _offset;
if (_op == "=")
op = 0;
else if (_op == ">")
op = 1;
else if (_op == ">=")
op = 2;
else if (_op == "<")
op = 3;
else if (_op == "<=")
op = 4;
else if (_op == "!=")
op = 5;
else
op = 255;
}
std::string op_str(){
if (op == 0)
return "=";
if (op == 1)
return ">";
if (op == 2)
return ">=";
if (op == 3)
return "<";
if (op == 4)
return "<=";
if (op == 5)
return "!=";
return "[Unknown op]";
}
virtual bool test(ns3::TraceFormat &tr) = 0;
virtual std::string str() = 0;
};
#define OP(type) \
do {\
switch (op){\
case 0: return *(type*)(((uint8_t*)&tr) + offset) == value;\
case 1: return *(type*)(((uint8_t*)&tr) + offset) > value;\
case 2: return *(type*)(((uint8_t*)&tr) + offset) >= value;\
case 3: return *(type*)(((uint8_t*)&tr) + offset) < value;\
case 4: return *(type*)(((uint8_t*)&tr) + offset) <= value;\
case 5: return *(type*)(((uint8_t*)&tr) + offset) != value;\
default: return false;\
}\
} while(0)
class ByteField : public Field{
public:
uint8_t value;
ByteField(uint32_t _offset, std::string &op, uint8_t _value) : Field(_offset, op), value(_value) {}
virtual bool test(ns3::TraceFormat &tr){
OP(uint8_t);
}
std::string str(){
std::stringstream s;
s << '[' << offset << ']' << op_str() << (int32_t)value;
return s.str();
}
};
class WordField : public Field{
public:
uint16_t value;
WordField(uint32_t _offset, std::string &op, uint16_t _value) : Field(_offset, op), value(_value) {}
virtual bool test(ns3::TraceFormat &tr){
OP(uint16_t);
}
std::string str(){
std::stringstream s;
s << '[' << offset << ']' << op_str() << value;
return s.str();
}
};
class DwordField : public Field{
public:
uint32_t value;
DwordField(uint32_t _offset, std::string &op, uint32_t _value) : Field(_offset, op), value(_value) {}
virtual bool test(ns3::TraceFormat &tr){
OP(uint32_t);
}
std::string str(){
std::stringstream s;
s << '[' << offset << ']' << op_str() << value;
return s.str();
}
};
class QwordField : public Field{
public:
uint64_t value;
QwordField(uint32_t _offset, std::string &op, uint64_t _value) : Field(_offset, op), value(_value) {}
virtual bool test(ns3::TraceFormat &tr){
OP(uint64_t);
}
std::string str(){
std::stringstream s;
s << '[' << offset << ']' << op_str() << value;
return s.str();
}
};
class Node{
public:
uint32_t type; // node type: 0:expr, 1:&, 2:|
Node* son[2];
Field* f;
Node(){
son[0] = son[1] = 0;
f = 0;
type = 0;
}
void set_op(std::string op){
if (op == "&")
type = 1;
else if (op == "|")
type = 2;
}
bool test(ns3::TraceFormat &tr){
if (type == 0){
//printf("test %s\n", f->str().c_str());
return f->test(tr);
}
if (type == 1)
return son[0]->test(tr) && son[1]->test(tr);
if (type == 2)
return son[0]->test(tr) || son[1]->test(tr);
return false;
}
void clear(){
if (son[0]){
son[0]->clear();
delete son[0];
}
if (son[1]){
son[1]->clear();
delete son[1];
}
if (f)
delete f;
}
};
/***************
* members
***************/
Node* root;
/******************
* methods
*****************/
TraceFilter() : root(NULL){}
// test a trace if it passes the filter
bool test(ns3::TraceFormat &tr){
if (root)
return root->test(tr);
return true;
}
// parse an filter expression
void parse(std::string expr){
root = _parse(expr);
}
// helper function: skip the spaces from idx i of str
static void skip_space(uint32_t &i, const std::string &str){
while (i < str.size() && isspace(str[i]))
i++;
}
// helper function: get a Field object from `field` `op` `value`
Field* GetField(std::string field, std::string op, std::string value){
Field *f = NULL;
uint64_t v;
sscanf(value.c_str(), "%li", &v);
if (field == "time"){
f = new QwordField(offsetof(ns3::TraceFormat, time), op, v);
}else if (field == "node"){
f = new WordField(offsetof(ns3::TraceFormat, node), op, v);
}else if (field == "nodeType"){
f = new ByteField(offsetof(ns3::TraceFormat, nodeType), op, v);
}else if (field == "intf"){
f = new ByteField(offsetof(ns3::TraceFormat, intf), op, v);
}else if (field == "qidx"){
f = new ByteField(offsetof(ns3::TraceFormat, qidx), op, v);
}else if (field == "qlen"){
f = new DwordField(offsetof(ns3::TraceFormat, qlen), op, v);
}else if (field == "sip"){
f = new DwordField(offsetof(ns3::TraceFormat, sip), op, v);
}else if (field == "dip"){
f = new DwordField(offsetof(ns3::TraceFormat, dip), op, v);
}else if (field == "size"){
f = new WordField(offsetof(ns3::TraceFormat, size), op, v);
}else if (field == "l3Prot"){
f = new ByteField(offsetof(ns3::TraceFormat, l3Prot), op, v);
}else if (field == "event"){
f = new ByteField(offsetof(ns3::TraceFormat, event), op, v);
}else if (field == "ecn"){
f = new ByteField(offsetof(ns3::TraceFormat, ecn), op, v);
}else if (field == "data.sport"){
f = new WordField(offsetof(ns3::TraceFormat, data.sport), op, v);
}else if (field == "data.dport"){
f = new WordField(offsetof(ns3::TraceFormat, data.dport), op, v);
}else if (field == "data.seq"){
f = new DwordField(offsetof(ns3::TraceFormat, data.seq), op, v);
}else if (field == "ack.sport"){
f = new WordField(offsetof(ns3::TraceFormat, ack.sport), op, v);
}else if (field == "ack.dport"){
f = new WordField(offsetof(ns3::TraceFormat, ack.dport), op, v);
}else if (field == "ack.flags"){
f = new ByteField(offsetof(ns3::TraceFormat, ack.flags), op, v);
}else if (field == "qp.sport"){
f = new WordField(offsetof(ns3::TraceFormat, qp.sport), op, v);
}else if (field == "qp.dport"){
f = new WordField(offsetof(ns3::TraceFormat, qp.dport), op, v);
}
return f;
}
#define OP_COMPARE "=|>|>=|<|<=|!="
#define BASE_EXPR_REGEX "\\s*([a-zA-Z0-9\\.]+)\\s*(" OP_COMPARE")\\s*([x,[:xdigit:]]+)\\s*"
// the real implementation of parse, allows recursion
Node* _parse(std::string expr){
expr = strip_outer_bracket(expr);
Node* res = NULL;
std::smatch m;
if (std::regex_match(expr, m, std::regex(BASE_EXPR_REGEX))){ // a base expression
std::string field = m[1].str();
std::string op = m[2].str();
std::string value = m[3].str();
Field *f = GetField(field, op, value);
if (f){
res = new Node();
res->f = f;
res->type = 0;
}else{ // maybe this is a short hand
res = parse_shorthand(field, op, value);
}
}else {
Node *left = NULL;
std::string op_str, right_str;
if (std::regex_match(expr, m, std::regex(BASE_EXPR_REGEX"(&|\\|)(.*)"))){ // a base express &| other things
// get left
std::string field = m[1].str();
std::string op = m[2].str();
std::string value = m[3].str();
Field *f = GetField(field, op, value);
if (f){
left = new Node();
left->f = f;
}else { // maybe this is a short hand
left = parse_shorthand(field, op, value);
if (left == NULL)
return NULL;
}
// assign right str
right_str = m[5].str();
op_str = m[4].str();
}else { // (base expression) &| other things
uint32_t start, i = 0;
// get left
skip_space(i, expr);
if (i >= expr.size())
return NULL;
start = i;
if (expr[i] == '('){
// find matching brackets
uint32_t c = 1;
for (i++; i < expr.size() && (expr[i] != ')' || c > 1); i++){
if (expr[i] == '(')
c++;
else if (expr[i] == ')')
c--;
}
if (i >= expr.size())
return NULL;
i++;
left = _parse(expr.substr(start + 1, i - start - 2));
}
if (!left)
return NULL;
// assign right str
std::string s = expr.substr(i, expr.size() - i);
if (std::regex_match(s, m, std::regex("\\s*(&|\\|)(.*)\\s*"))){
right_str = m[2].str();
op_str = m[1].str();
}
}
// get right
Node *right = _parse(right_str);
if (right){
res = new Node;
res->son[0] = left;
res->son[1] = right;
res->set_op(op_str);
}else {
left->clear();
delete left;
}
}
return res;
}
std::string strip_outer_bracket(std::string expr){
uint32_t i = 0, start, end;
skip_space(i, expr);
// if begin with '('
if (expr[i] == '('){
start = i+1;
uint32_t c = 1;
for (i++; i < expr.size() && (expr[i] != ')' || c > 1); i++){
if (expr[i] == '(')
c++;
else if (expr[i] == ')')
c--;
}
// if cannot find matching ')', return original
if (i >= expr.size())
return expr;
// matching ')' is at i
end = i;
// skip the spaces, see if we can reach the end of the string
i++;
skip_space(i, expr);
// rest of the string are spaces, this is a pair of outer bracket
if (i >= expr.size())
return strip_outer_bracket(expr.substr(start, end-start)); // recursively strip brackets
}
return expr;
}
Node* parse_shorthand(std::string shorthand, std::string op, std::string value){
if (shorthand == "flow" || shorthand == "biflow" || shorthand == "rflow"){ // forward flow, bi-directional flow, reverse flow
// parse 4-tuples here
uint32_t sip, dip;
uint16_t sport, dport;
if (op != "=") // using flow shorthand, must use '='
return NULL;
if (sscanf(value.c_str(), "%i,%i,%hu,%hu", &sip, &dip, &sport, &dport) == 4){
char buf[512];
if (shorthand == "flow"){
sprintf(buf, "sip=%u&dip=%u&((l3Prot=17&data.sport=%hu&data.dport=%hu)|((l3Prot=0xFC|l3Prot=0xFD)&ack.sport=%hu&ack.dport=%hu)|(l3Prot=0x0&qp.sport=%hu&qp.dport=%hu))", sip, dip, sport, dport, sport, dport, sport, dport);
return _parse(buf);
}else if (shorthand == "biflow"){
sprintf(buf, "(sip=%u&dip=%u&((l3Prot=17&data.sport=%hu&data.dport=%hu)|((l3Prot=0xFC|l3Prot=0xFD)&ack.sport=%hu&ack.dport=%hu)|(l3Prot=0x0&qp.sport=%hu&qp.dport=%hu)))|(sip=%u&dip=%u&((l3Prot=17&data.sport=%hu&data.dport=%hu)|((l3Prot=0xFC|l3Prot=0xFD)&ack.sport=%hu&ack.dport=%hu)|(l3Prot=0x0&qp.sport=%hu&qp.dport=%hu)))", sip, dip, sport, dport, sport, dport, sport, dport, dip, sip, dport, sport, dport, sport, dport, sport);
return _parse(buf);
}else if (shorthand == "rflow"){
sprintf(buf, "sip=%u&dip=%u&((l3Prot=17&data.sport=%hu&data.dport=%hu)|((l3Prot=0xFC|l3Prot=0xFD)&ack.sport=%hu&ack.dport=%hu)|(l3Prot=0x0&qp.sport=%hu&qp.dport=%hu))", dip, sip, dport, sport, dport, sport, dport, sport);
return _parse(buf);
}
}
}else if (shorthand == "queue"){
// parse "node,intf,qidx"
uint16_t node;
uint8_t intf, qidx;
if (op != "=") // using queue shorthand, must use '='
return NULL;
if (sscanf(value.c_str(), "%hu,%hhu,%hhu", &node, &intf,&qidx) == 3){
char buf[512];
sprintf(buf, "node=%u&intf=%u&qidx=%u", node, intf, qidx);
return _parse(buf);
}
}
return NULL;
}
// print the expression
std::string str(){
return str(root);
}
std::string str(Node* n){
if (n == NULL)
return "";
if (n->type == 0)
return n->f->str();
return '(' + str(n->son[0]) + ')' + (n->type == 1? '&' : '|') + '(' + str(n->son[1]) + ')';
}
#undef OP_COMPARE
#undef BASE_EXPR_REGEX
};
#endif /* TRACE_FILTER_HPP */