xref: /csrg-svn/sys/deprecated/bbnnet/icmp.c (revision 25202)
1*25202Skarels #ifdef	RCSIDENT
2*25202Skarels static char rcsident[] = "$Header: icmp.c,v 1.17 85/06/18 14:53:43 walsh Exp $";
3*25202Skarels #endif
4*25202Skarels 
5*25202Skarels #include "../h/param.h"
6*25202Skarels #include "../h/systm.h"
7*25202Skarels #include "../h/mbuf.h"
8*25202Skarels #include "../h/socket.h"
9*25202Skarels #include "../h/socketvar.h"
10*25202Skarels #include "../h/protosw.h"
11*25202Skarels #include "../h/syslog.h"
12*25202Skarels 
13*25202Skarels #include "../net/route.h"
14*25202Skarels #include "../net/if.h"
15*25202Skarels 
16*25202Skarels #include "../bbnnet/in.h"
17*25202Skarels #include "../bbnnet/net.h"
18*25202Skarels #include "../bbnnet/in_pcb.h"
19*25202Skarels #include "../bbnnet/in_var.h"
20*25202Skarels 
21*25202Skarels #include "../bbnnet/ip.h"
22*25202Skarels #include "../bbnnet/icmp.h"
23*25202Skarels #include "../bbnnet/nopcb.h"
24*25202Skarels #ifdef HMPTRAPS
25*25202Skarels #include "../bbnnet/hmp_traps.h"
26*25202Skarels #endif
27*25202Skarels 
28*25202Skarels #include "../h/errno.h"
29*25202Skarels #include "../h/time.h"
30*25202Skarels #include "../h/kernel.h"
31*25202Skarels 
32*25202Skarels #ifdef	RCSIDENT
33*25202Skarels static char rcsicmphdr[] = RCSICMPHDR;
34*25202Skarels #endif
35*25202Skarels 
36*25202Skarels extern int nosum;
37*25202Skarels 
38*25202Skarels #define NICTYPE	17
39*25202Skarels 
40*25202Skarels /* ICMP message formats */
41*25202Skarels #define ICBAD	0	/* unimplemented */
42*25202Skarels #define ICERR	1	/* error format (use header) */
43*25202Skarels #define ICDAT	2	/* data format (use id) */
44*25202Skarels #define ICINT	3	/* data format (handle internally) */
45*25202Skarels 
46*25202Skarels char icaction[NICTYPE] =
47*25202Skarels {
48*25202Skarels     ICDAT, ICBAD, ICBAD, ICERR, ICERR, ICERR, ICBAD,
49*25202Skarels 	ICBAD, ICINT, ICBAD, ICBAD, ICERR, ICERR, ICINT,
50*25202Skarels 	ICDAT, ICINT, ICDAT
51*25202Skarels } ;
52*25202Skarels 
53*25202Skarels #define ICLEN1	(sizeof(struct ip) + ICMPSIZE + sizeof(struct ip) + ICMP_ERRLEN)
54*25202Skarels #define ICLEN2	(sizeof(struct ip) + ICMPSIZE + 3 * sizeof(long))
55*25202Skarels 
56*25202Skarels int icpullup[NICTYPE] =
57*25202Skarels {
58*25202Skarels     0,	/* echo reply */
59*25202Skarels 	0,
60*25202Skarels 	0,
61*25202Skarels 	ICLEN1,	/* unreachable */
62*25202Skarels 	ICLEN1,	/* source quench */
63*25202Skarels 	ICLEN1,	/* redirect */
64*25202Skarels 	0,
65*25202Skarels 	0,
66*25202Skarels 	0,	/* echo request */
67*25202Skarels 	0,
68*25202Skarels 	0,
69*25202Skarels 	ICLEN1,	/* time exceeded */
70*25202Skarels 	ICLEN1,	/* parameter problem */
71*25202Skarels 	ICLEN2,	/* timestamp */
72*25202Skarels 	ICLEN2,	/* timestamp reply */
73*25202Skarels 	0,	/* information request */
74*25202Skarels 	0	/* information reply */
75*25202Skarels } ;
76*25202Skarels 
77*25202Skarels char icunrch[ICMP_UNRCH_NUM] =
78*25202Skarels {
79*25202Skarels     PRC_UNREACH_NET, PRC_UNREACH_HOST, PRC_UNREACH_PROTOCOL,
80*25202Skarels 	PRC_UNREACH_PORT, PRC_MSGSIZE, PRC_UNREACH_HOST
81*25202Skarels } ;
82*25202Skarels 
83*25202Skarels struct icmp_stat icmpstat;
84*25202Skarels 
85*25202Skarels 
86*25202Skarels u_long iptime()
87*25202Skarels {
88*25202Skarels     int s = spl7();	/* berkeley had spl6() */
89*25202Skarels     u_long t;
90*25202Skarels 
91*25202Skarels     t = (time.tv_sec % (24*60*60)) * 1000 + time.tv_usec / 1000;
92*25202Skarels     splx(s);
93*25202Skarels     return (htonl(t));
94*25202Skarels }
95*25202Skarels 
96*25202Skarels know_gateway2 (gaddr, list)
97*25202Skarels u_long	gaddr;
98*25202Skarels struct mbuf *list;
99*25202Skarels {
100*25202Skarels     register struct rtentry *rt;
101*25202Skarels 
102*25202Skarels     while (list)
103*25202Skarels     {
104*25202Skarels 	rt = mtod(list, struct rtentry *);
105*25202Skarels 	if ((rt->rt_flags & RTF_GATEWAY) &&
106*25202Skarels 	    (rt->rt_dst.sa_family == AF_INET) &&
107*25202Skarels 	    (((struct sockaddr_in *) &rt->rt_gateway)->sin_addr.s_addr == gaddr))
108*25202Skarels 		return (TRUE);
109*25202Skarels 	list = list->m_next;
110*25202Skarels     }
111*25202Skarels     return (FALSE);
112*25202Skarels }
113*25202Skarels 
114*25202Skarels know_gateway (gaddr)
115*25202Skarels u_long	gaddr;
116*25202Skarels {
117*25202Skarels     register int	i;
118*25202Skarels 
119*25202Skarels     for (i=0 ; i<RTHASHSIZ ; i++)
120*25202Skarels     {
121*25202Skarels 	if (know_gateway2 (gaddr, rthost[i]) ||
122*25202Skarels 	    know_gateway2 (gaddr, rtnet[i]))
123*25202Skarels 	    return (TRUE);
124*25202Skarels     }
125*25202Skarels     return (FALSE);
126*25202Skarels }
127*25202Skarels 
128*25202Skarels #ifdef BBNPING
129*25202Skarels /*
130*25202Skarels  * Note that pinging is done on a per-route basis.
131*25202Skarels  *
132*25202Skarels  * 1.  If a gateway is used by more than one route, then for routes
133*25202Skarels  *	with no active (measured by new data xfer) tcp connections,
134*25202Skarels  *	the gateway will be pinged.
135*25202Skarels  *	It is possible that every PINGTIME/2 seconds a gateway would
136*25202Skarels  *	be sent multiple icmp ECHO REQUESTS, but that is unlikely (uncommon)
137*25202Skarels  *	and we can worry about that if it actually proves to be a problem.
138*25202Skarels  *
139*25202Skarels  * 2.  Since the ping count is incremented on a per-route basis, but
140*25202Skarels  *	ECHO REPLIES are dealt with on a per-address basis, a gateway is
141*25202Skarels  *	not prematurely pinged out if it is used by more than one active
142*25202Skarels  *	routing entry.
143*25202Skarels  */
144*25202Skarels 
145*25202Skarels static check_ping(list)
146*25202Skarels register struct mbuf *list;
147*25202Skarels {
148*25202Skarels     register struct rtentry *rt;
149*25202Skarels     register struct sockaddr_in *sin;
150*25202Skarels     register struct mbuf *next;
151*25202Skarels 
152*25202Skarels     while (list)
153*25202Skarels     {
154*25202Skarels 	rt = mtod(list, struct rtentry *);
155*25202Skarels 	next = list->m_next;	/* in case remove it from list */
156*25202Skarels 
157*25202Skarels 	if ((rt->rt_flags & RTF_GATEWAY) &&
158*25202Skarels 	    (rt->rt_dst.sa_family == AF_INET))
159*25202Skarels 	{
160*25202Skarels 	    sin = (struct sockaddr_in *) &rt->rt_gateway;
161*25202Skarels 	    if ((rt->rt_refcnt > 0) && (rt->rt_flags & RTF_UP))
162*25202Skarels 	    {
163*25202Skarels 		if (rt->irt_pings >= MAXPING)
164*25202Skarels 		{
165*25202Skarels 		    /*
166*25202Skarels 		     * Too many unanswered pings.  re-route
167*25202Skarels 		     * connections using this gateway.  Usually,
168*25202Skarels 		     * this happens because the gateway is flooded
169*25202Skarels 		     * with traffic.
170*25202Skarels 		     */
171*25202Skarels 		    union { u_long ul; u_char c[4]; } a;
172*25202Skarels 
173*25202Skarels 		    a.ul = sin->sin_addr.s_addr;
174*25202Skarels 		    log(KERN_RECOV, "gw %d.%d.%d.%d pinged out\n",
175*25202Skarels 			a.c[0], a.c[1], a.c[2], a.c[3]);
176*25202Skarels 
177*25202Skarels 		    rt->irt_pings = 0;
178*25202Skarels 		    ip_gdown(sin->sin_addr.s_addr);
179*25202Skarels 		}
180*25202Skarels 		else
181*25202Skarels 		{
182*25202Skarels 		    /*
183*25202Skarels 		     * Ping him again.
184*25202Skarels 		     * See rcv_ack() for comparison with zero here.
185*25202Skarels 		     */
186*25202Skarels 		    rt->irt_pings ++;
187*25202Skarels 		    if (rt->irt_pings > 0)
188*25202Skarels 		    {
189*25202Skarels 			/*
190*25202Skarels 			 * count ping even if doesn't get to
191*25202Skarels 			 * interface (ENOBUFS) or other error
192*25202Skarels 			 * (EHOSTDOWN if no gateway at that
193*25202Skarels 			 * address on an IMP network).
194*25202Skarels 			 */
195*25202Skarels 
196*25202Skarels 			ping (sin->sin_addr);
197*25202Skarels 			icmpstat.ic_pings ++;
198*25202Skarels 		    }
199*25202Skarels 		    else
200*25202Skarels 			icmpstat.ic_svpings ++;
201*25202Skarels 		}
202*25202Skarels 	    }
203*25202Skarels 	    else
204*25202Skarels 	    {
205*25202Skarels 		if (rt->rt_flags & RTF_REINSTATE)
206*25202Skarels 		{
207*25202Skarels 		    /*
208*25202Skarels 		     * The gateway pinged out or died at some point.
209*25202Skarels 		     * Let's see if it's back up or if our
210*25202Skarels 		     * re-routing of current connections in ip_gdown
211*25202Skarels 		     * has let it breathe again.  Wait a while
212*25202Skarels 		     * before try to use it again.
213*25202Skarels 		     */
214*25202Skarels 		    rt->irt_gdown --;
215*25202Skarels 		    if (rt->irt_gdown <= 0)
216*25202Skarels 		    {
217*25202Skarels 			rt->irt_gdown = 0;
218*25202Skarels 			/*
219*25202Skarels 			 * Wait until we know it's alive
220*25202Skarels 			 * for certain.  Ping it.
221*25202Skarels 			 */
222*25202Skarels 			ping (sin->sin_addr);
223*25202Skarels 		    }
224*25202Skarels 		}
225*25202Skarels 	    }
226*25202Skarels 	}
227*25202Skarels 
228*25202Skarels 	list = next;
229*25202Skarels     }
230*25202Skarels }
231*25202Skarels 
232*25202Skarels static reset_ping(list, addr)
233*25202Skarels register struct mbuf *list;
234*25202Skarels register u_long	addr;
235*25202Skarels {
236*25202Skarels     register struct rtentry *rt;
237*25202Skarels 
238*25202Skarels     while (list)
239*25202Skarels     {
240*25202Skarels 	rt = mtod(list, struct rtentry *);
241*25202Skarels 	if ((rt->rt_flags & RTF_GATEWAY) &&
242*25202Skarels 	    (rt->rt_dst.sa_family == AF_INET))
243*25202Skarels 	{
244*25202Skarels 	    if (((struct sockaddr_in *) &rt->rt_gateway)->sin_addr.s_addr == addr)
245*25202Skarels 	    {
246*25202Skarels 		if (rt->rt_flags & RTF_REINSTATE)
247*25202Skarels 		{
248*25202Skarels 		    if (rt->irt_gdown == 0)
249*25202Skarels 		    {
250*25202Skarels 			/*
251*25202Skarels 			 * Was not a slow echo reply.  If was dead,
252*25202Skarels 			 * use it again.  If was flooded, new connections
253*25202Skarels 			 * can now use it (old shifted away).
254*25202Skarels 			 */
255*25202Skarels 			rt->rt_flags |= RTF_UP;
256*25202Skarels 			rt->rt_flags &= ~RTF_REINSTATE;
257*25202Skarels 			rt->rt_refcnt --; /* see ip_gdown() */
258*25202Skarels 		    }
259*25202Skarels 		}
260*25202Skarels 		else
261*25202Skarels 		    rt->irt_pings = 0;
262*25202Skarels 	    }
263*25202Skarels 	}
264*25202Skarels 	list = list->m_next;
265*25202Skarels     }
266*25202Skarels }
267*25202Skarels 
268*25202Skarels /*
269*25202Skarels  * Would be nice if we could use HOSTHASH/NETHASH/0, but the hashing is done
270*25202Skarels  * on the destination, not the intermediary gateway.
271*25202Skarels  */
272*25202Skarels got_ping(addr)
273*25202Skarels register u_long	addr;
274*25202Skarels {
275*25202Skarels     register int	i;
276*25202Skarels 
277*25202Skarels     for (i=0 ; i<RTHASHSIZ ; i++)
278*25202Skarels     {
279*25202Skarels 	reset_ping(rthost[i], addr);
280*25202Skarels 	reset_ping(rtnet[i], addr);
281*25202Skarels     }
282*25202Skarels }
283*25202Skarels #endif
284*25202Skarels 
285*25202Skarels /*
286*25202Skarels  * Process ICMP messages.  Called directly from ip_input processor.
287*25202Skarels  */
288*25202Skarels icmp(mp)
289*25202Skarels register struct mbuf *mp;
290*25202Skarels {
291*25202Skarels     register struct ip *ip;
292*25202Skarels     register struct icmp *icp;
293*25202Skarels     struct in_ifaddr *ia;
294*25202Skarels     int ilen;
295*25202Skarels     int prccode;
296*25202Skarels 
297*25202Skarels     icmpstat.ic_total ++;
298*25202Skarels 
299*25202Skarels     /*
300*25202Skarels      * see ip_input()
301*25202Skarels      */
302*25202Skarels     if ((mp->m_off > MMAXOFF) ||
303*25202Skarels 	(mp->m_len < sizeof(struct ip) + ICMPSIZE))
304*25202Skarels     {
305*25202Skarels 	if ((mp = m_pullup(mp, sizeof(struct ip) + ICMPSIZE)) == NULL)
306*25202Skarels 	{
307*25202Skarels 	    icmpstat.ic_tooshort ++;
308*25202Skarels 	    return;
309*25202Skarels 	}
310*25202Skarels     }
311*25202Skarels     ip = mtod(mp, struct ip *);
312*25202Skarels     icp = (struct icmp *) (ip+1);
313*25202Skarels 
314*25202Skarels     /*
315*25202Skarels      * watch for fools sending out broadcast ICMP packets
316*25202Skarels      * Don't check against inetifp, since is up to ip_input whether to receive
317*25202Skarels      * on some interface rather than send to self for input on dst interface.
318*25202Skarels      */
319*25202Skarels     ia = in_iawithaddr(ip->ip_dst, FALSE);
320*25202Skarels     if (ia == NULL)
321*25202Skarels     {
322*25202Skarels 	/* drop it */
323*25202Skarels 	m_freem(mp);
324*25202Skarels 	return;
325*25202Skarels     }
326*25202Skarels 
327*25202Skarels     /* filter out message types */
328*25202Skarels 
329*25202Skarels     if (icp->ic_type >= NICTYPE || icaction[icp->ic_type] == ICBAD)
330*25202Skarels     {
331*25202Skarels 	icmpstat.ic_drops++;
332*25202Skarels 	goto badret;
333*25202Skarels     }
334*25202Skarels 
335*25202Skarels     if (mp->m_len < icpullup[icp->ic_type])
336*25202Skarels     {
337*25202Skarels 	if ((mp = m_pullup(mp, icpullup[icp->ic_type])) == NULL)
338*25202Skarels 	{
339*25202Skarels 	    icmpstat.ic_tooshort ++;
340*25202Skarels 	    return;
341*25202Skarels 	}
342*25202Skarels 	ip = mtod(mp, struct ip *);
343*25202Skarels 	icp = (struct icmp *) (ip+1);
344*25202Skarels     }
345*25202Skarels     mp->m_off += sizeof(struct ip);
346*25202Skarels     mp->m_len -= sizeof(struct ip);
347*25202Skarels 
348*25202Skarels     ilen = ip->ip_len;
349*25202Skarels 
350*25202Skarels     {
351*25202Skarels     register u_short his_sum, our_sum;
352*25202Skarels 
353*25202Skarels     his_sum = (u_short)icp->ic_sum;
354*25202Skarels     icp->ic_sum = 0;
355*25202Skarels     if (his_sum != (our_sum = (u_short)in_cksum(mp, ilen)))
356*25202Skarels     {
357*25202Skarels 	icmpstat.ic_badsum++;
358*25202Skarels 	if (! nosum)
359*25202Skarels 	{
360*25202Skarels 	    /* note that the icmp header doesn't overlap IP */
361*25202Skarels #ifdef HMPTRAPS
362*25202Skarels 	    /* hmp_trap(T_ICMP_CKSUM, (caddr_t),0); */
363*25202Skarels #endif
364*25202Skarels 	    inet_cksum_err ("icmp", ip, (u_long) his_sum, (u_long) our_sum);
365*25202Skarels 	    netlog(mp);
366*25202Skarels 	    return;
367*25202Skarels 	}
368*25202Skarels     }
369*25202Skarels     }
370*25202Skarels 
371*25202Skarels     /*
372*25202Skarels      * Now do any processing.  Some messages are handled here,
373*25202Skarels      * others are passed up ctlinput path for further processing.
374*25202Skarels      */
375*25202Skarels 
376*25202Skarels     switch (icp->ic_type)
377*25202Skarels     {
378*25202Skarels 
379*25202Skarels       case ICMP_UNRCH:	/* destination unreachable */
380*25202Skarels 
381*25202Skarels 	if (icp->ic_code < ICMP_UNRCH_NUM)
382*25202Skarels 	{
383*25202Skarels 	    register int (*ctlfunc)();
384*25202Skarels 
385*25202Skarels 	    prccode = icunrch[icp->ic_code];
386*25202Skarels passup:
387*25202Skarels 	    ctlfunc = ipsw[icp->ic_iphdr.ip_p].ipsw_user->pr_ctlinput;
388*25202Skarels 	    (*ctlfunc) (prccode, (caddr_t) icp);
389*25202Skarels 	}
390*25202Skarels 	break;
391*25202Skarels 
392*25202Skarels       case ICMP_SRCQ:	/* source quench */
393*25202Skarels 
394*25202Skarels 	/*
395*25202Skarels 	 * At the IP level, we could try to reroute the connection and see if we
396*25202Skarels 	 * come up with a less loaded gateway.  Problem with this is that we know
397*25202Skarels 	 * total number of packets sent over a route, not the recent traffic load.
398*25202Skarels 	 */
399*25202Skarels 	icmpstat.ic_quenches++;
400*25202Skarels 	prccode = PRC_QUENCH;
401*25202Skarels #ifdef HMPTRAPS
402*25202Skarels 	/* hmp_trap(T_ICMP_SRCQ, (caddr_t)0, 0); */
403*25202Skarels #endif
404*25202Skarels 	goto passup;
405*25202Skarels 
406*25202Skarels       case ICMP_REDIR:	/* redirect */
407*25202Skarels 
408*25202Skarels 	icmpstat.ic_redirects ++;
409*25202Skarels 
410*25202Skarels 	/*
411*25202Skarels 	 * Sorry, we only trust the connected set of gateways
412*25202Skarels 	 * that includes gateways installed by the system
413*25202Skarels 	 * manager.
414*25202Skarels 	 */
415*25202Skarels 	if (know_gateway(ip->ip_src.s_addr))
416*25202Skarels 	{
417*25202Skarels 	    register struct mbuf **table;
418*25202Skarels 
419*25202Skarels 	    if (icp->ic_code == ICMP_REDIR_NET)
420*25202Skarels 	    {
421*25202Skarels 		prccode = PRC_REDIRECT_NET;
422*25202Skarels 		table = rtnet;
423*25202Skarels 	    }
424*25202Skarels 	    else
425*25202Skarels 	    {
426*25202Skarels 		prccode = PRC_REDIRECT_HOST;
427*25202Skarels 		table = rthost;
428*25202Skarels 	    }
429*25202Skarels 	    if (icmp_redirect_route (icp, table))
430*25202Skarels 		goto passup;
431*25202Skarels 	}
432*25202Skarels 	else
433*25202Skarels 	{
434*25202Skarels 	    /*
435*25202Skarels 	     * Who are you?  Why are you talking to us?
436*25202Skarels 	     * And how do we know the ip source isn't a lie?
437*25202Skarels 	     * (Eg., Catches Symbolics redirection of subnet broadcast.)
438*25202Skarels 	     */
439*25202Skarels 	    union { u_long ul; u_char c[4]; } a;
440*25202Skarels 
441*25202Skarels 	    a.ul = ip->ip_src.s_addr;
442*25202Skarels 	    log(KERN_RECOV, "Ignoring redirect from %d.%d.%d.%d\n",
443*25202Skarels 		a.c[0], a.c[1], a.c[2], a.c[3]);
444*25202Skarels 	}
445*25202Skarels #ifdef HMPTRAPS
446*25202Skarels 	/* hmp_trap(T_ICMP_REDIR, (caddr_t)0,0); */
447*25202Skarels #endif
448*25202Skarels 	break;
449*25202Skarels 
450*25202Skarels       case ICMP_ECHO:	/* echo */
451*25202Skarels 
452*25202Skarels 	icp->ic_type = ICMP_ECHOR;
453*25202Skarels 	icmpstat.ic_echoes++;
454*25202Skarels 	goto loopback;
455*25202Skarels 
456*25202Skarels       case ICMP_ECHOR:	/* echo reply */
457*25202Skarels 
458*25202Skarels 	/* check for gateway ping packets, look for
459*25202Skarels 	 * corresponding gateway entry and set echo count
460*25202Skarels 	 * to zero.
461*25202Skarels 	 */
462*25202Skarels #ifdef	BBNPING
463*25202Skarels 	if (icp->ic_id == MY_ECHO_ID)
464*25202Skarels 	    got_ping(ip->ip_src.s_addr);
465*25202Skarels #endif
466*25202Skarels 	break;
467*25202Skarels 
468*25202Skarels       case ICMP_TIMEX:	/* time exceeded */
469*25202Skarels 	/*
470*25202Skarels 	 * IP time to live field should be associated with the route so
471*25202Skarels 	 * that it can be dynamically adjusted for time exceeded in transit.
472*25202Skarels 	 * If did, would only need to "pass time exceeded in reassembly"
473*25202Skarels 	 * up to protocol (TCP) so that it can better try to avoid IP
474*25202Skarels 	 * fragmentation.
475*25202Skarels 	 */
476*25202Skarels 	icmpstat.ic_timex++;
477*25202Skarels 	prccode = (icp->ic_code == ICMP_TIMEX_XMT)
478*25202Skarels 	    ? PRC_TIMXCEED_INTRANS
479*25202Skarels 	    : PRC_TIMXCEED_REASS;
480*25202Skarels #ifdef HMPTRAPS
481*25202Skarels 	/* hmp_trap(T_ICMP_TIMEX, (caddr_t)0,0); */
482*25202Skarels #endif
483*25202Skarels 	goto passup;
484*25202Skarels 
485*25202Skarels       case ICMP_TIMES:	/* timestamp */
486*25202Skarels 
487*25202Skarels 	if (icp->ic_code == 0)
488*25202Skarels 	{
489*25202Skarels 	    icp->ic_type = ICMP_TIMESR;
490*25202Skarels 	    /*
491*25202Skarels 	     * Can now do timestamps in UT
492*25202Skarels 	     *
493*25202Skarels 	       icp->ic_trcv = (long)time.tv_sec | 0x80;
494*25202Skarels 	       icp->ic_txmt = (long)time.tv_sec | 0x80;
495*25202Skarels 	     */
496*25202Skarels 	    icp->ic_txmt = icp->ic_trcv = iptime();
497*25202Skarels 	    goto loopback;
498*25202Skarels 	}
499*25202Skarels 	break;
500*25202Skarels 
501*25202Skarels       case ICMP_INFO:	/* info request */
502*25202Skarels 	/*
503*25202Skarels 	 * He knows his host number, but not his network #,
504*25202Skarels 	 * fill in src & dst as he would have, had he known.
505*25202Skarels 	 */
506*25202Skarels 	{
507*25202Skarels 	register struct in_ifaddr *inaddress;
508*25202Skarels 	extern struct ifnet *inetifp;
509*25202Skarels 
510*25202Skarels 	icp->ic_type = ICMP_INFOR;
511*25202Skarels 	inaddress = in_iafromif(inetifp);
512*25202Skarels 	ip->ip_src.s_addr |= inaddress->ia_subnet;
513*25202Skarels 	ip->ip_dst = redir_addr(ip);
514*25202Skarels 	}
515*25202Skarels 	goto loopback;
516*25202Skarels 
517*25202Skarels       case ICMP_PARM:	/* parameter problem */
518*25202Skarels 	icmpstat.ic_parm++;
519*25202Skarels 	prccode = PRC_PARAMPROB;
520*25202Skarels #ifdef HMPTRAPS
521*25202Skarels 	/* hmp_trap(T_ICMP_PARM, (caddr_t)0,0); */
522*25202Skarels #endif
523*25202Skarels 	goto passup;
524*25202Skarels     }
525*25202Skarels 
526*25202Skarels badret :
527*25202Skarels     m_freem(mp);
528*25202Skarels     return;
529*25202Skarels 
530*25202Skarels loopback :
531*25202Skarels     {
532*25202Skarels 	struct in_addr temp;
533*25202Skarels 	register int error;
534*25202Skarels 
535*25202Skarels 	temp = ip->ip_src;
536*25202Skarels 	ip->ip_src = ip->ip_dst;
537*25202Skarels 	ip->ip_dst = temp;
538*25202Skarels 	/* ip->ip_p = IPPROTO_ICMP; still is from input */
539*25202Skarels 	/* ip->ip_tos = 0; use same tos for reply */
540*25202Skarels 
541*25202Skarels 	icp->ic_sum = in_cksum(mp, ilen);
542*25202Skarels 	mp->m_off -= sizeof(struct ip);
543*25202Skarels 	mp->m_len += sizeof(struct ip);
544*25202Skarels 	NOPCB_IPSEND (mp, (int)ip->ip_len, FALSE, error);
545*25202Skarels 
546*25202Skarels #ifdef lint
547*25202Skarels 	error = error;
548*25202Skarels #endif
549*25202Skarels 
550*25202Skarels     }
551*25202Skarels }
552*25202Skarels 
553*25202Skarels 
554*25202Skarels /*
555*25202Skarels  * Ping gateways in use to see if they are still alive.
556*25202Skarels  */
557*25202Skarels ic_timeo()
558*25202Skarels {
559*25202Skarels #ifdef BBNPING
560*25202Skarels     register int	i;
561*25202Skarels     register int	level;
562*25202Skarels     static int ictimer;
563*25202Skarels 
564*25202Skarels     if (--ictimer > 0)
565*25202Skarels 	return;
566*25202Skarels     ictimer = PINGTIME;
567*25202Skarels 
568*25202Skarels     level = splnet();
569*25202Skarels     for (i=0 ; i<RTHASHSIZ ; i++)
570*25202Skarels     {
571*25202Skarels 	check_ping(rthost[i]);
572*25202Skarels 	check_ping(rtnet[i]);
573*25202Skarels     }
574*25202Skarels     splx(level);
575*25202Skarels #endif
576*25202Skarels }
577*25202Skarels 
578*25202Skarels static struct rtentry *rtfind (dst, via, table)
579*25202Skarels struct in_addr	 dst;
580*25202Skarels struct in_addr	 via;
581*25202Skarels struct mbuf	*table[];
582*25202Skarels {
583*25202Skarels     register struct mbuf	*m;
584*25202Skarels 
585*25202Skarels     struct rtentry	*rt;
586*25202Skarels 
587*25202Skarels     if (table == rthost)
588*25202Skarels 	m = rthost[HOSTHASH(dst.s_addr) % RTHASHSIZ];
589*25202Skarels     else
590*25202Skarels     {
591*25202Skarels 	if (dst.s_addr)
592*25202Skarels 	{
593*25202Skarels 	    m = rtnet[NETHASH(dst) % RTHASHSIZ];
594*25202Skarels 	    dst.s_addr = iptonet(dst);
595*25202Skarels 	}
596*25202Skarels 	else
597*25202Skarels 	    m = rtnet[0];
598*25202Skarels     }
599*25202Skarels 
600*25202Skarels     while (m)
601*25202Skarels     {
602*25202Skarels 	struct in_addr d, g;
603*25202Skarels 
604*25202Skarels 	rt = mtod(m, struct rtentry *);
605*25202Skarels 	d = satoipa(&rt->rt_dst);
606*25202Skarels 	g = satoipa(&rt->rt_gateway);
607*25202Skarels 	if ((d.s_addr == dst.s_addr) &&
608*25202Skarels 	    (g.s_addr == via.s_addr) &&
609*25202Skarels 	    (rt->rt_dst.sa_family == AF_INET))
610*25202Skarels 	{
611*25202Skarels 	    /* then, hash values must be same. */
612*25202Skarels 	    return (rt);
613*25202Skarels 	}
614*25202Skarels 
615*25202Skarels 	m = m->m_next;
616*25202Skarels     }
617*25202Skarels 
618*25202Skarels     return (NULL);
619*25202Skarels }
620*25202Skarels 
621*25202Skarels 
622*25202Skarels icmp_redirect_route (ic, table)
623*25202Skarels struct icmp	*ic;
624*25202Skarels struct mbuf	*table[];
625*25202Skarels {
626*25202Skarels     struct ip	*ip;
627*25202Skarels     int flags;
628*25202Skarels     static struct sockaddr_in red_dst = { AF_INET } ;
629*25202Skarels     static struct sockaddr_in red_gtw = { AF_INET } ;
630*25202Skarels 
631*25202Skarels     ip = (struct ip *) ic->ic_data;
632*25202Skarels     /*
633*25202Skarels      * 1.  Make new routing entry so that new connections will use better
634*25202Skarels      * route.  But only make entry if have not already done so.
635*25202Skarels      */
636*25202Skarels     if (!rtfind(ip->ip_dst, ic->ic_gaddr, table))
637*25202Skarels     {
638*25202Skarels 	char	*err;
639*25202Skarels 
640*25202Skarels 	/* check reasonableness of redirect */
641*25202Skarels 
642*25202Skarels 	if (in_iawithnet(ic->ic_gaddr) == NULL)
643*25202Skarels 	{
644*25202Skarels 	    /*
645*25202Skarels 	     * Sorry, can't get there from here.
646*25202Skarels 	     */
647*25202Skarels 	    union { u_long ul; u_char c[4]; } g, f, t, v;
648*25202Skarels 
649*25202Skarels 	    err = "No interface for first hop";
650*25202Skarels perr :
651*25202Skarels 
652*25202Skarels 	    g.ul = (((struct ip *) (((char *) ic) - sizeof(struct ip)))->ip_src.s_addr);
653*25202Skarels 	    f.ul = ip->ip_src.s_addr;
654*25202Skarels 	    t.ul = ip->ip_dst.s_addr;
655*25202Skarels 	    v.ul = ic->ic_gaddr.s_addr;
656*25202Skarels 	    log(KERN_RECOV,
657*25202Skarels 		"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",
658*25202Skarels 		g.c[0], g.c[1], g.c[2], g.c[3],
659*25202Skarels 		f.c[0], f.c[1], f.c[2], f.c[3],
660*25202Skarels 		t.c[0], t.c[1], t.c[2], t.c[3],
661*25202Skarels 		v.c[0], v.c[1], v.c[2], v.c[3],
662*25202Skarels 		err);
663*25202Skarels 
664*25202Skarels 	    return (FALSE);
665*25202Skarels 	}
666*25202Skarels 
667*25202Skarels 	if (in_iawithaddr(ic->ic_gaddr, TRUE))
668*25202Skarels 	{
669*25202Skarels 	    /*
670*25202Skarels 	     * redirect to self is stupid, as is redirect to
671*25202Skarels 	     * broadcast address (which if_iawithaddr will match
672*25202Skarels 	     * for interfaces with IFF_BROADCAST set.)
673*25202Skarels 	     */
674*25202Skarels 	    err = "redirected to self";
675*25202Skarels 	    goto perr;
676*25202Skarels 	}
677*25202Skarels 
678*25202Skarels 	if (iptonet(ic->ic_gaddr) != iptonet(ip->ip_src))
679*25202Skarels 	{
680*25202Skarels 	    /*
681*25202Skarels 	     * Why is this gateway redirecting us?  It is not
682*25202Skarels 	     * giving us a first hop gateway that is on the
683*25202Skarels 	     * local net that we advertise.
684*25202Skarels 	     */
685*25202Skarels 	    err = "new first hop net <> src net";
686*25202Skarels 	    goto perr;
687*25202Skarels 	}
688*25202Skarels 
689*25202Skarels #ifdef done_in_icmp_c
690*25202Skarels 	if (! know_gateway(icmp source))
691*25202Skarels 	    /*
692*25202Skarels 	     * Sorry, we only trust the connected set of gateways
693*25202Skarels 	     * that includes gateways installed by the system
694*25202Skarels 	     * manager.  Who are you?  Why are you talking to us?
695*25202Skarels 	     */
696*25202Skarels 	    return;
697*25202Skarels #endif
698*25202Skarels 
699*25202Skarels 	/* o.k., I'll believe it */
700*25202Skarels 	flags = RTF_UP;
701*25202Skarels 	if (table == rthost)
702*25202Skarels 	{
703*25202Skarels 	    flags |= RTF_HOST;
704*25202Skarels 	    red_dst.sin_addr.s_addr = ip->ip_dst.s_addr;
705*25202Skarels 	}
706*25202Skarels 	else
707*25202Skarels 	{
708*25202Skarels 	    flags |= RTF_GATEWAY;
709*25202Skarels 	    red_dst.sin_addr.s_addr = iptonet(ip->ip_dst);
710*25202Skarels 	}
711*25202Skarels 	red_gtw.sin_addr.s_addr = ic->ic_gaddr.s_addr;
712*25202Skarels 	rtinit ((struct sockaddr *) &red_dst,
713*25202Skarels 	    (struct sockaddr *) &red_gtw,
714*25202Skarels 	    flags);
715*25202Skarels     }
716*25202Skarels     return (TRUE);
717*25202Skarels }
718*25202Skarels 
719*25202Skarels icmp_redirect_inp(inp, ic, table)
720*25202Skarels struct inpcb	*inp;
721*25202Skarels struct icmp	*ic;
722*25202Skarels struct mbuf	**table;
723*25202Skarels {
724*25202Skarels     struct rtentry *rt;
725*25202Skarels 
726*25202Skarels     /*
727*25202Skarels      * 2.  Redirect current connection.
728*25202Skarels      */
729*25202Skarels 
730*25202Skarels #ifdef neverdef
731*25202Skarels 	/*
732*25202Skarels 	 * This would try to balance load across gateways, but
733*25202Skarels 	 * that's something best done by the gateway before it
734*25202Skarels 	 * sends a redirect.  Also, consider 3 gateways of which
735*25202Skarels 	 * two are bad, and possibility of bouncing between the
736*25202Skarels 	 * two bad ones until their use counts got high enough.
737*25202Skarels 	 *
738*25202Skarels 	 * Currently, gateways only take into account # hops, not
739*25202Skarels 	 * load.
740*25202Skarels 	 */
741*25202Skarels 	if (rt = inp->inp_route.ro_rt)
742*25202Skarels 	{
743*25202Skarels 	    short	oflags;
744*25202Skarels 
745*25202Skarels 	    /* try to force a different path */
746*25202Skarels 	    oflags = rt->rt_flags;
747*25202Skarels 	    rt->rt_flags &= ~RTF_UP;
748*25202Skarels 	    /* but don't lose current route */
749*25202Skarels 	    rt->rt_refcnt ++;
750*25202Skarels 	    (void) ip_reroute (inp);
751*25202Skarels 	    rt->rt_refcnt --;
752*25202Skarels 	    rt->rt_flags = oflags;
753*25202Skarels 	}
754*25202Skarels #endif
755*25202Skarels 	if (rt = rtfind (ic->ic_iphdr.ip_dst, ic->ic_gaddr, table))
756*25202Skarels 	{
757*25202Skarels 	    if (rt->rt_flags & RTF_UP)
758*25202Skarels 	    {
759*25202Skarels 		/*
760*25202Skarels 		 * packets go out an interface with our local
761*25202Skarels 		 * IP address.  Know true from checks after
762*25202Skarels 		 * first call to rtfind above.
763*25202Skarels 		 *
764*25202Skarels 		 * Interface has to be at least as up as
765*25202Skarels 		 * for previous route, so don't bother to
766*25202Skarels 		 * check.
767*25202Skarels 		 */
768*25202Skarels 		if (inp->inp_route.ro_rt)
769*25202Skarels 		    rtfree (inp->inp_route.ro_rt);
770*25202Skarels 		inp->inp_route.ro_rt = rt;
771*25202Skarels 		rt->rt_refcnt ++;
772*25202Skarels 	    }
773*25202Skarels 	    else
774*25202Skarels 		log(KERN_RECOV, "ICMP Redirect to down route\n");
775*25202Skarels 	}
776*25202Skarels 	else
777*25202Skarels 	    log(KERN_RECOV, "ICMP Redirect route not installed?\n");
778*25202Skarels }
779