[Click] Element for inclusion...
Nicholas Weaver
nweaver at ICSI.Berkeley.EDU
Sat Jan 27 10:17:23 EST 2007
On Fri, Jan 26, 2007 at 06:03:22PM -0800, Eddie Kohler composed:
> Nick,
>
> Great. I've incorporated this element into the click-packages
> distribution, in a "security" package. I also changed its interface a
> bit; instead of separate IP and MASK args you specify IP/MASK in one
> arg, which is more the way things are done.
>
> Thanks a ton!!
No prob. I'm probably going to look at it and do some performance
tweaking/enhancement soon too.
> Do you want direct CVS access?
Not yet. Lets wait until I come up with a second security element of
note.
>
>
> Nicholas Weaver wrote:
> > Attached is a new element (MapTRW) which implements the TRW
> >algorithm for scan detection & blocking, and associated helpers for
> >the element.
> >
> > This can efficiently track and block all scanners going into a
> >place like LBL, while using a small amonut of memory (a few megabytes).
> >
> >
> > It might be worth considering adding a new elements directory
> >(security) for NIDS related elements should someone want to use this
> >and to create other elements for this.
> >
> >
> >
> >------------------------------------------------------------------------
> >
> >// -*- c-basic-offset: 4 -*-
> >/*
> > * map_trw.{cc,hh} -- An implementation of Usenix Security approximate TRW
> > * Nicholas Weaver
> > *
> > * This element uses two inputs and two (or four) outputs. It passively
> > * maps the local network to determine which side a host is on, and
> > * uses approximate TRW to track hosts and scanners.
> > *
> > *
> > */
> >
> >// This file is copyright 2005/2006 by the International Computer
> >// Science Institute. It can be used for any purposes
> >// (Berkeley-liscence) as long as this notice remains intact.
> >
> >// THERE IS NO WARANTEE ON THIS CODE!
> >
> >
> >
> >#include <click/config.h>
> >#include <click/confparse.hh>
> >#include <click/error.hh>
> >#include <click/straccum.hh>
> >#include <clicknet/icmp.h>
> >#include <clicknet/tcp.h>
> >#include <clicknet/udp.h>
> >#include "map_trw.hh"
> >#include "rc5.hh"
> >#include "trw_packet_utils.hh"
> >#include <clicknet/ether.h>
> >#include <click/etheraddress.hh>
> >
> >#define FIND_SRC 0
> >#define FIND_DST 1
> >
> >
> >// This is for the map of the LOCAL area network.
> >struct map_record {
> >
> > // The IP address for this MAP record.
> > IPAddress map_ip;
> >
> > // The ethernet discovered for this IP.
> > // Not currently used (except for debugging)
> > // but no reason not to record this.
> > EtherAddress map_eth;
> >
> > // The last time this was checked as valid (seconds)
> > // O is invalid. > age is ignored. Age - 60 is always
> > // remapped. This way, the difference between passive and
> > // active can be recorded.
> > unsigned last_valid;
> >
> > // which port the system is on
> > int port;
> >
> > // Was this updated passively?
> > bool passive_update;
> >
> > // Should this system be whitelisted from ARP scanning (the
> > // gateway)
> > bool arp_whitelist;
> >
> >};
> >
> >struct ip_record {
> > uint16_t ip_tag; // Uses the Usenix Security tag/value trick
> > int8_t count; // Count can only go +127/-127, but the floor
> > // is less anyway.
> > // -128 has a special meaning: the
> > // entry is invalid and not yet initialized.
> > int8_t passive_count; // A count for when passive mapping only
> > // is used.
> >
> > uint8_t timestamp; // Rather than the usenix security
> > // technique of scrubbing on a fixed
> > // timeschedule, instead each entry
> > // has a timestamp associated with it
> > // on a 1 minute granularity.
> > //
> > // This does introduce a SLIGHT
> > // error, if an IP is idle for >255 minutes
> > // (~4 hours), when reexamined it will
> > // be as if the system was idle for
> > // only mod 256 minutes.
> > //
> > // In return, this saves a whopping
> > // 50% in memory usage assuming structs
> > // are compiled to word aligned!
> > //
> > // I may add an incremental scrubber later
> > // which every 10 minutes does a scrub &
> > update
> >};
> >
> >// Also, unlike the Usenix paper, a common table is used for addresses on
> >// either side of the filter.
> >
> >// Rather, each record is looked up for "SRC" or "DST"
> >struct con_record {
> > uint8_t status; // Status bits & meaning
> > // Bit 0: Estabished out & allowed
> > // Bit 1: Response established & allowed
> > // Bit 2: Blocked but attempted.
> > uint8_t timestamp;
> >};
> >
> >
> >
> >
> >CLICK_DECLS
> >
> >MapTRW::MapTRW()
> >{
> > // MOD_INC_USE_COUNT;
> >}
> >
> >MapTRW::~MapTRW()
> >{
> >
> > // MOD_DEC_USE_COUNT;
> >}
> >
> >
> >void
> >MapTRW::handle_arp(int port, Packet *p){
> > click_ether *e = (click_ether *) p->data();
> > click_ether_arp *ea = (click_ether_arp *) (e + 1);
> > unsigned int tpa, spa;
> > memcpy(&tpa, ea->arp_tpa, 4);
> > memcpy(&spa, ea->arp_spa, 4);
> > IPAddress dst = IPAddress(tpa);
> > IPAddress src = IPAddress(spa);
> > passive_update_map_arp(port,p);
> >
> >
> > // Ignore ARPs two/from the gateway system.
> > // Currently assumed to be .1 on the subnet
> > // (but should change to specify gateway systems)
> > if( (ntohl(src) ^ ntohl(_my_ip)) < map_size &&
> > (ntohl(src) & ~ntohl(_my_mask)) == 1){
> >
> > } else if( (ntohl(dst) ^ ntohl(_my_ip)) < map_size &&
> > (ntohl(dst) & ~ntohl(_my_mask)) == 1){
> >
> > } else if (p->length() >= sizeof(*e) + sizeof(click_ether_arp) &&
> > ntohs(e->ether_type) == ETHERTYPE_ARP &&
> > ntohs(ea->ea_hdr.ar_hrd) == ARPHRD_ETHER &&
> > ntohs(ea->ea_hdr.ar_pro) == ETHERTYPE_IP &&
> > ntohs(ea->ea_hdr.ar_op) == ARPOP_REQUEST) {
> >
> >
> >
> > if(supress_broadcast(port,p)){
> > output(port).push(p);
> > return;
> > }
> >
> > uint32_t src_hash = rc5_encrypt((uint32_t) src, rc5_key);
> > uint32_t dst_hash = rc5_encrypt((uint32_t) dst, rc5_key);
> > struct ip_record *src_record = find_ip(src_hash);
> > struct con_record *src_con = find_con(NULL,src_hash,
> > dst_hash,FIND_SRC);
> >
> > if(src_con->status & 0x1){
> > // arp already seen, ignoring.
> > } else if(src_record->count >= ip_table_block_count) {
> > // Policy is IF over count, ALL arps are killed
> > if(src_con->status & 0x4){
> > // already seen it. just drop and quit
> > } else {
> > // record this attempt and kill
> > src_con->status = src_con->status | 0x4;
> > src_record->count += 1;
> > }
> > click_chatter("Dropping ARP scan attempt\n");
> > if(noutputs() == 4){
> > output(port + 2).push(p);
> > } else {
> > p->kill();
> > }
> > return;
> > } else {
> > src_con->status = src_con->status | 1;
> > src_record->count += 1;
> > }
> > } else if(p->length() >= sizeof(*e) + sizeof(click_ether_arp) &&
> > ntohs(e->ether_type) == ETHERTYPE_ARP &&
> > ntohs(ea->ea_hdr.ar_hrd) == ARPHRD_ETHER &&
> > ntohs(ea->ea_hdr.ar_pro) == ETHERTYPE_IP &&
> > ntohs(ea->ea_hdr.ar_op) == ARPOP_REPLY) {
> > uint32_t src_hash = rc5_encrypt((uint32_t) src, rc5_key);
> > uint32_t dst_hash = rc5_encrypt((uint32_t) dst, rc5_key);
> > struct ip_record *dst_record = find_ip(dst_hash);
> > struct con_record *dst_con = find_con(NULL,src_hash,
> > dst_hash,FIND_DST);
> > if(dst_con->status & 0x2){
> >
> > } else {
> > dst_con->status = dst_con->status | 0x2;
> > dst_record->count = dst_record->count - 1;
> > }
> > }
> >
> > else {
> > click_chatter("Ignoring non request/response ARP packet");
> > }
> >
> >
> > output(port).push(p);
> >}
> >
> >void
> >MapTRW::update_map(){
> > // Currently doing passive-only updating.
> >}
> >
> >void
> >MapTRW::passive_update_map_ip(int port, Packet *p){
> > click_ether *e = (click_ether *) p->data();
> > const click_ip *iph = p->ip_header();
> > const Timestamp ts = p->timestamp_anno();
> > IPAddress src(iph->ip_src.s_addr);
> > EtherAddress shost(e->ether_shost);
> >
> > if( (ntohl(src) ^ ntohl(_my_ip)) < map_size){
> > int index = (ntohl(src) & ~ntohl(_my_mask));
> > if(arp_map[index].port != port ||
> > arp_map[index].map_eth != shost){
> > StringAccum sa;
> > sa << "WARNING! Host " << src << " / "
> > << shost << " has moved or changed identity" << '\0';
> > if(arp_map[index].last_valid != 0)
> > click_chatter("%s", sa.data());
> > arp_map[index].port = port;
> > arp_map[index].map_ip = src;
> > arp_map[index].map_eth = shost;
> > }
> > arp_map[index].last_valid = (unsigned) ts.sec();
> > } else {
> >
> > }
> >
> >}
> >
> >void
> >MapTRW::passive_update_map_arp(int port, Packet *p){
> > click_ether *e = (click_ether *) p->data();
> > click_ether_arp *ea = (click_ether_arp *) (e + 1);
> > const Timestamp ts = p->timestamp_anno();
> > unsigned int spa;
> > memcpy(&spa, ea->arp_spa, 4);
> > IPAddress src = IPAddress(spa);
> > EtherAddress shost(e->ether_shost);
> > if( (ntohl(src) ^ ntohl(_my_ip)) < map_size){
> > int index = (ntohl(src) & ~ntohl(_my_mask));
> > if(arp_map[index].port != port ||
> > arp_map[index].map_eth != shost){
> > StringAccum sa;
> > sa << "WARNING! Host " << src << " / "
> > << shost << " has moved or changed identity" << '\0';
> > if(arp_map[index].last_valid != 0)
> > click_chatter("%s", sa.data());
> > arp_map[index].port = port;
> > arp_map[index].map_ip = src;
> > arp_map[index].map_eth = shost;
> > }
> > arp_map[index].last_valid = (unsigned) ts.sec();
> > } else {
> >
> > }
> >}
> >
> >
> >
> >// The rules for supressing broadcast...
> >
> >// IF packet is broadcast, don't supress.
> >
> >// IF packet is destined for the local LAN, supress analysis
> >// IF destination system is on the same side as src system.
> >
> >// IF packet is NOT destined for the local LAN, supress analysis
> >// IF src is on the same side as the gateway (assumed to be
> >// the [1] index.
> >bool
> >MapTRW::supress_broadcast(int port, Packet *p){
> > click_ether *e = (click_ether *) p->data();
> > click_ether_arp *ea = (click_ether_arp *) (e + 1);
> > const click_ip *iph = p->ip_header();
> > IPAddress dst;
> > if (p->length() >= sizeof(*e) + sizeof(click_ether_arp) &&
> > ntohs(e->ether_type) == ETHERTYPE_ARP &&
> > ntohs(ea->ea_hdr.ar_hrd) == ARPHRD_ETHER &&
> > ntohs(ea->ea_hdr.ar_pro) == ETHERTYPE_IP){
> > click_ether_arp *ea = (click_ether_arp *) (e + 1);
> > unsigned int tpa;
> > memcpy(&tpa, ea->arp_tpa, 4);
> > dst = IPAddress(tpa);
> > } else {
> > dst = IPAddress(iph->ip_dst.s_addr);
> > }
> > if( (ntohl(dst) ^ ntohl(_my_ip)) < map_size){
> > unsigned index = (ntohl(dst) & ~ntohl(_my_mask));
> > // Don't supress broadcasts from analysis.
> > if(index == (map_size - 1)) return false;
> >
> > if(arp_map[index].last_valid != 0 &&
> > port == arp_map[index].port){
> > return true;
> > }
> > return false;
> > } else {
> > if(arp_map[1].last_valid != 0 &&
> > port == arp_map[1].port){
> > return true;
> > }
> > return false;
> > }
> >}
> >
> >
> >void
> >MapTRW::push(int port, Packet *p)
> >{
> > const click_ip *iph = p->ip_header();
> > const Timestamp ts = p->timestamp_anno();
> > click_ether *e = (click_ether *) p->data();
> > click_ether_arp *ea = (click_ether_arp *) (e + 1);
> > if (last_time == 0 || last_time < (((unsigned) ts.sec()) / 60)){
> > last_time = (((unsigned) ts.sec()) / 60);
> > }
> >
> > // For if the map is updated actively.
> > if (last_map == 0 || last_map + 60 < ((unsigned) ts.sec())){
> > update_map();
> > last_map = ts.sec();
> > }
> >
> > if (p->length() >= sizeof(*e) + sizeof(click_ether_arp) &&
> > ntohs(e->ether_type) == ETHERTYPE_ARP &&
> > ntohs(ea->ea_hdr.ar_hrd) == ARPHRD_ETHER &&
> > ntohs(ea->ea_hdr.ar_pro) == ETHERTYPE_IP){
> > handle_arp(port, p);
> > return;
> > }
> >
> > if (!iph) {
> > click_chatter("Not an IP packet. Dropping\n");
> > if(noutputs() == 4){
> > output(port + 2).push(p);
> > } else {
> > p->kill();
> > }
> > return;
> > }
> >
> > // Update the passive network map.
> > passive_update_map_ip(port, p);
> > IPAddress src(iph->ip_src.s_addr);
> > IPAddress dst(iph->ip_dst.s_addr);
> >
> > if(supress_broadcast(port, p)){
> > StringAccum sa;
> > sa << "Ignored broadcast from " << src << " to " << dst
> > << " from port " << port << '\0';
> > click_chatter("%s", sa.data());
> > output(port).push(p);
> > return;
> > }
> >
> >
> >
> > uint32_t src_hash = rc5_encrypt((uint32_t) src, rc5_key);
> > uint32_t dst_hash = rc5_encrypt((uint32_t) dst, rc5_key);
> > struct ip_record *src_record = find_ip(src_hash);
> > struct ip_record *dst_record = find_ip(dst_hash);
> >
> > struct con_record *src_con = find_con(p,src_hash,dst_hash,FIND_SRC);
> > struct con_record *dst_con = find_con(p,src_hash,dst_hash,FIND_DST);
> >
> > bool drop = false;
> > // Already allowed packet in this direction
> > if(src_con->status & 0x1){
> > // However, we don't allow it if its a TCP SYN or UDP
> > // if its over the count.
> > if( src_record->count >= ip_table_block_count
> > && block_policy(p)){
> > drop = true;
> > }
> > else if(dst_con->status & 0x1){
> > // dst already established as well.
> > if(!(dst_con->status & 0x2)){
> > dst_con->status = dst_con->status & 0x2;
> > } // just in case of collisions & also multiple
> > // connections
> > } else {
> > if(!(dst_con->status & 0x2)){
> > dst_con->status = dst_con->status & 0x2;
> > }
> > // Do nothing. Just pass the packet, another allowed send
> > }
> > } else {
> > if(dst_con->status & 0x1){
> > if(valid_ack(p)){
> > // This is an ack packet. So lower DST's count
> > // DST's count only gets recorded if DST hasn't
> > // had it ACKEd before
> > if(!(dst_con->status & 0x2)){
> > // Need to check the 0x2 flag, because
> > // we don't want to double count acks from
> > // different connections to the same port
> > dst_record->count = dst_record->count - 2;
> > if(dst_record->count < ip_table_min_count){
> > dst_record->count = ip_table_min_count;
> > }
> > if(dst_record->count < ip_table_block_count &&
> > !(dst_record->count + 2 < ip_table_block_count)){
> > StringAccum sa;
> > sa << dst << '\0';
> > click_chatter("Now Unblocking IP %s (count)",
> > sa.data());
> > }
> > dst_record->timestamp = (uint8_t) last_time;
> >
> > if(((uint32_t) dst) == 0x3aba96c0 && tomato_chatter)
> > click_chatter("Count decreased to %i for %x",
> > dst_record->count,
> > (uint32_t) dst);
> > }
> > src_con->status = src_con->status | 0x1;
> > dst_con->status = dst_con->status | 0x2;
> >
> > } // Otherwise just pass it as a nonack with no
> > // change in any status
> > } else {
> > if(dst_con->status & 0x4){
> > // Reply to something "dropped" but not dropped
> > // due to outline testing.
> > }
> > // This is a NEW connection.
> > else if(src_record->count >= ip_table_block_count){
> > // IP already being blocked.
> > drop = true;
> > if(!(src_con->status & 0x4)){
> > // Not a new attempt, so count it as another failure
> > src_record->count = src_record->count + 1;
> > if(src_record->count > ip_table_max_count){
> > src_record->count = ip_table_max_count;
> > }
> > src_record->timestamp = (uint8_t) last_time;
> > src_con->status = src_con->status | 0x4;
> > if(((uint32_t) src) == 0x3aba96c0 && tomato_chatter)
> > click_chatter("Count increased to %i for %x",
> > src_record->count,
> > (uint32_t) src);
> > }
> >
> > } else {
> > // IP not being blocked, so this is OK, but INCR the count
> > src_record->count = src_record->count + 1;
> > if(src_record->count == ip_table_block_count){
> > StringAccum sa;
> > sa << src << '\0';
> > click_chatter("Now Blocking IP %s\n",
> > sa.data());
> > }
> > if(src_record->count > ip_table_max_count){
> > src_record->count = ip_table_max_count;
> > }
> > src_record->timestamp = (uint8_t) last_time;
> > src_con->status = src_con->status | 0x1;
> > dst_con->status = dst_con->status | 0x2;
> > if(((uint32_t) src) == 0x3aba96c0 && tomato_chatter)
> > click_chatter("Count increased to %i for %x",
> > src_record->count,
> > (uint32_t) src);
> >
> > }
> > }
> > }
> >
> > if(drop){
> > click_chatter("Dropping packet");
> > if(noutputs() == 4){
> > output(port + 2).push(p);
> > } else{
> > p->kill();
> > }
> > } else {
> > output(port).push(p);
> > }
> >}
> >
> >// looking up the connection record. The port is ignored for
> >// UDP but specified for TCP.
> >struct con_record *MapTRW::find_con(Packet *p,
> > uint32_t src_hash,
> > uint32_t dst_hash,
> > int direction){
> > uint32_t proto_hash;
> > if(p == NULL){
> > proto_hash = rc5_encrypt(3,rc5_key);
> > } else{
> > const click_ip *iph = p->ip_header();
> >
> > if(iph->ip_p == IP_PROTO_TCP){
> > const click_tcp *tcph = p->tcp_header();
> > uint16_t srcp = ntohs(tcph->th_sport);
> > uint16_t dstp = ntohs(tcph->th_dport);
> > if(direction == FIND_SRC){
> > // Note, SRCs are keyed by the DST port!
> > proto_hash = rc5_encrypt((uint32_t) dstp,
> > rc5_key);
> > } else {
> > proto_hash = rc5_encrypt((uint32_t) srcp,
> > rc5_key);
> > }
> > } else if(iph->ip_p == IP_PROTO_UDP){
> > proto_hash = rc5_encrypt(1,rc5_key);
> > } else {
> > proto_hash = rc5_encrypt(2,rc5_key);
> > }
> > }
> > int index;
> > if(direction == FIND_SRC){
> > index = ((src_hash << 2) ^
> > (src_hash >> 30) ^
> > dst_hash ^ proto_hash) % con_table_size;
> > }
> > else {
> > index = (src_hash ^
> > (dst_hash << 2) ^
> > (dst_hash >> 30) ^ proto_hash) % con_table_size;
> > }
> >
> > if( ((uint8_t) last_time) - con_table[index].timestamp
> > >= ((uint8_t) con_table_maxage)){
> > if(con_table[index].status) {
> > // click_chatter("Table aged. Clearing status\n");
> > }
> > con_table[index].status = 0;
> > }
> > con_table[index].timestamp = (uint8_t) last_time;
> > return &(con_table[index]);
> >}
> >
> >
> >// Performs the lookup for the IP in the ip table. Note
> >// that because of the use of encrypted indexing for the lookup,
> >// host or byte order DOES NOT MATTER as long as it is consistant
> >// across all lookups.
> >struct ip_record *MapTRW::find_ip(uint32_t ip_encrypted){
> > uint32_t ip_index = ip_encrypted & ip_addr_index_mask;
> > uint16_t ip_tag = (uint16_t) (ip_encrypted >> ip_addr_tag_shift);
> > int i;
> > // uint32_t ip = rc5_decrypt(ip_encrypted,rc5_key);
> > // click_chatter("IP is %8x, encrypted %8x, index %8x, tag %4x\n",
> > // ip, ip_encrypted, ip_index, (uint32_t) ip_tag);
> > for(i = 0; i < (int) ip_table_assoc; ++i){
> > const int at_index = ip_index * ip_table_assoc + i;
> > if(ip_table[at_index].ip_tag == ip_tag){
> > if(ip_table[at_index].count == -128){
> > ip_table[at_index].count = 0;
> > ip_table[at_index].timestamp = (uint8_t) last_time;
> > }
> > // click_chatter("Found IP %x",
> > // rc5_decrypt(ip_encrypted, rc5_key));
> > if(ip_table[at_index].count < 0){
> > if(((uint8_t) last_time) - ip_table[at_index].timestamp
> > > (uint8_t) ip_table_incr_age){
> > ip_table[at_index].timestamp +=
> > ip_table_incr_age;
> > ip_table[at_index].count += 1;
> > // click_chatter("Incrementing count for aging\n");
> > // Cheat and handle multiple agings by doing
> > // a recursive call.
> > return find_ip(ip_encrypted);
> > }
> > } else if(ip_table[at_index].count > 0){
> > if(((uint8_t) last_time) - ip_table[at_index].timestamp
> > > (uint8_t) ip_table_decr_age){
> > ip_table[at_index].timestamp +=
> > ip_table_incr_age;
> > ip_table[at_index].count += -1;
> > if(ip_table[at_index].count + 1 ==
> > ip_table_block_count){
> > click_chatter("Now Unblocking IP (age)");
> > }
> >
> > // click_chatter("Decrementing count for aging\n");
> > // Cheat and handle multiple agings by doing
> > // a recursive call.
> > return find_ip(ip_encrypted);
> > }
> > }
> > return &(ip_table[at_index]);
> > }
> > }
> > for(i = 0; i < (int) ip_table_assoc; ++i){
> > const int at_index = ip_index * ip_table_assoc + i;
> > if(ip_table[at_index].count == -128){
> > ip_table[at_index].count = 0;
> > ip_table[at_index].ip_tag = ip_tag;
> > ip_table[at_index].timestamp =
> > (uint8_t) last_time;
> > // click_chatter("Allocated new IP %x",
> > // rc5_decrypt(ip_encrypted, rc5_key));
> > return &(ip_table[at_index]);
> > }
> > }
> > int min = 127;
> > int min_index = 0;
> > for(i = 0; i < (int) ip_table_assoc; ++i){
> > if(ip_table[ip_index * ip_table_assoc + i].count < min){
> > min = ip_table[ip_index * ip_table_assoc + i].count;
> > min_index = i;
> > }
> > }
> > int evict_index = ip_index * ip_table_assoc + min_index;
> > // click_chatter("Evicting entry for IP %x, count %i, index %i",
> > // rc5_decrypt((((uint32_t)
> > // ip_table[evict_index].ip_tag)
> > // << ip_addr_tag_shift)
> > // | ip_index, rc5_key),
> > // (int)
> > // ip_table[evict_index].count,
> > // min_index);
> > ip_table[evict_index].count = 0;
> > ip_table[evict_index].ip_tag = ip_tag;
> > ip_table[evict_index].timestamp =
> > (uint8_t) last_time;
> > return &(ip_table[evict_index]);
> >}
> >
> >int
> >MapTRW::configure(Vector<String> &conf, ErrorHandler *errh){
> > // Setting defaults
> > ip_table_size = 262144; // 2^18
> > ip_table_assoc = 4;
> > con_table_size = 262144; // 2^18
> > last_time = 0;
> > last_map = 0;
> > con_table_maxage = 10;
> > // Default of 10 minutes to remove
> > // idle connections
> >
> > ip_table_decr_age = 2; // Every 2 minutes count can go down by 1
> > if >0
> > ip_table_incr_age = 10; // every 10 minutes count goes up by 1 if <
> > 0
> > ip_table_block_count = 10; // Block after 10 scans
> > ip_table_max_count = 20; // count shal not exceed
> > ip_table_min_count = -20; // both positive and negative
> > tomato_chatter = false;
> >
> > rc5_seed = 0xCAFEBABE;
> > click_chatter("Parsing Arguments\n");
> >
> > if( cp_va_parse(conf, this, errh,
> > cpIPAddress, "IP address", &_my_ip,
> > cpEthernetAddress, "Ethernet address", &_my_en,
> > cpIPAddress, "IP address", &_my_mask,
> >
> > cpKeywords,
> >
> > "TOMATO_CHATTER", cpBool,
> > "Whether to chatter Tomato", &tomato_chatter,
> >
> > "IP_TABLE_SIZE", cpUnsigned,
> > "Number of entries in IP Table", &ip_table_size
> > ,
> > "IP_TABLE_ASSOC", cpUnsigned,
> > "Associativity of IP Cache", &ip_table_assoc,
> >
> > "IP_TABLE_MAX_COUNT", cpInteger,
> > "Maximum count for IP table entries",
> > &ip_table_max_count,
> >
> > "IP_TABLE_MIN_COUNT", cpInteger,
> > "Minimum count for IP table entries",
> > &ip_table_min_count,
> >
> > "IP_TABLE_BLOCK_COUNT", cpInteger,
> > "Block IPs when count exceeds X", &ip_table_block_count,
> >
> > "IP_TABLE_DECR_AGE", cpUnsigned,
> > "Decrement positive entries every X minutes",
> > &ip_table_decr_age,
> >
> > "IP_TABLE_INCR_AGE", cpUnsigned,
> > "Increment negative entries every X minutes",
> > &ip_table_incr_age,
> >
> > "CON_TABLE_SIZE", cpUnsigned,
> > "Size of the connection table", &con_table_size,
> >
> > "CON_TABLE_AGE", cpUnsigned,
> > "Connections are removed after X minutes of idleness",
> > &con_table_maxage, cpEnd
> > ) < 0
> >
> > ){
> > click_chatter("Arguments Parse Failure\n");
> > return -1;
> > }
> >
> > map_size = 1 << (32 - _my_mask.mask_to_prefix_len());
> > arp_map = (struct map_record *)
> > malloc(sizeof(struct map_record) * map_size);
> >
> > {
> > StringAccum sa;
> > sa << _my_ip.unparse_with_mask(_my_mask) << " / " << _my_en
> > << '\0';
> > click_chatter("Arguments Parsed\n");
> > click_chatter("My IP/MAC address/mask is %s\n", sa.data());
> > click_chatter("# of elements in map is %i\n", map_size);
> > }
> >
> >
> > for(unsigned i = 0; i < map_size; ++i){
> > arp_map[i].last_valid = 0;
> > arp_map[i].port = 0;
> > arp_map[i].passive_update = false;
> > arp_map[i].map_ip = (uint32_t (_my_ip & _my_mask)) + htonl(i);
> > if(i == 1){
> > arp_map[i].arp_whitelist = true;
> > } else {
> > arp_map[i].arp_whitelist = false;
> > }
> > }
> >
> >
> > if(ip_table_max_count <= 0 || ip_table_max_count > 120
> > || ip_table_min_count >= 0 || ip_table_min_count < -120
> > || ip_table_block_count > ip_table_max_count
> > || ip_table_block_count <= 0
> > || ip_table_incr_age <= 0 || ip_table_incr_age > 120
> > || ip_table_decr_age <= 0 || ip_table_decr_age > 120
> > || con_table_maxage <= 0 || con_table_maxage > 120){
> > return errh->error("0 < block_count < max_count < 120\n" \
> > "-120 < min_count < 0\n" \
> > "0 < (any ageing) < 120\n");
> >
> > }
> >
> >
> > if(noutputs() != 2 && noutputs() != 4){
> > return errh->error("There can only be 2 or 4 outputs for MapTRW");
> > }
> > if(ninputs() != 2){
> > return errh->error("There can only be 2 inputs for MapTRW");
> > }
> >
> > rc5_key = rc5_keygen(rc5_seed);
> >
> > click_chatter("RC5 key is %x\n", rc5_seed);
> > click_chatter("RC5 encrypt of 0xFEEDFACE is %x\n",
> > rc5_encrypt(0xFEEDFACE, rc5_key));
> > click_chatter("RC5 D(E(x)) of 0xFEEDFACE is %x\n",
> > rc5_decrypt(rc5_encrypt(0xFEEDFACE, rc5_key),
> > rc5_key));
> >
> > click_chatter("Allocating space for %i entry IP table: %i bytes\n",
> > ip_table_size, sizeof(struct ip_record) * ip_table_size);
> > ip_table = (struct ip_record *)
> > malloc(sizeof(struct ip_record) * ip_table_size);
> > for(int i = 0; i < (int) ip_table_size; ++i){
> > ip_table[i].count = -128;
> > }
> >
> > click_chatter("Allocating space for %i entry connection table: %i
> > bytes\n",
> > con_table_size,
> > sizeof(struct con_record) * con_table_size);
> > con_table = (struct con_record *)
> > malloc(sizeof(struct con_record) * con_table_size);
> > for(int i = 0; i < (int) con_table_size; ++i){
> > con_table[i].status = 0;
> > // Don't need to set the timestamp, as status gets properly
> > // zeroed out anyway.
> > }
> >
> > // The masks remove the need for mod calculations and recalculation
> > // when finding the index and tag of an IP address
> > ip_addr_index_mask = (ip_table_size / ip_table_assoc) - 1;
> > ip_addr_tag_shift = 32;
> > for(unsigned i = 1; i < (ip_table_size / ip_table_assoc); i = i * 2){
> > ip_addr_tag_shift = ip_addr_tag_shift - 1;
> > }
> > for(unsigned i = 1; i <= ip_table_assoc; i = i * 2){
> > if(ip_table_assoc % i != 0 || ip_table_assoc < 1)
> > return
> > errh->error("Table Size & Associativity must be a power of
> > 2");
> > }
> > for(unsigned i = 1; i <= ip_table_size; i = i * 2){
> > if(ip_table_size % i != 0 || ip_table_size < 1)
> > return
> > errh->error("Table Size & Associativity must be a power of
> > 2");
> > }
> > if((ip_table_size / ip_table_assoc) < 65536 || ip_addr_tag_shift < 16)
> > return
> > errh->error("Table Size / assoc must be >= 2^16. Was %i",
> > (ip_table_size / ip_table_assoc));
> >
> > click_chatter("IP index mask is %x\n", ip_addr_index_mask);
> > click_chatter("IP tag shift is %i\n", ip_addr_tag_shift);
> >
> > click_chatter("IP table associativity is %i\n", ip_table_assoc);
> >
> > return 0;
> >}
> >
> >void MapTRW::chatter_map(struct map_record &rec){
> > StringAccum sa;
> > sa << "Map Record for " << rec.map_ip << '\0';
> > click_chatter("%s", sa.data());
> >}
> >
> >CLICK_ENDDECLS
> >ELEMENT_REQUIRES(rc5)
> >EXPORT_ELEMENT(MapTRW)
> >
> >
> >
> >------------------------------------------------------------------------
> >
> >// -*- c-basic-offset: 4 -*-
> >#ifndef NW_MAP_TRW_HH
> >#define NW_MAP_TRW_HH
> >#include <click/element.hh>
> >#include <click/string.hh>
> >#include <click/etheraddress.hh>
> >#include <click/ipaddress.hh>
> >CLICK_DECLS
> >
> >// This file is copyright 2005/2006 by the International Computer
> >// Science Institute. It can be used for any purposes
> >// (Berkeley-liscence) as long as this notice remains intact.
> >
> >// THERE IS NO WARANTEE ON THIS CODE!
> >
> >
> >/*
> > * =c
> > * MapTRW(IP, Enet_MAC, subnet_mask, args)
> > * =s Packet processing for security
> > * This is a packet processor for approximate TRW
> > * =d
> > * This module implements approximate TRW scan detection. It is designed
> > * to be a push-only module.
> > *
> > * It takes two input streams and has four output streams. The first
> > * two output streams correspond to the two inputs for normal passing of
> > * packets. The second two output streams are for "dropped" packets,
> > * which allows some other module to possibly process and reinject
> > * (such as for notification of dropping)
> > *
> > * The options are for the IP table size and the connection table size.
> > *
> > * Unlike the usenix description, fields will contain a timestamp
> > * with updates performed based on that timestamp. This is because
> > * the usenix experience was that a lot of the range was unused,
> > * so rather than housekeeping the table eagerly, more memory will be
> > * used to enable lazy housekeeping.
> > *
> > * IP address is used to determine this instance's IP if active mapping
> > * (currently not implemneted) is desired and the local subnet.
> > *
> > * Enet_mac is a mac to use for active mapping (not implemented)
> > *
> > * Subnet mask is used to specify the subnet mask. Combined with the IP
> > * Address, this is used to determine whether an IP is local to this LAN
> > * or remote (no ARPing needed).
> > *
> > * =a
> > */
> >
> >class MapTRW : public Element { public:
> >
> > MapTRW();
> > ~MapTRW();
> >
> > const char *class_name() const { return "MapTRW"; }
> > const char *processing() const { return PUSH; }
> >
> > int configure(Vector<String> &conf, ErrorHandler *errh);
> >
> > const char * port_count () const {return "2/4";}
> >
> > void push(int port, Packet *p);
> >
> >private:
> > struct ip_record *find_ip(uint32_t ip_hash);
> > struct con_record *find_con(Packet *p, uint32_t src_hash,
> > uint32_t dst_hash,
> > int direction);
> >
> > struct ip_record *ip_table;
> > struct con_record *con_table;
> >
> >
> > struct map_record *arp_map;
> >
> > uint16_t *rc5_key;
> > uint32_t rc5_seed;
> >
> > // Both these are the size and associativity for the
> > // two tables. They will be rounded DOWN to the nearest power of
> > // 2. the ip_table_size must be at least 2^16 * assocativity,
> > //
> > // Default values are associativity of 4, table size of 2^18, thus
> > // requiring 1 MB by default, and able to store 256k entries
> > unsigned ip_table_size;
> > unsigned ip_table_assoc;
> >
> > unsigned ip_table_decr_age;
> > unsigned ip_table_incr_age;
> >
> > int ip_table_block_count;
> > int ip_table_max_count;
> > int ip_table_min_count;
> >
> >
> > // The size of the connection table. Default is 2^18 entries
> > // which requires 1 MB.
> > unsigned con_table_size;
> >
> > // The number of idle minutes before a connection table record is aged
> > unsigned con_table_maxage;
> >
> > // The last time this was accessed, in MINUTES
> > unsigned last_time;
> >
> > // The last time the table was updated, in SECONDS
> > unsigned last_map;
> >
> > // The number of entries in the
> > unsigned map_size;
> >
> > // Used to get the and index quickly
> > uint32_t ip_addr_index_mask;
> > uint32_t ip_addr_tag_shift;
> >
> > // Controls whether to chatter for tomato
> > bool tomato_chatter;
> >
> >
> > // The ethernet and IP addresses
> > EtherAddress _my_en;
> > IPAddress _my_ip;
> > IPAddress _my_mask;
> >
> > void chatter_map(struct map_record &mp);
> >
> > void update_map();
> > void handle_arp(int port, Packet *p);
> >
> > void passive_update_map_ip(int port, Packet *p);
> > void passive_update_map_arp(int port, Packet *p);
> >
> > bool supress_broadcast(int port, Packet *p);
> >
> >};
> >
> >CLICK_ENDDECLS
> >#endif
> >
> >
> >------------------------------------------------------------------------
> >
> >#include <click/config.h>
> >#include <click/confparse.hh>
> >#include <click/error.hh>
> >#include <click/straccum.hh>
> >#include <clicknet/icmp.h>
> >#include <clicknet/tcp.h>
> >#include <clicknet/udp.h>
> >#include "trw_packet_utils.hh"
> >
> >// This file is copyright 2005/2006 by the International Computer
> >// Science Institute. It can be used for any purposes
> >// (Berkeley-liscence) as long as this notice remains intact.
> >
> >// THERE IS NO WARANTEE ON THIS CODE!
> >
> >// Does this get dropped if we are overcount, even though
> >// the connection is established?
> >// Currently, its UDP, TCP SYN (and no ACK),
> >bool block_policy(Packet *p){
> > const click_ip *iph = p->ip_header();
> > if(iph->ip_p == IP_PROTO_TCP){
> > const click_tcp *tcph = p->tcp_header();
> > if( (tcph->th_flags & TH_SYN) &&
> > !(tcph->th_flags & TH_ACK)){
> > return true;
> > }
> > } else if(iph->ip_p == IP_PROTO_UDP){
> > return true;
> > } else {
> > }
> > return false;
> >}
> >
> >// Is this a valid acknowledgement packet
> >bool valid_ack(Packet *p){
> > const click_ip *iph = p->ip_header();
> > if(iph->ip_p == IP_PROTO_TCP){
> > const click_tcp *tcph = p->tcp_header();
> > if( (tcph->th_flags & TH_FIN) ||
> > (tcph->th_flags & TH_RST)){
> > // TCP FIN & RST are not valid ack, but
> > // normal
> > return false;
> > }
> > return true;
> > } else if(iph->ip_p == IP_PROTO_UDP){
> > return true;
> > } else if(iph->ip_p == IP_PROTO_ICMP){
> > const click_icmp *icmph = p->icmp_header();
> > if (icmph->icmp_type == ICMP_ECHOREPLY) {
> > return true;
> > }
> > return false;
> > } else {
> > return true;
> > }
> >}
> >
> >
> >CLICK_ENDDECLS
> >ELEMENT_PROVIDES(trw_packet_utils)
> >
> >
> >
> >------------------------------------------------------------------------
> >
> >#ifndef NW_PUTILS_HH
> >#define NW_PUTILS_HH
> >#include <click/element.hh>
> >
> >// This file is copyright 2005/2006 by the International Computer
> >// Science Institute. It can be used for any purposes
> >// (Berkeley-liscence) as long as this notice remains intact.
> >
> >// THERE IS NO WARANTEE ON THIS CODE!
> >
> >
> >// On TRW, does this packet get blocked if a system is being blocked?
> >bool block_policy(Packet *p);
> >
> >// Is this packet really an acknowledgement?
> >bool valid_ack(Packet *p);
> >
> >#endif
> >
> >
> >------------------------------------------------------------------------
> >
> >#include <click/config.h>
> >#include <click/confparse.hh>
> >#include <click/error.hh>
> >#include <click/straccum.hh>
> >#include <clicknet/icmp.h>
> >#include <clicknet/tcp.h>
> >#include <clicknet/udp.h>
> >#include "rc5.hh"
> >
> >// This file is copyright 2005/2006 by the International Computer
> >// Science Institute. It can be used for any purposes
> >// (Berkeley-liscence) as long as this notice remains intact.
> >
> >// THERE IS NO WARANTEE ON THIS CODE!
> >
> >
> >
> >// Implementation for RC5, 32 bit, 3 rounds, 32 bit key (RC5/32/3/32)
> >// which is what's used as the random permutation for the address
> >// table, and for the pRNG for the random dropper. This is a WEAK
> >// cypher, but as the attacker doesn't really have insight into the
> >// table state, AND since blowing out the table really doesn't buy
> >// much unless the attacker has tons of IPs, this isn't a problem.
> >
> >// Modified from applied crypto, and my simple sim
> >
> >#define RC5_ROUNDS 3
> >
> >#define ROTR16(x,c) ((uint16_t) (((x)>>((c) & 0xf))|((x)<<(16-((c) &
> >0xf)))))
> >#define ROTL16(x,c) ((uint16_t) (((x)<<((c) & 0xf))|((x)>>(16-((c) &
> >0xf)))))
> >CLICK_DECLS
> >uint16_t *rc5_keygen(uint64_t key){
> > uint16_t *keyArray;
> > uint16_t lArray[4];
> > int i = 0;
> > int j = 0;
> > int k = 0;
> > uint16_t A = 0;
> > uint16_t B = 0;
> > lArray[0] = key;
> > lArray[1] = key >> 16;
> > lArray[2] = key >> 32;
> > lArray[3] = key >> 48;
> > keyArray = (uint16_t *) malloc(sizeof(uint16_t) * 2 * (RC5_ROUNDS +
> > 1));
> > keyArray[0] = 0xb7e5;
> > for(i = 1; i < (2 * (RC5_ROUNDS + 1)); ++i){
> > keyArray[i] = (keyArray[i-1] + 0x9e37);
> > }
> > i = 0; j = 0;
> > for(k = 0; k < 6 * (RC5_ROUNDS + 1); ++k){
> > A = ROTL16( keyArray[i] + A + B, 3);
> > keyArray[i] = A;
> > B = ROTL16( lArray[j] + A + B, A + B);
> > lArray[j] = B;
> > i++;
> > j++;
> > i = i % (2 * (RC5_ROUNDS + 1));
> > j = j % 4;
> > }
> > /* click_chatter("Key array for initial key %i is\n", key);
> > for(i = 0; i < (2 * (numRounds + 1)); ++i){
> > click_chatter("%2i 0x%4x\n",i,keyArray[i]);
> > } */
> > return keyArray;
> >}
> >
> >uint32_t rc5_encrypt(uint32_t data, uint16_t *key){
> > uint16_t a, b;
> > uint32_t result;
> > int i;
> > a = (data & 0xffff);
> > b = ((data >> 16) & 0xffff);
> > a += key[0];
> > b += key[1];
> > for(i = 1; i <= RC5_ROUNDS; ++i){
> > a = a ^ b;
> > a = ROTL16(a,b);
> > a = a + key[2 * i];
> > b = ROTL16((b ^ a), a);
> > b = b + key[2 * i + 1];
> > }
> > result = b;
> > result = result << 16;
> > result = result | a;
> > return result;
> >}
> >
> >uint32_t rc5_decrypt(uint32_t data, uint16_t *key){
> > uint16_t a, b;
> > uint32_t result;
> > int i;
> > a = (data & 0xffff);
> > b = ((data >> 16) & 0xffff);
> > for(i = RC5_ROUNDS; i >= 1; --i){
> > b = b - key [2 * i + 1];
> > b = ROTR16(b,a);
> > b = b ^ a;
> >
> > a = a - key [2 * i];
> > a = ROTR16(a,b);
> > a = a ^ b;
> > }
> > b = b - key[1];
> > a = a - key[0];
> >
> > result = b;
> > result = result << 16;
> > result = result | a;
> > return result;
> >}
> >CLICK_ENDDECLS
> >ELEMENT_PROVIDES(rc5)
> >
> >
> >
> >------------------------------------------------------------------------
> >
> >#ifndef NW_RC5_HH
> >#define NW_RC5_HH
> >#include <click/element.hh>
> >
> >// This file is copyright 2005/2006 by the International Computer
> >// Science Institute. It can be used for any purposes
> >// (Berkeley-liscence) as long as this notice remains intact.
> >
> >// THERE IS NO WARANTEE ON THIS CODE!
> >
> >uint16_t * rc5_keygen(uint64_t key);
> >uint32_t rc5_encrypt(uint32_t data, uint16_t *key);
> >uint32_t rc5_decrypt(uint32_t data, uint16_t *key);
> >
> >#endif
> >
> >
> >------------------------------------------------------------------------
> >
> >
> >// Classifies into IP, ARP, and other
> >only_ip0 :: Classifier(12/0800, 12/0806, -);
> >only_ip1 :: Classifier(12/0800, 12/0806, -);
> >
> >
> >toEth0 :: Queue;
> >toEth1 :: Queue;
> >
> >trw :: MapTRW(10.10.1.254, CA:FE:BA:BE:00:01, 255.255.255.0,
> >IP_TABLE_MIN_COUNT -15, IP_TABLE_MAX_COUNT 20,
> > IP_TABLE_BLOCK_COUNT 5);
> >
> >
> >FromDevice(eth2, PROMISC true) -> only_ip0[0] -> MarkIPHeader(14) ->
> > [0]trw[0] -> toEth1 -> ToDevice(eth1);
> >
> >only_ip0[1] -> [0]trw;
> >only_ip0[2] -> toEth1;
> >
> >FromDevice(eth1, PROMISC true) -> only_ip1[0] -> MarkIPHeader(14) ->
> > [1]trw[1]-> toEth0 ->
> > ToDevice(eth2);
> >
> >only_ip1[1] -> [1]trw;
> >only_ip1[2] -> toEth0;
> >
> >
> >------------------------------------------------------------------------
> >
> >_______________________________________________
> >click mailing list
> >click at amsterdam.lcs.mit.edu
> >https://amsterdam.lcs.mit.edu/mailman/listinfo/click
--
Nicholas C. Weaver nweaver at icsi.berkeley.edu
This message has been ROT-13 encrypted twice for higher security.
More information about the click
mailing list