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

#include "ricd.h"


static void hqlip_neighbor_down (hqlip_t *hqlip, hqlip_neighbor_t *neighbor);
static void hqlip_neighbor_start (hqlip_t *hqlip, hqlip_neighbor_t *neighbor);
static int hqlip_process_pdu (hqlip_t *hqlip, hqlip_neighbor_t *neighbor);
static spath_link_qos_t *spath_link_qos_create (hqlip_t *hqlip,
                       area_t *area1, area_t *area2,
                       int metric, link_qos_t *link_qos, u_long flags);
static void hqlip_trace_link_qos (u_long flags, trace_t *tr, char *head,
		      my_area_t *my_area, spath_link_qos_t *spath_link_qos);
static void hqlip_send_link_qos (hqlip_t *hqlip, my_area_t *my_area,
                     hqlip_neighbor_t *neighbor,
                     spath_link_qos_t *specified);
static void hqlip_calc_ilink (hqlip_t *hqlip, my_area_t *my_area, 
			      area_t *area2);
static int hqlip_spath_compare (u_char *a, u_char *b);
static void hqlip_update_database (hqlip_t *hqlip, my_area_t *my_area);
static void destroy_spath_link_qos (spath_link_qos_t *spath_link_qos);


static char *hqlip_pdus[] = {
    "",
    "KEEP_ALIVE",
    "LINK_QOS",
    "AREA_CENTER",
    "AREA_ADDR",
    "AREA_QOS",
    "SYNC",
};


link_qos_t *
copy_link_qos (link_qos_t *src, link_qos_t *dst)
{   
    assert (src);
    if (src == NULL)
        return (NULL);
    if (dst == NULL) {
        dst = New (link_qos_t);
    }
    *dst = *src;
    return (dst);
}   


static int
my_area_is_center (my_area_t *my_area)
{
    if (my_area == NULL || my_area->parent == NULL)
	return (0);
    return (my_area->parent && my_area->parent->winner &&
                my_area->parent->winner->area == my_area->area);
}

	
static link_qos_t *
hqlip_find_best_qos (LINKED_LIST *ll_link_qoses, req_qos_t *req_qos)
{
    int best_cost = 9999999 /* shut up the compiler */, cost;
    link_qos_t *best_qos = NULL, *link_qos;

    LL_Iterate (ll_link_qoses, link_qos) {
	if (req_qos) {
/* XXX
	    if (link_qos->pri != req_qos->pri) {
	        continue;
	    }
*/
	    if (link_qos->pps < req_qos->pps)
	        continue;
	    cost = req_qos->cd * link_qos->dly + req_qos->cf /* + area qos */;
	}
	else {
	    /* for the time being */
	    cost = link_qos->dly;
	}
	if (best_qos == NULL || best_cost > cost) {
	    best_cost = cost;
	    best_qos = link_qos;
	}
    }
if (best_qos)
trace (TR_PACKET, MRT->trace, "link-qos found pri %u pps %u cost %u\n",
	best_qos->pri, best_qos->pps, best_cost);
    return (best_qos);
}


static int
area_is_child (my_area_t *my_area, area_t *area)
{
    my_area_t *child;
    spath_link_qos_t *spath_link_qos;
    spath_area_addr_t *spath_area_addr;
    spath_area_qos_t *spath_area_qos;
    spath_area_center_t *spath_area_center;

    LL_Iterate (my_area->ll_children, child) {
	if (child->area == area)
	    return (TRUE);
    }

    /* dynamically determine this since area structure may change */
    /* I don't know how I can get relationship in foreign areas */

    LL_Iterate (my_area->ll_spath_link_qoses, spath_link_qos) {
	if (spath_link_qos->area1 == area)
	    return (TRUE);
/* can't beleive him ? */
/*
	if (!BIT_SET (lqos->flags, LINK_QOS_EXTERNAL)) {
	    if (lqos->area2 == area2)
		break;
	}
*/
    }
    LL_Iterate (my_area->ll_spath_area_addrs, spath_area_addr) {
	if (spath_area_addr->area == area)
	    return (TRUE);
    }
    LL_Iterate (my_area->ll_spath_area_qoses, spath_area_qos) {
	if (spath_area_qos->area == area)
	    return (TRUE);
    }
    LL_Iterate (my_area->ll_spath_area_centers, spath_area_center) {
	if (spath_area_center->area == area)
	    return (TRUE);
    }
    return (FALSE);
}


static int
area_is_descendant (my_area_t *my_area, area_t *area)
{
    my_area_t *child;

    if (my_area->area == area)
	return (FALSE);
    if (area_is_child (my_area, area))
	return (TRUE);
    LL_Iterate (my_area->ll_children, child) {
	if (area_is_descendant (child, area))
	    return (TRUE);
    }
    return (FALSE);
}


static int
area_is_local (area_t *area)
{
    if (area == NULL)
	return (TRUE);
    return (area->my_area != NULL);
}


#define area_is_r_local(area) area_is_local(area)
#ifdef notdef
/* I know it's inefficient */
/* the area and its centers are recursively local */
static int
area_is_r_local (area_t *area)
{
    if (area == NULL)
	return (TRUE);

    if (!area_is_local (area)) {
	/* foreign area */
/* trace (TR_PACKET, MRT->trace, "area %d:%a not local\n",  
	area->level, area->id); */
	return (FALSE);
    }
    if (area->level == 0) {
/* trace (TR_PACKET, MRT->trace, "area %d:%a is local\n", 
	area->level, area->id); */
	/* because of my_area */
	return (TRUE);
    }
    if (area->my_area->winner)
        return (area_is_r_local (area->my_area->winner->area));
/* trace (TR_PACKET, MRT->trace, "area %d:%a no center\n", 
	area->level, area->id); */
    return (FALSE);
}
#endif


/* check destination first to avoid loop */
static link_qos_t *
spath_calc_link_qos2 (hqlip_t *hqlip, my_area_t *my_area,
		      area_t *area1, area_t *area2, req_qos_t *req_qos,
		      link_qos_t *link_qos, int *metric, int count,
		      u_long *reason)
{
    spath_link_qos_t *spath_link_qos;

    if (count-- <= 0) {
        trace (TR_ERROR, hqlip->trace, "find_link_qos loop detected\n");
	if (reason && *reason == 0)
	    *reason = SRSVP_MSG_ERR_UNREACH;
	return (NULL);
    }

    /* area1 == NULL means local to somewhere */

    if (area_is_r_local (area1))
	area1 = NULL;
    assert (area2 != NULL);

    if (area1 == NULL)
        trace (TR_TRACE, hqlip->trace, "find_link_qos local -> %d:%a in %s\n", 
	   area2->level, area2->id, my_area->name);
    else
        trace (TR_TRACE, hqlip->trace, "find_link_qos %d:%a -> %d:%a in %s\n", 
	   area1->level, area1->id, area2->level, area2->id, my_area->name);

    while (area1 == NULL && area2 && area2->level >= 1 &&
		area2->my_area && area2->my_area->winner) {
	trace (TR_TRACE, hqlip->trace, 
		"area2 %d:%a is being replaced with the center %d:%a in %s\n", 
	   	area2->level, area2->id, 
		area2->my_area->winner->area->level, 
		area2->my_area->winner->area->id, area2->my_area->name);
	my_area = area2->my_area;
	area2 = area2->my_area->winner->area;
    }
    if (area_is_r_local (area2))
	area2 = NULL;

    if (area1 == NULL && area2 == NULL) {
        trace (TR_TRACE, hqlip->trace, "both are local in %s\n", 
		my_area->name);
        link_qos->flag = 0;
        link_qos->pri = SSPEC_PRI_USER;
        link_qos->loh = 0;
        link_qos->pps = (req_qos)? req_qos->pps: 999999 /* XXX */;
        link_qos->dly = 0;
	*metric = 0;
	if (reason)
	    *reason = 0;
        return (link_qos);
    }

    trace (TR_TRACE, hqlip->trace, "  list candidates:\n");
    LL_Iterate (my_area->ll_spath_link_qoses, spath_link_qos) {
	trace (TR_PACKET, hqlip->trace, "  %d:%a (%s)-> %d:%a (%s)%s\n", 
	    spath_link_qos->area1->level, spath_link_qos->area1->id,
	    area_is_r_local (spath_link_qos->area1)?"local":"",
	    spath_link_qos->area2->level, spath_link_qos->area2->id,
	    area_is_r_local (spath_link_qos->area2)?"local":"",
	BIT_TEST (spath_link_qos->flags, LINK_QOS_DELETED)?" deleted":"");
    }

    LL_Iterate (my_area->ll_spath_link_qoses, spath_link_qos) {

        if (BIT_TEST (spath_link_qos->flags, LINK_QOS_DELETED)) {
trace (TR_PACKET, hqlip->trace, "  skip %d:%a -> %d:%a (deleted)\n", 
	spath_link_qos->area1->level, spath_link_qos->area1->id,
	spath_link_qos->area2->level, spath_link_qos->area2->id);
	    continue;
	}

        if (area2 != spath_link_qos->area2) {
trace (TR_PACKET, hqlip->trace, "  skip %d:%a -> %d:%a (area2 x)\n", 
	spath_link_qos->area1->level, spath_link_qos->area1->id,
	spath_link_qos->area2->level, spath_link_qos->area2->id);
	    continue;
	}

        if ((area1 == NULL && area_is_r_local (spath_link_qos->area1)) ||
	    (area1 != NULL && area1 == spath_link_qos->area1)) {
	    /* area1 == null means elink(0,k) lookup */
	    /* exact match anyway */

	    link_qos_t *lqosp;
	    lqosp = hqlip_find_best_qos (spath_link_qos->ll_link_qoses, 
					 req_qos);
	    if (lqosp == NULL) {
trace (TR_PACKET, hqlip->trace, "  skip %d:%a -> %d:%a (qos x)\n", 
	spath_link_qos->area1->level, spath_link_qos->area1->id,
	spath_link_qos->area2->level, spath_link_qos->area2->id);
		if (reason)
	    	    *reason |= SRSVP_MSG_ERR_BANDWIDTH; /* XXX */
		continue;
	    }
trace (TR_PACKET, hqlip->trace, "  found at %d:%a -> %d:%a\n", 
	spath_link_qos->area1->level, spath_link_qos->area1->id,
	spath_link_qos->area2->level, spath_link_qos->area2->id);
		copy_link_qos (lqosp, link_qos);
		*metric = spath_link_qos->metric;
		if (reason)
	    	    *reason = 0;
		return (link_qos);
         }
    }

    if (area2 == NULL || area2->level == 0) {
	/* we don't handle a request such as remote to local */
        if (reason && *reason == 0)
	    *reason = SRSVP_MSG_ERR_UNREACH;
	return (NULL);
    }

    trace (TR_TRACE, hqlip->trace, "  no direct match:\n");
    LL_Iterate (my_area->ll_spath_link_qoses, spath_link_qos) {

        if (BIT_TEST (spath_link_qos->flags, LINK_QOS_DELETED))
	    continue;

        if (area2 != spath_link_qos->area2)
	    continue;

	/* skip it was tried */
        if ((area1 == NULL && area_is_r_local (spath_link_qos->area1)) ||
	    (area1 != NULL && area1 == spath_link_qos->area1))
		continue;

	{
	    link_qos_t lqos;
	    int mm = 0;
	    u_long errcode;
	    memset (&lqos, 0, sizeof (lqos));
if (area1)
trace (TR_PACKET, hqlip->trace, "  indirect %d:%a -> %d:%a\n", 
	area1->level, area1->id,
	spath_link_qos->area1->level, spath_link_qos->area1->id);
else
trace (TR_PACKET, hqlip->trace, "  indirect local -> %d:%a\n", 
	spath_link_qos->area1->level, spath_link_qos->area1->id);
	    if (spath_calc_link_qos2 (hqlip, my_area, 
		    area1, spath_link_qos->area1, req_qos, &lqos, &mm,
		    count, &errcode)) {
if (area1)
trace (TR_PACKET, hqlip->trace, "  found at %d:%a -> %d:%a\n", 
	area1->level, area1->id,
	spath_link_qos->area1->level, spath_link_qos->area1->id);
else
trace (TR_PACKET, hqlip->trace, "  found at local -> %d:%a\n", 
	spath_link_qos->area1->level, spath_link_qos->area1->id);
		link_qos->flag = 0;
		link_qos->pri = lqos.pri;
		link_qos->pps = 999999 /* XXX */;
		link_qos->loh = 0;
		if (link_qos->pps > lqos.pps)
		    link_qos->pps = lqos.pps;
		link_qos->dly += lqos.dly;
		*metric += mm;
		if (reason)
	    	    *reason = 0;
		return (link_qos);
	    }
	    if (reason && !BIT_TEST (errcode, SRSVP_MSG_ERR_UNREACH))
		*reason |= errcode;
	}
    }
    if (reason && *reason == 0)
	*reason = SRSVP_MSG_ERR_UNREACH;
    return (NULL);
}


static link_qos_t *
spath_calc_link_qos (hqlip_t *hqlip, my_area_t *my_area,
		     area_t *area1, area_t *area2, req_qos_t *req_qos,
		     link_qos_t *link_qos, int *metric, u_long *reason)
{
#define HQLIP_LOOKUP_COUNT 16
    if (reason)
	*reason = 0;
    if (metric)
	*metric = 0;
    if (link_qos)
	memset (link_qos, 0, sizeof (link_qos_t));
    return (spath_calc_link_qos2 (hqlip, my_area, area1, area2, req_qos,
				  link_qos, metric, HQLIP_LOOKUP_COUNT,
				  reason));
}


/* XXXXX */
/* transtion from a to b */
static int
hqlip_spath_link_qos_worse (spath_link_qos_t *a, spath_link_qos_t *b)
{
    link_qos_t *aqos, *bqos;

    if (BIT_TEST (a->flags, LINK_QOS_DELETED) &&
	    BIT_TEST (b->flags, LINK_QOS_DELETED))
	return (FALSE);
    if (!BIT_TEST (a->flags, LINK_QOS_DELETED) &&
	    BIT_TEST (b->flags, LINK_QOS_DELETED))
	return (TRUE);
    if (BIT_TEST (a->flags, LINK_QOS_DELETED) &&
	    !BIT_TEST (b->flags, LINK_QOS_DELETED))
	return (FALSE);

    LL_Iterate (a->ll_link_qoses, aqos) {
	LL_Iterate (b->ll_link_qoses, bqos) {
	    if (/* aqos->pri == bqos->pri && */
		    aqos->pps > bqos->pps)
		break;
	}
	/* XXX qos comparison */
	if (bqos)
	    return (TRUE);
    }
    return (FALSE);
}


/* XXXXX */
static int
hqlip_link_qos_comp (spath_link_qos_t *a, spath_link_qos_t *b)
{
    link_qos_t *aqos, *bqos;

    if (BIT_TEST (a->flags, LINK_QOS_DELETED) &&
	    BIT_TEST (b->flags, LINK_QOS_DELETED))
	return (0);
    if (!BIT_TEST (a->flags, LINK_QOS_DELETED) &&
	    BIT_TEST (b->flags, LINK_QOS_DELETED))
	return (-1);
    if (BIT_TEST (a->flags, LINK_QOS_DELETED) &&
	    !BIT_TEST (b->flags, LINK_QOS_DELETED))
	return (1);
    LL_Iterate (a->ll_link_qoses, aqos) {
	LL_Iterate (b->ll_link_qoses, bqos) {
            if (memcmp (aqos, bqos, sizeof (*aqos)) != 0)
                break;
        }
        /* XXX qos comparison */  
        if (bqos)
            return (1);
    }
    /* match */
    return (0);
}


static void
list_link_qos (hqlip_t *hqlip, my_area_t *my_area)
{
    spath_link_qos_t *spath_link_qos;

    LL_Iterate (my_area->ll_spath_link_qoses, spath_link_qos) {
        hqlip_trace_link_qos (TR_TRACE, my_area->trace, 
	      "  list", my_area, spath_link_qos);
    }
}


static int
hqlip_spath_link_qos_equal (spath_link_qos_t *a, spath_link_qos_t *b)
{
    return (a->area1 == b->area1 &&
	    a->area2 == b->area2 &&
	    a->neighbor == b->neighbor &&
	    a->flags == b->flags &&
	    hqlip_link_qos_comp (a, b) == 0);
}


/* a is being updated by b */
static int
hqlip_spath_link_qos_ready (spath_link_qos_t *a, spath_link_qos_t *b)
{
    int diff = 0;
    time_t now;

    /* local time used instead of b's tstamp */
    time (&now);
    if (b->neighbor)
	 diff = HQLIP_LINK_QOS_INTERVAL / 2;

    if (a->ll_bad_updates == NULL) {
	/* the previous was not bad */
	if (a->utime + HQLIP_LINK_QOS_INTERVAL - diff > now) {
	    trace (TR_TRACE, MRT->trace, 
		   "entry is not ready for update (%d sec later)\n",
		   a->utime + HQLIP_LINK_QOS_INTERVAL - diff - now);
	    return (FALSE);
	}
    }
    else {
	/* the previous was bad */
	if (a->ktime == 0) {
	    time_t *utimep;
	    int k = LL_GetCount (a->ll_bad_updates);
	    LL_Iterate (a->ll_bad_updates, utimep) {
	        trace (TR_TRACE, MRT->trace, 
		   "bad update %d time(s) before requires %d sec\n", k,
		   *utimep + k * HQLIP_LINK_QOS_INTERVAL - diff - now);
		if (a->ktime < *utimep + k * HQLIP_LINK_QOS_INTERVAL)
		    a->ktime = *utimep + k * HQLIP_LINK_QOS_INTERVAL;
		k--;
	    }
	}
	/* cached time is available */
	assert (a->ktime);
	if (a->ktime - diff > now) {
	    trace (TR_TRACE, MRT->trace, 
		   "entry is not ready for update (%d sec later)\n",
		   a->ktime - diff - now);
	    return (FALSE);
	}
    }
    return (TRUE);
}


static int
hqlip_inject_spath_link_qos (hqlip_t *hqlip, my_area_t *my_area, 
			     spath_link_qos_t *spath_link_qos)
{
    spath_link_qos_t *lqos;
    u_long flags_saved = 0;

    assert (spath_link_qos->ll_bad_updates == NULL);
    LL_Iterate (my_area->ll_spath_link_qoses, lqos) {

	if (lqos->area1 == spath_link_qos->area1 &&
	    lqos->area2 == spath_link_qos->area2) {

	    if (BIT_TEST (lqos->flags, LINK_QOS_DELETED) &&
	        BIT_TEST (spath_link_qos->flags, LINK_QOS_DELETED)) {
    	        hqlip_trace_link_qos (TR_TRACE, my_area->trace, 
        	BIT_TEST (lqos->flags, LINK_QOS_EXTERNAL)? 
			"already deleted (elink-qos)": 
			"already deleted (ilink-qos)", 
			my_area, spath_link_qos);
	        destroy_spath_link_qos (spath_link_qos);
		return (FALSE);
	    }

	    if (lqos->neighbor == NULL &&
		    hqlip_spath_link_qos_equal (lqos, spath_link_qos)) {
		/* exactly same (both were locally generated) */
		/* this happens when re-calculating */
    	        hqlip_trace_link_qos (TR_TRACE, my_area->trace, 
        	BIT_TEST (lqos->flags, LINK_QOS_EXTERNAL)? 
			"same (elink-qos)": "same (ilink-qos)", 
			my_area, spath_link_qos);
	        destroy_spath_link_qos (spath_link_qos);
	    	return (FALSE);
	    }

	    if (hqlip_spath_link_qos_worse (lqos, spath_link_qos)) {
		time_t *utimep = New (time_t);
		assert (spath_link_qos->ll_bad_updates == NULL);
	        spath_link_qos->ll_bad_updates = lqos->ll_bad_updates;
	        lqos->ll_bad_updates = NULL;
		*utimep = spath_link_qos->utime;
		LL_Add2 (spath_link_qos->ll_bad_updates, utimep);
		spath_link_qos->ktime = 0;
    	        hqlip_trace_link_qos (TR_TRACE, my_area->trace, 
			"registered as wrose", my_area, spath_link_qos);
	    }
	    /* avoid delete and delete condition */
	    else if (!BIT_TEST (spath_link_qos->flags, LINK_QOS_DELETED)) {
		if (!hqlip_spath_link_qos_ready (lqos, spath_link_qos)) {
		    if (spath_link_qos->neighbor) {
		        BIT_SET (spath_link_qos->flags, LINK_QOS_DELETED);
		        /* will be deleted */
    	       	        hqlip_trace_link_qos (TR_ERROR, my_area->trace, 
        		    BIT_TEST (lqos->flags, LINK_QOS_EXTERNAL)? 
			    "frequent (elink-qos)": "frequent (ilink-qos)", 
			    my_area, spath_link_qos);
		    }
		    else {
    	       	        hqlip_trace_link_qos (TR_WARN, my_area->trace, 
        		    BIT_TEST (lqos->flags, LINK_QOS_EXTERNAL)? 
			    "delayed (elink-qos)": "delayed (ilink-qos)", 
			    my_area, spath_link_qos);
		        if (lqos->delayed_link_qos)
			    destroy_spath_link_qos (lqos->delayed_link_qos);
		        lqos->delayed_link_qos = spath_link_qos;
		        return (FALSE);
		    }
	        }
	    }
	    LL_Remove (my_area->ll_spath_link_qoses, lqos);
	    flags_saved = lqos->flags;
    	    hqlip_trace_link_qos (TR_TRACE, my_area->trace, 
        	BIT_TEST (lqos->flags, LINK_QOS_EXTERNAL)? 
			"removed (elink-qos)": "removed (ilink-qos)", 
			my_area, lqos);
	    destroy_spath_link_qos (lqos);
	    break;
	}
    }

    assert (my_area->area->level > spath_link_qos->area1->level);
    assert (BIT_TEST (spath_link_qos->flags, LINK_QOS_EXTERNAL) ||
	    my_area->area->level > spath_link_qos->area2->level);

    /* always inject even in case of delete to keep history */
    /* there is a loop back link */
    /* assert (spath_link_qos->area1 != spath_link_qos->area2); */
    LL_Add (my_area->ll_spath_link_qoses, spath_link_qos);
    hqlip_trace_link_qos (TR_TRACE, my_area->trace, 
        BIT_TEST (spath_link_qos->flags, LINK_QOS_EXTERNAL)?
			  "inject (elink-qos)": "inject (ilink-qos)",
			  my_area, spath_link_qos);

    if (spath_link_qos->neighbor == NULL)
        hqlip_send_link_qos (hqlip, my_area, NULL, spath_link_qos);
    hqlip_update_database (hqlip, my_area);

#ifdef notdef
    if (my_area_is_center (my_area)) {
	if (BIT_TEST (spath_link_qos->flags, LINK_QOS_EXTERNAL)) {
	    trace (TR_TRACE, my_area->trace, 
		   "follwoing the external link to %d:%a\n",
		   spath_link_qos->area2->level,
		   spath_link_qos->area2->id);
	    /* try ilink calc with its elink area2 */
	    hqlip_calc_ilink (hqlip, my_area, spath_link_qos->area2);
	}
	else {
	    /* try ilink calc with all elink area2 */
	    LL_Iterate (my_area->ll_spath_link_qoses, lqos) {
		 if (BIT_TEST (lqos->flags, LINK_QOS_EXTERNAL)) {
	    	    hqlip_calc_ilink (hqlip, my_area, lqos->area2);
	    	    trace (TR_TRACE, my_area->trace, 
			   "following the internal link to %d:%a\n",
		   	   lqos->area2->level,
		   	   lqos->area2->id);
		}
	    }
	}
    }
#endif

    return (TRUE);
}


/* a new ilink will be injected into may_area->parent */
/* calculate my_area's center to elink's dest area2 */
/* my_area has to be a center since only a center does this */
static void
hqlip_calc_ilink (hqlip_t *hqlip, my_area_t *my_area, area_t *area2)
{
    req_qos_t req_qos;
    link_qos_t link_qos;
    spath_link_qos_t *spath_link_qos;
    int metric = 0;
    area_t *area1;
    u_long flags = 0;

    area1 = my_area->winner->area;
    trace (TR_TRACE, my_area->trace, 
	    "%s at %d:%a ilink-qos calc %d:%a -> %d:%a\n", 
	   my_area->name, my_area->area->level, my_area->area->id,
	   my_area->area->level, my_area->area->id,
	   area2->level, area2->id);

    if (BIT_TEST (my_area->flags, HQLIP_MY_AREA_SYNCING)) {
        trace (TR_TRACE, my_area->trace, "my area is under syncing\n");
	return;
    }
    if (BIT_TEST (my_area->parent->flags, HQLIP_MY_AREA_SYNCING)) {
        trace (TR_TRACE, my_area->trace, "my parent is under syncing\n");
	return;
    }
    if (my_area->winner == NULL) {
        trace (TR_TRACE, my_area->trace, "no center available now\n");
	return;
    }
/*
    if (!my_area_is_center (my_area)) {
        trace (TR_TRACE, my_area->trace, "I am not a center\n");
	return;
    }
*/

#if 0
    req_qos.pri = SSPEC_PRI_USER;
    req_qos.mtu = SSPEC_MTU_SIZE;
    req_qos.pps = SSPEC_AGPPS;
    req_qos.sec = 0;
    req_qos.cd = 1;
    req_qos.cf = 0;
    req_qos.rdly = 0;
    req_qos.rfee = 0;
#endif

    /* find if area2 has the same parent (my_area) */
    if (!area_is_child (my_area->parent, area2)) {
        BIT_SET (flags, LINK_QOS_EXTERNAL);
	/* we do only in this case for external */
	assert (area_is_r_local (area1));
    }

    /* center to elink dest */
    if (spath_calc_link_qos (hqlip, my_area, 
		BIT_TEST (flags, LINK_QOS_EXTERNAL)? area1: NULL,
			area2, NULL, &link_qos, &metric, NULL) == NULL) {
	trace (TR_WARN, my_area->trace, 
		"calic_ilink: link-qos not found %d:%a -> %d:%a\n", 
		area1->level, area1->id, area2->level, area2->id);

	BIT_SET (flags, LINK_QOS_DELETED);
    }

    spath_link_qos = spath_link_qos_create (hqlip, my_area->area, area2, 
					    metric, 
		BIT_TEST (flags, LINK_QOS_DELETED)? NULL: 
		copy_link_qos (&link_qos, NULL), flags);
    hqlip_inject_spath_link_qos (hqlip, my_area->parent, spath_link_qos);
}


static void
spath_calc_elink (hqlip_t *hqlip, my_area_t *my_area, 
		  hqlip_interface_t *vif, area_t *area)
{
    req_qos_t req_qos;
    link_qos_t link_qos;
    spath_link_qos_t *spath_link_qos;
    my_area_t *my_area0;
    int metric = 0;
    u_long flags = LINK_QOS_EXTERNAL;

    my_area0 = vif->my_area0;
    trace (TR_TRACE, my_area->trace, 
	    "calc_elink in %s at %d:%a elink-qos %d:%a -> %d:%a on %s\n", 
	   my_area->name, my_area->area->level, my_area->area->id,
	   my_area0->area->level, my_area0->area->id, 
	   area->level, area->id,
	   vif->interface->name);

#if 0
    req_qos.pri = SSPEC_PRI_USER;
    req_qos.mtu = SSPEC_MTU_SIZE;
    req_qos.pps = SSPEC_AGPPS;
    req_qos.sec = 0;
    req_qos.cd = 1;
    req_qos.cf = 0;
    req_qos.rdly = 0;
    req_qos.rfee = 0;
#endif
    if (area->my_area->winner == NULL) {
	trace (TR_WARN, my_area->trace, 
		"calc_elink: no center available for %d:%a\n", 
		area->level, area->id);
	return;
    }
    if (spath_calc_link_qos (hqlip, area->my_area, NULL, 
				area->my_area->winner->area, 
			 	NULL, &link_qos, &metric, NULL) == NULL) {
	trace (TR_TRACE, my_area->trace, 
		"calc_elink: elink-qos not found for %d:%a\n", 
		area->level, area->id);
	BIT_SET (flags, LINK_QOS_DELETED);
    }
    spath_link_qos = spath_link_qos_create (hqlip, my_area0->area, area, 
					    metric, 
			BIT_TEST (flags, LINK_QOS_DELETED)? NULL:
			copy_link_qos (&link_qos, NULL), flags);
    hqlip_inject_spath_link_qos (hqlip, my_area0->parent, spath_link_qos);
}


static void
hqlip_get_interface_mask (my_area_t *my_area, 
			  interface_bitset_t *interface_mask_p)
{
    my_area_t *child;

    if (my_area->area->level <= 0) {
	BITX_SET (interface_mask_p, my_area->vif->interface->index);
	return;
    }
    LL_Iterate (my_area->ll_children, child) {
	hqlip_get_interface_mask (child, interface_mask_p);
    }
}


/* for my_area's sub-areas */
static void
hqlip_update_elink (hqlip_t *hqlip, my_area_t *my_area, hqlip_interface_t *vif)
{
    my_area_t *child;

    if (my_area->area->level >= 2) {
	interface_bitset_t parent_ifmask;
	my_area_t *my_root = NULL;

	memset (&parent_ifmask, 0, sizeof (parent_ifmask));
	hqlip_get_interface_mask (my_area, &parent_ifmask);

        LL_Iterate (my_area->ll_children, child) {
	    interface_bitset_t children_ifmask;

	    memset (&children_ifmask, 0, sizeof (children_ifmask));
	    hqlip_get_interface_mask (child, &children_ifmask);
	    ifxor (&parent_ifmask, &children_ifmask, &parent_ifmask,
		   sizeof (interface_bitset_t));

	    if (BITX_TEST (&children_ifmask, vif->interface->index)) {
		assert (my_root == NULL);
                my_root = child;
	    }
	    else {
	        spath_calc_elink (hqlip, my_area, vif, child->area);
	    }
	}
	if (!ifzero (&parent_ifmask, sizeof (parent_ifmask))) {
	    int i;

	    assert (my_root);
	    for (i = 0; i < MAX_INTERFACES; i++) {
		if (!BITX_TEST (&parent_ifmask, i))
		    continue;
		spath_calc_elink (hqlip, my_area, 
				  hqlip->hqlip_interfaces[i], my_root->area);
	    }
	}
    }
}


static spath_link_qos_t *
spath_link_qos_create (hqlip_t *hqlip, 
                       area_t *area1, area_t *area2,
                       int metric, link_qos_t *link_qos, u_long flags)
{
    time_t now;

    spath_link_qos_t *spath_link_qos;
    spath_link_qos = New (spath_link_qos_t);
    spath_link_qos->area1 = area1;
    spath_link_qos->area2 = area2;
    spath_link_qos->metric = metric;
    spath_link_qos->ll_link_qoses = LL_Create (0);
    if (link_qos)
	LL_Add (spath_link_qos->ll_link_qoses, link_qos);
    time (&now);
    spath_link_qos->tstamp = now;
    spath_link_qos->flags = (flags | LINK_QOS_CHANGED);
    spath_link_qos->ctime = now;
    spath_link_qos->utime = now;
    return (spath_link_qos);
}


/* XXX non thread-safe */
static void 
hqlip_delayed_link_qos (hqlip_t *hqlip, my_area_t *my_area)
{
    my_area_t *child;
    spath_link_qos_t *spath_link_qos;
    LINKED_LIST *ll_delays = NULL;

    LL_Iterate (my_area->ll_children, child)
	hqlip_delayed_link_qos (hqlip, child);

    /* first gather delayed entries since the list may change */
    LL_Iterate (my_area->ll_spath_link_qoses, spath_link_qos) {
	if (spath_link_qos->delayed_link_qos) {
	   if (hqlip_spath_link_qos_ready (spath_link_qos, 
		    spath_link_qos->delayed_link_qos)) {
		LL_Add2 (ll_delays, spath_link_qos->delayed_link_qos);
		spath_link_qos->delayed_link_qos = NULL;
	    }
	}
    }

    if (ll_delays == NULL)
	return;

    LL_Iterate (ll_delays, spath_link_qos) {
    	trace (TR_TRACE, my_area->trace, "delayed entry being processed\n");
	hqlip_inject_spath_link_qos (hqlip, my_area, spath_link_qos);
    }
    LL_Destroy (ll_delays);
}


static void
hqlip_send_hello (hqlip_t *hqlip, hqlip_interface_t *vif)
{
    char buffer[HQLIP_UDP_SIZE], *cp = buffer;

    if (!hqlip->running)
	return;

    /* XXX should have a separate timer */
    hqlip_delayed_link_qos (hqlip, hqlip->root);

    if (vif->udp_sockfd >= 0) {
	if (vif->prefix) {
	    /* fill my address to avoid a zero length udp packet */
    	    int plen = 4;
	    assert (vif->prefix->family == hqlip->family);
#ifdef HAVE_IPV6
	    if (hqlip->family == AF_INET6)
	        plen = 16;
#endif /* HAVE_IPV6 */
    	    MRT_PUT_DATA (prefix_tochar (vif->prefix), plen, cp);
	}
	/* MSG_MULTI_LOOP should be redundant */
        send_packet (vif->udp_sockfd, buffer, cp - buffer, /*MSG_MULTI_LOOP*/0,
                     hqlip->all_hosts, HQLIP_UDP_PORT, vif->interface, 0);
    }
}


static int
hqlip_write (hqlip_neighbor_t *neighbor, u_char *data, int len)
{
    int ret;

    if (neighbor->sockfd < 0)
	return (-1);
    ret = write (neighbor->sockfd, data, len);
    if (ret < 0) {
	trace (TR_ERROR, neighbor->trace,
	       "write failed %m on fd %d\n", neighbor->sockfd);
    }
    else {
	trace (TR_PACKET, neighbor->trace, "write %d bytes\n", ret);
	neighbor->num_packets_sent++;
	Timer_Reset_Time (neighbor->keep_alive);
    }
    return (ret);
}


#ifndef HAVE_LIBPTHREAD
static void
destroy_packet (packet_t *packet)
{
    Delete (packet->data);
    Delete (packet);
}


static int
hqlip_flush_queue (hqlip_t *hqlip, hqlip_neighbor_t * neighbor)
{   
    packet_t *packet;
    int ret;
    
    for (;;) {
        pthread_mutex_lock (&neighbor->send_mutex_lock);
        packet = LL_GetHead (neighbor->send_queue);
        if (packet == NULL) {
	    /* end of queue */
            pthread_mutex_unlock (&neighbor->send_mutex_lock);
	    return (1);
        }
        LL_RemoveFn (neighbor->send_queue, packet, NULL);
        pthread_mutex_unlock (&neighbor->send_mutex_lock);

        ret = hqlip_write (neighbor, packet->data, packet->len);
    
        if (ret == 0) {
	    /* try again */
            pthread_mutex_lock (&neighbor->send_mutex_lock);
	    LL_Prepend (neighbor->send_queue, packet);
	    select_enable_fd_mask (neighbor->sockfd, SELECT_WRITE);
            pthread_mutex_unlock (&neighbor->send_mutex_lock);
	    return (0);
        }
    	destroy_packet (packet);
	if (ret < 0)
	    return (ret);
    }
    return (0);
}   
#endif /* HAVE_LIBPTHREAD */


static int 
hqlip_send_message (hqlip_t *hqlip, hqlip_neighbor_t *neighbor, 
		    int flags, int type, int level, int len, time_t tstamp,
		    u_char *data)
{
    assert (type >= HQLIP_MSG_KEEP_ALIVE && type <= HQLIP_MSG_SYNC);
    assert (len >= 0 && len + HQLIP_MSG_HDR_SIZE < HQLIP_MSG_SIZE);

    if (neighbor->sockfd < 0)
	return (-1);
    /* is timestamp required for keep alive? */
    if (tstamp == 0)
	time (&tstamp);
{
#ifdef HAVE_LIBPTHREAD
    u_char msgbuf[HQLIP_MSG_SIZE];
    u_char *cp = msgbuf;
#else
    u_char *cp = NewArray (u_char, len + HQLIP_MSG_HDR_SIZE);
    packet_t *packet = New (packet_t);
    packet->data = cp;
    packet->len = len + HQLIP_MSG_HDR_SIZE;
#endif /* HAVE_LIBPTHREAD */

    MRT_PUT_BYTE (flags, cp);
    MRT_PUT_BYTE (type, cp);
    MRT_PUT_BYTE (level, cp);
    MRT_PUT_BYTE (0, cp); /* reserved */
    MRT_PUT_SHORT (0, cp); /* reserved */
    MRT_PUT_SHORT (len + HQLIP_MSG_HDR_SIZE, cp); /* includes common header */
    MRT_PUT_LONG (tstamp, cp);
    if (len > 0)
	memcpy (cp, data, len);

#ifdef HAVE_LIBPTHREAD
    /* send it directly since I am a thread */
    return (hqlip_write (neighbor, msgbuf, len + HQLIP_MSG_HDR_SIZE));
#else
    pthread_mutex_lock (&neighbor->send_mutex_lock);
    LL_Append (neighbor->send_queue, packet);
    if (LL_GetCount (neighbor->send_queue) == 1) {
	select_enable_fd_mask (neighbor->sockfd, SELECT_WRITE);
    }
    pthread_mutex_unlock (&neighbor->send_mutex_lock);
    trace (TR_PACKET, neighbor->trace, "send %s at %d (%d bytes) queued\n",
           hqlip_pdus[type], level, len + HQLIP_MSG_HDR_SIZE);
    return (0);
#endif /* HAVE_LIBPTHREAD */
}
}


static void
hqlip_forward_message (hqlip_t *hqlip, my_area_t *my_area, 
		       hqlip_neighbor_t *from, 
		       int flags, int type, int level, int len, time_t tstamp,
		       u_char *data)
{
    hqlip_neighbor_t *neighbor;

    LL_Iterate (my_area->ll_neighbors, neighbor) {
	if (neighbor == from)
	    continue;
	hqlip_send_message (hqlip, neighbor,
                    	    flags, type, level, len, tstamp, data);
    }
}

    
static void
hqlip_send_keep_alive (hqlip_t *hqlip, hqlip_neighbor_t *neighbor)
{
    hqlip_send_message (hqlip, neighbor, 0, HQLIP_MSG_KEEP_ALIVE, 0, 0, 0,
			NULL);
}


static int
hqlip_send_sync (hqlip_t *hqlip, int level, hqlip_neighbor_t *neighbor)
{
    return (hqlip_send_message (hqlip, neighbor, 0, HQLIP_MSG_SYNC, level, 
				0, 0, NULL));
}


static u_char *
hqlip_put_area (area_t *area, u_char *cp)
{
    int len = 4; /* for address part */
    int afi = family2afi (area->id->family);
    u_char *addr = prefix_tochar (area->id);

    if (afi == AFI_IP6) {
	len = 8;
	if (area->level <= 0) {
	    /* host id part for level 0 */
	    addr += 8;
	}
    }

    MRT_PUT_BYTE (afi, cp);
    MRT_PUT_BYTE (area->level, cp);
    MRT_PUT_SHORT (len + 4, cp);
    MRT_PUT_DATA (addr, len, cp);
    return (cp);
}


static u_char *
hqlip_get_area (area_t **area_p, u_char *cp, hqlip_neighbor_t *neighbor)
{
    int afi, level, len;
    u_char addr[16], *start = addr;
    prefix_t *prefix;
    int bitlen = 32;

    MRT_GET_BYTE (afi, cp);
    MRT_GET_BYTE (level, cp);
    MRT_GET_SHORT (len, cp);
    if (afi != AFI_IP && afi != AFI_IP6) {
	trace (TR_INFO, neighbor->trace, "unknown afi %d\n", afi);
	return (NULL);
    }
    if (level <= 0 && level > HQLIP_AREA_LEVEL_INTERNET) {
	trace (TR_INFO, neighbor->trace, "wrong level %d\n", level);
	return (NULL);
    }
    if (len != 8 && len != 12) {
	trace (TR_INFO, neighbor->trace, "wrong len %d\n", len);
	return (NULL);
    }
    if (!((afi == AFI_IP && len == 8) || (afi == AFI_IP6 && len == 12))) {
	trace (TR_INFO, neighbor->trace, "afi %d and len %d mismatch\n");
	return (NULL);
    }
    if (afi == AFI_IP6) {
	if (level <= 0) {
	    memset (addr, 0, 8);
	    start += 8;
	}
	else {
	    memset (addr + 8, 0, 8);
	}
	bitlen = 128;
    }
    MRT_GET_DATA (start, len - 4, cp);
    prefix = New_Prefix (afi2family (afi), addr, bitlen);
    assert (area_p);
    *area_p = add_area (level, prefix);
    Deref_Prefix (prefix);
    return (cp);
}


static u_char *
hqlip_put_addr (prefix_t *prefix, u_char *cp)
{
    int len = 4; /* for address part */
    int afi = family2afi (prefix->family);
    int bitlen = prefix->bitlen;
    u_char *addr = prefix_tochar (prefix);

    if (afi == AFI_IP6) {
	len = 8;
	if (bitlen > 64)
	    bitlen = 64;
    }
    MRT_PUT_BYTE (afi, cp);
    MRT_PUT_BYTE (bitlen, cp);
    MRT_PUT_SHORT (len + 4, cp);
    MRT_PUT_DATA (addr, len, cp);
    return (cp);
}


static u_char *
hqlip_get_addr (prefix_t **prefix_p, u_char *cp, hqlip_neighbor_t *neighbor)
{
    int afi, bitlen, len;
    u_char addr[16];

    MRT_GET_BYTE (afi, cp);
    MRT_GET_BYTE (bitlen, cp);
    MRT_GET_SHORT (len, cp);
    if (afi != AFI_IP && afi != AFI_IP6) {
	trace (TR_INFO, neighbor->trace, "unknown afi %d\n", afi);
	return (NULL);
    }
    if (!(afi == AFI_IP && bitlen <= 32) || (afi == AFI_IP6 && bitlen <= 64)) {
	trace (TR_INFO, neighbor->trace, "wrong bitlen %d\n", bitlen);
	return (NULL);
    }
    if (len != 8 && len != 12) {
	trace (TR_INFO, neighbor->trace, "wrong len %d\n", len);
	return (NULL);
    }
    if (!((afi == AFI_IP && len == 8) || (afi == AFI_IP6 && len == 12))) {
	trace (TR_INFO, neighbor->trace, "afi %d and len %d mismatch\n");
	return (NULL);
    }
    if (afi == AFI_IP6)
	memset (addr + 8, 0, 8);
    MRT_GET_DATA (addr, len - 4, cp);
    assert (prefix_p);
    *prefix_p = New_Prefix (afi2family (afi), addr, bitlen);
    return (cp);
}


static char *
hqlip_prepare_link_qos (hqlip_t *hqlip, spath_link_qos_t *spath_link_qos, 
		        char *cp)
{
    link_qos_t *link_qos;

    cp = hqlip_put_area (spath_link_qos->area1, cp);
    cp = hqlip_put_area (spath_link_qos->area2, cp);
    if (!BIT_TEST (spath_link_qos->flags, LINK_QOS_DELETED)) {
        MRT_PUT_LONG (spath_link_qos->metric, cp);
	LL_Iterate (spath_link_qos->ll_link_qoses, link_qos) {
            MRT_PUT_BYTE ((link_qos->flag << 7) | link_qos->pri, cp);
            MRT_PUT_BYTE (link_qos->loh, cp);
            MRT_PUT_SHORT (link_qos->rsvd, cp);
            MRT_PUT_LONG (link_qos->pps, cp);
            MRT_PUT_LONG (link_qos->dly, cp);
	}
    }
    return (cp);
}


static void
hqlip_trace_link_qos (u_long flags, trace_t *tr, char *head,
		      my_area_t *my_area, spath_link_qos_t *spath_link_qos)
{
    link_qos_t *link_qos;

    trace (flags, tr, 
		"%s at %d:%s link-qos %d:%a -> %d:%a%s%s\n",
		head, my_area->area->level, my_area->name,
	spath_link_qos->area1->level, spath_link_qos->area1->id,
	spath_link_qos->area2->level, spath_link_qos->area2->id,
	BIT_TEST (spath_link_qos->flags, LINK_QOS_EXTERNAL)?
		" external": "",
	BIT_TEST (spath_link_qos->flags, LINK_QOS_DELETED)?
		" deleted": "");

    if (!BIT_TEST (spath_link_qos->flags, LINK_QOS_DELETED)) {
        LL_Iterate (spath_link_qos->ll_link_qoses, link_qos) {
            if (link_qos->flag)
                trace (flags, tr,
                        "  pri %u pps %u dly %u loh %u\n",
                        link_qos->pri, link_qos->pps, link_qos->dly,
                        link_qos->loh);
            else
                trace (flags, tr, 
                        "  pri %u pps %u dly %u\n",
                        link_qos->pri, link_qos->pps, link_qos->dly);
	}
    }
}


static void
hqlip_send_link_qos (hqlip_t *hqlip, my_area_t *my_area, 
		     hqlip_neighbor_t *neighbor, 
		     spath_link_qos_t *specified)
{
    spath_link_qos_t *spath_link_qos;
    hqlip_neighbor_t *receiver;

    LL_Iterate (my_area->ll_spath_link_qoses, spath_link_qos) {
	char msgbuf[HQLIP_MSG_SIZE], *cp = msgbuf;
        int flags = 0;

	if (specified && spath_link_qos != specified)
	    continue;
	if (neighbor && BIT_TEST (spath_link_qos->flags, LINK_QOS_DELETED))
	    continue;

        cp = hqlip_prepare_link_qos (hqlip, spath_link_qos, cp);

	/* if neighbor is supplied, it means the first syncing */
	if (neighbor)
	    flags |= HQLIP_MSGF_NOTSYNCED;
	if (BIT_TEST (spath_link_qos->flags, LINK_QOS_EXTERNAL))
	    flags |= HQLIP_MSGF_EXTERNAL;

        LL_Iterate (my_area->ll_neighbors, receiver) {
	    if (spath_link_qos->neighbor == receiver)
	        continue;
	    if (neighbor && neighbor != receiver)
	        continue;
            hqlip_send_message (hqlip, receiver, flags,
		        HQLIP_MSG_LINK_QOS, my_area->area->level, 
		        cp - msgbuf, spath_link_qos->tstamp, msgbuf);
	    hqlip_trace_link_qos (TR_PACKET, receiver->trace, "sent", 
				  my_area, spath_link_qos);
	}
    }
}


static void
destroy_spath_link_qos (spath_link_qos_t *spath_link_qos)
{
    if (spath_link_qos->ll_link_qoses)
	LL_DestroyFn (spath_link_qos->ll_link_qoses, Destroy);
    if (spath_link_qos->delayed_link_qos)
	destroy_spath_link_qos (spath_link_qos->delayed_link_qos);
    if (spath_link_qos->ll_bad_updates)
	LL_DestroyFn (spath_link_qos->ll_bad_updates, Destroy);
    Destroy (spath_link_qos);
}


static void
hqlip_recv_link_qos (hqlip_t *hqlip, hqlip_neighbor_t *neighbor, 
		     my_area_t *my_area, int flags, 
		     int len, time_t tstamp, u_char *cp)
{
    spath_link_qos_t *spath_link_qos, *slq;
    u_char *begin = cp;
    u_char *end = cp + len;
    time_t now;
    int dont_forward = 0;

    time (&now);
    spath_link_qos = New (spath_link_qos_t);
    cp = hqlip_get_area (&spath_link_qos->area1, cp, neighbor);
    cp = hqlip_get_area (&spath_link_qos->area2, cp, neighbor);
    if (cp >= end) {
	BIT_SET (spath_link_qos->flags, LINK_QOS_DELETED);
    }
    else {
        spath_link_qos->ll_link_qoses = LL_Create (0);
        MRT_GET_LONG (spath_link_qos->metric, cp);
	while (end - cp >= sizeof (link_qos_t)) {
	    int temp;
    	    link_qos_t *link_qos;

    	    link_qos = New (link_qos_t);
            MRT_GET_BYTE (temp, cp);
	    link_qos->flag = (temp >> 7);
	    link_qos->pri = (temp & 0x7f);
            MRT_GET_BYTE (link_qos->loh, cp);
            MRT_GET_SHORT (link_qos->rsvd, cp);
            MRT_GET_LONG (link_qos->pps, cp);
            MRT_GET_LONG (link_qos->dly, cp);
	    LL_Add2 (spath_link_qos->ll_link_qoses, link_qos);
	}
	if (cp != end) {
	    trace (TR_INFO, neighbor->trace, "wrong data boundary (%d)\n",
		  end - cp);
	    len = len - (end - cp); /* adjusted for forwarding XXX */
	}
    }

    spath_link_qos->tstamp = tstamp;
    spath_link_qos->ctime = now;
    spath_link_qos->utime = now;
    spath_link_qos->neighbor = neighbor;
    BIT_SET (spath_link_qos->flags, LINK_QOS_CHANGED);
    if (BIT_TEST (flags, HQLIP_MSGF_EXTERNAL))
	BIT_SET (spath_link_qos->flags, LINK_QOS_EXTERNAL);

    LL_Iterate (my_area->ll_spath_link_qoses, slq) {
	if (slq->area1 == spath_link_qos->area1 &&
	    slq->area2 == spath_link_qos->area2 &&
	    slq->neighbor != spath_link_qos->neighbor) {
	    trace (TR_TRACE, neighbor->trace, "the following is being "
			"updated by another router %s -> %s\n",
		   slq->neighbor? prefix_toa (slq->neighbor->prefix): "local",
		   spath_link_qos->neighbor? 
		       prefix_toa (spath_link_qos->neighbor->prefix): "local");
	    dont_forward++;
	}
	if (slq->area1 == spath_link_qos->area1 &&
	    slq->area2 == spath_link_qos->area2 &&
	    slq->neighbor == spath_link_qos->neighbor)
	    break;
    }

    if (spath_link_qos->area1->level >= my_area->area->level) {
        trace (TR_WARN, neighbor->trace, 
	       "link-qos area1 level %d wrong for %s at %d\n", 
		spath_link_qos->area1->level, my_area->name, 
		my_area->area->level);
	Delete (spath_link_qos);
  	return;
    }

    if (spath_link_qos->area1 == spath_link_qos->area2) {
        trace (TR_WARN, neighbor->trace, 
	       "link-qos both area1 and area2 is same with %d:%a\n", 
		spath_link_qos->area1->level, spath_link_qos->area1->id);
	Delete (spath_link_qos);
  	return;
    }

    if (BIT_TEST (spath_link_qos->flags, LINK_QOS_DELETED)) {
        hqlip_trace_link_qos (TR_PACKET, neighbor->trace, 
			      "recv (del)", my_area, spath_link_qos);
        if (hqlip_inject_spath_link_qos (hqlip, my_area, spath_link_qos)) {
	    if (!dont_forward)
	        hqlip_forward_message (hqlip, my_area, neighbor, flags, 
				HQLIP_MSG_LINK_QOS, my_area->area->level, 
				len, tstamp, begin);
	}
    }
    else if (slq == NULL) {
        hqlip_trace_link_qos (TR_PACKET, neighbor->trace, 
			      "recv (new)", my_area, spath_link_qos);
        if (hqlip_inject_spath_link_qos (hqlip, my_area, spath_link_qos)) {
	    if (!dont_forward)
	        hqlip_forward_message (hqlip, my_area, neighbor, flags, 
			       HQLIP_MSG_LINK_QOS, my_area->area->level, 
			       len, tstamp, begin);
	}
    }
    else if (tstamp > slq->tstamp) {
	/* newer */
        hqlip_trace_link_qos (TR_PACKET, neighbor->trace, 
			      "recv (update)", my_area, spath_link_qos);
        if (hqlip_inject_spath_link_qos (hqlip, my_area, spath_link_qos))
	    hqlip_forward_message (hqlip, my_area, neighbor, flags, 
				    HQLIP_MSG_LINK_QOS, my_area->area->level, 
				    len, tstamp, begin);
    }
    else {
        hqlip_trace_link_qos (TR_PACKET, neighbor->trace, 
				  "recv (ignore)", my_area, spath_link_qos);
	destroy_spath_link_qos (spath_link_qos);
    }
}


static void
hqlip_trace_area_center (u_long flags, trace_t *tr, char *head,
	      my_area_t *my_area, spath_area_center_t *spath_area_center)
{
    /* char strbuf[64]; */

    if (BIT_TEST (spath_area_center->flags, AREA_CENTER_DELETED))
        /*trace (flags, tr, "%s at %d:%s area-center %d:%a from %s deleted\n",*/
        trace (flags, tr, "%s at %d:%s area-center %d:%a deleted\n",
	   head, my_area->area->level, my_area->name,
	   spath_area_center->area->level, spath_area_center->area->id /*,
	   inet_ntop (AF_INET, &spath_area_center->router_id, 
			strbuf, sizeof (strbuf))*/);
    else
        /*trace (flags, tr, "%s at %d:%s area-center %d:%a from %s pri %d\n",*/
        trace (flags, tr, "%s at %d:%s area-center %d:%a pri %d\n",
	   head, my_area->area->level, my_area->name,
	   spath_area_center->area->level, spath_area_center->area->id,
	   /* inet_ntop (AF_INET, &spath_area_center->router_id, 
			strbuf, sizeof (strbuf)),*/
	   spath_area_center->pri);
}


static void
hqlip_send_area_center (hqlip_t *hqlip, my_area_t *my_area, 
		hqlip_neighbor_t *neighbor, spath_area_center_t *specified)
{
    spath_area_center_t *spath_area_center;
    hqlip_neighbor_t *receiver;

    LL_Iterate (my_area->ll_spath_area_centers, spath_area_center) {
	char msgbuf[HQLIP_MSG_SIZE], *cp = msgbuf;
        int flags = 0;

	if (specified && spath_area_center != specified)
	    continue;
	if (neighbor && 
		BIT_TEST (spath_area_center->flags, AREA_CENTER_DELETED))
	    continue;

	 /* originating & it's center is local */
	if (area_is_local (spath_area_center->area) && (
	      spath_area_center->area->my_area->winner == NULL ||
	      !area_is_local (spath_area_center->area->my_area->winner->area)))
	    continue;

        cp = hqlip_put_area (spath_area_center->area, cp);
        /* MRT_PUT_NETLONG (spath_area_center->router_id, cp); */
        if (!BIT_TEST (spath_area_center->flags, AREA_CENTER_DELETED)) {
            MRT_PUT_BYTE (spath_area_center->pri, cp);
            MRT_PUT_BYTE (0, cp);
            MRT_PUT_SHORT (0, cp);
	}
	/* if neighbor is supplied, it means the first syncing */
	if (neighbor)
	    flags |= HQLIP_MSGF_NOTSYNCED;

        LL_Iterate (my_area->ll_neighbors, receiver) {
	    if (spath_area_center->neighbor == receiver)
	        continue;
	    if (neighbor && neighbor != receiver)
	        continue;
            hqlip_send_message (hqlip, receiver, flags,
		        HQLIP_MSG_AREA_CENTER, my_area->area->level, 
		        cp - msgbuf, spath_area_center->tstamp, msgbuf);
	    hqlip_trace_area_center (TR_PACKET, receiver->trace, 
				  "sent", my_area, spath_area_center);
	}
    }
}


static int 
spath_area_center_compare (spath_area_center_t *a, spath_area_center_t *b)
{
    if (a->pri == b->pri) {
        if (a->area != b->area) {
    	    assert (0); /* should not happen */
	    return (0);
        }
	if (area_is_local (a->area) &&
		a->area->my_area->winner &&
	        area_is_local (a->area->my_area->winner->area))
	    return (-1);
        return (0);
        /* return (a->router_id - b->router_id); */
	    /* smaller router id wins */
    }
    return (a->pri - b->pri);
    /* smaller priority wins */
}


static void
hqlip_ask_descendant_calc (hqlip_t *hqlip, my_area_t *child,
			   area_t *area2)
{
    my_area_t *descendant;

    if (child->winner && area_is_r_local (child->winner->area))
	hqlip_calc_ilink (hqlip, child, area2);
    if (child->area->level <= 1)
	return;
    LL_Iterate (child->ll_children, descendant)
	hqlip_ask_descendant_calc (hqlip, descendant, area2);
}


static void
hqlip_update_database (hqlip_t *hqlip, my_area_t *my_area)
{
    my_area_t *ancestor, *child;
    my_area_t *me, *sister;
    spath_link_qos_t *spath_link_qos;

    assert (my_area->area->level > 0);
    if (my_area->parent == NULL /* internet area */) {
	my_area->exwinner = my_area->winner;
	return;
    }
    if (my_area->winner == NULL) {
	/* lost a center */
	/* XXX all entries have to delete */
	my_area->exwinner = my_area->winner;
	return;
    }

    trace (TR_TRACE, my_area->trace, "update database: (0,k)\n");

    /* general role -- I could this in the same way as the center does */

    /* 0) update (0, k) elink where k is myself */

    {
	interface_bitset_t parent_ifmask;
	interface_bitset_t my_ifmask;

	/* finding vifs that don't belong to my area */
	memset (&parent_ifmask, 0, sizeof (parent_ifmask));
	hqlip_get_interface_mask (my_area->parent, &parent_ifmask);

	memset (&my_ifmask, 0, sizeof (my_ifmask));
	hqlip_get_interface_mask (my_area, &my_ifmask);
	ifxor (&parent_ifmask, &my_ifmask, &parent_ifmask,
		   sizeof (interface_bitset_t));

	if (!ifzero (&parent_ifmask, sizeof (parent_ifmask))) {
	    int i;

	    for (i = 0; i < MAX_INTERFACES; i++) {
		if (!BITX_TEST (&parent_ifmask, i))
		    continue;
		spath_calc_elink (hqlip, my_area /* just for info */,
				  hqlip->hqlip_interfaces[i], my_area->area);
	    }
	}
    }

    if (!area_is_r_local (my_area->winner->area)) {
	my_area->exwinner = my_area->winner;
	return;
    }

    trace (TR_TRACE, my_area->trace, "update database: (k,j)\n");

    /* I changed so that it's local if it's a center */
    assert (area_is_local (my_area->winner->area));
#ifdef notdef
    if (!area_is_local (my_area->winner->area)) {
	/* the center on another router */
        trace (TR_TRACE, my_area->trace, 
	       "update database: center is on another router\n");
	my_area->exwinner = my_area->winner;
	return;
    }
#endif

    /* 1) (k, j) where k is me */

    /* just follow the existing ones */
    LL_Iterate (my_area->ll_spath_link_qoses, spath_link_qos) {
	/* areas unknown to me */
	if (BIT_TEST (spath_link_qos->flags, LINK_QOS_EXTERNAL) &&
		!area_is_local (spath_link_qos->area2)) {
	    trace (TR_TRACE, my_area->trace, 
		   "follwoing the external link to %d:%a\n",
		   spath_link_qos->area2->level,
		   spath_link_qos->area2->id);
	     /* try ilink calc with its elink area2 */
	    hqlip_calc_ilink (hqlip, my_area, spath_link_qos->area2);
	}
    }

    assert (my_area->area->level >= 1);
    ancestor = my_area;
    while ((ancestor = ancestor->parent) != NULL) {
	LL_Iterate (ancestor->ll_children, child) {
	    if (child != my_area &&
		    !area_is_descendant (child, my_area->area))
	        hqlip_calc_ilink (hqlip, my_area, child->area);
        }
    }
    
    /* 2) (i, k) where k is me */

    trace (TR_TRACE, my_area->trace, "update database: (i,k)\n");

    for (me = my_area; me->parent != NULL; me = me->parent) {
	LL_Iterate (me->parent->ll_children, sister) {
	    if (sister != me &&
		    !area_is_descendant (sister, me->area))
		hqlip_ask_descendant_calc (hqlip, sister, me->area);
	}
    }
    my_area->exwinner = my_area->winner;
    /* to know the center change */

#ifdef notdef
    assert (my_area->area->level > 0);
    if (my_area->winner == NULL) {
	/* lost a center */
	/* XXX all entries have to delete */
	return;
    }
    if (my_area->winner->area->my_area == NULL) {
	/* the center on another router */
	return;
    }

    if (my_area->parent != NULL /* not internet area */) {

        /* 1) update (0, k) elink where k is myself */

	interface_bitset_t parent_ifmask;
	interface_bitset_t my_ifmask;

	/* finding vifs that don't belong to my area */
	memset (&parent_ifmask, 0, sizeof (parent_ifmask));
	hqlip_get_interface_mask (my_area->parent, &parent_ifmask);

	memset (&my_ifmask, 0, sizeof (my_ifmask));
	hqlip_get_interface_mask (my_area, &my_ifmask);
	ifxor (&parent_ifmask, &my_ifmask, &parent_ifmask,
		   sizeof (interface_bitset_t));

	if (!ifzero (&parent_ifmask, sizeof (parent_ifmask))) {
	    int i;

	    for (i = 0; i < MAX_INTERFACES; i++) {
		if (!BITX_TEST (&parent_ifmask, i))
		    continue;
		spath_calc_elink (hqlip, my_area /* just for info */,
				  hqlip->hqlip_interfaces[i], my_area->area);
	    }
	}

	if (my_area_is_center (my_area)) {
            /* update (j, k) link where j is myself */
            LL_Iterate (my_area->ll_spath_link_qoses, spath_link_qos) {
	        if (BIT_TEST (spath_link_qos->flags, LINK_QOS_DELETED))
	            continue;
	        if (!BIT_TEST (spath_link_qos->flags, LINK_QOS_EXTERNAL))
	            continue;
	        hqlip_calc_ilink (hqlip, my_area, spath_link_qos->area2);
            }
	}
    }
#endif
}


static void 
hqlip_update_link_qos (hqlip_t *hqlip, my_area_t *my_area, int level)
{
    my_area_t *child;

    if (my_area->area->level < level)
	return;

    if (my_area->area->level == level) {
	hqlip_update_database (hqlip, my_area);
	return;
    }

    LL_Iterate (my_area->ll_children, child) {
	hqlip_update_link_qos (hqlip, child, level);
    }
}



int
hqlip_update_area_center (hqlip_t *hqlip, my_area_t *my_area,
			  spath_area_center_t *spath_area_center)
{
    spath_area_center_t *winner;

    if (BIT_TEST (my_area->flags, HQLIP_MY_AREA_SYNCING)) {
        trace (TR_TRACE, my_area->trace, "my area is under syncing\n");
	return (0);
    }

    assert (spath_area_center == NULL || 
		spath_area_center->area->level < my_area->area->level);
    if (my_area->winner && 
	    BIT_TEST (my_area->winner->flags, AREA_CENTER_DELETED)) {
	trace (TR_INFO, my_area->trace, 
	       "lost center %d:%a in %s level %d\n",
		my_area->winner->area->level, my_area->winner->area->id,
		my_area->name, my_area->area->level);
	my_area->winner = NULL;
    }

    /* it can't be a candidate */
    if (spath_area_center &&
	BIT_TEST (spath_area_center->flags, AREA_CENTER_DELETED))
	spath_area_center = NULL;
    
    if (my_area->winner) {
	if (spath_area_center) {
	    if (my_area->winner == spath_area_center)
		return (0);
	    if (spath_area_center_compare (my_area->winner, 
					   spath_area_center) > 0) {
		trace (TR_INFO, my_area->trace, 
	       	       "change center %d:%a -> %d:%a in %s level %d\n",
		        my_area->winner->area->level, 
			my_area->winner->area->id,
		        spath_area_center->area->level, 
		        spath_area_center->area->id,
		        my_area->name, my_area->area->level);
		my_area->winner = spath_area_center;
	        return (1);
	    }
	}
    }

    if (my_area->winner == NULL && spath_area_center) {
	trace (TR_INFO, my_area->trace, 
	       "new center %d:%a in %s level %d\n",
		spath_area_center->area->level, spath_area_center->area->id,
		my_area->name, my_area->area->level);
	my_area->winner = spath_area_center;
	return (1);
    }
  
    winner = NULL;
    LL_Iterate (my_area->ll_spath_area_centers, spath_area_center) {
	if (BIT_TEST (spath_area_center->flags, AREA_CENTER_DELETED))
	    continue;
	if (winner == NULL) {
	    winner = spath_area_center;
	}
	if (spath_area_center_compare (winner, spath_area_center) > 0) {
	    winner = spath_area_center;
        }
    }
    if (my_area->winner != winner) {
	if (my_area->winner && winner)
	    trace (TR_INFO, my_area->trace, 
		    "change center %d:%a -> %d:%a in %s level %d\n",
		    my_area->winner->area->level, my_area->winner->area->id,
		    winner->area->level, winner->area->id,
		    my_area->name, my_area->area->level);
	else if (my_area->winner)
	    trace (TR_INFO, my_area->trace, 
		    "lost center %d:%a in %s level %d\n",
		    my_area->winner->area->level, my_area->winner->area->id,
		    my_area->name, my_area->area->level);
	else if (winner)
	    trace (TR_INFO, my_area->trace, 
		    "new center %d:%a in %s level %d\n",
		    winner->area->level, winner->area->id,
		    my_area->name, my_area->area->level);
	my_area->winner = winner;
	return (1);
    }
    return (0);
}


static void
hqlip_recv_area_center (hqlip_t *hqlip, hqlip_neighbor_t *neighbor, 
		        my_area_t *my_area, int flags, 
		        int len, time_t tstamp, u_char *cp)
{
    spath_area_center_t *spath_area_center, *sac;
    u_char *begin = cp;
    u_char *end = cp + len;
    time_t now;
    int dont_forward = 0;

    time (&now);
    spath_area_center = New (spath_area_center_t);
    cp = hqlip_get_area (&spath_area_center->area, cp, neighbor);
    /* MRT_GET_NETLONG (spath_area_center->router_id, cp); */
    if (cp >= end) {
	BIT_SET (spath_area_center->flags, AREA_CENTER_DELETED);
    }
    else {
        MRT_GET_BYTE (spath_area_center->pri, cp);
        cp += 3;
	if (cp != end) {
	    trace (TR_INFO, neighbor->trace, "wrong data boundary (%d)\n",
		   end - cp);
	    len = len - (end - cp); /* adjusted for forwarding XXX */
	}
    }

    spath_area_center->tstamp = tstamp;
    spath_area_center->ctime = now;
    spath_area_center->utime = now;
    spath_area_center->neighbor = neighbor;
    BIT_SET (spath_area_center->flags, AREA_CENTER_CHANGED);

    if (spath_area_center->area->level >= my_area->area->level) {
	trace (TR_WARN, neighbor->trace, "wrong area level %d for %s at %d\n",
	       spath_area_center->area->level, my_area->name, 
	       my_area->area->level);
	Delete (spath_area_center);
	return;
    }
    /* check if there is the same priority */
    if (!BIT_TEST (spath_area_center->flags, AREA_CENTER_DELETED)) {
        LL_Iterate (my_area->ll_spath_area_centers, sac) {
    	    if (BIT_TEST (sac->flags, AREA_CENTER_DELETED))
		continue;
	    /* if area is the same, it's ok */
	    if (sac->area == spath_area_center->area)
		continue;
	    if (sac->pri == spath_area_center->pri) {
		hqlip_trace_area_center (TR_WARN, neighbor->trace, 
			"recv (pri)", my_area, spath_area_center);
		Delete (spath_area_center);
		return;
	    }
        }
    }

    LL_Iterate (my_area->ll_spath_area_centers, sac) {
	if (sac->area == spath_area_center->area &&
		sac->neighbor != spath_area_center->neighbor) {
	    trace (TR_TRACE, neighbor->trace, "the following is being "
			"updated by another router %s -> %s\n",
		 sac->neighbor? prefix_toa (sac->neighbor->prefix): "local",
		 spath_area_center->neighbor? 
		   prefix_toa (spath_area_center->neighbor->prefix): "local");
	    dont_forward++;
	}
	if (sac->area == spath_area_center->area &&
	    sac->neighbor == spath_area_center->neighbor /* &&
	    sac->router_id == spath_area_center->router_id */)
	    break;
    }

    if (BIT_TEST (spath_area_center->flags, AREA_CENTER_DELETED)) {
	if (sac && BIT_TEST (sac->flags, AREA_CENTER_DELETED)) {
            hqlip_trace_area_center (TR_PACKET, neighbor->trace, 
			      "recv (dup)", my_area, spath_area_center);
	}
	else if (sac == NULL) {
            hqlip_trace_area_center (TR_PACKET, neighbor->trace, 
			      "recv (none)", my_area, spath_area_center);
	}
	else {
            hqlip_trace_area_center (TR_PACKET, neighbor->trace, 
			      "recv (del)", my_area, spath_area_center);
	    LL_Remove (my_area->ll_spath_area_centers, sac);
	    Destroy (sac);
	    if (!dont_forward)
	        hqlip_forward_message (hqlip, my_area, neighbor, flags, 
				HQLIP_MSG_AREA_CENTER, my_area->area->level, 
				len, tstamp, begin);
	    if (sac == my_area->winner) {
		my_area->winner = NULL;
        	if (hqlip_update_area_center (hqlip, my_area, 
					      spath_area_center))
            	    hqlip_update_database (hqlip, my_area);
	    }
	}
	Delete (spath_area_center);
	return;
    }

    if (sac == NULL) {
	assert (my_area->area->level > spath_area_center->area->level);
	LL_Add (my_area->ll_spath_area_centers, spath_area_center);
	hqlip_trace_area_center (TR_PACKET, neighbor->trace, 
			"recv (new)", my_area, spath_area_center);
	if (!dont_forward)
	    hqlip_forward_message (hqlip, my_area, neighbor, flags, 
				HQLIP_MSG_AREA_CENTER, my_area->area->level, 
				len, tstamp, begin);
        if (hqlip_update_area_center (hqlip, my_area, spath_area_center))
            hqlip_update_database (hqlip, my_area);
    }
    else {
	if (tstamp > sac->tstamp || 
		BIT_TEST (sac->flags, AREA_CENTER_DELETED)) {
	    /* newer */
	    /* assert (sac->neighbor == spath_area_center->neighbor); */
	    LL_Remove (my_area->ll_spath_area_centers, sac);
	    Destroy (sac);
	    if (my_area->winner == sac)
		my_area->winner = NULL;
	    LL_Add (my_area->ll_spath_area_centers, spath_area_center);
	    hqlip_trace_area_center (TR_PACKET, neighbor->trace, 
			          "recv (update)", my_area, spath_area_center);
	    hqlip_forward_message (hqlip, my_area, neighbor, flags, 
				HQLIP_MSG_AREA_CENTER, my_area->area->level, 
				len, tstamp, begin);
            if (hqlip_update_area_center (hqlip, my_area, spath_area_center))
                hqlip_update_database (hqlip, my_area);
	}
	else {
	    hqlip_trace_area_center (TR_PACKET, neighbor->trace,
                                  "recv (ignore)", my_area, spath_area_center);
	    Delete (spath_area_center);
	}
    }
}


static void
hqlip_trace_area_addr (u_long flags, trace_t *tr, char *head,
	      my_area_t *my_area, spath_area_addr_t *spath_area_addr)
{
    trace (flags, tr, "%s at %d:%s area-addr %d:%a%s\n", head,
		my_area->area->level, my_area->name,
		spath_area_addr->area->level, spath_area_addr->area->id,
		BIT_TEST (spath_area_addr->flags, AREA_ADDR_DELETED)?
			" deleted": "");
    if (!BIT_TEST (spath_area_addr->flags, AREA_ADDR_DELETED)) {
	prefix_t *prefix;
        LL_Iterate (spath_area_addr->ll_prefixes, prefix)
            trace (flags, tr, "  addr %p\n", prefix);
    }
}


static void
hqlip_send_area_addr (hqlip_t *hqlip, my_area_t *my_area, 
		      hqlip_neighbor_t *neighbor, spath_area_addr_t *specified)
{
    spath_area_addr_t *spath_area_addr;
    prefix_t *prefix;
    hqlip_neighbor_t *receiver;

    LL_Iterate (my_area->ll_spath_area_addrs, spath_area_addr) {
	char msgbuf[HQLIP_MSG_SIZE], *cp = msgbuf;
        int flags = 0;

	if (specified && spath_area_addr != specified)
	    continue;
	if (neighbor && BIT_TEST (spath_area_addr->flags, AREA_ADDR_DELETED))
	    continue;

        cp = hqlip_put_area (spath_area_addr->area, cp);
        if (!BIT_TEST (spath_area_addr->flags, AREA_ADDR_DELETED)) {
	    LL_Iterate (spath_area_addr->ll_prefixes, prefix) {
                cp = hqlip_put_addr (prefix, cp);
	    }
	}

	/* if neighbor is supplied, it means the first syncing */
	if (neighbor)
	    flags |= HQLIP_MSGF_NOTSYNCED;

        LL_Iterate (my_area->ll_neighbors, receiver) {
	    if (spath_area_addr->neighbor == receiver)
	        continue;
	    if (neighbor && receiver != neighbor)
	        continue;
            hqlip_send_message (hqlip, receiver, flags,
		        HQLIP_MSG_AREA_ADDR, my_area->area->level, 
		        cp - msgbuf, spath_area_addr->tstamp, msgbuf);
	    hqlip_trace_area_addr (TR_PACKET, receiver->trace,
                                  "sent", my_area, spath_area_addr);
	}
    }
}


static void
destroy_spath_area_addr (spath_area_addr_t *spath_area_addr)
{
    if (spath_area_addr->ll_prefixes)
	LL_DestroyFn (spath_area_addr->ll_prefixes, 
			  (void_fn_t) Deref_Prefix);
    Delete (spath_area_addr);
}


static void
hqlip_recv_area_addr (hqlip_t *hqlip, hqlip_neighbor_t *neighbor, 
		      my_area_t *my_area, int flags, 
		      int len, time_t tstamp, u_char *cp)
{
    spath_area_addr_t *spath_area_addr, *saa;
    u_char *begin = cp;
    u_char *end = cp + len;
    time_t now;
    int dont_forward = 0;

    time (&now);
    spath_area_addr = New (spath_area_addr_t);
    spath_area_addr->ll_prefixes = LL_Create (0);
    cp = hqlip_get_area (&spath_area_addr->area, cp, neighbor);
    if (cp >= end) {
	BIT_SET (spath_area_addr->flags, AREA_ADDR_DELETED);
    }
    else {
	while (end - cp >= 8 /* 12 */) {
	    prefix_t *prefix;
    	    cp = hqlip_get_addr (&prefix, cp, neighbor);
	    /* XXX cp check */
    	    LL_Add2 (spath_area_addr->ll_prefixes, prefix);
	}
	if (cp != end) {
	    trace (TR_INFO, neighbor->trace, "wrong data boundary (%d)\n",
		  end - cp);
	    len = len - (end - cp); /* adjusted for forwarding XXX */
	}
    }

    spath_area_addr->tstamp = tstamp;
    spath_area_addr->ctime = now;
    spath_area_addr->utime = now;
    spath_area_addr->neighbor = neighbor;
    BIT_SET (spath_area_addr->flags, AREA_ADDR_CHANGED);

    LL_Iterate (my_area->ll_spath_area_addrs, saa) {
	if (saa->area == spath_area_addr->area &&
		saa->neighbor != spath_area_addr->neighbor) {
	    trace (TR_TRACE, neighbor->trace, "the following is being "
			"updated by another router %s -> %s\n",
		 saa->neighbor? prefix_toa (saa->neighbor->prefix): "local",
		 spath_area_addr->neighbor? 
		   prefix_toa (spath_area_addr->neighbor->prefix): "local");
	    dont_forward++;
	}
	if (saa->area == spath_area_addr->area &&
		saa->neighbor == spath_area_addr->neighbor)
	    break;
    }

    /* XXX prefix overlap check */

    if (BIT_TEST (spath_area_addr->flags, AREA_ADDR_DELETED)) {
	if (saa && BIT_TEST (saa->flags, AREA_ADDR_DELETED)) {
            hqlip_trace_area_addr (TR_PACKET, neighbor->trace, 
			      "recv (dup)", my_area, spath_area_addr);
	}
	else if (saa == NULL) {
            hqlip_trace_area_addr (TR_PACKET, neighbor->trace, 
			      "recv (none)", my_area, spath_area_addr);
	}
	else {
            hqlip_trace_area_addr (TR_PACKET, neighbor->trace, 
			      "recv (del)", my_area, spath_area_addr);
	    LL_Remove (my_area->ll_spath_area_addrs, saa);
	    destroy_spath_area_addr (saa);
	    if (!dont_forward)
	        hqlip_forward_message (hqlip, my_area, neighbor, flags, 
				HQLIP_MSG_AREA_ADDR, my_area->area->level, 
				len, tstamp, begin);
	}
	destroy_spath_area_addr (spath_area_addr);
	return;
    }

    if (saa == NULL) {
	hqlip_trace_area_addr (TR_PACKET, neighbor->trace,
                                  "recv (new)", my_area, spath_area_addr);  
	LL_Add (my_area->ll_spath_area_addrs, spath_area_addr);
	if (!dont_forward)
	    hqlip_forward_message (hqlip, my_area, neighbor, flags, 
				HQLIP_MSG_AREA_ADDR, my_area->area->level, 
				len, tstamp, begin);
    }
    else {
	if (tstamp > saa->tstamp ||
		BIT_TEST (saa->flags, AREA_ADDR_DELETED)) {
	    /* newer */
	    hqlip_trace_area_addr (TR_PACKET, neighbor->trace,
                                  "recv (update)", my_area, spath_area_addr);  
	    LL_Remove (my_area->ll_spath_area_addrs, saa);
	    destroy_spath_area_addr (saa);
	    LL_Add (my_area->ll_spath_area_addrs, spath_area_addr);
	    hqlip_forward_message (hqlip, my_area, neighbor, flags, 
				HQLIP_MSG_AREA_ADDR, my_area->area->level, 
				len, tstamp, begin);
	}
	else {
	    hqlip_trace_area_addr (TR_PACKET, neighbor->trace,
                                   "recv (ignore)", my_area, spath_area_addr);  
	    destroy_spath_area_addr (spath_area_addr);
	}
    }
}


static int
hqlip_read (hqlip_neighbor_t *neighbor, u_char *ptr, int len)
{
    int n;

    if (neighbor->sockfd < 0)
	return (-1);
    n = read (neighbor->sockfd, ptr, len);

    if (n < 0) {

	switch (errno) {
        case EWOULDBLOCK:
#if     defined(EAGAIN) && EAGAIN != EWOULDBLOCK
        case EAGAIN:
#endif  /* EAGAIN */
        case EINTR:
        case ENETUNREACH:
        case EHOSTUNREACH:
	    trace (TR_INFO, neighbor->trace, 
		   "READ FAILED (%m) -- OKAY TO IGNORE\n");
	    return (0);
	default:
	    trace (TR_WARN, neighbor->trace, "READ FAILED (%m)\n");
	    return (-1);
	}
    }
    else if (n == 0) {
	trace (TR_WARN, neighbor->trace, "READ FAILED EOF???\n");
	return (-1);
    }

    trace (TR_PACKET, neighbor->trace, "read %d bytes\n", n);
    return (n);
}


static int
hqlip_fill_packet (hqlip_neighbor_t *neighbor)
{
    int len, n;

    assert (neighbor);
    assert (neighbor->read_ptr >= neighbor->buffer);
    assert (neighbor->read_ptr <= 
		neighbor->buffer + sizeof (neighbor->buffer));
    assert (neighbor->start_ptr >= neighbor->buffer);
    assert (neighbor->start_ptr <= 
		neighbor->buffer + sizeof (neighbor->buffer));

    if ((len = neighbor->read_ptr - neighbor->start_ptr) == 0) {
	/* reset the pointers */
	neighbor->start_ptr = neighbor->buffer;
	neighbor->read_ptr = neighbor->buffer;
    }

    if (neighbor->buffer + sizeof (neighbor->buffer) - neighbor->read_ptr 
		< HQLIP_MSG_SIZE) {
	/* need to move them to the start to get more */
	memcpy (neighbor->buffer, neighbor->start_ptr, len);
	neighbor->start_ptr = neighbor->buffer;
	neighbor->read_ptr = neighbor->buffer + len;
    }

    if ((n = hqlip_read (neighbor, neighbor->read_ptr, HQLIP_MSG_SIZE)) < 0) {
	return (-1);
    }
    else if (n == 0) {
	return (0);
    }

    neighbor->read_ptr += n;
    assert (neighbor->read_ptr <= 
		neighbor->buffer + sizeof (neighbor->buffer));
    return (len + n);
}


static int
hqlip_get_packet (hqlip_neighbor_t *neighbor)
{
    int pdu_len, len;
    u_char *cp;

    neighbor->packet = NULL;

    /* need to be filled at least a header in buffer */
    /* check if the requested length of data already in buffer */
    if ((len = neighbor->read_ptr - neighbor->start_ptr) 
		< HQLIP_MSG_HDR_SIZE) {
	return (0);
    }

    cp = neighbor->start_ptr;
    HQLIP_PEEK_HDRLEN (pdu_len, cp);
    if (pdu_len < HQLIP_MSG_HDR_SIZE || pdu_len > HQLIP_MSG_SIZE) {
        neighbor->start_ptr = neighbor->read_ptr; /* eat up the input */
	trace (TR_WARN, neighbor->trace, "wrong message size %d\n", pdu_len);
	return (-1);
    }

    /* see if the total length packet in buffer */
    /* check if the requested length of data already in buffer */
    if (len < pdu_len) {
	return (0);
    }

    neighbor->packet = neighbor->start_ptr;
    neighbor->start_ptr += pdu_len;
    return (1);
}


static int
hqlip_get_pdu (hqlip_t *hqlip, hqlip_neighbor_t * neighbor)
{
    int ret;

    /* I know that the return value will not be used, but leave as it was */

    if ((ret = hqlip_fill_packet (neighbor)) < 0) {
	hqlip_neighbor_down (hqlip, neighbor);
	return (-1);
    }
    else if (ret == 0) {
        assert (neighbor->sockfd >= 0);
	select_enable_fd_mask (neighbor->sockfd, SELECT_READ);
	return (ret);
    }

    for (;;) {

        if ((ret = hqlip_get_packet (neighbor)) < 0) {
	    hqlip_neighbor_down (hqlip, neighbor);
	    return (-1);
        }
        else if (ret == 0) {
	    break;
        }
   
        if ((ret = hqlip_process_pdu (hqlip, neighbor)) < 0) {
	    hqlip_neighbor_down (hqlip, neighbor);
	    return (-1);
        }
    }

    if (neighbor->sockfd >= 0)
	select_enable_fd_mask (neighbor->sockfd, SELECT_READ);
    return (1);
}


static void
hqlip_connect_ready (hqlip_t *hqlip, hqlip_neighbor_t *neighbor)
{
    sockunion_t name;
    int namelen = sizeof (name);
#ifdef FIONBIO
    int optval = 0;
#endif /* FIONBIO */

    BIT_RESET (neighbor->flags, HQLIP_OPEN_IN_PROGRESS);

    if (neighbor->sockfd < 0) {
	trace (TR_WARN, neighbor->trace,
	       "connect to %a succeeded but sockfd has been closed\n",
	        neighbor->prefix);
	return;
    }

    /* see if we are really connected */
    if (getpeername (neighbor->sockfd, (struct sockaddr *) &name, 
			&namelen) < 0) {
	trace (TR_INFO, neighbor->trace,
	       "connect to %a failed (%m)\n", neighbor->prefix);
        hqlip_neighbor_down (hqlip, neighbor);
	return;
    }

    trace (TR_INFO, neighbor->trace, "outgoing connection succeeded\n");

#ifndef HAVE_LIBPTHREAD
    socket_set_nonblocking (neighbor->sockfd, 0);
#endif /* HAVE_LIBPTHREAD */
    hqlip_neighbor_start (hqlip, neighbor);
}


static int
hqlip_tcp_connect (hqlip_t *hqlip, hqlip_neighbor_t *neighbor,
		   prefix_t *local)
{
    int ret, port = HQLIP_TCP_PORT;
    int family, len;
    sockunion_t anyaddr;

    memset (&anyaddr, 0, sizeof (anyaddr));
    /* initiate a TCP connection */
    family = neighbor->prefix->family;
#ifdef HAVE_IPV6
    if (family == AF_INET6) {
	anyaddr.sin6.sin6_family = family;
	anyaddr.sin6.sin6_port = htons (port);
	memcpy (&anyaddr.sin6.sin6_addr, prefix_tochar (neighbor->prefix), 16);
	len = sizeof (anyaddr.sin6);
    }
    else
#endif /* HAVE_IPV6 */
    {
	anyaddr.sin.sin_family = family;
	anyaddr.sin.sin_port = htons (port);
	memcpy (&anyaddr.sin.sin_addr, prefix_tochar (neighbor->prefix), 4);
	len = sizeof (anyaddr.sin);
    }

    if ((neighbor->sockfd = socket (family, SOCK_STREAM, 0)) < 0) {
	trace (TR_ERROR, hqlip->trace, "socket open failed (%m)\n");
	return (-1);
    }

    if (local) {
	/* port will not be bound */
	if (socket_bind_port (neighbor->sockfd, family, 
			      prefix_tochar (local), 0) < 0 ) {
	    return (-1);
	}
    }

#ifndef HAVE_LIBPTHREAD
    /* always non-blocking. 
      if connect doesn't return, there is no way to resume it. */
    socket_set_nonblocking (neighbor->sockfd, 1);
#endif /* HAVE_LIBPTHREAD */

    BIT_SET (neighbor->flags, HQLIP_OPEN_IN_PROGRESS);
    trace (TR_TRACE, neighbor->trace,
	   "initiating connect to %a on sockfd %d\n",
	    neighbor->prefix, neighbor->sockfd);
    ret = connect (neighbor->sockfd, (struct sockaddr *)& anyaddr, len);
    if (ret < 0) {
	if (errno != EINPROGRESS) {
	    /* wait open timeout to delete the neighbor */
	    return (-1);
	}
	trace (TR_PACKET, hqlip->trace, "waiting on %d for write\n",
	       neighbor->sockfd);
	select_add_fd_event ("hqlip_connect_ready", neighbor->sockfd, 
			     SELECT_WRITE, TRUE, 
			     neighbor->schedule, hqlip_connect_ready, 
			     2, hqlip, neighbor);
	return (0);
    }
    hqlip_connect_ready (hqlip, neighbor);
    return (1);
}


static void
hqlip_neighbor_area_down (hqlip_t *hqlip, my_area_t *my_area,
			  hqlip_neighbor_t *neighbor)
{
    spath_link_qos_t *spath_link_qos;
    spath_area_center_t *spath_area_center;
    spath_area_addr_t *spath_area_addr;
    time_t now;

    if (my_area->parent != NULL) {
	/* starting with highest level */
	hqlip_neighbor_area_down (hqlip, my_area->parent, neighbor);
    }

    LL_Remove (my_area->ll_neighbors, neighbor);
    trace (TR_TRACE, neighbor->trace, 
   	"neighbor %a on %s removed from %s at %d\n", 
    	neighbor->prefix, neighbor->vif->interface->name,
	my_area->name, my_area->area->level);

    time (&now);
    BIT_RESET (my_area->flags, HQLIP_MY_AREA_SYNCING);
    LL_Iterate (my_area->ll_spath_link_qoses, spath_link_qos) {
	if (spath_link_qos->neighbor == neighbor) {
	    if (!BIT_TEST (spath_link_qos->flags, LINK_QOS_DELETED)) {
		time_t *utimep = New (time_t);
	        BIT_SET (spath_link_qos->flags, LINK_QOS_DELETED);
	        BIT_SET (spath_link_qos->flags, LINK_QOS_CHANGED);
    		hqlip_trace_link_qos (TR_PACKET, neighbor->trace, 
			  "deleted", my_area, spath_link_qos);
		hqlip_send_link_qos (hqlip, my_area, NULL,
				     spath_link_qos);
		*utimep = now;
		LL_Add2 (spath_link_qos->ll_bad_updates, utimep);
		spath_link_qos->ktime = 0;
    	        hqlip_trace_link_qos (TR_TRACE, my_area->trace, 
			"registered as worse", my_area, spath_link_qos);
	    }
	}
    }
    LL_Iterate (my_area->ll_spath_area_centers, spath_area_center) {
	if (spath_area_center->neighbor == neighbor) {
	    if (!BIT_TEST (spath_area_center->flags, 
			   AREA_CENTER_DELETED)) {
	        BIT_SET (spath_area_center->flags, AREA_CENTER_DELETED);
	        BIT_SET (spath_area_center->flags, AREA_CENTER_CHANGED);
    		hqlip_trace_area_center (TR_PACKET, neighbor->trace, 
			  "deleted", my_area, spath_area_center);
		hqlip_send_area_center (hqlip, my_area, NULL,
				        spath_area_center);
		if (my_area->winner == spath_area_center)
		    hqlip_update_area_center (hqlip, my_area, 
					      spath_area_center);
	    }
	}
    }
    LL_Iterate (my_area->ll_spath_area_addrs, spath_area_addr) {
	if (spath_area_addr->neighbor == neighbor) {
	    if (!BIT_TEST (spath_area_addr->flags, 
			   AREA_ADDR_DELETED)) {
	        BIT_SET (spath_area_addr->flags, AREA_ADDR_DELETED);
	        BIT_SET (spath_area_addr->flags, AREA_ADDR_CHANGED);
    		hqlip_trace_area_addr (TR_PACKET, neighbor->trace, 
			  "deleted", my_area, spath_area_addr);
		hqlip_send_area_addr (hqlip, my_area, NULL,
				      spath_area_addr);
	    }
	}
    }
    hqlip_update_database (hqlip, my_area);
    BIT_RESET (neighbor->flags, HQLIP_NEIGHBOR_CONNECTED);
}


static void
hqlip_neighbor_down (hqlip_t *hqlip, hqlip_neighbor_t *neighbor)
{
    if (BIT_TEST (neighbor->flags, HQLIP_NEIGHBOR_DELETED))
	return;

    if (BIT_TEST (neighbor->flags, HQLIP_NEIGHBOR_CONNECTED)) {
        hqlip_interface_t *vif = neighbor->vif;
        spath_link_qos_t *spath_link_qos;

	hqlip_neighbor_area_down (hqlip, vif->my_area0->parent, neighbor);

	spath_link_qos = spath_link_qos_create (hqlip, /* reversed */
                                add_area (0, neighbor->prefix), 
				vif->my_area0->area,
                                vif->metric, NULL, 0);     
    	/* spath_link_qos = neighbor->spath_link_qos; */
	BIT_SET (spath_link_qos->flags, LINK_QOS_DELETED);
        hqlip_inject_spath_link_qos (hqlip, vif->my_area0->parent, 
				     spath_link_qos);
    }
    trace (TR_WARN, neighbor->trace, 
	   "neighbor %a on %s going down\n", 
	    neighbor->prefix, neighbor->vif->interface->name);
    time (&neighbor->utime);
    BIT_SET (neighbor->flags, HQLIP_NEIGHBOR_DELETED);
    Timer_Turn_OFF (neighbor->timeout);
    Timer_Turn_OFF (neighbor->keep_alive);
    if (neighbor->sockfd >= 0) {
        BIT_RESET (neighbor->flags, HQLIP_OPEN_IN_PROGRESS);
	select_delete_fd (neighbor->sockfd);
        neighbor->sockfd = -1;
    }
#ifndef HAVE_LIBPTHREAD
    LL_Clear (neighbor->send_queue);
#endif /* HAVE_LIBPTHREAD */
    LL_Clear (neighbor->ll_packets);
    neighbor->read_ptr = neighbor->buffer;
    neighbor->start_ptr = neighbor->buffer;
    clear_schedule (neighbor->schedule);

}


static void
hqlip_timeout_neighbor (hqlip_t *hqlip, hqlip_neighbor_t *neighbor)
{
   /* since timeout event is queued, it may happen after deletion is done.
      so the info remains and reused later */
    if (BIT_TEST (neighbor->flags, HQLIP_NEIGHBOR_DELETED))
	return;
    trace (TR_WARN, neighbor->trace, 
	   "neighbor timeout on %s\n", neighbor->vif->interface->name);
    hqlip_neighbor_down (hqlip, neighbor);
}


static hqlip_neighbor_t *
hqlip_register_neighbor (hqlip_t *hqlip, prefix_t *prefix, 
		         hqlip_interface_t *vif)
{
    hqlip_neighbor_t *neighbor;
    char name[64];
    interface_t *interface = vif->interface;

    assert (hqlip);
    assert (prefix);

    LL_Iterate (vif->ll_neighbors, neighbor) {
	if (address_equal (neighbor->prefix, prefix)) {
    	    if (BIT_TEST (neighbor->flags, HQLIP_NEIGHBOR_DELETED)) {
    	        BIT_RESET (neighbor->flags, HQLIP_NEIGHBOR_DELETED);
    		trace (TR_INFO, neighbor->trace, 
	   		"neighbor recovered on %s\n", 
			neighbor->vif->interface->name);
	    }
	    assert (neighbor->vif->interface == interface);
	    if (neighbor->sockfd < 0)
		goto open_tcp_connection;
	    return (neighbor);
	}
    }
    neighbor = New (hqlip_neighbor_t);
    neighbor->prefix = Ref_Prefix (prefix);
    neighbor->trace = trace_copy (hqlip->trace);
    sprintf (name, "HQLIP %s", prefix_toa (prefix));
    set_trace (neighbor->trace, TRACE_PREPEND_STRING, name, 0);
    neighbor->sockfd = -1;
    neighbor->vif = vif;
    neighbor->flags = 0;
#ifndef HAVE_LIBPTHREAD
    neighbor->send_queue = LL_Create (LL_DestroyFunction, destroy_packet, 0);
#endif /* HAVE_LIBPTHREAD */
    neighbor->ll_packets = LL_Create (LL_DestroyFunction, Destroy,
				LL_CompareFunction, hqlip_spath_compare,
				LL_AutoSort, True, 0);
    neighbor->read_ptr = neighbor->buffer;
    neighbor->start_ptr = neighbor->buffer;
    time (&neighbor->ctime);
    time (&neighbor->utime);
    neighbor->keep_alive = New_Timer2 ("HQLIP keep alive timer",
                                vif->keep_alive_interval, 0,
                                hqlip->schedule, hqlip_send_keep_alive, 
				2, hqlip, neighbor);
    neighbor->timeout = New_Timer2 ("HQLIP neighbor timeout",
                HQLIP_KEEPALIVE_TIMEOUT (vif->keep_alive_interval),
				TIMER_ONE_SHOT, hqlip->schedule,
                                hqlip_timeout_neighbor, 2, hqlip, neighbor);
    neighbor->schedule  = New_Schedule (name, neighbor->trace);
    mrt_thread_create2 (name, neighbor->schedule, NULL, NULL);
    LL_Add2 (vif->ll_neighbors, neighbor);
    if (vif->prefix) {
	if (address_equal (prefix, vif->prefix)) {
	    /* loop back */
	    vif->myself = neighbor;
	}
    }
    else if (is_prefix_local_on (prefix, interface)) {
	/* loop back */
	vif->myself = neighbor;
    }
    trace (TR_INFO, neighbor->trace, 
	   "neighbor created on %s\n", neighbor->vif->interface->name);

open_tcp_connection:
    /* I need to know my address to compare */
    if (vif->prefix == NULL) {
	return (neighbor);
    }

    /* tcp connection must initiate from small to big */
    if (prefix_compare_wolen (prefix, vif->prefix) > 0) {
	hqlip_tcp_connect (hqlip, neighbor, vif->prefix);
	/* neighbor timeout is for both open timeout and keep alive timeout */
        Timer_Reset_Time (neighbor->timeout);
        Timer_Turn_ON (neighbor->timeout);
    }
    srsvp_create_neighbor (NULL, prefix, interface);
    return (neighbor);
}


static void
hqlip_recv_hello (hqlip_t *hqlip, int sockfd)
{
    u_char buffer[HQLIP_UDP_SIZE];
    prefix_t *source = NULL;
    hqlip_interface_t *vif;
    interface_t *interface = NULL;
    int len, ttl = 1;
    int sport = HQLIP_UDP_PORT;
    int plen = 4;

    if (!hqlip->running)
	return;

    if (sockfd < 0)
	return;

    len = recvmsgfrom (sockfd, buffer, sizeof (buffer), O_NONBLOCK,
                       &source, &sport, &interface, NULL, &ttl);
    select_enable_fd_mask (sockfd, SELECT_READ);
    if (source == NULL)
	return;
    if (interface == NULL) {
	Deref_Prefix (source);
	return;
    }

    if (!BITX_TEST (&hqlip->interface_mask, interface->index)) {
        trace (TR_WARN, hqlip->trace,
               "recv hello from %a on disabled interface %s\n",
               source, interface->name);
	Deref_Prefix (source);
        return;
    }   

    vif = hqlip->hqlip_interfaces[interface->index];
    assert (vif);

    if (sockfd != hqlip->udp_sockfd) {
        trace (TR_WARN, hqlip->trace,
               "recv hello from %a on %s "
	       "with vif sockfd %d (bug report)\n",
               source, interface->name, sockfd);
	Deref_Prefix (source);
	return;
    }

    if (source->family != hqlip->family) {
        trace (TR_WARN, hqlip->trace,
               "recv hello from %a on %s but family must be %s\n",
               source, interface->name, family2string (hqlip->family));
	Deref_Prefix (source);
	return;
    }

    if (sport != HQLIP_UDP_PORT) {
        trace (TR_PACKET, hqlip->trace,
               "recv hello from %a on %s with port %d but must be %d\n",
               source, interface->name, sport, HQLIP_UDP_PORT);
	Deref_Prefix (source);
	return;
    }
    if (ttl != HQLIP_HELLO_TTL) {
        trace (TR_WARN, hqlip->trace,
               "recv hello from %a on %s with port %d but must be %d\n",
               source, interface->name, ttl, HQLIP_HELLO_TTL);
	Deref_Prefix (source);
	return;
    }
#ifdef HAVE_IPV6
    if (hqlip->family == AF_INET6)
	plen = 16;
#endif /* HAVE_IPV6 */
    /* XXXXXX */
    if (len != 0 && len != plen) {
        trace (TR_WARN, hqlip->trace,
               "recv hello from %a on %s with len %d but must be 0 or %d\n",
               source, interface->name, len, plen);
	Deref_Prefix (source);
	return;
    }
    trace (TR_PACKET, hqlip->trace,
           "hello packet received from %a on %s len %d\n",
           source, interface->name, len);
#ifdef notdef
    schedule_event2 ("hqlip_accept_hello",
                 hqlip->schedule, hqlip_accept_hello, 3, 
		 hqlip, Ref_Prefix (source), vif);
#endif
    hqlip_register_neighbor (hqlip, source, vif);
    Deref_Prefix (source);
}


static void
hqlip_open_accept (hqlip_t *hqlip, hqlip_interface_t *vif)
{
    int new_sockfd, len;
    sockunion_t remote;
    prefix_t *local_prefix = NULL, *remote_prefix;
    hqlip_neighbor_t *neighbor;
    interface_t *interface = vif->interface;
    int sockfd = vif->tcp_sockfd;
   
    if (sockfd < 0)
	return;

    len = sizeof (remote);
    if ((new_sockfd = accept (sockfd,
            (struct sockaddr *) &remote, &len)) < 0) {
        trace (TR_ERROR, hqlip->trace, "accept (%m)\n");
        select_enable_fd_mask (sockfd, SELECT_READ);
        return; 
    }
    select_enable_fd_mask (sockfd, SELECT_READ);

    remote_prefix = sockaddr_toprefix ((struct sockaddr *) &remote);
    if (remote_prefix->family != hqlip->family) {
        trace (TR_WARN, hqlip->trace,
               "recv open from %a on %s but family must be %s\n",
               remote_prefix, vif->interface->name, 
	       family2string (hqlip->family));
	Deref_Prefix (remote_prefix);
        close (new_sockfd); 
	return;
    }

    if (!BITX_TEST (&hqlip->interface_mask, interface->index)) {
	/* must not happen */
        trace (TR_ERROR, hqlip->trace, 
	       "connection from %a on %s refused (interface disabled)\n",
	        remote_prefix, interface->name);
	Deref_Prefix (remote_prefix);
        close (new_sockfd); 
        return;
    }

    if (get_socket_addr (sockfd, 0, &local_prefix) < 0) {
        trace (TR_ERROR, hqlip->trace, "getsockname (%m)\n");
	Deref_Prefix (remote_prefix);
        close (new_sockfd); 
        return;
    }
    assert (local_prefix);

    if (local_prefix == NULL || prefix_is_unspecified (local_prefix)) {
	Deref_Prefix (local_prefix);
	local_prefix = Ref_Prefix (vif->prefix);
    }
    else {
        if (!address_equal (vif->prefix, local_prefix)) {
            trace (TR_ERROR, hqlip->trace, 
	           "connection from %a to %a on %s refused "
			"(local address unknown)\n",
		    remote_prefix, local_prefix, interface->name);
	    Deref_Prefix (local_prefix);
	    Deref_Prefix (remote_prefix);
            close (new_sockfd); 
            return;
	}
    }

    if (remote_prefix->family != local_prefix->family) {
        trace (TR_ERROR, hqlip->trace, 
	       "connection from %a to %a on %s refused (family mismatch)\n",
	        remote_prefix, local_prefix, interface->name);
	Deref_Prefix (local_prefix);
	Deref_Prefix (remote_prefix);
        close (new_sockfd); 
        return;
    }
    if (address_equal (remote_prefix, local_prefix)) {
        trace (TR_ERROR, hqlip->trace, 
	       "connection from %a to %a on %s refused (myself)\n",
		remote_prefix, local_prefix, interface->name);
	Deref_Prefix (local_prefix);
	Deref_Prefix (remote_prefix);
        close (new_sockfd); 
        return;
    }

    /* tcp connection must initiate from small to big */
    if (prefix_compare_wolen (remote_prefix, local_prefix) >= 0) {
        trace (TR_ERROR, hqlip->trace, 
	       "connection from %a to %a on %s refused (reverse direction)\n",
		remote_prefix, local_prefix, interface->name);
	Deref_Prefix (local_prefix);
	Deref_Prefix (remote_prefix);
        close (new_sockfd); 
        return;
    }

    LL_Iterate (vif->ll_neighbors, neighbor) {
	if (address_equal (neighbor->prefix, remote_prefix))
	    break;
    }
    if (neighbor == NULL) {
	neighbor = hqlip_register_neighbor (hqlip, remote_prefix, vif);
    }
    else if (neighbor->sockfd >= 0) {
        trace (TR_ERROR, hqlip->trace, 
	       "connection from %a to %a on %s refused (already connected)\n",
		remote_prefix, local_prefix, interface->name);
	Deref_Prefix (local_prefix);
	Deref_Prefix (remote_prefix);
        close (new_sockfd); 
        return;
    }

    trace (TR_INFO, hqlip->trace, 
	       "connection from %a to %a on %s accepted\n",
		remote_prefix, local_prefix, interface->name);
    neighbor->sockfd = new_sockfd;

    hqlip_neighbor_start (hqlip, neighbor);
    Deref_Prefix (local_prefix);
    Deref_Prefix (remote_prefix);
}


void 
hqlip_start (hqlip_t *hqlip)
{
    int level;

    /* initialize from the lowest level */
    for (level = 1; level <= HQLIP_AREA_LEVEL_INTERNET; level++) {
	hqlip_update_link_qos (hqlip, hqlip->root, level);
    }
    hqlip->running = 1;
}


void
hqlip_init (ricd_t *ricd)
{
    char *all_hosts = "224.0.0.1";
    char *name = "HQLIP";

#ifdef HAVE_IPV6
    if (ricd->family == AF_INET6) {
	/* to force the source to be a global */
	/* but ok since the ttl will be 1 */
        all_hosts = "ff0f::1";
        name = "HQLIP6";
    }
#endif /* HAVE_IPV6 */

    ricd->hqlip = New (hqlip_t);
    ricd->hqlip->trace = trace_copy (ricd->trace);
    set_trace (ricd->hqlip->trace, TRACE_PREPEND_STRING, name, 0);
    ricd->hqlip->family = ricd->family;
    ricd->hqlip->all_hosts = ascii2prefix (ricd->family, all_hosts);
    ricd->hqlip->ll_networks = LL_Create (0);
    ricd->hqlip->ll_areas = LL_Create (0);
    ricd->hqlip->ll_hqlip_interfaces = LL_Create (0);
    ricd->hqlip->keep_alive_interval = HQLIP_KEEPALIVE_INTERVAL;
    /* ricd->hqlip->router_id = MRT->default_id; */
    memset (&ricd->hqlip->interface_mask, 0, 
		sizeof (ricd->hqlip->interface_mask));
    ricd->hqlip->schedule  = New_Schedule (name, ricd->trace);
    mrt_thread_create2 (name, ricd->hqlip->schedule, NULL, NULL);
}


static int
hqlip_vif_udp_init (hqlip_t *hqlip, hqlip_interface_t *vif)
{
    int sockfd;
    u_char *bind_addr = (vif->prefix)? prefix_tochar (vif->prefix): NULL;

    if (hqlip->udp_count <= 0) {
	/* we need to have a socket for receiption since binding an address
	   refuses us from receiving a multicast packet */
	/* we have another sending only socket to bind a prefix in case
	   there are a couple of addresses available on a interface */
        sockfd = socket_open (hqlip->family, SOCK_DGRAM, 0);
        if (sockfd < 0)
	    return (sockfd);
        socket_reuse (sockfd, 1);
        socket_bind_port (sockfd, hqlip->family, NULL, HQLIP_UDP_PORT);
#ifdef HAVE_IPV6
        if (hqlip->family == AF_INET6) {
	    ipv6_pktinfo (sockfd, 1);
	    ipv6_recvhops (sockfd, 1);
        }
        else
#endif /* HAVE_IPV6 */
        {
            ip_pktinfo (sockfd, 1);
	    ip_recvttl (sockfd, 1);
        }
        hqlip->udp_sockfd = sockfd;
	hqlip->udp_count = 1;
        select_add_fd_event ("hqlip_recv_hello", sockfd,
                       	      SELECT_READ, TRUE, hqlip->schedule,
                       	      hqlip_recv_hello, 2, hqlip, sockfd);
    }

    sockfd = socket_open (hqlip->family, SOCK_DGRAM, 0);
    if (sockfd < 0)
	return (sockfd);

    socket_reuse (sockfd, 1);
    /* make sure we are a member since solaris requires it */
    /* not binding with vif's udp socket to make sure it doesn't receive */
    join_leave_group (hqlip->udp_sockfd, vif->interface, hqlip->all_hosts, 1);
    socket_bind_port (sockfd, hqlip->family, bind_addr, HQLIP_UDP_PORT);
#ifdef HAVE_IPV6
    if (hqlip->family == AF_INET6) {
	ipv6_multicast_loop (sockfd, 1); 
	ipv6_multicast_hops (sockfd, HQLIP_HELLO_TTL);
	ipv6_pktinfo (sockfd, 1);
	ipv6_recvhops (sockfd, 1);
    }
    else
#endif /* HAVE_IPV6 */
    {
	ip_multicast_loop (sockfd, 1);
	ip_multicast_hops (sockfd, HQLIP_HELLO_TTL);
        ip_pktinfo (sockfd, 1);
	ip_recvttl (sockfd, 1);
    }
    vif->udp_sockfd = sockfd;

    /* on some system, this is needed ... */
    select_add_fd_event ("hqlip_recv_hello", sockfd,
                       	  SELECT_READ, TRUE, hqlip->schedule,
                       	  hqlip_recv_hello, 2, hqlip, sockfd);
    return (sockfd);
}


static int
hqlip_vif_tcp_init (hqlip_t *hqlip, hqlip_interface_t *vif)
{
    int sockfd;
    u_char *bind_addr = (vif->prefix)? prefix_tochar (vif->prefix): NULL;

    if (vif->tcp_sockfd >= 0) {
	select_delete_fd (vif->tcp_sockfd);
	vif->tcp_sockfd = -1;
    }
    sockfd = socket_open (hqlip->family, SOCK_STREAM, 0);
    if (sockfd < 0)
	return (sockfd);
    socket_reuse (sockfd, 1);
    socket_bind_port (sockfd, hqlip->family, bind_addr, HQLIP_TCP_PORT);
    listen (sockfd, 5);
    vif->tcp_sockfd = sockfd;
    select_add_fd_event ("hqlip_open_accept", sockfd, 
			  SELECT_READ, TRUE, hqlip->schedule, 
			  hqlip_open_accept, 2, hqlip, vif);
    return (sockfd);
}


static void
hqlip_set_vif_prefix (hqlip_t *hqlip, hqlip_interface_t *vif, prefix_t *prefix)
{
    Deref_Prefix (vif->prefix);
    if (prefix == NULL) {
#ifdef HAVE_IPV6
	if (hqlip->family == AF_INET6)
	    vif->prefix = vif->interface->primary6->prefix;
	else
#endif /* HAVE_IPV6 */
        vif->prefix = vif->interface->primary->prefix;
    }
    else {
        vif->prefix = Ref_Prefix (prefix);
    }
}


void
hqlip_activate_interface (hqlip_t *hqlip,
			  hqlip_config_network_t *network, int on,
			  my_area_t *my_area0)
{
    hqlip_interface_t *vif;
    interface_t *interface = network->interface;

    if (on > 0 && BITX_TEST (&hqlip->interface_mask, interface->index)) {
	/* updating network */
	hqlip_neighbor_t *neighbor;
	vif = hqlip->hqlip_interfaces [interface->index];
	assert (vif);

	if (vif->keep_alive_interval != network->keep_alive_interval) {
	    vif->keep_alive_interval = (network->keep_alive_interval >= 0)?
						network->keep_alive_interval: 
						hqlip->keep_alive_interval;
	    LL_Iterate (vif->ll_neighbors, neighbor) {
	        Timer_Set_Time (neighbor->keep_alive, 
				vif->keep_alive_interval);
	        /* XXX need to restart timer to adjust immediately */
	    }
	}
	if (network->prefix && 
		!prefix_equal (vif->prefix, network->prefix)) {
	    /* to bind to a specific address to avoid from receiving 
		all hellos */
	    hqlip_set_vif_prefix (hqlip, vif, network->prefix);
	    hqlip_vif_udp_init (hqlip, vif);
	    hqlip_vif_tcp_init (hqlip, vif);
	}
	if (vif->metric != network->metric) {
	    vif->metric = network->metric;
	    /* XXX */
	}
	if (my_area0) {
	    vif->my_area0 = my_area0;
	    my_area0->vif = vif;
	}

	/* if_qos and link_qos are shared */
    }

    else if (on > 0 && !BITX_TEST (&hqlip->interface_mask, interface->index)) {
	/* new */
	if ((vif = hqlip->hqlip_interfaces [interface->index]) != NULL) {
	    Deref_Prefix (vif->prefix);
	    hqlip_set_vif_prefix (hqlip, vif, network->prefix);
	}
	else {
 	    vif = New (hqlip_interface_t);
            vif->interface = interface;
            vif->ll_neighbors = LL_Create (0);
	    vif->keep_alive_interval = (network->keep_alive_interval >= 0)?
						network->keep_alive_interval: 
						hqlip->keep_alive_interval;
	    vif->udp_sockfd = -1;
	    vif->tcp_sockfd = -1;
	    vif->metric = network->metric;
	    vif->if_qos = (network->config_if_qos)? 
				network->config_if_qos->if_qos: NULL;
	    vif->link_qos = copy_link_qos (network->config_link_qos->link_qos, 
					   NULL);
	    hqlip->hqlip_interfaces [interface->index] = vif;
	    if (my_area0) {
	        vif->my_area0 = my_area0;
	        my_area0->vif = vif;
	    }
	    LL_Add2 (hqlip->ll_hqlip_interfaces, vif);

	    hqlip_set_vif_prefix (hqlip, vif, network->prefix);
	    if (vif->my_area0 && BIT_TEST (interface->flags, IFF_LOOPBACK)) {
		spath_link_qos_t *spath_link_qos;
    		spath_link_qos = spath_link_qos_create (hqlip, /* reversed */
			       my_area0->area, my_area0->area,
			       vif->metric, 
			       copy_link_qos (vif->link_qos, NULL), 0);
    		hqlip_inject_spath_link_qos (hqlip, my_area0->parent, 
					     spath_link_qos);
		srsvp_create_neighbor (NULL, vif->prefix, interface);
	    }
	}
	BITX_SET (&hqlip->interface_mask, interface->index);
	assert (vif->udp_sockfd < 0);
	assert (vif->tcp_sockfd < 0);

	hqlip_vif_udp_init (hqlip, vif);
	hqlip_vif_tcp_init (hqlip, vif);

	hqlip_send_hello (hqlip, vif);
        vif->probe = New_Timer2 ("HQLIP hello timer", 
			       HQLIP_HELLO_INTERVAL, 0,
                               hqlip->schedule, 
			       (event_fn_t) hqlip_send_hello, 
			       2, hqlip, vif);
        timer_set_jitter (vif->probe, HQLIP_HELLO_JITTER);
    	Timer_Turn_ON (vif->probe);
    }
    else if (on < 0 && BITX_TEST (&hqlip->interface_mask, interface->index)) {
	vif = hqlip->hqlip_interfaces [interface->index];
	assert (vif);
	if (vif->my_area0 && BIT_TEST (interface->flags, IFF_LOOPBACK)) {
	    spath_link_qos_t *spath_link_qos;
    	    spath_link_qos = spath_link_qos_create (hqlip, /* reversed */
			       my_area0->area, my_area0->area,
			       vif->metric, 
			       copy_link_qos (vif->link_qos, NULL), 0);
	    BIT_SET (spath_link_qos->flags, LINK_QOS_DELETED);
    	    hqlip_inject_spath_link_qos (hqlip, my_area0->parent, 
					 spath_link_qos);
	}
	BITX_RESET (&hqlip->interface_mask, interface->index);
        Timer_Turn_OFF (vif->probe);
	if (vif->udp_sockfd >= 0) {
	    join_leave_group (hqlip->udp_sockfd, vif->interface, 
			      hqlip->all_hosts, 0);
	    select_delete_fd (vif->udp_sockfd);
	    vif->udp_sockfd = -1;
	}
	if (vif->tcp_sockfd >= 0) {
	/* interface_mask was off, hello timer off 
		so that gracefully stopping by protocol timeout */
	    select_delete_fd (vif->tcp_sockfd);
	    vif->tcp_sockfd = -1;
	}
        if (hqlip->udp_count++ <= 1) {
	    select_delete_fd (hqlip->udp_sockfd);
	    hqlip->udp_sockfd = -1;
	    hqlip->udp_count = 0;
        }
	Deref_Prefix (vif->prefix);
	vif->prefix = NULL;
    }
}


static link_qos_t *
hqlip_add_link_qos (link_qos_t *a, link_qos_t *b)
{
    a->flag = 0;
    a->loh = 0;
    /* XXX pps == 0 is considered as infinity */
    if (a->pps == 0 || a->pps > b->pps)
        a->pps = b->pps;
    a->dly += b->dly;
    return (a);
}


static void
hqlip_show_path_sub (uii_connection_t *uii, hqlip_t *hqlip, 
		     my_area_t *my_area, prefix_t *p1, prefix_t *p2,
		     link_qos_t *link_qos, int *metric)
{
    spath_area_addr_t *spath_area_addr;
    area_t *area1 = NULL, *area2 = NULL;
    assert (p1 != NULL || p2 != NULL);

    if (my_area->area->level <= 0) {
	uii_add_bulk_output (uii, 
		"at %s (%d:%a) terminated\n",
		my_area->name, my_area->area->level, my_area->area->id);
        link_qos->flag = 0;
        link_qos->pri = SSPEC_PRI_USER;
        link_qos->loh = 0;
        link_qos->pps = 999999 /* XXX */;
        link_qos->dly = 0;
	*metric = 0;
	return;
    }

    LL_Iterate (my_area->ll_spath_area_addrs, spath_area_addr) {
	prefix_t *prefix;
	if (BIT_TEST (spath_area_addr->flags, AREA_ADDR_DELETED))
	    continue;
	LL_Iterate (spath_area_addr->ll_prefixes, prefix) {
	    if (p1 && a_include_b (prefix, p1))
		area1 = spath_area_addr->area;
	    if (p2 && a_include_b (prefix, p2))
		area2 = spath_area_addr->area;
	    if ((p1 == NULL || area1) && (p2 == NULL || area2))
		break;
	}
    }

	if ((p1 && area1 == NULL) && (p2 && area2 == NULL)) {
	    uii_add_bulk_output (uii, 
		"at %s (%d:%a) both p1 %p and p2 %p not found\n",
		my_area->name, my_area->area->level, my_area->area->id,
		p1, p2);
	    return;
	}
	if (p1 && area1 == NULL) {
	    uii_add_bulk_output (uii, 
		"at %s (%d:%a) p1 %p not found\n",
		my_area->name, my_area->area->level, my_area->area->id, p1);
	    return;
	}
	if (p2 && area2 == NULL) {
	    uii_add_bulk_output (uii, 
		"at %s (%d:%a) p2 %p not found\n",
		my_area->name, my_area->area->level, my_area->area->id, p2);
	    return;
	}

	if (area1 == area2) {
	    if (area1->my_area == NULL) {
	        uii_add_bulk_output (uii, 
		    "at %s (%d:%a) both p1 %p and p2 %p fall into "
		    "foreign area %d:%a\n",
		    my_area->name, my_area->area->level, my_area->area->id,
		    p1, p2,
		    area1->level, area1->id);
		return;
	    }
	    uii_add_bulk_output (uii, 
		"at %s (%d:%a) going down to %s (%d:%a) for both\n",
		my_area->name, my_area->area->level, my_area->area->id,
		area1->my_area->name,
		area1->my_area->area->level, area1->my_area->area->id);
	    hqlip_show_path_sub (uii, hqlip, area1->my_area, p1, p2,
				 link_qos, metric);
	    return;
	}
	if (!area1->my_area && !area2->my_area) {
	    link_qos_t lqos;
	    int mm = 0;

	    uii_add_bulk_output (uii, 
		    "at %s (%d:%a) both p1 %p and p2 %p fall into "
		    "foreign area %d:%a and %d:%a\n",
		    my_area->name, my_area->area->level, my_area->area->id,
		    p1, p2,
		    area1->level, area1->id,
		    area2->level, area2->id);
    	    if (spath_calc_link_qos (hqlip, my_area, area1, area2, 
				     NULL, &lqos, &mm, NULL) == NULL) {
	        uii_add_bulk_output (uii, 
		    "at %s (%d:%a) path not found for %p -> %p\n",
		    my_area->name, my_area->area->level, my_area->area->id,
		    p1, p2);
	    }
	    else {
	        uii_add_bulk_output (uii, 
		    "at %s (%d:%a) path found for %p -> %p: "
			    "pri %d pps %d dly %d loh %d\n",
		    my_area->name, my_area->area->level, my_area->area->id,
		    p1, p2, lqos.pri, lqos.pps, lqos.dly, lqos.loh);

		hqlip_add_link_qos (link_qos, &lqos);
		*metric += mm;
	    }
	    return;
	}
	if (area1->my_area) {
	    link_qos_t lqos;
	    int mm = 0;
	    memset (&lqos, 0, sizeof (lqos));

	    uii_add_bulk_output (uii, 
		"at %s (%d:%a) going down to %s (%d:%a) for p1\n",
		my_area->name, my_area->area->level, my_area->area->id,
		area1->my_area->name,
		area1->my_area->area->level, area1->my_area->area->id);
	    hqlip_show_path_sub (uii, hqlip, area1->my_area, p1, NULL,
				 &lqos, &mm);
	    hqlip_add_link_qos (link_qos, &lqos);
	    *metric += mm;
	}
	else {
	    link_qos_t lqos;
	    int mm = 0;

	    uii_add_bulk_output (uii, 
		    "at %s (%d:%a) p1 %p falls into foreign area %d:%a\n",
		    my_area->name, my_area->area->level, my_area->area->id,
		    p1, area1->level, area1->id);
	    if (my_area->winner == NULL) {
	        uii_add_bulk_output (uii, 
		    "at %s (%d:%a) no center!\n",
		    my_area->name, my_area->area->level, my_area->area->id);
		return;
	    }
    	    if (spath_calc_link_qos (hqlip, my_area, 
				area1, my_area->winner->area, NULL,
				&lqos, &mm, NULL) == NULL) {
	        uii_add_bulk_output (uii, 
		    "at %s (%d:%a) path not found p1 (%d:%a) -> p2 (%d:%a) "
		    "(center)\n",
		    my_area->name, my_area->area->level, my_area->area->id,
		    area1->level, area1->id, 
		    my_area->winner->area->level, my_area->winner->area->id);
	    }
	    else {
	        uii_add_bulk_output (uii, 
		    "at %s (%d:%a) path found p1 (%d:%a) -> p2 (%d:%a) "
			    " (center) "
			    "pri %d pps %d dly %d loh %d\n",
		    my_area->name, my_area->area->level, my_area->area->id,
		    area1->level, area1->id, 
		    my_area->winner->area->level, my_area->winner->area->id,
		    lqos.pri, lqos.pps, lqos.dly, lqos.loh);
	        hqlip_add_link_qos (link_qos, &lqos);
	        *metric += mm;
	    }
	}
	if (area2->my_area) {
	    link_qos_t lqos;
	    int mm = 0;
	    memset (&lqos, 0, sizeof (lqos));

	    uii_add_bulk_output (uii, 
		"at %s (%d:%a) going down to %s (%d:%a) for p2\n",
		my_area->name, my_area->area->level, my_area->area->id,
		area2->my_area->name,
		area2->my_area->area->level, area2->my_area->area->id);
	    hqlip_show_path_sub (uii, hqlip, area2->my_area, NULL, p2,
				 &lqos, &mm);
	    hqlip_add_link_qos (link_qos, &lqos);
	    *metric += mm;
	}
	else {
	    link_qos_t lqos;
	    int mm = 0;
	    memset (&lqos, 0, sizeof (lqos));

	    uii_add_bulk_output (uii, 
		    "at %s (%d:%a) p2 %p falls into foreign area %d:%a\n",
		    my_area->name, my_area->area->level, my_area->area->id,
		    p2, area2->level, area2->id);
	    if (my_area->winner == NULL) {
	        uii_add_bulk_output (uii, 
		    "at %s (%d:%a) no center!\n",
		    my_area->name, my_area->area->level, my_area->area->id);
		return;
	    }
    	    if (spath_calc_link_qos (hqlip, my_area, 
				my_area->winner->area, area2, NULL,
				&lqos, &mm, NULL) == NULL) {
	        uii_add_bulk_output (uii, 
		    "at %s (%d:%a) path not found p1 (%d:%a) (center) "
		    "-> p2 (%d:%a)\n",
		    my_area->name, my_area->area->level, my_area->area->id,
		    my_area->winner->area->level, my_area->winner->area->id,
		    area2->level, area2->id);
	    }
	    else {
	        uii_add_bulk_output (uii, 
		    "at %s (%d:%a) path found p1 (%d:%a) (center) "
			    "-> p2 (%d:%a) pri %d pps %d dly %d loh %d\n",
		    my_area->name, my_area->area->level, my_area->area->id,
		    my_area->winner->area->level, my_area->winner->area->id,
		    area2->level, area2->id, 
		    lqos.pri, lqos.pps, lqos.dly, lqos.loh);
	        hqlip_add_link_qos (link_qos, &lqos);
	        *metric += mm;
	    }
	}

    if (spath_area_addr == NULL) {
	uii_add_bulk_output (uii, 
		"at %s (%d:%a) both p1 %p and p2 %p not found\n",
		my_area->name, my_area->area->level, my_area->area->id,
		p1, p2);
    }
}


int
hqlip_show_path (uii_connection_t *uii, char *ip, prefix_t *p1, prefix_t *p2)
{
    ricd_t *ricd = RICD;
    hqlip_t *hqlip;
    link_qos_t lqos;
    int mm = 0;

    memset (&lqos, 0, sizeof (lqos));
    if (strcasecmp (ip, "ipv6") == 0)
	ricd = RICD6;
    Delete (ip);

    hqlip = ricd->hqlip;
    hqlip_show_path_sub (uii, hqlip, hqlip->root, p1, p2, &lqos, &mm);
    uii_add_bulk_output (uii, 
	    "%p -> %p pri %d pps %d dly %d loh %d metric %d\n",
	    p1, p2, lqos.pri, lqos.pps, lqos.dly, lqos.loh, mm);
    return (1);
}


int
hqlip_show_neighbors (uii_connection_t *uii, char *ip, char *ifname)
{
    hqlip_interface_t *vif;
    hqlip_neighbor_t *neighbor;
    time_t now;
    interface_t *interface = NULL;
    ricd_t *ricd = RICD;
    hqlip_t *hqlip;

    if (strcasecmp (ip, "ipv6") == 0)
	ricd = RICD6;
    Delete (ip);

    if (ricd == NULL || ricd->hqlip == NULL)
	return (0);
    hqlip = ricd->hqlip;

    if (ifname) {
        interface = find_interface_byname (ifname);
        Delete (ifname);
        if (interface == NULL) {
            return (-1);
        }                            
    }                                   

    time (&now);
    uii_add_bulk_output (uii, "%-35s %7s %8s %3s %11s %11s\n",
                "Neighbor Address", "If", "Time", "UP", "KeepAlive", " Sent/Rcvd ");
    LL_Iterate (hqlip->ll_hqlip_interfaces, vif) {

	if (interface != NULL && interface != vif->interface)
	    continue;

	if (vif->ll_neighbors == NULL || LL_GetCount (vif->ll_neighbors) == 0)
	    continue;

	LL_Iterate (vif->ll_neighbors, neighbor) {
	    char buff[64], strbuf[64] = "";

	    if (vif->myself == neighbor)
	        sprintf (strbuf, " (self)");

	    if (neighbor->sockfd >= 0)
	        sprintf (strbuf, " (connected)");

	    if (!BITX_TEST (&hqlip->interface_mask, vif->interface->index))
	        sprintf (strbuf, " (deleted)");
	        
            uii_add_bulk_output (uii, 
		    "%-35a %7s %8s %3d %5d/%-5d %5d/%-5d %s\n",
		    neighbor->prefix, neighbor->vif->interface->name,
		    time2date (now - neighbor->utime, buff),
		    neighbor->num_session_up,
		    (neighbor->timeout->time_next_fire > 0)?
		    time_left (neighbor->timeout): 0,
		    neighbor->timeout->time_interval,
		    neighbor->num_packets_sent, neighbor->num_packets_recv,
		    strbuf);
	}
    }
    return (1);
}


static void
hqlip_show_sub_areas (uii_connection_t *uii, hqlip_t * hqlip, 
			my_area_t *my_area)
{
    spath_link_qos_t *spath_link_qos;
    spath_area_addr_t *spath_area_addr;
    spath_area_center_t *spath_area_center;

    if (my_area->ll_children) {
	my_area_t *child;
	LL_Iterate (my_area->ll_children, child) {
	    hqlip_show_sub_areas (uii, hqlip, child);
	}
    }

    uii_add_bulk_output (uii, "%-10s %2d:%-41a %-23s",
		my_area->name, my_area->area->level, my_area->area->id,
		(my_area->parent)? my_area->parent->name: "");
    if (my_area->center) {
    	uii_add_bulk_output (uii, " %3d %6d", 
			my_area->center->pri, my_area->pps);
    }
    else {
    	uii_add_bulk_output (uii, " %3s %6s", "", "");
    }
    uii_add_bulk_output (uii, " %5s %3s", "", "");
    if (my_area->ll_prefixes) {
	prefix_t *prefix;
	LL_Iterate (my_area->ll_prefixes, prefix) {
    	    uii_add_bulk_output (uii, " %p", prefix);
	}
    }
    uii_add_bulk_output (uii, "\n");

    LL_Iterate (my_area->ll_spath_link_qoses, spath_link_qos) {
	link_qos_t *link_qos;

    if (spath_link_qos->ll_link_qoses)
	LL_Iterate (spath_link_qos->ll_link_qoses, link_qos) {

    	    uii_add_bulk_output (uii, "%2s%s %-6s", 
	        BIT_TEST (spath_link_qos->flags, LINK_QOS_DELETED)? " D": "",
		BIT_TEST (spath_link_qos->flags, LINK_QOS_EXTERNAL)? "E": "I",
			"LINK");

    	    uii_add_bulk_output (uii, " %2d:%-17a -> %2d:%-17a",
		spath_link_qos->area1->level, spath_link_qos->area1->id,
		spath_link_qos->area2->level, spath_link_qos->area2->id);

            if (spath_link_qos->neighbor)
    	        uii_add_bulk_output (uii, " %-23a", 
		    spath_link_qos->neighbor->prefix);
	    else
    	        uii_add_bulk_output (uii, " %23s", "");

    	    uii_add_bulk_output (uii, " %3u %6u %5u",
			    link_qos->pri, link_qos->pps, link_qos->dly,
			    link_qos->loh);
	    if (link_qos->flag) 
		uii_add_bulk_output (uii, " %3u", link_qos->loh);
	    else
		uii_add_bulk_output (uii, " %3s", "");

	}
    	uii_add_bulk_output (uii, "\n");
    }
    LL_Iterate (my_area->ll_spath_area_addrs, spath_area_addr) {
	prefix_t *prefix;

            uii_add_bulk_output (uii, "%2s  %-6s", 
	        BIT_TEST (spath_area_addr->flags, AREA_ADDR_DELETED)? " D": "",
			"ADDR");
    	    uii_add_bulk_output (uii, " %2d:%-41a",
		spath_area_addr->area->level, spath_area_addr->area->id);

            if (spath_area_addr->neighbor)
    	        uii_add_bulk_output (uii, " %-23a", 
		    spath_area_addr->neighbor->prefix);
	    else
    	        uii_add_bulk_output (uii, " %23s", "");

    	    uii_add_bulk_output (uii, " %3s %6s %5s %3s", "", "", "", "");

    if (spath_area_addr->ll_prefixes)
	LL_Iterate (spath_area_addr->ll_prefixes, prefix)
    	    uii_add_bulk_output (uii, " %p", prefix);

    	uii_add_bulk_output (uii, "\n");
    }
    LL_Iterate (my_area->ll_spath_area_centers, spath_area_center) {

        uii_add_bulk_output (uii, "%2s%s %-6s", 
	        BIT_TEST (spath_area_center->flags, AREA_CENTER_DELETED)?
			" D": "",
		(my_area->winner == spath_area_center)?">":" ",
			"CENTER");
    	uii_add_bulk_output (uii, " %2d:%-41a",
		spath_area_center->area->level, spath_area_center->area->id);
        if (spath_area_center->neighbor)
    	        uii_add_bulk_output (uii, " %-23a", 
		    spath_area_center->neighbor->prefix);
	    else
    	        uii_add_bulk_output (uii, " %23s", "");

        uii_add_bulk_output (uii, " %3u", spath_area_center->pri);
    	uii_add_bulk_output (uii, " %6s %5s %3s", "", "", "");
    	uii_add_bulk_output (uii, "\n");
    }
}


int
hqlip_show_areas (uii_connection_t *uii, char *ip)
{
    ricd_t *ricd = RICD;
    hqlip_t *hqlip;

    if (strcasecmp (ip, "ipv6") == 0)
	ricd = RICD6;
    Delete (ip);

    if (ricd == NULL || ricd->hqlip == NULL)
	return (0);
    hqlip = ricd->hqlip;

    if (hqlip->ll_areas == NULL)
	return (0);

    uii_add_bulk_output (uii, "%10s %-44s %-23s %3s %5s %6s %3s %s\n",
	"AREA/TYPE", "   AREA ID(S)", "PARENT/FROM", "PRI", "PPS", 
	"DLY", "LOH", "ADDR(S)");
    hqlip_show_sub_areas (uii, hqlip, hqlip->root);
    return (1);
}


static void
hqlip_neighbor_start (hqlip_t *hqlip, hqlip_neighbor_t *neighbor)
{
    hqlip_interface_t *vif;
    my_area_t *my_area1, *my_area0, *my_area;
    spath_link_qos_t *spath_link_qos;

    time (&neighbor->utime);
    neighbor->num_packets_sent = 0;
    neighbor->num_packets_recv = 0;
    neighbor->num_session_up++;
    BIT_SET (neighbor->flags, HQLIP_NEIGHBOR_CONNECTED);
    neighbor->synced_level_bitset = 0;

    vif = neighbor->vif;
    my_area0 = vif->my_area0;
    my_area1 = vif->my_area0->parent;

    /* this has to be done before syncing to avoid duplicate send */
    /* (i,j) means a qos when area i receives from area j */
    spath_link_qos = spath_link_qos_create (hqlip, /* reversed */
			       add_area (0, neighbor->prefix), my_area0->area,
			       vif->metric, 
			       copy_link_qos (vif->link_qos, NULL), 0);
    /* neighbor->spath_link_qos = spath_link_qos; */
    hqlip_inject_spath_link_qos (hqlip, my_area1, spath_link_qos);

    /* mark sync on all applicable areas */
    my_area = my_area1;
    do {
	LL_Add3 (my_area->ll_neighbors, neighbor);
	BIT_SET (my_area->flags, HQLIP_MY_AREA_SYNCING);
    } while ((my_area = my_area->parent) != NULL);

    /* ready, set, go */
    Timer_Turn_ON (neighbor->keep_alive);
    Timer_Turn_ON (neighbor->timeout);
    select_add_fd_event ("hqlip_get_pdu", neighbor->sockfd, SELECT_READ,
                         1 /* on */, neighbor->schedule,
                         (event_fn_t) hqlip_get_pdu, 2, hqlip, neighbor);
#ifndef HAVE_LIBPTHREAD
    select_add_fd_event ("hqlip_flush_queue", neighbor->sockfd, SELECT_WRITE,
                         0 /* off */, neighbor->schedule,
                         (event_fn_t) hqlip_flush_queue, 2, hqlip, neighbor);
#endif /* HAVE_LIBPTHREAD */

    my_area = my_area1;
    do {
        hqlip_send_link_qos (hqlip, my_area, neighbor, NULL);
        hqlip_send_area_center (hqlip, my_area, neighbor, NULL);
        hqlip_send_area_addr (hqlip, my_area, neighbor, NULL);
        /* hqlip_send_area_qos (hqlip, my_area, neighbor, NULL); */
        hqlip_send_sync (hqlip, my_area->area->level, neighbor);
    } while ((my_area = my_area->parent) != NULL);
}


static int
hqlip_spath_compare (u_char *a, u_char *b)
{
    int a_flags, a_type, a_level, a_len, a_tstamp;
    int b_flags, b_type, b_level, b_len, b_tstamp;
    HQLIP_GET_HEADER (a_flags, a_type, a_level, a_len, a_tstamp, a);
    HQLIP_GET_HEADER (b_flags, b_type, b_level, b_len, b_tstamp, b);
    /* area center goes first */
    if (a_type == HQLIP_MSG_AREA_CENTER && b_type != HQLIP_MSG_AREA_CENTER)
	return (-1);
    if (a_type == HQLIP_MSG_LINK_QOS && b_type == HQLIP_MSG_LINK_QOS) {
	/* internal link qos goes first */
	if (!BIT_TEST (a_flags, LINK_QOS_EXTERNAL) &&
		BIT_TEST (b_flags, LINK_QOS_EXTERNAL))
	    return (-1);
    }
    /* don't care others */
    return (a_tstamp - b_tstamp);
}


static int
hqlip_process_pdu (hqlip_t *hqlip, hqlip_neighbor_t *neighbor)
{
    int flags, type, level, len;
    time_t tstamp;
    int error = 0;
    u_char *cp, *packet;
    my_area_t *my_area = NULL;
    hqlip_interface_t *vif;

    assert (neighbor);
    assert (neighbor->packet);
    vif = neighbor->vif;

    neighbor->num_packets_recv++;

    cp = neighbor->packet;
    HQLIP_GET_HEADER (flags, type, level, len, tstamp, cp);

    if (type >= HQLIP_MSG_KEEP_ALIVE && type <= HQLIP_MSG_SYNC) {
        trace (TR_PACKET, neighbor->trace, 
		"recv %s flags 0x%x level %d (%d bytes)\n",
	        hqlip_pdus[type], flags, level, len);
    }

    if (len < HQLIP_MSG_HDR_SIZE) {
        trace (TR_ERROR, neighbor->trace, 
	    "recv a message with too short length %d\n", len);
	return (-1);
    }

    if (level > HQLIP_AREA_LEVEL_INTERNET) {
        trace (TR_ERROR, neighbor->trace, 
	    "recv a message with too large level %d\n", level);
	return (-1);
    }

    switch (type) {
	case HQLIP_MSG_LINK_QOS:
	case HQLIP_MSG_AREA_CENTER:
	case HQLIP_MSG_AREA_ADDR:
	case HQLIP_MSG_AREA_QOS:
	case HQLIP_MSG_SYNC:
	    my_area = vif->my_area0;
	    while (my_area->area->level < level)
	        my_area = my_area->parent;
	    /* must stop at internet */
	    if (my_area->area->level != level) {
		trace (TR_PACKET, neighbor->trace,
	       		"no such a level %d here\n", level);
		return (0);
	    }
	    break;
    }

    switch (type) {
	case HQLIP_MSG_LINK_QOS:
	case HQLIP_MSG_AREA_CENTER:
	case HQLIP_MSG_AREA_ADDR:
	case HQLIP_MSG_AREA_QOS:
	    if (!BITM_TEST (neighbor->synced_level_bitset, level)) {
		packet = NewArray (u_char, len);
		memcpy (packet, neighbor->packet, len);
		LL_Append (neighbor->ll_packets, packet);
		return (0);
	    }
	    break;
    }

    switch (type) {
        case HQLIP_MSG_KEEP_ALIVE:
	    if (len != HQLIP_MSG_HDR_SIZE) {
		error = 1;
		break;
	    }
	    break;
	case HQLIP_MSG_LINK_QOS:
	    hqlip_recv_link_qos (hqlip, neighbor, my_area, flags, 
				 len - HQLIP_MSG_HDR_SIZE, tstamp, cp);
	    break;
	case HQLIP_MSG_AREA_CENTER:
	    hqlip_recv_area_center (hqlip, neighbor, my_area, flags, 
				    len - HQLIP_MSG_HDR_SIZE, tstamp, cp);
	    break;
	case HQLIP_MSG_AREA_ADDR:
	    hqlip_recv_area_addr (hqlip, neighbor, my_area, flags, 
				  len - HQLIP_MSG_HDR_SIZE, tstamp, cp);
	    break;
	case HQLIP_MSG_AREA_QOS:
	    break;
	case HQLIP_MSG_SYNC:
	    if (len != HQLIP_MSG_HDR_SIZE) {
		error = 1;
		break;
	    }
	    if (BITM_TEST (neighbor->synced_level_bitset, level)) {
		trace (TR_ERROR, neighbor->trace,
	       		"duplicate sync at %d\n", level);
	    }
	    LL_Iterate (neighbor->ll_packets, packet) {
		/* we can not change packet since ll_iterate gets confused */
		cp = packet;
    		HQLIP_GET_HEADER (flags, type, level, len, tstamp, cp);
    		switch (type) {
		case HQLIP_MSG_LINK_QOS:
	            hqlip_recv_link_qos (hqlip, neighbor, my_area, flags, 
				         len - HQLIP_MSG_HDR_SIZE, tstamp, cp);
	            break;
	        case HQLIP_MSG_AREA_CENTER:
	            hqlip_recv_area_center (hqlip, neighbor, my_area, flags, 
				    	    len - HQLIP_MSG_HDR_SIZE, tstamp, 
					    cp);
	            break;
		case HQLIP_MSG_AREA_ADDR:
	            hqlip_recv_area_addr (hqlip, neighbor, my_area, flags, 
				          len - HQLIP_MSG_HDR_SIZE, tstamp, 
					  cp);
	            break;
		case HQLIP_MSG_AREA_QOS:
	    	    break;
		default:
		    assert (0);
		    break;
		}
	    }
	    LL_Clear (neighbor->ll_packets);
	    BITM_SET (neighbor->synced_level_bitset, level);
	    /* XXX need to wait to finish sending? */
	    BIT_RESET (my_area->flags, HQLIP_MY_AREA_SYNCING);
    	    hqlip_update_area_center (hqlip, my_area, NULL);
	    /* children couldn't calculate since I'm syncing */
	    if (level > 1) {
    		my_area_t *child;
	        LL_Iterate (my_area->ll_children, child) {
                    hqlip_update_database (hqlip, child);
	        }
	    }
	    break;
	default:
            trace (TR_ERROR, neighbor->trace, 
		"recv unknown message type %d (%d bytes)\n", type, len);
	    return (-1);
    }
    if (error > 0) {
        trace (TR_ERROR, neighbor->trace, 
	    "recv %s with bad length %d\n", hqlip_pdus[type], len);
	return (-1);
    }
    else {
	Timer_Reset_Time (neighbor->timeout);
    }
    return (1);
}


hqlip_neighbor_t *
hqlip_find_nexthop (hqlip_t *hqlip, my_area_t *my_area, prefix_t *destin,
		    req_qos_t *req_qos, link_qos_t *link_qos, int *metric,
		    u_long *errcode)
{
    spath_area_addr_t *spath_area_addr;
    area_t *area2 = NULL;
    hqlip_neighbor_t *neighbor = NULL;

    if (my_area->area->level <= 0) {
	assert (area_is_local (my_area->area));
        link_qos->flag = 0;
        link_qos->pri = (req_qos)? req_qos->pri: SSPEC_PRI_USER;
        link_qos->loh = 0;
        link_qos->pps = (req_qos)? req_qos->pps: 999999 /* XXX */;
        link_qos->dly = 0;
	*metric = 0;
 	*errcode = 0;
	return (my_area->vif->myself);
    }

    LL_Iterate (my_area->ll_spath_area_addrs, spath_area_addr) {
	prefix_t *prefix;
	if (BIT_TEST (spath_area_addr->flags, AREA_ADDR_DELETED))
	    continue;
	LL_Iterate (spath_area_addr->ll_prefixes, prefix) {
	    if (a_include_b (prefix, destin)) {
		area2 = spath_area_addr->area;
		neighbor = spath_area_addr->neighbor;
		break;
	    }
	}
    }

    if (area2 == NULL) {
	*errcode = SRSVP_MSG_ERR_UNREACH;
	return (NULL);
    }
    if (!area_is_local (area2)) {
    	if (spath_calc_link_qos (hqlip, my_area, NULL, area2, req_qos,
				link_qos, metric, errcode) == NULL) {
	    return (NULL);
	}
	return (neighbor);
    }

    return (hqlip_find_nexthop (hqlip, area2->my_area, destin,
		     req_qos, link_qos, metric, errcode));
}


void 
hqlip_link_status (hqlip_t *hqlip, req_qos_t *req_qos, interface_t *interface,
		   int on)
{
    hqlip_interface_t *vif;
    hqlip_neighbor_t *neighbor;
    my_area_t *my_area0, *my_area1;
    spath_link_qos_t *spath_link_qos;

    assert (hqlip);
    assert (req_qos);
    assert (interface);
    vif = hqlip->hqlip_interfaces[interface->index];
    if (vif == NULL) {
        trace (TR_WARN, hqlip->trace, 
		"hqlip_link_status: interface %s not found\n",
		interface->name);
        Destroy (req_qos);
	return;
    }

    my_area0 = vif->my_area0;
    my_area1 = vif->my_area0->parent;

    trace (TR_TRACE, hqlip->trace, 
	  "hqlip_link_status: %s pps %d %s %d\n",
	   interface->name, vif->link_qos->pps, 
	   (on)? "minus": "plus", req_qos->pps);
    if (on) {
	vif->link_qos->pps -= req_qos->pps;
    }
    else {
	vif->link_qos->pps += req_qos->pps;
    }

    LL_Iterate (vif->ll_neighbors, neighbor) {
        if (BIT_TEST (neighbor->flags, HQLIP_NEIGHBOR_DELETED))
	    continue;
        if (!BIT_TEST (neighbor->flags, HQLIP_NEIGHBOR_CONNECTED))
	    continue;

        /* XXX */
        spath_link_qos = spath_link_qos_create (hqlip, /* reversed */
			add_area (0, neighbor->prefix), my_area0->area,
			vif->metric, 
			copy_link_qos (vif->link_qos, NULL), 0);
        hqlip_inject_spath_link_qos (hqlip, my_area1, spath_link_qos);
    }
    Destroy (req_qos);
}
