-/* ip_icmp.c 6.2 83/09/19 */
+/*
+ * Copyright (c) 1982, 1986 Regents of the University of California.
+ * All rights reserved. The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ *
+ * @(#)ip_icmp.c 7.2 (Berkeley) %G%
+ */
-#include "../h/param.h"
-#include "../h/systm.h"
-#include "../h/mbuf.h"
-#include "../h/protosw.h"
-#include "../h/socket.h"
-#include "../h/time.h"
-#include "../h/kernel.h"
+#include "param.h"
+#include "systm.h"
+#include "mbuf.h"
+#include "protosw.h"
+#include "socket.h"
+#include "time.h"
+#include "kernel.h"
#include "../net/route.h"
+#include "../net/if.h"
-#include "../netinet/in.h"
-#include "../netinet/in_systm.h"
-#include "../netinet/ip.h"
-#include "../netinet/ip_icmp.h"
-#include "../netinet/icmp_var.h"
+#include "in.h"
+#include "in_systm.h"
+#include "in_var.h"
+#include "ip.h"
+#include "ip_icmp.h"
+#include "icmp_var.h"
#ifdef ICMPPRINTFS
/*
* Generate an error packet of type error
* in response to bad packet ip.
*/
-icmp_error(oip, type, code)
+/*VARARGS4*/
+icmp_error(oip, type, code, ifp, dest)
struct ip *oip;
int type, code;
+ struct ifnet *ifp;
+ struct in_addr dest;
{
register unsigned oiplen = oip->ip_hl << 2;
register struct icmp *icp;
struct mbuf *m;
struct ip *nip;
+ unsigned icmplen;
#ifdef ICMPPRINTFS
if (icmpprintfs)
printf("icmp_error(%x, %d, %d)\n", oip, type, code);
#endif
- icmpstat.icps_error++;
+ if (type != ICMP_REDIRECT)
+ icmpstat.icps_error++;
/*
- * Make sure that the old IP packet had 8 bytes of data to return;
- * if not, don't bother. Also don't EVER error if the old
- * packet protocol was ICMP.
+ * Don't send error if not the first fragment of message.
+ * Don't EVER error if the old packet protocol was ICMP.
+ * (Could do ECHO, etc, but not error indications.)
*/
- if (oip->ip_len < 8) {
- icmpstat.icps_oldshort++;
+ if (oip->ip_off &~ (IP_MF|IP_DF))
goto free;
- }
- if (oip->ip_p == IPPROTO_ICMP) {
+ if (oip->ip_p == IPPROTO_ICMP && type != ICMP_REDIRECT) {
icmpstat.icps_oldicmp++;
goto free;
}
m = m_get(M_DONTWAIT, MT_HEADER);
if (m == NULL)
goto free;
- m->m_len = oiplen + 8 + ICMP_MINLEN;
+ icmplen = oiplen + MIN(8, oip->ip_len);
+ m->m_len = icmplen + ICMP_MINLEN;
m->m_off = MMAXOFF - m->m_len;
icp = mtod(m, struct icmp *);
- if ((u_int)type > ICMP_IREQREPLY)
+ if ((u_int)type > ICMP_MAXTYPE)
panic("icmp_error");
icmpstat.icps_outhist[type]++;
icp->icmp_type = type;
- icp->icmp_void = 0;
+ if (type == ICMP_REDIRECT)
+ icp->icmp_gwaddr = dest;
+ else
+ icp->icmp_void = 0;
if (type == ICMP_PARAMPROB) {
icp->icmp_pptr = code;
code = 0;
}
icp->icmp_code = code;
- bcopy((caddr_t)oip, (caddr_t)&icp->icmp_ip, oiplen + 8);
+ bcopy((caddr_t)oip, (caddr_t)&icp->icmp_ip, icmplen);
nip = &icp->icmp_ip;
nip->ip_len += oiplen;
nip->ip_len = htons((u_short)nip->ip_len);
/*
- * Now, copy old ip header in front of icmp
- * message. This allows us to reuse any source
- * routing info present.
+ * Now, copy old ip header in front of icmp message.
*/
+ if (m->m_len + oiplen > MLEN)
+ oiplen = sizeof(struct ip);
+ if (m->m_len + oiplen > MLEN)
+ panic("icmp len");
m->m_off -= oiplen;
+ m->m_len += oiplen;
nip = mtod(m, struct ip *);
bcopy((caddr_t)oip, (caddr_t)nip, oiplen);
- nip->ip_len = m->m_len + oiplen;
+ nip->ip_len = m->m_len;
nip->ip_p = IPPROTO_ICMP;
- /* icmp_send adds ip header to m_off and m_len, so we deduct here */
- m->m_off += oiplen;
- icmp_reflect(nip);
+ icmp_reflect(nip, ifp);
free:
m_freem(dtom(oip));
static struct sockproto icmproto = { AF_INET, IPPROTO_ICMP };
static struct sockaddr_in icmpsrc = { AF_INET };
static struct sockaddr_in icmpdst = { AF_INET };
+static struct sockaddr_in icmpgw = { AF_INET };
+struct in_ifaddr *ifptoia();
/*
* Process a received ICMP message.
*/
-icmp_input(m)
- struct mbuf *m;
+icmp_input(m, ifp)
+ register struct mbuf *m;
+ struct ifnet *ifp;
{
register struct icmp *icp;
register struct ip *ip = mtod(m, struct ip *);
int icmplen = ip->ip_len, hlen = ip->ip_hl << 2;
- int (*ctlfunc)(), code, i;
+ register int i;
+ struct in_ifaddr *ia;
+ int (*ctlfunc)(), code;
extern u_char ip_protox[];
+ extern struct in_addr in_makeaddr();
/*
* Locate icmp structure in mbuf, and check
icmpstat.icps_tooshort++;
goto free;
}
+ i = hlen + MIN(icmplen, ICMP_ADVLENMIN);
+ if ((m->m_off > MMAXOFF || m->m_len < i) &&
+ (m = m_pullup(m, i)) == 0) {
+ icmpstat.icps_tooshort++;
+ return;
+ }
+ ip = mtod(m, struct ip *);
m->m_len -= hlen;
m->m_off += hlen;
- /* need routine to make sure header is in this mbuf here */
icp = mtod(m, struct icmp *);
- i = icp->icmp_cksum;
- icp->icmp_cksum = 0;
- if (i != in_cksum(m, icmplen)) {
+ if (in_cksum(m, icmplen)) {
icmpstat.icps_checksum++;
goto free;
}
+ m->m_len += hlen;
+ m->m_off -= hlen;
#ifdef ICMPPRINTFS
/*
printf("icmp_input, type %d code %d\n", icp->icmp_type,
icp->icmp_code);
#endif
- if (icp->icmp_type > ICMP_IREQREPLY)
- goto free;
+ if (icp->icmp_type > ICMP_MAXTYPE)
+ goto raw;
icmpstat.icps_inhist[icp->icmp_type]++;
code = icp->icmp_code;
switch (icp->icmp_type) {
if (icmpprintfs)
printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
#endif
+ icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
if (ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput)
- (*ctlfunc)(code, (caddr_t)icp);
- goto free;
+ (*ctlfunc)(code, (struct sockaddr *)&icmpsrc);
+ break;
badcode:
icmpstat.icps_badcode++;
- goto free;
+ break;
case ICMP_ECHO:
icp->icmp_type = ICMP_ECHOREPLY;
case ICMP_TSTAMP:
if (icmplen < ICMP_TSLEN) {
icmpstat.icps_badlen++;
- goto free;
+ break;
}
icp->icmp_type = ICMP_TSTAMPREPLY;
icp->icmp_rtime = iptime();
goto reflect;
case ICMP_IREQ:
-#ifdef notdef
- /* fill in source address zero fields! */
+#define satosin(sa) ((struct sockaddr_in *)(sa))
+ if (in_netof(ip->ip_src) == 0 && (ia = ifptoia(ifp)))
+ ip->ip_src = in_makeaddr(in_netof(IA_SIN(ia)->sin_addr),
+ in_lnaof(ip->ip_src));
+ icp->icmp_type = ICMP_IREQREPLY;
goto reflect;
-#else
- goto free; /* not yet implemented: ignore */
-#endif
+
+ case ICMP_MASKREQ:
+ if (icmplen < ICMP_MASKLEN || (ia = ifptoia(ifp)) == 0)
+ break;
+ icp->icmp_type = ICMP_MASKREPLY;
+ icp->icmp_mask = ia->ia_netmask;
+ if (ip->ip_src.s_addr == 0) {
+ if (ia->ia_ifp->if_flags & IFF_BROADCAST)
+ ip->ip_src = satosin(&ia->ia_broadaddr)->sin_addr;
+ else if (ia->ia_ifp->if_flags & IFF_POINTOPOINT)
+ ip->ip_src = satosin(&ia->ia_dstaddr)->sin_addr;
+ }
+reflect:
+ ip->ip_len += hlen; /* since ip_input deducts this */
+ icmpstat.icps_reflect++;
+ icmpstat.icps_outhist[icp->icmp_type]++;
+ icmp_reflect(ip, ifp);
+ return;
case ICMP_REDIRECT:
- case ICMP_ECHOREPLY:
- case ICMP_TSTAMPREPLY:
- case ICMP_IREQREPLY:
if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)) {
icmpstat.icps_badlen++;
- goto free;
+ break;
}
/*
* Short circuit routing redirects to force
* immediate change in the kernel's routing
* tables. The message is also handed to anyone
* listening on a raw socket (e.g. the routing
- * daemon for use in updating it's tables).
+ * daemon for use in updating its tables).
*/
- if (icp->icmp_type == ICMP_REDIRECT) {
+ icmpgw.sin_addr = ip->ip_src;
+ icmpdst.sin_addr = icp->icmp_gwaddr;
+#ifdef ICMPPRINTFS
+ if (icmpprintfs)
+ printf("redirect dst %x to %x\n", icp->icmp_ip.ip_dst,
+ icp->icmp_gwaddr);
+#endif
+ if (code == ICMP_REDIRECT_NET || code == ICMP_REDIRECT_TOSNET) {
+ icmpsrc.sin_addr =
+ in_makeaddr(in_netof(icp->icmp_ip.ip_dst), INADDR_ANY);
+ rtredirect((struct sockaddr *)&icmpsrc,
+ (struct sockaddr *)&icmpdst, RTF_GATEWAY,
+ (struct sockaddr *)&icmpgw);
+ icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
+ pfctlinput(PRC_REDIRECT_NET,
+ (struct sockaddr *)&icmpsrc);
+ } else {
icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
- icmpdst.sin_addr = icp->icmp_gwaddr;
rtredirect((struct sockaddr *)&icmpsrc,
- (struct sockaddr *)&icmpdst);
+ (struct sockaddr *)&icmpdst, RTF_GATEWAY | RTF_HOST,
+ (struct sockaddr *)&icmpgw);
+ pfctlinput(PRC_REDIRECT_HOST,
+ (struct sockaddr *)&icmpsrc);
}
- icmpsrc.sin_addr = ip->ip_src;
- icmpdst.sin_addr = ip->ip_dst;
- raw_input(dtom(icp), &icmproto, (struct sockaddr *)&icmpsrc,
- (struct sockaddr *)&icmpdst);
- return;
+ break;
+ /*
+ * No kernel processing for the following;
+ * just fall through to send to raw listener.
+ */
+ case ICMP_ECHOREPLY:
+ case ICMP_TSTAMPREPLY:
+ case ICMP_IREQREPLY:
+ case ICMP_MASKREPLY:
default:
- goto free;
+ break;
}
-reflect:
- ip->ip_len += hlen; /* since ip_input deducts this */
- icmpstat.icps_reflect++;
- icmp_reflect(ip);
+
+raw:
+ icmpsrc.sin_addr = ip->ip_src;
+ icmpdst.sin_addr = ip->ip_dst;
+ raw_input(m, &icmproto, (struct sockaddr *)&icmpsrc,
+ (struct sockaddr *)&icmpdst);
return;
+
free:
- m_freem(dtom(ip));
+ m_freem(m);
}
/*
* Reflect the ip packet back to the source
- * TODO: rearrange ip source routing options.
*/
-icmp_reflect(ip)
- struct ip *ip;
+icmp_reflect(ip, ifp)
+ register struct ip *ip;
+ struct ifnet *ifp;
{
+ register struct in_ifaddr *ia;
struct in_addr t;
+ struct mbuf *opts = 0, *ip_srcroute();
+ int optlen = (ip->ip_hl << 2) - sizeof(struct ip);
t = ip->ip_dst;
ip->ip_dst = ip->ip_src;
+ /*
+ * If the incoming packet was addressed directly to us,
+ * use dst as the src for the reply. Otherwise (broadcast
+ * or anonymous), use the address which corresponds
+ * to the incoming interface.
+ */
+ for (ia = in_ifaddr; ia; ia = ia->ia_next) {
+ if (t.s_addr == IA_SIN(ia)->sin_addr.s_addr)
+ break;
+ if ((ia->ia_ifp->if_flags & IFF_BROADCAST) &&
+ t.s_addr == satosin(&ia->ia_broadaddr)->sin_addr.s_addr)
+ break;
+ }
+ if (ia == (struct in_ifaddr *)0)
+ ia = ifptoia(ifp);
+ if (ia == (struct in_ifaddr *)0)
+ ia = in_ifaddr;
+ t = IA_SIN(ia)->sin_addr;
ip->ip_src = t;
- icmp_send(ip);
+
+ if (optlen > 0) {
+ /*
+ * Retrieve any source routing from the incoming packet
+ * and strip out other options. Adjust the IP length.
+ */
+ opts = ip_srcroute();
+ ip->ip_len -= optlen;
+ ip_stripoptions(ip, (struct mbuf *)0);
+ }
+ icmp_send(ip, opts);
+ if (opts)
+ (void)m_free(opts);
+}
+
+struct in_ifaddr *
+ifptoia(ifp)
+ struct ifnet *ifp;
+{
+ register struct in_ifaddr *ia;
+
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if (ia->ia_ifp == ifp)
+ return (ia);
+ return ((struct in_ifaddr *)0);
}
/*
* Send an icmp packet back to the ip level,
* after supplying a checksum.
*/
-icmp_send(ip)
- struct ip *ip;
+icmp_send(ip, opts)
+ register struct ip *ip;
+ struct mbuf *opts;
{
register int hlen;
register struct icmp *icp;
m = dtom(ip);
hlen = ip->ip_hl << 2;
+ m->m_off += hlen;
+ m->m_len -= hlen;
icp = mtod(m, struct icmp *);
icp->icmp_cksum = 0;
icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen);
if (icmpprintfs)
printf("icmp_send dst %x src %x\n", ip->ip_dst, ip->ip_src);
#endif
- (void) ip_output(m, (struct mbuf *)0, (struct route *)0, 0);
+ (void) ip_output(m, opts, (struct route *)0, 0);
}
n_time
iptime()
{
- int s = spl6();
+ struct timeval atv;
u_long t;
- t = (time.tv_sec % (24*60*60)) * 1000 + time.tv_usec / 1000;
- splx(s);
+ microtime(&atv);
+ t = (atv.tv_sec % (24*60*60)) * 1000 + atv.tv_usec / 1000;
return (htonl(t));
}