/*
 *  Interface MIB architecture support
 *
 * $Id: ipv6scopezone_linux.c 14170 2007-04-29 02:22:12Z varun_c $
 */
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>

#include <net-snmp/agent/net-snmp-agent-includes.h>
#include <net-snmp/data_access/scopezone.h>
#include "ipv6scopezone_private.h"

#if defined (NETSNMP_ENABLE_IPV6)
static int _scopezone_v6(netsnmp_container* container, int idx_offset);
#endif

/*
 *
 * @retval  0 success
 * @retval -1 no container specified
 * @retval -2 could not open file
 * @retval -3 could not create entry (probably malloc)
 * @retval -4 file format error
 */
int
netsnmp_access_scopezone_container_arch_load(netsnmp_container* container,
                                             u_int load_flags)
{
    int rc1 = 0;
#if defined (NETSNMP_ENABLE_IPV6)
    int idx_offset = 0;

    if (NULL == container) {
        snmp_log(LOG_ERR, "no container specified/found for access_scopezone_\n");
        return -1;
    }

    rc1 = _scopezone_v6(container, idx_offset);
#endif
    if(rc1 > 0)
        rc1 = 0;
    return rc1;
}

#if defined (NETSNMP_ENABLE_IPV6)

/* scope identifiers, from kernel - include/net/ipv6.h */
#define IPV6_ADDR_LOOPBACK      0x0010U
#define IPV6_ADDR_LINKLOCAL     0x0020U
#define IPV6_ADDR_SITELOCAL     0x0040U

static int
_scopezone_v6(netsnmp_container* container, int idx_offset)
{

    /*
     * On Linux, we support only link-local scope zones.
     * Each interface, which has link-local address, gets unique scope
     * zone index.
     */
    FILE           *in;
    char            line[80], addr[40];
    int             if_index, pfx_len, scope, flags, rc = 0;
    int             last_if_index = -1;
    netsnmp_v6scopezone_entry *entry;
    
    netsnmp_assert(NULL != container);

#define PROCFILE "/proc/net/if_inet6"
    if (!(in = fopen(PROCFILE, "r"))) {
        DEBUGMSGTL(("access:scopezone:container","could not open " PROCFILE "\n"));
        return -2;
    }
    
    /*
     * address index prefix_len scope status if_name
     */
    while (fgets(line, sizeof(line), in)) {
        /*
         * fe800000000000000200e8fffe5b5c93 05 40 20 80 eth0
         *             A                    D  P  S  F  I
         * A: address
         * D: device number
         * P: prefix len
         * S: scope (see include/net/ipv6.h, net/ipv6/addrconf.c)
         * F: flags (see include/linux/rtnetlink.h, net/ipv6/addrconf.c)
         * I: interface
         */
        rc = sscanf(line, "%39s %04x %02x %02x %02x\n",
                    addr, &if_index, &pfx_len, &scope, &flags);
        if( 5 != rc ) {
            snmp_log(LOG_ERR, PROCFILE " data format error (%d!=5), line ==|%s|\n",
                     rc, line);
            continue;
        }
        DEBUGMSGTL(("access:scopezone:container",
                    "addr %s, index %d, pfx %d, scope %d, flags 0x%X\n",
                    addr, if_index, pfx_len, scope, flags));

        if (if_index < 0) {
            DEBUGMSGTL(("access:scopezone:container",
                        "Invalid interface index %d\n", if_index));
            continue;
        }
        if (! (scope & IPV6_ADDR_LINKLOCAL)) {
            DEBUGMSGTL(("access:scopezone:container", 
                        "The address is not link-local, skipping\n"));
            continue;
        }
        /* 
         * Check that the interface was not inserted before, just in case
         * one interface has two or more link-local addresses.
         */
        if (last_if_index == if_index) {
            DEBUGMSGTL(("access:scopezone:container", 
                        "The interface was already inserted, skipping\n"));
            continue;
        }

        last_if_index = if_index; 
        entry = netsnmp_access_scopezone_entry_create();
        if(NULL == entry) {
            rc = -3;
            break;
        }
        entry->ns_scopezone_index = ++idx_offset;
        entry->index = if_index;
        entry->scopezone_linklocal = if_index;
 
        CONTAINER_INSERT(container, entry);
    }
    fclose(in);
    if(rc<0)
        return rc;

    return idx_offset;
}
#endif 
