* Copyright (c) 1982, 1986, 1988, 1993
* The Regents of the University of California. All rights reserved.
* %sccs.include.redist.c%
* @(#)ip_icmp.c 8.1 (Berkeley) %G%
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip_icmp.h>
#include <netinet/icmp_var.h>
* ICMP routines: error generation, receive packet processing, and
* routines to turnaround packets back to the originator, and
* host table maintenance routines.
extern struct protosw inetsw
[];
* Generate an error packet of type error
* in response to bad packet ip.
icmp_error(n
, type
, code
, dest
, destifp
)
register struct ip
*oip
= mtod(n
, struct ip
*), *nip
;
register unsigned oiplen
= oip
->ip_hl
<< 2;
register struct icmp
*icp
;
printf("icmp_error(%x, %d, %d)\n", oip
, type
, code
);
if (type
!= ICMP_REDIRECT
)
* Don't send error if not the first fragment of message.
* Don't error if the old packet protocol was ICMP
* error message, only known informational types.
if (oip
->ip_off
&~ (IP_MF
|IP_DF
))
if (oip
->ip_p
== IPPROTO_ICMP
&& type
!= ICMP_REDIRECT
&&
dtom(oip
)->m_len
>= oiplen
+ ICMP_MINLEN
&&
n
->m_len
>= oiplen
+ ICMP_MINLEN
&&
!ICMP_INFOTYPE(((struct icmp
*)((caddr_t
)oip
+ oiplen
))->icmp_type
)) {
/* Don't send error in response to a multicast or broadcast packet */
if (n
->m_flags
& (M_BCAST
|M_MCAST
))
* First, formulate icmp message
m
= m_gethdr(M_DONTWAIT
, MT_HEADER
);
icmplen
= oiplen
+ min(8, oip
->ip_len
);
m
->m_len
= icmplen
+ ICMP_MINLEN
;
icp
= mtod(m
, struct icmp
*);
if ((u_int
)type
> ICMP_MAXTYPE
)
icmpstat
.icps_outhist
[type
]++;
if (type
== ICMP_REDIRECT
)
icp
->icmp_gwaddr
.s_addr
= dest
;
* The following assignments assume an overlay with the
* zeroed icmp_void field.
if (type
== ICMP_PARAMPROB
) {
} else if (type
== ICMP_UNREACH
&&
code
== ICMP_UNREACH_NEEDFRAG
&& destifp
) {
icp
->icmp_nextmtu
= htons(destifp
->if_mtu
);
bcopy((caddr_t
)oip
, (caddr_t
)&icp
->icmp_ip
, icmplen
);
nip
->ip_len
= htons((u_short
)(nip
->ip_len
+ oiplen
));
* Now, copy old ip header (without options)
* in front of icmp message.
if (m
->m_data
- sizeof(struct ip
) < m
->m_pktdat
)
m
->m_data
-= sizeof(struct ip
);
m
->m_len
+= sizeof(struct ip
);
m
->m_pkthdr
.len
= m
->m_len
;
m
->m_pkthdr
.rcvif
= n
->m_pkthdr
.rcvif
;
nip
= mtod(m
, struct ip
*);
bcopy((caddr_t
)oip
, (caddr_t
)nip
, sizeof(struct ip
));
nip
->ip_hl
= sizeof(struct ip
) >> 2;
nip
->ip_hl
= sizeof(struct ip
) >> 2;
nip
->ip_p
= IPPROTO_ICMP
;
static struct sockproto icmproto
= { AF_INET
, IPPROTO_ICMP
};
static struct sockaddr_in icmpsrc
= { sizeof (struct sockaddr_in
), AF_INET
};
static struct sockaddr_in icmpdst
= { sizeof (struct sockaddr_in
), AF_INET
};
static struct sockaddr_in icmpgw
= { sizeof (struct sockaddr_in
), AF_INET
};
struct sockaddr_in icmpmask
= { 8, 0 };
* Process a received ICMP message.
register struct icmp
*icp
;
register struct ip
*ip
= mtod(m
, struct ip
*);
int icmplen
= ip
->ip_len
;
void (*ctlfunc
) __P((int, struct sockaddr
*, struct ip
*));
extern u_char ip_protox
[];
* Locate icmp structure in mbuf, and check
* that not corrupted and of at least minimum length.
printf("icmp_input from %x to %x, len %d\n",
ntohl(ip
->ip_src
.s_addr
), ntohl(ip
->ip_dst
.s_addr
),
if (icmplen
< ICMP_MINLEN
) {
icmpstat
.icps_tooshort
++;
i
= hlen
+ min(icmplen
, ICMP_ADVLENMIN
);
if (m
->m_len
< i
&& (m
= m_pullup(m
, i
)) == 0) {
icmpstat
.icps_tooshort
++;
ip
= mtod(m
, struct ip
*);
icp
= mtod(m
, struct icmp
*);
if (in_cksum(m
, icmplen
)) {
icmpstat
.icps_checksum
++;
* Message type specific processing.
printf("icmp_input, type %d code %d\n", icp
->icmp_type
,
if (icp
->icmp_type
> ICMP_MAXTYPE
)
icmpstat
.icps_inhist
[icp
->icmp_type
]++;
switch (icp
->icmp_type
) {
case ICMP_UNREACH_PROTOCOL
:
case ICMP_UNREACH_SRCFAIL
:
case ICMP_UNREACH_NEEDFRAG
:
case ICMP_UNREACH_NET_UNKNOWN
:
case ICMP_UNREACH_NET_PROHIB
:
case ICMP_UNREACH_TOSNET
:
case ICMP_UNREACH_HOST_UNKNOWN
:
case ICMP_UNREACH_ISOLATED
:
case ICMP_UNREACH_HOST_PROHIB
:
case ICMP_UNREACH_TOSHOST
:
code
+= PRC_TIMXCEED_INTRANS
;
* Problem with datagram; advise higher level routines.
if (icmplen
< ICMP_ADVLENMIN
|| icmplen
< ICMP_ADVLEN(icp
) ||
icp
->icmp_ip
.ip_hl
< (sizeof(struct ip
) >> 2)) {
NTOHS(icp
->icmp_ip
.ip_len
);
printf("deliver to protocol %d\n", icp
->icmp_ip
.ip_p
);
icmpsrc
.sin_addr
= icp
->icmp_ip
.ip_dst
;
if (ctlfunc
= inetsw
[ip_protox
[icp
->icmp_ip
.ip_p
]].pr_ctlinput
)
(*ctlfunc
)(code
, (struct sockaddr
*)&icmpsrc
,
icp
->icmp_type
= ICMP_ECHOREPLY
;
if (icmplen
< ICMP_TSLEN
) {
icp
->icmp_type
= ICMP_TSTAMPREPLY
;
icp
->icmp_rtime
= iptime();
icp
->icmp_ttime
= icp
->icmp_rtime
; /* bogus, do later! */
#define satosin(sa) ((struct sockaddr_in *)(sa))
* We are not able to respond with all ones broadcast
* unless we receive it over a point-to-point interface.
if (icmplen
< ICMP_MASKLEN
)
switch (ip
->ip_dst
.s_addr
) {
icmpdst
.sin_addr
= ip
->ip_src
;
icmpdst
.sin_addr
= ip
->ip_dst
;
ia
= (struct in_ifaddr
*)ifaof_ifpforaddr(
(struct sockaddr
*)&icmpdst
, m
->m_pkthdr
.rcvif
);
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
= satosin(&ia
->ia_broadaddr
)->sin_addr
;
else if (ia
->ia_ifp
->if_flags
& IFF_POINTOPOINT
)
ip
->ip_src
= satosin(&ia
->ia_dstaddr
)->sin_addr
;
ip
->ip_len
+= hlen
; /* since ip_input deducts this */
icmpstat
.icps_outhist
[icp
->icmp_type
]++;
if (icmplen
< ICMP_ADVLENMIN
|| icmplen
< ICMP_ADVLEN(icp
) ||
icp
->icmp_ip
.ip_hl
< (sizeof(struct ip
) >> 2)) {
* 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
;
printf("redirect dst %x to %x\n", icp
->icmp_ip
.ip_dst
,
icmpsrc
.sin_addr
= icp
->icmp_ip
.ip_dst
;
rtredirect((struct sockaddr
*)&icmpsrc
,
(struct sockaddr
*)&icmpdst
,
(struct sockaddr
*)0, RTF_GATEWAY
| RTF_HOST
,
(struct sockaddr
*)&icmpgw
, (struct rtentry
**)0);
pfctlinput(PRC_REDIRECT_HOST
, (struct sockaddr
*)&icmpsrc
);
* No kernel processing for the following;
* just fall through to send to raw listener.
* Reflect the ip packet back to the source
register struct ip
*ip
= mtod(m
, struct ip
*);
register struct in_ifaddr
*ia
;
struct mbuf
*opts
= 0, *ip_srcroute();
int optlen
= (ip
->ip_hl
<< 2) - sizeof(struct ip
);
if (!in_canforward(ip
->ip_src
) &&
((ntohl(ip
->ip_src
.s_addr
) & IN_CLASSA_NET
) !=
(IN_LOOPBACKNET
<< IN_CLASSA_NSHIFT
))) {
m_freem(m
); /* Bad return address */
goto done
; /* Ip_output() will check for broadcast */
* 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
)
if ((ia
->ia_ifp
->if_flags
& IFF_BROADCAST
) &&
t
.s_addr
== satosin(&ia
->ia_broadaddr
)->sin_addr
.s_addr
)
if (ia
== (struct in_ifaddr
*)0)
ia
= (struct in_ifaddr
*)ifaof_ifpforaddr(
(struct sockaddr
*)&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)
t
= IA_SIN(ia
)->sin_addr
;
struct mbuf
*m
= dtom(ip
);
* 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;
printf("icmp_reflect optlen %d rt %d => ",
for (cnt
= optlen
; cnt
> 0; cnt
-= len
, cp
+= len
) {
if (len
<= 0 || len
> cnt
)
* Should check for overflow, but it "can't happen"
if (opt
== IPOPT_RR
|| opt
== IPOPT_TS
||
mtod(opts
, caddr_t
) + opts
->m_len
, len
);
/* Terminate & pad, if necessary */
if (cnt
= opts
->m_len
% 4) {
*(mtod(opts
, caddr_t
) + opts
->m_len
) =
printf("%d\n", opts
->m_len
);
* Now strip out original options by copying rest of first
* mbuf's data back, and adjust the IP length.
ip
->ip_hl
= sizeof(struct ip
) >> 2;
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
);
* Send an icmp packet back to the ip level,
* after supplying a checksum.
register struct ip
*ip
= mtod(m
, struct ip
*);
register struct icmp
*icp
;
icp
= mtod(m
, struct icmp
*);
icp
->icmp_cksum
= in_cksum(m
, ip
->ip_len
- hlen
);
printf("icmp_send dst %x src %x\n", ip
->ip_dst
, ip
->ip_src
);
(void) ip_output(m
, opts
, NULL
, 0, NULL
);
t
= (atv
.tv_sec
% (24*60*60)) * 1000 + atv
.tv_usec
/ 1000;
icmp_sysctl(name
, namelen
, oldp
, oldlenp
, newp
, newlen
)
/* all sysctl names at this level are terminal */
return (sysctl_int(oldp
, oldlenp
, newp
, newlen
, &icmpmaskrepl
));