#include "../h/param.h" #include "../h/dir.h" #include "../h/user.h" #include "../bbnnet/mbuf.h" #include "../bbnnet/net.h" #include "../bbnnet/ip.h" #include "../bbnnet/tcp.h" #include "../bbnnet/raw.h" #include "../bbnnet/ifcb.h" #include "../bbnnet/ucb.h" /* * ip_send 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 fragmented (if necessary) and sent to the local net * through the local net send routine. */ ip_send(up, mp, proto, len, optlen, options, asis) register struct ucb *up; register struct mbuf *mp; int proto, len, optlen; char *options; int asis; { register struct ifcb *ip; register struct ip *p, *q; register struct socket *ap; int hlen; u_short j; struct socket *ip_route(); p = mtod(mp, struct ip *); /* -> 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 */ ip = up->uc_srcif; if (up->uc_route == NULL) { if ((ap = ip_route(&p->ip_src, &p->ip_dst)) == NULL || (up->uc_route = h_make(ap, FALSE)) == NULL) { m_freem(mp); return(FALSE); } } else ap = &up->uc_route->h_addr; /* 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); } hlen = sizeof(struct ip) + optlen; /* fill in ip header fields */ if (!(asis&(RAWASIS+RAWVER))) { 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++; } /* * let ip_frag do the send if needed, otherwise do it directly. */ if (p->ip_len > ip->if_mtu) return(ip_frag(p, ip, ap, hlen, asis)); else { /* complete header, byte swap, and send to local net */ len = p->ip_len; p->ip_len = short_to_net(p->ip_len); p->ip_off = short_to_net(p->ip_off); if (!(asis&RAWASIS)) { p->ip_sum = 0; #ifndef mbb p->ip_sum = cksum(mp, hlen); #else j = cksum(mp, hlen); p->ip_sum = short_to_net(j); #endif mbb } return((*ip->if_send)(mp, ip, ap, len, ip->if_link, FALSE)); } } /* * ip_frag is called with a packet with a completed ip header * (except for checksum). It fragments the * packet, inserts the ip checksum, and calls the appropriate local net * output routine to send it to the net. */ ip_frag(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; u_short j; unsigned olen; m = dtom(p); if (p->ip_off & ip_df) { /* can't fragment */ 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; m = dtom(p); break; } else { /* more fragments */ /* allocate header mbuf for next fragment */ if ((mm = m_get(1)) == NULL) { m_freem(m); return(NULL); } 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 */ n = dtom(p); olen = p->ip_len; p->ip_len = short_to_net(p->ip_len); p->ip_off = short_to_net(p->ip_off); if (!(asis&RAWASIS)) { p->ip_sum = 0; #ifndef mbb p->ip_sum = cksum(n, hlen); #else j = cksum(n, hlen); p->ip_sum = short_to_net(j); #endif mbb } (*ip->if_send)(n, ip, ap, olen, ip->if_link, FALSE); p = mtod(m, struct ip *); /* ->new hdr */ len -= rnd; off += rnd; } /* complete header, byte swap, and send to local net */ olen = p->ip_len; p->ip_len = short_to_net(p->ip_len); p->ip_off = short_to_net(p->ip_off); if (!(asis&RAWASIS)) { p->ip_sum = 0; #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, ip->if_link, FALSE)); } /* * 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 ifcb *ip; register struct gway *gp; net_t snet, dnet; /* get network parts of src and dest addresses */ snet = iptonet(*src); dnet = iptonet(*dst); /* first, look for dest net in ifcb table */ if (snet == dnet) for (ip = netcb.n_ifcb_hd; ip != NULL; ip = ip->if_next) if (dnet == iptonet(ip->if_addr) && ip->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 & GWROUTE && snet == gp->g_lnet && gp->g_ifcb->if_avail) return(&gp->g_local); /* can't get theah from heah */ return (NULL); } /* * Look for a smart (routing) gateway, other than the one currently in use, * and use that for subsequent packet routing. Called from lower level * protocol when a local gateway is known to be unreachable */ ip_reroute(up, hp) register struct ucb *up; register struct host *hp; { register struct gway *gp; net_t lnet; /* look for smart gateway */ lnet = iptonet(hp->h_addr); for (gp=gateway; gp < gatewayNGATE; gp++) { if (gp->g_flags&GWROUTE && gp->g_ifcb->if_avail && lnet == gp->g_lnet && gp->g_local.s_addr != hp->h_addr.s_addr) { /* redirect */ h_free(hp); if ((up->uc_route=h_make(&gp->g_local, FALSE)) == NULL) return(FALSE); else return(TRUE); } } return(FALSE); } /* * 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_send 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; struct socket *ap; struct proto *r; int hlen; net_t net; struct socket *ip_route(); switch (up->uc_flags & RAWMASK) { 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 = mtod(m, struct ip *); /* insert source and destination addrs from ucb */ p->ip_src = up->uc_local; p->ip_dst = up->uc_host; /* call ip routine to compose rest of header */ if ((r = up->uc_proto) == NULL) goto rawfree; if (!ip_send(up, m, r->pr_num, len, 0, NULL, FALSE)) goto rawbad; break; case RAWASIS: /* send header asis */ case RAWVER: /* send asis but insert checksum */ /* copy header into first mbuf */ if ((n = m->m_next) == NULL) goto rawbad; p = mtod(n, struct ip *); hlen = p->ip_hl << 2; if (hlen > MSIZE || hlen > n->m_len) goto rawfree; m->m_off = MSIZE - hlen; m->m_len = hlen; q = mtod(m, struct ip *); bcopy((caddr_t)p, (caddr_t)q, hlen); n->m_off += hlen; n->m_len -= hlen; /* * Free any old routing entry, and make a new one for * this datagram. */ if (up->uc_route != NULL) h_free(up->uc_route); if ((ap = ip_route(&p->ip_src, &p->ip_dst)) == NULL || (up->uc_route = h_make(ap, TRUE)) == NULL) goto rawfree; /* * Determine an interface to send datagram out on. */ net = iptonet(*ap); for (ip = netcb.n_ifcb_hd; ip != NULL; ip = ip->if_next) if (iptonet(ip->if_addr) == net) break; if ((up->uc_srcif = ip) == NULL) goto rawfree; if (!ip_send(up, m, (short)q->ip_p, len, 0, NULL, up->uc_flags)) goto rawbad; break; default: m_freem(m); u.u_error = ENETPARM; } return; rawfree: m_freem(m); rawbad: u.u_error = ERAWBAD; }