📄 ipfilter.cc
字号:
/* * ipfilter.{cc,hh} -- IP-packet filter with tcpdumplike syntax * Eddie Kohler * * Copyright (c) 2000-2004 Mazu Networks, Inc. * * 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 "ipfilter.hh"#include <click/glue.hh>#include <click/error.hh>#include <click/confparse.hh>#include <click/straccum.hh>#include <clicknet/ip.h>#include <clicknet/tcp.h>#include <clicknet/icmp.h>#include <click/hashmap.hh>#include <click/integers.hh>#include <click/nameinfo.hh>CLICK_DECLSstatic const StaticNameDB::Entry type_entries[] = { { "ce", IPFilter::TYPE_IPCE }, { "dest", IPFilter::TYPE_SYNTAX }, { "dscp", IPFilter::FIELD_DSCP }, { "dst", IPFilter::TYPE_SYNTAX }, { "ect", IPFilter::TYPE_IPECT }, { "frag", IPFilter::TYPE_IPFRAG }, { "hl", IPFilter::FIELD_HL }, { "host", IPFilter::TYPE_HOST }, { "id", IPFilter::FIELD_ID }, { "ip", IPFilter::TYPE_SYNTAX }, { "len", IPFilter::FIELD_IPLEN }, { "net", IPFilter::TYPE_NET }, { "not", IPFilter::TYPE_SYNTAX }, { "opt", IPFilter::TYPE_TCPOPT }, { "port", IPFilter::TYPE_PORT }, { "proto", IPFilter::TYPE_PROTO }, { "src", IPFilter::TYPE_SYNTAX }, { "tos", IPFilter::FIELD_TOS }, { "ttl", IPFilter::FIELD_TTL }, { "type", IPFilter::FIELD_ICMP_TYPE }, { "unfrag", IPFilter::TYPE_IPUNFRAG }, { "vers", IPFilter::FIELD_VERSION }, { "win", IPFilter::FIELD_TCP_WIN }};static const StaticNameDB::Entry tcp_opt_entries[] = { { "ack", TH_ACK }, { "fin", TH_FIN }, { "psh", TH_PUSH }, { "rst", TH_RST }, { "syn", TH_SYN }, { "urg", TH_URG }};static const uint32_t db2type[] = { IPFilter::TYPE_PROTO, IPFilter::TYPE_PORT, IPFilter::TYPE_PORT, IPFilter::TYPE_TCPOPT, IPFilter::FIELD_ICMP_TYPE};static Stringunparse_word(int type, int proto, const String &word){ String tn = IPFilter::Primitive::unparse_type(0, type); String tr = IPFilter::Primitive::unparse_transp_proto(proto); if (tn) tn += " "; if (tr || (word && tn)) tr += " "; return tn + tr + word;}intIPFilter::lookup(String word, int type, int proto, uint32_t &data, ErrorHandler *errh) const{ // type queries always win if they occur if (type == 0 || type == TYPE_TYPE) if (NameInfo::query(NameInfo::T_IPFILTER_TYPE, this, word, &data, sizeof(uint32_t))) return (data == TYPE_SYNTAX ? -1 : TYPE_TYPE); // query each relevant database int got[5]; int32_t val[5]; got[0] = NameInfo::query(NameInfo::T_IP_PROTO, this, word, &val[0], sizeof(uint32_t)); got[1] = NameInfo::query(NameInfo::T_TCP_PORT, this, word, &val[1], sizeof(uint32_t)); got[2] = NameInfo::query(NameInfo::T_UDP_PORT, this, word, &val[2], sizeof(uint32_t)); got[3] = NameInfo::query(NameInfo::T_TCP_OPT, this, word, &val[3], sizeof(uint32_t)); got[4] = NameInfo::query(NameInfo::T_ICMP_TYPE, this, word, &val[4], sizeof(uint32_t)); // exit if no match if (!got[0] && !got[1] && !got[2] && !got[3] && !got[4]) return -1; // filter int tgot[5]; tgot[0] = got[0] && (type == 0 || type == TYPE_PROTO); tgot[1] = got[1] && (type == 0 || type == TYPE_PORT) && (proto == UNKNOWN || proto == IP_PROTO_TCP || proto == IP_PROTO_TCP_OR_UDP); tgot[2] = got[2] && (type == 0 || type == TYPE_PORT) && (proto == UNKNOWN || proto == IP_PROTO_UDP || proto == IP_PROTO_TCP_OR_UDP); tgot[3] = got[3] && (type == 0 || type == TYPE_TCPOPT) && (proto == UNKNOWN || proto == IP_PROTO_TCP || proto == IP_PROTO_TCP_OR_UDP); tgot[4] = got[4] && (type == 0 || type == FIELD_ICMP_TYPE) && (proto == UNKNOWN || proto == IP_PROTO_ICMP); // remove one of TCP and UDP port if they give the same value if (tgot[1] && tgot[2] && val[1] == val[2]) tgot[2] = false; // return int ngot = tgot[0] + tgot[1] + tgot[2] + tgot[3] + tgot[4]; if (ngot == 1) { for (int i = 0; i < 5; i++) if (tgot[i]) { data = val[i]; return db2type[i]; } } StringAccum sa; for (int i = 0; i < 5; i++) if (got[i]) { if (sa) sa << ", "; sa << '\'' << unparse_word(db2type[i], proto, word) << '\''; } if (errh) errh->error("'%s' is %s; try %s", unparse_word(type, proto, word).c_str(), (ngot > 1 ? "ambiguous" : "meaningless"), sa.c_str()); return -2;}static NameDB *dbs[2];voidIPFilter::static_initialize(){ dbs[0] = new StaticNameDB(NameInfo::T_IPFILTER_TYPE, String(), type_entries, sizeof(type_entries) / sizeof(type_entries[0])); dbs[1] = new StaticNameDB(NameInfo::T_TCP_OPT, String(), tcp_opt_entries, sizeof(tcp_opt_entries) / sizeof(tcp_opt_entries[0])); NameInfo::installdb(dbs[0], 0); NameInfo::installdb(dbs[1], 0);}voidIPFilter::static_cleanup(){ NameInfo::removedb(dbs[0]); NameInfo::removedb(dbs[1]); delete dbs[0]; delete dbs[1];}IPFilter::IPFilter(){}IPFilter::~IPFilter(){}//// CONFIGURATION//voidIPFilter::Primitive::clear(){ _type = _srcdst = 0; _transp_proto = UNKNOWN; _data = 0; _op = OP_EQ; _op_negated = false;}voidIPFilter::Primitive::set_type(int x, ErrorHandler *errh){ if (_type) errh->error("type specified twice"); _type = x;}voidIPFilter::Primitive::set_srcdst(int x, ErrorHandler *errh){ if (_srcdst) errh->error("'src' or 'dst' specified twice"); _srcdst = x;}voidIPFilter::Primitive::set_transp_proto(int x, ErrorHandler *errh){ if (_transp_proto != UNKNOWN && _transp_proto != x) errh->error("transport protocol specified twice"); _transp_proto = x;}intIPFilter::Primitive::set_mask(uint32_t full_mask, int shift, uint32_t provided_mask, ErrorHandler *errh){ uint32_t data = _u.u; uint32_t this_mask = (provided_mask ? provided_mask : full_mask); if ((this_mask & full_mask) != this_mask) return errh->error("mask 0x%X out of range (0-0x%X)", provided_mask, full_mask); if (_op == OP_GT || _op == OP_LT) { // Check for comparisons that are always true or false. if ((_op == OP_LT && (data == 0 || data > this_mask)) || (_op == OP_GT && data >= this_mask)) { bool will_be = (_op == OP_LT && data > this_mask ? !_op_negated : _op_negated); errh->warning("relation '%s %u' is always %s (range 0-%u)", unparse_op().cc(), data, (will_be ? "true" : "false"), this_mask); _u.u = _mask.u = 0; _op_negated = !will_be; _op = OP_EQ; return 0; } // value < X == !(value > (X - 1)) if (_op == OP_LT) { _u.u--; _op_negated = !_op_negated; _op = OP_GT; } _u.u = (_u.u << shift) | ((1 << shift) - 1); _mask.u = (this_mask << shift) | ((1 << shift) - 1); // Want (_u.u & _mask.u) == _u.u. // So change 'tcp[0] & 5 > 2' into the equivalent 'tcp[0] & 5 > 1': // find the highest bit in _u that is not set in _mask, // and turn on all lower bits. if ((_u.u & _mask.u) != _u.u) { uint32_t full_mask_u = (full_mask << shift) | ((1 << shift) - 1); uint32_t missing_bits = (_u.u & _mask.u) ^ (_u.u & full_mask_u); uint32_t add_mask = 0xFFFFFFFFU >> ffs_msb(missing_bits); _u.u = (_u.u | add_mask) & _mask.u; } return 0; } if (data > full_mask) return errh->error("value %u out of range (0-%u)", data, full_mask); _u.u = data << shift; _mask.u = this_mask << shift; return 0;}StringIPFilter::Primitive::unparse_type(int srcdst, int type){ StringAccum sa; switch (srcdst) { case SD_SRC: sa << "src "; break; case SD_DST: sa << "dst "; break; case SD_OR: sa << "src or dst "; break; case SD_AND: sa << "src and dst "; break; } switch (type) { case TYPE_NONE: sa << "<none>"; break; case TYPE_HOST: sa << "ip host"; break; case TYPE_PROTO: sa << "proto"; break; case TYPE_IPFRAG: sa << "ip frag"; break; case TYPE_PORT: sa << "port"; break; case TYPE_TCPOPT: sa << "tcp opt"; break; case TYPE_NET: sa << "ip net"; break; case TYPE_IPUNFRAG: sa << "ip unfrag"; break; case TYPE_IPECT: sa << "ip ect"; break; case TYPE_IPCE: sa << "ip ce"; break; default: if (type & TYPE_FIELD) { switch (type) { case FIELD_IPLEN: sa << "ip len"; break; case FIELD_ID: sa << "ip id"; break; case FIELD_VERSION: sa << "ip vers"; break; case FIELD_HL: sa << "ip hl"; break; case FIELD_TOS: sa << "ip tos"; break; case FIELD_DSCP: sa << "ip dscp"; break; case FIELD_TTL: sa << "ip ttl"; break; case FIELD_TCP_WIN: sa << "tcp win"; break; case FIELD_ICMP_TYPE: sa << "icmp type"; break; default: if (type & FIELD_PROTO_MASK) sa << unparse_transp_proto((type & FIELD_PROTO_MASK) >> FIELD_PROTO_SHIFT); else sa << "ip"; sa << "[...]"; break; } } else sa << "<unknown type " << type << ">"; break; } return sa.take_string();}StringIPFilter::Primitive::unparse_transp_proto(int transp_proto){ switch (transp_proto) { case UNKNOWN: return ""; case IP_PROTO_ICMP: return "icmp"; case IP_PROTO_IGMP: return "igmp"; case IP_PROTO_IPIP: return "ipip"; case IP_PROTO_TCP: return "tcp"; case IP_PROTO_UDP: return "udp"; case IP_PROTO_TCP_OR_UDP: return "tcpudp"; case IP_PROTO_TRANSP: return "transp"; default: return "ip proto " + String(transp_proto); }}StringIPFilter::Primitive::unparse_type() const{ return unparse_type(_srcdst, _type);}StringIPFilter::Primitive::unparse_op() const{ if (_op == OP_GT) return (_op_negated ? "<=" : ">"); else if (_op == OP_LT) return (_op_negated ? ">=" : "<"); else return (_op_negated ? "!=" : "=");}voidIPFilter::Primitive::simple_negate(){ assert(negation_is_simple()); _op_negated = !_op_negated; if (_type == TYPE_PROTO && _mask.u == 0xFF) _transp_proto = (_op_negated ? UNKNOWN : _u.i);}intIPFilter::Primitive::check(const Primitive &p, uint32_t provided_mask, ErrorHandler *errh){ int old_srcdst = _srcdst; // if _type is erroneous, return -1 right away if (_type < 0) return -1; // set _type if it was not specified if (!_type) { retry: switch (_data) { case TYPE_HOST: case TYPE_NET: case TYPE_TCPOPT: _type = _data; if (!_srcdst) _srcdst = p._srcdst; break; case TYPE_PROTO: _type = TYPE_PROTO; break; case TYPE_PORT: _type = TYPE_PORT; if (!_srcdst) _srcdst = p._srcdst; if (_transp_proto == UNKNOWN) _transp_proto = p._transp_proto; break; case TYPE_INT: if (!(p._type & TYPE_FIELD) && p._type != TYPE_PROTO && p._type != TYPE_PORT) return errh->error("specify header field or 'port'"); _data = p._type; goto retry; case TYPE_NONE: if (_transp_proto != UNKNOWN) _type = TYPE_PROTO; else return errh->error("partial directive"); break; default: if (_data & TYPE_FIELD) { _type = _data; if ((_type & FIELD_PROTO_MASK) && _transp_proto == UNKNOWN) _transp_proto = (_type & FIELD_PROTO_MASK) >> FIELD_PROTO_SHIFT; } else return errh->error("unknown type '%s'", unparse_type(0, _data).cc()); break; } } // check that _data and _type agree
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -