From 8a87055fccbbdfe8630d02d30932ab6f212d2f7c Mon Sep 17 00:00:00 2001 From: Jan Grashoefer Date: Mon, 13 Jun 2016 21:01:46 +0200 Subject: [PATCH] Fixed table expiration evaluation. The expiration attribute expression is now evaluated for every use. Thus later adjustments of the value (e.g. by redefining a const) will now take effect. Values less than 0 will disable expiration. --- src/Val.cc | 77 ++++++++++++------- src/Val.h | 9 ++- .../Baseline/language.expire-redef/output | 5 ++ testing/btest/language/expire-redef.bro | 38 +++++++++ 4 files changed, 98 insertions(+), 31 deletions(-) create mode 100644 testing/btest/Baseline/language.expire-redef/output create mode 100644 testing/btest/language/expire-redef.bro diff --git a/src/Val.cc b/src/Val.cc index 331aff3bcf..19fa56cd37 100644 --- a/src/Val.cc +++ b/src/Val.cc @@ -1323,7 +1323,7 @@ void TableVal::Init(TableType* t) { ::Ref(t); table_type = t; - expire_expr = 0; + expire_func = 0; expire_time = 0; expire_cookie = 0; timer = 0; @@ -1350,7 +1350,8 @@ TableVal::~TableVal() delete subnets; Unref(attrs); Unref(def_val); - Unref(expire_expr); + Unref(expire_func); + Unref(expire_time); } void TableVal::RemoveAll() @@ -1399,8 +1400,8 @@ void TableVal::SetAttrs(Attributes* a) Attr* ef = attrs->FindAttr(ATTR_EXPIRE_FUNC); if ( ef ) { - expire_expr = ef->AttrExpr(); - expire_expr->Ref(); + expire_func = ef->AttrExpr(); + expire_func->Ref(); } } @@ -1410,14 +1411,8 @@ void TableVal::CheckExpireAttr(attr_tag at) if ( a ) { - Val* timeout = a->AttrExpr()->Eval(0); - if ( ! timeout ) - { - a->AttrExpr()->Error("value of timeout not fixed"); - return; - } - - expire_time = timeout->AsInterval(); + expire_time = a->AttrExpr(); + expire_time->Ref(); if ( timer ) timer_mgr->Cancel(timer); @@ -1791,7 +1786,7 @@ Val* TableVal::Lookup(Val* index, bool use_default_val) if ( attrs && attrs->FindAttr(ATTR_EXPIRE_READ) ) { v->SetExpireAccess(network_time); - if ( LoggingAccess() && expire_time ) + if ( LoggingAccess() && ExpirationEnabled() ) ReadOperation(index, v); } @@ -1822,7 +1817,7 @@ Val* TableVal::Lookup(Val* index, bool use_default_val) if ( attrs && attrs->FindAttr(ATTR_EXPIRE_READ) ) { v->SetExpireAccess(network_time); - if ( LoggingAccess() && expire_time ) + if ( LoggingAccess() && ExpirationEnabled() ) ReadOperation(index, v); } @@ -1880,7 +1875,7 @@ TableVal* TableVal::LookupSubnetValues(const SubNetVal* search) if ( attrs && attrs->FindAttr(ATTR_EXPIRE_READ) ) { entry->SetExpireAccess(network_time); - if ( LoggingAccess() && expire_time ) + if ( LoggingAccess() && ExpirationEnabled() ) ReadOperation(s, entry); } } @@ -2174,6 +2169,10 @@ void TableVal::DoExpire(double t) if ( ! type ) return; // FIX ME ### + double timeout = GetExpireTime(); + if ( timeout < 0 ) + return; // Skip in case of invalid expiration value + PDict(TableEntryVal)* tbl = AsNonConstTable(); if ( ! expire_cookie ) @@ -2197,11 +2196,11 @@ void TableVal::DoExpire(double t) // correct, so we just need to wait. } - else if ( v->ExpireAccessTime() + expire_time < t ) + else if ( v->ExpireAccessTime() + timeout < t ) { Val* val = v->Value(); - if ( expire_expr ) + if ( expire_func ) { Val* idx = RecoverIndex(k); double secs = CallExpireFunc(idx); @@ -2221,7 +2220,7 @@ void TableVal::DoExpire(double t) { // User doesn't want us to expire // this now. - v->SetExpireAccess(network_time - expire_time + secs); + v->SetExpireAccess(network_time - timeout + secs); delete k; continue; } @@ -2258,9 +2257,29 @@ void TableVal::DoExpire(double t) InitTimer(table_expire_delay); } +double TableVal::GetExpireTime() + { + if ( expire_time ) + { + Val* timeout = expire_time->Eval(0); + if ( timeout && (timeout->AsInterval() >= 0) ) + { + return timeout->AsInterval(); + } + else + { + // invalid expiration interval + expire_time = 0; + if ( timer ) + timer_mgr->Cancel(timer); + } + } + return -1; + } + double TableVal::CallExpireFunc(Val* idx) { - if ( ! expire_expr ) + if ( ! expire_func ) { Unref(idx); return 0; @@ -2285,7 +2304,7 @@ double TableVal::CallExpireFunc(Val* idx) try { - Val* vf = expire_expr->Eval(0); + Val* vf = expire_func->Eval(0); if ( ! vf ) { @@ -2319,11 +2338,15 @@ double TableVal::CallExpireFunc(Val* idx) void TableVal::ReadOperation(Val* index, TableEntryVal* v) { + // Skip in case of invalid expiration value + double timeout = GetExpireTime(); + if ( timeout < 0 ) + return; // In theory we need to only propagate one update per &read_expire // interval to prevent peers from expiring intervals. To account for // practical issues such as latency, we send one update every half // &read_expire. - if ( network_time - v->LastReadUpdate() > expire_time / 2 ) + if ( network_time - v->LastReadUpdate() > timeout / 2 ) { StateAccess::Log(new StateAccess(OP_READ_IDX, this, index)); v->SetLastReadUpdate(network_time); @@ -2362,11 +2385,9 @@ bool TableVal::DoSerialize(SerialInfo* info) const state->did_index = false; info->s->WriteOpenTag(table_type->IsSet() ? "set" : "table"); - if ( ! SERIALIZE(expire_time) ) - return false; - SERIALIZE_OPTIONAL(attrs); - SERIALIZE_OPTIONAL(expire_expr); + SERIALIZE_OPTIONAL(expire_time); + SERIALIZE_OPTIONAL(expire_func); // Make sure nobody kills us in between. const_cast(this)->Ref(); @@ -2491,13 +2512,11 @@ bool TableVal::DoUnserialize(UnserialInfo* info) { DO_UNSERIALIZE(MutableVal); - if ( ! UNSERIALIZE(&expire_time) ) - return false; - Init((TableType*) type); UNSERIALIZE_OPTIONAL(attrs, Attributes::Unserialize(info)); - UNSERIALIZE_OPTIONAL(expire_expr, Expr::Unserialize(info)); + UNSERIALIZE_OPTIONAL(expire_time, Expr::Unserialize(info)); + UNSERIALIZE_OPTIONAL(expire_func, Expr::Unserialize(info)); while ( true ) { diff --git a/src/Val.h b/src/Val.h index a49a2e2235..542d18e09a 100644 --- a/src/Val.h +++ b/src/Val.h @@ -862,6 +862,11 @@ protected: // Calculates default value for index. Returns 0 if none. Val* Default(Val* index); + // Returns true if item expiration is defined. + bool ExpirationEnabled() { return expire_time != 0; } + // Returns the expiration time defined by create, read + // or write expire attribute or -1 for invalid values. + double GetExpireTime(); // Calls &expire_func and returns its return interval; // takes ownership of the reference. double CallExpireFunc(Val *idx); @@ -874,8 +879,8 @@ protected: TableType* table_type; CompositeHash* table_hash; Attributes* attrs; - double expire_time; - Expr* expire_expr; + Expr* expire_time; + Expr* expire_func; TableValTimer* timer; IterCookie* expire_cookie; PrefixTable* subnets; diff --git a/testing/btest/Baseline/language.expire-redef/output b/testing/btest/Baseline/language.expire-redef/output new file mode 100644 index 0000000000..d0a3a5dac1 --- /dev/null +++ b/testing/btest/Baseline/language.expire-redef/output @@ -0,0 +1,5 @@ +Run 0 +Run 1 +Run 2 +Expired: 0 --> some data +Run 3 diff --git a/testing/btest/language/expire-redef.bro b/testing/btest/language/expire-redef.bro new file mode 100644 index 0000000000..044e6bb950 --- /dev/null +++ b/testing/btest/language/expire-redef.bro @@ -0,0 +1,38 @@ +# @TEST-EXEC: btest-bg-run broproc bro %INPUT +# @TEST-EXEC: btest-bg-wait -k 5 +# @TEST-EXEC: cat broproc/.stdout > output +# @TEST-EXEC: btest-diff output + + +@load frameworks/communication/listen + +const exp_val = -1sec &redef; + +global expired: function(tbl: table[int] of string, idx: int): interval; +global data: table[int] of string &write_expire=exp_val &expire_func=expired; + +redef table_expire_interval = 1sec; +redef exp_val = 3sec; + +global runs = 0; +event do_it() + { + print fmt("Run %s", runs); + + ++runs; + if ( runs < 4 ) + schedule 1sec { do_it() }; + } + + +function expired(tbl: table[int] of string, idx: int): interval + { + print fmt("Expired: %s --> %s", idx, tbl[idx]); + return 0sec; + } + +event bro_init() &priority=-10 + { + data[0] = "some data"; + schedule 1sec { do_it() }; + }