BBN-Vax-TCP/bbnnet/ip_output.c

Compare this file to the similar file:
Show the results in this format:

#include "../h/param.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../bbnnet/mbuf.h"
#include "../bbnnet/net.h"
#include "../bbnnet/tcp.h"
#include "../bbnnet/ip.h"
#include "../bbnnet/ifcb.h"
#include "../bbnnet/ucb.h"

extern struct user u;

/*
 *	ip_output is called from tcp and passed an mbuf chain of a packet to
 *	send to the local network.  The first mbuf contains a tcp and partial
 *	ip header which is filled in.  After determining a local route for
 *	the packet, it is sent to the local net through ip_send.
 */
ip_output(up, mp, proto, len, optlen, options)
register struct ucb *up;
struct mbuf *mp;
int proto, len, optlen;
char *options;
{
	register struct ip *p, *q;
	int hlen;
	struct socket local;
	struct socket ip_route();

	p = (struct ip *)((int)mp + mp->m_off); /* -> ip header */

	/* Find route for datagram, if one has not been assigned.
	   Make a host table entry for the local network route to
	   the destination */

	if (up->uc_route == NULL) {
		local = ip_route(&p->ip_src, &p->ip_dst);
		if (local.s_addr == NULL || 
		    (up->uc_route = h_make(&local)) == NULL) {
			m_freem(mp);
			return(FALSE);
		} 
	}

	/* copy ip option string to header */

	if (optlen > 0) {
		mp->m_off -= optlen;
		mp->m_len += optlen;
		q = p;
		p = (struct ip *)((int)p - optlen);
		bcopy((caddr_t)q, (caddr_t)p, sizeof(struct ip)); 
		bcopy(options, (caddr_t)((int)p + sizeof(struct ip)), optlen);
	}

	/* fill in ip header fields */

	hlen = sizeof(struct ip) + optlen;
	p->ip_len = len + hlen;
	p->ip_p = proto;
	p->ip_v = IPVERSION;    
	p->ip_hl = hlen >> 2;
	p->ip_off = 0;
	p->ip_ttl = MAXTTL;
	p->ip_id = netcb.n_ip_cnt++;

	return(ip_send(p,up->uc_srcif->if_ifcb,&up->uc_route->h_addr,hlen,FALSE));
}

/*
 *	ip_send is called with a packet with a completed ip header 
 *	(except for checksum) by the raw internet driver.  It fragments the
 *	packet, inserts the ip checksum, and calls the appropriate local net
 *	output routine to send it to the net.
 */
ip_send(p, ip, ap, hlen, asis)
register struct ip *p;
register struct ifcb *ip;
struct socket *ap;
int hlen, asis;
{	 
	register struct mbuf *m, *n;
	register i, rnd;
	struct mbuf *mm;
	int adj, max, len, off;
	unsigned short j;
	unsigned olen;
	
	m = dtom(p);

	if (p->ip_len > ip->if_mtu) {		/* must fragment */
		if (p->ip_off & ip_df) {
			m_freem(m);
			return(FALSE);
		}
		max = ip->if_mtu - hlen;	/* max data length in frag */
		len = p->ip_len - hlen;         /* data length */
		off = 0;                        /* fragment offset */

		while (len > 0) {

			/* correct the header */

			p->ip_off |= off >> 3;

			/* find the end of the fragment */

			i = -hlen;
			while (m != NULL) {
				i += m->m_len;
				if (i > max)
					break;
				n = m;
				m = m->m_next;
			}

			if (i < max || m == NULL) {     /* last fragment */
				p->ip_off = p->ip_off & ~ip_mf;
				p->ip_len = i + hlen;
				break;

			} else {                        /* more fragments */

				/* allocate header mbuf for next fragment */

				if ((mm = m_get(1)) == NULL) {
					m_freem(m);
					return(FALSE);
				}

				p->ip_off |= ip_mf;

				/* terminate fragment at 8 byte boundary 
				   (round down) */

				i -= m->m_len;
        			rnd = i & ~7;           /* fragment length */
				adj = i - rnd;          /* leftover in mbuf */
				p->ip_len = rnd + hlen; 

				/* setup header for next fragment and 
				   append remaining fragment data */

				n->m_next = NULL;                   
				mm->m_next = m;        
				m = mm;
				m->m_off = MSIZE - hlen - adj;
				m->m_len = hlen + adj;

				/* copy old header to new */

				bcopy(p, (caddr_t)((int)m + m->m_off), hlen);

				/* copy leftover data from previous frag */

				if (adj) {
					n->m_len -= adj;
					bcopy((caddr_t)((int)n+n->m_len+n->m_off),
					      (caddr_t)((int)m+m->m_off+hlen), 
							adj);
				}
			}

                        /* finish ip leader by calculating checksum and doing
                           necessary byte-swapping and send to local net */
                        
			if (!asis) {
				p->ip_sum = 0;
				olen = p->ip_len;
				ip_to_net(p);
#ifndef mbb
				p->ip_sum = cksum(m, hlen);     
#else
				j = cksum(m, hlen);
				p->ip_sum = short_to_net(j);
#endif mbb
			}
			(*ip->if_send)(m, ip, ap, olen, IPLINK, asis);

			p = (struct ip *)((int)m + m->m_off);   /* ->new hdr */
			len -= rnd;
			off += rnd;
		}
	} 

	/* complete header, byte swap, and send to local net */

	if (!asis) {
		p->ip_sum = 0;
		olen = p->ip_len;
		ip_to_net(p);
#ifndef mbb
		p->ip_sum = cksum(m, hlen);     
#else
		j = cksum(m, hlen);
		p->ip_sum = short_to_net(j);
#endif mbb
	}
	return((*ip->if_send)(m, ip, ap, olen, IPLINK, asis));     
}

/*
 * Find a route to this destination.  Given the source and destination 
 * addresses, it returns a local net address
 * to send to (either the address of the destination itself or a gateway).
 */
struct socket ip_route(src, dst)
struct socket *src;
struct socket *dst;
{
	register struct if_local *lp;
	register struct gway *gp;
	struct socket nullsock;
	net_t snet, dnet;	

	nullsock.s_addr = 0;

	/* get network parts of src and dest addresses */

	snet = iptonet(*src);
	dnet = iptonet(*dst);	

	/* first, look for dest net in local nets table */

	for (lp = locnet; lp < &locnet[nlocnet]; lp++) 
		if (dnet == lp->if_ifcb->if_net && snet == dnet &&
		    lp->if_ifcb->if_avail) 
			return(*dst);

	/* no local access to dest net, search gateway table */

	for (gp = gateway; gp < gatewayNGATE; gp++)
		if (snet == gp->g_lnet && dnet == gp->g_fnet && 
		    gp->g_ifcb->if_avail) 
			return(gp->g_local);

	/* no direct gateway access, pick a smart gateway */

	for (gp = gateway; gp < gatewayNGATE; gp++)
		if (gp->g_flags & GWSMART && gp->g_ifcb->if_avail) 
			return(gp->g_local);

	/* can't get theah from heah */

	return (nullsock);
}

/*
 * Raw IP output routine.  Called from raw_write with the user's message
 * chained to an empty mbuf for header construction.  There are three
 * possibilities:  1) header is constructed by ip_output in the empty mbuf,
 * 2) header is supplied by the user, but the checksum is generated in
 * ip_send, or 3) header is supplied by the user including checksum.  In
 * cases (2) and (3), the user supplied header is copied into the first
 * mbuf which accomodates the local net header as well.
 */
ip_raw(up, m, len)
register struct ucb *up;
register struct mbuf *m;
int len;
{
	register struct ip *p, *q;
	register struct ifcb *ip;
	register struct mbuf *n;
	int hlen;
	net_t net;
	struct socket route;
	struct socket ip_route();

	switch (up->uc_flags & RAWFLAG) {

	case RAWCOMP:			/* compose header */

		/* build header in bottom of first mbuf */

		m->m_off = MSIZE - sizeof(struct ip);
		m->m_len = sizeof(struct ip);
		p = (struct ip *)((int)m + m->m_off);

		/* insert source and destination addrs from ucb */

		p->ip_src = up->uc_srcif->if_addr;
		p->ip_dst = up->uc_host;

		/* call ip routine to compose rest of header */

		if (!ip_output(up, m, up->uc_lo, len, 0, NULL))
			u.u_error = ERAWBAD;
		break;
	
	case RAWASIS:			/* send header asis */
	case RAWVER:			/* send asis but insert checksum */

		/* copy header into first mbuf */

		n = m->m_next;
		p = (struct ip *)((int)n + n->m_off);
		hlen = p->ip_hl << 2;
		if (hlen > MSIZE) 
			goto bad;
		m->m_off = MSIZE - hlen;
		m->m_len = hlen;
		q = (struct ip *)((int)m + m->m_off);
		bcopy((caddr_t)p, (caddr_t)q, hlen);
		n->m_off += hlen;
		n->m_len -= hlen;

		/* find an i/f to send this out on */

		route = ip_route(&q->ip_src, &q->ip_dst);
		if (route.s_addr == NULL)
			goto bad;
		net = iptonet(route);
		for (ip=ifcb; ip < &ifcb[nifcb]; ip++)
			if (ip->if_net == net)
				break;
		if (ip == &ifcb[nifcb])
			goto bad;
		if (!ip_send(q, ip, &route, hlen, up->uc_flags&RAWASIS))
			u.u_error = ERAWBAD;
		break;
	}
	return;

bad:
	m_freem(m);
	u.u_error = ERAWBAD;
}

/*
 * Take the non-byte data in the ip header as it is stored internally, and
 * convert it to the form that goes out over the network (i.e., either byte
 * swap or add holes).
 */
ip_to_net(p)
register struct ip *p;
{
	p->ip_len = short_to_net(p->ip_len);
	p->ip_id = short_to_net(p->ip_id);
	p->ip_off = short_to_net(p->ip_off);
}