filters: strange protocol restarts
Hello community!! And again I need to spread some light on BIRD behavior: why bird restarts protocol instead of just reloading it when no filter changes in configuration when using prefix set constant? There is simple configuration and steps to reproduce this ------------------------------------------------------------------------- # Configure logging log syslog all; log stderr all; router id 172.16.1.1; protocol device devices { scan time 15; } ### KRT 10 table rt_10; # This function does not forces "kernel" protocol to reload. function f1(prefix pfx) { if pfx ~ [ 192.168.0.0/16+ ] then return false; return true; } protocol kernel kernel10 { table rt_10; persist no; scan time 15; learn yes; device routes yes; kernel table 10; import where f1(net); export none; } ### KRT 20 table rt_20; # And this does, "kernel" protocol restarts without changing PS_1. define PS_1 = [ 192.168.0.0/16+ ]; function f2(prefix pfx) { if pfx ~ PS_1 then return false; return true; } There is nothing difficult in config: 2 functions f1() and f2() that do same thing except f2() matches specified as parameter prefix to a defined constant of type prefix set (PS_1). Starting bird, and invoking three times on console command to reconfigure bird (birdc configure) without altering configuration file I get following in my syslog: -------------------------------------------------------------------------------- Aug 21 18:50:54 gw1 bird: Started Aug 21 18:50:59 gw1 bird: Reconfiguring Aug 21 18:50:59 gw1 bird: Reloading protocol kernel20 Aug 21 18:50:59 gw1 bird: Restarting protocol kernel20 Aug 21 18:50:59 gw1 bird: Reconfigured Aug 21 18:51:08 gw1 bird: Reconfiguring Aug 21 18:51:08 gw1 bird: Reloading protocol kernel20 Aug 21 18:51:08 gw1 bird: Restarting protocol kernel20 Aug 21 18:51:08 gw1 bird: Reconfigured Aug 21 18:51:08 gw1 bird: Reconfiguring Aug 21 18:51:08 gw1 bird: Reloading protocol kernel20 Aug 21 18:51:08 gw1 bird: Restarting protocol kernel20 Aug 21 18:51:08 gw1 bird: Reconfigured Same thing can be reproduced with other protocols. This is not wery good to restart protocol when no REAL things changed in its filter, as it purges all routes from kernel table, and reinstalls them again causing flap when huge number of routes needed to be reinstalled. Can some one explain to me this behavior of BIRD? Does this mean that usage of constats with type prefix set is not recommended? Thanks for your support. -- SP5474-RIPE Sergey Popovich
On Wed, Aug 21, 2013 at 07:02:19PM +0300, Sergey Popovich wrote:
Hello community!!
And again I need to spread some light on BIRD behavior: why bird restarts protocol instead of just reloading it when no filter changes in configuration when using prefix set constant? ... Can some one explain to me this behavior of BIRD? Does this mean that usage of constats with type prefix set is not recommended?
Hello This is just a stupid mistake. Sets were not properly compared. Use attached patch. -- Elen sila lumenn' omentielvo Ondrej 'SanTiago' Zajicek (email: santiago@crfreenet.org) OpenPGP encrypted e-mails preferred (KeyID 0x11DEADC3, wwwkeys.pgp.net) "To err is human -- to blame it on a computer is even more so."
Среда, 21 августа 2013, 20:46 +02:00 от Ondrej Zajicek <santiago@crfreenet.org>:
On Wed, Aug 21, 2013 at 07:02:19PM +0300, Sergey Popovich wrote:
Hello community!!
And again I need to spread some light on BIRD behavior: why bird restarts protocol instead of just reloading it when no filter changes in configuration when using prefix set constant? ... Can some one explain to me this behavior of BIRD? Does this mean that usage of constats with type prefix set is not recommended?
Hello
This is just a stupid mistake. Sets were not properly compared. Use attached patch. Huge thanks! Tested and found working!
-- Elen sila lumenn' omentielvo
Ondrej 'SanTiago' Zajicek (email: santiago@crfreenet.org) OpenPGP encrypted e-mails preferred (KeyID 0x11DEADC3, wwwkeys.pgp.net) "To err is human -- to blame it on a computer is even more so."
-- SP5474-RIPE Sergey Popovich
В письме от 21 августа 2013 22:58:48 Вы написали:
Среда, 21 августа 2013, 20:46 +02:00 от Ondrej Zajicek <santiago@crfreenet.org>:
Can some one explain to me this behavior of BIRD? Does this mean that usage of constats with type prefix set is not recommended?
Hello
This is just a stupid mistake. Sets were not properly compared. Use attached patch.
Huge thanks! Tested and found working!
There are at least one other caveat related to comparison, not covered by this patch: How to determine if clist, eclist are empty? Statements like bgp_community_list = -empty- or bgp_ext_community_list = --empty-- gives filter runtime error. Same reason for comparing bgppath type with +empty+ (i.e. bgp_path = +empty+), however for this we could use something like bgp_path.len = 0)? Interesting thing about comparison I found in filter/test.conf2: clist ~ [(*,*)] but this is not documented. So I consider to submit my attempt to address this caveat and add some other useful things related to comparison: * Move same_tree() for SET comparison and trie_same() for PREFIX_SET comparison into val_compare(). Now statements like bgp_community_list = -empty- and bgp_ext_community = --empty-- works. I think this could be done as there at least one function, that violates return code of val_compare() (-1,0,1): pm_path_compare(). * Intdodoce list_compare() to compare clist and eclist as arrays of u32, not much useful, but I think it does not broke something. This is done to extend val_compare() to handle all known filter types (probably). * Introduce path_compare() to compare bgppath type. Compares path length, and element by element, considering AS_PATH_SET more longer. Reason for adding same as with list_compare(). * Add minor optimisations on speed when comparing with int_cmp(), uint_cmp(), u64_cmp(), by removing branching. * Split comparison to T_VOID in val_compare() in two parts, when types are equal (add case to switch) and when are not. Do similar in val_in_range(). * Document -empty-, --empty-- and +empty+ special purpose constants. Also document S.empty statement for use with dynamic attributes. Another patch, just adds some other possibilities to check length of clist, eclist and ip address. Last could be used to determine AFI for which bird is build (see example function in documentation section on patch). Patches tested, and found working, at least for me. -- SP5474-RIPE Sergey Popovich
On Fri, Sep 27, 2013 at 02:35:45PM +0300, Sergey Popovich wrote:
There are at least one other caveat related to comparison, not covered by this patch:
How to determine if clist, eclist are empty? Statements like bgp_community_list = -empty- or bgp_ext_community_list = --empty-- gives filter runtime error. Same reason for comparing bgppath type with +empty+ (i.e. bgp_path = +empty+), however for this we could use something like bgp_path.len = 0)?
Interesting thing about comparison I found in filter/test.conf2: clist ~ [(*,*)] but this is not documented.
It is documented: Special operators include <cf/˜/ for "is element of a set" operation - it can be used on element and set of elements of the same type (returning true if element is contained in the given set), ... or on clist and pair/quad set (returning true if there is an element of the clist that is also a member of the pair/quad set)
So I consider to submit my attempt to address this caveat and add some other useful things related to comparison:
* Move same_tree() for SET comparison and trie_same() for PREFIX_SET comparison into val_compare().
Well, that was my first idea w.r.t. the original problem with reconfigure, but having nonsensical '<' offends my algebraic sense ;-) and there is no good reason to imlement some consistent arbitrary ordering on such objects. Good solution would be to have val_same(), which would work on all types (and would be used where equality is tested), while val_compare would only work on ordered objects. You are right that pm_path_compare() already returns some nonsense.
* Split comparison to T_VOID in val_compare() in two parts, when types are equal (add case to switch) and when are not. Do similar in val_in_range().
* Document -empty-, --empty-- and +empty+ special purpose constants. Also document S.empty statement for use with dynamic attributes.
Well, these constants are not documented on purpose, as they are more like ad-hoc constants for testing purposes, not much needed when you have X.empty operator. X.empty operator could be documented, i wanted to do it already, the only problem i have with it is its name. When i see 'X.empty', i would think that 'empty' mainly as adjective (predicate for emptiness-testing), not as verb (operator of emptying). Having some predicate that would return true/false whether the path/list/set is empty would be useful, but both cannot be named 'empty'. Perhaps name the former 'reset'? Or keep current 'empty' and use some other name for the predicate? Any comments on this? -- Elen sila lumenn' omentielvo Ondrej 'SanTiago' Zajicek (email: santiago@crfreenet.org) OpenPGP encrypted e-mails preferred (KeyID 0x11DEADC3, wwwkeys.pgp.net) "To err is human -- to blame it on a computer is even more so."
В письме от 27 сентября 2013 17:07:55 пользователь Ondrej Zajicek написал:
It is documented:
Special operators include <cf/˜/ for "is element of a set" operation - it can be used on element and set of elements of the same type (returning true if element is contained in the given set), ... or on clist and pair/quad set (returning true if there is an element of the clist that is also a member of the pair/quad set)
Yes, really. But is not clear to me how to use this to test for community/ext community is non-empty.
Well, that was my first idea w.r.t. the original problem with reconfigure, but having nonsensical '<' offends my algebraic sense ;-) and there is no good reason to imlement some consistent arbitrary ordering on such objects.
Good solution would be to have val_same(), which would work on all types (and would be used where equality is tested), while val_compare would only work on ordered objects.
You are right that pm_path_compare() already returns some nonsense.
Thats my first idea. See [PATCH 03/12] in series.
Well, these constants are not documented on purpose, as they are more like ad-hoc constants for testing purposes, not much needed when you have X.empty operator.
Sure, no problem with ad-hoc constants, let them be *internal*. I just fix comparison/assignment with these constants in [PATCH 03/12].
X.empty operator could be documented, i wanted to do it already, the only problem i have with it is its name. When i see 'X.empty', i would think that 'empty' mainly as adjective (predicate for emptiness-testing), not as verb (operator of emptying).
Having some predicate that would return true/false whether the path/list/set is empty would be useful, but both cannot be named 'empty'. Perhaps name the former 'reset'? Or keep current 'empty' and use some other name for the predicate? Any comments on this?
No problem with name 'reset', but I think we should leave 'empty' for extended attributes for compatibility and document only 'reset'. This was done in [PATCH 06/12]. To determine if clist or eclist is empty, I suggest to use 'len' operator as this currently implemented for bgppath type, which is done in [PATCH 05/12]. I start new patch series based on this thread with changes to filtering code. I hope BIRD developers and community found useful these patches. -- SP5474-RIPE Sergey Popovich
On Mon, Sep 30, 2013 at 09:21:02PM +0300, Sergey Popovich wrote:
I start new patch series based on this thread with changes to filtering code. I hope BIRD developers and community found useful these patches.
Thanks, i will comment each patch file independently. Your patches seems to be somewhat malformed, probably reformatted by e-mail client. I prefer receiving patches as attachments. -- Elen sila lumenn' omentielvo Ondrej 'SanTiago' Zajicek (email: santiago@crfreenet.org) OpenPGP encrypted e-mails preferred (KeyID 0x11DEADC3, wwwkeys.pgp.net) "To err is human -- to blame it on a computer is even more so."
В письме от 3 октября 2013 12:57:45 Вы написали:
On Mon, Sep 30, 2013 at 09:21:02PM +0300, Sergey Popovich wrote:
I start new patch series based on this thread with changes to filtering code. I hope BIRD developers and community found useful these patches.
Thanks, i will comment each patch file independently. Your patches seems to be somewhat malformed, probably reformatted by e-mail client. I prefer receiving patches as attachments.
Yes sorry for that, thats new e-mail agent. I will send next patches as attachment. Thanks for your interest to my patches. -- SP5474-RIPE Sergey Popovich
* Add minor optimisations on speed when comparing with int_cmp(), uint_cmp(), u64_cmp(), by removing branching. * Reorder type checks in val_compare() and val_in_range(). --- filter/filter.c | 64 ++++++++++++++++++++++++------------------------------- 1 file changed, 28 insertions(+), 36 deletions(-) diff --git a/filter/filter.c b/filter/filter.c index 0fc10f1..f92ee9a 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -112,25 +112,22 @@ pm_format(struct f_path_mask *p, byte *buf, unsigned int size) *buf = 0; } -static inline int int_cmp(int i1, int i2) +static inline int +int_cmp(int i1, int i2) { - if (i1 == i2) return 0; - if (i1 < i2) return -1; - else return 1; + return (i1 > i2) - (i1 < i2); } -static inline int uint_cmp(unsigned int i1, unsigned int i2) +static inline int +uint_cmp(unsigned int i1, unsigned int i2) { - if (i1 == i2) return 0; - if (i1 < i2) return -1; - else return 1; + return (i1 > i2) - (i1 < i2); } -static inline int u64_cmp(u64 i1, u64 i2) +static inline int +u64_cmp(u64 i1, u64 i2) { - if (i1 == i2) return 0; - if (i1 < i2) return -1; - else return 1; + return (i1 > i2) - (i1 < i2); } /** @@ -147,14 +144,11 @@ val_compare(struct f_val v1, struct f_val v2) { int rc; - if ((v1.type == T_VOID) && (v2.type == T_VOID)) - return 0; - if (v1.type == T_VOID) /* Hack for else */ - return -1; - if (v2.type == T_VOID) - return 1; - if (v1.type != v2.type) { + if (v1.type == T_VOID) /* Hack for else */ + return -1; + if (v2.type == T_VOID) + return 1; #ifndef IPV6 /* IP->Quad implicit conversion */ if ((v1.type == T_QUAD) && (v2.type == T_IP)) @@ -181,15 +175,13 @@ val_compare(struct f_val v1, struct f_val v2) case T_PREFIX: if (rc = ipa_compare(v1.val.px.ip, v2.val.px.ip)) return rc; - if (v1.val.px.len < v2.val.px.len) - return -1; - if (v1.val.px.len > v2.val.px.len) - return 1; - return 0; + return int_cmp(v1.val.px.len, v2.val.px.len); case T_PATH_MASK: return pm_path_compare(v1.val.path_mask, v2.val.path_mask); case T_STRING: return strcmp(v1.val.s, v2.val.s); + case T_VOID: + return 0; default: debug( "Compare of unknown entities: %x\n", v1.type ); return CMP_ERROR; @@ -408,18 +400,6 @@ val_in_range(struct f_val v1, struct f_val v2) if (res != CMP_ERROR) return res; - - if ((v1.type == T_PREFIX) && (v2.type == T_PREFIX_SET)) - return trie_match_fprefix(v2.val.ti, &v1.val.px); - - if ((v1.type == T_CLIST) && (v2.type == T_SET)) - return clist_match_set(v1.val.ad, v2.val.t); - - if ((v1.type == T_ECLIST) && (v2.type == T_SET)) - return eclist_match_set(v1.val.ad, v2.val.t); - - if ((v1.type == T_PATH) && (v2.type == T_SET)) - return as_path_match_set(v1.val.ad, v2.val.t); if (v2.type == T_SET) switch (v1.type) { @@ -436,7 +416,19 @@ val_in_range(struct f_val v1, struct f_val v2) return 0; return !! (val_simple_in_range(v1, n->from)); /* We turn CMP_ERROR into compared ok, and that's fine */ } + case T_PATH: + return as_path_match_set(v1.val.ad, v2.val.t); + case T_CLIST: + return clist_match_set(v1.val.ad, v2.val.t); + case T_ECLIST: + return eclist_match_set(v1.val.ad, v2.val.t); + } + else if (v2.type == T_PREFIX_SET) + switch (v1.type) { + case T_PREFIX: + return trie_match_fprefix(v2.val.ti, &v1.val.px); } + return CMP_ERROR; } -- 1.7.10.4
On Mon, Sep 30, 2013 at 09:22:15PM +0300, Sergey Popovich wrote:
* Add minor optimisations on speed when comparing with int_cmp(), uint_cmp(), u64_cmp(), by removing branching.
Applied, thanks. Although i replaced return (i1 > i2) - (i1 < i2); by return (int)(i1 > i2) - (int)(i1 < i2); Former depends on proper overflow when uint is converted to int, which would probably works (as we use -fno-strict-overflow).
* Reorder type checks in val_compare() and val_in_range().
I skipped this as i simplified set matching in val_in_range() anyways. val_in_range() could be changed to use switch based on the type of the second argument, question is whether efficiency gains are large enough to outweight loses in readability. -- Elen sila lumenn' omentielvo Ondrej 'SanTiago' Zajicek (email: santiago@crfreenet.org) OpenPGP encrypted e-mails preferred (KeyID 0x11DEADC3, wwwkeys.pgp.net) "To err is human -- to blame it on a computer is even more so."
* Intdodoce list_compare() to compare clist and eclist as arrays of u32. eclist stored as u64 with MSB first, so we could compare it also as u32. * Introduce path_compare() to compare bgppath type. Compares path length, and element by element, considering AS_PATH_SET more longer. --- filter/filter.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ nest/a-path.c | 7 ----- nest/attrs.h | 4 +++ 3 files changed, 80 insertions(+), 7 deletions(-) diff --git a/filter/filter.c b/filter/filter.c index f92ee9a..6f59193 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -130,6 +130,77 @@ u64_cmp(u64 i1, u64 i2) return (i1 > i2) - (i1 < i2); } +static int +list_compare(struct adata *a1, struct adata *a2) +{ + u32 *l1, *l2; + int l, rc; + + rc = (a1 > a2) - (a1 < a2); + if (!rc) + return rc; + if ((!!a1) != (!!a2)) + return rc; + + rc = uint_cmp(a1->length, a2->length); + if (rc) + return rc; + + l1 = int_set_get_data(a1); + l2 = int_set_get_data(a2); + l = int_set_get_size(a1); + + while (l--) { + rc = uint_cmp(*l1++, *l2++); + if (rc) + return rc; + } + + return rc; +} + +static int +path_compare(struct adata *a1, struct adata *a2) +{ + u8 *p1, *q1; + u8 *p2, *q2; + int l, rc; + + rc = (a1 > a2) - (a1 < a2); + if (!rc) + return rc; + if ((!!a1) != (!!a2)) + return rc; + + rc = int_cmp(as_path_getlen(a1), as_path_getlen(a2)); + if (rc) + return rc; + + p1 = a1->data; + q1 = p1 + a1->length; + p2 = a2->data; + q2 = p2 + a2->length; + + while ((p1 < q1) && (p2 < q2)) { + rc = uint_cmp(*p1++, *p2++); + if (rc) + return -rc; /* AS_PATH_SET longer */ + + l = *p1++; + rc = int_cmp(l, *p2++); + if (rc) + return rc; + + while (l--) { + rc = uint_cmp(get_as(p1++), get_as(p2++)); + if (rc) + return rc; + } + } + + return int_cmp((p1 < q1), (p2 < q2)); +} + /** * val_compare - compare two values * @v1: first value @@ -180,6 +251,11 @@ val_compare(struct f_val v1, struct f_val v2) return pm_path_compare(v1.val.path_mask, v2.val.path_mask); case T_STRING: return strcmp(v1.val.s, v2.val.s); + case T_CLIST: + case T_ECLIST: + return list_compare(v1.val.ad, v2.val.ad); + case T_PATH: + return path_compare(v1.val.ad, v2.val.ad); case T_VOID: return 0; default: diff --git a/nest/a-path.c b/nest/a-path.c index b181298..1679886 100644 --- a/nest/a-path.c +++ b/nest/a-path.c @@ -15,13 +15,6 @@ #include "lib/string.h" #include "filter/filter.h" -// static inline void put_as(byte *data, u32 as) { put_u32(data, as); } -// static inline u32 get_as(byte *data) { return get_u32(data); } - -#define put_as put_u32 -#define get_as get_u32 -#define BS 4 - struct adata * as_path_prepend(struct linpool *pool, struct adata *olda, u32 as) { diff --git a/nest/attrs.h b/nest/attrs.h index 44a23e1..38639ae 100644 --- a/nest/attrs.h +++ b/nest/attrs.h @@ -25,6 +25,10 @@ * to 16bit slot (like in 16bit AS_PATH). See RFC 4893 for details */ +#define put_as put_u32 +#define get_as get_u32 +#define BS 4 + struct f_tree; struct adata *as_path_prepend(struct linpool *pool, struct adata *olda, u32 as); -- 1.7.10.4
On Mon, Sep 30, 2013 at 09:24:18PM +0300, Sergey Popovich wrote:
* Intdodoce list_compare() to compare clist and eclist as arrays of u32. eclist stored as u64 with MSB first, so we could compare it also as u32.
* Introduce path_compare() to compare bgppath type. Compares path length, and element by element, considering AS_PATH_SET more longer.
I replaced this with simple memcmp() (in adata_same()) for val_same(). Ordering is IMHO useless for clists and paths. -- Elen sila lumenn' omentielvo Ondrej 'SanTiago' Zajicek (email: santiago@crfreenet.org) OpenPGP encrypted e-mails preferred (KeyID 0x11DEADC3, wwwkeys.pgp.net) "To err is human -- to blame it on a computer is even more so."
val_same() compares passed @v1 and @v2 and returns 1 if they are same, 0 if not and 999 in case their types not comparable. Change pm_path_compare() to return 1 - if @m1 and @m2 are the same and 0 overwise. Rename pm_path_compare() to pm_path_same() according to its return values. Use it in val_same() to compare path masks. --- filter/filter.c | 102 +++++++++++++++++++++++++++++-------------------------- filter/filter.h | 1 + 2 files changed, 54 insertions(+), 49 deletions(-) diff --git a/filter/filter.c b/filter/filter.c index 6f59193..c089d94 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -58,20 +58,6 @@ adata_empty(struct linpool *pool, int l) return res; } -static int -pm_path_compare(struct f_path_mask *m1, struct f_path_mask *m2) -{ - while (1) { - if ((!m1) || (!m2)) - return !((!m1) && (!m2)); - - /* FIXME: buggy, should return -1, 0, 1; but it doesn't matter */ - if ((m1->kind != m2->kind) || (m1->val != m2->val)) return 1; - m1 = m1->next; - m2 = m2->next; - } -} - u32 f_eval_asn(struct f_inst *expr); static void @@ -247,8 +233,6 @@ val_compare(struct f_val v1, struct f_val v2) if (rc = ipa_compare(v1.val.px.ip, v2.val.px.ip)) return rc; return int_cmp(v1.val.px.len, v2.val.px.len); - case T_PATH_MASK: - return pm_path_compare(v1.val.path_mask, v2.val.path_mask); case T_STRING: return strcmp(v1.val.s, v2.val.s); case T_CLIST: @@ -259,7 +243,50 @@ val_compare(struct f_val v1, struct f_val v2) case T_VOID: return 0; default: - debug( "Compare of unknown entities: %x\n", v1.type ); + return CMP_ERROR; + } +} + +static int +pm_path_same(struct f_path_mask *m1, struct f_path_mask *m2) +{ + while (1) { + if ((!m1) || (!m2)) + return (!m1) && (!m2); + + if ((m1->kind != m2->kind) || (m1->val != m2->val)) + return 0; + + m1 = m1->next; + m2 = m2->next; + } +} + +/** + * val_same - compare two values + * @v1: first value + * @v2: second value + * + * Compares two values and returns 1 if they are same, 0 if not + * and 999 on error. + */ +int +val_same(struct f_val v1, struct f_val v2) +{ + int rc; + + rc = val_compare(v1, v2); + if (rc != CMP_ERROR) + return !rc; + + switch (v1.type) { + case T_PATH_MASK: + return pm_path_same(v1.val.path_mask, v2.val.path_mask); + case T_SET: + return same_tree(v1.val.t, v2.val.t); + case T_PREFIX_SET: + return trie_same(v1.val.ti, v2.val.ti); + default: return CMP_ERROR; } } @@ -776,19 +803,19 @@ interpret(struct f_inst *what) /* Relational operators */ -#define COMPARE(x) \ +#define COMPARE(f,x) \ TWOARGS; \ - i = val_compare(v1, v2); \ - if (i==CMP_ERROR) \ + i = f(v1, v2); \ + if (i == CMP_ERROR) \ runtime( "Can't compare values of incompatible types" ); \ res.type = T_BOOL; \ res.val.i = (x); \ break; - case P('!','='): COMPARE(i!=0); - case P('=','='): COMPARE(i==0); - case '<': COMPARE(i==-1); - case P('<','='): COMPARE(i!=1); + case P('!','='): COMPARE(val_same, i == 0); + case P('=','='): COMPARE(val_same, i != 0); + case '<': COMPARE(val_compare, i < 0); + case P('<','='): COMPARE(val_compare, i <= 0); case '!': ONEARG; @@ -1478,31 +1505,8 @@ i_same(struct f_inst *f1, struct f_inst *f2) } break; case 'C': - { - struct f_val *v1 = (struct f_val *) f1->a1.p; - struct f_val *v2 = (struct f_val *) f2->a1.p; - - /* Handle some cases that are not handled by val_compare() - also T_PATH, T_CLIST and T_ECLIST does not work, - but you cannot easily create such constants */ - - if ((v1->type == T_SET) && (v2->type == T_SET)) - { - if (!same_tree(v1->val.t, v2->val.t)) - return 0; - break; - } - - if ((v1->type == T_PREFIX_SET) && (v2->type == T_PREFIX_SET)) - { - if (!trie_same(v1->val.ti, v2->val.ti)) - return 0; - break; - } - - if (val_compare(*v1 , *v2)) - return 0; - } + if (!val_same(* (struct f_val *) f1->a1.p, * (struct f_val *) f2->a1.p)) + return 0; break; case 'V': if (strcmp((char *) f1->a2.p, (char *) f2->a2.p)) diff --git a/filter/filter.h b/filter/filter.h index dcac825..a3de530 100644 --- a/filter/filter.h +++ b/filter/filter.h @@ -116,6 +116,7 @@ int filter_same(struct filter *new, struct filter *old); int i_same(struct f_inst *f1, struct f_inst *f2); int val_compare(struct f_val v1, struct f_val v2); +int val_same(struct f_val v1, struct f_val v2); int tree_compare(const void *p1, const void *p2); void val_print(struct f_val v); -- 1.7.10.4
On Mon, Sep 30, 2013 at 09:27:24PM +0300, Sergey Popovich wrote:
val_same() compares passed @v1 and @v2 and returns 1 if they are same, 0 if not and 999 in case their types not comparable.
Change pm_path_compare() to return 1 - if @m1 and @m2 are the same and 0 overwise. Rename pm_path_compare() to pm_path_same() according to its return values. Use it in val_same() to compare path masks.
Thanks, applied. Although with minor changes - val_same() is now supposed to work on all possible values as inputs, so it does not return CMP_ERROR (different types would return 0).
@@ -1478,31 +1505,8 @@ i_same(struct f_inst *f1, struct f_inst *f2) } break; case 'C': - { + if (!val_same(* (struct f_val *) f1->a1.p, * (struct f_val *) f2->a1.p)) + return 0;
BTW, with your val_same() this code had a bug - CMP_ERROR from different types get converted to same. -- Elen sila lumenn' omentielvo Ondrej 'SanTiago' Zajicek (email: santiago@crfreenet.org) OpenPGP encrypted e-mails preferred (KeyID 0x11DEADC3, wwwkeys.pgp.net) "To err is human -- to blame it on a computer is even more so."
* Remove duplicate f_eval_asn() declaration: already in filter/filter.h * Move pm_format() from filter/filter.c to nest/a-path.c where other pm_* functions reside and also similar *_set_format() family already resides in nest/a-set.c. * Move tree_compare() from filter/filter.c to filter/tree.c and make it static as it only used in qsort(). Also remove definition from filter/filter.h. * Inline fprefix_get_bounds() from filter/filter.c in trie_add_prefix() at filter/filter.h as there is only one place where this code used. * Move tree_node_print() and tree_print() from filter/filter.c to filter/tree.c Make tree_node_print() static. Add global comment to tree_print() and declare it in filter.h. This is similar to trie_print() from filter/trie.c * Make printing of zero prefix in trie_print() at filter/trie.c to print AFI specific value. --- filter/filter.c | 93 ------------------------------------------------------- filter/filter.h | 19 +++++++++--- filter/tree.c | 41 ++++++++++++++++++++++++ filter/trie.c | 2 +- nest/a-path.c | 38 +++++++++++++++++++++++ nest/attrs.h | 1 + 6 files changed, 95 insertions(+), 99 deletions(-) diff --git a/filter/filter.c b/filter/filter.c index c089d94..7e91c71 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -58,46 +58,6 @@ adata_empty(struct linpool *pool, int l) return res; } -u32 f_eval_asn(struct f_inst *expr); - -static void -pm_format(struct f_path_mask *p, byte *buf, unsigned int size) -{ - byte *end = buf + size - 16; - - while (p) - { - if (buf > end) - { - strcpy(buf, " ..."); - return; - } - - switch(p->kind) - { - case PM_ASN: - buf += bsprintf(buf, " %u", p->val); - break; - - case PM_QUESTION: - buf += bsprintf(buf, " ?"); - break; - - case PM_ASTERISK: - buf += bsprintf(buf, " *"); - break; - - case PM_ASN_EXPR: - buf += bsprintf(buf, " %u", f_eval_asn((struct f_inst *) p->val)); - break; - } - - p = p->next; - } - - *buf = 0; -} - static inline int int_cmp(int i1, int i2) { @@ -291,30 +251,6 @@ val_same(struct f_val v1, struct f_val v2) } } -int -tree_compare(const void *p1, const void *p2) -{ - return val_compare((* (struct f_tree **) p1)->from, (* (struct f_tree **) p2)->from); -} - -void -fprefix_get_bounds(struct f_prefix *px, int *l, int *h) -{ - *l = *h = px->len & LEN_MASK; - - if (px->len & LEN_MINUS) - *l = 0; - - else if (px->len & LEN_PLUS) - *h = MAX_PREFIX_LENGTH; - - else if (px->len & LEN_RANGE) - { - *l = 0xff & (px->len >> 16); - *h = 0xff & (px->len >> 8); - } -} - /* * val_simple_in_range - check if @v1 ~ @v2 for everything except sets */ @@ -535,35 +471,6 @@ val_in_range(struct f_val v1, struct f_val v2) return CMP_ERROR; } -static void -tree_node_print(struct f_tree *t, char **sep) -{ - if (t == NULL) - return; - - tree_node_print(t->left, sep); - - logn(*sep); - val_print(t->from); - if (val_compare(t->from, t->to) != 0) - { - logn( ".." ); - val_print(t->to); - } - *sep = ", "; - - tree_node_print(t->right, sep); -} - -static void -tree_print(struct f_tree *t) -{ - char *sep = ""; - logn( "[" ); - tree_node_print(t, &sep); - logn( "] " ); -} - /* * val_print - format filter value */ diff --git a/filter/filter.h b/filter/filter.h index a3de530..9e60ef8 100644 --- a/filter/filter.h +++ b/filter/filter.h @@ -78,6 +78,7 @@ struct f_inst *f_generate_roa_check(struct symbol *sym, struct f_inst *prefix, s struct f_tree *build_tree(struct f_tree *); struct f_tree *find_tree(struct f_tree *t, struct f_val val); int same_tree(struct f_tree *t1, struct f_tree *t2); +void tree_print(struct f_tree *t); struct f_trie *f_new_trie(linpool *lp); void trie_add_prefix(struct f_trie *t, ip_addr px, int plen, int l, int h); @@ -85,13 +86,23 @@ int trie_match_prefix(struct f_trie *t, ip_addr px, int plen); int trie_same(struct f_trie *t1, struct f_trie *t2); void trie_print(struct f_trie *t); -void fprefix_get_bounds(struct f_prefix *px, int *l, int *h); - static inline void trie_add_fprefix(struct f_trie *t, struct f_prefix *px) { int l, h; - fprefix_get_bounds(px, &l, &h); + + l = h = px->len & LEN_MASK; + + if (px->len & LEN_MINUS) + l = 0; + else if (px->len & LEN_PLUS) + h = MAX_PREFIX_LENGTH; + else if (px->len & LEN_RANGE) + { + l = 0xff & (px->len >> 16); + h = 0xff & (px->len >> 8); + } + trie_add_prefix(t, px->ip, px->len & LEN_MASK, l, h); } @@ -117,8 +128,6 @@ int i_same(struct f_inst *f1, struct f_inst *f2); int val_compare(struct f_val v1, struct f_val v2); int val_same(struct f_val v1, struct f_val v2); -int tree_compare(const void *p1, const void *p2); - void val_print(struct f_val v); #define F_NOP 0 diff --git a/filter/tree.c b/filter/tree.c index f6ab75b..4690f85 100644 --- a/filter/tree.c +++ b/filter/tree.c @@ -53,6 +53,12 @@ build_tree_rec(struct f_tree **buf, int l, int h) return n; } +static int +tree_compare(const void *p1, const void *p2) +{ + return val_compare((* (struct f_tree **) p1)->from, + (* (struct f_tree **) p2)->from); +} /** * build_tree @@ -132,3 +138,38 @@ same_tree(struct f_tree *t1, struct f_tree *t2) return 0; return 1; } + +static void +tree_node_print(struct f_tree *t, char **sep) +{ + if (t == NULL) + return; + + tree_node_print(t->left, sep); + + logn(*sep); + val_print(t->from); + if (val_compare(t->from, t->to) != 0) + { + logn( ".." ); + val_print(t->to); + } + *sep = ", "; + + tree_node_print(t->right, sep); +} + +/** + * tree_print + * @t: tree to be printed + * + * Prints the tree to the log buffer. + */ +void +tree_print(struct f_tree *t) +{ + char *sep = ""; + logn( "[" ); + tree_node_print(t, &sep); + logn( "] " ); +} diff --git a/filter/trie.c b/filter/trie.c index 581332c..f42afb8 100644 --- a/filter/trie.c +++ b/filter/trie.c @@ -293,7 +293,7 @@ trie_print(struct f_trie *t) logn("["); if (t->zero) { - logn("0.0.0.0/0"); + logn("%I/%d", IPA_NONE, 0); sep = ", "; } trie_node_print(&t->root, &sep); diff --git a/nest/a-path.c b/nest/a-path.c index 1679886..cf129c1 100644 --- a/nest/a-path.c +++ b/nest/a-path.c @@ -434,6 +434,44 @@ pm_mark(struct pm_pos *pos, int i, int plen, int *nl, int *nh) *nh = j; } +void +pm_format(struct f_path_mask *p, byte *buf, unsigned int size) +{ + byte *end = buf + size - 16; + + while (p) + { + if (buf > end) + { + strcpy(buf, " ..."); + return; + } + + switch(p->kind) + { + case PM_ASN: + buf += bsprintf(buf, " %u", p->val); + break; + + case PM_QUESTION: + buf += bsprintf(buf, " ?"); + break; + + case PM_ASTERISK: + buf += bsprintf(buf, " *"); + break; + + case PM_ASN_EXPR: + buf += bsprintf(buf, " %u", f_eval_asn((struct f_inst *) p->val)); + break; + } + + p = p->next; + } + + *buf = 0; +} + /* AS path matching is nontrivial. Because AS path can * contain sets, it is not a plain wildcard matching. A set * in an AS path is interpreted as it might represent any diff --git a/nest/attrs.h b/nest/attrs.h index 38639ae..9468355 100644 --- a/nest/attrs.h +++ b/nest/attrs.h @@ -56,6 +56,7 @@ struct f_path_mask { }; int as_path_match(struct adata *path, struct f_path_mask *mask); +void pm_format(struct f_path_mask *p, byte *buf, unsigned int size); /* a-set.c */ -- 1.7.10.4
On Mon, Sep 30, 2013 at 09:27:31PM +0300, Sergey Popovich wrote:
* Remove duplicate f_eval_asn() declaration: already in filter/filter.h
* Move tree_compare() from filter/filter.c to filter/tree.c and make it static as it only used in qsort(). Also remove definition from filter/filter.h.
* Make printing of zero prefix in trie_print() at filter/trie.c to print AFI specific value.
Applied, thanks.
* Move pm_format() from filter/filter.c to nest/a-path.c where other pm_* functions reside and also similar *_set_format() family already resides in nest/a-set.c.
* Move tree_node_print() and tree_print() from filter/filter.c to filter/tree.c Make tree_node_print() static. Add global comment to tree_print() and declare it in filter.h. This is similar to trie_print() from filter/trie.c
This is a good idea, but i have currently some significant changes to these functions in another branch, So i would postpone this to avoid unnecessary conflicts.
* Inline fprefix_get_bounds() from filter/filter.c in trie_add_prefix() at filter/filter.h as there is only one place where this code used.
I don't think this is good idea. Even if fprefix_get_bounds() should be inlined, then it should be done by converting it to static inline function to keep clean semantic separation. -- Elen sila lumenn' omentielvo Ondrej 'SanTiago' Zajicek (email: santiago@crfreenet.org) OpenPGP encrypted e-mails preferred (KeyID 0x11DEADC3, wwwkeys.pgp.net) "To err is human -- to blame it on a computer is even more so."
Currently isn't possible to determine if clist or eclist defined (using defined() always return true for extended attributes of clist and eclist type, as they are special cased in P('e','s') instruction in interpret() function) or even known number of entries in these list. One of the way to determine if extended attribute of such type is empty is compare it to appropriate predefined constant: "- EMPTY -" for clist type, and "-- EMPTY --" for eclist type. However these constants, among with "+ EMPTY +" for bgppath type are intended for internal use and testing purposes and never documented. Add C.len operator just like for bgppath type to get number of elements in lists. Thats also enables to check if list is empty by checking if number of elements is zero. --- doc/bird.sgml | 2 ++ filter/filter.c | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index f43eb4b..050acf3 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1077,6 +1077,8 @@ incompatible with each other (that is to prevent you from shooting in the foot). no literals of this type. There are three special operators on clists: + <cf><m/C/.len</cf> returns the length of clist <m/C/. + <cf>add(<m/C/,<m/P/)</cf> adds pair (or quad) <m/P/ to clist <m/C/ and returns the result. If item <m/P/ is already in clist <m/C/, it does nothing. <m/P/ may also be a clist, diff --git a/filter/filter.c b/filter/filter.c index 7e91c71..8164ead 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -1065,7 +1065,9 @@ interpret(struct f_inst *what) switch(v1.type) { case T_PREFIX: res.val.i = v1.val.px.len; break; case T_PATH: res.val.i = as_path_getlen(v1.val.ad); break; - default: runtime( "Prefix or path expected" ); + case T_CLIST: res.val.i = v1.val.ad->length / 4; break; + case T_ECLIST: res.val.i = v1.val.ad->length / 8; break; + default: runtime( "Prefix, path, clist or eclist expected" ); } break; case P('c','p'): /* Convert prefix to ... */ -- 1.7.10.4
On Mon, Sep 30, 2013 at 09:30:38PM +0300, Sergey Popovich wrote:
Add C.len operator just like for bgppath type to get number of elements in lists. Thats also enables to check if list is empty by checking if number of elements is zero.
Applied, thanks. Although with minor changes: + case T_CLIST: res.val.i = int_set_get_size(v1.val.ad); break; + case T_ECLIST: res.val.i = ec_set_get_size(v1.val.ad); break; instead of: + case T_CLIST: res.val.i = v1.val.ad->length / 4; break; + case T_ECLIST: res.val.i = v1.val.ad->length / 8; break; -- Elen sila lumenn' omentielvo Ondrej 'SanTiago' Zajicek (email: santiago@crfreenet.org) OpenPGP encrypted e-mails preferred (KeyID 0x11DEADC3, wwwkeys.pgp.net) "To err is human -- to blame it on a computer is even more so."
Use RESET as predicate to set to empty value of dynamic attribute value in filters. Leave EMPTY for backwad compatibility, altought it not documented enywhere. Uninline f_generate_empty() and refactor code in error path. Remove MATCH and CONTAINS from keyword list. --- doc/bird.sgml | 4 ++++ filter/config.Y | 35 ++++++++++++++++------------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 050acf3..d1d7860 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1053,6 +1053,8 @@ incompatible with each other (that is to prevent you from shooting in the foot). I.e., <cf/filter/ do the same as <cf/delete/ with inverted set <m/A/. + <cf><m/P/.reset</cf> deletes all ASNs from path <m/P/. + Statement <cf><m/P/ = prepend(<m/P/, <m/A/);</cf> can be shortened to <cf><m/P/.prepend(<m/A/);</cf> if <m/P/ is appropriate route attribute (for example <cf/bgp_path/). Similarly for <cf/delete/ and <cf/filter/. @@ -1098,6 +1100,8 @@ incompatible with each other (that is to prevent you from shooting in the foot). set <m/P/. <m/P/ may also be a clist, which works analogously; i.e., it works as clist intersection. + <cf><m/C/.reset</cf> deletes all items from clist <m/C/. + Statement <cf><m/C/ = add(<m/C/, <m/P/);</cf> can be shortened to <cf><m/C/.add(<m/P/);</cf> if <m/C/ is appropriate route attribute (for example <cf/bgp_community/). Similarly for diff --git a/filter/config.Y b/filter/config.Y index 04acfba..faa02f5 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -115,32 +115,36 @@ f_new_ec_item(u32 kind, u32 ipv4_used, u32 key, u32 vf, u32 vt) return t; } -static inline struct f_inst * +static struct f_inst * f_generate_empty(struct f_inst *dyn) -{ - struct f_inst *e = f_new_inst(); - e->code = 'E'; +{ + struct f_inst *e; + u16 aux; switch (dyn->aux & EAF_TYPE_MASK) { case EAF_TYPE_AS_PATH: - e->aux = T_PATH; + aux = T_PATH; break; case EAF_TYPE_INT_SET: - e->aux = T_CLIST; + aux = T_CLIST; break; case EAF_TYPE_EC_SET: - e->aux = T_ECLIST; + aux = T_ECLIST; break; default: cf_error("Can't empty that attribute"); } + e = f_new_inst(); + e->code = 'E'; + e->aux = aux; + dyn->code = P('e','S'); dyn->a1.p = e; + return dyn; } - static inline struct f_inst * f_generate_dpair(struct f_inst *t1, struct f_inst *t2) { @@ -265,10 +269,9 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, PREFERENCE, LEN, DEFINED, - ADD, DELETE, CONTAINS, RESET, - PREPEND, FIRST, LAST, MATCH, + ADD, DELETE, RESET, EMPTY, + PREPEND, FIRST, LAST, ROA_CHECK, - EMPTY, FILTER, WHERE, EVAL) %nonassoc THEN @@ -727,14 +730,6 @@ term: | term '.' FIRST { $$ = f_new_inst(); $$->code = P('a','f'); $$->a1.p = $1; } | term '.' LAST { $$ = f_new_inst(); $$->code = P('a','l'); $$->a1.p = $1; } -/* Communities */ -/* This causes one shift/reduce conflict - | rtadot dynamic_attr '.' ADD '(' term ')' { } - | rtadot dynamic_attr '.' DELETE '(' term ')' { } - | rtadot dynamic_attr '.' CONTAINS '(' term ')' { } - | rtadot dynamic_attr '.' RESET{ } -*/ - | '+' EMPTY '+' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_PATH; } | '-' EMPTY '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_CLIST; } | '-' '-' EMPTY '-' '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_ECLIST; } @@ -880,6 +875,8 @@ cmd: | rtadot dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($2); } + | rtadot dynamic_attr '.' RESET ';' { $$ = f_generate_empty($2); } + | rtadot dynamic_attr '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex( P('A','p'), 'x', $2, $6 ); } | rtadot dynamic_attr '.' ADD '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'a', $2, $6 ); } | rtadot dynamic_attr '.' DELETE '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'd', $2, $6 ); } -- 1.7.10.4
On Mon, Sep 30, 2013 at 09:30:46PM +0300, Sergey Popovich wrote:
Use RESET as predicate to set to empty value of dynamic attribute value in filters. Leave EMPTY for backwad compatibility, altought it not documented enywhere.
Well, my previous comment related to this was more like a question for opinions of others about this naming issue. IMHO, if EMPTY is not reused as a predicate for testing emptiness, then we just should document it as an operator of emptying and RESET is unnecessary. It is true that with 'C.len = 0', separate predicate for testing emptiness is not necessary.
Uninline f_generate_empty() and refactor code in error path.
OK. BTW, the error path was correct, as the allocated resources would be freed with configuration anyways.
Remove MATCH and CONTAINS from keyword list.
OK. -- Elen sila lumenn' omentielvo Ondrej 'SanTiago' Zajicek (email: santiago@crfreenet.org) OpenPGP encrypted e-mails preferred (KeyID 0x11DEADC3, wwwkeys.pgp.net) "To err is human -- to blame it on a computer is even more so."
There are two types of filter errors: which could be checked and reported on configuration file parsing and runtime, which could not be checked at compile time but could be only triggered at runtime filter statement evaluation. Currently there no mechanism in BIRD to branch to alternative action (or gust ignore) in case of filter runtime error. This patch introduces try ... catch statement to catch an runtime errors and execute alternative action. --- doc/bird.sgml | 23 ++++++++++++++++++++++- filter/config.Y | 8 +++++++- filter/filter.c | 7 +++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index d1d7860..65fc207 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1139,7 +1139,7 @@ specify a prefix and an ASN as arguments. <sect>Control structures -<p>Filters support two control structures: conditions and case switches. +<p>Filters support tree control structures: conditions, case switches and filter runtime error handing. <p>Syntax of a condition is: <cf>if <M>boolean expression</M> then <M>command1</M>; else <M>command2</M>;</cf> and you can use <cf>{ @@ -1167,6 +1167,27 @@ if 1234 = i then printn "."; else { } </code> +<p>Syntax of filter runtime error handing statement is: +<cf>try <m/command1/ catch <m/command2/</cf>, where <cf/try/ statement +executes <cf><m/command1/</cf> and if it throws an filter runtime error +(for example dynamic attribute not present), <cf/catch/ statement +executes <cf><m/command2/</cf>. +An log entry is still created for statement in <cf><m/command1/</cf> which +throws an error. + +<p>Here is example on how to use <cf>try ... catch</cf> statement: + +<code> +try { + # If neighbor is not on directly connected subnet, or it denotes + # host itself: generate runtime error + gw = 192.0.2.2; +} catch { + # Setting gw unsuccessfully; mark route as blackhole + dest = RTD_BLACKHOLE; +} +</code> + <sect>Route attributes <p>A filter is implicitly passed a route, and it can access its diff --git a/filter/config.Y b/filter/config.Y index faa02f5..42b9e19 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -263,7 +263,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, ACCEPT, REJECT, ERROR, QUITBIRD, INT, BOOL, IP, PREFIX, PAIR, QUAD, EC, SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, - IF, THEN, ELSE, CASE, + IF, THEN, ELSE, CASE, TRY, CATCH, TRUE, FALSE, RT, RO, UNKNOWN, GENERIC, FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, CAST, DEST, IFNAME, IFINDEX, PREFERENCE, @@ -826,6 +826,12 @@ cmd: $$->a1.p = i; $$->a2.p = $6; } + | TRY block CATCH block { + $$ = f_new_inst(); + $$->code = P('T','C'); + $$->a1.p = $2; + $$->a2.p = $4; + } | SYM '=' term ';' { $$ = f_new_inst(); DBG( "Ook, we'll set value\n" ); diff --git a/filter/filter.c b/filter/filter.c index 8164ead..5610f3f 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -1323,6 +1323,12 @@ interpret(struct f_inst *what) res.val.i = roa_check(rtc->table, v1.val.px.ip, v1.val.px.len, as); break; + case P('T','C'): + res = interpret(what->a1.p); + if ((res.type == T_RETURN) && (res.val.i == F_ERROR)) + ARG(res, a2.p); + break; + default: bug( "Unknown instruction %d (%c)", what->code, what->code & 0xff); } @@ -1453,6 +1459,7 @@ i_same(struct f_inst *f1, struct f_inst *f2) if (strcmp(((struct f_inst_roa_check *) f1)->rtc->name, ((struct f_inst_roa_check *) f2)->rtc->name)) return 0; + case P('T','C'): TWOARGS; break; break; default: bug( "Unknown instruction %d in same (%c)", f1->code, f1->code & 0xff); -- 1.7.10.4
В письме от 30 сентября 2013 21:33:45 пользователь Sergey Popovich написал: ----------- @@ -1453,6 +1459,7 @@ i_same(struct f_inst *f1, struct f_inst *f2) if (strcmp(((struct f_inst_roa_check *) f1)->rtc->name, ((struct f_inst_roa_check *) f2)->rtc->name)) return 0; + case P('T','C'): TWOARGS; break; break; default: bug( "Unknown instruction %d in same (%c)", f1->code, f1->code & 0xff); ----------- There is typo in this chunk: I split P('R','C') with P('T','C'): no break on P('R','C'). Should be ----------- @@ -1453,6 +1459,7 @@ i_same(struct f_inst *f1, struct f_inst *f2) if (strcmp(((struct f_inst_roa_check *) f1)->rtc->name, ((struct f_inst_roa_check *) f2)->rtc->name)) return 0; break; + case P('T','C'): TWOARGS; break; default: bug( "Unknown instruction %d in same (%c)", f1->code, f1->code & 0xff); ----------- -- SP5474-RIPE Sergey Popovich
There are no separate operator in BIRD filter syntax which executes NO OPeration. However interpret() at filter/filter.c contains an instruction '0' to handle such operator. Use ';' as NOOP statement. --- filter/config.Y | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/filter/config.Y b/filter/config.Y index 42b9e19..fa87c76 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -810,7 +810,11 @@ var_list: /* EMPTY */ { $$ = NULL; } ; cmd: - IF term THEN block { + ';' { + $$ = f_new_inst(); + $$->code = '0'; + } + | IF term THEN block { $$ = f_new_inst(); $$->code = '?'; $$->a1.p = $2; -- 1.7.10.4
Get length in bits of IP address for the running BIRD in filters: 32 - for IPv4, and 128 - for IPv6. This is useful to determine address family for which BIRD was build in filters at runtime. --- doc/bird.sgml | 23 +++++++++++++++++++++++ filter/filter.c | 1 + 2 files changed, 24 insertions(+) diff --git a/doc/bird.sgml b/doc/bird.sgml index 65fc207..e4839a6 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -943,6 +943,29 @@ incompatible with each other (that is to prevent you from shooting in the foot). is either an IPv4 or IPv6 address. IP addresses are written in the standard notation (<cf/10.20.30.40/ or <cf/fec0:3:4::1/). You can apply special operator <cf>.mask(<M>num</M>)</cf> on values of type ip. It masks out all but first <cf><M>num</M></cf> bits from the IP address. So <cf/1.2.3.4.mask(8) = 1.0.0.0/ is true. + Also <cf><m/I/.len</cf> operator could be used to get length in bits of the + IP address, which is useful in filters to determine address family + (IPv4 or IPv6) of the BIRD instance at runtime. For example + +<code> +function afi() +{ + case net.ip.len { + 32: return 4; + 128: return 6; + } +} + +function is_bird_ipv4() +{ + return afi() = 4; +} + +function is_bird_ipv6() +{ + return afi() = 6; +} +</code> <tag/prefix/ This type can hold a network prefix consisting of IP address and prefix length. Prefix literals are written as <cf><M>ipaddress</M>/<M>pxlen</M></cf>, or diff --git a/filter/filter.c b/filter/filter.c index 5610f3f..3e8af1e 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -1067,6 +1067,7 @@ interpret(struct f_inst *what) case T_PATH: res.val.i = as_path_getlen(v1.val.ad); break; case T_CLIST: res.val.i = v1.val.ad->length / 4; break; case T_ECLIST: res.val.i = v1.val.ad->length / 8; break; + case T_IP: res.val.i = sizeof(ip_addr) * 8; break; /* Use this to determine AFI in filters */ default: runtime( "Prefix, path, clist or eclist expected" ); } break; -- 1.7.10.4
Replace debug() calls with DBG() macro to call debug() only when building BIRD with GLOBAL_DEBUG or LOCAL_DEBUG. Additionally fix build with GLOBAL_DEBUG enabled and --with-protocols=all. --- TODO | 1 - filter/filter.c | 6 +++--- lib/event.c | 2 +- lib/mempool.c | 2 +- lib/resource.c | 14 +++++++------- lib/slab.c | 4 ++-- nest/iface.c | 30 +++++++++++++++--------------- nest/locks.c | 4 ++-- nest/neighbor.c | 16 ++++++++-------- nest/proto.c | 18 +++++++++--------- nest/rt-attr.c | 40 ++++++++++++++++++++-------------------- nest/rt-fib.c | 12 ++++++------ nest/rt-table.c | 12 ++++++------ proto/ospf/neighbor.c | 2 +- proto/ospf/packet.c | 2 +- proto/rip/rip.c | 10 +++++----- proto/static/static.c | 12 ++++++------ sysdep/unix/io.c | 26 +++++++++++++------------- sysdep/unix/krt.c | 4 ++-- sysdep/unix/main.c | 4 ++-- 20 files changed, 110 insertions(+), 111 deletions(-) diff --git a/TODO b/TODO index 23cd187..d9b6a20 100644 --- a/TODO +++ b/TODO @@ -6,7 +6,6 @@ Core Globals ~~~~~~~ -- right usage of DBG vs. debug - logging and tracing; use appropriate log levels - check incoming packets and log errors!! - check log calls for trailing newlines and log levels followed by comma diff --git a/filter/filter.c b/filter/filter.c index 3e8af1e..69cf579 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -174,7 +174,7 @@ val_compare(struct f_val v1, struct f_val v2) return uint_cmp(ipa_to_u32(v1.val.px.ip), v2.val.i); #endif - debug( "Types do not match in val_compare\n" ); + DBG( "Types do not match in val_compare\n" ); return CMP_ERROR; } switch (v1.type) { @@ -797,7 +797,7 @@ interpret(struct f_inst *what) res.type = T_BOOL; break; case '0': - debug( "No operation\n" ); + DBG( "No operation\n" ); break; case P('p',','): ONEARG; @@ -1126,7 +1126,7 @@ interpret(struct f_inst *what) v1.type = T_VOID; t = find_tree(what->a2.p, v1); if (!t) { - debug( "No else statement?\n"); + DBG( "No else statement?\n"); break; } } diff --git a/lib/event.c b/lib/event.c index 916cf55..f1703cb 100644 --- a/lib/event.c +++ b/lib/event.c @@ -39,7 +39,7 @@ ev_dump(resource *r) { event *e = (event *) r; - debug("(code %p, data %p, %s)\n", + DBG("(code %p, data %p, %s)\n", e->hook, e->data, e->n.next ? "scheduled" : "inactive"); diff --git a/lib/mempool.c b/lib/mempool.c index 65072f9..42418e9 100644 --- a/lib/mempool.c +++ b/lib/mempool.c @@ -228,7 +228,7 @@ lp_dump(resource *r) ; for(cntl=0, c=m->first_large; c; c=c->next, cntl++) ; - debug("(chunk=%d threshold=%d count=%d+%d total=%d+%d)\n", + DBG("(chunk=%d threshold=%d count=%d+%d total=%d+%d)\n", m->chunk_size, m->threshold, cnt, diff --git a/lib/resource.c b/lib/resource.c index 42243aa..ae955a4 100644 --- a/lib/resource.c +++ b/lib/resource.c @@ -90,7 +90,7 @@ pool_dump(resource *P) pool *p = (pool *) P; resource *r; - debug("%s\n", p->name); + DBG("%s\n", p->name); indent += 3; WALK_LIST(r, p->inside) rdump(r); @@ -182,14 +182,14 @@ rdump(void *res) resource *r = res; bsprintf(x, "%%%ds%%p ", indent); - debug(x, "", r); + DBG(x, "", r); if (r) { - debug("%s ", r->class->name); + DBG("%s ", r->class->name); r->class->dump(r); } else - debug("NULL\n"); + DBG("NULL\n"); } size_t @@ -240,11 +240,11 @@ rlookup(unsigned long a) { resource *r; - debug("Looking up %08lx\n", a); + DBG("Looking up %08lx\n", a); if (r = pool_lookup(&root_pool.r, a)) rdump(r); else - debug("Not found.\n"); + DBG("Not found.\n"); } /** @@ -290,7 +290,7 @@ static void mbl_debug(resource *r) { struct mblock *m = (struct mblock *) r; - debug("(size=%d)\n", m->size); + DBG("(size=%d)\n", m->size); } static resource * diff --git a/lib/slab.c b/lib/slab.c index e236e26..78d535f 100644 --- a/lib/slab.c +++ b/lib/slab.c @@ -116,7 +116,7 @@ slab_dump(resource *r) WALK_LIST(o, s->objs) cnt++; - debug("(%d objects per %d bytes)\n", cnt, s->size); + DBG("(%d objects per %d bytes)\n", cnt, s->size); } static size_t @@ -339,7 +339,7 @@ slab_dump(resource *r) pc++; WALK_LIST(h, s->full_heads) fc++; - debug("(%de+%dp+%df blocks per %d objs per %d bytes)\n", ec, pc, fc, s->objs_per_slab, s->obj_size); + DBG("(%de+%dp+%df blocks per %d objs per %d bytes)\n", ec, pc, fc, s->objs_per_slab, s->obj_size); } static size_t diff --git a/nest/iface.c b/nest/iface.c index b4ab70c..c7050be 100644 --- a/nest/iface.c +++ b/nest/iface.c @@ -46,7 +46,7 @@ list iface_list; void ifa_dump(struct ifa *a) { - debug("\t%I, net %I/%-2d bc %I -> %I%s%s%s\n", a->ip, a->prefix, a->pxlen, a->brd, a->opposite, + DBG("\t%I, net %I/%-2d bc %I -> %I%s%s%s\n", a->ip, a->prefix, a->pxlen, a->brd, a->opposite, (a->flags & IF_UP) ? "" : " DOWN", (a->flags & IA_PRIMARY) ? "" : " SEC", (a->flags & IA_PEER) ? "PEER" : ""); @@ -64,28 +64,28 @@ if_dump(struct iface *i) { struct ifa *a; - debug("IF%d: %s", i->index, i->name); + DBG("IF%d: %s", i->index, i->name); if (i->flags & IF_SHUTDOWN) - debug(" SHUTDOWN"); + DBG(" SHUTDOWN"); if (i->flags & IF_UP) - debug(" UP"); + DBG(" UP"); else - debug(" DOWN"); + DBG(" DOWN"); if (i->flags & IF_ADMIN_UP) - debug(" LINK-UP"); + DBG(" LINK-UP"); if (i->flags & IF_MULTIACCESS) - debug(" MA"); + DBG(" MA"); if (i->flags & IF_BROADCAST) - debug(" BC"); + DBG(" BC"); if (i->flags & IF_MULTICAST) - debug(" MC"); + DBG(" MC"); if (i->flags & IF_LOOPBACK) - debug(" LOOP"); + DBG(" LOOP"); if (i->flags & IF_IGNORE) - debug(" IGN"); + DBG(" IGN"); if (i->flags & IF_TMP_DOWN) - debug(" TDOWN"); - debug(" MTU=%d\n", i->mtu); + DBG(" TDOWN"); + DBG(" MTU=%d\n", i->mtu); WALK_LIST(a, i->addrs) { ifa_dump(a); @@ -104,10 +104,10 @@ if_dump_all(void) { struct iface *i; - debug("Known network interfaces:\n"); + DBG("Known network interfaces:\n"); WALK_LIST(i, iface_list) if_dump(i); - debug("Router ID: %08x\n", config->router_id); + DBG("Router ID: %08x\n", config->router_id); } static inline unsigned diff --git a/nest/locks.c b/nest/locks.c index 7044d6a..ec7a3d6 100644 --- a/nest/locks.c +++ b/nest/locks.c @@ -88,9 +88,9 @@ olock_dump(resource *r) struct object_lock *l = (struct object_lock *) r; static char *olock_states[] = { "free", "locked", "waiting", "event" }; - debug("(%d:%s:%I:%d) [%s]\n", l->type, (l->iface ? l->iface->name : "?"), l->addr, l->port, olock_states[l->state]); + DBG("(%d:%s:%I:%d) [%s]\n", l->type, (l->iface ? l->iface->name : "?"), l->addr, l->port, olock_states[l->state]); if (!EMPTY_LIST(l->waiters)) - debug(" [wanted]\n"); + DBG(" [wanted]\n"); } static struct resclass olock_class = { diff --git a/nest/neighbor.c b/nest/neighbor.c index 11a980b..feaee5d 100644 --- a/nest/neighbor.c +++ b/nest/neighbor.c @@ -183,15 +183,15 @@ neigh_find2(struct proto *p, ip_addr *a, struct iface *ifa, unsigned flags) void neigh_dump(neighbor *n) { - debug("%p %I ", n, n->addr); + DBG("%p %I ", n, n->addr); if (n->iface) - debug("%s ", n->iface->name); + DBG("%s ", n->iface->name); else - debug("[] "); - debug("%s %p %08x scope %s", n->proto->name, n->data, n->aux, ip_scope_text(n->scope)); + DBG("[] "); + DBG("%s %p %08x scope %s", n->proto->name, n->data, n->aux, ip_scope_text(n->scope)); if (n->flags & NEF_STICKY) - debug(" STICKY"); - debug("\n"); + DBG(" STICKY"); + DBG("\n"); } /** @@ -206,13 +206,13 @@ neigh_dump_all(void) neighbor *n; int i; - debug("Known neighbors:\n"); + DBG("Known neighbors:\n"); WALK_LIST(n, sticky_neigh_list) neigh_dump(n); for(i=0; i<NEIGH_HASH_SIZE; i++) WALK_LIST(n, neigh_hash_table[i]) neigh_dump(n); - debug("\n"); + DBG("\n"); } static void diff --git a/nest/proto.c b/nest/proto.c index 60495aa..f66449e 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -635,31 +635,31 @@ protos_dump_all(void) struct proto *p; struct announce_hook *a; - debug("Protocols:\n"); + DBG("Protocols:\n"); WALK_LIST(p, active_proto_list) { - debug(" protocol %s state %s/%s\n", p->name, + DBG(" protocol %s state %s/%s\n", p->name, p_states[p->proto_state], c_states[p->core_state]); for (a = p->ahooks; a; a = a->next) { - debug("\tTABLE %s\n", a->table->name); + DBG("\tTABLE %s\n", a->table->name); if (a->in_filter) - debug("\tInput filter: %s\n", filter_name(a->in_filter)); + DBG("\tInput filter: %s\n", filter_name(a->in_filter)); if (a->out_filter != FILTER_REJECT) - debug("\tOutput filter: %s\n", filter_name(a->out_filter)); + DBG("\tOutput filter: %s\n", filter_name(a->out_filter)); } if (p->disabled) - debug("\tDISABLED\n"); + DBG("\tDISABLED\n"); else if (p->proto->dump) p->proto->dump(p); } WALK_LIST(p, inactive_proto_list) - debug(" inactive %s: state %s/%s\n", p->name, p_states[p->proto_state], c_states[p->core_state]); + DBG(" inactive %s: state %s/%s\n", p->name, p_states[p->proto_state], c_states[p->core_state]); WALK_LIST(p, initial_proto_list) - debug(" initial %s\n", p->name); + DBG(" initial %s\n", p->name); WALK_LIST(p, flush_proto_list) - debug(" flushing %s\n", p->name); + DBG(" flushing %s\n", p->name); } /** diff --git a/nest/rt-attr.c b/nest/rt-attr.c index 6aed318..64a6cc4 100644 --- a/nest/rt-attr.c +++ b/nest/rt-attr.c @@ -560,36 +560,36 @@ ea_dump(ea_list *e) if (!e) { - debug("NONE"); + DBG("NONE"); return; } while (e) { - debug("[%c%c%c]", + DBG("[%c%c%c]", (e->flags & EALF_SORTED) ? 'S' : 's', (e->flags & EALF_BISECT) ? 'B' : 'b', (e->flags & EALF_CACHED) ? 'C' : 'c'); for(i=0; i<e->count; i++) { eattr *a = &e->attrs[i]; - debug(" %02x:%02x.%02x", EA_PROTO(a->id), EA_ID(a->id), a->flags); + DBG(" %02x:%02x.%02x", EA_PROTO(a->id), EA_ID(a->id), a->flags); if (a->type & EAF_TEMP) - debug("T"); - debug("=%c", "?iO?I?P???S?????" [a->type & EAF_TYPE_MASK]); + DBG("T"); + DBG("=%c", "?iO?I?P???S?????" [a->type & EAF_TYPE_MASK]); if (a->type & EAF_ORIGINATED) - debug("o"); + DBG("o"); if (a->type & EAF_EMBEDDED) - debug(":%08x", a->u.data); + DBG(":%08x", a->u.data); else { int j, len = a->u.ptr->length; - debug("[%d]:", len); + DBG("[%d]:", len); for(j=0; j<len; j++) - debug("%02x", a->u.ptr->data[j]); + DBG("%02x", a->u.ptr->data[j]); } } if (e = e->next) - debug(" | "); + DBG(" | "); } } @@ -826,19 +826,19 @@ rta_dump(rta *a) static char *rtc[] = { "", " BC", " MC", " AC" }; static char *rtd[] = { "", " DEV", " HOLE", " UNREACH", " PROHIBIT" }; - debug("p=%s uc=%d %s %s%s%s h=%04x", + DBG("p=%s uc=%d %s %s%s%s h=%04x", a->proto->name, a->uc, rts[a->source], ip_scope_text(a->scope), rtc[a->cast], rtd[a->dest], a->hash_key); if (!(a->aflags & RTAF_CACHED)) - debug(" !CACHED"); - debug(" <-%I", a->from); + DBG(" !CACHED"); + DBG(" <-%I", a->from); if (a->dest == RTD_ROUTER) - debug(" ->%I", a->gw); + DBG(" ->%I", a->gw); if (a->dest == RTD_DEVICE || a->dest == RTD_ROUTER) - debug(" [%s]", a->iface ? a->iface->name : "???" ); + DBG(" [%s]", a->iface ? a->iface->name : "???" ); if (a->eattrs) { - debug(" EA: "); + DBG(" EA: "); ea_dump(a->eattrs); } } @@ -855,15 +855,15 @@ rta_dump_all(void) rta *a; unsigned int h; - debug("Route attribute cache (%d entries, rehash at %d):\n", rta_cache_count, rta_cache_limit); + DBG("Route attribute cache (%d entries, rehash at %d):\n", rta_cache_count, rta_cache_limit); for(h=0; h<rta_cache_size; h++) for(a=rta_hash_table[h]; a; a=a->next) { - debug("%p ", a); + DBG("%p ", a); rta_dump(a); - debug("\n"); + DBG("\n"); } - debug("\n"); + DBG("\n"); } void diff --git a/nest/rt-fib.c b/nest/rt-fib.c index 510aa76..b6d5d06 100644 --- a/nest/rt-fib.c +++ b/nest/rt-fib.c @@ -491,21 +491,21 @@ void dump(char *m) { unsigned int i; - debug("%s ... order=%d, size=%d, entries=%d\n", m, f.hash_order, f.hash_size, f.hash_size); + DBG("%s ... order=%d, size=%d, entries=%d\n", m, f.hash_order, f.hash_size, f.hash_size); for(i=0; i<f.hash_size; i++) { struct fib_node *n; struct fib_iterator *j; for(n=f.hash_table[i]; n; n=n->next) { - debug("%04x %04x %p %I/%2d", i, ipa_hash(n->prefix), n, n->prefix, n->pxlen); + DBG("%04x %04x %p %I/%2d", i, ipa_hash(n->prefix), n, n->prefix, n->pxlen); for(j=n->readers; j; j=j->next) - debug(" %p[%p]", j, j->node); - debug("\n"); + DBG(" %p[%p]", j, j->node); + DBG("\n"); } } fib_check(&f); - debug("-----\n"); + DBG("-----\n"); } void init(struct fib_node *n) @@ -552,7 +552,7 @@ next: goto next; } c = 1; - debug("got %p\n", z); + DBG("got %p\n", z); } FIB_ITERATE_END; dump("iter end"); diff --git a/nest/rt-table.c b/nest/rt-table.c index 16dd9bc..883cfb8 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -1106,12 +1106,12 @@ void rte_dump(rte *e) { net *n = e->net; - debug("%-1I/%2d ", n->n.prefix, n->n.pxlen); - debug("KF=%02x PF=%02x pref=%d lm=%d ", n->n.flags, e->pflags, e->pref, now-e->lastmod); + DBG("%-1I/%2d ", n->n.prefix, n->n.pxlen); + DBG("KF=%02x PF=%02x pref=%d lm=%d ", n->n.flags, e->pflags, e->pref, now-e->lastmod); rta_dump(e->attrs); if (e->attrs->proto->proto->dump_attrs) e->attrs->proto->proto->dump_attrs(e); - debug("\n"); + DBG("\n"); } /** @@ -1127,7 +1127,7 @@ rt_dump(rtable *t) net *n; struct announce_hook *a; - debug("Dump of routing table <%s>\n", t->name); + DBG("Dump of routing table <%s>\n", t->name); #ifdef DEBUGGING fib_check(&t->fib); #endif @@ -1139,8 +1139,8 @@ rt_dump(rtable *t) } FIB_WALK_END; WALK_LIST(a, t->hooks) - debug("\tAnnounces routes to protocol %s\n", a->proto->name); - debug("\n"); + DBG("\tAnnounces routes to protocol %s\n", a->proto->name); + DBG("\n"); } /** diff --git a/proto/ospf/neighbor.c b/proto/ospf/neighbor.c index 26d81dc..c1f2a0b 100644 --- a/proto/ospf/neighbor.c +++ b/proto/ospf/neighbor.c @@ -619,7 +619,7 @@ static void rxmt_timer_hook(timer * timer) { struct ospf_neighbor *n = (struct ospf_neighbor *) timer->data; - // struct proto *p = &n->ifa->oa->po->proto; + struct proto *p = &n->ifa->oa->po->proto; struct top_hash_entry *en; DBG("%s: RXMT timer fired on interface %s for neigh: %I.\n", diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c index 4338bc1..fb02f68 100644 --- a/proto/ospf/packet.c +++ b/proto/ospf/packet.c @@ -268,7 +268,7 @@ ospf_rx_hook(sock *sk, int size) /* Initially, the packet is associated with the 'master' iface */ struct ospf_iface *ifa = sk->data; struct proto_ospf *po = ifa->oa->po; - // struct proto *p = &po->proto; + struct proto *p = &po->proto; int src_local, dst_local UNUSED, dst_mcast; src_local = ipa_in_net(sk->faddr, ifa->addr->prefix, ifa->addr->pxlen); diff --git a/proto/rip/rip.c b/proto/rip/rip.c index 3ec070b..9f49c6b 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -512,9 +512,9 @@ rip_rx(sock *s, int size) static void rip_dump_entry( struct rip_entry *e ) { - debug( "%I told me %d/%d ago: to %I/%d go via %I, metric %d ", + DBG( "%I told me %d/%d ago: to %I/%d go via %I, metric %d ", e->whotoldme, e->updated-now, e->changed-now, e->n.prefix, e->n.pxlen, e->nexthop, e->metric ); - debug( "\n" ); + DBG( "\n" ); } /** @@ -637,16 +637,16 @@ rip_dump(struct proto *p) CHK_MAGIC; WALK_LIST( w, P->connections ) { struct rip_connection *n = (void *) w; - debug( "RIP: connection #%d: %I\n", n->num, n->addr ); + DBG( "RIP: connection #%d: %I\n", n->num, n->addr ); } i = 0; FIB_WALK( &P->rtable, e ) { - debug( "RIP: entry #%d: ", i++ ); + DBG( "RIP: entry #%d: ", i++ ); rip_dump_entry( (struct rip_entry *)e ); } FIB_WALK_END; i = 0; WALK_LIST( rif, P->interfaces ) { - debug( "RIP: interface #%d: %s, %I, busy = %x\n", i++, rif->iface?rif->iface->name:"(dummy)", rif->sock->daddr, rif->busy ); + DBG( "RIP: interface #%d: %s, %I, busy = %x\n", i++, rif->iface?rif->iface->name:"(dummy)", rif->sock->daddr, rif->busy ); } } diff --git a/proto/static/static.c b/proto/static/static.c index 9eee820..041c6a0 100644 --- a/proto/static/static.c +++ b/proto/static/static.c @@ -304,17 +304,17 @@ static_neigh_notify(struct neighbor *n) static void static_dump_rt(struct static_route *r) { - debug("%-1I/%2d: ", r->net, r->masklen); + DBG("%-1I/%2d: ", r->net, r->masklen); switch (r->dest) { case RTD_ROUTER: - debug("via %I\n", r->via); + DBG("via %I\n", r->via); break; case RTD_DEVICE: - debug("dev %s\n", r->if_name); + DBG("dev %s\n", r->if_name); break; default: - debug("rtd %d\n", r->dest); + DBG("rtd %d\n", r->dest); break; } } @@ -325,10 +325,10 @@ static_dump(struct proto *p) struct static_config *c = (void *) p->cf; struct static_route *r; - debug("Independent static routes:\n"); + DBG("Independent static routes:\n"); WALK_LIST(r, c->other_routes) static_dump_rt(r); - debug("Device static routes:\n"); + DBG("Device static routes:\n"); WALK_LIST(r, c->iface_routes) static_dump_rt(r); } diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index fcf5dd1..6fa1cc8 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -70,7 +70,7 @@ rf_dump(resource *r) { struct rfile *a = (struct rfile *) r; - debug("(FILE *%p)\n", a->f); + DBG("(FILE *%p)\n", a->f); } static struct resclass rf_class = { @@ -190,15 +190,15 @@ tm_dump(resource *r) { timer *t = (timer *) r; - debug("(code %p, data %p, ", t->hook, t->data); + DBG("(code %p, data %p, ", t->hook, t->data); if (t->randomize) - debug("rand %d, ", t->randomize); + DBG("rand %d, ", t->randomize); if (t->recurrent) - debug("recur %d, ", t->recurrent); + DBG("recur %d, ", t->recurrent); if (t->expires) - debug("expires in %d sec)\n", t->expires - now); + DBG("expires in %d sec)\n", t->expires - now); else - debug("inactive)\n"); + DBG("inactive)\n"); } static struct resclass tm_class = { @@ -298,14 +298,14 @@ tm_dump_them(char *name, list *l) node *n; timer *t; - debug("%s timers:\n", name); + DBG("%s timers:\n", name); WALK_LIST(n, *l) { t = SKIP_BACK(timer, n, n); - debug("%p ", t); + DBG("%p ", t); tm_dump(&t->r); } - debug("\n"); + DBG("\n"); } void @@ -560,7 +560,7 @@ sk_dump(resource *r) sock *s = (sock *) r; static char *sk_type_names[] = { "TCP<", "TCP>", "TCP", "UDP", "UDP/MC", "IP", "IP/MC", "MAGIC", "UNIX<", "UNIX", "DEL!" }; - debug("(%s, ud=%p, sa=%08x, sp=%d, da=%08x, dp=%d, tos=%d, ttl=%d, if=%s)\n", + DBG("(%s, ud=%p, sa=%08x, sp=%d, da=%08x, dp=%d, tos=%d, ttl=%d, if=%s)\n", sk_type_names[s->type], s->data, s->saddr, @@ -1527,14 +1527,14 @@ sk_dump_all(void) node *n; sock *s; - debug("Open sockets:\n"); + DBG("Open sockets:\n"); WALK_LIST(n, sock_list) { s = SKIP_BACK(sock, n, n); - debug("%p ", s); + DBG("%p ", s); sk_dump(&s->r); } - debug("\n"); + DBG("\n"); } #undef ERR diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index 6de6077..f3df5ba 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -551,14 +551,14 @@ krt_dump(struct proto *P) if (!KRT_CF->learn) return; - debug("KRT: Table of inheritable routes\n"); + DBG("KRT: Table of inheritable routes\n"); rt_dump(&p->krt_table); } static void krt_dump_attrs(rte *e) { - debug(" [m=%d,p=%d,t=%d]", e->u.krt.metric, e->u.krt.proto, e->u.krt.type); + DBG(" [m=%d,p=%d,t=%d]", e->u.krt.metric, e->u.krt.proto, e->u.krt.type); } #endif diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index bd80ba2..740c3ac 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -50,7 +50,7 @@ static int debug_flag = 0; void async_dump(void) { - debug("INTERNAL STATE DUMP\n\n"); + DBG("INTERNAL STATE DUMP\n\n"); rdump(&root_pool); sk_dump_all(); @@ -61,7 +61,7 @@ async_dump(void) rt_dump_all(); protos_dump_all(); - debug("\n"); + DBG("\n"); } /* -- 1.7.10.4
On Mon, Sep 30, 2013 at 09:34:39PM +0300, Sergey Popovich wrote:
Replace debug() calls with DBG() macro to call debug() only when building BIRD with GLOBAL_DEBUG or LOCAL_DEBUG.
This is not as it is supposed to be used. DBG() should be used where debugging is controlled by debug macros, while debug() should be used where it is user-requested, like in all dump commands. So:
diff --git a/filter/filter.c b/filter/filter.c index 3e8af1e..69cf579 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -174,7 +174,7 @@ val_compare(struct f_val v1, struct f_val v2) return uint_cmp(ipa_to_u32(v1.val.px.ip), v2.val.i); #endif - debug( "Types do not match in val_compare\n" ); + DBG( "Types do not match in val_compare\n" ); return CMP_ERROR; } switch (v1.type) { @@ -797,7 +797,7 @@ interpret(struct f_inst *what) res.type = T_BOOL; break; case '0': - debug( "No operation\n" ); + DBG( "No operation\n" ); break; case P('p',','): ONEARG; @@ -1126,7 +1126,7 @@ interpret(struct f_inst *what) v1.type = T_VOID; t = find_tree(what->a2.p, v1); if (!t) { - debug( "No else statement?\n"); + DBG( "No else statement?\n"); break;
These changes are OK, while
diff --git a/lib/mempool.c b/lib/mempool.c index 65072f9..42418e9 100644 --- a/lib/mempool.c +++ b/lib/mempool.c @@ -228,7 +228,7 @@ lp_dump(resource *r) ; for(cntl=0, c=m->first_large; c; c=c->next, cntl++) ; - debug("(chunk=%d threshold=%d count=%d+%d total=%d+%d)\n", + DBG("(chunk=%d threshold=%d count=%d+%d total=%d+%d)\n", m->chunk_size, m->threshold, cnt,
These are not. -- Elen sila lumenn' omentielvo Ondrej 'SanTiago' Zajicek (email: santiago@crfreenet.org) OpenPGP encrypted e-mails preferred (KeyID 0x11DEADC3, wwwkeys.pgp.net) "To err is human -- to blame it on a computer is even more so."
Currently filter code uses extended attribyte flags (EAF_*) in P('e','s') and P('e','S'). Filter types (T_*) in extended attributes are completely ignored in filter/f-util.c f_new_dynamic_attr(). It is difficult to map transparently in interpret() EAF_* to T_* and this could cause some errors in filter expressions: currently one known issue is handing "bgp_origin" attribute, that is defined with EAF_TYPE_INT at proto/bgp/config.Y, but really its value represented with T_ENUM_BGP_ORIGIN. Thus operating on "bgp_origin" requires type int rather than enum. Introduce empty type which implements * real * void type for bgp_atomic_aggr and could be used for not implemented types as stub, rather than wasting entry in T_ENUM_range. Make hidden function unset(), which unsets extended attributes from rta to work again (remove EAF_TEMP). --- filter/config.Y | 19 +++---- filter/f-util.c | 5 +- filter/filter.c | 158 ++++++++++++++++++++++------------------------------ filter/filter.h | 28 +++++++++- proto/bgp/config.Y | 4 +- 5 files changed, 103 insertions(+), 111 deletions(-) diff --git a/filter/config.Y b/filter/config.Y index fa87c76..a7a69a4 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -119,17 +119,12 @@ static struct f_inst * f_generate_empty(struct f_inst *dyn) { struct f_inst *e; - u16 aux; + u8 f_type = f_inst_aux_f_type(dyn->aux); - switch (dyn->aux & EAF_TYPE_MASK) { - case EAF_TYPE_AS_PATH: - aux = T_PATH; - break; - case EAF_TYPE_INT_SET: - aux = T_CLIST; - break; - case EAF_TYPE_EC_SET: - aux = T_ECLIST; + switch (f_type) { + case T_PATH: + case T_CLIST: + case T_ECLIST: break; default: cf_error("Can't empty that attribute"); @@ -137,7 +132,7 @@ f_generate_empty(struct f_inst *dyn) e = f_new_inst(); e->code = 'E'; - e->aux = aux; + e->aux = f_type; dyn->code = P('e','S'); dyn->a1.p = e; @@ -870,7 +865,7 @@ cmd: } | UNSET '(' rtadot dynamic_attr ')' ';' { $$ = $4; - $$->aux = EAF_TYPE_UNDEF | EAF_TEMP; + $$->aux = f_inst_aux(EAF_TYPE_UNDEF, T_VOID); $$->code = P('e','S'); $$->a1.p = NULL; } diff --git a/filter/f-util.c b/filter/f-util.c index def2b24..ad91002 100644 --- a/filter/f-util.c +++ b/filter/f-util.c @@ -24,11 +24,10 @@ f_new_inst(void) } struct f_inst * -f_new_dynamic_attr(int type, int f_type UNUSED, int code) +f_new_dynamic_attr(int type, int f_type, int code) { - /* FIXME: Remove the f_type parameter? */ struct f_inst *f = f_new_inst(); - f->aux = type; + f->aux = f_inst_aux(type, f_type); f->a2.i = code; return f; } diff --git a/filter/filter.c b/filter/filter.c index 69cf579..9b99f28 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -178,6 +178,7 @@ val_compare(struct f_val v1, struct f_val v2) return CMP_ERROR; } switch (v1.type) { + case T_EMPTY: case T_ENUM: case T_INT: case T_BOOL: @@ -480,6 +481,7 @@ val_print(struct f_val v) char buf2[1024]; switch (v.type) { case T_VOID: logn("(void)"); return; + case T_EMPTY: logn("(empty)"); return; case T_BOOL: logn(v.val.i ? "TRUE" : "FALSE"); return; case T_INT: logn("%d", v.val.i); return; case T_STRING: logn("%s", v.val.s); return; @@ -902,6 +904,9 @@ interpret(struct f_inst *what) ACCESS_RTE; { eattr *e = NULL; + + res.type = f_inst_aux_f_type(what->aux); + if (!(f_flags & FF_FORCE_TMPATTR)) e = ea_find( (*f_rte)->attrs->eattrs, what->a2.i ); if (!e) @@ -910,59 +915,38 @@ interpret(struct f_inst *what) e = ea_find( (*f_rte)->attrs->eattrs, what->a2.i ); if (!e) { - /* A special case: undefined int_set looks like empty int_set */ - if ((what->aux & EAF_TYPE_MASK) == EAF_TYPE_INT_SET) { - res.type = T_CLIST; + /* A special case: undefined int_set looks like empty int_set. + * The same special case applied also to ec_set. + */ + if ((res.type == T_CLIST) || (res.type == T_ECLIST)) res.val.ad = adata_empty(f_pool, 0); - break; - } - /* The same special case for ec_set */ - else if ((what->aux & EAF_TYPE_MASK) == EAF_TYPE_EC_SET) { - res.type = T_ECLIST; - res.val.ad = adata_empty(f_pool, 0); - break; - } - - /* Undefined value */ - res.type = T_VOID; + else + res.type = T_VOID; /* Undefined value */ break; } - switch (what->aux & EAF_TYPE_MASK) { - case EAF_TYPE_INT: - res.type = T_INT; - res.val.i = e->u.data; - break; - case EAF_TYPE_ROUTER_ID: - res.type = T_QUAD; + switch (res.type) { + case T_INT: + case T_QUAD: + case T_ENUM_BGP_ORIGIN: res.val.i = e->u.data; break; - case EAF_TYPE_OPAQUE: - res.type = T_ENUM_EMPTY; - res.val.i = 0; - break; - case EAF_TYPE_IP_ADDRESS: - res.type = T_IP; - struct adata * ad = e->u.ptr; - res.val.px.ip = * (ip_addr *) ad->data; - break; - case EAF_TYPE_AS_PATH: - res.type = T_PATH; - res.val.ad = e->u.ptr; - break; - case EAF_TYPE_INT_SET: - res.type = T_CLIST; - res.val.ad = e->u.ptr; + case T_IP: + { + struct adata * ad = e->u.ptr; + res.val.px.ip = * (ip_addr *) ad->data; + } break; - case EAF_TYPE_EC_SET: - res.type = T_ECLIST; + case T_PATH: + case T_CLIST: + case T_ECLIST: res.val.ad = e->u.ptr; break; - case EAF_TYPE_UNDEF: - res.type = T_VOID; + case T_EMPTY: + res.val.i = 0; break; default: - bug("Unknown type in e,a"); + bug( "Unknown type in 'e,a': %x", res.type ); } } break; @@ -970,71 +954,63 @@ interpret(struct f_inst *what) ACCESS_RTE; ONEARG; { - struct ea_list *l = lp_alloc(f_pool, sizeof(struct ea_list) + sizeof(eattr)); + struct ea_list *l; + u8 eaf = f_inst_aux_eaf(what->aux); + u8 f_type = f_inst_aux_f_type(what->aux); + + if (f_type != v1.type) { + if (f_type != T_QUAD) + runtime( "Attempt to set attribute to incompatible type" ); +#ifndef IPV6 + /* IP->Quad implicit conversion */ + if (v1.type == T_IP) + v1.val.i = ipa_to_u32(v1.val.px.ip); + else +#endif + /* T_INT for backward compatibility */ + if (v1.type != T_INT) + runtime( "Setting quad attribute to non-quad value" ); + } else { + if (f_type == T_EMPTY) + runtime( "Attempt to set empty attribute" ); + } + + l = lp_alloc(f_pool, sizeof(struct ea_list) + sizeof(eattr)); l->next = NULL; l->flags = EALF_SORTED; l->count = 1; l->attrs[0].id = what->a2.i; l->attrs[0].flags = 0; - l->attrs[0].type = what->aux | EAF_ORIGINATED; - switch (what->aux & EAF_TYPE_MASK) { - case EAF_TYPE_INT: - if (v1.type != T_INT) - runtime( "Setting int attribute to non-int value" ); - l->attrs[0].u.data = v1.val.i; - break; + l->attrs[0].type = (eaf | EAF_ORIGINATED); - case EAF_TYPE_ROUTER_ID: -#ifndef IPV6 - /* IP->Quad implicit conversion */ - if (v1.type == T_IP) { - l->attrs[0].u.data = ipa_to_u32(v1.val.px.ip); - break; - } -#endif - /* T_INT for backward compatibility */ - if ((v1.type != T_QUAD) && (v1.type != T_INT)) - runtime( "Setting quad attribute to non-quad value" ); + switch (f_type) { + case T_INT: + case T_QUAD: + case T_ENUM_BGP_ORIGIN: l->attrs[0].u.data = v1.val.i; break; - - case EAF_TYPE_OPAQUE: - runtime( "Setting opaque attribute is not allowed" ); - break; - case EAF_TYPE_IP_ADDRESS: - if (v1.type != T_IP) - runtime( "Setting ip attribute to non-ip value" ); - int len = sizeof(ip_addr); - struct adata *ad = lp_alloc(f_pool, sizeof(struct adata) + len); - ad->length = len; - (* (ip_addr *) ad->data) = v1.val.px.ip; - l->attrs[0].u.ptr = ad; - break; - case EAF_TYPE_AS_PATH: - if (v1.type != T_PATH) - runtime( "Setting path attribute to non-path value" ); - l->attrs[0].u.ptr = v1.val.ad; - break; - case EAF_TYPE_INT_SET: - if (v1.type != T_CLIST) - runtime( "Setting clist attribute to non-clist value" ); - l->attrs[0].u.ptr = v1.val.ad; + case T_IP: + { + struct adata *ad = lp_alloc(f_pool, sizeof(struct adata) + sizeof(ip_addr)); + ad->length = sizeof(ip_addr); + (* (ip_addr *) ad->data) = v1.val.px.ip; + l->attrs[0].u.ptr = ad; + } break; - case EAF_TYPE_EC_SET: - if (v1.type != T_ECLIST) - runtime( "Setting eclist attribute to non-eclist value" ); + case T_PATH: + case T_CLIST: + case T_ECLIST: l->attrs[0].u.ptr = v1.val.ad; break; - case EAF_TYPE_UNDEF: - if (v1.type != T_VOID) - runtime( "Setting void attribute to non-void value" ); + case T_VOID: l->attrs[0].u.data = 0; break; - default: bug("Unknown type in e,S"); + default: + bug( "Unknown type in 'e,S': %x", f_type ); } - if (!(what->aux & EAF_TEMP) && (!(f_flags & FF_FORCE_TMPATTR))) { + if (!(eaf & EAF_TEMP) && !(f_flags & FF_FORCE_TMPATTR)) { f_rta_cow(); l->next = (*f_rte)->attrs->eattrs; (*f_rte)->attrs->eattrs = l; diff --git a/filter/filter.h b/filter/filter.h index 9e60ef8..aed4418 100644 --- a/filter/filter.h +++ b/filter/filter.h @@ -144,8 +144,8 @@ void val_print(struct f_val v); #define T_MASK 0xff /* Internal types */ -/* Do not use type of zero, that way we'll see errors easier. */ -#define T_VOID 1 +#define T_VOID 1 /* Do not use type of zero, that way we'll see errors easier. */ +#define T_EMPTY 2 /* Special hack for atomic_aggr */ /* User visible types, which fit in int */ #define T_INT 0x10 @@ -164,7 +164,6 @@ void val_print(struct f_val v); #define T_ENUM_RTD 0x34 #define T_ENUM_ROA 0x35 /* new enums go here */ -#define T_ENUM_EMPTY 0x3f /* Special hack for atomic_aggr */ #define T_ENUM T_ENUM_LO ... T_ENUM_HI @@ -195,6 +194,29 @@ void val_print(struct f_val v); #define SA_IFINDEX 10 +/* Encode EAF_* and T_* in 'struct f_inst' aux field, + * as filters operate mostly on its native internal types T_*, + * but sometimes need access to EAF_* (for example EAF_TEMP). + */ + +static inline u16 +f_inst_aux(u8 eaf, u8 f_type) +{ + return (((u16) eaf) << 8) | (f_type & T_MASK); +} + +static inline int +f_inst_aux_eaf(u16 aux) +{ + return (aux >> 8); +} + +static inline int +f_inst_aux_f_type(u16 aux) +{ + return ((aux) & T_MASK); +} + struct f_tree { struct f_tree *left, *right; struct f_val from, to; diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index d5e5aac..19f7a21 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -123,9 +123,9 @@ CF_ADDTO(dynamic_attr, BGP_MED CF_ADDTO(dynamic_attr, BGP_LOCAL_PREF { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(EAP_BGP, BA_LOCAL_PREF)); }) CF_ADDTO(dynamic_attr, BGP_ATOMIC_AGGR - { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(EAP_BGP, BA_ATOMIC_AGGR)); }) + { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_EMPTY, EA_CODE(EAP_BGP, BA_ATOMIC_AGGR)); }) CF_ADDTO(dynamic_attr, BGP_AGGREGATOR - { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(EAP_BGP, BA_AGGREGATOR)); }) + { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_EMPTY, EA_CODE(EAP_BGP, BA_AGGREGATOR)); }) CF_ADDTO(dynamic_attr, BGP_COMMUNITY { $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(EAP_BGP, BA_COMMUNITY)); }) CF_ADDTO(dynamic_attr, BGP_ORIGINATOR_ID -- 1.7.10.4
BIRD has variety of types, used to represent most common attributes in protocols. But sometimes wery complex attributes might be found. An good example of such attribute is BGP_AGGREGATOR which consists from an INT, and QUAD types (ASN,ROUTER_ID). Rather than making *special* type for aggregator, introduce general infrastructure for structured types and implement aggregator as STRUCT ASID with its literal format written as { ASN, ROUTER_ID }. Variables of STRUCT ASID also supported. Introduce additional operator for dereference structure members: ->, So for example bgp_aggregator->'ai.as' gies an ASN number from structure. Assignment to structure members also supported on both extended attributes and variables. --- conf/cf-lex.l | 30 +++++++++++ conf/conf.h | 2 + conf/confbase.Y | 4 +- doc/bird.sgml | 61 ++++++++++++++++++++-- filter/config.Y | 96 ++++++++++++++++++++++++++++++++++ filter/f-util.c | 91 ++++++++++++++++++++++++++++++-- filter/filter.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++++ filter/filter.h | 31 +++++++++-- nest/route.h | 1 + proto/bgp/attrs.c | 14 +++-- proto/bgp/config.Y | 2 +- 11 files changed, 457 insertions(+), 21 deletions(-) diff --git a/conf/cf-lex.l b/conf/cf-lex.l index b1bbeae..5996f30 100644 --- a/conf/cf-lex.l +++ b/conf/cf-lex.l @@ -252,6 +252,7 @@ else: { \[\= return PO; \=\] return PC; +\-\> return REF; %% static int @@ -512,6 +513,33 @@ cf_define_symbol(struct symbol *sym, int type, void *def) return sym; } +/** + * cf_define_symbol_if_undef - redefine symbol with new meaning + * @sym: symbol to be defined + * @type: symbol class to assign + * @def: class dependent data + * + * Takes symbol and re-defines it to the new type if it's type was + * undefined previously (%SYM_VOID) or returns symbol unmodified + * if it's type same as specified with @type parameter, overwise + * an error is reported via cf_error(). + */ +struct symbol * +cf_define_symbol_if_undef(struct symbol *sym, int type, void *def) +{ + if (sym->class) + { + if (sym->class != type) + cf_error("Symbol already defined"); + } + else + { + sym->class = type; + sym->def = def; + } + return sym; +} + static void cf_lex_init_kh(void) { @@ -642,6 +670,8 @@ cf_symbol_class_name(struct symbol *sym) return "routing table"; case SYM_ROA: return "ROA table"; + case SYM_REF: + return "reference to struct member"; default: return "unknown type"; } diff --git a/conf/conf.h b/conf/conf.h index 2862429..5aaa1f3 100644 --- a/conf/conf.h +++ b/conf/conf.h @@ -115,6 +115,7 @@ struct symbol { #define SYM_FILTER 4 #define SYM_TABLE 5 #define SYM_ROA 6 +#define SYM_REF 255 #define SYM_VARIABLE 0x100 /* 0x100-0x1ff are variable types */ #define SYM_CONSTANT 0x200 /* 0x200-0x2ff are variable types */ @@ -141,6 +142,7 @@ void cf_lex_init(int is_cli, struct config *c); struct symbol *cf_find_symbol(byte *c); struct symbol *cf_default_name(char *template, int *counter); struct symbol *cf_define_symbol(struct symbol *symbol, int type, void *def); +struct symbol *cf_define_symbol_if_undef(struct symbol *sym, int type, void *def); void cf_push_scope(struct symbol *); void cf_pop_scope(void); struct symbol *cf_walk_symbols(struct config *cf, struct symbol *sym, int *pos); diff --git a/conf/confbase.Y b/conf/confbase.Y index c6678e7..f5f701f 100644 --- a/conf/confbase.Y +++ b/conf/confbase.Y @@ -62,7 +62,7 @@ CF_DECLS struct timeformat *tf; } -%token END CLI_MARKER INVALID_TOKEN ELSECOL DDOT +%token END CLI_MARKER INVALID_TOKEN ELSECOL DDOT REF %token GEQ LEQ NEQ AND OR %token PO PC %token <i> NUM ENUM @@ -84,7 +84,7 @@ CF_DECLS %left '+' '-' %left '*' '/' '%' %left '!' -%nonassoc '.' +%nonassoc '.' REF CF_KEYWORDS(DEFINE, ON, OFF, YES, NO) diff --git a/doc/bird.sgml b/doc/bird.sgml index e4839a6..5791c0b 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1049,6 +1049,60 @@ function is_bird_ipv6() variables of such type, but some route attributes are of enumeration type. Enumeration types are incompatible with each other. + <tag/asid struct/ + Structured types are supported in filters. Structure aggregates fixed set of + objects with possibly different types in given order. Each object within structure + has it's own label by which object can be referenced. Literals of structured type look + like <cf/{ 1, 192.168.1.1, ... }/ and order of objects in literals is important. + You can not define your own structures, but any literal of predefined structure + could be used in configuration. Standard operations like assignment to variable + of structured type, printing, comparison also supported. + + There is special operator on structured type <cf/->/ which could be used for + referencing labeled object in structured type. This allows retrieving/assignment + of particular object in structured type. + + BIRD defines following structured types: + + <cf/asid/ represents AS number of integer type, followed by a router ID of quad + type. There are two corresponding labels to reference each object within structure: + <cf/ai.as/ refering to AS number object and <cf/ai.as/ refering to router ID object. + This type is used to represent <cf/bgp_aggregator/ attribute. + + There examples on how to work with structured types in filters: +<code> +define C_IP = 10.20.20.20; +define C_BGP_AGGREGATOR = { 10, C_IP }; + +function make_aggregator(int asn; quad router_id) +asid struct ai; +{ + ai = { asn, router_id }; + return ai; +} + +function print_aggregator(asid struct ai) +{ + print "aggregator: ", ai; + print "ai.as: ", ai->'ai.as', ", ai.id: ", ai->'ai.id'; + print "--"; +} + +function test_aggregator() +{ + print { 10, 10.10.10.10 }->'ai.as', ", ", { 10, 10.10.10.10 }->'ai.id'; + print_aggregator({ 10, 10.10.10.10 }); + print_aggregator(C_BGP_AGGREGATOR); + + bgp_aggregator = { 10, 10.30.30.30 }; + print_aggregator(bgp_aggregator); + + bgp_aggregator->'ai.as' = 1; + bgp_aggregator->'ai.id' = 1.1.1.1; + print_aggregator(bgp_aggregator); +} +</code> + <tag/bgppath/ BGP path is a list of autonomous system numbers. You can't write literals of this type. There are several special operators on bgppaths: @@ -1705,9 +1759,10 @@ with `<tt/O/') are optional. has been aggregated from multiple routes by some router on the path from the originator. -<!-- we don't handle aggregators right since they are of a very obscure type - <tag>bgp_aggregator</tag> ---> + <tag>asid struct bgp_aggregator</tag> This is an optional attribute which carries + ASN and router ID that formed aggregated route. See <cf/asid struct/ type + description for more information on type of this attribute. + <tag>clist <cf/bgp_community/ [O]</tag> List of community values associated with the route. Each such value is a pair (represented as a <cf/pair/ data type inside the filters) of 16-bit integers, the first of them containing the number of the AS which defines diff --git a/filter/config.Y b/filter/config.Y index a7a69a4..77b2446 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -250,6 +250,48 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv) return rv; } +static inline struct f_inst * +f_generate_ref(struct f_inst *t, struct symbol *s) +{ + struct f_inst *rv; + + s = cf_define_symbol_if_undef(s, SYM_REF, NULL); + + if (t->code == 'C') { + struct f_val res, *v = t->a1.p; + + res.type = T_VOID; + switch (v->type) { + case T_STRUCT_ASID: + if (!strcmp(s->name, "ai.as")) { + res.type = T_INT; + res.val.i = v->val.ai.as; + } else if (!strcmp(s->name, "ai.id")) { + res.type = T_QUAD; + res.val.i = v->val.ai.id; + } + break; + default: + cf_error("Expected structured type: %x", v->type); + } + + if (res.type == T_VOID) + cf_error("Unknown member in structure: %s", s->name); + + NEW_F_VAL; + rv = f_new_inst(); + rv->code = 'C'; + rv->a1.p = val; + memcpy(val, &res, sizeof(*val)); + } else { + rv = f_new_inst(); + rv->code = P('s','s'); + rv->a1.p = s; + rv->a2.p = t; + } + + return rv; +} CF_DECLS @@ -259,6 +301,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, INT, BOOL, IP, PREFIX, PAIR, QUAD, EC, SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, IF, THEN, ELSE, CASE, TRY, CATCH, + STRUCT, ASID, TRUE, FALSE, RT, RO, UNKNOWN, GENERIC, FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, CAST, DEST, IFNAME, IFINDEX, PREFERENCE, @@ -313,6 +356,7 @@ type: | BGPPATH { $$ = T_PATH; } | CLIST { $$ = T_CLIST; } | ECLIST { $$ = T_ECLIST; } + | ASID STRUCT { $$ = T_STRUCT_ASID; } | type SET { switch ($1) { case T_INT: @@ -628,6 +672,7 @@ constant: constructor: '(' term ',' term ')' { $$ = f_generate_dpair($2, $4); } | '(' ec_kind ',' term ',' term ')' { $$ = f_generate_ec($2, $4, $6); } + | '{' term ',' term '}' { $$ = f_generate_struct(T_STRUCT_ASID, $2, $4); } ; @@ -719,6 +764,8 @@ term: | rtadot dynamic_attr { $$ = $2; $$->code = P('e','a'); } + | term REF SYM { $$ = f_generate_ref($1, $3); } + | term '.' IP { $$ = f_new_inst(); $$->code = P('c','p'); $$->a1.p = $1; $$->aux = T_IP; } | term '.' LEN { $$ = f_new_inst(); $$->code = 'L'; $$->a1.p = $1; } | term '.' MASK '(' term ')' { $$ = f_new_inst(); $$->code = P('i','M'); $$->a1.p = $1; $$->a2.p = $5; } @@ -877,6 +924,55 @@ cmd: $$->a1.p = $2; $$->a2.p = build_tree( $4 ); } + | rtadot dynamic_attr REF SYM '=' term ';' { + struct f_inst_data *get_dyn; + struct f_inst *set_ref; + char **c; + + $4 = cf_define_symbol_if_undef($4, SYM_REF, NULL); + + get_dyn = f_new_inst2(sizeof(char *)); + get_dyn->i = *($2); + get_dyn->i.code = P('e','a'); + c = (char **) get_dyn->data; + *c = $4->name; + + set_ref = f_new_inst(); + set_ref->code = P('s','S'); + set_ref->a1.p = get_dyn; + set_ref->a2.p = $6; + + $$ = $2; + $$->code = P('e','S'); + $$->a1.p = set_ref; + } + | SYM REF SYM '=' term ';' { + struct f_inst_data *get_sym; + struct f_inst *set_ref; + char **c; + + if (($1->class & ~T_MASK) != SYM_VARIABLE) + cf_error( "You may set only variables." ); + + $3 = cf_define_symbol_if_undef($3, SYM_REF, NULL); + + get_sym = f_new_inst2(sizeof(char *)); + get_sym->i.code = 'V'; + get_sym->i.a1.p = $1->def; + get_sym->i.a2.p = $1->name; + c = (char **) get_sym->data; + *c = $3->name; + + set_ref = f_new_inst(); + set_ref->code = P('s','S'); + set_ref->a1.p = get_sym; + set_ref->a2.p = $5; + + $$ = f_new_inst(); + $$->code = 's'; + $$->a1.p = $1; + $$->a2.p = set_ref; + } | rtadot dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($2); } diff --git a/filter/f-util.c b/filter/f-util.c index ad91002..a02d058 100644 --- a/filter/f-util.c +++ b/filter/f-util.c @@ -15,14 +15,21 @@ struct f_inst * f_new_inst(void) { - struct f_inst * ret; - ret = cfg_alloc(sizeof(struct f_inst)); - ret->code = ret->aux = 0; - ret->arg1 = ret->arg2 = ret->next = NULL; + struct f_inst *ret; + ret = cfg_allocz(sizeof(struct f_inst)); ret->lineno = ifs->lino; return ret; } +struct f_inst_data * +f_new_inst2(unsigned int size) +{ + struct f_inst_data *ret; + ret = cfg_allocz(sizeof(struct f_inst_data) + size); + ret->i.lineno = ifs->lino; + return ret; +} + struct f_inst * f_new_dynamic_attr(int type, int f_type, int code) { @@ -71,6 +78,82 @@ f_generate_roa_check(struct symbol *sym, struct f_inst *prefix, struct f_inst *a return &ret->i; } +struct f_inst * +f_generate_struct(int type, ...) +{ + struct f_inst *rv; + va_list args; + + va_start(args, type); + + switch (type) { + case T_STRUCT_ASID: + { + struct f_inst *t1 = va_arg(args, struct f_inst *); + struct f_inst *t2 = va_arg(args, struct f_inst *); + int c1 = 0, c2 = 0; + struct f_asid ai = { 0, 0 }; + + if (t1->code == 'c') { + if (t1->aux != T_INT) + cf_error("Can't operate with non-integer value of AS part in AS,ID constructor"); + + ai.as = t1->a2.i; + c1 = 1; + } + + if (t2->code == 'c') { + if (t2->aux != T_QUAD) + cf_error("Can't operate with non-quad value of ID part in AS,ID constructor"); + + ai.id = t2->a2.i; + c2 = 1; + } + +#ifndef IPV6 + /* IP->Quad implicit conversion */ + else if (t2->code == 'C') { + struct f_val *val = t2->a1.p; + + if (val->type == T_QUAD) + ai.id = val->val.i; + else if (val->type == T_IP) + ai.id = ipa_to_u32(val->val.px.ip); + else + cf_error("Can't operate with non-quad value of ID part in AS,ID constructor"); + + c2 = 1; + } +#endif + + if (c1 && c2) { + NEW_F_VAL; + rv = f_new_inst(); + rv->code = 'C'; + rv->a1.p = val; + val->type = T_STRUCT_ASID; + val->val.ai = ai; + } else { + rv = f_new_inst(); + rv->code = P('m','a'); + rv->a1.p = t1; + rv->a2.p = t2; + } + } + break; + + default: + rv = NULL; + } + + va_end(args); + + if (!rv) + cf_error("Unknown structure type: %x", type); + + return rv; +} + char * filter_name(struct filter *filter) { diff --git a/filter/filter.c b/filter/filter.c index 9b99f28..4b41d4f 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -50,6 +50,19 @@ #define CMP_ERROR 999 +/* [S]tructured types [E]rror codes and their text representation */ +#define SE_OK 0 +#define SE_NOT_STRUCT 1 +#define SE_IN_TYPE 2 +#define SE_NO_MEMBER 3 + +static char *se_desc[] = { + [SE_OK] = "", + [SE_NOT_STRUCT] = "Requested type is not structure", + [SE_IN_TYPE] = "Attempt to set member to incompatible type", + [SE_NO_MEMBER] = "Unknown member in structure", +}; + static struct adata * adata_empty(struct linpool *pool, int l) { @@ -188,6 +201,9 @@ val_compare(struct f_val v1, struct f_val v2) return uint_cmp(v1.val.i, v2.val.i); case T_EC: return u64_cmp(v1.val.ec, v2.val.ec); + case T_STRUCT_ASID: + return u64_cmp((((u64) v1.val.ai.as) << 32) | v1.val.ai.id, + (((u64) v2.val.ai.as) << 32) | v2.val.ai.id); case T_IP: return ipa_compare(v1.val.px.ip, v2.val.px.ip); case T_PREFIX: @@ -490,6 +506,7 @@ val_print(struct f_val v) case T_PAIR: logn("(%d,%d)", v.val.i >> 16, v.val.i & 0xffff); return; case T_QUAD: logn("%R", v.val.i); return; case T_EC: ec_format(buf2, v.val.ec); logn("%s", buf2); return; + case T_STRUCT_ASID: logn("{ %u, %R }", v.val.ai.as, v.val.ai.id); return; case T_PREFIX_SET: trie_print(v.val.ti); return; case T_SET: tree_print(v.val.t); return; case T_ENUM: logn("(enum %x)%d", v.type, v.val.i); return; @@ -710,6 +727,102 @@ interpret(struct f_inst *what) break; } + case P('m','a'): + { + TWOARGS; + + struct f_asid ai; + + if (v1.type != T_INT) + runtime("Can't operate with non-integer value of AS part in AS,ID constructor"); + ai.as = v1.val.i; + + if (v2.type == T_QUAD) + ai.id = v2.val.i; +#ifndef IPV6 + /* IP->Quad implicit conversion */ + else if (v2.type == T_IP) + ai.id = ipa_to_u32(v2.val.px.ip); +#endif + else + runtime("Can't operate with non-quad value of ID part in AS,ID constructor"); + + res.val.ai = ai; + res.type = T_STRUCT_ASID; + break; + } + + case P('s','s'): + { + char *name; + + ARG(v2, a2.p); + + sym = what->a1.p; + name = sym->name; + + i = SE_OK; + switch (v2.type) { + case T_STRUCT_ASID: + if (!strcmp(name, "ai.as")) { + res.type = T_INT; + res.val.i = v2.val.ai.as; + } else if (!strcmp(name, "ai.id")) { + res.type = T_QUAD; + res.val.i = v2.val.ai.id; + } else + i = SE_NO_MEMBER; + break; + default: + i = SE_NOT_STRUCT; + } + + if (i != SE_OK) + runtime(se_desc[i]); + } + break; + + case P('s','S'): + { + char **pname; + char *name; + + TWOARGS; + + pname = (char **) ((struct f_inst_data *) what->a1.p)->data; + name = *pname; + + res = v1; + i = SE_OK; + switch (res.type) { + case T_STRUCT_ASID: + if (!strcmp(name, "ai.as")) { + if (v2.type == T_INT) + res.val.ai.as = v2.val.i; + else + i = SE_IN_TYPE; + } else if (!strcmp(name, "ai.id")) { + if (v2.type == T_QUAD) + res.val.ai.id = v2.val.i; +#ifndef IPV6 + /* IP->Quad implicit conversion */ + else if (v2.type == T_IP) + res.val.ai.id = ipa_to_u32(v2.val.px.ip); +#endif + else + i = SE_IN_TYPE; + } else + i = SE_NO_MEMBER; + break; + default: + i = SE_NOT_STRUCT; + } + + if (i != SE_OK) + runtime(se_desc[i]); + } + break; + /* Relational operators */ #define COMPARE(f,x) \ @@ -937,6 +1050,12 @@ interpret(struct f_inst *what) res.val.px.ip = * (ip_addr *) ad->data; } break; + case T_STRUCT_ASID: + { + struct adata * ad = e->u.ptr; + res.val.ai = * (struct f_asid *) ad->data; + } + break; case T_PATH: case T_CLIST: case T_ECLIST: @@ -998,6 +1117,14 @@ interpret(struct f_inst *what) l->attrs[0].u.ptr = ad; } break; + case T_STRUCT_ASID: + { + struct adata *ad = lp_alloc(f_pool, sizeof(struct adata) + sizeof(struct f_asid)); + ad->length = sizeof(struct f_asid); + (* (struct f_asid *) ad->data) = v1.val.ai; + l->attrs[0].u.ptr = ad; + } + break; case T_PATH: case T_CLIST: case T_ECLIST: @@ -1352,6 +1479,7 @@ i_same(struct f_inst *f1, struct f_inst *f2) case '&': case P('m','p'): case P('m','c'): + case P('m','a'): case P('!','='): case P('=','='): case '<': @@ -1361,6 +1489,7 @@ i_same(struct f_inst *f1, struct f_inst *f2) case '~': TWOARGS; break; case P('d','e'): ONEARG; break; + case P('s','s'): case 's': ARG(v2, a2.p); { @@ -1374,6 +1503,23 @@ i_same(struct f_inst *f1, struct f_inst *f2) } break; + case P('s','S'): + TWOARGS; + { + char **pname1, **pname2; + char *name1, *name2; + + pname1 = (char **) ((struct f_inst_data *) f1->a1.p)->data; + name1 = *pname1; + + pname2 = (char **) ((struct f_inst_data *) f2->a1.p)->data; + name2 = *pname2; + + if (strcmp(name1, name2)) + return 0; + } + break; + case 'c': switch (f1->aux) { diff --git a/filter/filter.h b/filter/filter.h index aed4418..970d093 100644 --- a/filter/filter.h +++ b/filter/filter.h @@ -29,6 +29,11 @@ struct f_inst { /* Instruction */ int lineno; }; +struct f_inst_data { + struct f_inst i; + byte data[0]; +}; + #define arg1 a1.p #define arg2 a2.p @@ -48,11 +53,17 @@ struct f_prefix { /* If range then prefix must be in range (len >> 16 & 0xff, len >> 8 & 0xff) */ }; +struct f_asid { + u32 as; + u32 id; +}; + struct f_val { int type; union { int i; u64 ec; + struct f_asid ai; /* ip_addr ip; Folded into prefix */ struct f_prefix px; char *s; @@ -69,10 +80,12 @@ struct filter { }; struct f_inst *f_new_inst(void); +struct f_inst_data *f_new_inst2(unsigned int size); struct f_inst *f_new_dynamic_attr(int type, int f_type, int code); /* Type as core knows it, type as filters know it, and code of dynamic attribute */ struct f_tree *f_new_tree(void); struct f_inst *f_generate_complex(int operation, int operation_aux, struct f_inst *dyn, struct f_inst *argument); struct f_inst *f_generate_roa_check(struct symbol *sym, struct f_inst *prefix, struct f_inst *asn); +struct f_inst *f_generate_struct(int type, ...); struct f_tree *build_tree(struct f_tree *); @@ -177,9 +190,21 @@ void val_print(struct f_val v); #define T_ECLIST 0x26 /* Extended community list */ #define T_EC 0x27 /* Extended community value, u64 */ -#define T_RETURN 0x40 -#define T_SET 0x80 -#define T_PREFIX_SET 0x81 +/* Put set types in 0x40..0x4f range */ +#define T_SET 0x40 /* General set type */ +#define T_PREFIX_SET 0x41 /* Special set for prefixes */ +/* new sets go here */ + +/* Put structured types in 0x50..0x5f range */ +#define T_STRUCT_LO 0x50 +#define T_STRUCT_HI 0x5f + +#define T_STRUCT_ASID 0x50 /* AS,ID structure (BGP_AGGREGATOR) */ +/* new structs go here */ + +#define T_STRUCT T_STRUCT_LO ... T_STRUCT_HI + +#define T_RETURN 0x80 #define SA_FROM 1 diff --git a/nest/route.h b/nest/route.h index 35b5fa1..7aadfc9 100644 --- a/nest/route.h +++ b/nest/route.h @@ -392,6 +392,7 @@ typedef struct eattr { #define EAF_TYPE_IP_ADDRESS 0x04 /* IP address */ #define EAF_TYPE_ROUTER_ID 0x05 /* Router ID (IPv4 address) */ #define EAF_TYPE_AS_PATH 0x06 /* BGP AS path (encoding per RFC 1771:4.3) */ +#define EAF_TYPE_BGP_AGGREGATOR 0x08 /* BGP AGGREGATOR attribute */ #define EAF_TYPE_INT_SET 0x0a /* Set of u32's (e.g., a community list) */ #define EAF_TYPE_EC_SET 0x0e /* Set of pairs of u32's - ext. community list */ #define EAF_TYPE_UNDEF 0x0f /* `force undefined' entry */ diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index c27a498..706ed7d 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -231,14 +231,10 @@ bgp_check_aggregator(struct bgp_proto *p, byte *a UNUSED, int len) static void bgp_format_aggregator(eattr *a, byte *buf, int buflen UNUSED) { - struct adata *ad = a->u.ptr; - byte *data = ad->data; - u32 as; - - as = get_u32(data); - data += 4; + struct adata *ad = a->u.ptr; + struct f_asid *ai = (struct f_asid *) ad->data; - bsprintf(buf, "%d.%d.%d.%d AS%u", data[0], data[1], data[2], data[3], as); + bsprintf(buf, "%R AS%u", ai->id, ai->as); } static int @@ -302,7 +298,7 @@ static struct attr_desc bgp_attr_table[] = { NULL, NULL }, { "atomic_aggr", 0, BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_ATOMIC_AGGR */ NULL, NULL }, - { "aggregator", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_AGGREGATOR */ + { "aggregator", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_BGP_AGGREGATOR, 1,/* BA_AGGREGATOR */ bgp_check_aggregator, bgp_format_aggregator }, { "community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_INT_SET, 1, /* BA_COMMUNITY */ bgp_check_community, NULL }, @@ -597,6 +593,7 @@ bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains) memcpy(w, &ip, len); break; } + case EAF_TYPE_BGP_AGGREGATOR: case EAF_TYPE_INT_SET: case EAF_TYPE_EC_SET: { @@ -1660,6 +1657,7 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin case EAF_TYPE_IP_ADDRESS: ipa_ntoh(*(ip_addr *)ad->data); break; + case EAF_TYPE_BGP_AGGREGATOR: case EAF_TYPE_INT_SET: case EAF_TYPE_EC_SET: { diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 19f7a21..0c949ff 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -125,7 +125,7 @@ CF_ADDTO(dynamic_attr, BGP_LOCAL_PREF CF_ADDTO(dynamic_attr, BGP_ATOMIC_AGGR { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_EMPTY, EA_CODE(EAP_BGP, BA_ATOMIC_AGGR)); }) CF_ADDTO(dynamic_attr, BGP_AGGREGATOR - { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_EMPTY, EA_CODE(EAP_BGP, BA_AGGREGATOR)); }) + { $$ = f_new_dynamic_attr(EAF_TYPE_BGP_AGGREGATOR, T_STRUCT_ASID, EA_CODE(EAP_BGP, BA_AGGREGATOR)); }) CF_ADDTO(dynamic_attr, BGP_COMMUNITY { $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(EAP_BGP, BA_COMMUNITY)); }) CF_ADDTO(dynamic_attr, BGP_ORIGINATOR_ID -- 1.7.10.4
participants (3)
-
Ondrej Zajicek -
Sergey Popovich -
Сергей Попович