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