/*
   Modifications:
   1998-07-01 - Arnaldo Carvalho de Melo - GNU gettext instead of catgets,
   snprintf instead of sprintf
 */

#include "config.h"

#if HAVE_AFINET6
#include <asm/types.h>
#include <asm/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
/* #include <net/route.h> realy broken */
#include <ctype.h>
#include <errno.h>
#include <netdb.h>
#include <resolv.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#ifndef __GLIBC__
#include <netinet6/ipv6_route.h>	/* glibc doesn't have this */
#endif
#include "version.h"
#include "net-support.h"
#include "pathnames.h"
#include "intl.h"
#include "net-features.h"

/* this is from linux/include/net/ndisc.h */
/*
 *    Neighbor Cache Entry States (7.3.2.)
 */

/*
 *    The lsb is set for states that have a timer associated
 */

#define NUD_NONE	0x00
#define NUD_INCOMPLETE	0x11
#define NUD_REACHABLE	0x20
#define NUD_STALE	0x30
#define NUD_DELAY	0x41
#define NUD_PROBE	0x51
#define NUD_FAILED	0x60	/* neighbour discovery failed     */

#define NUD_IN_TIMER	0x01

#define NDISC_QUEUE_LEN	3

#define NCF_NOARP		0x0100	/* no ARP needed on this device */
#define NCF_SUBNET		0x0200	/* NC entry for subnet            */
#define NCF_INVALID		0x0400
#define NCF_DELAY_EXPIRED	0x0800	/* time to move to PROBE  */
#define NCF_ROUTER		0x1000	/* neighbour is a router  */
#define NCF_HHVALID		0x2000	/* Hardware header is valid       */


extern struct aftype inet6_aftype;


int rprint_fib6(int ext, int numeric)
{
    char buff[4096], iface[16], flags[16];
    char addr6[128], naddr6[128];
    struct sockaddr_in6 saddr6, snaddr6;
    int num, iflags, metric, refcnt, use, prefix_len, slen;
    FILE *fp = fopen(_PATH_PROCNET_ROUTE6, "r");
    
    char addr6p[8][5], saddr6p[8][5], naddr6p[8][5];

    if (!fp) {
	perror(_PATH_PROCNET_ROUTE6);
        printf(_("INET6 (IPv6) not configured in this system.\n"));
	return 1;
    }
    printf(_("Kernel IPv6 routing table\n"));

    printf(_("Destination                                 "
	     "Next Hop                                "
	     "Flags Metric Ref    Use Iface\n"));

    while (fgets(buff, 1023, fp)) {
	num = sscanf(buff, "%4s%4s%4s%4s%4s%4s%4s%4s %02x %4s%4s%4s%4s%4s%4s%4s%4s %02x %4s%4s%4s%4s%4s%4s%4s%4s %08x %08x %08x %08x %s\n",
		     addr6p[0], addr6p[1], addr6p[2], addr6p[3],
		     addr6p[4], addr6p[5], addr6p[6], addr6p[7],
		     &prefix_len,
		     saddr6p[0], saddr6p[1], saddr6p[2], saddr6p[3],
		     saddr6p[4], saddr6p[5], saddr6p[6], saddr6p[7],
		     &slen,
		     naddr6p[0], naddr6p[1], naddr6p[2], naddr6p[3],
		     naddr6p[4], naddr6p[5], naddr6p[6], naddr6p[7],
		     &metric, &use, &refcnt, &iflags, iface);
#if 0
	if (num < 23)
	    continue;
#endif
	if (!(iflags & RTF_UP))
	    continue;
	/* Fetch and resolve the target address. */
	snprintf(addr6, sizeof(addr6), "%s:%s:%s:%s:%s:%s:%s:%s",
		 addr6p[0], addr6p[1], addr6p[2], addr6p[3],
		 addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
	inet6_aftype.input(1, addr6, (struct sockaddr *) &saddr6);
	snprintf(addr6, sizeof(addr6), "%s/%d",
		 inet6_aftype.sprint((struct sockaddr *) &saddr6, 1),
		 prefix_len);

	/* Fetch and resolve the nexthop address. */
	snprintf(naddr6, sizeof(naddr6), "%s:%s:%s:%s:%s:%s:%s:%s",
		 naddr6p[0], naddr6p[1], naddr6p[2], naddr6p[3],
		 naddr6p[4], naddr6p[5], naddr6p[6], naddr6p[7]);
	inet6_aftype.input(1, naddr6, (struct sockaddr *) &snaddr6);
	snprintf(naddr6, sizeof(naddr6), "%s",
		 inet6_aftype.sprint((struct sockaddr *) &snaddr6, 1));

	/* Decode the flags. */
	strcpy(flags, "U");
	if (iflags & RTF_GATEWAY)
	    strcat(flags, "G");
	if (iflags & RTF_HOST)
	    strcat(flags, "H");
	if (iflags & RTF_DEFAULT)
	    strcat(flags, "D");
	if (iflags & RTF_ADDRCONF)
	    strcat(flags, "A");
	if (iflags & RTF_CACHE)
	    strcat(flags, "C");

	/* Print the info. */
	printf("%-43s %-39s %-5s %-6d %-2d %7d %-8s\n",
	       addr6, naddr6, flags, metric, refcnt, use, iface);
    }

    (void) fclose(fp);
    return (0);
}

int rprint_cache6(int ext, int numeric)
{
    char buff[4096], iface[16], flags[16];
    char addr6[128], haddr[20], statestr[20];
    struct sockaddr_in6 saddr6;
    int type, num, refcnt, prefix_len, location, state, gc;
    long tstamp, expire, ndflags, reachable, stale, delete;
    FILE *fp = fopen(_PATH_PROCNET_NDISC, "r");
    char addr6p[8][5], haddrp[6][3];

    if (!fp) {
	ESYSNOT("nd_print", "ND Table");
	return 1;
    }
    printf(_("Kernel IPv6 Neighbour Cache\n"));

    if (ext == 2)
	printf(_("Neighbour                                   "
		 "HW Address        "
		 "Iface    Flags Ref State\n"));
    else
	printf(_("Neighbour                                   "
		 "HW Address        "
	"Iface    Flags Ref State            Stale(sec) Delete(sec)\n"));


    while (fgets(buff, 1023, fp)) {
	num = sscanf(buff, "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %08lx %08lx %08lx %04x %04x %04lx %8s %2s%2s%2s%2s%2s%2s\n",
		     addr6p[0], addr6p[1], addr6p[2], addr6p[3],
		     addr6p[4], addr6p[5], addr6p[6], addr6p[7],
		     &location, &prefix_len, &type, &state, &expire, &tstamp, &reachable, &gc, &refcnt,
		     &ndflags, iface,
	haddrp[0], haddrp[1], haddrp[2], haddrp[3], haddrp[4], haddrp[5]);

	/* Fetch and resolve the nexthop address. */
	snprintf(addr6, sizeof(addr6), "%s:%s:%s:%s:%s:%s:%s:%s",
		 addr6p[0], addr6p[1], addr6p[2], addr6p[3],
		 addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
	inet6_aftype.input(1, addr6, (struct sockaddr *) &saddr6);
	snprintf(addr6, sizeof(addr6), "%s/%d",
	       inet6_aftype.sprint((struct sockaddr *) &saddr6, numeric),
		 prefix_len);

	/* Fetch the  hardware address. */
	snprintf(haddr, sizeof(haddr), "%s:%s:%s:%s:%s:%s",
	haddrp[0], haddrp[1], haddrp[2], haddrp[3], haddrp[4], haddrp[5]);

	/* Decode the flags. */
	flags[0] = '\0';
	if (ndflags & NCF_NOARP)
	    strcat(flags, "N");
	if (ndflags & NCF_SUBNET)
	    strcat(flags, "S");
	if (ndflags & NCF_INVALID)
	    strcat(flags, "I");
	if (ndflags & NCF_DELAY_EXPIRED)
	    strcat(flags, "D");
	if (ndflags & NCF_ROUTER)
	    strcat(flags, "R");
	if (ndflags & NCF_HHVALID)
	    strcat(flags, "H");

	/* Decode the state */
	switch (state) {
	case NUD_NONE:
	    strcpy(statestr, "NONE");
	    break;
	case NUD_INCOMPLETE:
	    strcpy(statestr, "INCOMPLETE");
	    break;
	case NUD_REACHABLE:
	    strcpy(statestr, "REACHABLE");
	    break;
	case NUD_STALE:
	    strcpy(statestr, "STALE");
	    break;
	case NUD_DELAY:
	    strcpy(statestr, "DELAY");
	    break;
	case NUD_PROBE:
	    strcpy(statestr, "PROBE");
	    break;
	case NUD_FAILED:
	    strcpy(statestr, "FAILED");
	    break;
	case NUD_IN_TIMER:
	    strcpy(statestr, "IN TIMER");
	    break;
	default:
	    snprintf(statestr, sizeof(statestr), "UNKNOWN %02x", state);
	    break;
	}

	/* Print the info. */
	printf("%-43s %-17s %-8s %-5s %-3d %-16s",
	       addr6, haddr, iface, flags, refcnt, statestr);

	stale = 0;
	if (state == NUD_REACHABLE)
	    stale = reachable > tstamp ? reachable - tstamp : 0;
	delete = gc > tstamp ? gc - tstamp : 0;
	if (ext != 2) {
	    printf(" %-9ld ", stale / HZ);
	    if (refcnt)
		printf(" * ");
	    else
		printf(" %-7ld ", delete / HZ);
	}
	printf("\n");
    }

    (void) fclose(fp);
    return (0);
}

int INET6_rprint(int options)
{
    int ext = options & FLAG_EXT;
    int numeric = options & (FLAG_NUM | FLAG_SYM);
    int rc = E_INTERN;

    if (options & FLAG_FIB)
	if ((rc = rprint_fib6(ext, numeric)))
	    return (rc);

    if (options & FLAG_CACHE)
	if ((rc = rprint_cache6(ext, numeric)))
	    return (rc);
    return (rc);
}

#endif				/* HAVE_AFINET6 */
