www.pudn.com > vxworks_tcpip_code.rar > ip_input.c
/* ip_input.c - internet input routines */
/* Copyright 1984 - 1999 Wind River Systems, Inc. */
#include "copyright_wrs.h"
/*
* Copyright (c) 1982, 1986, 1988, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ip_input.c 8.2 (Berkeley) 1/4/94
*/
/*
modification history
--------------------
01r,08nov99,pul merging T2 cumulative patch 2
01q,04jun99,spm corrected ip_forward() routine to handle source routing
options for hosts (SPR #27727)
01p,28feb99,ham corrected comment below - (wrong)34040 -> (right)32767.
01o,28feb99,ham fixed reassembly problem larger ip_off 34040, spr#23250.
01n,09jan98,vin changed IP_CKSUM_RECV_MASK to IP_DO_CHECKSUM_RCV
01m,13nov97,vin added HTONS(ip->ip_len) in ip_forward before icmp_error,
consolidated all flags into ipCfgFlags.
01l,28oct97,vin reverted on the fix in version01j, what was vin thinking.
01k,15jul97,spm made IP checksum calculation configurable (SPR #7836)
01j,05jun97,vin fixed problem in ip_forward().HTONS(ip_len).
01i,17apr97,vin added more functionality to the ipFilterHook.
01h,08mar97,vin added mCastRouteFwdHook to access mcast routing code.
01g,11apr97,rjc added ipFilter Hook and strongEnded flag processing.
01f,04mar97,vin added fix to null domain pointer in ip_init SPR 8078.
01e,05feb97,rjc tos routing changes
01d,31jan97,vin changed declaration according to prototype decl in protosw.h
01c,07jan96,vin moved iptime() from ip_icmp, added _icmpErrorHook instead
of icmp_error() for scalability, added ipfragttl.
01b,30oct96,vin re-worked ipreassembly, made it free from dtom macros.
added ipReAssembly, replace m_get(..) with mBufClGet(..)
01a,03mar96,vin created from BSD4.4 stuff,integrated with 02r of ip_input.c
*/
#include "vxWorks.h"
#include "tickLib.h"
#include "netLib.h"
#include "net/systm.h"
#include "net/mbuf.h"
#include "net/domain.h"
#include "net/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"
extern _insque();
extern _remque();
extern sysClkRateGet();
extern VOIDFUNCPTR _icmpErrorHook;
extern FUNCPTR _ipFilterHook; /* ipFilter hook defined in netLib.c */extern FUNCPTR _mCastRouteFwdHook; /* WRS mcast forward command hook */
/* globals */
#ifndef IPFORWARDING
#ifdef GATEWAY
#define IPFORWARDING 1 /* forward IP packets not for us */
#else /* GATEWAY */
#define IPFORWARDING 0 /* don't forward IP packets not for us */
#endif /* GATEWAY */
#endif /* IPFORWARDING */
#ifndef IPSENDREDIRECTS
#define IPSENDREDIRECTS 1
#endif
#define FORWARD_BROADCASTS
#ifdef FORWARD_BROADCASTS
#define DIRECTED_BROADCAST
FUNCPTR proxyBroadcastHook = NULL;
#endif
int _ipCfgFlags; /* ip configurations, bit field flag */
int ip_defttl = IPDEFTTL; /* default IP ttl */
int ipfragttl = IPFRAGTTL; /* default IP fragment timeout */
#ifdef DIAGNOSTIC
int ipprintfs = 0;
#endif
int ipStrongEnded = FALSE; /* default ip ended'ness */
extern struct domain inetdomain;
extern struct protosw inetsw[];
u_char ip_protox[IPPROTO_MAX];
int ipqmaxlen = IFQ_MAXLEN;
struct in_ifaddr *in_ifaddr; /* first inet address */
struct ifqueue ipintrq;
struct ipstat ipstat;
struct ipq ipq; /* ip reass. queue */
u_short ip_id; /* ip packet ctr, for ids */
/*
* We need to save the IP options in case a protocol wants to respond
* to an incoming packet over the same route if the packet got here
* using IP source routing. This allows connection establishment and
* maintenance when the remote end is on a network that is not known
* to us.
*/
int ip_nhops = 0;
static struct ip_srcrt {
struct in_addr dst; /* final destination */
char nop; /* one NOP to align */
char srcopt[IPOPT_OFFSET + 1]; /* OPTVAL, OLEN and OFFSET */
struct in_addr route[MAX_IPOPTLEN/sizeof(struct in_addr)];
} ip_srcrt;
/* forward declarations */
static void save_rte (u_char *, struct in_addr);
LOCAL struct mbuf * ipReAssemble (FAST struct mbuf * pMbuf,
FAST struct ipq * pIpFragQueue);
/*
* IP initialization: fill in IP protocol switch table.
* All protocols not implemented in kernel go to raw IP protocol handler.
*/
void
ip_init()
{
register struct protosw *pr;
register int i;
pr = pffindproto(PF_INET, IPPROTO_RAW, SOCK_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 != NULL) &&
pr->pr_domain->dom_family == PF_INET &&
pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW)
ip_protox[pr->pr_protocol] = pr - inetsw;
ipq.next = ipq.prev = &ipq;
ip_id = iptime() & 0xffff;
ipintrq.ifq_maxlen = ipqmaxlen;
return;
}
struct sockaddr_in ipaddr = { sizeof(ipaddr), AF_INET };
struct route ipforward_rt;
/*
* Ip input routine. Checksum and byte swap header. If fragmented
* try to reassemble. Process options. Pass to next level.
*/
void
ipintr()
{
register struct ip *ip;
register struct mbuf *m;
register struct ipq *fp;
register struct in_ifaddr *ia;
int hlen, s;
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)
return;
#ifdef DIAGNOSTIC
if ((m->m_flags & M_PKTHDR) == 0)
panic("ipintr no HDR");
#endif
/*
* If no IP addresses have been set yet but the interfaces
* are receiving, can't do anything with incoming packets yet.
*/
if (in_ifaddr == NULL)
goto bad;
ipstat.ips_total++;
if (m->m_len < sizeof (struct ip) &&
(m = m_pullup(m, sizeof (struct ip))) == 0) {
ipstat.ips_toosmall++;
goto next;
}
ip = mtod(m, struct ip *);
if (ip->ip_v != IPVERSION) {
ipstat.ips_badvers++;
goto bad;
}
hlen = ip->ip_hl << 2;
if (hlen < sizeof(struct ip)) { /* 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 (_ipCfgFlags & IP_DO_CHECKSUM_RCV)
{
if ( (ip->ip_sum = in_cksum(m, hlen)) != 0)
{
ipstat.ips_badsum++;
goto bad;
}
}
/*
* Convert fields to host representation.
*/
NTOHS(ip->ip_len);
if (ip->ip_len < hlen) {
ipstat.ips_badlen++;
goto bad;
}
NTOHS(ip->ip_id);
NTOHS(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.
*/
if (m->m_pkthdr.len < ip->ip_len) {
ipstat.ips_tooshort++;
goto bad;
}
if (m->m_pkthdr.len > ip->ip_len) {
if (m->m_len == m->m_pkthdr.len) {
m->m_len = ip->ip_len;
m->m_pkthdr.len = ip->ip_len;
} else
m_adj(m, ip->ip_len - m->m_pkthdr.len);
}
/* ipFilterHook example fireWall Hook processing */
if (_ipFilterHook != NULL)
{
/* process the packet if the hook returns other than TRUE */
if ((*_ipFilterHook) (m->m_pkthdr.rcvif, &m, &ip, hlen) == TRUE)
goto next; /* the hook should free the mbuf */
}
/*
* 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).
*/
ip_nhops = 0; /* for source routed packets */
if (hlen > sizeof (struct ip) && ip_dooptions(m))
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 ip is strong ended we will accept packets on an interface
* only if destination in ip hdr matches ip address on the
* receiving interface and not just any interface.
*/
if ((ipStrongEnded == TRUE) &&
(ia->ia_ifp != m->m_pkthdr.rcvif))
continue;
if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr)
goto ours;
if (
#ifdef DIRECTED_BROADCAST
ia->ia_ifp == m->m_pkthdr.rcvif &&
#endif
(ia->ia_ifp->if_flags & IFF_BROADCAST)) {
u_long t;
if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr ==
ip->ip_dst.s_addr)
goto ours;
if (ip->ip_dst.s_addr == ia->ia_netbroadcast.s_addr)
goto ours;
/*
* Look for all-0's host part (old broadcast addr),
* either for subnet or net.
*/
t = ntohl(ip->ip_dst.s_addr);
if (t == ia->ia_subnet)
goto ours;
if (t == ia->ia_net)
goto ours;
}
}
if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
struct in_multi *inm;
if (_mCastRouteFwdHook != NULL) {
/*
* If we are acting as a multicast router, all
* incoming multicast packets are passed to the
* kernel-level multicast forwarding function.
* The packet is returned (relatively) intact; if
* ip_mforward() returns a non-zero value, the packet
* must be discarded, else it may be accepted below.
*
* (The IP ident field is put in the same byte order
* as expected when ip_mforward() is called from
* ip_output().)
*/
ip->ip_id = htons(ip->ip_id);
if ((*_mCastRouteFwdHook)(m, m->m_pkthdr.rcvif) != 0) {
ipstat.ips_cantforward++;
m_freem(m);
goto next;
}
ip->ip_id = ntohs(ip->ip_id);
/*
* The process-level routing demon needs to receive
* all multicast IGMP packets, whether or not this
* host belongs to their destination groups.
*/
if (ip->ip_p == IPPROTO_IGMP)
goto ours;
ipstat.ips_forward++;
}
/*
* See if we belong to the destination multicast group on the
* arrival interface.
*/
IN_LOOKUP_MULTI(ip->ip_dst, m->m_pkthdr.rcvif, inm);
if (inm == NULL) {
ipstat.ips_cantforward++;
m_freem(m);
goto next;
}
goto ours;
}
if (ip->ip_dst.s_addr == (u_long)INADDR_BROADCAST)
goto ours;
if (ip->ip_dst.s_addr == INADDR_ANY)
goto ours;
/*
* Not for us; forward if possible and desirable.
*/
if ((_ipCfgFlags & IP_DO_FORWARDING) == 0) {
ipstat.ips_cantforward++;
m_freem(m);
} else
ip_forward(m, 0);
goto next;
ours:
/*
* If offset or IP_MF are set, must reassemble.
* Otherwise, nothing need be done.
* (We could look in the reassembly queue to see
* if the packet was previously fragmented,
* but it's not worth the time; just let them time out.)
*/
if (ip->ip_off &~ IP_DF) {
/*
* 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 &= ~1;
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 & 1 || ip->ip_off) {
ipstat.ips_fragments++;
if ((m = ipReAssemble (m, fp)) == NULL)
goto next;
ipstat.ips_reassembled++;
} else
if (fp)
ip_freef(fp);
} else
ip->ip_len -= hlen;
#ifdef FORWARD_BROADCASTS
if ((m->m_flags & M_BCAST) && (proxyBroadcastHook != NULL))
(* proxyBroadcastHook) (m, m->m_pkthdr.rcvif);
#endif
/*
* Switch out to protocol's input routine.
*/
ipstat.ips_delivered++;
(*inetsw[ip_protox[ip->ip_p]].pr_input)(m, hlen);
goto next;
bad:
m_freem(m);
goto next;
}
/*******************************************************************************
*
* ipReAssemble - reassemble ip fragments
*
* This function reassembles the ip fragments and returns the pointer to the
* mbuf chain if reassembly is complete orelse returns null.
*
* RETURNS: pMbuf/NULL
*
* SEE ALSO: ,
*
* NOMANUAL
*/
LOCAL struct mbuf * ipReAssemble
(
FAST struct mbuf * pMbuf, /* pointer to mbuf chain fragment */
FAST struct ipq * pIpFragQueue /* pointer to ip fragment queue */
)
{
FAST struct mbuf ** pPtrMbuf; /* pointer to ptr to mbuf */
FAST struct mbuf * pMbPktFrag = NULL; /* pointer to the packet */
FAST struct ip * pIpHdr; /* pointer to ip header */
FAST struct ipasfrag * pIpHdrFrag; /* ipfragment header */
FAST int len;
FAST struct mbuf * pMbufTmp; /* pointer to mbuf */
pIpHdr = mtod (pMbuf, struct ip *);
pMbuf->m_nextpkt = NULL;
/*
* If first fragment to arrive, create a reassembly queue.
*/
if (pIpFragQueue == 0)
{
if ((pMbufTmp = mBufClGet (M_DONTWAIT, MT_FTABLE, sizeof(struct ipq),
TRUE)) == NULL)
goto dropFrag;
pIpFragQueue = mtod(pMbufTmp, struct ipq *);
insque(pIpFragQueue, &ipq);
pIpFragQueue->ipq_ttl = ipfragttl; /* configuration parameter */
pIpFragQueue->ipq_p = pIpHdr->ip_p;
pIpFragQueue->ipq_id = pIpHdr->ip_id;
pIpFragQueue->ipq_src = ((struct ip *)pIpHdr)->ip_src;
pIpFragQueue->ipq_dst = ((struct ip *)pIpHdr)->ip_dst;
pIpFragQueue->pMbufHdr = pMbufTmp; /* back pointer to mbuf */
pIpFragQueue->pMbufPkt = pMbuf; /* first fragment received */
goto ipChkReAssembly;
}
for (pPtrMbuf = &(pIpFragQueue->pMbufPkt); *pPtrMbuf != NULL;
pPtrMbuf = &(*pPtrMbuf)->m_nextpkt)
{
pMbPktFrag = *pPtrMbuf;
pIpHdrFrag = mtod(pMbPktFrag, struct ipasfrag *);
if ((USHORT)pIpHdr->ip_off > (USHORT)pIpHdrFrag->ip_off)
{
if ((len = (signed int)((USHORT)pIpHdrFrag->ip_off +
pIpHdrFrag->ip_len -
(USHORT)pIpHdr->ip_off)) > 0)
{
if (len >= pIpHdr->ip_len)
goto dropFrag; /* drop the fragment */
pIpHdrFrag->ip_len -= len;
m_adj(pMbPktFrag, -len); /* trim from the tail */
}
if (pMbPktFrag->m_nextpkt == NULL)
{ /* this is the likely case most of the time */
pMbPktFrag->m_nextpkt = pMbuf; /* insert the fragment */
pMbuf->m_nextpkt = NULL;
break;
}
}
else /* if pIpHdr->ip_off <= pIpHdrFrag->ip_off */
{
/* if complete overlap */
while (((USHORT)pIpHdr->ip_off + pIpHdr->ip_len) >=
((USHORT)pIpHdrFrag->ip_off + pIpHdrFrag->ip_len))
{
*pPtrMbuf = (*pPtrMbuf)->m_nextpkt;
pMbPktFrag->m_nextpkt = NULL;
m_freem (pMbPktFrag); /* drop the fragment */
pMbPktFrag = *pPtrMbuf;
if (pMbPktFrag == NULL)
break;
pIpHdrFrag = mtod(pMbPktFrag, struct ipasfrag *);
}
/* if partial overlap trim my data at the end*/
if ((pMbPktFrag != NULL) &&
((len = (((USHORT)pIpHdr->ip_off + pIpHdr->ip_len) -
pIpHdrFrag->ip_off)) > 0))
{
pIpHdr->ip_len -= len;
m_adj (pMbuf, -len); /* trim from the tail */
}
pMbuf->m_nextpkt = pMbPktFrag;
*pPtrMbuf = pMbuf; /* insert the current fragment */
break;
}
}
ipChkReAssembly:
len = 0;
for (pMbPktFrag = pIpFragQueue->pMbufPkt; pMbPktFrag != NULL;
pMbPktFrag = pMbPktFrag->m_nextpkt)
{
pIpHdrFrag = mtod(pMbPktFrag, struct ipasfrag *);
if ((USHORT)pIpHdrFrag->ip_off != len)
return (NULL);
len += pIpHdrFrag->ip_len;
}
if (pIpHdrFrag->ipf_mff & 1) /* last fragment's mff bit still set */
return (NULL);
/* reassemble and concatenate all fragments */
pMbuf = pIpFragQueue->pMbufPkt;
pMbufTmp = pMbuf->m_nextpkt;
pMbuf->m_nextpkt = NULL;
while (pMbufTmp != NULL)
{
pMbPktFrag = pMbufTmp;
pIpHdrFrag = mtod(pMbPktFrag, struct ipasfrag *);
pMbPktFrag->m_data += pIpHdrFrag->ip_hl << 2; /* remove the header */
pMbPktFrag->m_len -= pIpHdrFrag->ip_hl << 2;
pMbufTmp = pMbPktFrag->m_nextpkt;
pMbPktFrag->m_nextpkt = NULL ;
m_cat (pMbuf, pMbPktFrag); /* concatenate the fragment */
}
pIpHdrFrag = mtod(pMbuf, struct ipasfrag *);
pIpHdrFrag->ip_len = len; /* length of the ip packet */
pIpHdrFrag->ipf_mff &= ~1;
remque(pIpFragQueue);
(void) m_free (pIpFragQueue->pMbufHdr);
/* some debugging cruft by sklower, below, will go away soon */
if (pMbuf->m_flags & M_PKTHDR)
{ /* XXX this should be done elsewhere */
len = 0;
for (pMbufTmp = pMbuf; pMbufTmp; pMbufTmp = pMbufTmp->m_next)
len += pMbufTmp->m_len;
pMbuf->m_pkthdr.len = len;
}
return (pMbuf); /* return the assembled packet */
dropFrag:
ipstat.ips_fragdropped++;
m_freem (pMbuf); /* free the fragment */
return (NULL);
}
/*
* Free a fragment reassembly header and all
* associated datagrams.
*/
void
ip_freef(fp)
struct ipq *fp;
{
struct mbuf ** pPtrMbuf;
struct mbuf * pMbuf;
/* free all fragments */
pPtrMbuf = &(fp->pMbufPkt);
while (*pPtrMbuf)
{
pMbuf = (*pPtrMbuf)->m_nextpkt;
m_freem (*pPtrMbuf);
*pPtrMbuf = pMbuf;
}
remque(fp);
(void) m_free(fp->pMbufHdr);
}
n_time
iptime()
{
u_long t;
/* t is time in milliseconds (i hope) */
t = ((int) tickGet () * 1000) / sysClkRateGet ();
return (htonl(t));
}
/*
* IP timer processing;
* if a timer expires on a reassembly
* queue, discard it.
*/
void
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) {
ipstat.ips_fragtimeout++;
ip_freef(fp->prev);
}
}
splx(s);
return;
}
/*
* Drain off all datagram fragments.
*/
void
ip_drain()
{
while (ipq.next != &ipq) {
ipstat.ips_fragdropped++;
ip_freef(ipq.next);
}
return;
}
/*
* Do option processing on a datagram,
* possibly discarding it if bad options are encountered,
* or forwarding it if source-routed.
* Returns 1 if packet has been forwarded/freed,
* 0 if the packet should be processed further.
*/
int
ip_dooptions(m)
struct mbuf *m;
{
register struct ip *ip = mtod(m, struct ip *);
register u_char *cp;
register struct ip_timestamp *ipt;
register struct in_ifaddr *ia;
int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0;
struct in_addr *sin, dst;
n_time ntime;
dst = ip->ip_dst;
cp = (u_char *)(ip + 1);
cnt = (ip->ip_hl << 2) - sizeof (struct ip);
for (; cnt > 0; cnt -= optlen, cp += optlen) {
opt = cp[IPOPT_OPTVAL];
if (opt == IPOPT_EOL)
break;
if (opt == IPOPT_NOP)
optlen = 1;
else {
optlen = cp[IPOPT_OLEN];
if (optlen <= 0 || optlen > cnt) {
code = &cp[IPOPT_OLEN] - (u_char *)ip;
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 is on directly accessible net.
*/
case IPOPT_LSRR:
case IPOPT_SSRR:
if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
code = &cp[IPOPT_OFFSET] - (u_char *)ip;
goto bad;
}
ipaddr.sin_addr = ip->ip_dst;
TOS_SET (&ipaddr, ip->ip_tos); /* tos routing */
ia = (struct in_ifaddr *)
ifa_ifwithaddr((struct sockaddr *)&ipaddr);
if (ia == 0) {
if (opt == IPOPT_SSRR) {
type = ICMP_UNREACH;
code = ICMP_UNREACH_SRCFAIL;
goto bad;
}
/*
* Loose routing, and not at next destination
* yet; nothing to do except forward.
*/
break;
}
off--; /* 0 origin */
if (off > optlen - sizeof(struct in_addr)) {
/*
* End of source route. Should be for us.
*/
save_rte(cp, ip->ip_src);
break;
}
/*
* locate outgoing interface
*/
bcopy((caddr_t)(cp + off), (caddr_t)&ipaddr.sin_addr,
sizeof(ipaddr.sin_addr));
if (opt == IPOPT_SSRR) {
#define INA struct in_ifaddr *
#define SA struct sockaddr *
if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0)
ia = (INA)ifa_ifwithnet((SA)&ipaddr);
} else
ia = ip_rtaddr(ipaddr.sin_addr);
if (ia == 0) {
type = ICMP_UNREACH;
code = ICMP_UNREACH_SRCFAIL;
goto bad;
}
ip->ip_dst = ipaddr.sin_addr;
bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
(caddr_t)(cp + off), sizeof(struct in_addr));
cp[IPOPT_OFFSET] += sizeof(struct in_addr);
/*
* Let ip_intr's mcast routing check handle mcast pkts
*/
forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr));
break;
case IPOPT_RR:
if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
code = &cp[IPOPT_OFFSET] - (u_char *)ip;
goto bad;
}
/*
* If no space remains, ignore.
*/
off--; /* 0 origin */
if (off > optlen - sizeof(struct in_addr))
break;
bcopy((caddr_t)(&ip->ip_dst), (caddr_t)&ipaddr.sin_addr,
sizeof(ipaddr.sin_addr));
/*
* locate outgoing interface; if we're the destination,
* use the incoming interface (should be same).
*/
if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 &&
(ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {
type = ICMP_UNREACH;
code = ICMP_UNREACH_HOST;
goto bad;
}
bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
(caddr_t)(cp + off), sizeof(struct in_addr));
cp[IPOPT_OFFSET] += sizeof(struct in_addr);
break;
case IPOPT_TS:
code = cp - (u_char *)ip;
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 + ipt->ipt_ptr - 1);
switch (ipt->ipt_flg) {
case IPOPT_TS_TSONLY:
break;
case IPOPT_TS_TSANDADDR:
if (ipt->ipt_ptr + sizeof(n_time) +
sizeof(struct in_addr) > ipt->ipt_len)
goto bad;
ipaddr.sin_addr = dst;
ia = (INA)ifaof_ifpforaddr((SA)&ipaddr,
m->m_pkthdr.rcvif);
if (ia == 0)
continue;
bcopy((caddr_t)&IA_SIN(ia)->sin_addr,
(caddr_t)sin, sizeof(struct in_addr));
ipt->ipt_ptr += sizeof(struct in_addr);
break;
case IPOPT_TS_PRESPEC:
if (ipt->ipt_ptr + sizeof(n_time) +
sizeof(struct in_addr) > ipt->ipt_len)
goto bad;
bcopy((caddr_t)sin, (caddr_t)&ipaddr.sin_addr,
sizeof(struct in_addr));
if (ifa_ifwithaddr((SA)&ipaddr) == 0)
continue;
ipt->ipt_ptr += sizeof(struct in_addr);
break;
default:
goto bad;
}
ntime = iptime();
bcopy((caddr_t)&ntime, (caddr_t)cp + ipt->ipt_ptr - 1,
sizeof(n_time));
ipt->ipt_ptr += sizeof(n_time);
}
}
if (forward) {
ip_forward(m, 1);
return (1);
}
return (0);
bad:
ip->ip_len -= ip->ip_hl << 2; /* XXX icmp_error adds in hdr length */
if (_icmpErrorHook != NULL)
(*_icmpErrorHook) (m, type, code, 0, 0);
ipstat.ips_badoptions++;
return (1);
}
/*
* Given address of next destination (final or next hop),
* return internet address info of interface to be used to get there.
*/
struct in_ifaddr *
ip_rtaddr(dst)
struct in_addr dst;
{
register struct sockaddr_in *sin;
sin = (struct sockaddr_in *) &ipforward_rt.ro_dst;
if (ipforward_rt.ro_rt == 0 || dst.s_addr != sin->sin_addr.s_addr) {
if (ipforward_rt.ro_rt) {
RTFREE(ipforward_rt.ro_rt);
ipforward_rt.ro_rt = 0;
}
sin->sin_family = AF_INET;
sin->sin_len = sizeof(*sin);
sin->sin_addr = dst;
rtalloc(&ipforward_rt);
}
if (ipforward_rt.ro_rt == 0)
return ((struct in_ifaddr *)0);
return ((struct in_ifaddr *) ipforward_rt.ro_rt->rt_ifa);
}
/*
* Save incoming source route for use in replies,
* to be picked up later by ip_srcroute if the receiver is interested.
*/
void
save_rte(option, dst)
u_char *option;
struct in_addr dst;
{
unsigned olen;
olen = option[IPOPT_OLEN];
#ifdef DIAGNOSTIC
if (ipprintfs)
printf("save_rte: olen %d\n", olen);
#endif
if (olen > sizeof(ip_srcrt) - (1 + sizeof(dst)))
return;
bcopy((caddr_t)option, (caddr_t)ip_srcrt.srcopt, olen);
ip_nhops = (olen - IPOPT_OFFSET - 1) / sizeof(struct in_addr);
ip_srcrt.dst = dst;
}
/*
* Retrieve incoming source route for use in replies,
* in the same form used by setsockopt.
* The first hop is placed before the options, will be removed later.
*/
struct mbuf *
ip_srcroute()
{
register struct in_addr *p, *q;
register struct mbuf *m;
if (ip_nhops == 0)
return ((struct mbuf *)0);
m= mBufClGet(M_DONTWAIT, MT_SOOPTS, CL_SIZE_128, TRUE);
if (m == 0)
return ((struct mbuf *)0);
#define OPTSIZ (sizeof(ip_srcrt.nop) + sizeof(ip_srcrt.srcopt))
/* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */
m->m_len = ip_nhops * sizeof(struct in_addr) + sizeof(struct in_addr) +
OPTSIZ;
#ifdef DIAGNOSTIC
if (ipprintfs)
printf("ip_srcroute: nhops %d mlen %d", ip_nhops, m->m_len);
#endif
/*
* First save first hop for return route
*/
p = &ip_srcrt.route[ip_nhops - 1];
*(mtod(m, struct in_addr *)) = *p--;
#ifdef DIAGNOSTIC
if (ipprintfs)
printf(" hops %lx", ntohl(mtod(m, struct in_addr *)->s_addr));
#endif
/*
* Copy option fields and padding (nop) to mbuf.
*/
ip_srcrt.nop = IPOPT_NOP;
ip_srcrt.srcopt[IPOPT_OFFSET] = IPOPT_MINOFF;
bcopy((caddr_t)&ip_srcrt.nop,
mtod(m, caddr_t) + sizeof(struct in_addr), OPTSIZ);
q = (struct in_addr *)(mtod(m, caddr_t) +
sizeof(struct in_addr) + OPTSIZ);
#undef OPTSIZ
/*
* Record return path as an IP source route,
* reversing the path (pointers are now aligned).
*/
while (p >= ip_srcrt.route) {
#ifdef DIAGNOSTIC
if (ipprintfs)
printf(" %lx", ntohl(q->s_addr));
#endif
*q++ = *p--;
}
/*
* Last hop goes to final destination.
*/
*q = ip_srcrt.dst;
#ifdef DIAGNOSTIC
if (ipprintfs)
printf(" %lx\n", ntohl(q->s_addr));
#endif
return (m);
}
/*
* 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.
* XXX should be deleted; last arg currently ignored.
*/
void
ip_stripoptions(m, mopt)
register struct mbuf *m;
struct mbuf *mopt;
{
register int i;
struct ip *ip = mtod(m, struct ip *);
register caddr_t opts;
int olen;
olen = (ip->ip_hl<<2) - sizeof (struct ip);
opts = (caddr_t)(ip + 1);
i = m->m_len - (sizeof (struct ip) + olen);
bcopy(opts + olen, opts, (unsigned)i);
m->m_len -= olen;
if (m->m_flags & M_PKTHDR)
m->m_pkthdr.len -= olen;
ip->ip_hl = sizeof(struct ip) >> 2;
}
u_char inetctlerrmap[PRC_NCMDS] = {
0, 0, 0, 0,
0, EMSGSIZE, EHOSTDOWN, EHOSTUNREACH,
EHOSTUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED,
EMSGSIZE, EHOSTUNREACH, 0, 0,
0, 0, 0, 0,
ENOPROTOOPT
};
/* Added to allow the user to determine if this packet should be forwarded. */
int (*inCanForwardHook)( struct in_addr in );
/*
* 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 repertoire
* of codes and types.
*
* If not forwarding, just drop the packet. This could be confusing
* if (_ipCfgFlags & IP_DO_FORWARDING) was zero but some routing protocol was
* advancing us as a gateway to somewhere. However, we must let the routing
* protocol deal with that.
*
* The srcrt parameter indicates whether the packet is being forwarded
* via a source route.
*/
void
ip_forward(m, srcrt)
struct mbuf *m;
int srcrt;
{
register struct ip *ip = mtod(m, struct ip *);
register struct sockaddr_in *sin;
register struct rtentry *rt;
int error, type = 0, code = 0;
struct mbuf *mcopy;
n_long dest;
struct ifnet *destifp;
dest = 0;
#ifdef DIAGNOSTIC
if (ipprintfs)
printf("forward: src %x dst %x ttl %x\n", ip->ip_src,
ip->ip_dst, ip->ip_ttl);
#endif
if (m->m_flags & M_BCAST || in_canforward(ip->ip_dst) == 0) {
ipstat.ips_cantforward++;
m_freem(m);
return;
}
/* Added to allow the user access to filter packets. */
if ( inCanForwardHook && (inCanForwardHook( ip->ip_dst ) == 0) ) {
ipstat.ips_cantforward++;
m_freem(m);
return;
}
HTONS(ip->ip_id);
if (ip->ip_ttl <= IPTTLDEC) {
if (_icmpErrorHook != NULL)
{
HTONS(ip->ip_len);
(*_icmpErrorHook)(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS,
dest, 0);
}
return;
}
ip->ip_ttl -= IPTTLDEC;
sin = (struct sockaddr_in *)&ipforward_rt.ro_dst;
if ((rt = ipforward_rt.ro_rt) == 0 ||
ip->ip_dst.s_addr != sin->sin_addr.s_addr) {
if (ipforward_rt.ro_rt) {
RTFREE(ipforward_rt.ro_rt);
ipforward_rt.ro_rt = 0;
}
sin->sin_family = AF_INET;
sin->sin_len = sizeof(*sin);
sin->sin_addr = ip->ip_dst;
rtalloc(&ipforward_rt);
if (ipforward_rt.ro_rt == 0) {
if (_icmpErrorHook != NULL)
(*_icmpErrorHook)(m, ICMP_UNREACH,
ICMP_UNREACH_HOST, dest, 0);
return;
}
rt = ipforward_rt.ro_rt;
}
/*
* Save at most 64 bytes of the packet in case
* we need to generate an ICMP message to the src.
*/
mcopy = m_copy(m, 0, min((int)ip->ip_len, 64));
/*
* If forwarding packet using same interface that it came in on,
* perhaps should send a redirect to sender to shortcut a hop.
* Only send redirect if source is sending directly to us,
* and if packet was not source routed (or has any options).
* Also, don't send redirect if forwarding using a default route
* or a route modified by a redirect.
*/
#define satosin(sa) ((struct sockaddr_in *)(sa))
if (rt->rt_ifp == m->m_pkthdr.rcvif &&
(rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0 &&
satosin(rt_key(rt))->sin_addr.s_addr != 0 &&
(_ipCfgFlags & IP_DO_REDIRECT) && !srcrt) {
#define RTA(rt) ((struct in_ifaddr *)(rt->rt_ifa))
u_long src = ntohl(ip->ip_src.s_addr);
if (RTA(rt) &&
(src & RTA(rt)->ia_subnetmask) == RTA(rt)->ia_subnet) {
if (rt->rt_flags & RTF_GATEWAY)
dest = satosin(rt->rt_gateway)->sin_addr.s_addr;
else
dest = ip->ip_dst.s_addr;
/* Router requirements says to only send host redirects */
type = ICMP_REDIRECT;
code = ICMP_REDIRECT_HOST;
#ifdef DIAGNOSTIC
if (ipprintfs)
printf("redirect (%d) to %lx\n", code, (u_long)dest);
#endif
}
}
/* set the M_FORWARD flag, to suggest route is to be entered into
* fast path.
*/
m->m_flags |= M_FORWARD;
error = ip_output(m, (struct mbuf *)0, &ipforward_rt, IP_FORWARDING
#ifdef DIRECTED_BROADCAST
| IP_ALLOWBROADCAST
#endif
, 0);
if (error)
ipstat.ips_cantforward++;
else {
ipstat.ips_forward++;
if (type)
ipstat.ips_redirectsent++;
else {
if (mcopy)
m_freem(mcopy);
return;
}
}
if (mcopy == NULL)
return;
destifp = NULL;
switch (error) {
case 0: /* forwarded, but need redirect */
/* type, code set above */
break;
case ENETUNREACH: /* shouldn't happen, checked above */
case EHOSTUNREACH:
case ENETDOWN:
case EHOSTDOWN:
default:
type = ICMP_UNREACH;
code = ICMP_UNREACH_HOST;
break;
case EMSGSIZE:
type = ICMP_UNREACH;
code = ICMP_UNREACH_NEEDFRAG;
if (ipforward_rt.ro_rt)
destifp = ipforward_rt.ro_rt->rt_ifp;
ipstat.ips_cantfrag++;
break;
case ENOBUFS:
type = ICMP_SOURCEQUENCH;
code = 0;
break;
}
if (_icmpErrorHook != NULL)
(*_icmpErrorHook)(mcopy, type, code, dest, destifp);
}
#ifdef SYSCTL_SUPPORT
int
ip_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
int *name;
u_int namelen;
void *oldp;
size_t *oldlenp;
void *newp;
size_t newlen;
{
/* All sysctl names at this level are terminal. */
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case IPCTL_FORWARDING:
return (sysctl_int(oldp, oldlenp, newp, newlen, &ipforwarding));
case IPCTL_SENDREDIRECTS:
return (sysctl_int(oldp, oldlenp, newp, newlen,
&ipsendredirects));
case IPCTL_DEFTTL:
return (sysctl_int(oldp, oldlenp, newp, newlen, &ip_defttl));
#ifdef notyet
case IPCTL_DEFMTU:
return (sysctl_int(oldp, oldlenp, newp, newlen, &ip_mtu));
#endif
default:
return (EOPNOTSUPP);
}
/* NOTREACHED */
}
#endif /* SYSCTL_SUPPORT */