/*
 *===========================================================================
 *                    ipnet_sysctl_for_each_rtab_cb
 *===========================================================================
 * Description: Dumps all routes entries for every route table.
 * Parameters:  rtab - The route table to dump.
 *              d - The route dump parameters.
 * Returns:
 *
 */
IP_STATIC void
ipnet_sysctl_for_each_rtab_cb(Ipcom_route *rtab, Ipnet_sysctl_route_data *d)
{
    if (d->vr != IPNET_ROUTE_GET_VR(rtab))
        return;
    if (d->domain != 0 && d->domain != IPNET_ROUTE_GET_FAMILY(rtab))
        return;

    d->table = IPNET_ROUTE_GET_TABLE(rtab);
    ipcom_route_walk_tree(rtab,
                          (Ipcom_route_walk_cb) ipnet_sysctl_route_dump_cb,
                          d);
}
/*
 *===========================================================================
 *                    ipnet_rtnetlink_route_foreach_vr
 *===========================================================================
 * Description: Hash table foreach callback. Called once for each virtual
 *              router that exists in the system.
 * Parameters:  rtab  - route table
 *              param - callback parameters.
 * Returns:     -
 *
 */
IP_STATIC void
ipnet_rtnetlink_route_foreach_vr_family(Ipcom_route *rtab, Ipnet_rtnetlink_route_family_t *param)
{
    int                new_offs = 0;
    Ip_ptrdiff_t       table    = IPNET_ROUTE_GET_TABLE(rtab);
    Ip_u16             vr       = IPNET_ROUTE_GET_VR(rtab);
    Ipnet_route_entry  *rt      = IP_NULL;

    /* This traversal is already aborted */
    if (param->state.aborted)
        return;

    /* Only handle rtabs of selected address family */
    if (IPNET_ROUTE_GET_FAMILY(rtab) != param->family)
        return;

    /* Check if specific VR shall be dumped */
    if (IP_BIT_ISFALSE(param->nlmsg->nlmsg_flags, IP_NLM_F_VR_UNSPEC) && (param->mem->vr != IPCOM_VR_ANY) && (param->mem->vr != vr))
        return;

    /*
     * If this is a trailer message buffer, make sure it
     * starts dumping where the last message ended
     */
    if (param->state.cont)
    {
        Ipnet_route_entry  *rtt = (Ipnet_route_entry*)(*param->state.rt);

        /* Verify current state */
        if (*param->state.vr != vr || *param->state.table != table)
            return;

        if (rtt != IP_NULL)
        {
            rt = ipnet_route_first_entry(IPNET_ROUTE_GET_FAMILY(rtab),
                                         vr,
                                         table);
            while (rt != IP_NULL)
            {
                if (rt == rtt)
                    break;
                rt = ipnet_route_next_entry(rt);
            }
        }
    }


    /* Reset the continue variable; we're on the move again */
    param->state.cont = 0;

    /* Get first unless we've found one to continue from */
    if (rt == IP_NULL)
        rt = ipnet_route_first_entry(IPNET_ROUTE_GET_FAMILY(rtab),
                                     vr,
                                     table);

    /* */
    while (rt)
    {
        /* Do not dump the 255.255.255.255, 224.0.0.0/4 or IPv6 multicast routes. */
        /* Do not dump routes that are part of multipath routes either */
        if (IP_BIT_ISFALSE(rt->hdr.flags, IPNET_RTF_X_MCAST_RO | IPNET_RTF_X_BCAST_RO | IPNET_RTF_X_HIDDEN)
            && (rt->prev == IP_NULL))
        {
#ifdef IPCOM_USE_INET
            if (param->family == IP_AF_INET)
            {
                new_offs = ipnet_rtnetlink_ip4_route_fill_info (param->mem,
                                                                rt,
                                                                param->nlmsg->nlmsg_seq,
                                                                IP_RTM_NEWROUTE,
                                                                IP_NLM_F_MULTI);
            }
            else
#endif
#ifdef IPCOM_USE_INET6
            if (param->family == IP_AF_INET6)
            {
                new_offs = ipnet_rtnetlink_ip6_route_fill_info (param->mem,
                                                                rt,
                                                                param->nlmsg->nlmsg_seq,
                                                                IP_RTM_NEWROUTE,
                                                                IP_NLM_F_MULTI);
            }
            else
#endif
            {
                IP_PANIC();
            }

            if (new_offs < 0)
                goto aborted;
        }

        rt = ipnet_route_next_entry(rt);

        /* */
        if (rt && new_offs)
            goto aborted;
    }

    return;

aborted:
    param->state.aborted = 1;
    *param->state.rt     = (Ip_ptrdiff_t)rt;
    *param->state.table  = (Ip_ptrdiff_t)table;
    *param->state.vr     = (Ip_ptrdiff_t)vr;
}
/*
 *===========================================================================
 *                    ipnet_rtnetlink_route_foreach_vr
 *===========================================================================
 * Description: Hash table foreach callback. Called once for each virtual
 *              router that exists in the system.
 * Parameters:  rtab  - route table
 *              param - callback parameters.
 * Returns:     -
 *
 */
IP_STATIC void
ipnet_rtnetlink_neigh_foreach_vr_family(Ipcom_route *rtab, Ipnet_rtnetlink_neigh_family_t *param)
{
    Ip_u16             vr       = IPNET_ROUTE_GET_VR(rtab);
    Ipnet_route_entry  *rt      = IP_NULL;

    /* This traversal is already aborted */
    if (param->state.aborted)
        return;

    /* Only handle rtabs of selected address family */
    if (param->family != IP_AF_UNSPEC && IPNET_ROUTE_GET_FAMILY(rtab) != param->family)
        return;

    /* We only have neigh values in the default table */
    if (IPNET_ROUTE_GET_TABLE(rtab) != IPCOM_ROUTE_TABLE_DEFAULT)
        return;

    /* Check if specific VR shall be dumped */
    if (IP_BIT_ISFALSE(param->nlmsg->nlmsg_flags, IP_NLM_F_VR_UNSPEC) && (param->mem->vr != IPCOM_VR_ANY) && (param->mem->vr != vr))
        return;

    /*
     * If this is a trailer message buffer, make sure it
     * starts dumping where the last message ended
     */
    if (param->state.cont)
    {
        Ipnet_route_entry  *rtt = (Ipnet_route_entry*)(*param->state.rt);

        /* Verify current state */
        if (*param->state.vr != vr)
            return;

        if (rtt != IP_NULL)
        {
            rt = ipnet_route_first_entry(IPNET_ROUTE_GET_FAMILY(rtab),
                                         vr,
                                         IPNET_ROUTE_GET_TABLE(rtab));
            while (rt != IP_NULL)
            {
                if (rt == rtt)
                    break;
                rt = ipnet_route_next_entry(rt);
            }
        }
    }


    /* Reset the continue variable; we're on the move again */
    param->state.cont = 0;

    /* Get first unless we've found one to continue from */
    if (rt == IP_NULL)
        rt = ipnet_route_first_entry(IPNET_ROUTE_GET_FAMILY(rtab),
                                     vr,
                                     IPNET_ROUTE_GET_TABLE(rtab));

    /* */
    while (rt)
    {
        do
        {
            if (ipnet_rtnetlink_neigh_is(rt))
                break;
            rt = ipnet_route_next_entry(rt);
        } while (rt != IP_NULL);

        if (rt != IP_NULL)
        {
            int ret = 0;
            /* Verify this particular NC entry */
#ifdef IPCOM_USE_INET
            if (IPNET_ROUTE_GET_FAMILY(rtab) == IP_AF_INET)
                ret = ipnet_rtnetlink_ip4_neigh_fill_info(param->mem, rt, param->nlmsg->nlmsg_seq, IP_RTM_NEWNEIGH, IP_NLM_F_MULTI);
#endif
#ifdef IPCOM_USE_INET6
            if (IPNET_ROUTE_GET_FAMILY(rtab) == IP_AF_INET6)
                ret = ipnet_rtnetlink_ip6_neigh_fill_info(param->mem, rt, param->nlmsg->nlmsg_seq, IP_RTM_NEWNEIGH, IP_NLM_F_MULTI);
#endif
            if (ret < 0)
                goto aborted;

            /* Get next */
            rt = ipnet_route_next_entry(rt);
        }
    }

    return;

aborted:
    param->state.aborted = 1;
    *param->state.rt     = (Ip_ptrdiff_t)rt;
    *param->state.vr     = (Ip_ptrdiff_t)vr;
}