-/* ip_input.c 1.29 81/12/23 */
+/* ip_input.c 1.65 83/02/23 */
#include "../h/param.h"
#include "../h/systm.h"
-#include "../h/clock.h"
#include "../h/mbuf.h"
+#include "../h/domain.h"
#include "../h/protosw.h"
#include "../h/socket.h"
-#include "../net/in.h"
-#include "../net/in_systm.h"
+#include "../h/errno.h"
+#include "../h/time.h"
+#include "../h/kernel.h"
+
#include "../net/if.h"
-#include "../net/ip.h" /* belongs before in.h */
-#include "../net/ip_var.h"
-#include "../net/ip_icmp.h"
-#include "../net/tcp.h"
+#include "../net/route.h"
+
+#include "../netinet/in.h"
+#include "../netinet/in_pcb.h"
+#include "../netinet/in_systm.h"
+#include "../netinet/ip.h"
+#include "../netinet/ip_var.h"
+#include "../netinet/ip_icmp.h"
+#include "../netinet/tcp.h"
u_char ip_protox[IPPROTO_MAX];
+int ipqmaxlen = IFQ_MAXLEN;
+struct ifnet *ifinet; /* first inet interface */
/*
* IP initialization: fill in IP protocol switch table.
register struct protosw *pr;
register int i;
-COUNT(IP_INIT);
pr = pffindproto(PF_INET, IPPROTO_RAW);
if (pr == 0)
panic("ip_init");
for (i = 0; i < IPPROTO_MAX; i++)
- ip_protox[i] = pr - protosw;
- for (pr = protosw; pr <= protoswLAST; pr++)
+ ip_protox[i] = pr - inetsw;
+ for (pr = inetdomain.dom_protosw;
+ pr <= inetdomain.dom_protoswNPROTOSW; pr++)
if (pr->pr_family == PF_INET &&
pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW)
- ip_protox[pr->pr_protocol] = pr - protosw;
+ ip_protox[pr->pr_protocol] = pr - inetsw;
ipq.next = ipq.prev = &ipq;
- ip_id = time & 0xffff;
+ ip_id = time.tv_sec & 0xffff;
+ ipintrq.ifq_maxlen = ipqmaxlen;
+ ifinet = if_ifwithaf(AF_INET);
}
u_char ipcksum = 1;
struct ip *ip_reass();
+struct sockaddr_in ipaddr = { AF_INET };
/*
* Ip input routine. Checksum and byte swap header. If fragmented
{
register struct ip *ip;
register struct mbuf *m;
- struct mbuf *m0, *mopt;
+ struct mbuf *m0;
register int i;
register struct ipq *fp;
int hlen, s;
-COUNT(IPINTR);
next:
/*
* Get next datagram off input queue and get IP header
if (m == 0)
return;
if ((m->m_off > MMAXOFF || m->m_len < sizeof (struct ip)) &&
- (m = m_pullup(m, sizeof (struct ip))) == 0)
- return;
+ (m = m_pullup(m, sizeof (struct ip))) == 0) {
+ ipstat.ips_toosmall++;
+ goto next;
+ }
ip = mtod(m, struct ip *);
if ((hlen = ip->ip_hl << 2) > m->m_len) {
- if ((m = m_pullup(m, hlen)) == 0)
- return;
+ if ((m = m_pullup(m, hlen)) == 0) {
+ ipstat.ips_badhlen++;
+ goto next;
+ }
ip = mtod(m, struct ip *);
}
if (ipcksum)
if (ip->ip_sum = in_cksum(m, hlen)) {
- printf("ip_sum %x\n", ip->ip_sum); /* XXX */
ipstat.ips_badsum++;
goto bad;
}
-#if vax
/*
* Convert fields to host representation.
*/
ip->ip_len = ntohs((u_short)ip->ip_len);
+ if (ip->ip_len < hlen) {
+ ipstat.ips_badlen++;
+ goto bad;
+ }
ip->ip_id = ntohs(ip->ip_id);
ip->ip_off = ntohs((u_short)ip->ip_off);
-#endif
/*
* Check that the amount of data in the buffers
* Trim mbufs if longer than we expect.
* Drop packet if shorter than we expect.
*/
- i = 0;
+ i = -ip->ip_len;
m0 = m;
- for (; m != NULL; m = m->m_next)
+ for (;;) {
i += m->m_len;
- m = m0;
- if (i != ip->ip_len) {
- if (i < ip->ip_len) {
+ if (m->m_next == 0)
+ break;
+ m = m->m_next;
+ }
+ if (i != 0) {
+ if (i < 0) {
ipstat.ips_tooshort++;
goto bad;
}
- m_adj(m, ip->ip_len - i);
+ if (i <= m->m_len)
+ m->m_len -= i;
+ else
+ m_adj(m0, -i);
}
+ m = m0;
/*
* Process options and, if not destined for us,
- * ship it on.
+ * ship it on. ip_dooptions returns 1 when an
+ * error was detected (causing an icmp message
+ * to be sent).
*/
- if (hlen > sizeof (struct ip))
- ip_dooptions(ip);
- if (ifnet && ip->ip_dst.s_addr != ifnet->if_addr.s_addr &&
- if_ifwithaddr(ip->ip_dst) == 0) {
-printf("ip->ip_dst %x ip->ip_ttl %x\n", ip->ip_dst, ip->ip_ttl);
- if (--ip->ip_ttl == 0) {
- icmp_error(ip, ICMP_TIMXCEED, 0);
- goto next;
- }
- mopt = m_get(M_DONTWAIT);
- if (mopt == 0)
- goto bad;
- ip_stripoptions(ip, mopt);
- (void) ip_output(m0, mopt);
+ if (hlen > sizeof (struct ip) && ip_dooptions(ip))
+ goto next;
+
+ /*
+ * Fast check on the first internet
+ * interface in the list.
+ */
+ if (ifinet) {
+ struct sockaddr_in *sin;
+
+ sin = (struct sockaddr_in *)&ifinet->if_addr;
+ if (sin->sin_addr.s_addr == ip->ip_dst.s_addr)
+ goto ours;
+ sin = (struct sockaddr_in *)&ifinet->if_broadaddr;
+ if ((ifinet->if_flags & IFF_BROADCAST) &&
+ sin->sin_addr.s_addr == ip->ip_dst.s_addr)
+ goto ours;
+ }
+/* BEGIN GROT */
+#include "nd.h"
+#if NND > 0
+ /*
+ * Diskless machines don't initially know
+ * their address, so take packets from them
+ * if we're acting as a network disk server.
+ */
+ if (ip->ip_dst.s_addr == INADDR_ANY &&
+ (in_netof(ip->ip_src) == INADDR_ANY &&
+ in_lnaof(ip->ip_src) != INADDR_ANY))
+ goto ours;
+#endif
+/* END GROT */
+ ipaddr.sin_addr = ip->ip_dst;
+ if (if_ifwithaddr((struct sockaddr *)&ipaddr) == 0) {
+ ip_forward(ip);
goto next;
}
+ours:
/*
* Look for queue of fragments
* of this datagram.
m = dtom(ip);
} else
if (fp)
- (void) ip_freef(fp);
+ ip_freef(fp);
/*
* Switch out to protocol's input routine.
*/
- (*protosw[ip_protox[ip->ip_p]].pr_input)(m);
+ (*inetsw[ip_protox[ip->ip_p]].pr_input)(m);
goto next;
bad:
m_freem(m);
struct mbuf *t;
int hlen = ip->ip_hl << 2;
int i, next;
-COUNT(IP_REASS);
/*
* Presence of header sizes in mbufs
* If first fragment to arrive, create a reassembly queue.
*/
if (fp == 0) {
- if ((t = m_get(1)) == NULL)
+ if ((t = m_get(M_WAIT, MT_FTABLE)) == NULL)
goto dropfrag;
- t->m_off = MMINOFF;
fp = mtod(t, struct ipq *);
insque(fp, &ipq);
fp->ipq_ttl = IPFRAGTTL;
i = (ip->ip_off + ip->ip_len) - q->ip_off;
if (i < q->ip_len) {
q->ip_len -= i;
+ q->ip_off += i;
m_adj(dtom(q), i);
break;
}
t = m->m_next;
m->m_next = 0;
m_cat(m, t);
- while ((q = q->ipf_next) != (struct ipasfrag *)fp)
- m_cat(m, dtom(q));
+ q = q->ipf_next;
+ while (q != (struct ipasfrag *)fp) {
+ t = dtom(q);
+ q = q->ipf_next;
+ m_cat(m, t);
+ }
/*
* Create header for new ip packet by
* Free a fragment reassembly header and all
* associated datagrams.
*/
-struct ipq *
ip_freef(fp)
struct ipq *fp;
{
- register struct ipasfrag *q;
- struct mbuf *m;
-COUNT(IP_FREEF);
+ register struct ipasfrag *q, *p;
- for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next)
+ for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = p) {
+ p = q->ipf_next;
+ ip_deq(q);
m_freem(dtom(q));
- m = dtom(fp);
- fp = fp->next;
- remque(fp->prev);
- (void) m_free(m);
- return (fp);
+ }
+ remque(fp);
+ (void) m_free(dtom(fp));
}
/*
register struct ipasfrag *p, *prev;
{
-COUNT(IP_ENQ);
p->ipf_prev = prev;
p->ipf_next = prev->ipf_next;
prev->ipf_next->ipf_prev = p;
register struct ipasfrag *p;
{
-COUNT(IP_DEQ);
p->ipf_prev->ipf_next = p->ipf_next;
p->ipf_next->ipf_prev = p->ipf_prev;
}
register struct ipq *fp;
int s = splnet();
-COUNT(IP_SLOWTIMO);
fp = ipq.next;
if (fp == 0) {
splx(s);
return;
}
- while (fp != &ipq)
- if (--fp->ipq_ttl == 0)
- fp = ip_freef(fp);
- else
- fp = fp->next;
+ while (fp != &ipq) {
+ --fp->ipq_ttl;
+ fp = fp->next;
+ if (fp->prev->ipq_ttl == 0)
+ ip_freef(fp->prev);
+ }
splx(s);
}
ip_drain()
{
-COUNT(IP_DRAIN);
while (ipq.next != &ipq)
- (void) ip_freef(ipq.next);
+ ip_freef(ipq.next);
}
/*
struct ip *ip;
{
register u_char *cp;
- int opt, optlen, cnt;
+ int opt, optlen, cnt, code, type;
struct in_addr *sin;
register struct ip_timestamp *ipt;
register struct ifnet *ifp;
struct in_addr t;
-COUNT(IP_DOOPTIONS);
cp = (u_char *)(ip + 1);
cnt = (ip->ip_hl << 2) - sizeof (struct ip);
for (; cnt > 0; cnt -= optlen, cp += optlen) {
* address on directly accessible net.
*/
case IPOPT_LSRR:
+ case IPOPT_SSRR:
if (cp[2] < 4 || cp[2] > optlen - (sizeof (long) - 1))
break;
sin = (struct in_addr *)(cp + cp[2]);
- ifp = if_ifwithaddr(*sin);
+ ipaddr.sin_addr = *sin;
+ ifp = if_ifwithaddr((struct sockaddr *)&ipaddr);
+ type = ICMP_UNREACH, code = ICMP_UNREACH_SRCFAIL;
if (ifp == 0) {
if (opt == IPOPT_SSRR)
goto bad;
if (cp[2] > optlen - (sizeof (long) - 1))
break;
ip->ip_dst = sin[1];
- if (opt == IPOPT_SSRR && if_ifonnetof(ip->ip_dst)==0)
+ if (opt == IPOPT_SSRR &&
+ if_ifonnetof(in_netof(ip->ip_dst)) == 0)
goto bad;
break;
case IPOPT_TS:
+ code = cp - (u_char *)ip;
+ type = ICMP_PARAMPROB;
ipt = (struct ip_timestamp *)cp;
if (ipt->ipt_len < 5)
goto bad;
case IPOPT_TS_TSANDADDR:
if (ipt->ipt_ptr + 8 > ipt->ipt_len)
goto bad;
- /* stamp with ``first'' interface address */
- *sin++ = ifnet->if_addr;
+ if (ifinet == 0)
+ goto bad; /* ??? */
+ *sin++ = ((struct sockaddr_in *)&ifinet->if_addr)->sin_addr;
break;
case IPOPT_TS_PRESPEC:
- if (if_ifwithaddr(*sin) == 0)
+ ipaddr.sin_addr = *sin;
+ if (if_ifwithaddr((struct sockaddr *)&ipaddr) == 0)
continue;
if (ipt->ipt_ptr + 8 > ipt->ipt_len)
goto bad;
ipt->ipt_ptr += 4;
}
}
- return;
+ return (0);
bad:
- /* SHOULD FORCE ICMP MESSAGE */
- return;
+ icmp_error(ip, type, code);
+ return (1);
}
/*
register int i;
register struct mbuf *m;
int olen;
-COUNT(IP_STRIPOPTIONS);
olen = (ip->ip_hl<<2) - sizeof (struct ip);
m = dtom(ip);
bcopy((caddr_t)ip+olen, (caddr_t)ip, (unsigned)i);
m->m_len -= olen;
}
+
+u_char inetctlerrmap[] = {
+ ECONNABORTED, ECONNABORTED, 0, 0,
+ 0, 0,
+ EHOSTDOWN, EHOSTUNREACH, ENETUNREACH, EHOSTUNREACH,
+ ECONNREFUSED, ECONNREFUSED, EMSGSIZE, 0,
+ 0, 0, 0, 0
+};
+
+ip_ctlinput(cmd, arg)
+ int cmd;
+ caddr_t arg;
+{
+ struct in_addr *in;
+ int tcp_abort(), udp_abort();
+ extern struct inpcb tcb, udb;
+
+ if (cmd < 0 || cmd > PRC_NCMDS)
+ return;
+ if (inetctlerrmap[cmd] == 0)
+ return; /* XXX */
+ if (cmd == PRC_IFDOWN)
+ in = &((struct sockaddr_in *)arg)->sin_addr;
+ else if (cmd == PRC_HOSTDEAD || cmd == PRC_HOSTUNREACH)
+ in = (struct in_addr *)arg;
+ else
+ in = &((struct icmp *)arg)->icmp_ip.ip_dst;
+/* THIS IS VERY QUESTIONABLE, SHOULD HIT ALL PROTOCOLS */
+ in_pcbnotify(&tcb, in, (int)inetctlerrmap[cmd], tcp_abort);
+ in_pcbnotify(&udb, in, (int)inetctlerrmap[cmd], udp_abort);
+}
+
+int ipprintfs = 0;
+int ipforwarding = 1;
+/*
+ * Forward a packet. If some error occurs return the sender
+ * and icmp packet. Note we can't always generate a meaningful
+ * icmp message because icmp doesn't have a large enough repetoire
+ * of codes and types.
+ */
+ip_forward(ip)
+ register struct ip *ip;
+{
+ register int error, type, code;
+ struct mbuf *mopt, *mcopy;
+
+ if (ipprintfs)
+ printf("forward: src %x dst %x ttl %x\n", ip->ip_src,
+ ip->ip_dst, ip->ip_ttl);
+ if (ipforwarding == 0) {
+ /* can't tell difference between net and host */
+ type = ICMP_UNREACH, code = ICMP_UNREACH_NET;
+ goto sendicmp;
+ }
+ if (ip->ip_ttl < IPTTLDEC) {
+ type = ICMP_TIMXCEED, code = ICMP_TIMXCEED_INTRANS;
+ goto sendicmp;
+ }
+ ip->ip_ttl -= IPTTLDEC;
+ mopt = m_get(M_DONTWAIT, MT_DATA);
+ if (mopt == NULL) {
+ m_freem(dtom(ip));
+ return;
+ }
+
+ /*
+ * Save at most 64 bytes of the packet in case
+ * we need to generate an ICMP message to the src.
+ */
+ mcopy = m_copy(dtom(ip), 0, imin(ip->ip_len, 64));
+ ip_stripoptions(ip, mopt);
+
+ /* last 0 here means no directed broadcast */
+ if ((error = ip_output(dtom(ip), mopt, (struct route *)0, 0)) == 0) {
+ if (mcopy)
+ m_freem(mcopy);
+ return;
+ }
+ ip = mtod(mcopy, struct ip *);
+ type = ICMP_UNREACH, code = 0; /* need ``undefined'' */
+ switch (error) {
+
+ case ENETUNREACH:
+ case ENETDOWN:
+ code = ICMP_UNREACH_NET;
+ break;
+
+ case EMSGSIZE:
+ code = ICMP_UNREACH_NEEDFRAG;
+ break;
+
+ case EPERM:
+ code = ICMP_UNREACH_PORT;
+ break;
+
+ case ENOBUFS:
+ type = ICMP_SOURCEQUENCH;
+ break;
+
+ case EHOSTDOWN:
+ case EHOSTUNREACH:
+ code = ICMP_UNREACH_HOST;
+ break;
+ }
+sendicmp:
+ icmp_error(ip, type, code);
+}