📄 ip_icmp.c
字号:
break;
default:
goto badcode;
}
goto deliver;
case ICMP_TIMXCEED:
if (code > 1)
goto badcode;
code += PRC_TIMXCEED_INTRANS;
goto deliver;
case ICMP_PARAMPROB:
if (code > 1)
goto badcode;
code = PRC_PARAMPROB;
goto deliver;
case ICMP_SOURCEQUENCH:
if (code)
goto badcode;
code = PRC_QUENCH;
deliver:
/*
* Problem with datagram; advise higher level routines.
*/
if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp) ||
icp->icmp_ip.ip_hl < (sizeof(struct ip) >> 2)) {
icmpstat.icps_badlen++;
goto freeit;
}
if (IN_MULTICAST(icp->icmp_ip.ip_dst.s_addr))
goto badcode;
NTOHS(icp->icmp_ip.ip_len);
#ifdef INET6
/* Get more contiguous data for a v6 in v4 ICMP message. */
if (icp->icmp_ip.ip_p == IPPROTO_IPV6) {
if (icmplen < ICMP_V6ADVLENMIN ||
icmplen < ICMP_V6ADVLEN(icp)) {
icmpstat.icps_badlen++;
goto freeit;
} else {
if (!(m = m_pullup(m, (ip->ip_hl << 2) +
ICMP_V6ADVLEN(icp)))) {
icmpstat.icps_tooshort++;
return;
}
ip = mtod(m, struct ip *);
icp = (struct icmp *)(m->m_data + (ip->ip_hl << 2));
}
}
#endif /* INET6 */
#ifdef ICMPPRINTFS
if (icmpprintfs)
printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
#endif
icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
/*
* XXX if the packet contains [IPv4 AH TCP], we can't make a
* notification to TCP layer.
*/
ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput;
if (ctlfunc)
(*ctlfunc)(code, sintosa(&icmpsrc), &icp->icmp_ip);
break;
badcode:
icmpstat.icps_badcode++;
break;
case ICMP_ECHO:
if (!icmpbmcastecho &&
(m->m_flags & (M_MCAST | M_BCAST)) != 0) {
icmpstat.icps_bmcastecho++;
break;
}
icp->icmp_type = ICMP_ECHOREPLY;
goto reflect;
case ICMP_TSTAMP:
if (!icmpbmcastecho &&
(m->m_flags & (M_MCAST | M_BCAST)) != 0) {
icmpstat.icps_bmcastecho++;
break;
}
if (icmplen < ICMP_TSLEN) {
icmpstat.icps_badlen++;
break;
}
icp->icmp_type = ICMP_TSTAMPREPLY;
icp->icmp_rtime = iptime();
icp->icmp_ttime = icp->icmp_rtime; /* bogus, do later! */
goto reflect;
case ICMP_MASKREQ:
if (icmpmaskrepl == 0)
break;
/*
* We are not able to respond with all ones broadcast
* unless we receive it over a point-to-point interface.
*/
if (icmplen < ICMP_MASKLEN) {
icmpstat.icps_badlen++;
break;
}
if (ip->ip_dst.s_addr == INADDR_BROADCAST ||
ip->ip_dst.s_addr == INADDR_ANY)
icmpdst.sin_addr = ip->ip_src;
else
icmpdst.sin_addr = ip->ip_dst;
ia = ifatoia(ifaof_ifpforaddr(sintosa(&icmpdst),
m->m_pkthdr.rcvif));
if (ia == 0)
break;
icp->icmp_type = ICMP_MASKREPLY;
icp->icmp_mask = ia->ia_sockmask.sin_addr.s_addr;
if (ip->ip_src.s_addr == 0) {
if (ia->ia_ifp->if_flags & IFF_BROADCAST)
ip->ip_src = ia->ia_broadaddr.sin_addr;
else if (ia->ia_ifp->if_flags & IFF_POINTOPOINT)
ip->ip_src = 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(m);
return;
case ICMP_REDIRECT:
if (code > 3)
goto badcode;
if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp) ||
icp->icmp_ip.ip_hl < (sizeof(struct ip) >> 2)) {
icmpstat.icps_badlen++;
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 its tables).
*/
icmpgw.sin_addr = ip->ip_src;
icmpdst.sin_addr = icp->icmp_gwaddr;
#ifdef ICMPPRINTFS
if (icmpprintfs) {
char buf[4 * sizeof "123"];
strcpy(buf, inet_ntoa(icp->icmp_ip.ip_dst));
printf("redirect dst %s to %s\n",
buf, inet_ntoa(icp->icmp_gwaddr));
}
#endif
icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
rtredirect(sintosa(&icmpsrc), sintosa(&icmpdst),
(struct sockaddr *)0, RTF_GATEWAY | RTF_HOST,
sintosa(&icmpgw), (struct rtentry **)0);
pfctlinput(PRC_REDIRECT_HOST, sintosa(&icmpsrc));
#if 0 /*KAME IPSEC*/
key_sa_routechange((struct sockaddr *)&icmpsrc);
#endif
break;
/*
* No kernel processing for the following;
* just fall through to send to raw listener.
*/
case ICMP_ECHOREPLY:
case ICMP_ROUTERADVERT:
case ICMP_ROUTERSOLICIT:
case ICMP_TSTAMPREPLY:
case ICMP_IREQREPLY:
case ICMP_MASKREPLY:
default:
break;
}
raw:
rip_input(m, hlen, proto);
return;
freeit:
m_freem(m);
}
/*
* Reflect the ip packet back to the source
*/
void
icmp_reflect(m)
struct mbuf *m;
{
register struct ip *ip = mtod(m, struct ip *);
register struct in_ifaddr *ia;
struct in_addr t;
struct mbuf *opts = 0;
int optlen = (ip->ip_hl << 2) - sizeof(struct ip);
if (!in_canforward(ip->ip_src) &&
((ip->ip_src.s_addr & IN_CLASSA_NET) !=
htonl(IN_LOOPBACKNET << IN_CLASSA_NSHIFT))) {
m_freem(m); /* Bad return address */
goto done; /* ip_output() will check for broadcast */
}
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.tqh_first; ia; ia = ia->ia_list.tqe_next) {
if (t.s_addr == ia->ia_addr.sin_addr.s_addr)
break;
if ((ia->ia_ifp->if_flags & IFF_BROADCAST) &&
t.s_addr == ia->ia_broadaddr.sin_addr.s_addr)
break;
}
icmpdst.sin_addr = t;
if (ia == (struct in_ifaddr *)0)
ia = ifatoia(ifaof_ifpforaddr(sintosa(&icmpdst),
m->m_pkthdr.rcvif));
/*
* The following happens if the packet was not addressed to us,
* and was received on an interface with no IP address.
*/
if (ia == (struct in_ifaddr *)0)
ia = in_ifaddr.tqh_first;
t = ia->ia_addr.sin_addr;
ip->ip_src = t;
ip->ip_ttl = MAXTTL;
if (optlen > 0) {
register u_char *cp;
int opt, cnt;
u_int len;
/*
* Retrieve any source routing from the incoming packet;
* add on any record-route or timestamp options.
*/
cp = (u_char *) (ip + 1);
if ((opts = ip_srcroute()) == 0 &&
(opts = m_gethdr(M_DONTWAIT, MT_HEADER))) {
opts->m_len = sizeof(struct in_addr);
mtod(opts, struct in_addr *)->s_addr = 0;
}
if (opts) {
#ifdef ICMPPRINTFS
if (icmpprintfs)
printf("icmp_reflect optlen %d rt %d => ",
optlen, opts->m_len);
#endif
for (cnt = optlen; cnt > 0; cnt -= len, cp += len) {
opt = cp[IPOPT_OPTVAL];
if (opt == IPOPT_EOL)
break;
if (opt == IPOPT_NOP)
len = 1;
else {
len = cp[IPOPT_OLEN];
if (len <= 0 || len > cnt)
break;
}
/*
* Should check for overflow, but it "can't happen"
*/
if (opt == IPOPT_RR || opt == IPOPT_TS ||
opt == IPOPT_SECURITY) {
bcopy((caddr_t)cp,
mtod(opts, caddr_t) + opts->m_len, len);
opts->m_len += len;
}
}
/* Terminate & pad, if necessary */
if ((cnt = opts->m_len % 4) != 0) {
for (; cnt < 4; cnt++) {
*(mtod(opts, caddr_t) + opts->m_len) =
IPOPT_EOL;
opts->m_len++;
}
}
#ifdef ICMPPRINTFS
if (icmpprintfs)
printf("%d\n", opts->m_len);
#endif
}
/*
* Now strip out original options by copying rest of first
* mbuf's data back, and adjust the IP length.
*/
ip->ip_len -= optlen;
ip->ip_hl = sizeof(struct ip) >> 2;
m->m_len -= optlen;
if (m->m_flags & M_PKTHDR)
m->m_pkthdr.len -= optlen;
optlen += sizeof(struct ip);
bcopy((caddr_t)ip + optlen, (caddr_t)(ip + 1),
(unsigned)(m->m_len - sizeof(struct ip)));
}
m->m_flags &= ~(M_BCAST|M_MCAST);
icmp_send(m, opts);
done:
if (opts)
(void)m_free(opts);
}
/*
* Send an icmp packet back to the ip level,
* after supplying a checksum.
*/
void
icmp_send(m, opts)
register struct mbuf *m;
struct mbuf *opts;
{
register struct ip *ip = mtod(m, struct ip *);
register int hlen;
register struct icmp *icp;
hlen = ip->ip_hl << 2;
m->m_data += hlen;
m->m_len -= hlen;
icp = mtod(m, struct icmp *);
icp->icmp_cksum = 0;
icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen);
m->m_data -= hlen;
m->m_len += hlen;
#ifdef ICMPPRINTFS
if (icmpprintfs) {
char buf[4 * sizeof "123"];
strcpy(buf, inet_ntoa(ip->ip_dst));
printf("icmp_send dst %s src %s\n",
buf, inet_ntoa(ip->ip_src));
}
#endif
#if 0 /*KAME IPSEC*/
m->m_pkthdr.rcvif = NULL;
#endif /*IPSEC*/
(void) ip_output(m, opts, NULL, 0, NULL, NULL);
}
n_time
iptime()
{
struct timeval atv;
u_long t;
microtime(&atv);
t = (atv.tv_sec % (24*60*60)) * 1000 + atv.tv_usec / 1000;
return (htonl(t));
}
#ifdef CYGPKG_NET_SYSCTL
int
icmp_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
int *name;
u_int namelen;
void *oldp;
size_t *oldlenp;
void *newp;
size_t newlen;
{
/* All sysctl names at this level are terminal. */
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case ICMPCTL_MASKREPL:
return (sysctl_int(oldp, oldlenp, newp, newlen, &icmpmaskrepl));
case ICMPCTL_BMCASTECHO:
return (sysctl_int(oldp, oldlenp, newp, newlen, &icmpbmcastecho));
default:
return (ENOPROTOOPT);
}
/* NOTREACHED */
}
#endif // CYGPKG_NET_SYSCTL
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -