/*
* $Id: rpsl_commands.c,v 1.10 2002/10/17 20:02:31 ljb Exp $
* originally Id: rpsl_commands.c,v 1.22 1998/07/31 15:42:39 gerald Exp
*/
#include <stdio.h>
#include <string.h>
#include "mrt.h"
#include "trace.h"
#include <time.h>
#include <signal.h>
#include "config_file.h"
#include <sys/types.h>
#include <ctype.h>
#include "radix.h"
#include "hash.h"
#include "stack.h"
#include <fcntl.h>
#include "irrd.h"
extern trace_t *default_trace;
/* a list of prefix range types */
enum EXPAND_TYPE {
NO_EXPAND, ROUTE_SET_EXPAND, OTHER_EXPAND };
/* local routines */
void update_members_list (irr_database_t *database, char *range_op,
enum EXPAND_TYPE expand_flag,
LINKED_LIST *ll_member_examined,
LINKED_LIST *ll_setlist, LINKED_LIST *ll_set_names,
STACK *stack, irr_connection_t *irr);
void mbrs_by_ref_set (irr_database_t *database, char *range_op,
enum EXPAND_TYPE expand_flag, LINKED_LIST *ll_setlist,
char *set_name, LINKED_LIST *ll_mbr_by_ref, irr_connection_t *irr);
char *rpsl_macro_expand_add (char *range, char *name,
irr_connection_t *irr, char *dbname);
void SL_Add (LINKED_LIST *ll_setlist, char *member, char *range_op, enum EXPAND_TYPE expand_flag, irr_connection_t *irr, irr_database_t *database);
int set_name (char *);
/* as-set/rs-set expansion !ias-bar */
void irr_set_expand (irr_connection_t *irr, char *name) {
irr_database_t *database;
STACK *stack;
LINKED_LIST *ll_member_examined, *ll_setlist;
char *set_name, *last_set_name, *mstr, *db;
char *lasts, *range_op, key[BUFSIZE];
int first, dup, expand_flag = NO_EXPAND;
hash_spec_t *hash_spec;
if (strchr(name, ',') != NULL) {
strtok_r(name, ",", &lasts);
/* check if we are expanding a route-set */
if (strchr(name, ':') != NULL)
set_name = strchr(name,':') + 1;
else
set_name = name;
if (!strncasecmp (set_name, "rs-", 3))
expand_flag = ROUTE_SET_EXPAND;
else
expand_flag = OTHER_EXPAND;
}
convert_toupper (name);
stack = Create_STACK (500);
ll_member_examined = LL_Create (LL_DestroyFunction, free, NULL);
ll_setlist = LL_Create (LL_AutoSort, True, LL_CompareFunction, irr_comp,
LL_DestroyFunction, free, NULL);
mstr = rpsl_macro_expand_add (" ", name, irr, NULL);
Push (stack, mstr);
LL_Add (ll_member_examined, strdup (name));
while (Get_STACK_Count (stack) > 0) {
mstr = (char *) Pop (stack);
/* might want to check the examined list to see if this set name
has been examined already */
first = 1;
lasts = NULL;
range_op = strtok_r (mstr, ",", &lasts);
if (!strcmp (range_op, " ")) range_op = NULL;
set_name = strtok_r (NULL, ",", &lasts);
while ((db = strtok_r (NULL, ",", &lasts)) != NULL) {
if ((database = find_database (db)) == NULL) {
trace (ERROR, default_trace, "irr_set_expand(): Database not found %s\n", db);
break;
}
irr_lock (database);
make_setobj_key (key, set_name);
if ((hash_spec = fetch_hash_spec (database, key, UNPACK)) != NULL) {
first = 0;
update_members_list (database, range_op, expand_flag, ll_member_examined,
ll_setlist, hash_spec->ll_1, stack, irr);
mbrs_by_ref_set (database, range_op, expand_flag, ll_setlist,
set_name, hash_spec->ll_2, irr);
Delete_hash_spec (hash_spec);
}
irr_unlock (database);
if (first == 0)
break;
}
free (mstr);
}
first = 1;
dup = 0;
last_set_name = "";
LL_ContIterate (ll_setlist, set_name) {
if (!first) {
/* since list is sorted, any duplicates should be consecutive */
if (strcmp (last_set_name, set_name) == 0)
dup = 1;
else /* add a space before each item */
irr_add_answer (irr, " ");
}
if (!dup) { /* only print this if not a duplicate */
irr_add_answer (irr, "%s", set_name);
last_set_name = set_name;
} else
dup = 0;
first = 0;
}
irr_send_answer (irr);
LL_Destroy (ll_setlist);
LL_Destroy (ll_member_examined);
Destroy_STACK (stack);
}
void mbrs_by_ref_set (irr_database_t *database, char *range_op,
enum EXPAND_TYPE expand_flag, LINKED_LIST *ll_setlist,
char *set_name, LINKED_LIST *ll_mbr_by_ref, irr_connection_t *irr) {
char *member, *maint, key[BUFSIZE];
hash_spec_t *hash_spec;
if (ll_mbr_by_ref == NULL)
return;
if (expand_flag == NO_EXPAND) {
LL_ContIterate (ll_mbr_by_ref, member) {
LL_Add(ll_setlist, strdup (member));
}
return;
}
/* Find all the route or autnum's which reference the set name
* (via 'members-of:').
*/
LL_ContIterate (ll_mbr_by_ref, maint) {
make_spec_key (key, maint, set_name);
if ((hash_spec = fetch_hash_spec (database, key, UNPACK)) != NULL) {
LL_ContIterate (hash_spec->ll_1, member) {
SL_Add (ll_setlist, member, range_op, expand_flag, irr, database);
}
Delete_hash_spec (hash_spec);
}
}
}
void update_members_list (irr_database_t *database, char *range_op, enum EXPAND_TYPE expand_flag,
LINKED_LIST *ll_member_examined,
LINKED_LIST *ll_setlist, LINKED_LIST *ll_set_names,
STACK *stack, irr_connection_t *irr) {
char *member, *p, *r;
char buffer[BUFSIZE], range_buf[512];
int already_examined, len = 0;
if (ll_set_names == NULL)
return;
LL_ContIterate (ll_set_names, member) {
if ((expand_flag == NO_EXPAND) || !set_name (member)) {
SL_Add (ll_setlist, member, range_op, expand_flag, irr, database);
} else { /* we have a set name */
already_examined = 0;
LL_ContIterate (ll_member_examined, p) {
if (!strcmp (p, member)) {
already_examined = 1;
break;
}
}
if (!already_examined) {
strcpy(range_buf, " "); /* initialize to empty range */
r = member;
/* Need to seperate the range op from the set
name for rpsl_macro_expand_add() */
if ((p = strchr (member, '^')) != NULL) {
strncpy (buffer, member, p - member);
buffer[p - member] = '\0';
len = strlen(p);
if (len < 512) /* don't overflow buffer */
strcpy(range_buf,p);
r = buffer; /* this is the set name without the range op */
}
if (range_op != NULL) { /* append existing range_op */
if ( (len + strlen(range_op)) < 512 ) {
if (p == NULL)
range_buf[0] = '\0';
strcat(range_buf, range_op);
}
}
p = rpsl_macro_expand_add (range_buf, r, irr, database->name);
Push (stack, p);
LL_Add (ll_member_examined, strdup (r));
}
}
}
} /* void update_members_list() */
/* a list of prefix range types */
enum PREFIX_RANGE_TYPE {
INVALID_RANGE, EXCLUSIVE_RANGE, INCLUSIVE_RANGE, VALUE_RANGE };
enum PREFIX_RANGE_TYPE prefix_range_parse( char *range, unsigned int *start, unsigned int *end ) {
char *p;
p = range;
if (*p == '+')
return INCLUSIVE_RANGE;
if (*p == '-')
return EXCLUSIVE_RANGE;
if (strchr(p, '-') != NULL) {
if (sscanf(p, "%u-%u", start, end) != 2)
return INVALID_RANGE;
else
return VALUE_RANGE;
}
if (sscanf(p, "%u", start) != 1)
return INVALID_RANGE;
else {
*end = *start;
return VALUE_RANGE;
}
}
/*
* Sorted and unique linked list add. *p is the potential item to be added.
* Also appends a range operator to *p.
*
*/
void SL_Add (LINKED_LIST *ll_setlist, char *member, char *range_op, enum EXPAND_TYPE expand_flag, irr_connection_t *irr, irr_database_t *database) {
char buffer[BUFSIZE], rangestr[32];
char *q, *temp_ptr, *last, *range_ptr;
hash_spec_t *hash_spec;
irr_database_t *db;
unsigned int bitlen;
enum PREFIX_RANGE_TYPE range_op_type, prefix_range_type;
unsigned int range_op_start, range_op_end, prefix_range_start, prefix_range_end;
/* if performing a route-set expansion, check for AS numbers and lookup route prefixes which list the AS as their origin */
if ( expand_flag == ROUTE_SET_EXPAND && !strncasecmp(member, "AS", 2)) {
make_gas_key(buffer, member + 2);
LL_ContIterate (irr->ll_database, db) { /* search over all databases */
if (db != database)
irr_lock(db); /* don't lock if searching current database */
if ((hash_spec = fetch_hash_spec(db, buffer, FAST)) != NULL) {
if (hash_spec->len1 > 0) {
q = strdup(hash_spec->gas_answer);
temp_ptr = strtok_r(q, " ", &last);
while (temp_ptr != NULL && *temp_ptr != '\0') {
SL_Add(ll_setlist, temp_ptr, range_op, expand_flag, irr, database);
temp_ptr = strtok_r(NULL, " ", &last);
}
free(q);
Delete_hash_spec(hash_spec);
}
}
if (db != database)
irr_unlock(db);
}
return;
}
strcpy (buffer, member);
if (range_op == NULL)
goto add_member;
if (*range_op != '^') { /* should start with a '^' */
trace (ERROR, default_trace, "SL_Add(): range_op does not start with a '^' : %s\n", range_op);
goto add_member;
}
range_ptr = strdup(range_op + 1);
q = strtok_r(range_ptr, "^", &last);
while (q != NULL && *q != '\0') {
range_op_type = prefix_range_parse(q, &range_op_start, &range_op_end);
if (range_op_type == INVALID_RANGE) {
trace (ERROR, default_trace, "SL_Add(): range_op is invalid : %s\n", range_op);
return;
}
/* should have a prefix length to be legal */
if ( (temp_ptr = strchr(member, '/')) != NULL )
bitlen = atoi(temp_ptr + 1);
else {
trace (ERROR, default_trace, "SL_Add(): prefix missing: %s\n", member);
free(range_ptr);
return;
}
if ( (temp_ptr = strchr(buffer, '^')) == NULL ) /* check for range on prefix */
prefix_range_start = (prefix_range_end = bitlen);
else {
prefix_range_type = prefix_range_parse(temp_ptr + 1, &prefix_range_start, &prefix_range_end);
if (prefix_range_type == INVALID_RANGE) {
free(range_ptr);
trace (ERROR, default_trace, "SL_Add(): prefix range is invalid : %s\n", buffer);
return;
}
*temp_ptr = 0; /* terminate string at range */
if (prefix_range_type == EXCLUSIVE_RANGE) {
prefix_range_start = bitlen + 1;
prefix_range_end = 32;
} else if (prefix_range_type == INCLUSIVE_RANGE) {
prefix_range_start = bitlen;
prefix_range_end = 32;
}
}
if (range_op_type == INCLUSIVE_RANGE) {
range_op_start = prefix_range_start;
range_op_end = 32;
} else if (range_op_type == EXCLUSIVE_RANGE) {
range_op_start = prefix_range_start + 1;
range_op_end = 32;
} else if (range_op_type == VALUE_RANGE) {
if (range_op_end < prefix_range_start) {
free(range_ptr);
return; /* apply an less specific range to a more specific one */
}
if (prefix_range_start > range_op_start)
range_op_start = prefix_range_start;
}
if (range_op_start > range_op_end) {
free(range_ptr);
return; /* specific range exceeds maximum, toss prefix */
}
if ( (bitlen == range_op_start) && (bitlen == range_op_end) )
strcpy(rangestr,"");
else if (range_op_end == range_op_start)
sprintf(rangestr,"^%d", range_op_start);
else if (range_op_end == 32 && range_op_start == bitlen)
strcpy(rangestr,"^+");
else if (range_op_end == 32 && range_op_start == (bitlen + 1))
strcpy(rangestr,"^-");
else
sprintf(rangestr,"^%d-%d",range_op_start, range_op_end);
strcat(buffer, rangestr);
q = strtok_r(NULL, "^", &last);
}
free(range_ptr);
add_member:
LL_Add(ll_setlist, strdup (buffer));
}
char *rpsl_macro_expand_add (char *range, char *name, irr_connection_t *irr,
char *dbname) {
irr_database_t *database;
char buffer[BUFSIZE];
strcpy (buffer, range);
strcat (buffer, ",");
strcat (buffer, name);
convert_toupper (buffer); /* canonicalize the key for hash lookups */
if (dbname != NULL) {
strcat (buffer, ",");
strcat (buffer, dbname);
}
LL_ContIterate (irr->ll_database, database) {
if (dbname == NULL || strcasecmp (database->name, dbname)) {
strcat (buffer, ",");
strcat (buffer, database->name);
}
}
return (strdup (buffer));
}
int set_name (char *name) {
if (name == NULL)
return 0;
if (!strncasecmp (name, "as-", 3) ||
!strncasecmp (name, "rs-", 3) ||
!strncasecmp (name, "rtrs-", 5) ||
!strncasecmp (name, "fltr-", 5) ||
!strncasecmp (name, "prng-", 5) ||
strchr (name, ':'))
return 1;
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1