/*
 *  krt_rtread_radix.c,v 1.7 1994/02/10 21:10:01 jch Exp
 */

/* Gated Release 3.5 */
/* Copyright (c) 1990,1991,1992,1993,1994 by Cornell University. All rights */
/* reserved. Refer to Particulars and other Copyright notices at the end of this */
/* file. */


#define	INCLUDE_ROUTE
#define	INCLUDE_KVM
#define	INCLUDE_FILE
#define INCLUDE_NLIST
#include "include.h"
#ifdef	PROTO_INET
#include "inet.h"
#endif	/* PROTO_INET */
#ifdef	PROTO_ISO
#include "iso.h"
#endif	/* PROTO_ISO */
#include "krt.h"
#include "krt_var.h"

struct	nlist	*krt_radix_head;

/*
 * OSF/1 supports the BSD routing socket (the good news), but does not
 * support the getkerninfo system call (the bad news).  The radix tree
 * IS available, however, by groping through kernel memory (YUCK!).
 *
 */

/*
 * Read a socket from kernel memory
 */
static sockaddr_un *
krt_getaddr __PF3(tp, task *,
		  dst, struct sockaddr *,
		  af, int)
{
    u_long data[32];
    struct sockaddr *sa = (struct sockaddr *) data;

    if (dst == (struct sockaddr *)0)
	return	(sockaddr_un *) 0;

    if (KVM_READ(kd,
		 dst,
		 sa,
		 sizeof *sa) < 0) {
	trace_log_tp(tp,
		     0,
		     LOG_ERR,
		     ("krt_getaddr: reading socket: %s",
		      KVM_GETERR(kd, "kvm_read error")));
	return (sockaddr_un *) 0;
    }

    if (sa->sa_len > sizeof *sa) {
	if (KVM_READ(kd,
		     dst,
		     (u_char *) sa + sizeof *sa,
		     sa->sa_len - sizeof *sa) < 0) {
	    trace_log_tp(tp,
			 0,
			 LOG_ERR,
			 ("krt_getaddr: reading socket: %s",
			  KVM_GETERR(kd, "kvm_read error")));
	    return (sockaddr_un *) 0;
	}
    } else if (sa->sa_len < 2) {
	/* We have a null address */

	if (af) {
	    /* It's OK, its a mask */

	    sa->sa_len = 2;
	    sa->sa_family = af;
	} else {
	    /* It's not OK. */

	    return (sockaddr_un *) 0;
	}
    }

    /*
     * OK, now we have the sockaddr, copy it, and return it!
     */
    return sock2gated(sa, unix_socksize(sa));
}


/*
 * Read a radix tree from kernel memory
 */
static void
krt_rtread_getnode __PF3(tp, task *,
			 rn, struct radix_node *,
			 rtp, rt_parms *)
{
    struct radix_node rnode;
    struct rtentry krt;

    do {
	if (KVM_READ(kd,
		     rn,
		     &rnode,
		     sizeof(rnode)) < 0) {
	    trace_log_tp(tp,
			 0,
			 LOG_ERR,
			 ("krt_rtread: reading radix node: %s",
			  KVM_GETERR(kd, "kvm_read error")));
	    return;
	}

	if (rnode.rn_b < 0) {
	    if ((rnode.rn_flags & RNF_ROOT) == 0) {
		if (KVM_READ(kd,
			     rn,
			     &krt,
			     sizeof(krt)) < 0) {
		    trace_log_tp(tp,
				 0,
				 LOG_ERR,
				 ("krt_rtread: reading rtentry: %s",
				  KVM_GETERR(kd, "kvm_read error")));
		    return;
		}

		/* OK, now get the relevant fields */
		/* Get the address (key) */
		rtp->rtp_dest = krt_getaddr(tp, rt_key(&krt), 0);

		/* Get the mask */
		if (BIT_TEST(krt.rt_flags, RTF_HOST)) {
		    rtp->rtp_dest_mask = sockhostmask(rtp->rtp_dest);
		} else {
		    rtp->rtp_dest_mask = krt_getaddr(tp, rt_mask(&krt), rtp->rtp_dest->a.sa_family);
		}

		/* Get the gateway */
		rtp->rtp_router = krt_getaddr(tp, krt.rt_gateway, 0);

		/* Get the state flags */
		rtp->rtp_state = krt_flags_to_state((flag_t) krt.rt_flags);

		krt_rtread_add(tp,
			       rtp,
			       (flag_t) krt.rt_flags,
			       FALSE,
			       "REMNANT");
	    }
	} else {
	    krt_rtread_getnode(tp, rnode.rn_l, rtp);
	    krt_rtread_getnode(tp, rnode.rn_r, rtp);
	}
    } while (rn = rnode.rn_dupedkey) ;

    return;
}


/*  Read the kernel's routing table */
/*ARGSUSED*/
int
krt_rtread __PF1(tp, task *)
{
    struct radix_node_head *rnh, head;
    rt_parms rtparms;
    int	errval = 0;

    bzero((caddr_t) &rtparms, sizeof (rtparms));
    rtparms.rtp_n_gw = 1;

    if (!kd) {
	errval = EBADF;
	goto Return;
    }

    trace_only_tp(tp,
		  TRC_NL_BEFORE,
		  ("krt_rtread: Initial routes read from kernel (radix tree via kmem):"));

    if (krt_radix_head->n_value) {
	if (KVM_READ(kd,
		     krt_radix_head->n_value,
		     &rnh,
		     sizeof(rnh)) < 0) {
	    trace_log_tp(tp,
			 0,
			 LOG_ERR,
			 ("krt_rtread: reading radix_head: %s",
			  KVM_GETERR(kd, "kvm_read error")));
	    errval = EINVAL;
	    goto Return;
	}
    }

    /* The main loop */

    for (; rnh; rnh = head.rnh_next) {
	if (KVM_READ(kd,
		     rnh,
		     &head,
		     sizeof(head)) < 0) {
	    trace_log_tp(tp,
			 0,
			 LOG_ERR,
			 ("krt_rtread: readint radix head: %s",
			  KVM_GETERR(kd, "kvm_read error")));
	    errval = EINVAL;
	    goto Return;
	}

	switch (head.rnh_af) {
#ifdef	PROTO_INET
	case AF_INET:
#endif	/* PROTO_INET */
	    krt_rtread_getnode(tp, head.rnh_treetop, &rtparms);
	    break;

	default:
	    break;
	}
    }

Return:
    return errval;
}
