/*
 * $Id: ricdconf.c,v 2.2 2000/05/23 08:04:55 masaki Exp $
 */

#include "ricd.h"
#include "config_file.h"


static int hqlip_register_area_center (hqlip_t *hqlip, my_area_t *my_area, 
					int pri);
static int
hqlip_register_area_addr (uii_connection_t *uii, my_area_t *my_area, int on);

config_ricd_t *CONFIG_RICD;

/*
 * turn on/off the interface
 */
static void
ricd_activate_interface (ricd_t *ricd, hqlip_config_network_t *network, 
			 int on, my_area_t *my_area)
{
    schedule_event2 ("hqlip_activate_interface",
                     ricd->hqlip->schedule, 
		     (event_fn_t) hqlip_activate_interface,
                     4, ricd->hqlip, network, on, my_area);
    schedule_event2 ("srsvp_activate_interface",
                     ricd->srsvp->schedule, 
		     (event_fn_t) srsvp_activate_interface,
                     4, ricd->srsvp, network->interface, network->prefix, on);
}


my_area_t *
my_area_new (hqlip_t *hqlip, char *name, area_t *area)
{
    my_area_t *my_area;
    char strbuf[64];

    my_area = New (my_area_t);
    my_area->name = strdup (name);
    my_area->area = area;
    area->my_area = my_area;
    if (area->level < HQLIP_AREA_LEVEL_INTERNET) {
        my_area->parent = hqlip->root;
        LL_Add2 (hqlip->root->ll_children, my_area);
    }
    my_area->ll_neighbors = LL_Create (0);
    my_area->ll_children = LL_Create (0);
    my_area->ll_prefixes = LL_Create (0);
    my_area->ll_spath_link_qoses = LL_Create (0);
    my_area->ll_spath_area_centers = LL_Create (0);
    my_area->ll_spath_area_addrs = LL_Create (0);
    my_area->ll_spath_area_qoses = LL_Create (0);
    my_area->trace = trace_copy (hqlip->trace);
    sprintf (strbuf, "area %s (%d:%s)", name, my_area->area->level, 
		prefix_toa (my_area->area->id));
    set_trace (my_area->trace, TRACE_PREPEND_STRING, strbuf, 0);
/*
    my_area->schedule = New_Schedule (name, hqlip->trace);
    mrt_thread_create2 (name, my_area->schedule, NULL, NULL);
*/

    return (my_area);
}


static int
config_ric_network_if_qos_prefix (uii_connection_t *uii, char *name,
			char *if_qos_name, char *link_qos_name,
			prefix_t *prefix)
{
    hqlip_t *hqlip = CONFIG_RICD->ricd->hqlip;
    hqlip_config_network_t *network;
    config_if_qos_t *config_if_qos = NULL;
    config_link_qos_t *config_link_qos = NULL;
    area_t *area0, *area1;
    my_area_t *my_area0, *my_area1;

    LL_Iterate (hqlip->ll_networks, network) {
        if (strcasecmp (name, network->interface->name) == 0) { 
	    break;
        }
    }

    if (uii->negative) {
	if (network != NULL) {
	    if (!BIT_TEST (network->flags, HQLIP_NETWORK_DELETED)) {
    	        ricd_activate_interface (CONFIG_RICD->ricd, network, 0, NULL);
	        network->flags |= HQLIP_NETWORK_DELETED;
		network->config_area0->flags |= HQLIP_AREA_DELETED;
		network->config_area1->flags |= HQLIP_AREA_DELETED;
		if (network->config_area0->my_area->center)
		    hqlip_register_area_center (hqlip, 
			    network->config_area0->my_area, -1);
		if (network->config_area1->my_area->center)
		    hqlip_register_area_center (hqlip, 
			    network->config_area1->my_area, -1);
		if (hqlip_register_area_addr (uii, 
			network->config_area0->my_area, 0) < 0) {
		    Delete (name);
		    return (-1);
		}
		if (hqlip_register_area_addr (uii, 
			network->config_area1->my_area, 0) < 0) {
		    Delete (name);
		    return (-1);
		}
	    }
	}
        Delete (name);
	return (0);
    }

    if (if_qos_name) {
        LL_Iterate (CONFIG_RICD->ll_config_if_qoses, config_if_qos) {
	    if (strcasecmp (config_if_qos->name, if_qos_name) == 0)
	        break;
        }
        if (config_if_qos == NULL) {
	    config_notice (TR_ERROR, uii, "no such if-qos: %s\n", if_qos_name);
	    Delete (name);
	    Delete (if_qos_name);
	    Delete (link_qos_name);
	    Deref_Prefix (prefix);
	    return (-1);
	}
    }
    
    LL_Iterate (CONFIG_RICD->ll_config_link_qoses, config_link_qos) {
	if (strcasecmp (config_link_qos->name, link_qos_name) == 0)
	    break;
    }
    if (config_link_qos == NULL) {
	config_notice (TR_ERROR, uii, "no such link-qos: %s\n", link_qos_name);
	Delete (name);
	Delete (if_qos_name);
	Delete (link_qos_name);
	Deref_Prefix (prefix);
	return (-1);
    }
    
    if (network == NULL) {
	interface_t *interface;
	config_area_t *config_area;
	prefix_t *prefix0, *prefix1;
	int plen = 32;
	spath_area_center_t *spath_area_center;

	interface = find_interface_byname (name);
	if (interface == NULL) {
	    config_notice (TR_ERROR, uii, "no such interface: %s\n", name);
	    Delete (name);
	    Delete (if_qos_name);
	    Delete (link_qos_name);
	    Deref_Prefix (prefix);
	    return (-1);
	}
        if (!BIT_TEST (interface->flags, IFF_MULTICAST)) {
	    config_notice (TR_ERROR, uii, "no multicast available on %s\n", 
			   name);
	    Delete (name);
	    Delete (if_qos_name);
	    Delete (link_qos_name);
	    Deref_Prefix (prefix);
	    return (-1);
	}
	if (prefix && !is_prefix_local_on (prefix, interface)) {
	    config_notice (TR_ERROR, uii, "no such prefix on %s\n", 
			   name);
	    Delete (name);
	    Delete (if_qos_name);
	    Delete (link_qos_name);
	    Deref_Prefix (prefix);
	    return (-1);
	}

	network = New (hqlip_config_network_t);
	network->interface = interface;
	network->config_if_qos = config_if_qos;
	network->config_link_qos = config_link_qos;
	network->prefix = Ref_Prefix (prefix);
	network->keep_alive_interval = -1;
	network->metric = -1;
	LL_Add2 (hqlip->ll_networks, network);

 	if (prefix == NULL) {
#ifdef HAVE_IPV6
	    if (hqlip->family == AF_INET6)
	        prefix = Ref_Prefix (interface->primary6->prefix);
	    else
#endif /* HAVE_IPV6 */
	    prefix = Ref_Prefix (interface->primary->prefix);
	}
#ifdef HAVE_IPV6
	if (prefix->family == AF_INET6)
	    plen = 128;
#endif /* HAVE_IPV6 */
	prefix0 = New_Prefix (prefix->family, prefix_tochar (prefix), plen);
	netmasking (prefix0->family, prefix_tochar (prefix0), prefix0->bitlen);
	area0 = add_area (0, prefix0);
	my_area0 = my_area_new (hqlip, name, area0);
	LL_Add2 (my_area0->ll_prefixes, prefix0);

	/* register myself as a center */
        spath_area_center = New (spath_area_center_t);
        spath_area_center->area = area0;
        spath_area_center->pri = 0;
        /* spath_area_center->router_id = hqlip->router_id; */
        time (&spath_area_center->tstamp);
        LL_Add2 (my_area0->ll_spath_area_centers, spath_area_center);
	my_area0->winner = spath_area_center;

	config_area = New (config_area_t);
	config_area->name = strdup (name);
	config_area->my_area = my_area0;
        LL_Add2 (hqlip->ll_areas, config_area);
	network->config_area0 = config_area;

	prefix1 = New_Prefix (prefix->family, prefix_tochar (prefix), 
			      prefix->bitlen);
	netmasking (prefix1->family, prefix_tochar (prefix1), prefix1->bitlen);
	area1 = add_area (1, prefix1);
	my_area1 = my_area_new (hqlip, name, area1);
	my_area0->parent = my_area1;
	LL_Add2 (my_area1->ll_children, my_area0);
	LL_Remove (hqlip->root->ll_children, my_area0);
	LL_Add2 (my_area1->ll_prefixes, prefix1);
	config_area = New (config_area_t);
	config_area->name = strdup (name);
	config_area->my_area = my_area1;
        LL_Add2 (hqlip->ll_areas, config_area);
	network->config_area1 = config_area;

    	ricd_activate_interface (CONFIG_RICD->ricd, network, 1, my_area0);
	/* here to avoid reconnection */
	if (hqlip_register_area_addr (uii, my_area0, 1) < 0) {
    	    Delete (name);
    	    Delete (if_qos_name);
    	    Delete (link_qos_name);
    	    Deref_Prefix (prefix);
	    return (-1);
	}
	if (hqlip_register_area_addr (uii, my_area1, 1) < 0) {
    	    Delete (name);
    	    Delete (if_qos_name);
    	    Delete (link_qos_name);
    	    Deref_Prefix (prefix);
	    return (-1);
	}
/*
	hqlip_register_area_center (hqlip, my_area0, 0);
	hqlip_register_area_center (hqlip, my_area1, 0);
*/
    }
    else {
	if (BIT_TEST (network->flags, HQLIP_NETWORK_DELETED)) {
	    network->config_area0->flags &= ~HQLIP_AREA_DELETED;
	    network->config_area1->flags &= ~HQLIP_AREA_DELETED;
	    network->flags &= ~HQLIP_NETWORK_DELETED;
	if (hqlip_register_area_addr (uii, network->config_area0->my_area, 
				      1) < 0)
	    return (-1);
	if (hqlip_register_area_addr (uii, network->config_area1->my_area, 
				      1) < 0) {
    	    Delete (name);
    	    Delete (if_qos_name);
    	    Delete (link_qos_name);
    	    Deref_Prefix (prefix);
	    return (-1);
	}
/*
	    hqlip_register_area_center (hqlip, 
		network->config_area0->my_area, 0);
	    hqlip_register_area_center (hqlip, 
		network->config_area0->my_area, 0);
*/
	}
	network->config_if_qos = config_if_qos;
	network->config_link_qos = config_link_qos;
    	ricd_activate_interface (CONFIG_RICD->ricd, network, 
					2, NULL);
    }
    Delete (name);
    Delete (if_qos_name);
    Delete (link_qos_name);
    Deref_Prefix (prefix);
    return (1);
}


static int
config_ric_network_qos_prefix (uii_connection_t *uii, char *name,
			       char *link_qos_name, prefix_t *prefix)
{
    return (config_ric_network_if_qos_prefix (uii, name, NULL, link_qos_name,
					      prefix));
}


static int
config_ric_network_if_qos (uii_connection_t *uii, char *name,
			   char *if_qos_name, char *link_qos_name)
{
    return (config_ric_network_if_qos_prefix (uii, name, if_qos_name, 
					      link_qos_name, NULL));
}


static int
config_ric_network_qos (uii_connection_t *uii, char *name,
			char *link_qos_name)
{
    return (config_ric_network_if_qos_prefix (uii, name, NULL, 
					      link_qos_name, NULL));
}


static int
config_ric_network_keep_alive (uii_connection_t *uii, char *name,
			       int keep_alive)
{
    hqlip_t *hqlip = CONFIG_RICD->ricd->hqlip;
    hqlip_config_network_t *network;
    int changed = 0;

    LL_Iterate (hqlip->ll_networks, network) {
        if (strcasecmp (name, network->interface->name) == 0) { 
	    break;
        }
    }
    if (network == NULL || BIT_TEST (network->flags, HQLIP_NETWORK_DELETED)) {
	config_notice (TR_ERROR, uii, "no such network: %s\n", name);
	Delete (name);
	return (-1);
    }

    if (uii->negative) {
	if (network == NULL || 
		BIT_TEST (network->flags, HQLIP_NETWORK_DELETED) ||
		network->keep_alive_interval < 0) {
	    Delete (name);
	    return (0);
	}
	network->keep_alive_interval = -1;
	changed++;
    }
    else {
        if (network == NULL || 
		BIT_TEST (network->flags, HQLIP_NETWORK_DELETED)) {
	    config_notice (TR_ERROR, uii, "no such network: %s\n", name);
	    Delete (name);
	    return (-1);
        }
	if (network->keep_alive_interval != keep_alive) {
	    network->keep_alive_interval = keep_alive;
	    changed++;
	}
    }
    if (changed)
    	ricd_activate_interface (CONFIG_RICD->ricd, network, 2, NULL);
    Delete (name);
    return (1);
}


static int
config_ric_network_metric (uii_connection_t *uii, char *name,
			   int metric)
{
    hqlip_t *hqlip = CONFIG_RICD->ricd->hqlip;
    hqlip_config_network_t *network;
    int changed = 0;

    LL_Iterate (hqlip->ll_networks, network) {
        if (strcasecmp (name, network->interface->name) == 0) { 
	    break;
        }
    }

    if (uii->negative) {
	if (network == NULL || 
		BIT_TEST (network->flags, HQLIP_NETWORK_DELETED) || 
		network->metric < 0) {
	    Delete (name);
	    return (0);
	}
	network->metric = -1;
	changed++;
    }
    else {
        if (network == NULL || 
		BIT_TEST (network->flags, HQLIP_NETWORK_DELETED)) {
	    config_notice (TR_ERROR, uii, "no such network: %s\n", name);
	    Delete (name);
	    return (-1);
        }
	if (network->metric != metric) {
	    network->metric = metric;
	    changed++;
	}
    }
    if (changed)
    	ricd_activate_interface (CONFIG_RICD->ricd, network, 2, NULL);
    Delete (name);
    return (1);
}


static int
hqlip_register_area_center (hqlip_t *hqlip, my_area_t *my_area, int pri)
{
    spath_area_center_t *spath_area_center;

    /* register/unregister myself upto/from the parent */
    if (pri < 0) {
	LL_Iterate (my_area->parent->ll_spath_area_centers,
		    spath_area_center) {
	    /* learned from the neighbor may be deleted */
	    if (BIT_TEST (spath_area_center->flags, AREA_CENTER_DELETED))
		continue;
	    if (spath_area_center->area == my_area->area &&
	        spath_area_center->neighbor == NULL /* &&
	        spath_area_center->router_id == hqlip->router_id*/) {
	        BIT_SET (spath_area_center->flags, AREA_CENTER_DELETED);
		/* XXX it's gone. need to flood the info, though */
	    	trace (TR_TRACE, my_area->trace, 
			"area %s withdrawn from center candidate in %s\n",
			my_area->name, my_area->parent->name);
			/* XXX check the current center */
		hqlip_update_area_center (hqlip, my_area->parent, 
					  spath_area_center);
		break;
	    }
	}
    }
    else {
	if (my_area->center == NULL) {
            spath_area_center = New (spath_area_center_t);
            spath_area_center->area = my_area->area;
            spath_area_center->pri = pri;
            /* spath_area_center->router_id = hqlip->router_id; */
            time (&spath_area_center->tstamp);
	    BIT_SET (spath_area_center->flags, AREA_CENTER_CHANGED);
	    my_area->center = spath_area_center;
	    /* XXX assuming configration doesn't change while running */
	}
	else if (my_area->center->pri != pri) {
	    my_area->center->pri = pri;
	    BIT_SET (my_area->center->flags, AREA_CENTER_CHANGED);
	}
	assert (my_area->parent);
        LL_Iterate (my_area->parent->ll_spath_area_centers, spath_area_center) {
	    if (BIT_TEST (spath_area_center->flags, AREA_CENTER_DELETED))
		continue;
	    if (spath_area_center->pri == my_area->center->pri &&
	        spath_area_center->neighbor == NULL /*&&
	        spath_area_center->router_id == my_area->center->router_id*/) {
		trace (TR_WARN, my_area->trace, 
			"area %s can't regiester as a center in %s "
			"due to the same priority %d with area %d:%a\n",
			my_area->name, my_area->parent->name,
			spath_area_center->pri,
			spath_area_center->area->level,
			spath_area_center->area->id);
		return (-1);
	    }
	}
        LL_Iterate (my_area->parent->ll_spath_area_centers, spath_area_center) {
	    if (spath_area_center->area == my_area->center->area &&
	            spath_area_center->neighbor == NULL)
		break;
	}
	if (spath_area_center) {
	    BIT_RESET (spath_area_center->flags, AREA_CENTER_DELETED);
	    if (my_area->center->pri != spath_area_center->pri) {
	        spath_area_center->pri = my_area->center->pri;
	        BIT_SET (spath_area_center->flags, AREA_CENTER_CHANGED);
	        hqlip_update_area_center (hqlip, my_area->parent, 
					  spath_area_center);
	    }
	}
	else {
            spath_area_center = New (spath_area_center_t);
            spath_area_center->area = my_area->center->area;
            spath_area_center->pri = my_area->center->pri;
            /* spath_area_center->router_id = hqlip->router_id; */
            spath_area_center->tstamp = my_area->center->tstamp;
	    BIT_SET (spath_area_center->flags, AREA_CENTER_CHANGED);
            LL_Add2 (my_area->parent->ll_spath_area_centers, 
		     spath_area_center);
	    trace (TR_TRACE, my_area->trace, 
		    "area %s registered to center candidate in %s\n",
		    my_area->name, my_area->parent->name);
	    hqlip_update_area_center (hqlip, my_area->parent, 
				      spath_area_center);
	}
    }
    return (1);
}


static int
config_ric_network_center (uii_connection_t *uii, char *name,
			   int pri, int pps, int level)
{
    hqlip_t *hqlip = CONFIG_RICD->ricd->hqlip;
    hqlip_config_network_t *network;
    config_area_t *config_area = NULL;

    if (level < 0 || level > 1) {
	config_notice (TR_ERROR, uii, "level must be 0 or 1\n");
	Delete (name);
	return (-1);
    }

    LL_Iterate (hqlip->ll_networks, network) {
        if (strcasecmp (name, network->interface->name) == 0) { 
	    break;
        }
    }

    if (network) {
	if (level == 0)
	    config_area = network->config_area0;
        else
	    config_area = network->config_area1;
    }

    if (uii->negative) {
	if (network == NULL || 
		BIT_TEST (network->flags, HQLIP_NETWORK_DELETED) || 
		config_area->my_area->center == NULL) {
	    Delete (name);
	    return (0);
	}
	if (hqlip_register_area_center (hqlip, config_area->my_area, -1) < 0) {
	    Delete (name);
	    return (-1);
	 }
	Delete (config_area->my_area->center);
	config_area->my_area->center = NULL;
    }
    else {
        if (network == NULL || 
		BIT_TEST (network->flags, HQLIP_NETWORK_DELETED)) {
	    config_notice (TR_ERROR, uii, "no such network: %s\n", name);
	    Delete (name);
	    return (-1);
        }
	config_area->my_area->pps = pps;
	if (config_area->my_area->center != NULL) {
	    Delete (config_area->my_area->center);
	    config_area->my_area->center = NULL;
	}
	if (hqlip_register_area_center (hqlip, config_area->my_area, pri) < 0) {
	    Delete (name);
	    return (-1);
	}
    }
    Delete (name);
    return (1);
}


static void
get_config_router_ric (ricd_t *ricd)
{
    hqlip_config_network_t *network;
    config_area_t *config_area;
    config_aggregate_t *aggregate;

#ifdef HAVE_IPV6
    if (ricd->family == AF_INET6) {
        config_add_output ("router ric ipv6\n");
    }
    else
#endif /* HAVE_IPV6 */
    config_add_output ("router ric\n");

    LL_Iterate (ricd->hqlip->ll_networks, network) {
	char strbuf[MAXLINE] = "";
	if (network->config_if_qos || network->config_link_qos)
	    sprintf (strbuf + strlen (strbuf), " %s", "qos");
	if (network->config_if_qos)
	    sprintf (strbuf + strlen (strbuf), " %s", 
			network->config_if_qos->name);
	if (network->config_link_qos)
	    sprintf (strbuf + strlen (strbuf), " %s", 
			network->config_link_qos->name);
	if (network->prefix)
            config_add_output ("  network %s%s prefix %p\n", 
			   network->interface->name, strbuf, network->prefix);
	else
            config_add_output ("  network %s%s\n", 
			   network->interface->name, strbuf);
	if (network->keep_alive_interval >= 0)
            config_add_output ("  network %s keep-alive %d\n", 
			        network->interface->name,
				network->keep_alive_interval);
	if (network->metric >= 0)
            config_add_output ("  network %s metric %d\n", 
			       network->interface->name,
			       network->metric);
	if (network->config_area0->my_area->center)
            config_add_output ("  network %s center %d %d level 0\n", 
			       network->interface->name,
			       network->config_area0->my_area->center->pri, 
			       network->config_area0->my_area->pps);
	if (network->config_area1->my_area->center)
            config_add_output ("  network %s center %d %d level 1\n",
			       network->interface->name,
			       network->config_area1->my_area->center->pri, 
			       network->config_area1->my_area->pps);
    }

    LL_Iterate (ricd->hqlip->ll_areas, config_area) {
	if (config_area->my_area->area->level <= 1)
	    continue;
	if (config_area->my_area->area->level >= HQLIP_AREA_LEVEL_INTERNET)
	    continue;
	if (config_area->my_area->area->id)
            config_add_output ("  area %s level %d id %a\n", 
			   config_area->name, 
			   config_area->my_area->area->level,
			   config_area->my_area->area->id);
	else
            config_add_output ("  area %s level %d\n", 
			   config_area->name, 
			   config_area->my_area->area->level);
	if (config_area->ll_aggregates) {
	    LL_Iterate (config_area->ll_aggregates, aggregate) {
	        if (aggregate->name)
            	    config_add_output ("  area %s aggregate %s masklen %d\n", 
			   config_area->name, aggregate->name,
			   aggregate->prefix->bitlen);
		else
            	    config_add_output ("  area %s aggregate %p\n", 
			   config_area->name, aggregate->prefix);
	    }
	}
	if (config_area->my_area->center) {
            config_add_output ("  area %s center %d %d\n", 
				config_area->name,
				config_area->my_area->center->pri, 
				config_area->my_area->pps);
	}
    }
}


static int
config_router_ric (uii_connection_t *uii, char *ipv6)
{
    ricd_t *ricd = RICD;

#ifndef HAVE_IPV6
    if (ipv6) {
	config_notice (TR_ERROR, uii, "NO IPV6 support available\n");
	Delete (ipv6);
	return (-1);
    }
#endif /* HAVE_IPV6 */
    if (ipv6) {
	ricd = RICD6;
	Delete (ipv6);
    }
    if (uii->negative) {
        hqlip_config_network_t *network;

        if (ricd == NULL || !ricd->running)
            return (0);    
        ricd->running = 0;
        config_del_module (CF_DELIM, "router ric", get_config_router_ric,
                           ricd);

	LL_Iterate (ricd->hqlip->ll_networks, network) {
    	    ricd_activate_interface (CONFIG_RICD->ricd, network, 0, NULL);
	    LL_Remove (ricd->hqlip->ll_networks, network);
	    Delete (network);
	}

	CONFIG_RICD->ricd = NULL;
        return (1);
    }
    ricd->running++;
    uii->previous[++uii->prev_level] = uii->state;
    uii->state = UII_CONFIG_ROUTER_RIC;
    config_add_module (CF_DELIM, "router ric", get_config_router_ric, ricd);
    CONFIG_RICD->ricd = ricd;
    return (1);
}


static void
get_config_if_qos (config_if_qos_t *config_if_qos)
{
    config_add_output ("if-qos %s %d %d %d %d\n", config_if_qos->name,
		        config_if_qos->if_qos->pps,
    			config_if_qos->if_qos->qos_pps,
    			config_if_qos->if_qos->ann_pps,
    			config_if_qos->if_qos->dly);
}


static int
config_x_if_qos (uii_connection_t *uii, char *name, int pps, int qos_pps,
	      int ann_pps, int dly)
{
    config_if_qos_t *config_if_qos;

    LL_Iterate (CONFIG_RICD->ll_config_if_qoses, config_if_qos) {
	if (strcasecmp (config_if_qos->name, name) == 0)
	    break;
    }
    if (uii->negative) {
	Delete (name);
	if (config_if_qos == NULL)
	    return (0);
	config_if_qos->flags |= CONFIG_QOS_DELETED;
        config_del_module (0, "if-qos", get_config_if_qos, config_if_qos);
	return (1);
    }

    if (config_if_qos == NULL) {
	config_if_qos = New (config_if_qos_t);
	config_if_qos->name = strdup (name);
	config_if_qos->flags = 0;
	config_if_qos->if_qos = New (if_qos_t);
	LL_Add2 (CONFIG_RICD->ll_config_if_qoses, config_if_qos);
    }
    config_if_qos->if_qos->pps = pps;
    config_if_qos->if_qos->qos_pps = qos_pps;
    config_if_qos->if_qos->ann_pps = ann_pps;
    config_if_qos->if_qos->dly = dly;
    config_if_qos->flags &= ~CONFIG_QOS_DELETED;
    config_add_module (0, "if-qos", get_config_if_qos, config_if_qos);
    Delete (name);
    return (1);
}


static void
get_config_link_qos (config_link_qos_t *config_link_qos)
{
    if (config_link_qos->link_qos->flag)
        config_add_output ("link-qos %s %u %u %u %u\n", 
			config_link_qos->name,
		        config_link_qos->link_qos->pri,
    			config_link_qos->link_qos->pps,
    			config_link_qos->link_qos->dly,
    			config_link_qos->link_qos->loh);
    else
        config_add_output ("link-qos %s %u %u %u\n", 
			config_link_qos->name,
		        config_link_qos->link_qos->pri,
    			config_link_qos->link_qos->pps,
    			config_link_qos->link_qos->dly);
}


static int
config_x_link_qos (uii_connection_t *uii, char *name, int pri, int pps, 
		 int dly, int loh)
{
    config_link_qos_t *config_link_qos;

    LL_Iterate (CONFIG_RICD->ll_config_link_qoses, config_link_qos) {
	if (strcasecmp (config_link_qos->name, name) == 0)
	    break;
    }
    if (uii->negative) {
	Delete (name);
	if (config_link_qos == NULL)
	    return (0);
	config_link_qos->flags |= CONFIG_QOS_DELETED;
        config_del_module (0, "link-qos", get_config_link_qos, config_link_qos);
	return (1);
    }

    if (config_link_qos == NULL) {
	config_link_qos = New (config_link_qos_t);
	config_link_qos->name = strdup (name);
	config_link_qos->flags = 0;
	config_link_qos->link_qos = New (link_qos_t);
	LL_Add2 (CONFIG_RICD->ll_config_link_qoses, config_link_qos);
    }
    config_link_qos->link_qos->pri = pri;
    config_link_qos->link_qos->pps = pps;
    config_link_qos->link_qos->dly = dly;
    config_link_qos->link_qos->loh = (loh >= 0)? loh: 0;
    config_link_qos->link_qos->flag = (loh >= 0);
    config_link_qos->flags &= ~CONFIG_QOS_DELETED;
    config_add_module (0, "link-qos", get_config_link_qos, config_link_qos);
    Delete (name);
    return (1);
}


static void
get_config_area_qos (config_area_qos_t *config_area_qos)
{
    config_add_output ("area-qos %s %u %u %u %u\n", 
			config_area_qos->name,
		        config_area_qos->area_qos->pri,
    			config_area_qos->area_qos->ctu,
    			config_area_qos->area_qos->bfee,
    			config_area_qos->area_qos->pfee);
}


static int
config_x_area_qos (uii_connection_t *uii, char *name, 
		 int pri, int ctu, int bfee, int pfee)
{
    config_area_qos_t *config_area_qos;

    LL_Iterate (CONFIG_RICD->ll_config_area_qoses, config_area_qos) {
	if (strcasecmp (config_area_qos->name, name) == 0)
	    break;
    }
    if (uii->negative) {
	Delete (name);
	if (config_area_qos == NULL)
	    return (0);
	config_area_qos->flags |= CONFIG_QOS_DELETED;
        config_del_module (0, "area-qos", get_config_area_qos, config_area_qos);
	return (1);
    }

    if (config_area_qos == NULL) {
	config_area_qos = New (config_area_qos_t);
	config_area_qos->name = strdup (name);
	config_area_qos->flags = 0;
	config_area_qos->area_qos = New (area_qos_t);
	LL_Add2 (CONFIG_RICD->ll_config_area_qoses, config_area_qos);
    }
    config_area_qos->area_qos->pri = pri;
    config_area_qos->area_qos->ctu = ctu;
    config_area_qos->area_qos->bfee = bfee;
    config_area_qos->area_qos->pfee = pfee;
    config_area_qos->flags &= ~CONFIG_QOS_DELETED;
    config_add_module (0, "area-qos", get_config_area_qos, config_area_qos);
    Delete (name);
    return (1);
}


static void
get_config_req_qos (config_req_qos_t *config_req_qos)
{
    config_add_output ("req-qos %s %u %u %u %u %u %u %u %u\n", 
			config_req_qos->name,
		        config_req_qos->req_qos->pri,
		        config_req_qos->req_qos->mtu,
    			config_req_qos->req_qos->pps,
    			config_req_qos->req_qos->sec,
    			config_req_qos->req_qos->cd,
    			config_req_qos->req_qos->cf,
    			config_req_qos->req_qos->rdly,
    			config_req_qos->req_qos->rfee);
}


static int
config_x_req_qos (uii_connection_t *uii, char *name, int pri,
		int mtu, int pps, int sec, int cd, int cf, int rdly, int rfee)
{
    config_req_qos_t *config_req_qos;

    LL_Iterate (CONFIG_RICD->ll_config_req_qoses, config_req_qos) {
	if (strcasecmp (config_req_qos->name, name) == 0)
	    break;
    }
    if (uii->negative) {
	Delete (name);
	if (config_req_qos == NULL)
	    return (0);
	config_req_qos->flags |= CONFIG_QOS_DELETED;
        config_del_module (0, "req-qos", get_config_req_qos, config_req_qos);
	return (1);
    }

    if (config_req_qos == NULL) {
	config_req_qos = New (config_req_qos_t);
	config_req_qos->name = strdup (name);
	config_req_qos->flags = 0;
	config_req_qos->req_qos = New (req_qos_t);
	LL_Add2 (CONFIG_RICD->ll_config_req_qoses, config_req_qos);
    }
    config_req_qos->req_qos->pri = pri;
    config_req_qos->req_qos->mtu = mtu;
    config_req_qos->req_qos->pps = pps;
    config_req_qos->req_qos->sec = sec;
    config_req_qos->req_qos->cd = cd;
    config_req_qos->req_qos->cf = cf;
    config_req_qos->req_qos->rdly = rdly;
    config_req_qos->req_qos->rfee = rfee;
    config_req_qos->flags &= ~CONFIG_QOS_DELETED;
    config_add_module (0, "req-qos", get_config_req_qos, config_req_qos);
    Delete (name);
    return (1);
}


static int
config_ric_area_level_id (uii_connection_t *uii, char *name, int level,
			  prefix_t *id)
{
    hqlip_t *hqlip = CONFIG_RICD->ricd->hqlip;
    config_area_t *config_area;

    if (level < 2 || level > HQLIP_AREA_LEVEL_INTERNET) {
	config_notice (TR_ERROR, uii, "area must be >=2 && < %d\n",
			HQLIP_AREA_LEVEL_INTERNET);
	Delete (name);
	Deref_Prefix (id);
	return (-1);
    }

    LL_Iterate (hqlip->ll_areas, config_area) {
        if (strcasecmp (name, config_area->name) == 0) { 
	    break;
        }
    }

    if (uii->negative) {
	if (config_area != NULL) {
	    if (!BIT_TEST (config_area->flags, HQLIP_AREA_DELETED)) {
	        config_area->flags |= HQLIP_AREA_DELETED;
	    }
	}
	Delete (name);
	return (0);
    }

    if (config_area == NULL) {
	config_area = New (config_area_t);
	config_area->name = strdup (name);
	config_area->flags = 0;
	config_area->my_area = my_area_new (hqlip, name, add_area (level, id));
        LL_Add2 (hqlip->ll_areas, config_area);
    }
    else {
	config_area->flags &= ~HQLIP_AREA_DELETED;
	config_area->my_area->flags &= ~HQLIP_MY_AREA_DELETED;
    }
    if (config_area->my_area->area->level != level ||
	!address_equal (config_area->my_area->area->id, id)) {
	config_area->my_area->area = add_area (level, id);
	/* I don't know if it works */
    }

    Deref_Prefix (id);
    Delete (name);
    return (1);
}

static int
config_ric_area_level (uii_connection_t *uii, char *name, int level)
{
    return (config_ric_area_level_id (uii, name, level, NULL));
}


static LINKED_LIST *
find_sub_areas (hqlip_t *hqlip, my_area_t *my_area, prefix_t *prefix, 
		LINKED_LIST *ll)
{
    config_area_t *config_area;
    prefix_t *b;

    LL_Iterate (hqlip->ll_areas, config_area) {
	if (config_area->my_area->area->level == my_area->area->level)
	    continue;
	if (config_area->my_area->area->level >= my_area->area->level)
	    continue;

	/* XXX stack up the lowest to top only */
	if (config_area->my_area->parent != my_area 
		&& config_area->my_area->parent != hqlip->root)
	    continue;

	if (config_area->my_area->ll_prefixes == NULL ||
		LL_GetCount (config_area->my_area->ll_prefixes) <= 0)
	    continue;
	LL_Iterate (config_area->my_area->ll_prefixes, b) {
	    if (a_include_b (prefix, b)) {
		if (ll == NULL) {
		    LL_Add2 (ll, config_area->my_area);
		    trace (TR_TRACE, hqlip->trace, "sub-area found: %s\n",
			   config_area->my_area->name);
		}
		else {
		    /* to avoid duplicates */
		    my_area_t *a;
		    LL_Iterate (ll, a) {
			if (a == config_area->my_area)
			    break;
		    }
		    if (a == NULL) {
			LL_Add2 (ll, config_area->my_area);
		        trace (TR_TRACE, hqlip->trace, "sub-area found: %s\n",
			       config_area->my_area->name);
		    }
		}
	    }
	}
    }
    return (ll);
}



static int
hqlip_config_re_eval_areas (uii_connection_t *uii, config_area_t *config_area)
{
    prefix_t *prefix;
    LINKED_LIST *ll = LL_Create (0);
    my_area_t *my_area, *child;
    hqlip_t *hqlip = CONFIG_RICD->ricd->hqlip;
    config_aggregate_t *aggregate;

    LL_ClearFn (config_area->my_area->ll_prefixes, 
		(LL_DestroyProc) Deref_Prefix);
    LL_Iterate (config_area->ll_aggregates, aggregate) {
	/* since aggregate may have duplicated prefixes */
	LL_Iterate (config_area->my_area->ll_prefixes, prefix) {
	    if (prefix_equal (aggregate->prefix, prefix))
		break;
	}
	if (prefix == NULL)
            LL_Add2 (config_area->my_area->ll_prefixes, 
		     Ref_Prefix (aggregate->prefix));
    }
    assert (config_area->my_area->ll_prefixes);

    LL_Iterate (config_area->my_area->ll_prefixes, prefix) {
	find_sub_areas (hqlip, config_area->my_area, prefix, ll);
    }
    /* check for delete */
    LL_Iterate (config_area->my_area->ll_children, child) {
        LL_Iterate (ll, my_area) {
	    if (my_area == child)
		break;
	}
	if (my_area == NULL) {
	    my_area_t *prev;
	    config_notice (TR_INFO, uii, "area %s parent changed %s -> %s\n",
			child->name, config_area->my_area->name, 
			hqlip->root->name);
	    assert (child->parent == config_area->my_area);

	    if (child->center)
	        hqlip_register_area_center (hqlip, child, -1);
            if (hqlip_register_area_addr (uii, child, 0) < 0) {
    		LL_Destroy (ll);
		return (-1);
	    }

	    prev = LL_GetPrev (config_area->my_area->ll_children, child);
	    LL_Remove (child->parent->ll_children, child);
	    child->parent = hqlip->root;
	    LL_Add2 (hqlip->root->ll_children, child);
	    /* XXX should search for a new parent */
	    child = prev;
	}
    }

    /* check for add */
    LL_Iterate (ll, my_area) {
	LL_Iterate (config_area->my_area->ll_children, child) {
	    if (child == my_area)
		break;
	}
	if (child == NULL) {
	    assert (my_area->parent != config_area->my_area);
	    config_notice (TR_INFO, uii, "area %s parent changed %s -> %s\n",
			    my_area->name, my_area->parent->name,
			    config_area->my_area->name);
	    if (my_area->center)
	        hqlip_register_area_center (hqlip, my_area, -1);
            if (hqlip_register_area_addr (uii, my_area, 0) < 0) {
    		LL_Destroy (ll);
		return (-1);
	    }
	    LL_Remove (my_area->parent->ll_children, my_area);
	    my_area->parent = config_area->my_area;
	    LL_Add (my_area->parent->ll_children, my_area);
	    if (my_area->center)
	        hqlip_register_area_center (hqlip, my_area, 
			/* no change */ my_area->center->pri);
            if (hqlip_register_area_addr (uii, my_area, 1) < 0) {
    		LL_Destroy (ll);
		return (-1);
	    }
        }   
    }
    LL_Destroy (ll);
    return (1);
}


/* area address info into config_area's parent ll_spath_area_addrs */
static int
hqlip_register_area_addr (uii_connection_t *uii, my_area_t *my_area, int on)
{
    spath_area_addr_t *spath_area_addr;
    prefix_t *pp, *prefix;

    LL_Iterate (my_area->parent->ll_spath_area_addrs, spath_area_addr) {

	if (spath_area_addr->area != my_area->area)
	    continue;

	LL_Iterate (my_area->ll_prefixes, prefix) {
	    prefix_t *pp2;

            if (on) {
	        LL_Iterate (my_area->parent->ll_prefixes, pp2) {
		    if (a_include_b (pp2, prefix))
		        break;
	        }
	        if (pp2 == NULL) {
		    config_notice (TR_ERROR, uii, 
		        "%p in area %s can not be included in area %s\n", 
		        prefix, my_area->name, my_area->parent->name);
		    return (-1);
	        }
	    }

	    LL_Iterate (spath_area_addr->ll_prefixes, pp) {
		if (prefix_equal (pp, prefix)) {
		    break;
		}
	    }

            if (on) {
		if (!pp) {
		    LL_Add (spath_area_addr->ll_prefixes, Ref_Prefix (prefix));
		    spath_area_addr->flags |= AREA_ADDR_CHANGED;
	    	    trace (TR_TRACE, my_area->trace, 
			"inject area-addr %p in %s\n", prefix,
			my_area->parent->name);
		}
	    }
	    else {
		if (pp) {
		    LL_Remove (spath_area_addr->ll_prefixes, pp);
	    	    trace (TR_TRACE, my_area->trace, 
			"removed area-addr %p in %s\n", prefix,
			my_area->parent->name);
		    Deref_Prefix (pp);
		    spath_area_addr->flags |= AREA_ADDR_CHANGED;
		    if (LL_GetCount (spath_area_addr->ll_prefixes) <= 0) {
			spath_area_addr->flags |= AREA_ADDR_DELETED;
			LL_Destroy (spath_area_addr->ll_prefixes);
			spath_area_addr->ll_prefixes = NULL;
			/* we broke the linked list, so have to return */
			LL_Remove (my_area->parent->ll_spath_area_addrs,
				   spath_area_addr);
			Delete (spath_area_addr);
			return (1);
		    }
		}
		else {
	    	    trace (TR_TRACE, my_area->trace, 
			"no area-addr %p in %s\n", prefix,
			my_area->parent->name);
		    /* continue */
		}
	    }
	}
	break;
    }

    if (spath_area_addr == NULL) {
	if (on) {
	    spath_area_addr = New (spath_area_addr_t);
	    spath_area_addr->area = my_area->area;
	    spath_area_addr->ll_prefixes = LL_Create (0);
	    spath_area_addr->flags |= AREA_ADDR_CHANGED;
	    LL_Add (my_area->parent->ll_spath_area_addrs, spath_area_addr);
	    LL_Iterate (my_area->ll_prefixes, prefix) {
	        trace (TR_TRACE, my_area->trace, 
		       "inject area-addr %p in %s (1st time)\n", prefix,
		        my_area->parent->name);
	        LL_Add2 (spath_area_addr->ll_prefixes, Ref_Prefix (prefix));
	    }
	}
	else {
	   trace (TR_TRACE, my_area->trace, 
		  "no area %s in %s\n", my_area->name, my_area->parent->name);
	   return (0);
	}
    }
    return (1);
}


static int
config_ric_area_aggregate_prefix (uii_connection_t *uii, char *name, 
				  prefix_t *prefix)
{
    hqlip_t *hqlip = CONFIG_RICD->ricd->hqlip;
    config_area_t *config_area;
    config_aggregate_t *aggregate;

    LL_Iterate (hqlip->ll_areas, config_area) {
        if (strcasecmp (name, config_area->name) == 0) { 
	    break;
        }
    }

    if (uii->negative) {
	if (config_area != NULL || 
	    	!BIT_TEST (config_area->flags, HQLIP_AREA_DELETED)) {
	    if (config_area->ll_aggregates) {
		LL_Iterate (config_area->ll_aggregates, aggregate) {
		    if (aggregate->name)
			continue;
		    if (prefix_equal (aggregate->prefix, prefix))
			break;
		}
		if (aggregate) {
		    LL_Remove (config_area->ll_aggregates, aggregate);
		    assert (aggregate->name == NULL);
		    Deref_Prefix (aggregate->prefix);
		    Delete (aggregate);
		    hqlip_config_re_eval_areas (uii, config_area);
		    hqlip_register_area_addr (uii, config_area->my_area, 0);
		}
	    }
	}
	Delete (name);
	Deref_Prefix (prefix);
	return (0);
    }

    if (config_area == NULL) {
	config_notice (TR_ERROR, uii, "no such area: %s\n", name);
	Delete (name);
	Deref_Prefix (prefix);
	return (-1);
    }
    if (config_area->ll_aggregates == NULL)
	config_area->ll_aggregates = LL_Create (0);
    LL_Iterate (config_area->ll_aggregates, aggregate) {
	if (aggregate->name)
	    continue;
	if (prefix_equal (aggregate->prefix, prefix))
	    break;
    }
    if (aggregate == NULL) {
	aggregate = New (config_aggregate_t);
	aggregate->name = NULL;
	aggregate->prefix = Ref_Prefix (prefix);
	LL_Add2 (config_area->ll_aggregates, aggregate);
        hqlip_config_re_eval_areas (uii, config_area);
	hqlip_register_area_addr (uii, config_area->my_area, 1);
    }

    Delete (name);
    Deref_Prefix (prefix);
    return (1);
}


static int
config_ric_area_aggregate_len (uii_connection_t *uii, char *name, 
			       char *area_name, int masklen)
{
    hqlip_t *hqlip = CONFIG_RICD->ricd->hqlip;
    config_area_t *config_area;
    config_aggregate_t *aggregate;

    LL_Iterate (hqlip->ll_areas, config_area) {
        if (strcasecmp (name, config_area->name) == 0) { 
	    break;
        }
    }

    if (uii->negative) {
	if (config_area != NULL || 
	    	!BIT_TEST (config_area->flags, HQLIP_AREA_DELETED)) {
	    if (config_area->ll_aggregates) {
		LL_Iterate (config_area->ll_aggregates, aggregate) {
		    if (aggregate->name == NULL)
			continue;
		    if (strcasecmp (aggregate->name, area_name) == 0)
			break;
		}
		if (aggregate) {
		    LL_Remove (config_area->ll_aggregates, aggregate);
		    Delete (aggregate->name);
		    Deref_Prefix (aggregate->prefix);
		    Delete (aggregate);
    		    hqlip_config_re_eval_areas (uii, config_area);
		    hqlip_register_area_addr (uii, config_area->my_area, 0);
		}
	    }
	}
	Delete (name);
	Delete (area_name);
	return (0);
    }

    if (config_area == NULL) {
	config_notice (TR_ERROR, uii, "no such area: %s\n", name);
	Delete (name);
	Delete (area_name);
	return (-1);
    }
    if (config_area->ll_aggregates == NULL)
	config_area->ll_aggregates = LL_Create (0);
    LL_Iterate (config_area->ll_aggregates, aggregate) {
	if (aggregate->name == NULL)
	    continue;
	if (strcasecmp (aggregate->name, area_name) == 0)
	    break;
    }
    if (aggregate == NULL) {
	hqlip_config_network_t *network;
	prefix_t *prefix;
        LL_Iterate (hqlip->ll_networks, network) {
            if (strcasecmp (area_name, network->interface->name) == 0)
	        break;
        }
        if (network == NULL) {
	    config_notice (TR_ERROR, uii, "no such network: %s\n", area_name);
	    Delete (name);
	    Delete (area_name);
	    return (-1);
        }
	prefix = LL_GetHead (network->config_area1->my_area->ll_prefixes);
	assert (prefix);
	aggregate = New (config_aggregate_t);
	aggregate->name = strdup (area_name);
	prefix = New_Prefix (prefix->family, prefix_tochar (prefix), masklen);
	if (prefix->bitlen < masklen) {
	    config_notice (TR_WARN, uii, "masklen %d is longer than %p\n", 
			   masklen, prefix->bitlen);
	}
	netmasking (prefix->family, prefix_tochar (prefix), masklen);
	aggregate->prefix = Ref_Prefix (prefix);
	LL_Add2 (config_area->ll_aggregates, aggregate);
    	hqlip_config_re_eval_areas (uii, config_area);
	hqlip_register_area_addr (uii, config_area->my_area, 1);
    }

    Delete (name);
    Delete (area_name);
    return (1);
}


static int
config_ric_area_center (uii_connection_t *uii, char *name, int pri, int pps)
{
    hqlip_t *hqlip = CONFIG_RICD->ricd->hqlip;
    config_area_t *config_area;

    LL_Iterate (hqlip->ll_areas, config_area) {
        if (strcasecmp (name, config_area->name) == 0) { 
	    break;
        }
    }

    if (uii->negative) {
	if (config_area != NULL || 
	    	!BIT_TEST (config_area->flags, HQLIP_AREA_DELETED)) {
	    if (config_area->my_area->center) {
	        hqlip_register_area_center (hqlip, config_area->my_area, -1);
		Delete (config_area->my_area->center);
		config_area->my_area->center = NULL;
	    }
	}
	Delete (name);
	return (0);
    }

    if (config_area == NULL) {
	config_notice (TR_ERROR, uii, "no such area: %s\n", name);
	Delete (name);
	return (-1);
    }
    if (config_area->my_area->center) {
	Delete (config_area->my_area->center);
	config_area->my_area->center = NULL;
    }
    config_area->my_area->pps = pps;
    if (hqlip_register_area_center (hqlip, config_area->my_area, pri) < 0) {
        Delete (name);
	return (-1);
    }

    Delete (name);
    return (1);
}


static void
get_config_rvp (ricd_rvp_t *rvp)
{
    config_add_output ("rvp %p %a\n", rvp->prefix, rvp->address);
}


static int
config_rvp (uii_connection_t *uii, prefix_t *prefix, prefix_t *address)
{
    ricd_rvp_t *rvp;

    LL_Iterate (CONFIG_RICD->ll_rvps, rvp) {
	if (prefix_equal (rvp->prefix, prefix) &&
	    prefix_equal (rvp->address, address))
	    break;
    }
    if (uii->negative) {
	if (rvp) {
	    config_del_module (0, "rvp", get_config_rvp, rvp);
	    LL_Remove (CONFIG_RICD->ll_rvps, rvp);
	    Deref_Prefix (rvp->prefix);
	    Deref_Prefix (rvp->address);
	}
	Deref_Prefix (prefix);
	Deref_Prefix (address);
	return (0);
    }

    if (rvp == NULL) {
	rvp = New (ricd_rvp_t);
	LL_Add2 (CONFIG_RICD->ll_rvps, rvp);
        config_add_module (0, "rvp", get_config_rvp, rvp);
    }
    else {
	Deref_Prefix (rvp->prefix);
	Deref_Prefix (rvp->address);
    }
    rvp->prefix = prefix;
    rvp->address = address;
    return (1);
}


prefix_t *
ricd_get_rvp (prefix_t *prefix)
{
    ricd_rvp_t *rvp;

    /* XXX multiple rvps */
    LL_Iterate (CONFIG_RICD->ll_rvps, rvp) {
	if (a_include_b (rvp->prefix, prefix))
	    return (rvp->address);
    }
    return (NULL);
}


void
ricd_init_config (void)
{
    assert (CONFIG_RICD == NULL);
    CONFIG_RICD = New (config_ricd_t);
    CONFIG_RICD->ricd = NULL;
    CONFIG_RICD->ll_config_if_qoses = LL_Create (0);
    CONFIG_RICD->ll_config_link_qoses = LL_Create (0);
    CONFIG_RICD->ll_config_area_qoses = LL_Create (0);
    CONFIG_RICD->ll_config_req_qoses = LL_Create (0);
    CONFIG_RICD->ll_rvps = LL_Create (0);

    set_uii (UII, UII_PROMPT, UII_CONFIG_ROUTER_RIC, "Router RIC> ", 0);

    uii_add_command2 (UII_NORMAL, 0, 
    		     "show (ip|ipv6) srsvp neighbor [%s|null]", 
    		     srsvp_show_neighbors, "Shows SRSVP neighbors");

    uii_add_command2 (UII_NORMAL, 0, 
    		     "show (ip|ipv6) flows", 
    		     srsvp_show_flows, "Shows SRSVP flows");
    uii_add_command2 (UII_NORMAL, 0, 
    		     "flow (send|recv) %adest port %d (udp|tcp) %sreq-qos",
    		     srsvp_flow_request_by_user, 
		     "Requests creation of flow to SRSVP");
    uii_add_command2 (UII_NORMAL, 0, 
    		     "ip flow (send|recv) %Adest port %d (udp|tcp) %sreq-qos",
    		     srsvp_flow_request_by_user, 
		     "Requests creation of flow to SRSVP");
    uii_add_command2 (UII_NORMAL, 0, 
    		 "ipv6 flow (send|recv) %Mdest port %d (udp|tcp) %sreq-qos",
    		     srsvp_flow_request_by_user, 
		     "Requests creation of flow to SRSVP");
    uii_add_command2 (UII_NORMAL, 0, "clear (ip|ipv6) flow %d",
    		      srsvp_no_flow_request_by_user, 
		      "Destroys flow of SRSVP");

    uii_add_command2 (UII_NORMAL, 0, 
    		     "show (ip|ipv6) hqlip neighbor [%s|null]", 
    		     hqlip_show_neighbors, "Shows HQLIP neighbors");

    uii_add_command2 (UII_NORMAL, 0, "show (ip|ipv6) hqlip areas", 
    		     hqlip_show_areas, "Shows HQLIP areas");

    uii_add_command2 (UII_NORMAL, 0, "show (ip|ipv6) hqlip path %p %p", 
		      hqlip_show_path, "Calculates HQLIP path");

    uii_add_command2 (UII_CONFIG, 0, 
    		"if-qos %sname %dif-pps %dqos-pps %dann-pps %dqueue-delay",
                      config_x_if_qos, "Define interface qos");
    uii_add_command2 (UII_CONFIG, 0, "no if-qos %sname",
                      config_x_if_qos, "Undefine interface qos");
    uii_add_command2 (UII_CONFIG, 0, 
    		      "link-qos %sname %dpri %dpps %ddly [%dloh|nil]",
                      config_x_link_qos, "Define link qos");
    uii_add_command2 (UII_CONFIG, 0, "no link-qos %sname",
                      config_x_link_qos, "Undefine link qos");
    uii_add_command2 (UII_CONFIG, 0, 
    		      "area-qos %sname %dpri %dctu %dbfee %dpfee",
                      config_x_area_qos, "Define area qos");
    uii_add_command2 (UII_CONFIG, 0, "no area-qos %sname",
                      config_x_area_qos, "Undefine area qos");
    uii_add_command2 (UII_CONFIG, 0, 
    	"req-qos %sname %dpri %dmtu %dpps %dsec %dcd %dcf %drdly %drfee",
                      config_x_req_qos, "Define req qos");
    uii_add_command2 (UII_CONFIG, 0, "no req-qos %sname",
                      config_x_area_qos, "Undefine req qos");
    uii_add_command2 (UII_CONFIG, 0, "rvp %p %a",
                      config_rvp, "Define rvp");
    uii_add_command2 (UII_CONFIG, 0, "no rvp %p %a",
                      config_rvp, "Undefine rvp");

    uii_add_command2 (UII_CONFIG, 0, "router ric [ipv6|null]",
                      config_router_ric,
    		      "Enables RIC routing protocols (HQLIP/SRSVP)");
    uii_add_command2 (UII_CONFIG, 0, "no router ric [ipv6|null]",
                      config_router_ric,
    		      "Disables RIC routing protocols (HQLIP/SRSVP)");

    uii_add_command2 (UII_CONFIG_ROUTER_RIC, 0,
                      "network %nif-name qos %slink-qos",
                      config_ric_network_qos,
                      "Turns ON RIC routing on the interface with qos");
    uii_add_command2 (UII_CONFIG_ROUTER_RIC, 0,
                  "network %nif-name qos %slink-qos prefix %p",
                      config_ric_network_qos_prefix,
          "Turns ON RIC routing on the interface with qos with the prefix");
    uii_add_command2 (UII_CONFIG_ROUTER_RIC, 0,
                      "network %nif-name qos %sif-qos %slink-qos",
                      config_ric_network_if_qos,
                      "Turns ON RIC routing on the interface with qos");
    uii_add_command2 (UII_CONFIG_ROUTER_RIC, 0,
                  "network %nif-name qos %sif-qos %slink-qos prefix %p",
                      config_ric_network_if_qos_prefix,
          "Turns ON RIC routing on the interface with qos with the prefix");
    uii_add_command2 (UII_CONFIG_ROUTER_RIC, 0,
                      "no network %nif-name",
                      config_ric_network_qos,
                      "Turns OFF RIC routing on the interface");
    uii_add_command2 (UII_CONFIG_ROUTER_RIC, 0,
                      "network %nif-name keep-alive %d",
                      config_ric_network_keep_alive,
                      "Set keep-alive interval");
    uii_add_command2 (UII_CONFIG_ROUTER_RIC, 0,
                      "no network %nif-name keep-alive",
                      config_ric_network_keep_alive,
                      "Reset keep-alive interval");
    uii_add_command2 (UII_CONFIG_ROUTER_RIC, 0,
                      "network %nif-name metric %d",
                      config_ric_network_metric, "Set metric");
    uii_add_command2 (UII_CONFIG_ROUTER_RIC, 0,
                      "no network %nif-name metric",
                      config_ric_network_metric, "Reset metric");
    uii_add_command2 (UII_CONFIG_ROUTER_RIC, 0,
                      "network %n center %dpri %dpps level %d",
                      config_ric_network_center, "Define center");
    uii_add_command2 (UII_CONFIG_ROUTER_RIC, 0,
                      "no network %n center",
                      config_ric_network_center, "Undefine center");

#ifdef notdef
    uii_add_command2 (UII_CONFIG_ROUTER_RIC, 0,
                      "area %n level %d",
                      config_ric_area_level, "Define area");
#endif
    uii_add_command2 (UII_CONFIG_ROUTER_RIC, 0,
                      "area %n level %d id %M",
                      config_ric_area_level_id, "Define area with id");
    uii_add_command2 (UII_CONFIG_ROUTER_RIC, 0,
                      "no area %n level %d",
                      config_ric_area_level, "Undefine area");

    uii_add_command2 (UII_CONFIG_ROUTER_RIC, 0,
                      "area %n aggregate %m",
                      config_ric_area_aggregate_prefix, "Include sub-area");
    uii_add_command2 (UII_CONFIG_ROUTER_RIC, 0,
                      "no area %n aggregate %m",
                      config_ric_area_aggregate_prefix, "Exclude sub-area");
    uii_add_command2 (UII_CONFIG_ROUTER_RIC, 0,
                      "area %n aggregate %n masklen %d",
                       config_ric_area_aggregate_len, "Include sub-area");
    uii_add_command2 (UII_CONFIG_ROUTER_RIC, 0,
                      "no area %n aggregate %n",
                      config_ric_area_aggregate_len, "Exclude sub-area");
    uii_add_command2 (UII_CONFIG_ROUTER_RIC, 0,
                      "area %n center %dpri %dpps",
                      config_ric_area_center, "Define center");
    uii_add_command2 (UII_CONFIG_ROUTER_RIC, 0,
                      "no area %n center",
                      config_ric_area_center, "Undefine center");

{
    prefix_t *prefix;
    config_area_t *config_area;
    char *name = "Internet";
    my_area_t *my_area;

    prefix = ascii2prefix (AF_INET, "0.0.0.0/0");
    my_area = my_area_new (RICD->hqlip, name,
				add_area (HQLIP_AREA_LEVEL_INTERNET, prefix));
    LL_Add2 (my_area->ll_prefixes, Ref_Prefix (prefix));
    config_area = New (config_area_t);
    config_area->name = strdup (name);
    config_area->my_area = my_area;
    LL_Add2 (RICD->hqlip->ll_areas, config_area);
    RICD->hqlip->root = my_area;
    Deref_Prefix (prefix);

#ifdef HAVE_IPV6
    prefix = ascii2prefix (AF_INET6, "::/0");
    my_area = my_area_new (RICD6->hqlip, name,
				add_area (HQLIP_AREA_LEVEL_INTERNET, prefix));
    LL_Add2 (my_area->ll_prefixes, Ref_Prefix (prefix));
    config_area = New (config_area_t);
    config_area->name = strdup (name);
    config_area->my_area = my_area;
    LL_Add2 (RICD6->hqlip->ll_areas, config_area);
    RICD6->hqlip->root = my_area;
    Deref_Prefix (prefix);
#endif /* HAVE_IPV6 */
}
}

