X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/6a7455e4d238f9f21835babe517fa1980ede72c1..15aae2a01acfd47907155f1dfecc7b2a98c2ab4b:/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 445b0759a0..1f2cd30802 100644 --- a/usr/src/sys/netinet/ip_input.c +++ b/usr/src/sys/netinet/ip_input.c @@ -1,62 +1,104 @@ -/* ip_input.c 1.10 81/10/31 */ +/* ip_input.c 1.56 82/10/23 */ #include "../h/param.h" #include "../h/systm.h" -#include "../h/clock.h" #include "../h/mbuf.h" -#include "../inet/cksum.h" -#include "../inet/inet.h" -#include "../inet/inet_systm.h" -#include "../inet/imp.h" -#include "../inet/ip.h" /* belongs before inet.h */ -#include "../inet/ip_icmp.h" -#include "../inet/tcp.h" - -int nosum = 0; - -struct ip *ip_reass(); +#include "../h/protosw.h" +#include "../h/socket.h" +#include +#include +#include "../h/kernel.h" + +#include "../net/if.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 input routines. + * IP initialization: fill in IP protocol switch table. + * All protocols not implemented in kernel go to raw IP protocol handler. */ +ip_init() +{ + register struct protosw *pr; + register int i; + + 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 && + pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW) + ip_protox[pr->pr_protocol] = pr - protosw; + ipq.next = ipq.prev = &ipq; + 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 * try to reassamble. If complete and fragment queue exists, discard. * Process options. Pass to next level. */ -ip_input(m0) - struct mbuf *m0; +ipintr() { - register struct ip *ip; /* known to be r11 in CKSUM below */ - register struct mbuf *m = m0; + register struct ip *ip; + register struct mbuf *m; + struct mbuf *m0; register int i; - register struct ipq *q; register struct ipq *fp; - int hlen; + int hlen, s; -COUNT(IP_INPUT); +next: /* - * Check header and byteswap. + * Get next datagram off input queue and get IP header + * in first mbuf. */ + s = splimp(); + IF_DEQUEUE(&ipintrq, m); + splx(s); + if (m == 0) + return; + if ((m->m_off > MMAXOFF || m->m_len < sizeof (struct ip)) && + (m = m_pullup(m, sizeof (struct ip))) == 0) + return; ip = mtod(m, struct ip *); if ((hlen = ip->ip_hl << 2) > m->m_len) { - printf("ip hdr ovflo\n"); - m_freem(m); - return; - } - CKSUM_IPCHK(m, ip, r11, hlen); - if (ip->ip_sum) { - printf("ip_sum %x\n", ip->ip_sum); - netstat.ip_badsum++; - if (!nosum) { - m_freem(m); + if ((m = m_pullup(m, hlen)) == 0) return; - } + ip = mtod(m, struct ip *); } - ip->ip_len = ntohs(ip->ip_len); + 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 || pdp11 || ns16032 + /* + * Convert fields to host representation. + */ + ip->ip_len = ntohs((u_short)ip->ip_len); ip->ip_id = ntohs(ip->ip_id); - ip->ip_off = ntohs(ip->ip_off); + ip->ip_off = ntohs((u_short)ip->ip_off); +#endif /* * Check that the amount of data in the buffers @@ -64,34 +106,57 @@ COUNT(IP_INPUT); * Trim mbufs if longer than we expect. * Drop packet if shorter than we expect. */ - i = 0; - for (; m != NULL; m = m->m_next) + i = -ip->ip_len; + m0 = m; + for (;;) { i += m->m_len; - m = m0; - if (i != ip->ip_len) { - if (i < ip->ip_len) { - printf("ip_input: short packet\n"); - m_freem(m); - return; + 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, hlen); - if (ip->ip_dst.s_addr != n_lhost.s_addr) { - if (--ip->ip_ttl == 0) { - icmp_error(ip, ICMP_TIMXCEED); - return; - } - ip_output(dtom(ip)); - return; + 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. @@ -111,9 +176,9 @@ found: * convert offset of this to bytes. */ ip->ip_len -= hlen; - ip->ip_mff = 0; + ((struct ipasfrag *)ip)->ipf_mff = 0; if (ip->ip_off & IP_MF) - ip->ip_mff = 1; + ((struct ipasfrag *)ip)->ipf_mff = 1; ip->ip_off <<= 3; /* @@ -121,10 +186,10 @@ found: * or if this is not the first fragment, * attempt reassembly; if it succeeds, proceed. */ - if (ip->ip_mff || ip->ip_off) { - ip = ip_reass(ip, fp); + if (((struct ipasfrag *)ip)->ipf_mff || ip->ip_off) { + ip = ip_reass((struct ipasfrag *)ip, fp); if (ip == 0) - return; + goto next; hlen = ip->ip_hl << 2; m = dtom(ip); } else @@ -132,46 +197,28 @@ found: (void) ip_freef(fp); /* - * Switch out to protocol specific routine. - * SHOULD GO THROUGH PROTOCOL SWITCH TABLE + * Switch out to protocol's input routine. */ - switch (ip->ip_p) { - - case IPPROTO_ICMP: - icmp_input(m); - break; - - case IPPROTO_TCP: - if (hlen > sizeof (struct ip)) - ip_stripoptions(ip, hlen); - tcp_input(m); - break; - - case IPPROTO_UDP: - if (hlen > sizeof (struct ip)) - ip_stripoptions(ip, hlen); - udp_input(m); - break; - - default: - raw_input(m); - break; - } + (*protosw[ip_protox[ip->ip_p]].pr_input)(m); + goto next; +bad: + m_freem(m); + goto next; } /* * Take incoming datagram fragment and try to - * reassamble it into whole datagram. If a chain for + * reassemble it into whole datagram. If a chain for * reassembly of this datagram already exists, then it * is given as fp; otherwise have to make a chain. */ struct ip * ip_reass(ip, fp) - register struct ip *ip; + register struct ipasfrag *ip; register struct ipq *fp; { register struct mbuf *m = dtom(ip); - register struct ip *q; + register struct ipasfrag *q; struct mbuf *t; int hlen = ip->ip_hl << 2; int i, next; @@ -187,23 +234,24 @@ ip_reass(ip, fp) * If first fragment to arrive, create a reassembly queue. */ if (fp == 0) { - if ((t = m_get(1)) == NULL) + if ((t = m_get(M_WAIT)) == NULL) goto dropfrag; - t->m_off = MMINOFF; fp = mtod(t, struct ipq *); insque(fp, &ipq); fp->ipq_ttl = IPFRAGTTL; fp->ipq_p = ip->ip_p; fp->ipq_id = ip->ip_id; - fp->ipq_next = fp->ipq_prev = (struct ip *)fp; - fp->ipq_src = ip->ip_src; - fp->ipq_dst = ip->ip_dst; + fp->ipq_next = fp->ipq_prev = (struct ipasfrag *)fp; + fp->ipq_src = ((struct ip *)ip)->ip_src; + fp->ipq_dst = ((struct ip *)ip)->ip_dst; + q = (struct ipasfrag *)fp; + goto insert; } /* * Find a segment which begins after this one does. */ - for (q = fp->ipq_next; q != (struct ip *)fp; q = q->ip_next) + for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next) if (q->ip_off > ip->ip_off) break; @@ -212,8 +260,8 @@ ip_reass(ip, fp) * our data already. If so, drop the data from the incoming * segment. If it provides all of our data, drop us. */ - if (q->ip_prev != (struct ip *)fp) { - i = q->ip_prev->ip_off + q->ip_prev->ip_len - ip->ip_off; + if (q->ipf_prev != (struct ipasfrag *)fp) { + i = q->ipf_prev->ip_off + q->ipf_prev->ip_len - ip->ip_off; if (i > 0) { if (i >= ip->ip_len) goto dropfrag; @@ -227,30 +275,32 @@ ip_reass(ip, fp) * While we overlap succeeding segments trim them or, * if they are completely covered, dequeue them. */ - while (q != (struct ip *)fp && ip->ip_off + ip->ip_len > q->ip_off) { + while (q != (struct ipasfrag *)fp && ip->ip_off + ip->ip_len > q->ip_off) { 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; } - q = q->ip_next; - m_freem(dtom(q->ip_prev)); - ip_deq(q->ip_prev); + q = q->ipf_next; + m_freem(dtom(q->ipf_prev)); + ip_deq(q->ipf_prev); } +insert: /* * Stick new segment in its place; * check for complete reassembly. */ - ip_enq(ip, q->ip_prev); + ip_enq(ip, q->ipf_prev); next = 0; - for (q = fp->ipq_next; q != (struct ip *)fp; q = q->ip_next) { + for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next) { if (q->ip_off != next) return (0); next += q->ip_len; } - if (q->ip_prev->ip_mff) + if (q->ipf_prev->ipf_mff) return (0); /* @@ -261,8 +311,12 @@ ip_reass(ip, fp) t = m->m_next; m->m_next = 0; m_cat(m, t); - while ((q = q->ip_next) != (struct ip *)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 @@ -272,14 +326,14 @@ ip_reass(ip, fp) */ ip = fp->ipq_next; ip->ip_len = next; - ip->ip_src = fp->ipq_src; - ip->ip_dst = fp->ipq_dst; + ((struct ip *)ip)->ip_src = fp->ipq_src; + ((struct ip *)ip)->ip_dst = fp->ipq_dst; remque(fp); - m_free(dtom(fp)); + (void) m_free(dtom(fp)); m = dtom(ip); - m->m_len += sizeof (struct ip); - m->m_off -= sizeof (struct ip); - return (ip); + m->m_len += sizeof (struct ipasfrag); + m->m_off -= sizeof (struct ipasfrag); + return ((struct ip *)ip); dropfrag: m_freem(m); @@ -294,15 +348,15 @@ struct ipq * ip_freef(fp) struct ipq *fp; { - register struct ip *q; + register struct ipasfrag *q; struct mbuf *m; - for (q = fp->ipq_next; q != (struct ip *)fp; q = q->ip_next) + for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next) m_freem(dtom(q)); m = dtom(fp); fp = fp->next; remque(fp->prev); - m_free(m); + (void) m_free(m); return (fp); } @@ -311,27 +365,24 @@ ip_freef(fp) * Like insque, but pointers in middle of structure. */ ip_enq(p, prev) - register struct ip *p; - register struct ip *prev; + register struct ipasfrag *p, *prev; { -COUNT(IP_ENQ); - p->ip_prev = prev; - p->ip_next = prev->ip_next; - prev->ip_next->ip_prev = p; - prev->ip_next = p; + p->ipf_prev = prev; + p->ipf_next = prev->ipf_next; + prev->ipf_next->ipf_prev = p; + prev->ipf_next = p; } /* * To ip_enq as remque is to insque. */ ip_deq(p) - register struct ip *p; + register struct ipasfrag *p; { -COUNT(IP_DEQ); - p->ip_prev->ip_next = p->ip_next; - p->ip_next->ip_prev = p->ip_prev; + p->ipf_prev->ipf_next = p->ipf_next; + p->ipf_next->ipf_prev = p->ipf_prev; } /* @@ -339,22 +390,34 @@ COUNT(IP_DEQ); * if a timer expires on a reassembly * queue, discard it. */ -ip_timeo() +ip_slowtimo() { - register struct ip *q; register struct ipq *fp; int s = splnet(); -COUNT(IP_TIMEO); - for (fp = ipq.next; fp != &ipq; ) + 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; - timeout(ip_timeo, 0, hz); splx(s); } +/* + * Drain off all datagram fragments. + */ +ip_drain() +{ + + while (ipq.next != &ipq) + (void) ip_freef(ipq.next); +} + /* * Do option processing on a datagram, * possibly discarding it if bad options @@ -364,8 +427,11 @@ ip_dooptions(ip) struct ip *ip; { register u_char *cp; - int opt, optlen, cnt, s; - struct socket *sp; + int opt, optlen, cnt, code, type; + struct in_addr *sin; + register struct ip_timestamp *ipt; + register struct ifnet *ifp; + struct in_addr t; cp = (u_char *)(ip + 1); cnt = (ip->ip_hl << 2) - sizeof (struct ip); @@ -382,110 +448,215 @@ ip_dooptions(ip) default: break; + /* + * Source routing with record. + * Find interface with current destination address. + * If none on this machine then drop if strictly routed, + * or do nothing if loosely routed. + * Record interface address and bring up next address + * component. If strictly routed make sure next + * address on directly accessible net. + */ case IPOPT_LSRR: case IPOPT_SSRR: - if (cp[2] < 4 || cp[2] > optlen - 3) + if (cp[2] < 4 || cp[2] > optlen - (sizeof (long) - 1)) + break; + sin = (struct in_addr *)(cp + cp[2]); + 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; break; - sp = (struct socket *)(cp+cp[2]); - if (n_lhost.s_addr == *(u_long *)sp) { - if (opt == IPOPT_SSRR) { - /* make sure *sp directly accessible*/ - } - ip->ip_dst = *sp; - *sp = n_lhost; - cp[2] += 4; } + t = ip->ip_dst; ip->ip_dst = *sin; *sin = t; + cp[2] += 4; + if (cp[2] > optlen - (sizeof (long) - 1)) + break; + ip->ip_dst = sin[1]; + if (opt == IPOPT_SSRR && + if_ifonnetof(in_netof(ip->ip_dst)) == 0) + goto bad; break; case IPOPT_TS: - if (cp[2] < 5) + code = cp - (u_char *)ip; + type = ICMP_PARAMPROB; + ipt = (struct ip_timestamp *)cp; + if (ipt->ipt_len < 5) goto bad; - if (cp[2] > cp[1] - 3) { - if ((cp[3] & 0xf0) == 0xf0) + if (ipt->ipt_ptr > ipt->ipt_len - sizeof (long)) { + if (++ipt->ipt_oflw == 0) goto bad; - cp[3] += 0x10; break; } - sp = (struct socket *)(cp+cp[2]); - switch (cp[3] & 0xf) { + sin = (struct in_addr *)(cp+cp[2]); + switch (ipt->ipt_flg) { case IPOPT_TS_TSONLY: break; case IPOPT_TS_TSANDADDR: - if (cp[2] > cp[1] - 7) + if (ipt->ipt_ptr + 8 > ipt->ipt_len) goto bad; + if (ifinet == 0) + goto bad; /* ??? */ + *sin++ = ((struct sockaddr_in *)&ifinet->if_addr)->sin_addr; break; case IPOPT_TS_PRESPEC: - if (*(u_long *)sp != n_lhost.s_addr) - break; - if (cp[2] > cp[1] - 7) + ipaddr.sin_addr = *sin; + if (!if_ifwithaddr((struct sockaddr *)&ipaddr)) + continue; + if (ipt->ipt_ptr + 8 > ipt->ipt_len) goto bad; - cp[1] += 4; + ipt->ipt_ptr += 4; break; default: goto bad; } - s = spl6(); - *(int *)sp = (time % SECDAY) * 1000 + (lbolt*1000/hz); - splx(s); - cp[1] += 4; + *(n_time *)sin = iptime(); + ipt->ipt_ptr += 4; } } return (0); bad: - /* SHOULD FORCE ICMP MESSAGE */ - return (-1); + icmp_error(ip, type, code); + return (1); } /* - * Strip out IP options, e.g. before passing - * to higher level protocol in the kernel. + * Strip out IP options, at higher + * level protocol in the kernel. + * Second argument is buffer to which options + * will be moved, and return value is their length. */ -ip_stripoptions(ip) +ip_stripoptions(ip, mopt) struct ip *ip; + struct mbuf *mopt; { register int i; register struct mbuf *m; - char *op; int olen; -COUNT(IP_OPT); olen = (ip->ip_hl<<2) - sizeof (struct ip); - op = (caddr_t)ip + olen; - m = dtom(++ip); + m = dtom(ip); + ip++; + if (mopt) { + mopt->m_len = olen; + mopt->m_off = MMINOFF; + bcopy((caddr_t)ip, mtod(m, caddr_t), (unsigned)olen); + } i = m->m_len - (sizeof (struct ip) + olen); - bcopy((caddr_t)ip+olen, (caddr_t)ip, i); - m->m_len -= i; + bcopy((caddr_t)ip+olen, (caddr_t)ip, (unsigned)i); + m->m_len -= olen; } -/* stubs */ - -icmp_error(ip, error) +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; - m_freem(dtom(ip)); + 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); } -icmp_input(m) - struct mbuf *m; +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); + if (mopt == 0) { + m_freem(dtom(ip)); + return; + } - printf("icmp_input %x\n", m); -} + /* + * 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); -udp_input(m) - struct mbuf *m; -{ + /* 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) { - printf("udp_input %x\n", m); -} + case ENETUNREACH: + case ENETDOWN: + code = ICMP_UNREACH_NET; + break; -raw_input(m) - struct mbuf *m; -{ + case EMSGSIZE: + code = ICMP_UNREACH_NEEDFRAG; + break; - printf("raw_input %x\n", m); + 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); }