Commit a80439c7 authored by Tom Barbette's avatar Tom Barbette

GTP elements for SuperFluidity project

parent 1742667d
/*
* gtpdecap.{cc,hh} -- element encapsulates packet in UDP/IP header
* Benjie Chen, Eddie Kohler
* gtpdecap.{cc,hh}
* Tom Barbette
*
* Copyright (c) 1999-2000 Massachusetts Institute of Technology
* Copyright (c) 2007 Regents of the University of California
* Copyright (c) 2018 University of Liege
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
......
/*
* gtpencap.{cc,hh} -- element encapsulates packet in UDP/IP header
* Benjie Chen, Eddie Kohler
* gtpencap.{cc,hh}
* Tom Barbette
*
* Copyright (c) 1999-2000 Massachusetts Institute of Technology
* Copyright (c) 2007 Regents of the University of California
* Copyright (c) 2018 University of Liege
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
......@@ -24,6 +23,7 @@
#include <click/standard/alignmentinfo.hh>
#include "gtpencap.hh"
CLICK_DECLS
GTPEncap::GTPEncap()
......
/*
* gtpfilter.{cc,hh} -- GTP Filtering element
* Tom Barbette
*
* Copyright (c) 2018 University of Liege
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, subject to the conditions
* listed in the Click LICENSE file. These conditions include: you must
* preserve this copyright notice, and you cannot mention the copyright
* holders in advertising related to the Software without their permission.
* The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
* notice is a summary of the Click LICENSE file; the license in that file is
* legally binding.
*/
#include <click/config.h>
#include <clicknet/ip.h>
#include <clicknet/udp.h>
#include <click/args.hh>
#include <click/error.hh>
#include <click/glue.hh>
#include "gtpfilter.hh"
CLICK_DECLS
GTPFilter::GTPFilter()
{
}
GTPFilter::~GTPFilter()
{
}
int
GTPFilter::configure(Vector<String> &conf, ErrorHandler *errh)
{
if (Args(conf, this, errh)
.complete() < 0)
return -1;
return 0;
}
Packet *
GTPFilter::simple_action(Packet *p_in)
{
Packet* p = p_in;
const click_ip *ip = reinterpret_cast<const click_ip *>(p->data());
const click_udp *udp = reinterpret_cast<const click_udp *>(p->data() + sizeof(click_ip));
const click_gtp *gtp = reinterpret_cast<const click_gtp *>(p->data() + sizeof(click_ip) + sizeof(click_udp));
//GTPTuple gtp_id(IP6Address(ip->ip_src), IPAddress(ip->ip_dst), ntohs(udp->uh_sport), ntohl(gtp->gtp_teid));
return p;
}
#if HAVE_BATCH
PacketBatch*
GTPFilter::simple_action_batch(PacketBatch* batch) {
EXECUTE_FOR_EACH_PACKET(GTPFilter::simple_action,batch);
return batch;
}
#endif
String GTPFilter::read_handler(Element *e, void *thunk)
{
GTPFilter *u = static_cast<GTPFilter *>(e);
switch ((uintptr_t) thunk) {
default:
return String();
}
}
void GTPFilter::add_handlers()
{
}
CLICK_ENDDECLS
EXPORT_ELEMENT(GTPFilter)
ELEMENT_MT_SAFE(GTPFilter)
#ifndef CLICK_GTPFilter_HH
#define CLICK_GTPFilter_HH
#include <click/batchelement.hh>
#include <click/glue.hh>
#include <clicknet/gtp.h>
CLICK_DECLS
/*
=c
GTPFilter(TEID eid)
=s gtp
=d
*/
class GTPFilter : public BatchElement { public:
GTPFilter() CLICK_COLD;
~GTPFilter() CLICK_COLD;
const char *class_name() const { return "GTPFilter"; }
const char *port_count() const { return PORTS_1_1; }
const char *flags() const { return "A"; }
int configure(Vector<String> &, ErrorHandler *) CLICK_COLD;
bool can_live_reconfigure() const { return true; }
void add_handlers() CLICK_COLD;
Packet *simple_action(Packet *);
#if HAVE_BATCH
PacketBatch* simple_action_batch(PacketBatch *);
#endif
private:
static String read_handler(Element *, void *) CLICK_COLD;
};
CLICK_ENDDECLS
#endif
/*
* GTPLookup.{cc,hh}
* Tom Barbette
*
* Copyright (c) 2018 University of Liege
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, subject to the conditions
* listed in the Click LICENSE file. These conditions include: you must
* preserve this copyright notice, and you cannot mention the copyright
* holders in advertising related to the Software without their permission.
* The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
* notice is a summary of the Click LICENSE file; the license in that file is
* legally binding.
*/
#include <click/config.h>
#include <clicknet/gtp.h>
#include <clicknet/icmp.h>
#include <clicknet/udp.h>
#include <clicknet/ip.h>
#include <click/args.hh>
#include <click/error.hh>
#include <click/glue.hh>
#include "gtplookup.hh"
#include "gtptable.hh"
CLICK_DECLS
GTPLookup::GTPLookup()
{
}
GTPLookup::~GTPLookup()
{
}
int
GTPLookup::configure(Vector<String> &conf, ErrorHandler *errh)
{
Element* e;
if (Args(conf, this, errh)
.read_mp("TABLE",e)
.complete() < 0)
return -1;
if (!e || !e->cast("GTPTable"))
return errh->error("Unknown GTPTable");
_table = static_cast<GTPTable*>(e);
return 0;
}
int
GTPLookup::process(int port, Packet* p_in) {
//click_jiffies_t now = click_jiffies();
IPFlowID inner(p_in);
if (p_in->ip_header()->ip_p != 17 && p_in->ip_header()->ip_p != 6) {
inner.set_dport(0);
inner.set_sport(0);
}
auto gtp_tunnel = _table->_inmap.find(inner);
if (!gtp_tunnel) {
click_chatter("UNKNOWN PACKETS FROM TOF !!?");
click_chatter("%s",inner.unparse().c_str());
return -1;
} else {
//gtp_tunnel->last_seen = now;
if (likely(gtp_tunnel->known)) { //This is the GTP_OUT directly
} else { //This is the GTP_IN, we must resolve and update
auto gtp_out = _table->_gtpmap.find(*gtp_tunnel);
if (!gtp_out) {
click_chatter("Mapping is still unknown ! Dropping packets. Choose a closer ping server...");
return -1;
}
*gtp_tunnel = *gtp_out;
gtp_tunnel->known = true;
}
WritablePacket *p = p_in->push(sizeof(click_gtp) + sizeof(click_udp) + sizeof(click_ip));
click_gtp *gtp = reinterpret_cast<click_gtp *>(p->data() + sizeof(click_udp) + sizeof(click_ip));
gtp->gtp_v = 1;
gtp->gtp_pt = 1;
gtp->gtp_reserved = 0;
gtp->gtp_flags = 0;
gtp->gtp_msg_type = 0xff;
gtp->gtp_msg_len = htons(p->length() - sizeof(click_gtp));
gtp->gtp_teid = htonl(gtp_tunnel->gtp_id);
click_ip *ip = reinterpret_cast<click_ip *>(p->data());
click_udp *udp = reinterpret_cast<click_udp *>(ip + 1);
// set up IP header
ip->ip_v = 4;
ip->ip_hl = sizeof(click_ip) >> 2;
ip->ip_len = htons(p->length());
ip->ip_id = htons(_id.fetch_and_add(1));
ip->ip_p = IP_PROTO_UDP;
ip->ip_src = gtp_tunnel->ip_id.saddr();
ip->ip_dst = gtp_tunnel->ip_id.daddr();
p->set_dst_ip_anno(gtp_tunnel->ip_id.daddr());
ip->ip_tos = 0;
ip->ip_off = 0;
ip->ip_ttl = 250;
ip->ip_sum = 0;
#if HAVE_FAST_CHECKSUM && FAST_CHECKSUM_ALIGNED
if (_aligned)
ip->ip_sum = ip_fast_csum((unsigned char *)ip, sizeof(click_ip) >> 2);
else
ip->ip_sum = click_in_cksum((unsigned char *)ip, sizeof(click_ip));
#elif HAVE_FAST_CHECKSUM
ip->ip_sum = ip_fast_csum((unsigned char *)ip, sizeof(click_ip) >> 2);
#else
ip->ip_sum = click_in_cksum((unsigned char *)ip, sizeof(click_ip));
#endif
p->set_ip_header(ip, sizeof(click_ip));
// set up UDP header
udp->uh_sport = gtp_tunnel->ip_id.sport();
udp->uh_dport = gtp_tunnel->ip_id.dport();
uint16_t len = p->length() - sizeof(click_ip);
udp->uh_ulen = htons(len);
udp->uh_sum = 0;
unsigned csum = click_in_cksum((unsigned char *)udp, len);
udp->uh_sum = click_in_cksum_pseudohdr(csum, ip, len);
return 0;
}
}
void
GTPLookup::push(int port, Packet *p)
{
int o = process(port,p);
checked_output_push(o, p);
}
#if HAVE_BATCH
void
GTPLookup::push_batch(int port, PacketBatch* batch) {
auto fnt = [this,port](Packet*p) {
return process(port,p);
};
CLASSIFY_EACH_PACKET(2,fnt,batch,checked_output_push_batch);
return;
}
#endif
CLICK_ENDDECLS
EXPORT_ELEMENT(GTPLookup)
#ifndef CLICK_GTPLookup_HH
#define CLICK_GTPLookup_HH
#include <click/batchelement.hh>
#include <click/ipflowid.hh>
#include <click/hashtablemp.hh>
CLICK_DECLS
/*
=c
GTPLookup()
=s gtp
decapsulate the GTP packet and set the GTP TEID in the aggregate annotation
=d
=a GTPEncap
*/
class GTPTable;
class GTPLookup : public BatchElement { public:
GTPLookup() CLICK_COLD;
~GTPLookup() CLICK_COLD;
const char *class_name() const { return "GTPLookup"; }
const char *port_count() const { return "1/1"; }
const char *flow_code() const { return "x/x"; }
const char *flags() const { return PUSH; }
int configure(Vector<String> &, ErrorHandler *) CLICK_COLD;
bool can_live_reconfigure() const { return true; }
int process(int, Packet*);
void push(int, Packet *);
#if HAVE_BATCH
void push_batch(int port, PacketBatch *);
#endif
private:
GTPTable *_table;
atomic_uint32_t _id;
};
CLICK_ENDDECLS
#endif
/*
* GTPTable.{cc,hh}
* Tom Barbette
*
* Copyright (c) 2018 University of Liege
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, subject to the conditions
* listed in the Click LICENSE file. These conditions include: you must
* preserve this copyright notice, and you cannot mention the copyright
* holders in advertising related to the Software without their permission.
* The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
* notice is a summary of the Click LICENSE file; the license in that file is
* legally binding.
*/
#include <click/config.h>
#include <clicknet/gtp.h>
#include <clicknet/icmp.h>
#include <clicknet/udp.h>
#include <clicknet/ip.h>
#include <click/args.hh>
#include <click/error.hh>
#include <click/glue.hh>
#include "gtptable.hh"
CLICK_DECLS
GTPTable::GTPTable() : _verbose(true)
{
}
GTPTable::~GTPTable()
{
}
int
GTPTable::configure(Vector<String> &conf, ErrorHandler *errh)
{
uint32_t eid;
if (Args(conf, this, errh)
.read_mp("PING_DST",_ping_dst)
.complete() < 0)
return -1;
return 0;
}
int
GTPTable::process(int port, Packet* p) {
click_jiffies_t now = click_jiffies();
if (port == 0) {
const click_ip *ip = reinterpret_cast<const click_ip *>(p->data());
unsigned hlen = ip->ip_hl << 2;
//const click_udp *udp = reinterpret_cast<const click_udp *>(p->data() + hlen);
const click_gtp *gtp = reinterpret_cast<const click_gtp *>(p->data() + 28);
GTPFlowID gtp_in = GTPFlowID(IPFlowID(p),ntohl(gtp->gtp_teid));
int sz = sizeof(click_gtp);
if (gtp->gtp_flags)
sz += 4;
sz+= hlen + sizeof(click_udp);
bool known;
{//Block to protect hashtable pointer scope
GTPFlowTable::write_ptr gtp_out = _gtpmap.find_write(gtp_in);
if (!gtp_out) {
known = false;
if (_verbose)
click_chatter("Unknown, emitting ping FOR TEID %u",gtp_in.gtp_id);
uint32_t gen = click_random();
uint16_t icmp_id = gen + 3;
uint16_t icmp_seq = gen >> 16 + 3;
//size_t hsz = sizeof(click_ip) + sizeof(click_icmp_echo);
size_t data_size = 60;
WritablePacket* q = Packet::make(sz + data_size);
if (q) {
//Craft the ICMP PING
memcpy(q->data(), p->data(), sz);
click_ip* oip = (click_ip*)q->data();
oip->ip_len = ntohs(q->length());
oip->ip_sum = 0;
oip->ip_sum = click_in_cksum((unsigned char *)oip, hlen);
click_udp *oudph = (click_udp*)(q->data() + sizeof(click_ip));
int len = q->length() - sizeof(click_ip);
oudph->uh_ulen = htons(len);
oudph->uh_sum = 0;
//unsigned csum = click_in_cksum((unsigned char *)oudph, len);
//oudph->uh_sum = click_in_cksum_pseudohdr(csum, oip, len);
q->pull(sz);
memset(q->data(), '\0', data_size);
click_ip *nip = reinterpret_cast<click_ip *>(q->data());
nip->ip_v = 4;
nip->ip_hl = sizeof(click_ip) >> 2;
nip->ip_len = htons(q->length());
uint16_t ip_id = (gen % 0xFFFF) + 1; // ensure ip_id != 0
nip->ip_id = htons(ip_id);
nip->ip_p = IP_PROTO_ICMP; /* icmp */
nip->ip_ttl = 200;
nip->ip_src = IPAddress(((click_ip*)(p->data() + sz))->ip_src);
nip->ip_dst = _ping_dst;
nip->ip_sum = click_in_cksum((unsigned char *)nip, sizeof(click_ip));
click_icmp_echo *icp = (struct click_icmp_echo *) (nip + 1);
icp->icmp_type = ICMP_ECHO;
icp->icmp_code = 0;
icp->icmp_identifier = icmp_id;
icp->icmp_sequence = icmp_seq;
icp->icmp_cksum = click_in_cksum((const unsigned char *)icp, data_size - sizeof(click_ip));
q->set_dst_ip_anno(IPAddress(_ping_dst));
q->set_ip_header(nip, sizeof(click_ip));
ICMPFlowID icmpflowid(q);
if (_verbose)
click_chatter("Adding ICMP MAP %s %s %u %u FOR TEID %u",
icmpflowid.saddr().unparse().c_str(),
icmpflowid.daddr().unparse().c_str(),
icmpflowid.id(),
icmpflowid.seq(),gtp_in.gtp_id);
_icmp_map.find_insert(icmpflowid,gtp_in);
q = q->push(sz);
oip = (click_ip*)q->data();
if (in_batch_mode)
checked_output_push_batch(1,PacketBatch::make_from_packet(q));
else
output_push(1,q);
}
} else { //Else flow is known and has a mapping
gtp_out->last_seen = now;
gtp_out.release();
if (_verbose)
click_chatter("Already seen GTP!");
known = true;
}
}
//Pull packet to inner header
p->pull(sz);
const click_ip *innerip = reinterpret_cast<const click_ip *>(p->data());
unsigned innerhlen = innerip->ip_hl << 2;
p->set_ip_header((click_ip*)p->data(), innerhlen);
IPFlowID inner(p,true);
if (innerip->ip_p != 17 && innerip->ip_p != 6) {
inner.set_dport(0);
inner.set_sport(0);
}
//Now add a reverse mapping to the REV 4 tupple -> GTP IN or OUT if known
{
if (_verbose) {
click_chatter("Setting INNER mapping for TEID %u",gtp_in.gtp_id);
click_chatter("%s",inner.unparse().c_str());
}
INMap::ptr gtp_ptr = _inmap.find_insert(inner,GTPFlowIDMAP(gtp_in));
gtp_ptr->last_seen = now;
/*if (known && !gtp_ptr->known) {
if (_verbose)
click_chatter("Inner mapping is now known !");
*gtp_ptr = *_gtpmap.find(gtp_in);
gtp_ptr->known = true; //Inner mapping is now mapping the OUT directly !
}*/
gtp_ptr.release();
}
return 0;
} else {
const click_gtp *gtp = reinterpret_cast<const click_gtp *>(p->data() + 28);
GTPFlowID gtp_out = GTPFlowID(IPFlowID(p,false),ntohl(gtp->gtp_teid));
click_chatter("PING RECEIVED, TEID %u",gtp_out.gtp_id);
GTPFlowID gtp_in;
const click_ip* innerip = reinterpret_cast<const click_ip*>(gtp + 1);
ICMPFlowID icmpflowid(innerip,true);
if (_verbose)
click_chatter("Searching ICMP MAP %s %s %u %u FOR TEID OUT %u",
icmpflowid.saddr().unparse().c_str(),
icmpflowid.daddr().unparse().c_str(),
icmpflowid.id(),
icmpflowid.seq(),gtp_out.gtp_id);
if (!_icmp_map.find_remove(icmpflowid,gtp_in)) {
click_chatter("Unknown FLOW ! Dropping packet");
return -1;
}
if (_verbose) {
click_chatter("PING RECEIVED, IN TEID %u",gtp_in.gtp_id);
click_chatter("PING RECEIVED, ADDING MAP TEID %u->%u",gtp_in.gtp_id, gtp_out.gtp_id);
}
_gtpmap.set(gtp_in,gtp_out);
assert(*_gtpmap.find(gtp_in) == gtp_out);
//Delete the packet
return -1;
}
}
void
GTPTable::push(int port, Packet *p)
{
int o = process(port,p);
if (o == 3) //If 3, we just do nothing (packet is waiting in list)
return;
checked_output_push(o, p);
}
#if HAVE_BATCH
void
GTPTable::push_batch(int port, PacketBatch* batch) {
auto fnt = [this,port](Packet*p) {
return process(port,p);
};
CLASSIFY_EACH_PACKET(4,fnt,batch,[this](int o, PacketBatch*batch){if (o == 3) return;checked_output_push_batch(o,batch);});
return;
}
#endif
CLICK_ENDDECLS
EXPORT_ELEMENT(GTPTable)
#ifndef CLICK_GTPTable_HH
#define CLICK_GTPTable_HH
#include <click/batchelement.hh>
#include <click/ipflowid.hh>
#include <click/icmpflowid.hh>
#include <click/hashtablemp.hh>
CLICK_DECLS
class GTPFlowID {public:
IPFlowID ip_id;
uint32_t gtp_id;
inline GTPFlowID() : ip_id(), gtp_id(0) {
}
inline GTPFlowID(IPFlowID ip, uint32_t gtp) : ip_id(ip), gtp_id(gtp){
}
/** @brief Hash function.
* @return The hash value of this IPFlowID.
*
* Equal IPFlowID objects always have equal hashcode() values. */
inline hashcode_t hashcode() const;
inline bool operator==(const GTPFlowID &o) const{
return o.ip_id == ip_id && o.gtp_id == gtp_id;
}
};
inline hashcode_t GTPFlowID::hashcode() const
{
// more complicated hashcode, but causes less collision
hashcode_t hash = ip_id.hashcode();
hash ^= gtp_id;
return hash;
}
class GTPFlowIDMAP : public GTPFlowID{public:
click_jiffies_t last_seen;
bool known;
inline GTPFlowIDMAP() : GTPFlowID(), last_seen(0),known(false) {
}
inline GTPFlowIDMAP(GTPFlowID id) : GTPFlowID(id), last_seen(0),known(false) {
}
};
/*
=c
GTPTable()
=s gtp
decapsulate the GTP packet and set the GTP TEID in the aggregate annotation
=d
=a GTPEncap
*/
class GTPLookup;
class GTPTable : public BatchElement { public:
GTPTable() CLICK_COLD;
~GTPTable() CLICK_COLD;
const char *class_name() const { return "GTPTable"; }
const char *port_count() const { return "2/2"; }
const char *flow_code() const { return "xy/xz"; }
const char *flags() const { return PUSH; }
int configure(Vector<String> &, ErrorHandler *) CLICK_COLD;
bool can_live_reconfigure() const { return true; }
int process(int, Packet*);
void push(int, Packet *);
#if HAVE_BATCH
void push_batch(int port, PacketBatch *);
#endif
private:
//Map from GTP_IN to GTP_OUT.
typedef HashTableMP<GTPFlowID,GTPFlowIDMAP> GTPFlowTable;
GTPFlowTable _gtpmap;
//Map of Inner IP to GTP_IN, or GTP_OUT if known is set
typedef HashTableMP<IPFlowID,GTPFlowIDMAP> INMap;
INMap _inmap;
typedef HashTableMP<ICMPFlowID,GTPFlowID> ResolvMap;
ResolvMap _icmp_map;
bool _verbose;
IPAddress _ping_dst;
friend class GTPLookup;
};
CLICK_ENDDECLS
#endif
......@@ -57,12 +57,24 @@ void HTTPServer::update_fd_set() {
}
int HTTPServer::configure(Vector<String> &conf, ErrorHandler *errh) {
Vector<String> alias_map;
if (Args(conf, this, errh)
.read_p("PORT", _port) //TODO AUTH
.read_all("ALIAS_MAP", alias_map)
.read("VERBOSE", _verbose)
.complete() < 0)
return -1;
for (String a : alias_map) {
int s = a.find_left(':');
if (s == -1)