The attached patch (tested to varying degrees in different points of its lifetime, compiles and performs as expected on my system) adds the ability to perform these operations Also attached is a patch that uses pairxlatmove instead of pairmove, which is the way I actually use it. The patch adds these operations reply ^= { } reply ^== () { } reply ^. { } reply ^.= () { } reply $= { } reply $== () { } reply $. { } reply $.= () { } ======================================== reply ^= { Attribute1 += "Value1" } Will insert the attribute at the top of the list. reply ^== ( Attribute2 == "Value2" ) { Attribute1 += "Value1" } Will insert attribute1 before Attribute2 if found, otherwise it behaves like ^= ^. and ^.= have the same difference as .= and = namely they call pairadd instead of pairmove Otherwise they are the same. Likewise provided are $= , $. , $== and $.= which insert AFTER the attribute. IOW $= and $. are synonymous with = and .= but are provided for completeness. $== and $.= are quite usefull in and of themselves. ========================================== Motivation: Cisco NAS's will kick users who assign a VRF after assigning an IP address. The VRF must come first. This allows this form in my policy.txt file. ====cut here========= policy add_inter_vrf { reply ^== ( reply:Cisco-Avpair =~ "lcp:interface-config") { Cisco-Avpair += "lcp:interface-config=ip vrf forwarding CHL-PRIVATE" } if (!(reply:Cisco-Avpair =~ "lcp:interface-config=ip address.*")) { reply $== (reply:Cisco-AVpair == "lcp:interface-config=ip vrf forwarding CHL-PRIVATE") { Cisco-Avpair += "lcp:interface-config=ip unnumbered l10" } } if (!(reply:Framed-IP-Address =* "")) { reply = { Cisco-Avpair += "ip:addr-pool=privatepool" } } } ====cut here========= Enjoy! #! /bin/sh /usr/share/dpatch/dpatch-run ## 240-before-after-where-head-tail.dpatch by <joe@nameserver3.ttec.com> ## ## All lines beginning with `## DP:' are a description of the patch. ## DP: No description. @DPATCH@ diff -urNad freeradius-1.1.0~/src/modules/rlm_policy/evaluate.c freeradius-1.1.0/src/modules/rlm_policy/evaluate.c --- freeradius-1.1.0~/src/modules/rlm_policy/evaluate.c 2005-11-27 12:22:35.000000000 -0500 +++ freeradius-1.1.0/src/modules/rlm_policy/evaluate.c 2005-11-27 12:22:35.000000000 -0500 @@ -882,6 +882,7 @@ VALUE_PAIR **vps = NULL; VALUE_PAIR *vp, *head, **tail; const policy_item_t *attr; + policy_lex_t this_how; this = (const policy_attributes_t *) item; @@ -932,20 +933,119 @@ tail = &(vp->next); } - switch (this->how) { + this_how = this->how; +retry_how: + switch (this_how) { case POLICY_LEX_SET_EQUALS: /* dangerous: removes all previous things! */ pairfree(vps); *vps = head; break; + case POLICY_LEX_AFTER_TAIL_ASSIGN: case POLICY_LEX_ASSIGN: /* 'union' */ - pairmove(vps, head); + pairmove(vps, &head); pairfree(&head); break; + case POLICY_LEX_BEFORE_HEAD_ASSIGN: + pairmove(&head, &vps); + pairfree(vps); + *vps = head; + break; + + case POLICY_LEX_AFTER_TAIL_EQUALS: case POLICY_LEX_CONCAT_EQUALS: pairadd(vps, head); break; + + case POLICY_LEX_BEFORE_HEAD_EQUALS: + pairadd(&head, *vps); + *vps = head; + break; + + case POLICY_LEX_BEFORE_WHERE_EQUALS: + case POLICY_LEX_AFTER_WHERE_EQUALS: + case POLICY_LEX_BEFORE_WHERE_ASSIGN: + case POLICY_LEX_AFTER_WHERE_ASSIGN: + /* find location*/ + { + VALUE_PAIR *vpprev = NULL, *vpnext = NULL, *lvp; + + for(lvp = *vps; lvp; vpprev = lvp, lvp = lvp->next) { + vpnext = lvp->next; + lvp->next = NULL; + if (evaluate_condition(state, this->where_loc)) + break; + lvp->next = vpnext; + } + + if (lvp) { + switch(this_how) { + case POLICY_LEX_BEFORE_WHERE_EQUALS: + case POLICY_LEX_BEFORE_WHERE_ASSIGN: + if (vpprev) { + lvp->next = vpnext; + vpnext = lvp; + vpprev->next = NULL; + lvp = vpprev; + } + default: /* always reached */ + break; + } + + switch(this_how) { + case POLICY_LEX_BEFORE_WHERE_EQUALS: + if (vpprev) + pairadd(&lvp, head); + else + *vps = lvp = head; + break; + case POLICY_LEX_AFTER_WHERE_EQUALS: + pairadd(&lvp, head); + break; + case POLICY_LEX_BEFORE_WHERE_ASSIGN: + if (vpprev) { + pairmove(&lvp, &head); + pairfree(&head); + } + else + *vps = lvp = head; + break; + case POLICY_LEX_AFTER_WHERE_ASSIGN: + pairmove(&lvp, &head); + pairfree(&head); + break; + default:/*never reached*/ + break; + } + for( ; lvp && lvp->next; lvp = lvp->next); + if (lvp) + lvp->next = vpnext; + break; + } + + switch(this_how) { + case POLICY_LEX_BEFORE_WHERE_EQUALS: + this_how = POLICY_LEX_BEFORE_HEAD_EQUALS; + break; + case POLICY_LEX_AFTER_WHERE_EQUALS: + this_how = POLICY_LEX_AFTER_TAIL_EQUALS; + break; + case POLICY_LEX_BEFORE_WHERE_ASSIGN: + this_how = POLICY_LEX_BEFORE_HEAD_ASSIGN; + break; + case POLICY_LEX_AFTER_WHERE_ASSIGN: + this_how = POLICY_LEX_AFTER_TAIL_ASSIGN; + break; + default: /*never reached*/ + break; + } + goto retry_how; + } + fprintf(stderr, "HUH?\n"); + pairfree(&head); + return 0; + break; /* never reach here */ default: fprintf(stderr, "HUH?\n"); diff -urNad freeradius-1.1.0~/src/modules/rlm_policy/parse.c freeradius-1.1.0/src/modules/rlm_policy/parse.c --- freeradius-1.1.0~/src/modules/rlm_policy/parse.c 2005-11-27 12:22:35.000000000 -0500 +++ freeradius-1.1.0/src/modules/rlm_policy/parse.c 2005-11-27 12:22:35.000000000 -0500 @@ -106,6 +106,14 @@ { ">=", POLICY_LEX_GT }, { "=~", POLICY_LEX_RX_EQUALS }, { "!~", POLICY_LEX_RX_NOT_EQUALS }, + { "^=", POLICY_LEX_BEFORE_HEAD_ASSIGN }, + { "^==", POLICY_LEX_BEFORE_WHERE_ASSIGN }, + { "^.", POLICY_LEX_BEFORE_HEAD_EQUALS }, + { "^.=", POLICY_LEX_BEFORE_WHERE_EQUALS }, + { "$=", POLICY_LEX_AFTER_TAIL_ASSIGN }, + { "$==", POLICY_LEX_AFTER_WHERE_ASSIGN }, + { "$.", POLICY_LEX_AFTER_TAIL_EQUALS }, + { "$.=", POLICY_LEX_AFTER_WHERE_EQUALS }, { ".=", POLICY_LEX_CONCAT_EQUALS }, { ":=", POLICY_LEX_SET_EQUALS }, { "-=", POLICY_LEX_MINUS_EQUALS }, @@ -219,6 +227,56 @@ } *token = POLICY_LEX_BAD; return input + 1; + + case '^': + if (input[1] == '.' ) { + if (input[2] == '=') { + *token = POLICY_LEX_BEFORE_WHERE_EQUALS; + return input + 3; + } + else { + *token = POLICY_LEX_BEFORE_HEAD_EQUALS; + return input + 2; + } + } + else if (input[1] == '=') { + if (input[2] == '=') { + *token = POLICY_LEX_BEFORE_WHERE_ASSIGN; + return input + 3; + } + else { + *token = POLICY_LEX_BEFORE_HEAD_ASSIGN; + return input + 2; + } + } + + *token = POLICY_LEX_BAD; + return input + 1; + + case '$': + if (input[1] == '.' ) { + if (input[2] == '=') { + *token = POLICY_LEX_AFTER_WHERE_EQUALS; + return input + 3; + } + else { + *token = POLICY_LEX_AFTER_TAIL_EQUALS; + return input + 2; + } + } + else if (input[1] == '=') { + if (input[2] == '=') { + *token = POLICY_LEX_AFTER_WHERE_ASSIGN; + return input + 3; + } + else { + *token = POLICY_LEX_AFTER_TAIL_ASSIGN; + return input + 2; + } + } + + *token = POLICY_LEX_BAD; + return input + 1; case ':': if (input[1] == '=') { @@ -935,9 +993,31 @@ policy_lex_t token; policy_attributes_t *this; char buffer[32]; + + this = rad_malloc(sizeof(*this)); + if (!this) { + fprintf(stderr, "%s[%d]: rad_malloc failed\n", + lexer->filename, lexer->lineno); + return 0; + } + memset(this, 0, sizeof(*this)); + this->item.type = POLICY_TYPE_ATTRIBUTE_LIST; token = policy_lex_file(lexer, 0, buffer, sizeof(buffer)); switch (token) { + case POLICY_LEX_BEFORE_WHERE_EQUALS: + case POLICY_LEX_AFTER_WHERE_EQUALS: + case POLICY_LEX_BEFORE_WHERE_ASSIGN: + case POLICY_LEX_AFTER_WHERE_ASSIGN: + if (!parse_condition(lexer, &(this->where_loc))) { + rlm_policy_free_item((policy_item_t *)this); + return 0; + } + break; + case POLICY_LEX_BEFORE_HEAD_EQUALS: + case POLICY_LEX_AFTER_TAIL_EQUALS: + case POLICY_LEX_BEFORE_HEAD_ASSIGN: + case POLICY_LEX_AFTER_TAIL_ASSIGN: case POLICY_LEX_ASSIGN: case POLICY_LEX_SET_EQUALS: case POLICY_LEX_CONCAT_EQUALS: @@ -947,13 +1027,10 @@ fprintf(stderr, "%s[%d]: Unexpected token %s\n", lexer->filename, lexer->lineno, lrad_int2str(rlm_policy_tokens, token, "?")); + rlm_policy_free_item((policy_item_t *)this); return 0; /* unknown */ } - - this = rad_malloc(sizeof(*this)); - memset(this, 0, sizeof(*this)); - this->item.type = POLICY_TYPE_ATTRIBUTE_LIST; this->item.lineno = lexer->lineno; this->where = where; this->how = token; diff -urNad freeradius-1.1.0~/src/modules/rlm_policy/rlm_policy.h freeradius-1.1.0/src/modules/rlm_policy/rlm_policy.h --- freeradius-1.1.0~/src/modules/rlm_policy/rlm_policy.h 2005-11-27 12:22:35.000000000 -0500 +++ freeradius-1.1.0/src/modules/rlm_policy/rlm_policy.h 2005-11-27 12:23:09.000000000 -0500 @@ -80,6 +80,14 @@ POLICY_LEX_CONCAT_EQUALS, /* .= */ POLICY_LEX_VARIABLE, /* %{foo} */ POLICY_LEX_FUNCTION, /* Hmmm... */ + POLICY_LEX_BEFORE_HEAD_ASSIGN, /* ^= */ + POLICY_LEX_BEFORE_WHERE_ASSIGN, /* ^== */ + POLICY_LEX_BEFORE_HEAD_EQUALS, /* ^. */ + POLICY_LEX_BEFORE_WHERE_EQUALS, /* ^.= */ + POLICY_LEX_AFTER_TAIL_ASSIGN, /* $= */ + POLICY_LEX_AFTER_WHERE_ASSIGN, /* $== */ + POLICY_LEX_AFTER_TAIL_EQUALS, /* $. */ + POLICY_LEX_AFTER_WHERE_EQUALS, /* $.= */ POLICY_LEX_DOUBLE_QUOTED_STRING, POLICY_LEX_SINGLE_QUOTED_STRING, POLICY_LEX_BACK_QUOTED_STRING, @@ -157,6 +165,7 @@ policy_reserved_word_t where; /* where to do it */ policy_lex_t how; /* how to do */ policy_item_t *attributes; /* things to do */ + policy_item_t *where_loc; /* search for location in list*/ /* FIXME: VALUE_PAIR *vps; */ } policy_attributes_t; #! /bin/sh /usr/share/dpatch/dpatch-run ## 245-policy-xlat.dpatch by <joe@nameserver3.ttec.com> ## ## All lines beginning with `## DP:' are a description of the patch. ## DP: No description. @DPATCH@ diff -urNad freeradius-1.1.0~/src/modules/rlm_policy/evaluate.c freeradius-1.1.0/src/modules/rlm_policy/evaluate.c --- freeradius-1.1.0~/src/modules/rlm_policy/evaluate.c 2005-11-27 11:39:20.000000000 -0500 +++ freeradius-1.1.0/src/modules/rlm_policy/evaluate.c 2005-11-27 11:40:02.000000000 -0500 @@ -943,12 +943,12 @@ case POLICY_LEX_AFTER_TAIL_ASSIGN: case POLICY_LEX_ASSIGN: /* 'union' */ - pairmove(vps, &head); + pairxlatmove(NULL, vps, &head); pairfree(&head); break; case POLICY_LEX_BEFORE_HEAD_ASSIGN: - pairmove(&head, &vps); + pairxlatmove(NULL, &head, &vps); pairfree(vps); *vps = head; break; @@ -1005,14 +1005,14 @@ break; case POLICY_LEX_BEFORE_WHERE_ASSIGN: if (vpprev) { - pairmove(&lvp, &head); + pairxlatmove(NULL, &lvp, &head); pairfree(&head); } else *vps = lvp = head; break; case POLICY_LEX_AFTER_WHERE_ASSIGN: - pairmove(&lvp, &head); + pairxlatmove(NULL, &lvp, &head); pairfree(&head); break; default:/*never reached*/