xref: /csrg-svn/sys/deprecated/bbnnet/icmp.c (revision 25205)
125202Skarels #ifdef	RCSIDENT
225202Skarels static char rcsident[] = "$Header: icmp.c,v 1.17 85/06/18 14:53:43 walsh Exp $";
325202Skarels #endif
425202Skarels 
525202Skarels #include "../h/param.h"
625202Skarels #include "../h/systm.h"
725202Skarels #include "../h/mbuf.h"
825202Skarels #include "../h/socket.h"
925202Skarels #include "../h/socketvar.h"
1025202Skarels #include "../h/protosw.h"
1125202Skarels #include "../h/syslog.h"
1225202Skarels 
1325202Skarels #include "../net/route.h"
1425202Skarels #include "../net/if.h"
1525202Skarels 
1625202Skarels #include "../bbnnet/in.h"
1725202Skarels #include "../bbnnet/net.h"
1825202Skarels #include "../bbnnet/in_pcb.h"
1925202Skarels #include "../bbnnet/in_var.h"
2025202Skarels 
2125202Skarels #include "../bbnnet/ip.h"
2225202Skarels #include "../bbnnet/icmp.h"
2325202Skarels #include "../bbnnet/nopcb.h"
2425202Skarels #ifdef HMPTRAPS
2525202Skarels #include "../bbnnet/hmp_traps.h"
2625202Skarels #endif
2725202Skarels 
2825202Skarels #include "../h/errno.h"
2925202Skarels #include "../h/time.h"
3025202Skarels #include "../h/kernel.h"
3125202Skarels 
3225202Skarels #ifdef	RCSIDENT
3325202Skarels static char rcsicmphdr[] = RCSICMPHDR;
3425202Skarels #endif
3525202Skarels 
3625202Skarels extern int nosum;
3725202Skarels 
3825202Skarels #define NICTYPE	17
3925202Skarels 
4025202Skarels /* ICMP message formats */
4125202Skarels #define ICBAD	0	/* unimplemented */
4225202Skarels #define ICERR	1	/* error format (use header) */
4325202Skarels #define ICDAT	2	/* data format (use id) */
4425202Skarels #define ICINT	3	/* data format (handle internally) */
4525202Skarels 
4625202Skarels char icaction[NICTYPE] =
4725202Skarels {
4825202Skarels     ICDAT, ICBAD, ICBAD, ICERR, ICERR, ICERR, ICBAD,
4925202Skarels 	ICBAD, ICINT, ICBAD, ICBAD, ICERR, ICERR, ICINT,
5025202Skarels 	ICDAT, ICINT, ICDAT
5125202Skarels } ;
5225202Skarels 
5325202Skarels #define ICLEN1	(sizeof(struct ip) + ICMPSIZE + sizeof(struct ip) + ICMP_ERRLEN)
5425202Skarels #define ICLEN2	(sizeof(struct ip) + ICMPSIZE + 3 * sizeof(long))
5525202Skarels 
5625202Skarels int icpullup[NICTYPE] =
5725202Skarels {
5825202Skarels     0,	/* echo reply */
5925202Skarels 	0,
6025202Skarels 	0,
6125202Skarels 	ICLEN1,	/* unreachable */
6225202Skarels 	ICLEN1,	/* source quench */
6325202Skarels 	ICLEN1,	/* redirect */
6425202Skarels 	0,
6525202Skarels 	0,
6625202Skarels 	0,	/* echo request */
6725202Skarels 	0,
6825202Skarels 	0,
6925202Skarels 	ICLEN1,	/* time exceeded */
7025202Skarels 	ICLEN1,	/* parameter problem */
7125202Skarels 	ICLEN2,	/* timestamp */
7225202Skarels 	ICLEN2,	/* timestamp reply */
7325202Skarels 	0,	/* information request */
7425202Skarels 	0	/* information reply */
7525202Skarels } ;
7625202Skarels 
7725202Skarels char icunrch[ICMP_UNRCH_NUM] =
7825202Skarels {
7925202Skarels     PRC_UNREACH_NET, PRC_UNREACH_HOST, PRC_UNREACH_PROTOCOL,
8025202Skarels 	PRC_UNREACH_PORT, PRC_MSGSIZE, PRC_UNREACH_HOST
8125202Skarels } ;
8225202Skarels 
8325202Skarels struct icmp_stat icmpstat;
8425202Skarels 
8525202Skarels 
iptime()8625202Skarels u_long iptime()
8725202Skarels {
8825202Skarels     int s = spl7();	/* berkeley had spl6() */
8925202Skarels     u_long t;
9025202Skarels 
9125202Skarels     t = (time.tv_sec % (24*60*60)) * 1000 + time.tv_usec / 1000;
9225202Skarels     splx(s);
9325202Skarels     return (htonl(t));
9425202Skarels }
9525202Skarels 
know_gateway2(gaddr,list)9625202Skarels know_gateway2 (gaddr, list)
9725202Skarels u_long	gaddr;
9825202Skarels struct mbuf *list;
9925202Skarels {
10025202Skarels     register struct rtentry *rt;
10125202Skarels 
10225202Skarels     while (list)
10325202Skarels     {
10425202Skarels 	rt = mtod(list, struct rtentry *);
10525202Skarels 	if ((rt->rt_flags & RTF_GATEWAY) &&
10625202Skarels 	    (rt->rt_dst.sa_family == AF_INET) &&
10725202Skarels 	    (((struct sockaddr_in *) &rt->rt_gateway)->sin_addr.s_addr == gaddr))
10825202Skarels 		return (TRUE);
10925202Skarels 	list = list->m_next;
11025202Skarels     }
11125202Skarels     return (FALSE);
11225202Skarels }
11325202Skarels 
know_gateway(gaddr)11425202Skarels know_gateway (gaddr)
11525202Skarels u_long	gaddr;
11625202Skarels {
11725202Skarels     register int	i;
11825202Skarels 
11925202Skarels     for (i=0 ; i<RTHASHSIZ ; i++)
12025202Skarels     {
12125202Skarels 	if (know_gateway2 (gaddr, rthost[i]) ||
12225202Skarels 	    know_gateway2 (gaddr, rtnet[i]))
12325202Skarels 	    return (TRUE);
12425202Skarels     }
12525202Skarels     return (FALSE);
12625202Skarels }
12725202Skarels 
12825202Skarels #ifdef BBNPING
12925202Skarels /*
13025202Skarels  * Note that pinging is done on a per-route basis.
13125202Skarels  *
13225202Skarels  * 1.  If a gateway is used by more than one route, then for routes
13325202Skarels  *	with no active (measured by new data xfer) tcp connections,
13425202Skarels  *	the gateway will be pinged.
13525202Skarels  *	It is possible that every PINGTIME/2 seconds a gateway would
13625202Skarels  *	be sent multiple icmp ECHO REQUESTS, but that is unlikely (uncommon)
13725202Skarels  *	and we can worry about that if it actually proves to be a problem.
13825202Skarels  *
13925202Skarels  * 2.  Since the ping count is incremented on a per-route basis, but
14025202Skarels  *	ECHO REPLIES are dealt with on a per-address basis, a gateway is
14125202Skarels  *	not prematurely pinged out if it is used by more than one active
14225202Skarels  *	routing entry.
14325202Skarels  */
14425202Skarels 
check_ping(list)14525202Skarels static check_ping(list)
14625202Skarels register struct mbuf *list;
14725202Skarels {
14825202Skarels     register struct rtentry *rt;
14925202Skarels     register struct sockaddr_in *sin;
15025202Skarels     register struct mbuf *next;
15125202Skarels 
15225202Skarels     while (list)
15325202Skarels     {
15425202Skarels 	rt = mtod(list, struct rtentry *);
15525202Skarels 	next = list->m_next;	/* in case remove it from list */
15625202Skarels 
15725202Skarels 	if ((rt->rt_flags & RTF_GATEWAY) &&
15825202Skarels 	    (rt->rt_dst.sa_family == AF_INET))
15925202Skarels 	{
16025202Skarels 	    sin = (struct sockaddr_in *) &rt->rt_gateway;
16125202Skarels 	    if ((rt->rt_refcnt > 0) && (rt->rt_flags & RTF_UP))
16225202Skarels 	    {
16325202Skarels 		if (rt->irt_pings >= MAXPING)
16425202Skarels 		{
16525202Skarels 		    /*
16625202Skarels 		     * Too many unanswered pings.  re-route
16725202Skarels 		     * connections using this gateway.  Usually,
16825202Skarels 		     * this happens because the gateway is flooded
16925202Skarels 		     * with traffic.
17025202Skarels 		     */
17125202Skarels 		    union { u_long ul; u_char c[4]; } a;
17225202Skarels 
17325202Skarels 		    a.ul = sin->sin_addr.s_addr;
174*25205Skarels 		    log(LOG_INFO, "gw %d.%d.%d.%d pinged out\n",
17525202Skarels 			a.c[0], a.c[1], a.c[2], a.c[3]);
17625202Skarels 
17725202Skarels 		    rt->irt_pings = 0;
17825202Skarels 		    ip_gdown(sin->sin_addr.s_addr);
17925202Skarels 		}
18025202Skarels 		else
18125202Skarels 		{
18225202Skarels 		    /*
18325202Skarels 		     * Ping him again.
18425202Skarels 		     * See rcv_ack() for comparison with zero here.
18525202Skarels 		     */
18625202Skarels 		    rt->irt_pings ++;
18725202Skarels 		    if (rt->irt_pings > 0)
18825202Skarels 		    {
18925202Skarels 			/*
19025202Skarels 			 * count ping even if doesn't get to
19125202Skarels 			 * interface (ENOBUFS) or other error
19225202Skarels 			 * (EHOSTDOWN if no gateway at that
19325202Skarels 			 * address on an IMP network).
19425202Skarels 			 */
19525202Skarels 
19625202Skarels 			ping (sin->sin_addr);
19725202Skarels 			icmpstat.ic_pings ++;
19825202Skarels 		    }
19925202Skarels 		    else
20025202Skarels 			icmpstat.ic_svpings ++;
20125202Skarels 		}
20225202Skarels 	    }
20325202Skarels 	    else
20425202Skarels 	    {
20525202Skarels 		if (rt->rt_flags & RTF_REINSTATE)
20625202Skarels 		{
20725202Skarels 		    /*
20825202Skarels 		     * The gateway pinged out or died at some point.
20925202Skarels 		     * Let's see if it's back up or if our
21025202Skarels 		     * re-routing of current connections in ip_gdown
21125202Skarels 		     * has let it breathe again.  Wait a while
21225202Skarels 		     * before try to use it again.
21325202Skarels 		     */
21425202Skarels 		    rt->irt_gdown --;
21525202Skarels 		    if (rt->irt_gdown <= 0)
21625202Skarels 		    {
21725202Skarels 			rt->irt_gdown = 0;
21825202Skarels 			/*
21925202Skarels 			 * Wait until we know it's alive
22025202Skarels 			 * for certain.  Ping it.
22125202Skarels 			 */
22225202Skarels 			ping (sin->sin_addr);
22325202Skarels 		    }
22425202Skarels 		}
22525202Skarels 	    }
22625202Skarels 	}
22725202Skarels 
22825202Skarels 	list = next;
22925202Skarels     }
23025202Skarels }
23125202Skarels 
reset_ping(list,addr)23225202Skarels static reset_ping(list, addr)
23325202Skarels register struct mbuf *list;
23425202Skarels register u_long	addr;
23525202Skarels {
23625202Skarels     register struct rtentry *rt;
23725202Skarels 
23825202Skarels     while (list)
23925202Skarels     {
24025202Skarels 	rt = mtod(list, struct rtentry *);
24125202Skarels 	if ((rt->rt_flags & RTF_GATEWAY) &&
24225202Skarels 	    (rt->rt_dst.sa_family == AF_INET))
24325202Skarels 	{
24425202Skarels 	    if (((struct sockaddr_in *) &rt->rt_gateway)->sin_addr.s_addr == addr)
24525202Skarels 	    {
24625202Skarels 		if (rt->rt_flags & RTF_REINSTATE)
24725202Skarels 		{
24825202Skarels 		    if (rt->irt_gdown == 0)
24925202Skarels 		    {
25025202Skarels 			/*
25125202Skarels 			 * Was not a slow echo reply.  If was dead,
25225202Skarels 			 * use it again.  If was flooded, new connections
25325202Skarels 			 * can now use it (old shifted away).
25425202Skarels 			 */
25525202Skarels 			rt->rt_flags |= RTF_UP;
25625202Skarels 			rt->rt_flags &= ~RTF_REINSTATE;
25725202Skarels 			rt->rt_refcnt --; /* see ip_gdown() */
25825202Skarels 		    }
25925202Skarels 		}
26025202Skarels 		else
26125202Skarels 		    rt->irt_pings = 0;
26225202Skarels 	    }
26325202Skarels 	}
26425202Skarels 	list = list->m_next;
26525202Skarels     }
26625202Skarels }
26725202Skarels 
26825202Skarels /*
26925202Skarels  * Would be nice if we could use HOSTHASH/NETHASH/0, but the hashing is done
27025202Skarels  * on the destination, not the intermediary gateway.
27125202Skarels  */
got_ping(addr)27225202Skarels got_ping(addr)
27325202Skarels register u_long	addr;
27425202Skarels {
27525202Skarels     register int	i;
27625202Skarels 
27725202Skarels     for (i=0 ; i<RTHASHSIZ ; i++)
27825202Skarels     {
27925202Skarels 	reset_ping(rthost[i], addr);
28025202Skarels 	reset_ping(rtnet[i], addr);
28125202Skarels     }
28225202Skarels }
28325202Skarels #endif
28425202Skarels 
28525202Skarels /*
28625202Skarels  * Process ICMP messages.  Called directly from ip_input processor.
28725202Skarels  */
icmp(mp)28825202Skarels icmp(mp)
28925202Skarels register struct mbuf *mp;
29025202Skarels {
29125202Skarels     register struct ip *ip;
29225202Skarels     register struct icmp *icp;
29325202Skarels     struct in_ifaddr *ia;
29425202Skarels     int ilen;
29525202Skarels     int prccode;
29625202Skarels 
29725202Skarels     icmpstat.ic_total ++;
29825202Skarels 
29925202Skarels     /*
30025202Skarels      * see ip_input()
30125202Skarels      */
30225202Skarels     if ((mp->m_off > MMAXOFF) ||
30325202Skarels 	(mp->m_len < sizeof(struct ip) + ICMPSIZE))
30425202Skarels     {
30525202Skarels 	if ((mp = m_pullup(mp, sizeof(struct ip) + ICMPSIZE)) == NULL)
30625202Skarels 	{
30725202Skarels 	    icmpstat.ic_tooshort ++;
30825202Skarels 	    return;
30925202Skarels 	}
31025202Skarels     }
31125202Skarels     ip = mtod(mp, struct ip *);
31225202Skarels     icp = (struct icmp *) (ip+1);
31325202Skarels 
31425202Skarels     /*
31525202Skarels      * watch for fools sending out broadcast ICMP packets
31625202Skarels      * Don't check against inetifp, since is up to ip_input whether to receive
31725202Skarels      * on some interface rather than send to self for input on dst interface.
31825202Skarels      */
31925202Skarels     ia = in_iawithaddr(ip->ip_dst, FALSE);
32025202Skarels     if (ia == NULL)
32125202Skarels     {
32225202Skarels 	/* drop it */
32325202Skarels 	m_freem(mp);
32425202Skarels 	return;
32525202Skarels     }
32625202Skarels 
32725202Skarels     /* filter out message types */
32825202Skarels 
32925202Skarels     if (icp->ic_type >= NICTYPE || icaction[icp->ic_type] == ICBAD)
33025202Skarels     {
33125202Skarels 	icmpstat.ic_drops++;
33225202Skarels 	goto badret;
33325202Skarels     }
33425202Skarels 
33525202Skarels     if (mp->m_len < icpullup[icp->ic_type])
33625202Skarels     {
33725202Skarels 	if ((mp = m_pullup(mp, icpullup[icp->ic_type])) == NULL)
33825202Skarels 	{
33925202Skarels 	    icmpstat.ic_tooshort ++;
34025202Skarels 	    return;
34125202Skarels 	}
34225202Skarels 	ip = mtod(mp, struct ip *);
34325202Skarels 	icp = (struct icmp *) (ip+1);
34425202Skarels     }
34525202Skarels     mp->m_off += sizeof(struct ip);
34625202Skarels     mp->m_len -= sizeof(struct ip);
34725202Skarels 
34825202Skarels     ilen = ip->ip_len;
34925202Skarels 
35025202Skarels     {
35125202Skarels     register u_short his_sum, our_sum;
35225202Skarels 
35325202Skarels     his_sum = (u_short)icp->ic_sum;
35425202Skarels     icp->ic_sum = 0;
35525202Skarels     if (his_sum != (our_sum = (u_short)in_cksum(mp, ilen)))
35625202Skarels     {
35725202Skarels 	icmpstat.ic_badsum++;
35825202Skarels 	if (! nosum)
35925202Skarels 	{
36025202Skarels 	    /* note that the icmp header doesn't overlap IP */
36125202Skarels #ifdef HMPTRAPS
36225202Skarels 	    /* hmp_trap(T_ICMP_CKSUM, (caddr_t),0); */
36325202Skarels #endif
36425202Skarels 	    inet_cksum_err ("icmp", ip, (u_long) his_sum, (u_long) our_sum);
36525202Skarels 	    netlog(mp);
36625202Skarels 	    return;
36725202Skarels 	}
36825202Skarels     }
36925202Skarels     }
37025202Skarels 
37125202Skarels     /*
37225202Skarels      * Now do any processing.  Some messages are handled here,
37325202Skarels      * others are passed up ctlinput path for further processing.
37425202Skarels      */
37525202Skarels 
37625202Skarels     switch (icp->ic_type)
37725202Skarels     {
37825202Skarels 
37925202Skarels       case ICMP_UNRCH:	/* destination unreachable */
38025202Skarels 
38125202Skarels 	if (icp->ic_code < ICMP_UNRCH_NUM)
38225202Skarels 	{
38325202Skarels 	    register int (*ctlfunc)();
38425202Skarels 
38525202Skarels 	    prccode = icunrch[icp->ic_code];
38625202Skarels passup:
38725202Skarels 	    ctlfunc = ipsw[icp->ic_iphdr.ip_p].ipsw_user->pr_ctlinput;
38825202Skarels 	    (*ctlfunc) (prccode, (caddr_t) icp);
38925202Skarels 	}
39025202Skarels 	break;
39125202Skarels 
39225202Skarels       case ICMP_SRCQ:	/* source quench */
39325202Skarels 
39425202Skarels 	/*
39525202Skarels 	 * At the IP level, we could try to reroute the connection and see if we
39625202Skarels 	 * come up with a less loaded gateway.  Problem with this is that we know
39725202Skarels 	 * total number of packets sent over a route, not the recent traffic load.
39825202Skarels 	 */
39925202Skarels 	icmpstat.ic_quenches++;
40025202Skarels 	prccode = PRC_QUENCH;
40125202Skarels #ifdef HMPTRAPS
40225202Skarels 	/* hmp_trap(T_ICMP_SRCQ, (caddr_t)0, 0); */
40325202Skarels #endif
40425202Skarels 	goto passup;
40525202Skarels 
40625202Skarels       case ICMP_REDIR:	/* redirect */
40725202Skarels 
40825202Skarels 	icmpstat.ic_redirects ++;
40925202Skarels 
41025202Skarels 	/*
41125202Skarels 	 * Sorry, we only trust the connected set of gateways
41225202Skarels 	 * that includes gateways installed by the system
41325202Skarels 	 * manager.
41425202Skarels 	 */
41525202Skarels 	if (know_gateway(ip->ip_src.s_addr))
41625202Skarels 	{
41725202Skarels 	    register struct mbuf **table;
41825202Skarels 
41925202Skarels 	    if (icp->ic_code == ICMP_REDIR_NET)
42025202Skarels 	    {
42125202Skarels 		prccode = PRC_REDIRECT_NET;
42225202Skarels 		table = rtnet;
42325202Skarels 	    }
42425202Skarels 	    else
42525202Skarels 	    {
42625202Skarels 		prccode = PRC_REDIRECT_HOST;
42725202Skarels 		table = rthost;
42825202Skarels 	    }
42925202Skarels 	    if (icmp_redirect_route (icp, table))
43025202Skarels 		goto passup;
43125202Skarels 	}
43225202Skarels 	else
43325202Skarels 	{
43425202Skarels 	    /*
43525202Skarels 	     * Who are you?  Why are you talking to us?
43625202Skarels 	     * And how do we know the ip source isn't a lie?
43725202Skarels 	     * (Eg., Catches Symbolics redirection of subnet broadcast.)
43825202Skarels 	     */
43925202Skarels 	    union { u_long ul; u_char c[4]; } a;
44025202Skarels 
44125202Skarels 	    a.ul = ip->ip_src.s_addr;
442*25205Skarels 	    log(LOG_INFO, "Ignoring redirect from %d.%d.%d.%d\n",
44325202Skarels 		a.c[0], a.c[1], a.c[2], a.c[3]);
44425202Skarels 	}
44525202Skarels #ifdef HMPTRAPS
44625202Skarels 	/* hmp_trap(T_ICMP_REDIR, (caddr_t)0,0); */
44725202Skarels #endif
44825202Skarels 	break;
44925202Skarels 
45025202Skarels       case ICMP_ECHO:	/* echo */
45125202Skarels 
45225202Skarels 	icp->ic_type = ICMP_ECHOR;
45325202Skarels 	icmpstat.ic_echoes++;
45425202Skarels 	goto loopback;
45525202Skarels 
45625202Skarels       case ICMP_ECHOR:	/* echo reply */
45725202Skarels 
45825202Skarels 	/* check for gateway ping packets, look for
45925202Skarels 	 * corresponding gateway entry and set echo count
46025202Skarels 	 * to zero.
46125202Skarels 	 */
46225202Skarels #ifdef	BBNPING
46325202Skarels 	if (icp->ic_id == MY_ECHO_ID)
46425202Skarels 	    got_ping(ip->ip_src.s_addr);
46525202Skarels #endif
46625202Skarels 	break;
46725202Skarels 
46825202Skarels       case ICMP_TIMEX:	/* time exceeded */
46925202Skarels 	/*
47025202Skarels 	 * IP time to live field should be associated with the route so
47125202Skarels 	 * that it can be dynamically adjusted for time exceeded in transit.
47225202Skarels 	 * If did, would only need to "pass time exceeded in reassembly"
47325202Skarels 	 * up to protocol (TCP) so that it can better try to avoid IP
47425202Skarels 	 * fragmentation.
47525202Skarels 	 */
47625202Skarels 	icmpstat.ic_timex++;
47725202Skarels 	prccode = (icp->ic_code == ICMP_TIMEX_XMT)
47825202Skarels 	    ? PRC_TIMXCEED_INTRANS
47925202Skarels 	    : PRC_TIMXCEED_REASS;
48025202Skarels #ifdef HMPTRAPS
48125202Skarels 	/* hmp_trap(T_ICMP_TIMEX, (caddr_t)0,0); */
48225202Skarels #endif
48325202Skarels 	goto passup;
48425202Skarels 
48525202Skarels       case ICMP_TIMES:	/* timestamp */
48625202Skarels 
48725202Skarels 	if (icp->ic_code == 0)
48825202Skarels 	{
48925202Skarels 	    icp->ic_type = ICMP_TIMESR;
49025202Skarels 	    /*
49125202Skarels 	     * Can now do timestamps in UT
49225202Skarels 	     *
49325202Skarels 	       icp->ic_trcv = (long)time.tv_sec | 0x80;
49425202Skarels 	       icp->ic_txmt = (long)time.tv_sec | 0x80;
49525202Skarels 	     */
49625202Skarels 	    icp->ic_txmt = icp->ic_trcv = iptime();
49725202Skarels 	    goto loopback;
49825202Skarels 	}
49925202Skarels 	break;
50025202Skarels 
50125202Skarels       case ICMP_INFO:	/* info request */
50225202Skarels 	/*
50325202Skarels 	 * He knows his host number, but not his network #,
50425202Skarels 	 * fill in src & dst as he would have, had he known.
50525202Skarels 	 */
50625202Skarels 	{
50725202Skarels 	register struct in_ifaddr *inaddress;
50825202Skarels 	extern struct ifnet *inetifp;
50925202Skarels 
51025202Skarels 	icp->ic_type = ICMP_INFOR;
51125202Skarels 	inaddress = in_iafromif(inetifp);
51225202Skarels 	ip->ip_src.s_addr |= inaddress->ia_subnet;
51325202Skarels 	ip->ip_dst = redir_addr(ip);
51425202Skarels 	}
51525202Skarels 	goto loopback;
51625202Skarels 
51725202Skarels       case ICMP_PARM:	/* parameter problem */
51825202Skarels 	icmpstat.ic_parm++;
51925202Skarels 	prccode = PRC_PARAMPROB;
52025202Skarels #ifdef HMPTRAPS
52125202Skarels 	/* hmp_trap(T_ICMP_PARM, (caddr_t)0,0); */
52225202Skarels #endif
52325202Skarels 	goto passup;
52425202Skarels     }
52525202Skarels 
52625202Skarels badret :
52725202Skarels     m_freem(mp);
52825202Skarels     return;
52925202Skarels 
53025202Skarels loopback :
53125202Skarels     {
53225202Skarels 	struct in_addr temp;
53325202Skarels 	register int error;
53425202Skarels 
53525202Skarels 	temp = ip->ip_src;
53625202Skarels 	ip->ip_src = ip->ip_dst;
53725202Skarels 	ip->ip_dst = temp;
53825202Skarels 	/* ip->ip_p = IPPROTO_ICMP; still is from input */
53925202Skarels 	/* ip->ip_tos = 0; use same tos for reply */
54025202Skarels 
54125202Skarels 	icp->ic_sum = in_cksum(mp, ilen);
54225202Skarels 	mp->m_off -= sizeof(struct ip);
54325202Skarels 	mp->m_len += sizeof(struct ip);
54425202Skarels 	NOPCB_IPSEND (mp, (int)ip->ip_len, FALSE, error);
54525202Skarels 
54625202Skarels #ifdef lint
54725202Skarels 	error = error;
54825202Skarels #endif
54925202Skarels 
55025202Skarels     }
55125202Skarels }
55225202Skarels 
55325202Skarels 
55425202Skarels /*
55525202Skarels  * Ping gateways in use to see if they are still alive.
55625202Skarels  */
ic_timeo()55725202Skarels ic_timeo()
55825202Skarels {
55925202Skarels #ifdef BBNPING
56025202Skarels     register int	i;
56125202Skarels     register int	level;
56225202Skarels     static int ictimer;
56325202Skarels 
56425202Skarels     if (--ictimer > 0)
56525202Skarels 	return;
56625202Skarels     ictimer = PINGTIME;
56725202Skarels 
56825202Skarels     level = splnet();
56925202Skarels     for (i=0 ; i<RTHASHSIZ ; i++)
57025202Skarels     {
57125202Skarels 	check_ping(rthost[i]);
57225202Skarels 	check_ping(rtnet[i]);
57325202Skarels     }
57425202Skarels     splx(level);
57525202Skarels #endif
57625202Skarels }
57725202Skarels 
rtfind(dst,via,table)57825202Skarels static struct rtentry *rtfind (dst, via, table)
57925202Skarels struct in_addr	 dst;
58025202Skarels struct in_addr	 via;
58125202Skarels struct mbuf	*table[];
58225202Skarels {
58325202Skarels     register struct mbuf	*m;
58425202Skarels 
58525202Skarels     struct rtentry	*rt;
58625202Skarels 
58725202Skarels     if (table == rthost)
58825202Skarels 	m = rthost[HOSTHASH(dst.s_addr) % RTHASHSIZ];
58925202Skarels     else
59025202Skarels     {
59125202Skarels 	if (dst.s_addr)
59225202Skarels 	{
59325202Skarels 	    m = rtnet[NETHASH(dst) % RTHASHSIZ];
59425202Skarels 	    dst.s_addr = iptonet(dst);
59525202Skarels 	}
59625202Skarels 	else
59725202Skarels 	    m = rtnet[0];
59825202Skarels     }
59925202Skarels 
60025202Skarels     while (m)
60125202Skarels     {
60225202Skarels 	struct in_addr d, g;
60325202Skarels 
60425202Skarels 	rt = mtod(m, struct rtentry *);
60525202Skarels 	d = satoipa(&rt->rt_dst);
60625202Skarels 	g = satoipa(&rt->rt_gateway);
60725202Skarels 	if ((d.s_addr == dst.s_addr) &&
60825202Skarels 	    (g.s_addr == via.s_addr) &&
60925202Skarels 	    (rt->rt_dst.sa_family == AF_INET))
61025202Skarels 	{
61125202Skarels 	    /* then, hash values must be same. */
61225202Skarels 	    return (rt);
61325202Skarels 	}
61425202Skarels 
61525202Skarels 	m = m->m_next;
61625202Skarels     }
61725202Skarels 
61825202Skarels     return (NULL);
61925202Skarels }
62025202Skarels 
62125202Skarels 
62225202Skarels icmp_redirect_route (ic, table)
62325202Skarels struct icmp	*ic;
62425202Skarels struct mbuf	*table[];
62525202Skarels {
62625202Skarels     struct ip	*ip;
62725202Skarels     int flags;
62825202Skarels     static struct sockaddr_in red_dst = { AF_INET } ;
62925202Skarels     static struct sockaddr_in red_gtw = { AF_INET } ;
63025202Skarels 
63125202Skarels     ip = (struct ip *) ic->ic_data;
63225202Skarels     /*
63325202Skarels      * 1.  Make new routing entry so that new connections will use better
63425202Skarels      * route.  But only make entry if have not already done so.
63525202Skarels      */
63625202Skarels     if (!rtfind(ip->ip_dst, ic->ic_gaddr, table))
63725202Skarels     {
63825202Skarels 	char	*err;
63925202Skarels 
64025202Skarels 	/* check reasonableness of redirect */
64125202Skarels 
64225202Skarels 	if (in_iawithnet(ic->ic_gaddr) == NULL)
64325202Skarels 	{
64425202Skarels 	    /*
64525202Skarels 	     * Sorry, can't get there from here.
64625202Skarels 	     */
64725202Skarels 	    union { u_long ul; u_char c[4]; } g, f, t, v;
64825202Skarels 
64925202Skarels 	    err = "No interface for first hop";
65025202Skarels perr :
65125202Skarels 
65225202Skarels 	    g.ul = (((struct ip *) (((char *) ic) - sizeof(struct ip)))->ip_src.s_addr);
65325202Skarels 	    f.ul = ip->ip_src.s_addr;
65425202Skarels 	    t.ul = ip->ip_dst.s_addr;
65525202Skarels 	    v.ul = ic->ic_gaddr.s_addr;
656*25205Skarels 	    log(LOG_INFO,
65725202Skarels 		"Ignoring ICMP redirect from gw %d.%d.%d.%d? to go from %d.%d.%d.%d to %d.%d.%d.%d via %d.%d.%d.%d : %s\n",
65825202Skarels 		g.c[0], g.c[1], g.c[2], g.c[3],
65925202Skarels 		f.c[0], f.c[1], f.c[2], f.c[3],
66025202Skarels 		t.c[0], t.c[1], t.c[2], t.c[3],
66125202Skarels 		v.c[0], v.c[1], v.c[2], v.c[3],
66225202Skarels 		err);
66325202Skarels 
66425202Skarels 	    return (FALSE);
66525202Skarels 	}
66625202Skarels 
66725202Skarels 	if (in_iawithaddr(ic->ic_gaddr, TRUE))
66825202Skarels 	{
66925202Skarels 	    /*
67025202Skarels 	     * redirect to self is stupid, as is redirect to
67125202Skarels 	     * broadcast address (which if_iawithaddr will match
67225202Skarels 	     * for interfaces with IFF_BROADCAST set.)
67325202Skarels 	     */
67425202Skarels 	    err = "redirected to self";
67525202Skarels 	    goto perr;
67625202Skarels 	}
67725202Skarels 
67825202Skarels 	if (iptonet(ic->ic_gaddr) != iptonet(ip->ip_src))
67925202Skarels 	{
68025202Skarels 	    /*
68125202Skarels 	     * Why is this gateway redirecting us?  It is not
68225202Skarels 	     * giving us a first hop gateway that is on the
68325202Skarels 	     * local net that we advertise.
68425202Skarels 	     */
68525202Skarels 	    err = "new first hop net <> src net";
68625202Skarels 	    goto perr;
68725202Skarels 	}
68825202Skarels 
68925202Skarels #ifdef done_in_icmp_c
69025202Skarels 	if (! know_gateway(icmp source))
69125202Skarels 	    /*
69225202Skarels 	     * Sorry, we only trust the connected set of gateways
69325202Skarels 	     * that includes gateways installed by the system
69425202Skarels 	     * manager.  Who are you?  Why are you talking to us?
69525202Skarels 	     */
69625202Skarels 	    return;
69725202Skarels #endif
69825202Skarels 
69925202Skarels 	/* o.k., I'll believe it */
70025202Skarels 	flags = RTF_UP;
70125202Skarels 	if (table == rthost)
70225202Skarels 	{
70325202Skarels 	    flags |= RTF_HOST;
70425202Skarels 	    red_dst.sin_addr.s_addr = ip->ip_dst.s_addr;
70525202Skarels 	}
70625202Skarels 	else
70725202Skarels 	{
70825202Skarels 	    flags |= RTF_GATEWAY;
70925202Skarels 	    red_dst.sin_addr.s_addr = iptonet(ip->ip_dst);
71025202Skarels 	}
71125202Skarels 	red_gtw.sin_addr.s_addr = ic->ic_gaddr.s_addr;
71225202Skarels 	rtinit ((struct sockaddr *) &red_dst,
71325202Skarels 	    (struct sockaddr *) &red_gtw,
71425202Skarels 	    flags);
71525202Skarels     }
71625202Skarels     return (TRUE);
71725202Skarels }
71825202Skarels 
71925202Skarels icmp_redirect_inp(inp, ic, table)
72025202Skarels struct inpcb	*inp;
72125202Skarels struct icmp	*ic;
72225202Skarels struct mbuf	**table;
72325202Skarels {
72425202Skarels     struct rtentry *rt;
72525202Skarels 
72625202Skarels     /*
72725202Skarels      * 2.  Redirect current connection.
72825202Skarels      */
72925202Skarels 
73025202Skarels #ifdef neverdef
73125202Skarels 	/*
73225202Skarels 	 * This would try to balance load across gateways, but
73325202Skarels 	 * that's something best done by the gateway before it
73425202Skarels 	 * sends a redirect.  Also, consider 3 gateways of which
73525202Skarels 	 * two are bad, and possibility of bouncing between the
73625202Skarels 	 * two bad ones until their use counts got high enough.
73725202Skarels 	 *
73825202Skarels 	 * Currently, gateways only take into account # hops, not
73925202Skarels 	 * load.
74025202Skarels 	 */
74125202Skarels 	if (rt = inp->inp_route.ro_rt)
74225202Skarels 	{
74325202Skarels 	    short	oflags;
74425202Skarels 
74525202Skarels 	    /* try to force a different path */
74625202Skarels 	    oflags = rt->rt_flags;
74725202Skarels 	    rt->rt_flags &= ~RTF_UP;
74825202Skarels 	    /* but don't lose current route */
74925202Skarels 	    rt->rt_refcnt ++;
75025202Skarels 	    (void) ip_reroute (inp);
75125202Skarels 	    rt->rt_refcnt --;
75225202Skarels 	    rt->rt_flags = oflags;
75325202Skarels 	}
75425202Skarels #endif
75525202Skarels 	if (rt = rtfind (ic->ic_iphdr.ip_dst, ic->ic_gaddr, table))
75625202Skarels 	{
75725202Skarels 	    if (rt->rt_flags & RTF_UP)
75825202Skarels 	    {
75925202Skarels 		/*
76025202Skarels 		 * packets go out an interface with our local
76125202Skarels 		 * IP address.  Know true from checks after
76225202Skarels 		 * first call to rtfind above.
76325202Skarels 		 *
76425202Skarels 		 * Interface has to be at least as up as
76525202Skarels 		 * for previous route, so don't bother to
76625202Skarels 		 * check.
76725202Skarels 		 */
76825202Skarels 		if (inp->inp_route.ro_rt)
76925202Skarels 		    rtfree (inp->inp_route.ro_rt);
77025202Skarels 		inp->inp_route.ro_rt = rt;
77125202Skarels 		rt->rt_refcnt ++;
77225202Skarels 	    }
77325202Skarels 	    else
774*25205Skarels 		log(LOG_INFO, "ICMP Redirect to down route\n");
77525202Skarels 	}
77625202Skarels 	else
777*25205Skarels 	    log(LOG_INFO, "ICMP Redirect route not installed?\n");
77825202Skarels }
779