X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/e6b33a0337d0d220510616ff5ecf3b428600ed0f..36bb5f9457b331be70c5595ae9f76489369de5ed:/usr/src/sys/netinet/ip_input.c diff --git a/usr/src/sys/netinet/ip_input.c b/usr/src/sys/netinet/ip_input.c index f0e6057595..1867ef30ed 100644 --- a/usr/src/sys/netinet/ip_input.c +++ b/usr/src/sys/netinet/ip_input.c @@ -1,20 +1,29 @@ -/* ip_input.c 1.30 82/02/15 */ - -#include "../h/param.h" -#include "../h/systm.h" -#include "../h/clock.h" -#include "../h/mbuf.h" -#include "../h/protosw.h" -#include "../h/socket.h" -#include "../net/in.h" -#include "../net/in_systm.h" +/* ip_input.c 6.8 84/12/20 */ + +#include "param.h" +#include "systm.h" +#include "mbuf.h" +#include "domain.h" +#include "protosw.h" +#include "socket.h" +#include "errno.h" +#include "time.h" +#include "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 "in.h" +#include "in_pcb.h" +#include "in_systm.h" +#include "ip.h" +#include "ip_var.h" +#include "ip_icmp.h" +#include "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. @@ -25,22 +34,25 @@ ip_init() 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++) - if (pr->pr_family == PF_INET && + ip_protox[i] = pr - inetsw; + for (pr = inetdomain.dom_protosw; + pr < inetdomain.dom_protoswNPROTOSW; pr++) + if (pr->pr_domain->dom_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 @@ -51,12 +63,11 @@ ipintr() { 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 @@ -68,29 +79,34 @@ next: 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 @@ -98,40 +114,58 @@ next: * 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++; + m = m0; 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; + } + 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. @@ -169,12 +203,12 @@ found: 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); @@ -197,7 +231,6 @@ ip_reass(ip, fp) struct mbuf *t; int hlen = ip->ip_hl << 2; int i, next; -COUNT(IP_REASS); /* * Presence of header sizes in mbufs @@ -210,9 +243,8 @@ COUNT(IP_REASS); * If first fragment to arrive, create a reassembly queue. */ if (fp == 0) { - if ((t = m_get(M_WAIT)) == 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; @@ -256,6 +288,7 @@ COUNT(IP_REASS); 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; } @@ -287,8 +320,12 @@ insert: 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 @@ -316,21 +353,18 @@ dropfrag: * 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)); } /* @@ -341,7 +375,6 @@ ip_enq(p, prev) register struct ipasfrag *p, *prev; { -COUNT(IP_ENQ); p->ipf_prev = prev; p->ipf_next = prev->ipf_next; prev->ipf_next->ipf_prev = p; @@ -355,7 +388,6 @@ ip_deq(p) register struct ipasfrag *p; { -COUNT(IP_DEQ); p->ipf_prev->ipf_next = p->ipf_next; p->ipf_next->ipf_prev = p->ipf_prev; } @@ -370,17 +402,17 @@ ip_slowtimo() 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); } @@ -390,9 +422,8 @@ COUNT(IP_SLOWTIMO); ip_drain() { -COUNT(IP_DRAIN); while (ipq.next != &ipq) - (void) ip_freef(ipq.next); + ip_freef(ipq.next); } /* @@ -404,13 +435,12 @@ ip_dooptions(ip) 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) { @@ -419,8 +449,11 @@ COUNT(IP_DOOPTIONS); break; if (opt == IPOPT_NOP) optlen = 1; - else + else { optlen = cp[1]; + if (optlen <= 0 || optlen >= cnt) + goto bad; + } switch (opt) { default: @@ -436,10 +469,13 @@ COUNT(IP_DOOPTIONS); * 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; @@ -450,11 +486,14 @@ COUNT(IP_DOOPTIONS); 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; @@ -472,12 +511,14 @@ COUNT(IP_DOOPTIONS); 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; @@ -491,10 +532,10 @@ COUNT(IP_DOOPTIONS); ipt->ipt_ptr += 4; } } - return; + return (0); bad: - /* SHOULD FORCE ICMP MESSAGE */ - return; + icmp_error(ip, type, code); + return (1); } /* @@ -510,7 +551,6 @@ ip_stripoptions(ip, mopt) register int i; register struct mbuf *m; int olen; -COUNT(IP_STRIPOPTIONS); olen = (ip->ip_hl<<2) - sizeof (struct ip); m = dtom(ip); @@ -518,9 +558,123 @@ COUNT(IP_STRIPOPTIONS); if (mopt) { mopt->m_len = olen; mopt->m_off = MMINOFF; - bcopy((caddr_t)ip, mtod(m, caddr_t), (unsigned)olen); + bcopy((caddr_t)ip, mtod(mopt, caddr_t), (unsigned)olen); } i = m->m_len - (sizeof (struct ip) + olen); bcopy((caddr_t)ip+olen, (caddr_t)ip, (unsigned)i); m->m_len -= olen; } + +u_char inetctlerrmap[PRC_NCMDS] = { + ECONNABORTED, ECONNABORTED, 0, 0, + 0, 0, EHOSTDOWN, EHOSTUNREACH, + ENETUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED, + EMSGSIZE, 0, 0, 0, + 0, 0, 0, 0 +}; + +ip_ctlinput(cmd, arg) + int cmd; + caddr_t arg; +{ + struct in_addr *in; + int in_rtchange(), 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 */ + if (cmd == PRC_REDIRECT_NET || cmd == PRC_REDIRECT_HOST) { + in_pcbnotify(&tcb, in, 0, in_rtchange); + in_pcbnotify(&udb, in, 0, in_rtchange); + } else { + 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); + + error = ip_output(dtom(ip), mopt, (struct route *)0, IP_FORWARDING); + if (error == 0) { + if (mcopy) + m_freem(mcopy); + return; + } + if (mcopy == NULL) + 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); +}