1df8bae1dSRodney W. Grimes /* 2df8bae1dSRodney W. Grimes * Copyright (c) 1982, 1986, 1988, 1993 3df8bae1dSRodney W. Grimes * The Regents of the University of California. All rights reserved. 4df8bae1dSRodney W. Grimes * 5df8bae1dSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 6df8bae1dSRodney W. Grimes * modification, are permitted provided that the following conditions 7df8bae1dSRodney W. Grimes * are met: 8df8bae1dSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 9df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 10df8bae1dSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 11df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 12df8bae1dSRodney W. Grimes * documentation and/or other materials provided with the distribution. 13df8bae1dSRodney W. Grimes * 3. All advertising materials mentioning features or use of this software 14df8bae1dSRodney W. Grimes * must display the following acknowledgement: 15df8bae1dSRodney W. Grimes * This product includes software developed by the University of 16df8bae1dSRodney W. Grimes * California, Berkeley and its contributors. 17df8bae1dSRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 18df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 19df8bae1dSRodney W. Grimes * without specific prior written permission. 20df8bae1dSRodney W. Grimes * 21df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31df8bae1dSRodney W. Grimes * SUCH DAMAGE. 32df8bae1dSRodney W. Grimes * 33df8bae1dSRodney W. Grimes * @(#)ip_input.c 8.2 (Berkeley) 1/4/94 3458938916SGarrett Wollman * $Id: ip_input.c,v 1.47 1996/09/08 13:45:49 davidg Exp $ 3558938916SGarrett Wollman * $ANA: ip_input.c,v 1.5 1996/09/18 14:34:59 wollman Exp $ 36df8bae1dSRodney W. Grimes */ 37df8bae1dSRodney W. Grimes 3858938916SGarrett Wollman #define _IP_VHL 3958938916SGarrett Wollman 4074a9466cSGary Palmer #include "opt_ipfw.h" 4174a9466cSGary Palmer 42df8bae1dSRodney W. Grimes #include <sys/param.h> 43df8bae1dSRodney W. Grimes #include <sys/systm.h> 44df8bae1dSRodney W. Grimes #include <sys/malloc.h> 45df8bae1dSRodney W. Grimes #include <sys/mbuf.h> 46df8bae1dSRodney W. Grimes #include <sys/domain.h> 47df8bae1dSRodney W. Grimes #include <sys/protosw.h> 48df8bae1dSRodney W. Grimes #include <sys/socket.h> 49df8bae1dSRodney W. Grimes #include <sys/errno.h> 50df8bae1dSRodney W. Grimes #include <sys/time.h> 51df8bae1dSRodney W. Grimes #include <sys/kernel.h> 521025071fSGarrett Wollman #include <sys/syslog.h> 53b5e8ce9fSBruce Evans #include <sys/sysctl.h> 54df8bae1dSRodney W. Grimes 55df8bae1dSRodney W. Grimes #include <net/if.h> 56df8bae1dSRodney W. Grimes #include <net/route.h> 57748e0b0aSGarrett Wollman #include <net/netisr.h> 58df8bae1dSRodney W. Grimes 59df8bae1dSRodney W. Grimes #include <netinet/in.h> 60df8bae1dSRodney W. Grimes #include <netinet/in_systm.h> 61b5e8ce9fSBruce Evans #include <netinet/in_var.h> 62df8bae1dSRodney W. Grimes #include <netinet/ip.h> 63df8bae1dSRodney W. Grimes #include <netinet/in_pcb.h> 64df8bae1dSRodney W. Grimes #include <netinet/in_var.h> 65df8bae1dSRodney W. Grimes #include <netinet/ip_var.h> 66df8bae1dSRodney W. Grimes #include <netinet/ip_icmp.h> 6758938916SGarrett Wollman #include <machine/in_cksum.h> 68df8bae1dSRodney W. Grimes 69f0068c4aSGarrett Wollman #include <sys/socketvar.h> 706ddbf1e2SGary Palmer 716ddbf1e2SGary Palmer #ifdef IPFIREWALL 726ddbf1e2SGary Palmer #include <netinet/ip_fw.h> 736ddbf1e2SGary Palmer #endif 746ddbf1e2SGary Palmer 751c5de19aSGarrett Wollman int rsvp_on = 0; 76f708ef1bSPoul-Henning Kamp static int ip_rsvp_on; 77f0068c4aSGarrett Wollman struct socket *ip_rsvpd; 78f0068c4aSGarrett Wollman 79d4fb926cSGarrett Wollman static int ipforwarding = 0; 800312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_FORWARDING, forwarding, CTLFLAG_RW, 810312fbe9SPoul-Henning Kamp &ipforwarding, 0, ""); 820312fbe9SPoul-Henning Kamp 83d4fb926cSGarrett Wollman static int ipsendredirects = 1; /* XXX */ 840312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_SENDREDIRECTS, redirect, CTLFLAG_RW, 850312fbe9SPoul-Henning Kamp &ipsendredirects, 0, ""); 860312fbe9SPoul-Henning Kamp 87df8bae1dSRodney W. Grimes int ip_defttl = IPDEFTTL; 880312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_DEFTTL, ttl, CTLFLAG_RW, 890312fbe9SPoul-Henning Kamp &ip_defttl, 0, ""); 900312fbe9SPoul-Henning Kamp 910312fbe9SPoul-Henning Kamp static int ip_dosourceroute = 0; 920312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_SOURCEROUTE, sourceroute, CTLFLAG_RW, 930312fbe9SPoul-Henning Kamp &ip_dosourceroute, 0, ""); 94df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC 950312fbe9SPoul-Henning Kamp static int ipprintfs = 0; 96df8bae1dSRodney W. Grimes #endif 97df8bae1dSRodney W. Grimes 98df8bae1dSRodney W. Grimes extern struct domain inetdomain; 99df8bae1dSRodney W. Grimes extern struct protosw inetsw[]; 100df8bae1dSRodney W. Grimes u_char ip_protox[IPPROTO_MAX]; 1010312fbe9SPoul-Henning Kamp static int ipqmaxlen = IFQ_MAXLEN; 102df8bae1dSRodney W. Grimes struct in_ifaddr *in_ifaddr; /* first inet address */ 103df8bae1dSRodney W. Grimes struct ifqueue ipintrq; 1040312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_INTRQMAXLEN, intr_queue_maxlen, CTLFLAG_RD, 1050312fbe9SPoul-Henning Kamp &ipintrq.ifq_maxlen, 0, ""); 1060312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_INTRQDROPS, intr_queue_drops, CTLFLAG_RD, 1070312fbe9SPoul-Henning Kamp &ipintrq.ifq_drops, 0, ""); 108df8bae1dSRodney W. Grimes 109f23b4c91SGarrett Wollman struct ipstat ipstat; 110f708ef1bSPoul-Henning Kamp static struct ipq ipq; 111f23b4c91SGarrett Wollman 1120312fbe9SPoul-Henning Kamp #ifdef IPCTL_DEFMTU 1130312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_DEFMTU, mtu, CTLFLAG_RW, 1140312fbe9SPoul-Henning Kamp &ip_mtu, 0, ""); 1150312fbe9SPoul-Henning Kamp #endif 1160312fbe9SPoul-Henning Kamp 11758938916SGarrett Wollman #if !defined(COMPAT_IPFW) || COMPAT_IPFW == 1 11858938916SGarrett Wollman #undef COMPAT_IPFW 11958938916SGarrett Wollman #define COMPAT_IPFW 1 12058938916SGarrett Wollman #else 12158938916SGarrett Wollman #undef COMPAT_IPFW 12258938916SGarrett Wollman #endif 12358938916SGarrett Wollman 12458938916SGarrett Wollman #ifdef COMPAT_IPFW 12523bf9953SPoul-Henning Kamp /* Firewall hooks */ 12623bf9953SPoul-Henning Kamp ip_fw_chk_t *ip_fw_chk_ptr; 12723bf9953SPoul-Henning Kamp ip_fw_ctl_t *ip_fw_ctl_ptr; 128e7319babSPoul-Henning Kamp 129fed1c7e9SSøren Schmidt /* IP Network Address Translation (NAT) hooks */ 130fed1c7e9SSøren Schmidt ip_nat_t *ip_nat_ptr; 131fed1c7e9SSøren Schmidt ip_nat_ctl_t *ip_nat_ctl_ptr; 13258938916SGarrett Wollman #endif 133fed1c7e9SSøren Schmidt 134e7319babSPoul-Henning Kamp /* 135df8bae1dSRodney W. Grimes * We need to save the IP options in case a protocol wants to respond 136df8bae1dSRodney W. Grimes * to an incoming packet over the same route if the packet got here 137df8bae1dSRodney W. Grimes * using IP source routing. This allows connection establishment and 138df8bae1dSRodney W. Grimes * maintenance when the remote end is on a network that is not known 139df8bae1dSRodney W. Grimes * to us. 140df8bae1dSRodney W. Grimes */ 1410312fbe9SPoul-Henning Kamp static int ip_nhops = 0; 142df8bae1dSRodney W. Grimes static struct ip_srcrt { 143df8bae1dSRodney W. Grimes struct in_addr dst; /* final destination */ 144df8bae1dSRodney W. Grimes char nop; /* one NOP to align */ 145df8bae1dSRodney W. Grimes char srcopt[IPOPT_OFFSET + 1]; /* OPTVAL, OLEN and OFFSET */ 146df8bae1dSRodney W. Grimes struct in_addr route[MAX_IPOPTLEN/sizeof(struct in_addr)]; 147df8bae1dSRodney W. Grimes } ip_srcrt; 148df8bae1dSRodney W. Grimes 14993e0e116SJulian Elischer #ifdef IPDIVERT 15093e0e116SJulian Elischer /* 15193e0e116SJulian Elischer * Shared variable between ip_input() and ip_reass() to communicate 15293e0e116SJulian Elischer * about which packets, once assembled from fragments, get diverted, 15393e0e116SJulian Elischer * and to which port. 15493e0e116SJulian Elischer */ 15593e0e116SJulian Elischer static u_short frag_divert_port; 15693e0e116SJulian Elischer #endif 15793e0e116SJulian Elischer 158df8bae1dSRodney W. Grimes static void save_rte __P((u_char *, struct in_addr)); 1590312fbe9SPoul-Henning Kamp static void ip_deq __P((struct ipasfrag *)); 1600312fbe9SPoul-Henning Kamp static int ip_dooptions __P((struct mbuf *)); 1610312fbe9SPoul-Henning Kamp static void ip_enq __P((struct ipasfrag *, struct ipasfrag *)); 1620312fbe9SPoul-Henning Kamp static void ip_forward __P((struct mbuf *, int)); 1630312fbe9SPoul-Henning Kamp static void ip_freef __P((struct ipq *)); 1640312fbe9SPoul-Henning Kamp static struct ip * 1650312fbe9SPoul-Henning Kamp ip_reass __P((struct ipasfrag *, struct ipq *)); 1660312fbe9SPoul-Henning Kamp static struct in_ifaddr * 1670312fbe9SPoul-Henning Kamp ip_rtaddr __P((struct in_addr)); 1680312fbe9SPoul-Henning Kamp static void ipintr __P((void)); 169df8bae1dSRodney W. Grimes /* 170df8bae1dSRodney W. Grimes * IP initialization: fill in IP protocol switch table. 171df8bae1dSRodney W. Grimes * All protocols not implemented in kernel go to raw IP protocol handler. 172df8bae1dSRodney W. Grimes */ 173df8bae1dSRodney W. Grimes void 174df8bae1dSRodney W. Grimes ip_init() 175df8bae1dSRodney W. Grimes { 176df8bae1dSRodney W. Grimes register struct protosw *pr; 177df8bae1dSRodney W. Grimes register int i; 178df8bae1dSRodney W. Grimes 179df8bae1dSRodney W. Grimes pr = pffindproto(PF_INET, IPPROTO_RAW, SOCK_RAW); 180df8bae1dSRodney W. Grimes if (pr == 0) 181df8bae1dSRodney W. Grimes panic("ip_init"); 182df8bae1dSRodney W. Grimes for (i = 0; i < IPPROTO_MAX; i++) 183df8bae1dSRodney W. Grimes ip_protox[i] = pr - inetsw; 184df8bae1dSRodney W. Grimes for (pr = inetdomain.dom_protosw; 185df8bae1dSRodney W. Grimes pr < inetdomain.dom_protoswNPROTOSW; pr++) 186df8bae1dSRodney W. Grimes if (pr->pr_domain->dom_family == PF_INET && 187df8bae1dSRodney W. Grimes pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW) 188df8bae1dSRodney W. Grimes ip_protox[pr->pr_protocol] = pr - inetsw; 189df8bae1dSRodney W. Grimes ipq.next = ipq.prev = &ipq; 190df8bae1dSRodney W. Grimes ip_id = time.tv_sec & 0xffff; 191df8bae1dSRodney W. Grimes ipintrq.ifq_maxlen = ipqmaxlen; 192b83e4314SPoul-Henning Kamp #ifdef IPFIREWALL 193b83e4314SPoul-Henning Kamp ip_fw_init(); 194b83e4314SPoul-Henning Kamp #endif 195fed1c7e9SSøren Schmidt #ifdef IPNAT 196fed1c7e9SSøren Schmidt ip_nat_init(); 197fed1c7e9SSøren Schmidt #endif 198fed1c7e9SSøren Schmidt 199df8bae1dSRodney W. Grimes } 200df8bae1dSRodney W. Grimes 2010312fbe9SPoul-Henning Kamp static struct sockaddr_in ipaddr = { sizeof(ipaddr), AF_INET }; 202f708ef1bSPoul-Henning Kamp static struct route ipforward_rt; 203df8bae1dSRodney W. Grimes 204df8bae1dSRodney W. Grimes /* 205df8bae1dSRodney W. Grimes * Ip input routine. Checksum and byte swap header. If fragmented 206df8bae1dSRodney W. Grimes * try to reassemble. Process options. Pass to next level. 207df8bae1dSRodney W. Grimes */ 208c67b1d17SGarrett Wollman void 209c67b1d17SGarrett Wollman ip_input(struct mbuf *m) 210df8bae1dSRodney W. Grimes { 21123bf9953SPoul-Henning Kamp struct ip *ip; 21223bf9953SPoul-Henning Kamp struct ipq *fp; 21323bf9953SPoul-Henning Kamp struct in_ifaddr *ia; 21409bb5f75SPoul-Henning Kamp int hlen; 215df8bae1dSRodney W. Grimes 216df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC 217df8bae1dSRodney W. Grimes if ((m->m_flags & M_PKTHDR) == 0) 21858938916SGarrett Wollman panic("ip_input no HDR"); 219df8bae1dSRodney W. Grimes #endif 220df8bae1dSRodney W. Grimes /* 221df8bae1dSRodney W. Grimes * If no IP addresses have been set yet but the interfaces 222df8bae1dSRodney W. Grimes * are receiving, can't do anything with incoming packets yet. 223df8bae1dSRodney W. Grimes */ 224df8bae1dSRodney W. Grimes if (in_ifaddr == NULL) 225df8bae1dSRodney W. Grimes goto bad; 226df8bae1dSRodney W. Grimes ipstat.ips_total++; 22758938916SGarrett Wollman 22858938916SGarrett Wollman if (m->m_pkthdr.len < sizeof(struct ip)) 22958938916SGarrett Wollman goto tooshort; 23058938916SGarrett Wollman 23158938916SGarrett Wollman #ifdef DIAGNOSTIC 23258938916SGarrett Wollman if (m->m_len < sizeof(struct ip)) 23358938916SGarrett Wollman panic("ipintr mbuf too short"); 23458938916SGarrett Wollman #endif 23558938916SGarrett Wollman 236df8bae1dSRodney W. Grimes if (m->m_len < sizeof (struct ip) && 237df8bae1dSRodney W. Grimes (m = m_pullup(m, sizeof (struct ip))) == 0) { 238df8bae1dSRodney W. Grimes ipstat.ips_toosmall++; 239c67b1d17SGarrett Wollman return; 240df8bae1dSRodney W. Grimes } 241df8bae1dSRodney W. Grimes ip = mtod(m, struct ip *); 24258938916SGarrett Wollman 24358938916SGarrett Wollman if (IP_VHL_V(ip->ip_vhl) != IPVERSION) { 244df8bae1dSRodney W. Grimes ipstat.ips_badvers++; 245df8bae1dSRodney W. Grimes goto bad; 246df8bae1dSRodney W. Grimes } 24758938916SGarrett Wollman 24858938916SGarrett Wollman hlen = IP_VHL_HL(ip->ip_vhl) << 2; 249df8bae1dSRodney W. Grimes if (hlen < sizeof(struct ip)) { /* minimum header length */ 250df8bae1dSRodney W. Grimes ipstat.ips_badhlen++; 251df8bae1dSRodney W. Grimes goto bad; 252df8bae1dSRodney W. Grimes } 253df8bae1dSRodney W. Grimes if (hlen > m->m_len) { 254df8bae1dSRodney W. Grimes if ((m = m_pullup(m, hlen)) == 0) { 255df8bae1dSRodney W. Grimes ipstat.ips_badhlen++; 256c67b1d17SGarrett Wollman return; 257df8bae1dSRodney W. Grimes } 258df8bae1dSRodney W. Grimes ip = mtod(m, struct ip *); 259df8bae1dSRodney W. Grimes } 26058938916SGarrett Wollman if (hlen == sizeof(struct ip)) { 26158938916SGarrett Wollman ip->ip_sum = in_cksum_hdr(ip); 26258938916SGarrett Wollman } else { 263623ae52eSPoul-Henning Kamp ip->ip_sum = in_cksum(m, hlen); 26458938916SGarrett Wollman } 265623ae52eSPoul-Henning Kamp if (ip->ip_sum) { 266df8bae1dSRodney W. Grimes ipstat.ips_badsum++; 267df8bae1dSRodney W. Grimes goto bad; 268df8bae1dSRodney W. Grimes } 269df8bae1dSRodney W. Grimes 270df8bae1dSRodney W. Grimes /* 271df8bae1dSRodney W. Grimes * Convert fields to host representation. 272df8bae1dSRodney W. Grimes */ 273df8bae1dSRodney W. Grimes NTOHS(ip->ip_len); 274df8bae1dSRodney W. Grimes if (ip->ip_len < hlen) { 275df8bae1dSRodney W. Grimes ipstat.ips_badlen++; 276df8bae1dSRodney W. Grimes goto bad; 277df8bae1dSRodney W. Grimes } 278df8bae1dSRodney W. Grimes NTOHS(ip->ip_id); 279df8bae1dSRodney W. Grimes NTOHS(ip->ip_off); 280df8bae1dSRodney W. Grimes 281df8bae1dSRodney W. Grimes /* 282df8bae1dSRodney W. Grimes * Check that the amount of data in the buffers 283df8bae1dSRodney W. Grimes * is as at least much as the IP header would have us expect. 284df8bae1dSRodney W. Grimes * Trim mbufs if longer than we expect. 285df8bae1dSRodney W. Grimes * Drop packet if shorter than we expect. 286df8bae1dSRodney W. Grimes */ 287df8bae1dSRodney W. Grimes if (m->m_pkthdr.len < ip->ip_len) { 28858938916SGarrett Wollman tooshort: 289df8bae1dSRodney W. Grimes ipstat.ips_tooshort++; 290df8bae1dSRodney W. Grimes goto bad; 291df8bae1dSRodney W. Grimes } 292df8bae1dSRodney W. Grimes if (m->m_pkthdr.len > ip->ip_len) { 293df8bae1dSRodney W. Grimes if (m->m_len == m->m_pkthdr.len) { 294df8bae1dSRodney W. Grimes m->m_len = ip->ip_len; 295df8bae1dSRodney W. Grimes m->m_pkthdr.len = ip->ip_len; 296df8bae1dSRodney W. Grimes } else 297df8bae1dSRodney W. Grimes m_adj(m, ip->ip_len - m->m_pkthdr.len); 298df8bae1dSRodney W. Grimes } 2994dd1662bSUgen J.S. Antsilevich /* 3004dd1662bSUgen J.S. Antsilevich * IpHack's section. 3014dd1662bSUgen J.S. Antsilevich * Right now when no processing on packet has done 3024dd1662bSUgen J.S. Antsilevich * and it is still fresh out of network we do our black 3034dd1662bSUgen J.S. Antsilevich * deals with it. 30493e0e116SJulian Elischer * - Firewall: deny/allow/divert 305fed1c7e9SSøren Schmidt * - Xlate: translate packet's addr/port (NAT). 3064dd1662bSUgen J.S. Antsilevich * - Wrap: fake packet's addr/port <unimpl.> 3074dd1662bSUgen J.S. Antsilevich * - Encapsulate: put it in another IP and send out. <unimp.> 3084dd1662bSUgen J.S. Antsilevich */ 309df8bae1dSRodney W. Grimes 31058938916SGarrett Wollman #ifdef COMPAT_IPFW 31193e0e116SJulian Elischer if (ip_fw_chk_ptr) { 31293e0e116SJulian Elischer int action; 31393e0e116SJulian Elischer 31493e0e116SJulian Elischer #ifdef IPDIVERT 31593e0e116SJulian Elischer action = (*ip_fw_chk_ptr)(&ip, hlen, 31693e0e116SJulian Elischer m->m_pkthdr.rcvif, ip_divert_ignore, &m); 31793e0e116SJulian Elischer #else 31893e0e116SJulian Elischer action = (*ip_fw_chk_ptr)(&ip, hlen, m->m_pkthdr.rcvif, 0, &m); 31993e0e116SJulian Elischer #endif 32093e0e116SJulian Elischer if (action == -1) 321539e53baSPoul-Henning Kamp return; 32293e0e116SJulian Elischer if (action != 0) { 32393e0e116SJulian Elischer #ifdef IPDIVERT 32493e0e116SJulian Elischer frag_divert_port = action; 32593e0e116SJulian Elischer goto ours; 32693e0e116SJulian Elischer #else 32793e0e116SJulian Elischer goto bad; /* ipfw said divert but we can't */ 32893e0e116SJulian Elischer #endif 32993e0e116SJulian Elischer } 33093e0e116SJulian Elischer } 331100ba1a6SJordan K. Hubbard 332fed1c7e9SSøren Schmidt if (ip_nat_ptr && !(*ip_nat_ptr)(&ip, &m, IP_NAT_IN)) 333fed1c7e9SSøren Schmidt return; 33458938916SGarrett Wollman #endif 335fed1c7e9SSøren Schmidt 336df8bae1dSRodney W. Grimes /* 337df8bae1dSRodney W. Grimes * Process options and, if not destined for us, 338df8bae1dSRodney W. Grimes * ship it on. ip_dooptions returns 1 when an 339df8bae1dSRodney W. Grimes * error was detected (causing an icmp message 340df8bae1dSRodney W. Grimes * to be sent and the original packet to be freed). 341df8bae1dSRodney W. Grimes */ 342df8bae1dSRodney W. Grimes ip_nhops = 0; /* for source routed packets */ 343df8bae1dSRodney W. Grimes if (hlen > sizeof (struct ip) && ip_dooptions(m)) 344c67b1d17SGarrett Wollman return; 345df8bae1dSRodney W. Grimes 346f0068c4aSGarrett Wollman /* greedy RSVP, snatches any PATH packet of the RSVP protocol and no 347f0068c4aSGarrett Wollman * matter if it is destined to another node, or whether it is 348f0068c4aSGarrett Wollman * a multicast one, RSVP wants it! and prevents it from being forwarded 349f0068c4aSGarrett Wollman * anywhere else. Also checks if the rsvp daemon is running before 350f0068c4aSGarrett Wollman * grabbing the packet. 351f0068c4aSGarrett Wollman */ 3521c5de19aSGarrett Wollman if (rsvp_on && ip->ip_p==IPPROTO_RSVP) 353f0068c4aSGarrett Wollman goto ours; 354f0068c4aSGarrett Wollman 355df8bae1dSRodney W. Grimes /* 356df8bae1dSRodney W. Grimes * Check our list of addresses, to see if the packet is for us. 357df8bae1dSRodney W. Grimes */ 358df8bae1dSRodney W. Grimes for (ia = in_ifaddr; ia; ia = ia->ia_next) { 359df8bae1dSRodney W. Grimes #define satosin(sa) ((struct sockaddr_in *)(sa)) 360df8bae1dSRodney W. Grimes 361df8bae1dSRodney W. Grimes if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr) 362df8bae1dSRodney W. Grimes goto ours; 3636ed666afSPoul-Henning Kamp if (ia->ia_ifp && ia->ia_ifp->if_flags & IFF_BROADCAST) { 364df8bae1dSRodney W. Grimes if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr == 365df8bae1dSRodney W. Grimes ip->ip_dst.s_addr) 366df8bae1dSRodney W. Grimes goto ours; 367df8bae1dSRodney W. Grimes if (ip->ip_dst.s_addr == ia->ia_netbroadcast.s_addr) 368df8bae1dSRodney W. Grimes goto ours; 369df8bae1dSRodney W. Grimes } 370df8bae1dSRodney W. Grimes } 371df8bae1dSRodney W. Grimes if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) { 372df8bae1dSRodney W. Grimes struct in_multi *inm; 373df8bae1dSRodney W. Grimes if (ip_mrouter) { 374df8bae1dSRodney W. Grimes /* 375df8bae1dSRodney W. Grimes * If we are acting as a multicast router, all 376df8bae1dSRodney W. Grimes * incoming multicast packets are passed to the 377df8bae1dSRodney W. Grimes * kernel-level multicast forwarding function. 378df8bae1dSRodney W. Grimes * The packet is returned (relatively) intact; if 379df8bae1dSRodney W. Grimes * ip_mforward() returns a non-zero value, the packet 380df8bae1dSRodney W. Grimes * must be discarded, else it may be accepted below. 381df8bae1dSRodney W. Grimes * 382df8bae1dSRodney W. Grimes * (The IP ident field is put in the same byte order 383df8bae1dSRodney W. Grimes * as expected when ip_mforward() is called from 384df8bae1dSRodney W. Grimes * ip_output().) 385df8bae1dSRodney W. Grimes */ 386df8bae1dSRodney W. Grimes ip->ip_id = htons(ip->ip_id); 387f0068c4aSGarrett Wollman if (ip_mforward(ip, m->m_pkthdr.rcvif, m, 0) != 0) { 388df8bae1dSRodney W. Grimes ipstat.ips_cantforward++; 389df8bae1dSRodney W. Grimes m_freem(m); 390c67b1d17SGarrett Wollman return; 391df8bae1dSRodney W. Grimes } 392df8bae1dSRodney W. Grimes ip->ip_id = ntohs(ip->ip_id); 393df8bae1dSRodney W. Grimes 394df8bae1dSRodney W. Grimes /* 395df8bae1dSRodney W. Grimes * The process-level routing demon needs to receive 396df8bae1dSRodney W. Grimes * all multicast IGMP packets, whether or not this 397df8bae1dSRodney W. Grimes * host belongs to their destination groups. 398df8bae1dSRodney W. Grimes */ 399df8bae1dSRodney W. Grimes if (ip->ip_p == IPPROTO_IGMP) 400df8bae1dSRodney W. Grimes goto ours; 401df8bae1dSRodney W. Grimes ipstat.ips_forward++; 402df8bae1dSRodney W. Grimes } 403df8bae1dSRodney W. Grimes /* 404df8bae1dSRodney W. Grimes * See if we belong to the destination multicast group on the 405df8bae1dSRodney W. Grimes * arrival interface. 406df8bae1dSRodney W. Grimes */ 407df8bae1dSRodney W. Grimes IN_LOOKUP_MULTI(ip->ip_dst, m->m_pkthdr.rcvif, inm); 408df8bae1dSRodney W. Grimes if (inm == NULL) { 409df8bae1dSRodney W. Grimes ipstat.ips_cantforward++; 410df8bae1dSRodney W. Grimes m_freem(m); 411c67b1d17SGarrett Wollman return; 412df8bae1dSRodney W. Grimes } 413df8bae1dSRodney W. Grimes goto ours; 414df8bae1dSRodney W. Grimes } 415df8bae1dSRodney W. Grimes if (ip->ip_dst.s_addr == (u_long)INADDR_BROADCAST) 416df8bae1dSRodney W. Grimes goto ours; 417df8bae1dSRodney W. Grimes if (ip->ip_dst.s_addr == INADDR_ANY) 418df8bae1dSRodney W. Grimes goto ours; 419df8bae1dSRodney W. Grimes 420df8bae1dSRodney W. Grimes /* 421df8bae1dSRodney W. Grimes * Not for us; forward if possible and desirable. 422df8bae1dSRodney W. Grimes */ 423df8bae1dSRodney W. Grimes if (ipforwarding == 0) { 424df8bae1dSRodney W. Grimes ipstat.ips_cantforward++; 425df8bae1dSRodney W. Grimes m_freem(m); 426df8bae1dSRodney W. Grimes } else 427df8bae1dSRodney W. Grimes ip_forward(m, 0); 428c67b1d17SGarrett Wollman return; 429df8bae1dSRodney W. Grimes 430df8bae1dSRodney W. Grimes ours: 431100ba1a6SJordan K. Hubbard 43263f8d699SJordan K. Hubbard /* 433df8bae1dSRodney W. Grimes * If offset or IP_MF are set, must reassemble. 434df8bae1dSRodney W. Grimes * Otherwise, nothing need be done. 435df8bae1dSRodney W. Grimes * (We could look in the reassembly queue to see 436df8bae1dSRodney W. Grimes * if the packet was previously fragmented, 437df8bae1dSRodney W. Grimes * but it's not worth the time; just let them time out.) 438df8bae1dSRodney W. Grimes */ 439df8bae1dSRodney W. Grimes if (ip->ip_off &~ IP_DF) { 440df8bae1dSRodney W. Grimes if (m->m_flags & M_EXT) { /* XXX */ 441df8bae1dSRodney W. Grimes if ((m = m_pullup(m, sizeof (struct ip))) == 0) { 442df8bae1dSRodney W. Grimes ipstat.ips_toosmall++; 44393e0e116SJulian Elischer #ifdef IPDIVERT 44493e0e116SJulian Elischer frag_divert_port = 0; 44593e0e116SJulian Elischer #endif 446c67b1d17SGarrett Wollman return; 447df8bae1dSRodney W. Grimes } 448df8bae1dSRodney W. Grimes ip = mtod(m, struct ip *); 449df8bae1dSRodney W. Grimes } 450df8bae1dSRodney W. Grimes /* 451df8bae1dSRodney W. Grimes * Look for queue of fragments 452df8bae1dSRodney W. Grimes * of this datagram. 453df8bae1dSRodney W. Grimes */ 454df8bae1dSRodney W. Grimes for (fp = ipq.next; fp != &ipq; fp = fp->next) 455df8bae1dSRodney W. Grimes if (ip->ip_id == fp->ipq_id && 456df8bae1dSRodney W. Grimes ip->ip_src.s_addr == fp->ipq_src.s_addr && 457df8bae1dSRodney W. Grimes ip->ip_dst.s_addr == fp->ipq_dst.s_addr && 458df8bae1dSRodney W. Grimes ip->ip_p == fp->ipq_p) 459df8bae1dSRodney W. Grimes goto found; 460df8bae1dSRodney W. Grimes fp = 0; 461df8bae1dSRodney W. Grimes found: 462df8bae1dSRodney W. Grimes 463df8bae1dSRodney W. Grimes /* 464df8bae1dSRodney W. Grimes * Adjust ip_len to not reflect header, 465df8bae1dSRodney W. Grimes * set ip_mff if more fragments are expected, 466df8bae1dSRodney W. Grimes * convert offset of this to bytes. 467df8bae1dSRodney W. Grimes */ 468df8bae1dSRodney W. Grimes ip->ip_len -= hlen; 469df8bae1dSRodney W. Grimes ((struct ipasfrag *)ip)->ipf_mff &= ~1; 470df8bae1dSRodney W. Grimes if (ip->ip_off & IP_MF) 471df8bae1dSRodney W. Grimes ((struct ipasfrag *)ip)->ipf_mff |= 1; 472df8bae1dSRodney W. Grimes ip->ip_off <<= 3; 473df8bae1dSRodney W. Grimes 474df8bae1dSRodney W. Grimes /* 475df8bae1dSRodney W. Grimes * If datagram marked as having more fragments 476df8bae1dSRodney W. Grimes * or if this is not the first fragment, 477df8bae1dSRodney W. Grimes * attempt reassembly; if it succeeds, proceed. 478df8bae1dSRodney W. Grimes */ 479df8bae1dSRodney W. Grimes if (((struct ipasfrag *)ip)->ipf_mff & 1 || ip->ip_off) { 480df8bae1dSRodney W. Grimes ipstat.ips_fragments++; 481df8bae1dSRodney W. Grimes ip = ip_reass((struct ipasfrag *)ip, fp); 482df8bae1dSRodney W. Grimes if (ip == 0) 483c67b1d17SGarrett Wollman return; 484df8bae1dSRodney W. Grimes ipstat.ips_reassembled++; 485df8bae1dSRodney W. Grimes m = dtom(ip); 486df8bae1dSRodney W. Grimes } else 487df8bae1dSRodney W. Grimes if (fp) 488df8bae1dSRodney W. Grimes ip_freef(fp); 489df8bae1dSRodney W. Grimes } else 490df8bae1dSRodney W. Grimes ip->ip_len -= hlen; 491df8bae1dSRodney W. Grimes 49293e0e116SJulian Elischer #ifdef IPDIVERT 49393e0e116SJulian Elischer /* 49493e0e116SJulian Elischer * Divert packets here to the divert protocol if required 49593e0e116SJulian Elischer */ 49693e0e116SJulian Elischer if (frag_divert_port) { 49793e0e116SJulian Elischer ip_divert_port = frag_divert_port; 49893e0e116SJulian Elischer frag_divert_port = 0; 49993e0e116SJulian Elischer (*inetsw[ip_protox[IPPROTO_DIVERT]].pr_input)(m, hlen); 50093e0e116SJulian Elischer return; 50193e0e116SJulian Elischer } 50293e0e116SJulian Elischer #endif 50393e0e116SJulian Elischer 504df8bae1dSRodney W. Grimes /* 505df8bae1dSRodney W. Grimes * Switch out to protocol's input routine. 506df8bae1dSRodney W. Grimes */ 507df8bae1dSRodney W. Grimes ipstat.ips_delivered++; 508df8bae1dSRodney W. Grimes (*inetsw[ip_protox[ip->ip_p]].pr_input)(m, hlen); 509c67b1d17SGarrett Wollman return; 510df8bae1dSRodney W. Grimes bad: 511df8bae1dSRodney W. Grimes m_freem(m); 512c67b1d17SGarrett Wollman } 513c67b1d17SGarrett Wollman 514c67b1d17SGarrett Wollman /* 515c67b1d17SGarrett Wollman * IP software interrupt routine - to go away sometime soon 516c67b1d17SGarrett Wollman */ 517c67b1d17SGarrett Wollman static void 518c67b1d17SGarrett Wollman ipintr(void) 519c67b1d17SGarrett Wollman { 520c67b1d17SGarrett Wollman int s; 521c67b1d17SGarrett Wollman struct mbuf *m; 522c67b1d17SGarrett Wollman 523c67b1d17SGarrett Wollman while(1) { 524c67b1d17SGarrett Wollman s = splimp(); 525c67b1d17SGarrett Wollman IF_DEQUEUE(&ipintrq, m); 526c67b1d17SGarrett Wollman splx(s); 527c67b1d17SGarrett Wollman if (m == 0) 528c67b1d17SGarrett Wollman return; 529c67b1d17SGarrett Wollman ip_input(m); 530c67b1d17SGarrett Wollman } 531df8bae1dSRodney W. Grimes } 532df8bae1dSRodney W. Grimes 533748e0b0aSGarrett Wollman NETISR_SET(NETISR_IP, ipintr); 534748e0b0aSGarrett Wollman 535df8bae1dSRodney W. Grimes /* 536df8bae1dSRodney W. Grimes * Take incoming datagram fragment and try to 537df8bae1dSRodney W. Grimes * reassemble it into whole datagram. If a chain for 538df8bae1dSRodney W. Grimes * reassembly of this datagram already exists, then it 539df8bae1dSRodney W. Grimes * is given as fp; otherwise have to make a chain. 540df8bae1dSRodney W. Grimes */ 5410312fbe9SPoul-Henning Kamp static struct ip * 542df8bae1dSRodney W. Grimes ip_reass(ip, fp) 543df8bae1dSRodney W. Grimes register struct ipasfrag *ip; 544df8bae1dSRodney W. Grimes register struct ipq *fp; 545df8bae1dSRodney W. Grimes { 546df8bae1dSRodney W. Grimes register struct mbuf *m = dtom(ip); 547df8bae1dSRodney W. Grimes register struct ipasfrag *q; 548df8bae1dSRodney W. Grimes struct mbuf *t; 549df8bae1dSRodney W. Grimes int hlen = ip->ip_hl << 2; 550df8bae1dSRodney W. Grimes int i, next; 551df8bae1dSRodney W. Grimes 552df8bae1dSRodney W. Grimes /* 553df8bae1dSRodney W. Grimes * Presence of header sizes in mbufs 554df8bae1dSRodney W. Grimes * would confuse code below. 555df8bae1dSRodney W. Grimes */ 556df8bae1dSRodney W. Grimes m->m_data += hlen; 557df8bae1dSRodney W. Grimes m->m_len -= hlen; 558df8bae1dSRodney W. Grimes 559df8bae1dSRodney W. Grimes /* 560df8bae1dSRodney W. Grimes * If first fragment to arrive, create a reassembly queue. 561df8bae1dSRodney W. Grimes */ 562df8bae1dSRodney W. Grimes if (fp == 0) { 563df8bae1dSRodney W. Grimes if ((t = m_get(M_DONTWAIT, MT_FTABLE)) == NULL) 564df8bae1dSRodney W. Grimes goto dropfrag; 565df8bae1dSRodney W. Grimes fp = mtod(t, struct ipq *); 566df8bae1dSRodney W. Grimes insque(fp, &ipq); 567df8bae1dSRodney W. Grimes fp->ipq_ttl = IPFRAGTTL; 568df8bae1dSRodney W. Grimes fp->ipq_p = ip->ip_p; 569df8bae1dSRodney W. Grimes fp->ipq_id = ip->ip_id; 570df8bae1dSRodney W. Grimes fp->ipq_next = fp->ipq_prev = (struct ipasfrag *)fp; 571df8bae1dSRodney W. Grimes fp->ipq_src = ((struct ip *)ip)->ip_src; 572df8bae1dSRodney W. Grimes fp->ipq_dst = ((struct ip *)ip)->ip_dst; 57393e0e116SJulian Elischer #ifdef IPDIVERT 57493e0e116SJulian Elischer fp->ipq_divert = 0; 57593e0e116SJulian Elischer #endif 576df8bae1dSRodney W. Grimes q = (struct ipasfrag *)fp; 577df8bae1dSRodney W. Grimes goto insert; 578df8bae1dSRodney W. Grimes } 579df8bae1dSRodney W. Grimes 580df8bae1dSRodney W. Grimes /* 581df8bae1dSRodney W. Grimes * Find a segment which begins after this one does. 582df8bae1dSRodney W. Grimes */ 583df8bae1dSRodney W. Grimes for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next) 584df8bae1dSRodney W. Grimes if (q->ip_off > ip->ip_off) 585df8bae1dSRodney W. Grimes break; 586df8bae1dSRodney W. Grimes 587df8bae1dSRodney W. Grimes /* 588df8bae1dSRodney W. Grimes * If there is a preceding segment, it may provide some of 589df8bae1dSRodney W. Grimes * our data already. If so, drop the data from the incoming 590df8bae1dSRodney W. Grimes * segment. If it provides all of our data, drop us. 591df8bae1dSRodney W. Grimes */ 592df8bae1dSRodney W. Grimes if (q->ipf_prev != (struct ipasfrag *)fp) { 593df8bae1dSRodney W. Grimes i = q->ipf_prev->ip_off + q->ipf_prev->ip_len - ip->ip_off; 594df8bae1dSRodney W. Grimes if (i > 0) { 595df8bae1dSRodney W. Grimes if (i >= ip->ip_len) 596df8bae1dSRodney W. Grimes goto dropfrag; 597df8bae1dSRodney W. Grimes m_adj(dtom(ip), i); 598df8bae1dSRodney W. Grimes ip->ip_off += i; 599df8bae1dSRodney W. Grimes ip->ip_len -= i; 600df8bae1dSRodney W. Grimes } 601df8bae1dSRodney W. Grimes } 602df8bae1dSRodney W. Grimes 603df8bae1dSRodney W. Grimes /* 604df8bae1dSRodney W. Grimes * While we overlap succeeding segments trim them or, 605df8bae1dSRodney W. Grimes * if they are completely covered, dequeue them. 606df8bae1dSRodney W. Grimes */ 607df8bae1dSRodney W. Grimes while (q != (struct ipasfrag *)fp && ip->ip_off + ip->ip_len > q->ip_off) { 608e7c81944SDavid Greenman struct mbuf *m0; 609e7c81944SDavid Greenman 610df8bae1dSRodney W. Grimes i = (ip->ip_off + ip->ip_len) - q->ip_off; 611df8bae1dSRodney W. Grimes if (i < q->ip_len) { 612df8bae1dSRodney W. Grimes q->ip_len -= i; 613df8bae1dSRodney W. Grimes q->ip_off += i; 614df8bae1dSRodney W. Grimes m_adj(dtom(q), i); 615df8bae1dSRodney W. Grimes break; 616df8bae1dSRodney W. Grimes } 617e7c81944SDavid Greenman m0 = dtom(q); 618df8bae1dSRodney W. Grimes q = q->ipf_next; 619df8bae1dSRodney W. Grimes ip_deq(q->ipf_prev); 620e7c81944SDavid Greenman m_freem(m0); 621df8bae1dSRodney W. Grimes } 622df8bae1dSRodney W. Grimes 623df8bae1dSRodney W. Grimes insert: 62493e0e116SJulian Elischer 62593e0e116SJulian Elischer #ifdef IPDIVERT 62693e0e116SJulian Elischer /* 62793e0e116SJulian Elischer * Any fragment diverting causes the whole packet to divert 62893e0e116SJulian Elischer */ 62993e0e116SJulian Elischer if (frag_divert_port != 0) 63093e0e116SJulian Elischer fp->ipq_divert = frag_divert_port; 63193e0e116SJulian Elischer frag_divert_port = 0; 63293e0e116SJulian Elischer #endif 63393e0e116SJulian Elischer 634df8bae1dSRodney W. Grimes /* 635df8bae1dSRodney W. Grimes * Stick new segment in its place; 636df8bae1dSRodney W. Grimes * check for complete reassembly. 637df8bae1dSRodney W. Grimes */ 638df8bae1dSRodney W. Grimes ip_enq(ip, q->ipf_prev); 639df8bae1dSRodney W. Grimes next = 0; 640df8bae1dSRodney W. Grimes for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next) { 641df8bae1dSRodney W. Grimes if (q->ip_off != next) 642df8bae1dSRodney W. Grimes return (0); 643df8bae1dSRodney W. Grimes next += q->ip_len; 644df8bae1dSRodney W. Grimes } 645df8bae1dSRodney W. Grimes if (q->ipf_prev->ipf_mff & 1) 646df8bae1dSRodney W. Grimes return (0); 647df8bae1dSRodney W. Grimes 648df8bae1dSRodney W. Grimes /* 649df8bae1dSRodney W. Grimes * Reassembly is complete; concatenate fragments. 650df8bae1dSRodney W. Grimes */ 651df8bae1dSRodney W. Grimes q = fp->ipq_next; 652df8bae1dSRodney W. Grimes m = dtom(q); 653df8bae1dSRodney W. Grimes t = m->m_next; 654df8bae1dSRodney W. Grimes m->m_next = 0; 655df8bae1dSRodney W. Grimes m_cat(m, t); 656df8bae1dSRodney W. Grimes q = q->ipf_next; 657df8bae1dSRodney W. Grimes while (q != (struct ipasfrag *)fp) { 658df8bae1dSRodney W. Grimes t = dtom(q); 659df8bae1dSRodney W. Grimes q = q->ipf_next; 660df8bae1dSRodney W. Grimes m_cat(m, t); 661df8bae1dSRodney W. Grimes } 662df8bae1dSRodney W. Grimes 66393e0e116SJulian Elischer #ifdef IPDIVERT 66493e0e116SJulian Elischer /* 66593e0e116SJulian Elischer * Record divert port for packet, if any 66693e0e116SJulian Elischer */ 66793e0e116SJulian Elischer frag_divert_port = fp->ipq_divert; 66893e0e116SJulian Elischer #endif 66993e0e116SJulian Elischer 670df8bae1dSRodney W. Grimes /* 671df8bae1dSRodney W. Grimes * Create header for new ip packet by 672df8bae1dSRodney W. Grimes * modifying header of first packet; 673df8bae1dSRodney W. Grimes * dequeue and discard fragment reassembly header. 674df8bae1dSRodney W. Grimes * Make header visible. 675df8bae1dSRodney W. Grimes */ 676df8bae1dSRodney W. Grimes ip = fp->ipq_next; 677df8bae1dSRodney W. Grimes ip->ip_len = next; 678df8bae1dSRodney W. Grimes ip->ipf_mff &= ~1; 679df8bae1dSRodney W. Grimes ((struct ip *)ip)->ip_src = fp->ipq_src; 680df8bae1dSRodney W. Grimes ((struct ip *)ip)->ip_dst = fp->ipq_dst; 681df8bae1dSRodney W. Grimes remque(fp); 682df8bae1dSRodney W. Grimes (void) m_free(dtom(fp)); 683df8bae1dSRodney W. Grimes m = dtom(ip); 684df8bae1dSRodney W. Grimes m->m_len += (ip->ip_hl << 2); 685df8bae1dSRodney W. Grimes m->m_data -= (ip->ip_hl << 2); 686df8bae1dSRodney W. Grimes /* some debugging cruft by sklower, below, will go away soon */ 687df8bae1dSRodney W. Grimes if (m->m_flags & M_PKTHDR) { /* XXX this should be done elsewhere */ 688df8bae1dSRodney W. Grimes register int plen = 0; 689df8bae1dSRodney W. Grimes for (t = m; m; m = m->m_next) 690df8bae1dSRodney W. Grimes plen += m->m_len; 691df8bae1dSRodney W. Grimes t->m_pkthdr.len = plen; 692df8bae1dSRodney W. Grimes } 693df8bae1dSRodney W. Grimes return ((struct ip *)ip); 694df8bae1dSRodney W. Grimes 695df8bae1dSRodney W. Grimes dropfrag: 696df8bae1dSRodney W. Grimes ipstat.ips_fragdropped++; 697df8bae1dSRodney W. Grimes m_freem(m); 698df8bae1dSRodney W. Grimes return (0); 699df8bae1dSRodney W. Grimes } 700df8bae1dSRodney W. Grimes 701df8bae1dSRodney W. Grimes /* 702df8bae1dSRodney W. Grimes * Free a fragment reassembly header and all 703df8bae1dSRodney W. Grimes * associated datagrams. 704df8bae1dSRodney W. Grimes */ 7050312fbe9SPoul-Henning Kamp static void 706df8bae1dSRodney W. Grimes ip_freef(fp) 707df8bae1dSRodney W. Grimes struct ipq *fp; 708df8bae1dSRodney W. Grimes { 709df8bae1dSRodney W. Grimes register struct ipasfrag *q, *p; 710df8bae1dSRodney W. Grimes 711df8bae1dSRodney W. Grimes for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = p) { 712df8bae1dSRodney W. Grimes p = q->ipf_next; 713df8bae1dSRodney W. Grimes ip_deq(q); 714df8bae1dSRodney W. Grimes m_freem(dtom(q)); 715df8bae1dSRodney W. Grimes } 716df8bae1dSRodney W. Grimes remque(fp); 717df8bae1dSRodney W. Grimes (void) m_free(dtom(fp)); 718df8bae1dSRodney W. Grimes } 719df8bae1dSRodney W. Grimes 720df8bae1dSRodney W. Grimes /* 721df8bae1dSRodney W. Grimes * Put an ip fragment on a reassembly chain. 722df8bae1dSRodney W. Grimes * Like insque, but pointers in middle of structure. 723df8bae1dSRodney W. Grimes */ 7240312fbe9SPoul-Henning Kamp static void 725df8bae1dSRodney W. Grimes ip_enq(p, prev) 726df8bae1dSRodney W. Grimes register struct ipasfrag *p, *prev; 727df8bae1dSRodney W. Grimes { 728df8bae1dSRodney W. Grimes 729df8bae1dSRodney W. Grimes p->ipf_prev = prev; 730df8bae1dSRodney W. Grimes p->ipf_next = prev->ipf_next; 731df8bae1dSRodney W. Grimes prev->ipf_next->ipf_prev = p; 732df8bae1dSRodney W. Grimes prev->ipf_next = p; 733df8bae1dSRodney W. Grimes } 734df8bae1dSRodney W. Grimes 735df8bae1dSRodney W. Grimes /* 736df8bae1dSRodney W. Grimes * To ip_enq as remque is to insque. 737df8bae1dSRodney W. Grimes */ 7380312fbe9SPoul-Henning Kamp static void 739df8bae1dSRodney W. Grimes ip_deq(p) 740df8bae1dSRodney W. Grimes register struct ipasfrag *p; 741df8bae1dSRodney W. Grimes { 742df8bae1dSRodney W. Grimes 743df8bae1dSRodney W. Grimes p->ipf_prev->ipf_next = p->ipf_next; 744df8bae1dSRodney W. Grimes p->ipf_next->ipf_prev = p->ipf_prev; 745df8bae1dSRodney W. Grimes } 746df8bae1dSRodney W. Grimes 747df8bae1dSRodney W. Grimes /* 748df8bae1dSRodney W. Grimes * IP timer processing; 749df8bae1dSRodney W. Grimes * if a timer expires on a reassembly 750df8bae1dSRodney W. Grimes * queue, discard it. 751df8bae1dSRodney W. Grimes */ 752df8bae1dSRodney W. Grimes void 753df8bae1dSRodney W. Grimes ip_slowtimo() 754df8bae1dSRodney W. Grimes { 755df8bae1dSRodney W. Grimes register struct ipq *fp; 756df8bae1dSRodney W. Grimes int s = splnet(); 757df8bae1dSRodney W. Grimes 758df8bae1dSRodney W. Grimes fp = ipq.next; 759df8bae1dSRodney W. Grimes if (fp == 0) { 760df8bae1dSRodney W. Grimes splx(s); 761df8bae1dSRodney W. Grimes return; 762df8bae1dSRodney W. Grimes } 763df8bae1dSRodney W. Grimes while (fp != &ipq) { 764df8bae1dSRodney W. Grimes --fp->ipq_ttl; 765df8bae1dSRodney W. Grimes fp = fp->next; 766df8bae1dSRodney W. Grimes if (fp->prev->ipq_ttl == 0) { 767df8bae1dSRodney W. Grimes ipstat.ips_fragtimeout++; 768df8bae1dSRodney W. Grimes ip_freef(fp->prev); 769df8bae1dSRodney W. Grimes } 770df8bae1dSRodney W. Grimes } 771df8bae1dSRodney W. Grimes splx(s); 772df8bae1dSRodney W. Grimes } 773df8bae1dSRodney W. Grimes 774df8bae1dSRodney W. Grimes /* 775df8bae1dSRodney W. Grimes * Drain off all datagram fragments. 776df8bae1dSRodney W. Grimes */ 777df8bae1dSRodney W. Grimes void 778df8bae1dSRodney W. Grimes ip_drain() 779df8bae1dSRodney W. Grimes { 780df8bae1dSRodney W. Grimes while (ipq.next != &ipq) { 781df8bae1dSRodney W. Grimes ipstat.ips_fragdropped++; 782df8bae1dSRodney W. Grimes ip_freef(ipq.next); 783df8bae1dSRodney W. Grimes } 784ce29ab3aSGarrett Wollman 785ce29ab3aSGarrett Wollman in_rtqdrain(); 786df8bae1dSRodney W. Grimes } 787df8bae1dSRodney W. Grimes 788df8bae1dSRodney W. Grimes /* 789df8bae1dSRodney W. Grimes * Do option processing on a datagram, 790df8bae1dSRodney W. Grimes * possibly discarding it if bad options are encountered, 791df8bae1dSRodney W. Grimes * or forwarding it if source-routed. 792df8bae1dSRodney W. Grimes * Returns 1 if packet has been forwarded/freed, 793df8bae1dSRodney W. Grimes * 0 if the packet should be processed further. 794df8bae1dSRodney W. Grimes */ 7950312fbe9SPoul-Henning Kamp static int 796df8bae1dSRodney W. Grimes ip_dooptions(m) 797df8bae1dSRodney W. Grimes struct mbuf *m; 798df8bae1dSRodney W. Grimes { 799df8bae1dSRodney W. Grimes register struct ip *ip = mtod(m, struct ip *); 800df8bae1dSRodney W. Grimes register u_char *cp; 801df8bae1dSRodney W. Grimes register struct ip_timestamp *ipt; 802df8bae1dSRodney W. Grimes register struct in_ifaddr *ia; 803df8bae1dSRodney W. Grimes int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0; 804df8bae1dSRodney W. Grimes struct in_addr *sin, dst; 805df8bae1dSRodney W. Grimes n_time ntime; 806df8bae1dSRodney W. Grimes 807df8bae1dSRodney W. Grimes dst = ip->ip_dst; 808df8bae1dSRodney W. Grimes cp = (u_char *)(ip + 1); 80958938916SGarrett Wollman cnt = (IP_VHL_HL(ip->ip_vhl) << 2) - sizeof (struct ip); 810df8bae1dSRodney W. Grimes for (; cnt > 0; cnt -= optlen, cp += optlen) { 811df8bae1dSRodney W. Grimes opt = cp[IPOPT_OPTVAL]; 812df8bae1dSRodney W. Grimes if (opt == IPOPT_EOL) 813df8bae1dSRodney W. Grimes break; 814df8bae1dSRodney W. Grimes if (opt == IPOPT_NOP) 815df8bae1dSRodney W. Grimes optlen = 1; 816df8bae1dSRodney W. Grimes else { 817df8bae1dSRodney W. Grimes optlen = cp[IPOPT_OLEN]; 818df8bae1dSRodney W. Grimes if (optlen <= 0 || optlen > cnt) { 819df8bae1dSRodney W. Grimes code = &cp[IPOPT_OLEN] - (u_char *)ip; 820df8bae1dSRodney W. Grimes goto bad; 821df8bae1dSRodney W. Grimes } 822df8bae1dSRodney W. Grimes } 823df8bae1dSRodney W. Grimes switch (opt) { 824df8bae1dSRodney W. Grimes 825df8bae1dSRodney W. Grimes default: 826df8bae1dSRodney W. Grimes break; 827df8bae1dSRodney W. Grimes 828df8bae1dSRodney W. Grimes /* 829df8bae1dSRodney W. Grimes * Source routing with record. 830df8bae1dSRodney W. Grimes * Find interface with current destination address. 831df8bae1dSRodney W. Grimes * If none on this machine then drop if strictly routed, 832df8bae1dSRodney W. Grimes * or do nothing if loosely routed. 833df8bae1dSRodney W. Grimes * Record interface address and bring up next address 834df8bae1dSRodney W. Grimes * component. If strictly routed make sure next 835df8bae1dSRodney W. Grimes * address is on directly accessible net. 836df8bae1dSRodney W. Grimes */ 837df8bae1dSRodney W. Grimes case IPOPT_LSRR: 838df8bae1dSRodney W. Grimes case IPOPT_SSRR: 839df8bae1dSRodney W. Grimes if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) { 840df8bae1dSRodney W. Grimes code = &cp[IPOPT_OFFSET] - (u_char *)ip; 841df8bae1dSRodney W. Grimes goto bad; 842df8bae1dSRodney W. Grimes } 843df8bae1dSRodney W. Grimes ipaddr.sin_addr = ip->ip_dst; 844df8bae1dSRodney W. Grimes ia = (struct in_ifaddr *) 845df8bae1dSRodney W. Grimes ifa_ifwithaddr((struct sockaddr *)&ipaddr); 846df8bae1dSRodney W. Grimes if (ia == 0) { 847df8bae1dSRodney W. Grimes if (opt == IPOPT_SSRR) { 848df8bae1dSRodney W. Grimes type = ICMP_UNREACH; 849df8bae1dSRodney W. Grimes code = ICMP_UNREACH_SRCFAIL; 850df8bae1dSRodney W. Grimes goto bad; 851df8bae1dSRodney W. Grimes } 852df8bae1dSRodney W. Grimes /* 853df8bae1dSRodney W. Grimes * Loose routing, and not at next destination 854df8bae1dSRodney W. Grimes * yet; nothing to do except forward. 855df8bae1dSRodney W. Grimes */ 856df8bae1dSRodney W. Grimes break; 857df8bae1dSRodney W. Grimes } 858df8bae1dSRodney W. Grimes off--; /* 0 origin */ 859df8bae1dSRodney W. Grimes if (off > optlen - sizeof(struct in_addr)) { 860df8bae1dSRodney W. Grimes /* 861df8bae1dSRodney W. Grimes * End of source route. Should be for us. 862df8bae1dSRodney W. Grimes */ 863df8bae1dSRodney W. Grimes save_rte(cp, ip->ip_src); 864df8bae1dSRodney W. Grimes break; 865df8bae1dSRodney W. Grimes } 8661025071fSGarrett Wollman 8671025071fSGarrett Wollman if (!ip_dosourceroute) { 8681025071fSGarrett Wollman char buf[4*sizeof "123"]; 8691025071fSGarrett Wollman strcpy(buf, inet_ntoa(ip->ip_dst)); 8701025071fSGarrett Wollman 8711025071fSGarrett Wollman log(LOG_WARNING, 8721025071fSGarrett Wollman "attempted source route from %s to %s\n", 8731025071fSGarrett Wollman inet_ntoa(ip->ip_src), buf); 8741025071fSGarrett Wollman type = ICMP_UNREACH; 8751025071fSGarrett Wollman code = ICMP_UNREACH_SRCFAIL; 8761025071fSGarrett Wollman goto bad; 8771025071fSGarrett Wollman } 8781025071fSGarrett Wollman 879df8bae1dSRodney W. Grimes /* 880df8bae1dSRodney W. Grimes * locate outgoing interface 881df8bae1dSRodney W. Grimes */ 88294a5d9b6SDavid Greenman (void)memcpy(&ipaddr.sin_addr, cp + off, 883df8bae1dSRodney W. Grimes sizeof(ipaddr.sin_addr)); 8841025071fSGarrett Wollman 885df8bae1dSRodney W. Grimes if (opt == IPOPT_SSRR) { 886df8bae1dSRodney W. Grimes #define INA struct in_ifaddr * 887df8bae1dSRodney W. Grimes #define SA struct sockaddr * 888df8bae1dSRodney W. Grimes if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0) 889df8bae1dSRodney W. Grimes ia = (INA)ifa_ifwithnet((SA)&ipaddr); 890df8bae1dSRodney W. Grimes } else 891df8bae1dSRodney W. Grimes ia = ip_rtaddr(ipaddr.sin_addr); 892df8bae1dSRodney W. Grimes if (ia == 0) { 893df8bae1dSRodney W. Grimes type = ICMP_UNREACH; 894df8bae1dSRodney W. Grimes code = ICMP_UNREACH_SRCFAIL; 895df8bae1dSRodney W. Grimes goto bad; 896df8bae1dSRodney W. Grimes } 897df8bae1dSRodney W. Grimes ip->ip_dst = ipaddr.sin_addr; 89894a5d9b6SDavid Greenman (void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr), 89994a5d9b6SDavid Greenman sizeof(struct in_addr)); 900df8bae1dSRodney W. Grimes cp[IPOPT_OFFSET] += sizeof(struct in_addr); 901df8bae1dSRodney W. Grimes /* 902df8bae1dSRodney W. Grimes * Let ip_intr's mcast routing check handle mcast pkts 903df8bae1dSRodney W. Grimes */ 904df8bae1dSRodney W. Grimes forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr)); 905df8bae1dSRodney W. Grimes break; 906df8bae1dSRodney W. Grimes 907df8bae1dSRodney W. Grimes case IPOPT_RR: 908df8bae1dSRodney W. Grimes if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) { 909df8bae1dSRodney W. Grimes code = &cp[IPOPT_OFFSET] - (u_char *)ip; 910df8bae1dSRodney W. Grimes goto bad; 911df8bae1dSRodney W. Grimes } 912df8bae1dSRodney W. Grimes /* 913df8bae1dSRodney W. Grimes * If no space remains, ignore. 914df8bae1dSRodney W. Grimes */ 915df8bae1dSRodney W. Grimes off--; /* 0 origin */ 916df8bae1dSRodney W. Grimes if (off > optlen - sizeof(struct in_addr)) 917df8bae1dSRodney W. Grimes break; 91894a5d9b6SDavid Greenman (void)memcpy(&ipaddr.sin_addr, &ip->ip_dst, 919df8bae1dSRodney W. Grimes sizeof(ipaddr.sin_addr)); 920df8bae1dSRodney W. Grimes /* 921df8bae1dSRodney W. Grimes * locate outgoing interface; if we're the destination, 922df8bae1dSRodney W. Grimes * use the incoming interface (should be same). 923df8bae1dSRodney W. Grimes */ 924df8bae1dSRodney W. Grimes if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 && 925df8bae1dSRodney W. Grimes (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) { 926df8bae1dSRodney W. Grimes type = ICMP_UNREACH; 927df8bae1dSRodney W. Grimes code = ICMP_UNREACH_HOST; 928df8bae1dSRodney W. Grimes goto bad; 929df8bae1dSRodney W. Grimes } 93094a5d9b6SDavid Greenman (void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr), 93194a5d9b6SDavid Greenman sizeof(struct in_addr)); 932df8bae1dSRodney W. Grimes cp[IPOPT_OFFSET] += sizeof(struct in_addr); 933df8bae1dSRodney W. Grimes break; 934df8bae1dSRodney W. Grimes 935df8bae1dSRodney W. Grimes case IPOPT_TS: 936df8bae1dSRodney W. Grimes code = cp - (u_char *)ip; 937df8bae1dSRodney W. Grimes ipt = (struct ip_timestamp *)cp; 938df8bae1dSRodney W. Grimes if (ipt->ipt_len < 5) 939df8bae1dSRodney W. Grimes goto bad; 940df8bae1dSRodney W. Grimes if (ipt->ipt_ptr > ipt->ipt_len - sizeof (long)) { 941df8bae1dSRodney W. Grimes if (++ipt->ipt_oflw == 0) 942df8bae1dSRodney W. Grimes goto bad; 943df8bae1dSRodney W. Grimes break; 944df8bae1dSRodney W. Grimes } 945df8bae1dSRodney W. Grimes sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1); 946df8bae1dSRodney W. Grimes switch (ipt->ipt_flg) { 947df8bae1dSRodney W. Grimes 948df8bae1dSRodney W. Grimes case IPOPT_TS_TSONLY: 949df8bae1dSRodney W. Grimes break; 950df8bae1dSRodney W. Grimes 951df8bae1dSRodney W. Grimes case IPOPT_TS_TSANDADDR: 952df8bae1dSRodney W. Grimes if (ipt->ipt_ptr + sizeof(n_time) + 953df8bae1dSRodney W. Grimes sizeof(struct in_addr) > ipt->ipt_len) 954df8bae1dSRodney W. Grimes goto bad; 955df8bae1dSRodney W. Grimes ipaddr.sin_addr = dst; 956df8bae1dSRodney W. Grimes ia = (INA)ifaof_ifpforaddr((SA)&ipaddr, 957df8bae1dSRodney W. Grimes m->m_pkthdr.rcvif); 958df8bae1dSRodney W. Grimes if (ia == 0) 959df8bae1dSRodney W. Grimes continue; 96094a5d9b6SDavid Greenman (void)memcpy(sin, &IA_SIN(ia)->sin_addr, 96194a5d9b6SDavid Greenman sizeof(struct in_addr)); 962df8bae1dSRodney W. Grimes ipt->ipt_ptr += sizeof(struct in_addr); 963df8bae1dSRodney W. Grimes break; 964df8bae1dSRodney W. Grimes 965df8bae1dSRodney W. Grimes case IPOPT_TS_PRESPEC: 966df8bae1dSRodney W. Grimes if (ipt->ipt_ptr + sizeof(n_time) + 967df8bae1dSRodney W. Grimes sizeof(struct in_addr) > ipt->ipt_len) 968df8bae1dSRodney W. Grimes goto bad; 96994a5d9b6SDavid Greenman (void)memcpy(&ipaddr.sin_addr, sin, 970df8bae1dSRodney W. Grimes sizeof(struct in_addr)); 971df8bae1dSRodney W. Grimes if (ifa_ifwithaddr((SA)&ipaddr) == 0) 972df8bae1dSRodney W. Grimes continue; 973df8bae1dSRodney W. Grimes ipt->ipt_ptr += sizeof(struct in_addr); 974df8bae1dSRodney W. Grimes break; 975df8bae1dSRodney W. Grimes 976df8bae1dSRodney W. Grimes default: 977df8bae1dSRodney W. Grimes goto bad; 978df8bae1dSRodney W. Grimes } 979df8bae1dSRodney W. Grimes ntime = iptime(); 98094a5d9b6SDavid Greenman (void)memcpy(cp + ipt->ipt_ptr - 1, &ntime, 981df8bae1dSRodney W. Grimes sizeof(n_time)); 982df8bae1dSRodney W. Grimes ipt->ipt_ptr += sizeof(n_time); 983df8bae1dSRodney W. Grimes } 984df8bae1dSRodney W. Grimes } 985df8bae1dSRodney W. Grimes if (forward) { 986df8bae1dSRodney W. Grimes ip_forward(m, 1); 987df8bae1dSRodney W. Grimes return (1); 988df8bae1dSRodney W. Grimes } 989df8bae1dSRodney W. Grimes return (0); 990df8bae1dSRodney W. Grimes bad: 99158938916SGarrett Wollman ip->ip_len -= IP_VHL_HL(ip->ip_vhl) << 2; /* XXX icmp_error adds in hdr length */ 992df8bae1dSRodney W. Grimes icmp_error(m, type, code, 0, 0); 993df8bae1dSRodney W. Grimes ipstat.ips_badoptions++; 994df8bae1dSRodney W. Grimes return (1); 995df8bae1dSRodney W. Grimes } 996df8bae1dSRodney W. Grimes 997df8bae1dSRodney W. Grimes /* 998df8bae1dSRodney W. Grimes * Given address of next destination (final or next hop), 999df8bae1dSRodney W. Grimes * return internet address info of interface to be used to get there. 1000df8bae1dSRodney W. Grimes */ 10010312fbe9SPoul-Henning Kamp static struct in_ifaddr * 1002df8bae1dSRodney W. Grimes ip_rtaddr(dst) 1003df8bae1dSRodney W. Grimes struct in_addr dst; 1004df8bae1dSRodney W. Grimes { 1005df8bae1dSRodney W. Grimes register struct sockaddr_in *sin; 1006df8bae1dSRodney W. Grimes 1007df8bae1dSRodney W. Grimes sin = (struct sockaddr_in *) &ipforward_rt.ro_dst; 1008df8bae1dSRodney W. Grimes 1009df8bae1dSRodney W. Grimes if (ipforward_rt.ro_rt == 0 || dst.s_addr != sin->sin_addr.s_addr) { 1010df8bae1dSRodney W. Grimes if (ipforward_rt.ro_rt) { 1011df8bae1dSRodney W. Grimes RTFREE(ipforward_rt.ro_rt); 1012df8bae1dSRodney W. Grimes ipforward_rt.ro_rt = 0; 1013df8bae1dSRodney W. Grimes } 1014df8bae1dSRodney W. Grimes sin->sin_family = AF_INET; 1015df8bae1dSRodney W. Grimes sin->sin_len = sizeof(*sin); 1016df8bae1dSRodney W. Grimes sin->sin_addr = dst; 1017df8bae1dSRodney W. Grimes 10182c17fe93SGarrett Wollman rtalloc_ign(&ipforward_rt, RTF_PRCLONING); 1019df8bae1dSRodney W. Grimes } 1020df8bae1dSRodney W. Grimes if (ipforward_rt.ro_rt == 0) 1021df8bae1dSRodney W. Grimes return ((struct in_ifaddr *)0); 1022df8bae1dSRodney W. Grimes return ((struct in_ifaddr *) ipforward_rt.ro_rt->rt_ifa); 1023df8bae1dSRodney W. Grimes } 1024df8bae1dSRodney W. Grimes 1025df8bae1dSRodney W. Grimes /* 1026df8bae1dSRodney W. Grimes * Save incoming source route for use in replies, 1027df8bae1dSRodney W. Grimes * to be picked up later by ip_srcroute if the receiver is interested. 1028df8bae1dSRodney W. Grimes */ 1029df8bae1dSRodney W. Grimes void 1030df8bae1dSRodney W. Grimes save_rte(option, dst) 1031df8bae1dSRodney W. Grimes u_char *option; 1032df8bae1dSRodney W. Grimes struct in_addr dst; 1033df8bae1dSRodney W. Grimes { 1034df8bae1dSRodney W. Grimes unsigned olen; 1035df8bae1dSRodney W. Grimes 1036df8bae1dSRodney W. Grimes olen = option[IPOPT_OLEN]; 1037df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC 1038df8bae1dSRodney W. Grimes if (ipprintfs) 1039df8bae1dSRodney W. Grimes printf("save_rte: olen %d\n", olen); 1040df8bae1dSRodney W. Grimes #endif 1041df8bae1dSRodney W. Grimes if (olen > sizeof(ip_srcrt) - (1 + sizeof(dst))) 1042df8bae1dSRodney W. Grimes return; 10430453d3cbSBruce Evans bcopy(option, ip_srcrt.srcopt, olen); 1044df8bae1dSRodney W. Grimes ip_nhops = (olen - IPOPT_OFFSET - 1) / sizeof(struct in_addr); 1045df8bae1dSRodney W. Grimes ip_srcrt.dst = dst; 1046df8bae1dSRodney W. Grimes } 1047df8bae1dSRodney W. Grimes 1048df8bae1dSRodney W. Grimes /* 1049df8bae1dSRodney W. Grimes * Retrieve incoming source route for use in replies, 1050df8bae1dSRodney W. Grimes * in the same form used by setsockopt. 1051df8bae1dSRodney W. Grimes * The first hop is placed before the options, will be removed later. 1052df8bae1dSRodney W. Grimes */ 1053df8bae1dSRodney W. Grimes struct mbuf * 1054df8bae1dSRodney W. Grimes ip_srcroute() 1055df8bae1dSRodney W. Grimes { 1056df8bae1dSRodney W. Grimes register struct in_addr *p, *q; 1057df8bae1dSRodney W. Grimes register struct mbuf *m; 1058df8bae1dSRodney W. Grimes 1059df8bae1dSRodney W. Grimes if (ip_nhops == 0) 1060df8bae1dSRodney W. Grimes return ((struct mbuf *)0); 1061df8bae1dSRodney W. Grimes m = m_get(M_DONTWAIT, MT_SOOPTS); 1062df8bae1dSRodney W. Grimes if (m == 0) 1063df8bae1dSRodney W. Grimes return ((struct mbuf *)0); 1064df8bae1dSRodney W. Grimes 1065df8bae1dSRodney W. Grimes #define OPTSIZ (sizeof(ip_srcrt.nop) + sizeof(ip_srcrt.srcopt)) 1066df8bae1dSRodney W. Grimes 1067df8bae1dSRodney W. Grimes /* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */ 1068df8bae1dSRodney W. Grimes m->m_len = ip_nhops * sizeof(struct in_addr) + sizeof(struct in_addr) + 1069df8bae1dSRodney W. Grimes OPTSIZ; 1070df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC 1071df8bae1dSRodney W. Grimes if (ipprintfs) 1072df8bae1dSRodney W. Grimes printf("ip_srcroute: nhops %d mlen %d", ip_nhops, m->m_len); 1073df8bae1dSRodney W. Grimes #endif 1074df8bae1dSRodney W. Grimes 1075df8bae1dSRodney W. Grimes /* 1076df8bae1dSRodney W. Grimes * First save first hop for return route 1077df8bae1dSRodney W. Grimes */ 1078df8bae1dSRodney W. Grimes p = &ip_srcrt.route[ip_nhops - 1]; 1079df8bae1dSRodney W. Grimes *(mtod(m, struct in_addr *)) = *p--; 1080df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC 1081df8bae1dSRodney W. Grimes if (ipprintfs) 1082df8bae1dSRodney W. Grimes printf(" hops %lx", ntohl(mtod(m, struct in_addr *)->s_addr)); 1083df8bae1dSRodney W. Grimes #endif 1084df8bae1dSRodney W. Grimes 1085df8bae1dSRodney W. Grimes /* 1086df8bae1dSRodney W. Grimes * Copy option fields and padding (nop) to mbuf. 1087df8bae1dSRodney W. Grimes */ 1088df8bae1dSRodney W. Grimes ip_srcrt.nop = IPOPT_NOP; 1089df8bae1dSRodney W. Grimes ip_srcrt.srcopt[IPOPT_OFFSET] = IPOPT_MINOFF; 109094a5d9b6SDavid Greenman (void)memcpy(mtod(m, caddr_t) + sizeof(struct in_addr), 109194a5d9b6SDavid Greenman &ip_srcrt.nop, OPTSIZ); 1092df8bae1dSRodney W. Grimes q = (struct in_addr *)(mtod(m, caddr_t) + 1093df8bae1dSRodney W. Grimes sizeof(struct in_addr) + OPTSIZ); 1094df8bae1dSRodney W. Grimes #undef OPTSIZ 1095df8bae1dSRodney W. Grimes /* 1096df8bae1dSRodney W. Grimes * Record return path as an IP source route, 1097df8bae1dSRodney W. Grimes * reversing the path (pointers are now aligned). 1098df8bae1dSRodney W. Grimes */ 1099df8bae1dSRodney W. Grimes while (p >= ip_srcrt.route) { 1100df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC 1101df8bae1dSRodney W. Grimes if (ipprintfs) 1102df8bae1dSRodney W. Grimes printf(" %lx", ntohl(q->s_addr)); 1103df8bae1dSRodney W. Grimes #endif 1104df8bae1dSRodney W. Grimes *q++ = *p--; 1105df8bae1dSRodney W. Grimes } 1106df8bae1dSRodney W. Grimes /* 1107df8bae1dSRodney W. Grimes * Last hop goes to final destination. 1108df8bae1dSRodney W. Grimes */ 1109df8bae1dSRodney W. Grimes *q = ip_srcrt.dst; 1110df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC 1111df8bae1dSRodney W. Grimes if (ipprintfs) 1112df8bae1dSRodney W. Grimes printf(" %lx\n", ntohl(q->s_addr)); 1113df8bae1dSRodney W. Grimes #endif 1114df8bae1dSRodney W. Grimes return (m); 1115df8bae1dSRodney W. Grimes } 1116df8bae1dSRodney W. Grimes 1117df8bae1dSRodney W. Grimes /* 1118df8bae1dSRodney W. Grimes * Strip out IP options, at higher 1119df8bae1dSRodney W. Grimes * level protocol in the kernel. 1120df8bae1dSRodney W. Grimes * Second argument is buffer to which options 1121df8bae1dSRodney W. Grimes * will be moved, and return value is their length. 1122df8bae1dSRodney W. Grimes * XXX should be deleted; last arg currently ignored. 1123df8bae1dSRodney W. Grimes */ 1124df8bae1dSRodney W. Grimes void 1125df8bae1dSRodney W. Grimes ip_stripoptions(m, mopt) 1126df8bae1dSRodney W. Grimes register struct mbuf *m; 1127df8bae1dSRodney W. Grimes struct mbuf *mopt; 1128df8bae1dSRodney W. Grimes { 1129df8bae1dSRodney W. Grimes register int i; 1130df8bae1dSRodney W. Grimes struct ip *ip = mtod(m, struct ip *); 1131df8bae1dSRodney W. Grimes register caddr_t opts; 1132df8bae1dSRodney W. Grimes int olen; 1133df8bae1dSRodney W. Grimes 113458938916SGarrett Wollman olen = (IP_VHL_HL(ip->ip_vhl) << 2) - sizeof (struct ip); 1135df8bae1dSRodney W. Grimes opts = (caddr_t)(ip + 1); 1136df8bae1dSRodney W. Grimes i = m->m_len - (sizeof (struct ip) + olen); 1137df8bae1dSRodney W. Grimes bcopy(opts + olen, opts, (unsigned)i); 1138df8bae1dSRodney W. Grimes m->m_len -= olen; 1139df8bae1dSRodney W. Grimes if (m->m_flags & M_PKTHDR) 1140df8bae1dSRodney W. Grimes m->m_pkthdr.len -= olen; 114158938916SGarrett Wollman ip->ip_vhl = IP_MAKE_VHL(IPVERSION, sizeof(struct ip) >> 2); 1142df8bae1dSRodney W. Grimes } 1143df8bae1dSRodney W. Grimes 1144df8bae1dSRodney W. Grimes u_char inetctlerrmap[PRC_NCMDS] = { 1145df8bae1dSRodney W. Grimes 0, 0, 0, 0, 1146df8bae1dSRodney W. Grimes 0, EMSGSIZE, EHOSTDOWN, EHOSTUNREACH, 1147df8bae1dSRodney W. Grimes EHOSTUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED, 1148df8bae1dSRodney W. Grimes EMSGSIZE, EHOSTUNREACH, 0, 0, 1149df8bae1dSRodney W. Grimes 0, 0, 0, 0, 1150df8bae1dSRodney W. Grimes ENOPROTOOPT 1151df8bae1dSRodney W. Grimes }; 1152df8bae1dSRodney W. Grimes 1153df8bae1dSRodney W. Grimes /* 1154df8bae1dSRodney W. Grimes * Forward a packet. If some error occurs return the sender 1155df8bae1dSRodney W. Grimes * an icmp packet. Note we can't always generate a meaningful 1156df8bae1dSRodney W. Grimes * icmp message because icmp doesn't have a large enough repertoire 1157df8bae1dSRodney W. Grimes * of codes and types. 1158df8bae1dSRodney W. Grimes * 1159df8bae1dSRodney W. Grimes * If not forwarding, just drop the packet. This could be confusing 1160df8bae1dSRodney W. Grimes * if ipforwarding was zero but some routing protocol was advancing 1161df8bae1dSRodney W. Grimes * us as a gateway to somewhere. However, we must let the routing 1162df8bae1dSRodney W. Grimes * protocol deal with that. 1163df8bae1dSRodney W. Grimes * 1164df8bae1dSRodney W. Grimes * The srcrt parameter indicates whether the packet is being forwarded 1165df8bae1dSRodney W. Grimes * via a source route. 1166df8bae1dSRodney W. Grimes */ 11670312fbe9SPoul-Henning Kamp static void 1168df8bae1dSRodney W. Grimes ip_forward(m, srcrt) 1169df8bae1dSRodney W. Grimes struct mbuf *m; 1170df8bae1dSRodney W. Grimes int srcrt; 1171df8bae1dSRodney W. Grimes { 1172df8bae1dSRodney W. Grimes register struct ip *ip = mtod(m, struct ip *); 1173df8bae1dSRodney W. Grimes register struct sockaddr_in *sin; 1174df8bae1dSRodney W. Grimes register struct rtentry *rt; 117526f9a767SRodney W. Grimes int error, type = 0, code = 0; 1176df8bae1dSRodney W. Grimes struct mbuf *mcopy; 1177df8bae1dSRodney W. Grimes n_long dest; 1178df8bae1dSRodney W. Grimes struct ifnet *destifp; 1179df8bae1dSRodney W. Grimes 1180df8bae1dSRodney W. Grimes dest = 0; 1181df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC 1182df8bae1dSRodney W. Grimes if (ipprintfs) 118361ce519bSPoul-Henning Kamp printf("forward: src %lx dst %lx ttl %x\n", 1184623ae52eSPoul-Henning Kamp ip->ip_src.s_addr, ip->ip_dst.s_addr, ip->ip_ttl); 1185df8bae1dSRodney W. Grimes #endif 1186100ba1a6SJordan K. Hubbard 1187100ba1a6SJordan K. Hubbard 1188df8bae1dSRodney W. Grimes if (m->m_flags & M_BCAST || in_canforward(ip->ip_dst) == 0) { 1189df8bae1dSRodney W. Grimes ipstat.ips_cantforward++; 1190df8bae1dSRodney W. Grimes m_freem(m); 1191df8bae1dSRodney W. Grimes return; 1192df8bae1dSRodney W. Grimes } 1193df8bae1dSRodney W. Grimes HTONS(ip->ip_id); 1194df8bae1dSRodney W. Grimes if (ip->ip_ttl <= IPTTLDEC) { 1195df8bae1dSRodney W. Grimes icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, dest, 0); 1196df8bae1dSRodney W. Grimes return; 1197df8bae1dSRodney W. Grimes } 1198df8bae1dSRodney W. Grimes ip->ip_ttl -= IPTTLDEC; 1199df8bae1dSRodney W. Grimes 1200df8bae1dSRodney W. Grimes sin = (struct sockaddr_in *)&ipforward_rt.ro_dst; 1201df8bae1dSRodney W. Grimes if ((rt = ipforward_rt.ro_rt) == 0 || 1202df8bae1dSRodney W. Grimes ip->ip_dst.s_addr != sin->sin_addr.s_addr) { 1203df8bae1dSRodney W. Grimes if (ipforward_rt.ro_rt) { 1204df8bae1dSRodney W. Grimes RTFREE(ipforward_rt.ro_rt); 1205df8bae1dSRodney W. Grimes ipforward_rt.ro_rt = 0; 1206df8bae1dSRodney W. Grimes } 1207df8bae1dSRodney W. Grimes sin->sin_family = AF_INET; 1208df8bae1dSRodney W. Grimes sin->sin_len = sizeof(*sin); 1209df8bae1dSRodney W. Grimes sin->sin_addr = ip->ip_dst; 1210df8bae1dSRodney W. Grimes 12112c17fe93SGarrett Wollman rtalloc_ign(&ipforward_rt, RTF_PRCLONING); 1212df8bae1dSRodney W. Grimes if (ipforward_rt.ro_rt == 0) { 1213df8bae1dSRodney W. Grimes icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, dest, 0); 1214df8bae1dSRodney W. Grimes return; 1215df8bae1dSRodney W. Grimes } 1216df8bae1dSRodney W. Grimes rt = ipforward_rt.ro_rt; 1217df8bae1dSRodney W. Grimes } 1218df8bae1dSRodney W. Grimes 1219df8bae1dSRodney W. Grimes /* 1220df8bae1dSRodney W. Grimes * Save at most 64 bytes of the packet in case 1221df8bae1dSRodney W. Grimes * we need to generate an ICMP message to the src. 1222df8bae1dSRodney W. Grimes */ 1223df8bae1dSRodney W. Grimes mcopy = m_copy(m, 0, imin((int)ip->ip_len, 64)); 1224df8bae1dSRodney W. Grimes 1225df8bae1dSRodney W. Grimes /* 1226df8bae1dSRodney W. Grimes * If forwarding packet using same interface that it came in on, 1227df8bae1dSRodney W. Grimes * perhaps should send a redirect to sender to shortcut a hop. 1228df8bae1dSRodney W. Grimes * Only send redirect if source is sending directly to us, 1229df8bae1dSRodney W. Grimes * and if packet was not source routed (or has any options). 1230df8bae1dSRodney W. Grimes * Also, don't send redirect if forwarding using a default route 1231df8bae1dSRodney W. Grimes * or a route modified by a redirect. 1232df8bae1dSRodney W. Grimes */ 1233df8bae1dSRodney W. Grimes #define satosin(sa) ((struct sockaddr_in *)(sa)) 1234df8bae1dSRodney W. Grimes if (rt->rt_ifp == m->m_pkthdr.rcvif && 1235df8bae1dSRodney W. Grimes (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0 && 1236df8bae1dSRodney W. Grimes satosin(rt_key(rt))->sin_addr.s_addr != 0 && 1237df8bae1dSRodney W. Grimes ipsendredirects && !srcrt) { 1238df8bae1dSRodney W. Grimes #define RTA(rt) ((struct in_ifaddr *)(rt->rt_ifa)) 1239df8bae1dSRodney W. Grimes u_long src = ntohl(ip->ip_src.s_addr); 1240df8bae1dSRodney W. Grimes 1241df8bae1dSRodney W. Grimes if (RTA(rt) && 1242df8bae1dSRodney W. Grimes (src & RTA(rt)->ia_subnetmask) == RTA(rt)->ia_subnet) { 1243df8bae1dSRodney W. Grimes if (rt->rt_flags & RTF_GATEWAY) 1244df8bae1dSRodney W. Grimes dest = satosin(rt->rt_gateway)->sin_addr.s_addr; 1245df8bae1dSRodney W. Grimes else 1246df8bae1dSRodney W. Grimes dest = ip->ip_dst.s_addr; 1247df8bae1dSRodney W. Grimes /* Router requirements says to only send host redirects */ 1248df8bae1dSRodney W. Grimes type = ICMP_REDIRECT; 1249df8bae1dSRodney W. Grimes code = ICMP_REDIRECT_HOST; 1250df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC 1251df8bae1dSRodney W. Grimes if (ipprintfs) 1252df8bae1dSRodney W. Grimes printf("redirect (%d) to %lx\n", code, (u_long)dest); 1253df8bae1dSRodney W. Grimes #endif 1254df8bae1dSRodney W. Grimes } 1255df8bae1dSRodney W. Grimes } 1256df8bae1dSRodney W. Grimes 1257b97d15cbSGarrett Wollman error = ip_output(m, (struct mbuf *)0, &ipforward_rt, 1258b97d15cbSGarrett Wollman IP_FORWARDING, 0); 1259df8bae1dSRodney W. Grimes if (error) 1260df8bae1dSRodney W. Grimes ipstat.ips_cantforward++; 1261df8bae1dSRodney W. Grimes else { 1262df8bae1dSRodney W. Grimes ipstat.ips_forward++; 1263df8bae1dSRodney W. Grimes if (type) 1264df8bae1dSRodney W. Grimes ipstat.ips_redirectsent++; 1265df8bae1dSRodney W. Grimes else { 1266df8bae1dSRodney W. Grimes if (mcopy) 1267df8bae1dSRodney W. Grimes m_freem(mcopy); 1268df8bae1dSRodney W. Grimes return; 1269df8bae1dSRodney W. Grimes } 1270df8bae1dSRodney W. Grimes } 1271df8bae1dSRodney W. Grimes if (mcopy == NULL) 1272df8bae1dSRodney W. Grimes return; 1273df8bae1dSRodney W. Grimes destifp = NULL; 1274df8bae1dSRodney W. Grimes 1275df8bae1dSRodney W. Grimes switch (error) { 1276df8bae1dSRodney W. Grimes 1277df8bae1dSRodney W. Grimes case 0: /* forwarded, but need redirect */ 1278df8bae1dSRodney W. Grimes /* type, code set above */ 1279df8bae1dSRodney W. Grimes break; 1280df8bae1dSRodney W. Grimes 1281df8bae1dSRodney W. Grimes case ENETUNREACH: /* shouldn't happen, checked above */ 1282df8bae1dSRodney W. Grimes case EHOSTUNREACH: 1283df8bae1dSRodney W. Grimes case ENETDOWN: 1284df8bae1dSRodney W. Grimes case EHOSTDOWN: 1285df8bae1dSRodney W. Grimes default: 1286df8bae1dSRodney W. Grimes type = ICMP_UNREACH; 1287df8bae1dSRodney W. Grimes code = ICMP_UNREACH_HOST; 1288df8bae1dSRodney W. Grimes break; 1289df8bae1dSRodney W. Grimes 1290df8bae1dSRodney W. Grimes case EMSGSIZE: 1291df8bae1dSRodney W. Grimes type = ICMP_UNREACH; 1292df8bae1dSRodney W. Grimes code = ICMP_UNREACH_NEEDFRAG; 1293df8bae1dSRodney W. Grimes if (ipforward_rt.ro_rt) 1294df8bae1dSRodney W. Grimes destifp = ipforward_rt.ro_rt->rt_ifp; 1295df8bae1dSRodney W. Grimes ipstat.ips_cantfrag++; 1296df8bae1dSRodney W. Grimes break; 1297df8bae1dSRodney W. Grimes 1298df8bae1dSRodney W. Grimes case ENOBUFS: 1299df8bae1dSRodney W. Grimes type = ICMP_SOURCEQUENCH; 1300df8bae1dSRodney W. Grimes code = 0; 1301df8bae1dSRodney W. Grimes break; 1302df8bae1dSRodney W. Grimes } 1303df8bae1dSRodney W. Grimes icmp_error(mcopy, type, code, dest, destifp); 1304df8bae1dSRodney W. Grimes } 1305df8bae1dSRodney W. Grimes 1306df8bae1dSRodney W. Grimes int 1307f0068c4aSGarrett Wollman ip_rsvp_init(struct socket *so) 1308f0068c4aSGarrett Wollman { 1309f0068c4aSGarrett Wollman if (so->so_type != SOCK_RAW || 1310f0068c4aSGarrett Wollman so->so_proto->pr_protocol != IPPROTO_RSVP) 1311f0068c4aSGarrett Wollman return EOPNOTSUPP; 1312f0068c4aSGarrett Wollman 1313f0068c4aSGarrett Wollman if (ip_rsvpd != NULL) 1314f0068c4aSGarrett Wollman return EADDRINUSE; 1315f0068c4aSGarrett Wollman 1316f0068c4aSGarrett Wollman ip_rsvpd = so; 13171c5de19aSGarrett Wollman /* 13181c5de19aSGarrett Wollman * This may seem silly, but we need to be sure we don't over-increment 13191c5de19aSGarrett Wollman * the RSVP counter, in case something slips up. 13201c5de19aSGarrett Wollman */ 13211c5de19aSGarrett Wollman if (!ip_rsvp_on) { 13221c5de19aSGarrett Wollman ip_rsvp_on = 1; 13231c5de19aSGarrett Wollman rsvp_on++; 13241c5de19aSGarrett Wollman } 1325f0068c4aSGarrett Wollman 1326f0068c4aSGarrett Wollman return 0; 1327f0068c4aSGarrett Wollman } 1328f0068c4aSGarrett Wollman 1329f0068c4aSGarrett Wollman int 1330f0068c4aSGarrett Wollman ip_rsvp_done(void) 1331f0068c4aSGarrett Wollman { 1332f0068c4aSGarrett Wollman ip_rsvpd = NULL; 13331c5de19aSGarrett Wollman /* 13341c5de19aSGarrett Wollman * This may seem silly, but we need to be sure we don't over-decrement 13351c5de19aSGarrett Wollman * the RSVP counter, in case something slips up. 13361c5de19aSGarrett Wollman */ 13371c5de19aSGarrett Wollman if (ip_rsvp_on) { 13381c5de19aSGarrett Wollman ip_rsvp_on = 0; 13391c5de19aSGarrett Wollman rsvp_on--; 13401c5de19aSGarrett Wollman } 1341f0068c4aSGarrett Wollman return 0; 1342f0068c4aSGarrett Wollman } 1343