/* $Header:   /src/bos/3.2/9121/mfd/cmd/net/ospf/port/PVCS/oport_kern.c_v   1.2   27 Aug 1991 14:13:24   build  $ */

#include "../ospf.h"
#define DO_EQUAL_COST

#ifdef BSD_TEST

static int k_sock;

static struct  RTM_MSG {
	struct	rt_msghdr rtm;
	struct 	sockaddr_in sa[3];
} rtm_msg;


/*
 * Initialize routing socket and flush kernel routing table
 */
void
krt_sock_init()
{
	int bufsize, rt_count, seqno, rlen;
	char *buf, *next, *lim;
	struct rt_msghdr *rtm;
	struct sockaddr *sa;

	if ((k_sock = socket(PF_ROUTE, SOCK_RAW, 0))  < 0) {
		perror("ospfd: k_sock_init:");
		exit(1);
	}

	shutdown(k_sock, 0); /* Don't want to read back our messages */

	if ((rt_count = getkerninfo(KINFO_RT_DUMP, 0, 0, 0)) < 0) {
		perror("ospf: k_sock_init: route-getkerninfo-estimate");
		exit(1);
	}

	if ((buf = malloc(rt_count)) == 0) { 
		fprintf(stderr,"malloc failed\n");
		exit(1);
	}

	if ((rlen = getkerninfo(KINFO_RT_DUMP, buf, &rt_count, 0)) < 0) {
		perror("ospf: k_sock_init: retrieval of routing table");
		exit(1);
	}

	lim = buf + rt_count;

	for (next = buf; next < lim; next += rtm->rtm_msglen) {
		rtm = (struct rt_msghdr *)next;
		sa = (struct sockaddr *)(rtm + 1);

		/*
 		 * If host route has not been addeded add as static direct
		 */
		if (((rtm->rtm_flags & RTF_GATEWAY)) == 0) {
    		    struct sockaddr_in *s_dst = (struct sockaddr_in *)sa, *s_gw;
    		    struct avl_node s;
    		    OROUTE *r, *gw;

    		    CLEAR_BUF(&s, sizeof(AVL));

		    s_gw = (s_dst + 1);
		    /* 
		     * Handle point to point interface
		     */
    		    r = RT_FIND(s_dst->sin_addr.s_addr);
    		    if ((!r) || (RT_DEST(r)  != s_dst->sin_addr.s_addr)) {
			/*
			 * Find nexthop block for gateway
			 */
			if (rtm->rtm_addrs & RTA_GATEWAY)
    		    		gw = RT_FIND(s_gw->sin_addr.s_addr);

    		    	if ((!gw) || (RT_DEST(gw)  != s_gw->sin_addr.s_addr))
			{
				continue;
			}

			STATIC_INFO_ALLOC(&s);
			STATIC_NH_NDX(&s) =
	    		    add_nh_entry(RT_NH_NDX(gw,0),
					 s_dst->sin_addr.s_addr,
				         NH_DIRECT);
			STATIC_PREF(&s) = STATIC_ROUTE_PREF;
			r = RT_INSERT(s_dst->sin_addr.s_addr,
	    			0xffffffff,
		      		&STATIC_NH_NDX(&s),
		      		1,
		      		PR_STATIC,
		      		STATIC_INFO(&s));
			RT_DIRECT(r) = TRUE;
			/* Will choose static... */
			choose_proto(r);
		    }
		    continue;
		}

		if (sa->sa_family != AF_INET)
			continue;

		rtm->rtm_type = RTM_DELETE;
		rtm->rtm_seq = seqno;
		if ((rlen = write(k_sock, next, rtm->rtm_msglen)) < 0) {
			perror("ospf: k_sock_init() write to rt socket");
			break;
		}
		seqno++;
	}
	free(buf);
}

#define DFLT_MASK_SIZE	8

/*
 * Get the mask size
 */
int
masksiz(mask)
u_long32 mask;
{
    u_char8 *c = (u_char *) &mask;
    int i;
    
    if (mask == HOST_NET_MASK)
	return(0);

    for(i = 4;i;i--)
	if (c[i - 1]) return(i + sizeof(u_long32));
    return(DFLT_MASK_SIZE);
}

#ifndef DO_EQUAL_COST

/*
 * Do the add, change or delete
 * The question still remains whether the kernel will allow a directly
 * reachable net to be changed to an non-direct gateway
 */
int
krt_action(r, what, old_cnt, old_nh)
int what;
OROUTE *r;
int old_cnt;
u_char8 old_nh[];
{
    int 	len, rlen;
    struct 	sockaddr_in *sa;
    int 	mask_size = masksiz(RT_MASK(r));
    int		flags = 0;

    /* Set up destination */
    CLEAR_BUF(rtm_msg,sizeof(struct RTM_MSG));
    len = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in);
    sa = rtm_msg.sa;
    sa->sin_len = sizeof(struct sockaddr_in);
    sa->sin_family = AF_INET;
    sa->sin_port = 0;
    sa->sin_addr.s_addr = RT_DEST(r);
    sa++;
    rtm_msg.rtm.rtm_addrs = RTA_DST;
    rtm_msg.rtm.rtm_version = RTM_VERSION;

    switch(what) {
	case RTM_ADD:
	case RTM_CHANGE:

	    /* set up gateway */
	    len += sizeof(struct sockaddr_in);
    	    sa->sin_len = sizeof(struct sockaddr_in);
	    sa->sin_family = AF_INET;
	    sa->sin_port = 0;
	    sa->sin_addr.s_addr = RT_NH(r,0);
	    sa++;
    	    rtm_msg.rtm.rtm_addrs |=  RTA_GATEWAY;
	    flags |= RTF_UP;
	 
	    /* Fall through */
	case RTM_DELETE:
	    /* Set up mask */
	    if (mask_size) {
		if (mask_size > 4) {
		    /* set up mask */
		    sa->sin_family = AF_INET;
		    sa->sin_port = 0;
		    sa->sin_addr.s_addr = RT_MASK(r);
	    	}
    		rtm_msg.rtm.rtm_addrs |=  RTA_NETMASK;
	        len += mask_size;
		/* Sockaddr - sin_zero */
    	        sa->sin_len = mask_size;
	    } else
		flags |= RTF_HOST;
	    flags |= RTF_GATEWAY;
	    break;
    }

    rtm_msg.rtm.rtm_type = what;
    rtm_msg.rtm.rtm_flags = flags;
    rtm_msg.rtm.rtm_msglen = len;

    if ((rlen = write(k_sock, &rtm_msg, len)) < 0) {
	fprintf(stderr,"Net: %s",lntoa(RT_DEST(r)));
  	perror("ospf: krt_action() write to rt socket");
	return(FALSE);
    }
}

#else

/*
 * Do the add, change or delete
 * The question still remains whether the kernel will allow a directly
 * reachable net to be changed to an non-direct gateway
 */
int
krt_action(r, what, old_cnt, old_nh)
OROUTE *r;
int what;
int old_cnt;
u_char8 old_nh[];
{
    int 	len, rlen;
    int 	mask_size = masksiz(RT_MASK(r));
    int		flags;
    int		i, action, action_cnt[2];
    struct 	sockaddr_in *sa;
    u_long32	action_list[2][MAXNH];

#define RT_DELETES	0
#define RT_ADDS		1

    action_cnt[0] = action_cnt[1] = 0;

    switch(what) {
	case RTM_ADD:
	    printf("RTM_ADD:");
	    action_cnt[RT_ADDS] = RT_NH_CNT(r);
	    for (i=0;i<action_cnt[RT_ADDS];i++)
		action_list[RT_ADDS][i] = RT_NH(r,i);
	    break;

	case RTM_DELETE:
	    printf("RTM_DELETE:");
#ifdef NOTDEF
	    action_cnt[RT_DELETES] = old_cnt;
	    for (i=0;i<action_cnt[RT_DELETES];i++)
		action_list[RT_DELETES][i] = RT_NH(r,i);
#endif
	    action_cnt[RT_DELETES] = (old_cnt) ? 1 : 0;
	    action_list[RT_DELETES][0] = RT_NH(r,0);
	    break;

	case RTM_CHANGE:
	    action_cnt[RT_ADDS] = RT_NH_CNT(r);
	    for (i=0;i<action_cnt[RT_ADDS];i++)
		action_list[RT_ADDS][i] = RT_NH(r,i);

#ifdef NOTDEF
	    action_cnt[RT_DELETES] = old_cnt;
	    for (i=0;i<action_cnt[RT_DELETES];i++)
		action_list[RT_DELETES][i] = nh_block[old_nh[i]].nh_addr;
#endif
	    action_cnt[RT_DELETES] = (old_cnt) ? 1 : 0;
	    action_list[RT_DELETES][0] = RT_NH(r,0);

	    printf("RTM_CHANGE: addcnt %d deletecnt %d\n",RT_NH_CNT(r),old_cnt);
	    break;
    }


    for(action = RT_DELETES;action <= RT_ADDS;action++) {
	for (i=0;i<action_cnt[action];i++)
	{
	    printf("Action: %s Dest: %s Gateway: %s\n",
		(action == RT_DELETES) ? "delete" : "add",
		lntoa(RT_DEST(r)),
		lntoa(action_list[action][i]));
		
	    /* Set up destination */
	    CLEAR_BUF(rtm_msg,sizeof(struct RTM_MSG));
	    sa = rtm_msg.sa;
	    sa->sin_len = sizeof(struct sockaddr_in);
	    sa->sin_family = AF_INET;
	    sa->sin_port = 0;
	    sa->sin_addr.s_addr = RT_DEST(r);
	    rtm_msg.rtm.rtm_addrs = RTA_DST;
	    rtm_msg.rtm.rtm_version = RTM_VERSION;

	    flags = 0;
	    sa++;
    	    rtm_msg.rtm.rtm_type = 0;
    	    rtm_msg.rtm.rtm_flags = 0;
    	    rtm_msg.rtm.rtm_msglen = 0;
    	    len = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in);

    	    switch(action) {
	    	case RT_ADDS:
	    	/* set up gateway */
	    	len += sizeof(struct sockaddr_in);
    	    	sa->sin_len = sizeof(struct sockaddr_in);
	    	sa->sin_family = AF_INET;
	    	sa->sin_port = 0;
	    	sa->sin_addr.s_addr = action_list[action][i];
	    	sa++;
    	    	rtm_msg.rtm.rtm_addrs |=  RTA_GATEWAY;
	    	flags |= RTF_UP;
	 
	    	/* Fall through */
	    	case RT_DELETES:
	    	/* Set up mask */
	    	if (mask_size) {
		    if (mask_size > 4) {
		    	/* set up mask */
		    	sa->sin_family = AF_INET;
		    	sa->sin_port = 0;
		    	sa->sin_addr.s_addr = RT_MASK(r);
	    	    }
    		    rtm_msg.rtm.rtm_addrs |=  RTA_NETMASK;
	            len += mask_size;
		    /* Sockaddr - sin_zero */
    	            sa->sin_len = mask_size;
	    	} else
		    flags |= RTF_HOST;
	    	flags |= RTF_GATEWAY;
	    	break;
	    }
    	    rtm_msg.rtm.rtm_type = (action == RT_ADDS) ? RTM_ADD : RTM_DELETE;
    	    rtm_msg.rtm.rtm_flags = flags;
    	    rtm_msg.rtm.rtm_msglen = len;

    	    if ((rlen = write(k_sock, &rtm_msg, len)) < 0) {
		    sprintf(_ospf_prt_buf, "Route socket write %s",
			    strerror(errno));
		    OSPF_LOG(_ospf_prt_buf);
		    printf("Dulpicate route action: %s %s\n",
			lntoa(RT_DEST(r)),
    	    		rtm_msg.rtm.rtm_type = (action == RT_ADDS) ? 
				"RTM_ADD" : "RTM_DELETE");
#ifdef NOTDEF
  		    perror("ospf: krt_action() write to rt socket");
		    return(FALSE);
#endif
    	    }
	}
    }
}
#endif

#endif
