/********************************************************************** * Copyright (c) Digital Equipment Corporation 1984, 1985, 1986. * * All Rights Reserved. * * Reference "/usr/src/COPYRIGHT" for applicable restrictions. * **********************************************************************/ /* * Copyright (c) 1982 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. * * SCCSID: @(#)ip_input.c 3.0 4/21/86 * Based on: @(#)ip_input.c 6.11 (Berkeley) 6/8/85 */ #include <sys/param.h> #include <sys/systm.h> #include <sys/mbuf.h> #include <sys/domain.h> #include <sys/protosw.h> #include <sys/socket.h> #include <errno.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/in_var.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 in_ifaddr *in_ifaddr; /* first inet address */ /* * 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 - 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) { if (pr->pr_protocol >= IPPROTO_MAX) panic("ip_init: proto"); ip_protox[pr->pr_protocol] = pr - inetsw; } ipq.next = ipq.prev = &ipq; #ifndef pdp11 ip_id = time.tv_sec & 0xffff; #else pdp11 ip_id = time & 0xffff; #endif pdp11 ipintrq.ifq_maxlen = ipqmaxlen; } 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. */ ipintr() { register struct ip *ip; register struct mbuf *m; struct mbuf *m0; register int i; register struct ipq *fp; register struct in_ifaddr *ia; int hlen, s; #ifdef pdp11 segm map; saveseg5(map); #endif pdp11 next: /* * Get next datagram off input queue and get IP header * in first mbuf. */ s = splimp(); IF_DEQUEUE(&ipintrq, m); splx(s); if (m == 0) { #ifdef pdp11 restorseg5(map); #endif pdp11 return; } if ((m->m_off > MMAXOFF || m->m_len < sizeof (struct ip)) && (m = m_pullup(m, sizeof (struct ip))) == 0) { ipstat.ips_toosmall++; goto next; } ip = mtod(m, struct ip *); hlen = ip->ip_hl << 2; if (hlen < 10) { /* minimum header length */ ipstat.ips_badhlen++; goto bad; } if (hlen > m->m_len) { 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)) { ipstat.ips_badsum++; goto bad; } /* * 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); /* * Check that the amount of data in the buffers * is as at least much as the IP header would have us expect. * Trim mbufs if longer than we expect. * Drop packet if shorter than we expect. */ i = -ip->ip_len; m0 = m; for (;;) { i += m->m_len; if (m->m_next == 0) break; m = m->m_next; } if (i != 0) { if (i < 0) { ipstat.ips_tooshort++; m = m0; goto bad; } 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. ip_dooptions returns 1 when an * error was detected (causing an icmp message * to be sent and the original packet to be freed). */ if (hlen > sizeof (struct ip) && ip_dooptions(ip)) goto next; /* * Check our list of addresses, to see if the packet is for us. */ for (ia = in_ifaddr; ia; ia = ia->ia_next) { #define satosin(sa) ((struct sockaddr_in *)(sa)) if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr) break; if ((ia->ia_ifp->if_flags & IFF_BROADCAST) && satosin(&ia->ia_broadaddr)->sin_addr.s_addr == ip->ip_dst.s_addr) break; /* * Look for all-0's host part (old broadcast addr). */ if ((ia->ia_ifp->if_flags & IFF_BROADCAST) && ia->ia_subnet == ntohl(ip->ip_dst.s_addr)) break; } if (ia == (struct in_ifaddr *)0) { ip_forward(ip); goto next; } /* * Look for queue of fragments * of this datagram. */ for (fp = ipq.next; fp != &ipq; fp = fp->next) if (ip->ip_id == fp->ipq_id && ip->ip_src.s_addr == fp->ipq_src.s_addr && ip->ip_dst.s_addr == fp->ipq_dst.s_addr && ip->ip_p == fp->ipq_p) goto found; fp = 0; found: /* * Adjust ip_len to not reflect header, * set ip_mff if more fragments are expected, * convert offset of this to bytes. */ ip->ip_len -= hlen; ((struct ipasfrag *)ip)->ipf_mff = 0; if (ip->ip_off & IP_MF) ((struct ipasfrag *)ip)->ipf_mff = 1; ip->ip_off <<= 3; /* * If datagram marked as having more fragments * or if this is not the first fragment, * attempt reassembly; if it succeeds, proceed. */ if (((struct ipasfrag *)ip)->ipf_mff || ip->ip_off) { ip = ip_reass((struct ipasfrag *)ip, fp); if (ip == 0) goto next; hlen = ip->ip_hl << 2; m = dtom(ip); } else if (fp) ip_freef(fp); /* * Switch out to protocol's input routine. */ #ifdef pdp11 if ((i = ip->ip_p) >= IPPROTO_MAX) i = IPPROTO_psudoRAW; (*inetsw[ip_protox[i]].pr_input)(m); #else pdp11 (*inetsw[ip_protox[ip->ip_p]].pr_input)(m); #endif pdp11 goto next; bad: m_freem(m); goto next; } #ifdef pdp11 #define DTOM(q) ((q)->ipf_mbuf) /* the mbuf for this fragment */ #else #define DTOM(q) dtom(q) #endif /* * Take incoming datagram fragment and try to * 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. */ static struct ip * ip_reass(ip, fp) register struct ipasfrag *ip; register struct ipq *fp; { register struct mbuf *m = dtom(ip); register struct ipasfrag *q; #ifdef pdp11 register struct ipasfrag *ipf; #endif pdp11 struct mbuf *t; int hlen = ip->ip_hl << 2; int i, next; /* * Presence of header sizes in mbufs * would confuse code below. */ m->m_off += hlen; m->m_len -= hlen; /* * If first fragment to arrive, create a reassembly queue. */ if (fp == 0) { #ifdef vax if ((t = m_get(M_WAIT, MT_FTABLE)) == NULL) goto dropfrag; fp = mtod(t, struct ipq *); #else pdp11 MSGET(fp, struct ipq, MT_FTABLE, 0, M_DONTWAIT); if (fp == NULL) goto dropfrag; #endif 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 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 ipasfrag *)fp; q = q->ipf_next) if (q->ip_off > ip->ip_off) break; /* * If there is a preceding segment, it may provide some of * our data already. If so, drop the data from the incoming * segment. If it provides all of our data, drop us. */ 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; #ifdef vax m_adj(dtom(ip), i); #else pdp11 m_adj(m, i); #endif ip->ip_off += i; ip->ip_len -= i; } } /* * While we overlap succeeding segments trim them or, * if they are completely covered, dequeue them. */ 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->ipf_next; m_freem(DTOM(q->ipf_prev)); ip_deq(q->ipf_prev); #ifdef pdp11 MSFREE(q->ipf_prev); #endif pdp11 } insert: /* * Stick new segment in its place; * check for complete reassembly. */ #ifdef pdp11 MSGET(ipf, struct ipasfrag, MT_FTABLE, 0, M_DONTWAIT); if (ipf == 0) goto dropfrag; bcopy(ip, ipf, sizeof *ipf); ipf->ipf_mbuf = m; ip = ipf; #endif ip_enq(ip, q->ipf_prev); next = 0; 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->ipf_prev->ipf_mff) return (0); /* * Reassembly is complete; concatenate fragments. */ q = fp->ipq_next; m = DTOM(q); #ifdef pdp11 hlen = q->ip_hl << 2; #endif pdp11 t = m->m_next; m->m_next = 0; m_cat(m, t); #ifdef pdp11 ipf = q; #endif pdp11 q = q->ipf_next; #ifdef pdp11 MSFREE(ipf); #endif pdp11 while (q != (struct ipasfrag *)fp) { t = DTOM(q); #ifdef pdp11 ipf = q; #endif q = q->ipf_next; #ifdef pdp11 MSFREE(ipf); #endif pdp11 m_cat(m, t); } /* * Create header for new ip packet by * modifying header of first packet; * dequeue and discard fragment reassembly header. * Make header visible. */ #ifdef vax ip = fp->ipq_next; #else pdp11 m->m_len += hlen; m->m_off -= hlen; ip = mtod(m, struct ipasfrag *); #endif ip->ip_len = next; ((struct ip *)ip)->ip_src = fp->ipq_src; ((struct ip *)ip)->ip_dst = fp->ipq_dst; remque(fp); #ifdef vax (void) m_free(dtom(fp)); m = dtom(ip); m->m_len += sizeof (struct ipasfrag); m->m_off -= sizeof (struct ipasfrag); #else pdp11 MSFREE(fp); #endif pdp11 return ((struct ip *)ip); dropfrag: m_freem(m); return (0); } /* * Free a fragment reassembly header and all * associated datagrams. */ static ip_freef(fp) struct ipq *fp; { register struct ipasfrag *q, *p; for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = p) { p = q->ipf_next; ip_deq(q); m_freem(DTOM(q)); #ifdef pdp11 MSFREE(q); #endif pdp11 } remque(fp); #ifdef vax (void) m_free(dtom(fp)); #else pdp11 MSFREE(fp); #endif } /* * Put an ip fragment on a reassembly chain. * Like insque, but pointers in middle of structure. */ static ip_enq(p, prev) register struct ipasfrag *p, *prev; { 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. */ static ip_deq(p) register struct ipasfrag *p; { p->ipf_prev->ipf_next = p->ipf_next; p->ipf_next->ipf_prev = p->ipf_prev; } /* * IP timer processing; * if a timer expires on a reassembly * queue, discard it. */ ip_slowtimo() { register struct ipq *fp; int s = splnet(); fp = ipq.next; if (fp == 0) { splx(s); return; } while (fp != &ipq) { --fp->ipq_ttl; fp = fp->next; if (fp->prev->ipq_ttl == 0) ip_freef(fp->prev); } splx(s); } /* * Drain off all datagram fragments. */ ip_drain() { while (ipq.next != &ipq) ip_freef(ipq.next); } /* * Do option processing on a datagram, * possibly discarding it if bad options * are encountered. */ static ip_dooptions(ip) struct ip *ip; { register u_char *cp; int opt, optlen, cnt, code, type; struct in_addr *sin; register struct ip_timestamp *ipt; register struct ifaddr *ifa; struct in_addr t; cp = (u_char *)(ip + 1); cnt = (ip->ip_hl << 2) - sizeof (struct ip); for (; cnt > 0; cnt -= optlen, cp += optlen) { opt = cp[0]; if (opt == IPOPT_EOL) break; if (opt == IPOPT_NOP) optlen = 1; else { optlen = cp[1]; if (optlen <= 0 || optlen >= cnt) goto bad; } switch (opt) { 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 - (sizeof (long) - 1)) break; sin = (struct in_addr *)(cp + cp[2]); ipaddr.sin_addr = *sin; ifa = ifa_ifwithaddr((struct sockaddr *)&ipaddr); type = ICMP_UNREACH, code = ICMP_UNREACH_SRCFAIL; if (ifa == 0) { if (opt == IPOPT_SSRR) goto bad; break; } 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 && in_iaonnetof(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; if (ipt->ipt_ptr > ipt->ipt_len - sizeof (long)) { if (++ipt->ipt_oflw == 0) goto bad; break; } sin = (struct in_addr *)(cp+cp[2]); switch (ipt->ipt_flg) { case IPOPT_TS_TSONLY: break; case IPOPT_TS_TSANDADDR: if (ipt->ipt_ptr + 8 > ipt->ipt_len) goto bad; if (in_ifaddr == 0) goto bad; /* ??? */ *sin++ = IA_SIN(in_ifaddr)->sin_addr; break; case IPOPT_TS_PRESPEC: ipaddr.sin_addr = *sin; if (ifa_ifwithaddr((struct sockaddr *)&ipaddr) == 0) continue; if (ipt->ipt_ptr + 8 > ipt->ipt_len) goto bad; ipt->ipt_ptr += 4; break; default: goto bad; } *(n_time *)sin = iptime(); ipt->ipt_ptr += 4; } } return (0); bad: icmp_error(ip, type, code); return (1); } /* * 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, mopt) struct ip *ip; struct mbuf *mopt; { register int i; register struct mbuf *m; int olen; olen = (ip->ip_hl<<2) - sizeof (struct ip); m = dtom(ip); ip++; if (mopt) { #ifdef pdp11 MAPSAVE(); #endif pdp11 mopt->m_len = olen; mopt->m_off = MMINOFF; #ifndef pdp11 bcopy((caddr_t)ip, mtod(mopt, caddr_t), (unsigned)olen); #else pdp11 if (olen) MBCOPY(m, sizeof *ip, mopt, 0, olen); MAPREST(); #endif pdp11 } #ifdef pdp11 if (olen == 0) return #endif pdp11 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 }; int ipprintfs = 0; int ipforwarding = 1; /* * Forward a packet. If some error occurs return the sender * an 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. */ static ip_forward(ip) register struct ip *ip; { register int error, type, code; struct mbuf *mcopy; #ifdef pdp11 struct mbuf *m = dtom(ip); #endif pdp11 if (ipprintfs) printf("forward: src %x dst %x ttl %x\n", ip->ip_src, ip->ip_dst, ip->ip_ttl); ip->ip_id = htons(ip->ip_id); 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; /* * Save at most 64 bytes of the packet in case * we need to generate an ICMP message to the src. */ #ifdef vax mcopy = m_copy(dtom(ip), 0, imin(ip->ip_len, 64)); #else pdp11 mcopy = m_copy(m, 0, MIN(ip->ip_len, 64)); #endif pdp11 #ifdef vax error = ip_output(dtom(ip), (struct mbuf *)0, (struct route *)0, IP_FORWARDING); #else pdp11 error = ip_output(m, (struct mbuf *)0, (struct route *)0, IP_FORWARDING); #endif if (error == 0) { if (mcopy) m_freem(mcopy); ipstat.ips_forward++; return; } ipstat.ips_cantforward++; 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); }