fixes for range locking
[unix-history] / usr / src / sys / deprecated / bbnnet / ip_input.c
#ifdef RCSIDENT
static char rcsident[] = "$Header: ip_input.c,v 1.39 85/07/31 09:31:26 walsh Exp $";
#endif
#include "../h/param.h"
#include "../h/mbuf.h"
#include "../h/socket.h"
#include "../h/socketvar.h"
#include "../h/errno.h"
#include "../h/protosw.h"
#include "../h/domain.h"
#include "../h/syslog.h"
#include "../net/if.h"
#include "../net/route.h"
#include "../net/raw_cb.h"
#include "../bbnnet/in.h"
#include "../bbnnet/net.h"
#include "../bbnnet/in_pcb.h"
#include "../bbnnet/in_var.h"
#include "../bbnnet/ip.h"
#include "../bbnnet/icmp.h"
#ifdef HMP
#include "../bbnnet/hmp_traps.h"
#endif
#ifdef RCSIDENT
static char rcsiphdr[] = RCSIPHDR;
#endif
#define rawreaders(r) ((r)->rcb_next != (r))
#define any_rawreaders() rawreaders(&rawcb)
#define FIELD_OFF(fld, type) (((char *) (&(((type *) 0)->fld))) - ((char *) 0))
struct in_stat otherstat;
struct ip_stat ipstat;
struct {
struct ipq *n_ip_head; /* -> top of ip reass. queue */
struct ipq *n_ip_tail; /* -> end of ip reass. queue */
} ipfrags;
/*
* The protocol layers above IP assume the IP header and the protocol
* header are contiguous. However, need to save the options in case
* a connection oriented protocol (RDP/TCP) wants to respond to an
* incoming packet (SYN) over the same route if the packet got here
* using IP source routing. This allows connection establishment and
* maintenance when the remote end is on a network that is not known
* to our gateways. Only applicable options are Loose/Strict source
* routing, so rather than saving them in ip_stripopt() and reinterpreting
* them, we'll set up something specific and appropriate here.
*/
int nosum = 0;
int ip_nhops = 0;
struct in_addr ip_hops[MAX_IPOPTLEN / sizeof(struct in_addr)];
/* use a dispatch table for protocol dispatching */
struct ipswitch ipsw[IPPROTO_MAX];
extern struct ip *ip_reass();
ip_init()
{
register struct protosw *pr;
register int i;
pr = pffindproto(PF_INET, IPPROTO_IP, SOCK_RAW);
/* build our internal switch table */
for(i=0; i < IPPROTO_MAX; i++)
{
ipsw[i].ipsw_raw = pr;
ipsw[i].ipsw_user = 0;
ipsw[i].ipsw_hlen = sizeof(struct ip);
}
for(pr = inetdomain.dom_protosw; pr < inetdomain.dom_protoswNPROTOSW; pr++)
{
if (pr->pr_protocol >= IPPROTO_MAX)
panic("ip_init");
if (pr->pr_type == SOCK_RAW)
ipsw[pr->pr_protocol].ipsw_raw = pr;
else
ipsw[pr->pr_protocol].ipsw_user = pr;
}
ipintrq.ifq_maxlen = IFQ_MAXLEN; /* got a better number? */
}
/*
* Being global might be a little gross, but we just got the interface pointer
* passed up the week before 4.3 release.
*/
struct ifnet *inetifp;
/*
* IP network software interrupt service routine. Dequeue a message from the
* ip input queue, and pass to internal ip input processor (ip_input).
*/
ipintr()
{
extern char *panicstr;
register struct mbuf *m;
register int s;
/*
* Half-hearted attempt to prevent recursive panics due to network
* bugs since panic calls boot, which lowers spl. Note that the
* drivers will still store packets in mbufs and that some processing
* (ARP, Chaosnet) that occurs at splimp() will still proceed. At
* least this should preserve state information for post-mortem.
*/
if (panicstr == NULL)
{
for (;;)
{
/* for 4.3, Berkeley finally changed imp code to queue up messages
* for ctlinput path, so don't need to check for them here with our
* old queueing mechanism
*/
s = splimp();
IF_DEQUEUEIF (&ipintrq, m, inetifp);
splx(s);
if (m == NULL)
return;
ip_input(m);
}
}
}
/*
* IP level input routine
*
* Called from local net level upon recpt of an internet datagram or fragment.
* This routine does fragment reassembly, if necessary, and passes completed
* datagrams to higher level protocol processing routines on the basis of the
* ip header protocol field. It is passed a pointer to an mbuf chain
* containing the datagram/fragment. The mbuf offset+length are set to point
* at the ip header.
*/
ip_input(mp)
register struct mbuf *mp;
{
register struct ip *ip;
register int hlen;
register int i;
register struct mbuf *m;
register struct ipq *fp;
int fragsize = 0;
struct in_ifaddr *ia;
int j;
struct inpcb fwdinp;
ipstat.ip_total ++;
/*
* make sure dtom() macro works by getting header out of mbufs using
* pages, also make sure header contiguous in first mbuf.
*/
if ((mp->m_off > MMAXOFF) || (mp->m_len < sizeof(struct ip)))
{
/*
* Might as well avoid doing m_pullup twice.
* Common case is using cluster for large chunk of data.
*/
if (mp->m_len < FIELD_OFF(ip_p, struct ip) + sizeof(ip->ip_p))
i = sizeof(struct ip);
else
{
ip = mtod(mp, struct ip *);
i = ipsw[ip->ip_p].ipsw_hlen;
}
if ((mp = m_pullup(mp, i)) == NULL)
{
ipstat.ip_tooshort ++;
return;
}
}
ip = mtod(mp, struct ip *);
/*
* make sure header does not overflow mbuf (is contiguous)
*/
hlen = ip->ip_hl << IP_HLSHIFT;
if (hlen > mp->m_len)
{
if ((mp = m_pullup(mp, hlen)) == NULL)
{
ip_log(ip, "ip header overflow");
#ifdef HMPTRAPS
/* hmp_trap(T_IP_OVFLO, (caddr_t)0,0); */
#else
/* netlog(mp); no longer have mbuf list */
#endif
return;
}
ip = mtod(mp, struct ip *);
}
/*
* Adjust msg length to remove any driver padding. Make sure that
* message length matches ip length.
*/
for (i = 0, m = mp; m->m_next != NULL; m = m->m_next)
i += m->m_len;
i -= ntohs((u_short)ip->ip_len) - m->m_len;
if (i != 0)
{
if (i > (int)m->m_len)
m_adj(mp, -i);
else if (i < 0)
{
ip_log(ip, "truncated ip packet");
#ifdef HMPTRAPS
/* hmp_trap(T_IP_TRUNC, (caddr_t)0, 0); */
#else
netlog(mp);
#endif
return;
}
else
m->m_len -= i;
}
i = (u_short)ip->ip_sum;
ip->ip_sum = 0;
/* used to do inline cksum here via sed */
if (i != (j = (u_short)in_cksum(dtom(ip), hlen)))
{
ipstat.ip_badsum++;
if (!nosum)
{
#ifdef HMPTRAPS
/* hmp_trap(T_IP_CKSUM, (caddr_t)0,0); */
#endif
inet_cksum_err ("ip", ip, (u_long) i, (u_long) j);
ip->ip_sum = i;
ic_errmsg (icmp_addr(ip), ip->ip_src,
ICMP_PARM, 0, FIELD_OFF(ip_sum, struct ip),
hlen + ICMP_ERRLEN, (char *) ip);
netlog(mp);
return;
}
}
/*
* Make sure this packet is addressed to us before we put fields
* in host order and do reassembly (fragments may reach destination
* by separate routes.)
* Remember that IP source routing option can change ip_dst, so have
* to do option processing for all incoming packets with options.
* Even if the packet turns out to be for us and we strip them.
*/
fwdinp.inp_route.ro_rt = NULL; /* in case fwd, but no options */
ip_nhops = 0; /* for source routed TCP/RDP... connections */
if (hlen > sizeof(struct ip))
/*
* Any route allocated by ip_opt() 1. will be used by
* ip_forward(), and 2. will only be needed by ip_forward()
*/
if (! ip_opt (ip, hlen, &fwdinp))
{
ip_log (ip, "ip option error");
netlog (mp);
return;
}
/*
* if packet is not for us then forward
*/
if ((ia = in_iawithaddr(ip->ip_dst, TRUE)) == NULL)
{
ip_forward (&fwdinp, ip, mp, hlen);
return;
}
ip->ip_len = ntohs((u_short)ip->ip_len);
if ((int)(ip->ip_len -= hlen) < 0)
{ /* length of data */
ip_log(ip, "ip header length error");
#ifdef HMPTRAPS
/* hmp_trap(T_IP_HLEN, (caddr_t)0,0); */
#else
netlog(mp);
#endif
return;
}
ip->ip_off = ntohs((u_short)ip->ip_off);
ip->ip_mff = ((ip->ip_off & ip_mf) ? TRUE : FALSE);
ip->ip_off <<= IP_OFFSHIFT;
/* look for chain on reassembly queue with this header */
for (fp = ipfrags.n_ip_head; (fp != NULL && (
ip->ip_src.s_addr != fp->iqh.ip_src.s_addr ||
ip->ip_dst.s_addr != fp->iqh.ip_dst.s_addr ||
ip->ip_id != fp->iqh.ip_id ||
ip->ip_p != fp->iqh.ip_p)); fp = fp->iq_next);
if (!ip->ip_mff && ip->ip_off == 0)
{ /* not fragmented */
if (fp != NULL)
{ /* free existing reass chain */
struct ip *q;
struct ip *p;
q = fp->iqx.ip_next; /* free mbufs assoc. w/chain */
while (q != (struct ip *)fp)
{
p = q->ip_next;
m_freem(dtom(q));
q = p;
}
ip_freef(fp); /* free header */
}
/*
* The options aren't of any use to higher level
* protocols or of any concern to the user process.
*/
if (hlen > sizeof(struct ip))
ip_stripopt (ip, hlen);
}
else
{
ip = ip_reass(ip, fp, &fragsize);
if (ip == NULL)
return;
mp = dtom(ip);
}
/* call next level with completed datagram */
/* any raw readers?? */
/* if (rawreaders((struct rawcb *)ipsw[ip->ip_p].ipsw_raw->pr_ppcbq)) */
if (any_rawreaders())
{
otherstat.in_total++;
if (m = m_copy(mp, 0, M_COPYALL))
ipsw[ip->ip_p].ipsw_raw->pr_input(m);
}
if (ip->ip_p == IPPROTO_TCP)
/* wish I didn't need this special case for fragsize */
tcp_input(mp, fragsize);
else if (ipsw[ip->ip_p].ipsw_user != 0)
/* There's a protocol implementation for these packets */
ipsw[ip->ip_p].ipsw_user->pr_input(mp);
else if (ip->ip_p == IPPROTO_ICMP)
/*
* Since don't want user to get a non-raw ICMP socket, did not make
* an entry in the protocol jump table; also wanted to be able to
* make raw ICMP socket.
*/
icmp (mp);
else
{
/*
* Don't bother everyone on the net, and remember some other
* host may support the protocol.
*/
if ((!in_broadcast(ip->ip_src)) && (!in_broadcast(ip->ip_dst)))
ic_errmsg (icmp_addr(ip), ip->ip_src,
ICMP_UNRCH, ICMP_UNRCH_PR, FIELD_OFF(ip_p, struct ip),
sizeof(struct ip) + ICMP_ERRLEN, (char *) ip);
/* get rid of the packet */
ipstat.ip_drops++;
m_freem(mp);
}
}
/*
* We've received an IP fragment. Try to perform IP reassembly.
*/
struct ip *ip_reass(ip, fp, fragsize)
register struct ip *ip;
register struct ipq *fp;
int *fragsize;
{
register struct ip *q, *savq;
register struct mbuf *mp;
int hlen;
int i;
mp = dtom(ip);
hlen = ip->ip_hl << IP_HLSHIFT;
if (ip->ip_off != 0)
{
/*
* Only the first fragment retains the IP header.
*/
mp->m_off += hlen;
mp->m_len -= hlen;
}
if (fp == NULL)
{
/*
* This is the first fragment of the IP datagram that we've
* received. Set up reassembly q header.
*/
struct mbuf *m;
if ((m = m_get(M_WAIT, MT_FTABLE)) == NULL)
{
m_freem(mp);
return(NULL);
}
fp = mtod(m, struct ipq *);
fp->iqx.ip_next = fp->iqx.ip_prev = (struct ip *)fp;
bcopy((caddr_t)ip, (caddr_t)&fp->iqh, sizeof(struct ip));
fp->iqx.ip_ttl = MAXTTL;
fp->iq_size = 0;
/*
* and enter this into the list of fragmented IP datagrams
*/
fp->iq_next = NULL;
fp->iq_prev = ipfrags.n_ip_tail;
if (ipfrags.n_ip_head != NULL)
ipfrags.n_ip_tail->iq_next = fp;
else
ipfrags.n_ip_head = fp;
ipfrags.n_ip_tail = fp;
}
/*
* Merge fragment into reass.q
*
* Algorithm: Match start and end bytes of new
* fragment with fragments on the queue. If no
* overlaps are found, add new frag. to the queue.
* Otherwise, adjust start and end of new frag. so no
* overlap and add remainder to queue. If any
* fragments are completely covered by the new one, or
* if the new one is completely duplicated, free the
* fragments.
*/
q = fp->iqx.ip_next; /* -> top of reass. chain */
ip->ip_end = ip->ip_off + ip->ip_len - 1;
/* record the maximum fragment size for TCP */
fp->iq_size = MAX(ip->ip_len, fp->iq_size);
/* skip frags which new doesn't overlap at end */
while ((q != (struct ip *)fp) && (ip->ip_off > q->ip_end))
q = q->ip_next;
if (q == (struct ip *)fp)
{ /* frag at end of chain */
ip_enq(ip, fp->iqx.ip_prev);
}
else
{ /* frag doesn't overlap any */
if (ip->ip_end < q->ip_off)
{
ip_enq(ip, q->ip_prev);
/* new overlaps beginning of next frag only */
}
else if (ip->ip_end < q->ip_end)
{
if ((i = ip->ip_end-q->ip_off+1) < ip->ip_len)
{
ip->ip_len -= i;
ip->ip_end -= i;
m_adj(mp, -i);
ip_enq(ip, q->ip_prev);
}
else
m_freem(mp);
/* new overlaps end of previous frag */
}
else
{
savq = q;
if (ip->ip_off <= q->ip_off)
{
/* complete cover */
savq = q->ip_prev;
ip_deq(q);
m_freem(dtom(q));
}
else
{ /* overlap */
if ((i = q->ip_end-ip->ip_off+1)
< ip->ip_len)
{
ip->ip_off += i;
ip->ip_len -= i;
m_adj(mp, i);
}
else
ip->ip_len = 0;
}
/* new overlaps at beginning of successor frags */
q = savq->ip_next;
while ((q != (struct ip *)fp) &&
(ip->ip_len != 0) &&
(q->ip_off <= ip->ip_end))
/* complete cover */
if (q->ip_end <= ip->ip_end)
{
struct ip *next;
next = q->ip_next;
ip_deq(q);
m_freem(dtom(q));
q = next;
}
else
{ /* overlap */
if ((i = ip->ip_end-q->ip_off+1) < ip->ip_len)
{
ip->ip_len -= i;
ip->ip_end -= i;
m_adj(mp, -i);
}
else
ip->ip_len = 0;
break;
}
/* enqueue whatever is left of new before successors */
if (ip->ip_len != 0)
{
ip_enq(ip, savq);
}
else
m_freem(mp);
}
}
/* check for completed fragment reassembly */
if ((i = ip_done(&fp->iqx)) == 0)
return(NULL);
ip = fp->iqx.ip_next; /* -> top mbuf */
ip->ip_len = i; /* total data length */
*fragsize = fp->iq_size; /* remember for TCP */
/* option processing */
if ((hlen = ip->ip_hl<<IP_HLSHIFT) > sizeof(struct ip))
ip_stripopt (ip, hlen);
ip_mergef(&fp->iqx); /* clean frag chain */
/* copy src/dst internet address to header mbuf */
ip->ip_src = fp->iqh.ip_src;
ip->ip_dst = fp->iqh.ip_dst;
ip_freef(fp); /* dequeue header */
return(ip);
}
/*
* Let people control gateway action by patching this:
*/
int ip_forwarding = FALSE;
/*
* Try to forward the packet. Act like a gateway.
*/
ip_forward (fwdinp, ip, mp, hlen)
struct inpcb *fwdinp;
register struct ip *ip;
struct mbuf *mp;
int hlen;
{
register int type, code;
register u_short len;
register struct mbuf *mcopy;
register unsigned icmplen;
int error;
/*
* Also copy forwarded packets, just like copy TCP/UDP/RDP...
* packets sent to us so that can debug gateway action problems.
* It's easy enough for the user-level program to filter these
* out.
*/
if (any_rawreaders())
if (mcopy = m_copy(mp, 0, M_COPYALL))
raw_ip_input (mcopy);
if (ip->ip_ttl)
ip->ip_ttl --;
len = ntohs(ip->ip_len);
icmplen = MIN(len, hlen + ICMP_ERRLEN);
if (ip_forwarding && ip->ip_ttl)
{
/*
* Save chunk of ip packet in case there is an error
*/
mcopy = m_copy (mp, 0, (int)icmplen);
/*
* The packet is not sourced by the local machine, so
* save ourselves a useless call to ip_route in ip_send().
* Also, don't want ip_send to work if sending from
* 8.7.0.2 to 192.1.11.1 via 8.0.0.16, and 8.0.0.16
* knows about a default gateway on net 8. This can
* cause an ENETUNREACH if 192.1.11 is not a network
* that default gateway knows about. [8.0.0.16 is on
* 192.1.11 and ip_route uses default gateway on net 8
* while rtalloc uses local interface]
*
* This route should be found by rtalloc, so let's do
* it here.
*/
if (fwdinp->inp_route.ro_rt == NULL)
{
/* Isn't a source routed packet */
struct rtentry *rt;
struct sockaddr_in *sin;
bzero ((caddr_t) fwdinp, sizeof(*fwdinp));
sin = (struct sockaddr_in *) &fwdinp->inp_route.ro_dst;
sin->sin_family = AF_INET;
sin->sin_addr = ip->ip_dst;
rtalloc (&fwdinp->inp_route);
/*
* Check to see if should send ICMP redirect. Don't
* send redirect if source routing was used.
*/
if ((rt = fwdinp->inp_route.ro_rt) != NULL)
{
sin = (struct sockaddr_in *) &rt->rt_gateway;
if (! (rt->rt_flags & (RTF_GATEWAY|RTF_HOST)))
send_redirect (ip, ip->ip_dst, ICMP_REDIR_HOST, icmplen);
else if (iptonet(ip->ip_src) == iptonet(sin->sin_addr))
send_redirect (ip, sin->sin_addr, ICMP_REDIR_NET, icmplen);
}
}
if (fwdinp->inp_route.ro_rt == NULL)
{
/* no way to get there from here */
m_freem(mp);
type = ICMP_UNRCH;
code = ICMP_UNRCH_NET;
}
else
{
error = ip_send (fwdinp, mp, (int)len, TRUE);
rtfree(fwdinp->inp_route.ro_rt);
fwdinp->inp_route.ro_rt = NULL;
if (! error)
{
ipstat.ip_forwarded ++;
if (mcopy)
m_freem(mcopy);
return;
}
type = ICMP_UNRCH;
switch (error)
{
case ENETUNREACH:
case ENETDOWN:
code = ICMP_UNRCH_NET;
break;
case EMSGSIZE:
code = ICMP_UNRCH_FRAG;
break;
case EHOSTDOWN:
case EHOSTUNREACH:
code = ICMP_UNRCH_HOST;
break;
case ENOBUFS:
type = ICMP_SRCQ;
break;
default:
log(LOG_INFO, "ip_forward: error %d\n", error);
}
}
}
else
{
if (ip->ip_ttl == 0)
{
type = ICMP_TIMEX;
code = ICMP_TIMEX_XMT;
}
else
{
type = ICMP_UNRCH;
code = ICMP_UNRCH_NET;
}
mcopy = mp;
if (fwdinp->inp_route.ro_rt)
{
/* was source routed by IP option */
rtfree (fwdinp->inp_route.ro_rt);
fwdinp->inp_route.ro_rt = NULL;
}
}
if (mcopy)
{
ip = mtod(mcopy, struct ip *);
ic_errmsg (redir_addr(ip), ip->ip_src, type, code, 0, icmplen, (char *) ip);
#ifdef HMPTRAPS
/* hmp_trap(T_IP_ADDRS, (caddr_t) 0, 0); */
#else
if (ip_forwarding)
/*
* If not acting as a gateway, don't want some one else's
* misconception to flood our console or logfile. This error
* can be found through netstat an ip_drops.
*/
ip_log(ip, "ip forwarding error");
netlog(mcopy);
#endif
}
ipstat.ip_drops++;
}
/*
* Check to see if fragment reassembly is complete
*/
ip_done(p)
register struct ip *p;
{
register struct ip *q;
register next;
q = p->ip_next;
if (q->ip_off != 0)
return(0);
do
{
next = q->ip_end + 1;
q = q->ip_next;
}
while ((q != p) && (q->ip_off == next));
if ((q == p) && !(q->ip_prev->ip_mff)) /* all fragments in */
return(next); /* total data length */
else
return(0);
}
/*
* Merge mbufs of fragments of completed datagram
*/
ip_mergef(p)
register struct ip *p;
{
register struct mbuf *m, *n;
register struct ip *q;
int dummy;
q = p->ip_next; /* -> bottom of reass chain */
n = (struct mbuf *)&dummy; /* dummy for init assignment */
while (q != p)
{ /* through chain */
/*
* If free mbuf holding q, cannot access q->ip_next in case
* that mbuf is used by device code for an incoming packet.
*/
register struct ip *next;
next = q->ip_next;
n->m_next = m = dtom(q);
while (m != NULL)
{
if (m->m_len != 0)
{
n = m;
m = m->m_next;
}
else /* free null mbufs */
n->m_next = m = m_free(m);
}
q = next;
}
}
/*
* Dequeue and free reass.q header
*/
ip_freef(fp)
register struct ipq *fp;
{
if (fp->iq_prev != NULL)
(fp->iq_prev)->iq_next = fp->iq_next;
else
ipfrags.n_ip_head = fp->iq_next;
if (fp->iq_next != NULL)
(fp->iq_next)->iq_prev = fp->iq_prev;
else
ipfrags.n_ip_tail = fp->iq_prev;
m_free(dtom(fp));
}
ip_stripopt (ip, hlen)
struct ip *ip;
int hlen;
{
int optlen;
if ((optlen = (hlen - sizeof(struct ip))) > 0)
{
struct mbuf *m;
caddr_t end_of_ip, end_of_opt;
unsigned len;
m = dtom(ip);
end_of_ip = (char *) (ip +1);
end_of_opt = end_of_ip + optlen;
len = m->m_len - hlen;
bcopy (end_of_opt, end_of_ip, len);
m->m_len -= optlen;
}
}
/*
* Process ip options
* FALSE -> options were in error, and an icmp message has been sent
*/
#define OFF_OLEN 1
#define OFF_OFFSET 2
#define MIN_OFF 4 /* since option is a 1, not 0, based array */
/*
* Record route in same form as ip_setopt()
*/
save_rte (option, dst)
u_char *option;
struct in_addr dst;
{
int olen;
int off;
u_char *x;
struct in_addr *p, *q;
if (ip_nhops != 0)
{
/* Use both loose and strict source routing? */
log(LOG_INFO, "ip_nhops %d\n", ip_nhops);
ip_nhops = 0;
return;
}
olen = option[OFF_OLEN];
if (olen > sizeof(ip_hops))
{
log(LOG_INFO, "save_rte: olen %d\n", olen);
return;
}
off = option[OFF_OFFSET];
p = (struct in_addr *) (&option[off - 1]);
q = (struct in_addr *) (&option[MIN_OFF -1]);
x = (u_char *) ip_hops;
x[0] = IP_NOP_OPT;
x[1] = option[0]; /* loose/strict source routing */
x[2] = (p - q) * sizeof(struct in_addr) + 3;
x[3] = MIN_OFF;
ip_nhops ++; /* = 1 (1 long for opt hdr) */
p--; /* p points at first hop for return route */
ip_hops[p-q+2] = (*p); /* save first hop after return route option */
p--; /* it is in what will be ip_hops[ip_nhops] */
/* record return path as an IP source route */
while (p >= q)
{
ip_hops[ip_nhops] = (*p);
p--;
ip_nhops ++;
}
/* remember eventual destination is in the option field */
ip_hops[ip_nhops] = dst;
ip_nhops ++;
}
ip_opt (ip, hlen, fwdinp)
register struct ip *ip;
struct inpcb *fwdinp;
{
register u_char *endopt;
register u_char *option;
register int olen;
register int off;
int type;
int code;
struct in_addr nexthop;
struct in_ifaddr *ia;
endopt = ((u_char *) ip) + hlen;
option = (u_char *) (ip +1);
while (option < endopt)
{
switch (*option)
{
case IP_END_OPT:
return (TRUE);
case IP_NOP_OPT:
olen = sizeof(u_char);
break;
case IP_SEC_OPT:
olen = option[OFF_OLEN];
/* so much for security */
break;
case IP_LRTE_OPT:
olen = option[OFF_OLEN];
off = option[OFF_OFFSET];
if (off < MIN_OFF)
{
type = ICMP_PARM;
code = &option[OFF_OFFSET] - ((u_char *) ip);
goto err;
}
off--; /* adjust for use by C */
if (in_iawithaddr(ip->ip_dst, TRUE) == NULL)
/*
* With loose routing, may take a few hops
* to get to current nexthop.
*/
break;
if (off > (olen - sizeof(struct in_addr)))
{
/* hints all used up. pkt for us */
save_rte (option, ip->ip_src);
break;
}
nexthop = *((struct in_addr *) (option + off));
/*
* record outgoing interface
*/
if (! ip_opt_route(fwdinp, nexthop))
{
type = ICMP_UNRCH;
/* net? frag? host? */
code = ICMP_UNRCH_SRC;
goto err;
}
ip->ip_dst = nexthop;
option[OFF_OFFSET] += sizeof(struct in_addr);
if (fwdinp->inp_route.ro_rt == NULL)
/*
* Destined for ourselves, and we're
* just going to strip the options off
*/
break;
*((struct in_addr *) (option + off)) =
IA_INADDR(in_iafromif (fwdinp->inp_route.ro_rt->rt_ifp));
break;
case IP_SRTE_OPT:
olen = option[OFF_OLEN];
off = option[OFF_OFFSET];
if (off < MIN_OFF)
{
type = ICMP_PARM;
code = &option[OFF_OFFSET] - ((u_char *) ip);
goto err;
}
off--; /* adjust for use by C */
if (in_iawithaddr(ip->ip_dst, TRUE) == NULL)
{
/* strict path -> someone goofed */
/* should have come in on us for us */
type = ICMP_UNRCH;
code = ICMP_UNRCH_SRC;
goto err;
}
if (off > (olen - sizeof(struct in_addr)))
{
/* hints all used up */
save_rte (option, ip->ip_src);
break;
}
nexthop = *((struct in_addr *) (option + off));
if ((ia = in_iawithnet(nexthop)) == NULL)
{
/* strict path -> someone goofed
* we should be directly connected to
* next hop
*/
type = ICMP_UNRCH;
code = ICMP_UNRCH_SRC;
goto err;
}
*((struct in_addr *) (option + off)) = IA_INADDR(ia);
ip->ip_dst = nexthop;
option[OFF_OFFSET] += sizeof(struct in_addr);
break;
case IP_TIME_OPT:
olen = option[OFF_OLEN];
off = option[OFF_OFFSET];
if (off < MIN_OFF)
{
type = ICMP_PARM;
code = &option[OFF_OFFSET] - ((u_char *) ip);
goto err;
}
off--; /* adjust for use by C */
if (off > (olen - sizeof(u_long)))
{
/* increment overflow count */
if ((option[3] & 0xf0) == 0xf0)
{
/* overflow overflowed */
type = ICMP_PARM;
code = &option[OFF_OLEN] - ((u_char *) ip);
goto err;
}
option[3] += 0x10;
break;
}
if (option[3] & 2)
{
/* want specific host to stamp */
ia = in_iawithaddr(*((struct in_addr *) (option + off)), FALSE);
if (ia == NULL)
break;
}
else
ia = in_ifaddr;
if (option[3] & 1)
{
/* record stamping host */
*((struct in_addr *) (option + off)) = IA_INADDR (ia);
off += sizeof(struct in_addr);
option[OFF_OFFSET] += sizeof(struct in_addr);
}
if (off > (olen - sizeof(u_long)))
{
option[3] += 0x10;
break;
}
*((u_long *) (option + off)) = iptime();
option[OFF_OFFSET] += sizeof(u_long);
break;
case IP_STRID_OPT:
olen = option[OFF_OLEN];
break;
case IP_RRTE_OPT:
olen = option[OFF_OLEN];
off = option[OFF_OFFSET];
if (off < MIN_OFF)
{
type = ICMP_PARM;
code = &option[OFF_OFFSET] - ((u_char *) ip);
goto err;
}
off--; /* adjust for use by C */
if (off > (olen - sizeof(u_long)))
/* no space left for recording route */
break;
/* record outgoing interface */
if (! ip_opt_route(fwdinp, ip->ip_dst))
{
type = ICMP_UNRCH;
code = ICMP_UNRCH_NET;
goto err;
}
option[OFF_OFFSET] += sizeof(struct in_addr);
if (fwdinp->inp_route.ro_rt == NULL)
/*
* Destined for us, and we're just
* going to strip options off
*/
break;
*((struct in_addr *) (option + off)) =
IA_INADDR(in_iafromif (fwdinp->inp_route.ro_rt->rt_ifp));
break;
}
option += olen;
}
return (TRUE);
err :
ic_errmsg (icmp_addr(ip), ip->ip_src,
type, code, option - ((u_char *) ip), hlen, (char *) ip);
return (FALSE);
}
#undef OFF_OLEN
#undef OFF_OFFSET
#undef MIN_OFF
ip_opt_route (fwdinp, dst)
register struct inpcb *fwdinp;
struct in_addr dst;
{
register struct sockaddr_in *sin;
/* in case they use several options involving routing */
if (fwdinp->inp_route.ro_rt)
return (TRUE);
bzero ((caddr_t) fwdinp, sizeof(*fwdinp));
sin = (struct sockaddr_in *) &fwdinp->inp_route.ro_dst;
/* not sure ip_send cares about this stuff ... */
sin->sin_family = AF_INET;
sin->sin_addr = dst;
/* Don't allocate route if not forwarding packet.
* This saves us from doing a check in ip_input() to see
* if we should do a rtfree() for an uncommon occurrence.
*/
if (in_iawithaddr(dst, TRUE) != NULL)
return (TRUE);
rtalloc (&fwdinp->inp_route);
return (fwdinp->inp_route.ro_rt != NULL);
}
/*
* IP fragment reassembly timeout routine.
*/
ip_timeo()
{
register struct ip *p, *q;
register struct ipq *fp, *next;
register int s;
static int timflag;
/* check once per sec */
if (timflag = !timflag) /* looks strange, doesn't it? */
return;
/* search through reass.q */
s = splnet();
for (fp = ipfrags.n_ip_head; fp != NULL; fp = next)
{
/*
* If fragment times out, mbufs are freed, and can't
* use next pointer since mbuf may be grabbed by
* an interface at splimp
*/
next = fp->iq_next;
if (--(fp->iqx.ip_ttl) == 0)
{ /* time to die */
q = fp->iqx.ip_next; /* free mbufs assoc. w/chain */
while (q != (struct ip *)fp)
{
p = q->ip_next;
/* ### generate timed out in reassembly msg */
#ifdef HMPTRAPS
/* hmp_trap(T_IP_FDROP, (caddr_t)0,0); */
#else
m_freem(dtom(q));
#endif
q = p;
}
ip_freef(fp); /* free header */
}
}
splx(s);
}
/*
* Called at splimp from uipc_mbuf.c
* Network code needs to free up space! IP fragments dropped.
*/
ip_drain()
{
register struct ip *p, *q;
register struct ipq *fp;
while (fp = ipfrags.n_ip_head)
{
q = fp->iqx.ip_next; /* free mbufs assoc w/chain */
while (q != (struct ip *)fp)
{
p = q->ip_next;
m_freem(dtom(q));
q = p;
}
ip_freef(fp); /* free header */
}
}
#include "../h/syslog.h"
inet_cksum_err (protoname, ip, was, should_be)
char *protoname;
struct ip *ip;
u_long was;
u_long should_be;
{
union { u_long ul; u_char c[4]; } s, d;
s.ul = ip->ip_src.s_addr;
d.ul = ip->ip_dst.s_addr;
log (LOG_INFO,
"%s checksum was 0x%x not 0x%x src %d.%d.%d.%d dst %d.%d.%d.%d\n",
protoname, was, should_be,
s.c[0], s.c[1], s.c[2], s.c[3],
d.c[0], d.c[1], d.c[2], d.c[3]);
}
ip_log(ip, emsg)
struct ip *ip;
char *emsg;
{
union { u_long ul; u_char c[4]; } s, d;
s.ul = ip->ip_src.s_addr;
d.ul = ip->ip_dst.s_addr;
log(LOG_INFO, "%s: src %d.%d.%d.%d dst %d.%d.%d.%d\n",
emsg,
s.c[0], s.c[1], s.c[2], s.c[3],
d.c[0], d.c[1], d.c[2], d.c[3]);
}
no_route (msg, from, to)
char *msg;
struct in_addr from;
struct in_addr to;
{
union { u_long ul; u_char c[4]; } f, t;
f.ul = from.s_addr;
t.ul = to.s_addr;
log(LOG_INFO, "%s: no route %d.%d.%d.%d -> %d.%d.%d.%d\n",
msg,
f.c[0], f.c[1], f.c[2], f.c[3],
t.c[0], t.c[1], t.c[2], t.c[3]);
}