* Copyright (c) 1982, 1986 Regents of the University of California.
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* @(#)ip_icmp.c 7.9 (Berkeley) %G%
#include "../net/route.h"
* ICMP routines: error generation, receive packet processing, and
* routines to turnaround packets back to the originator, and
* host table maintenance routines.
* Generate an error packet of type error
* in response to bad packet ip.
icmp_error(n
, type
, code
, dest
)
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
&&
!ICMP_INFOTYPE(((struct icmp
*)((caddr_t
)oip
+ oiplen
))->icmp_type
)) {
* 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
)
if (type
== ICMP_PARAMPROB
) {
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 in front of icmp message.
if (m
->m_len
+ oiplen
> MHLEN
)
oiplen
= sizeof(struct ip
);
if (m
->m_data
- oiplen
< m
->m_pktdat
)
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
, oiplen
);
nip
->ip_p
= IPPROTO_ICMP
;
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.
register struct icmp
*icp
;
register struct ip
*ip
= mtod(m
, struct ip
*);
int icmplen
= ip
->ip_len
;
extern u_char ip_protox
[];
extern struct in_addr
in_makeaddr();
* Locate icmp structure in mbuf, and check
* that not corrupted and of at least minimum length.
printf("icmp_input from %x, len %d\n", ip
->ip_src
, icmplen
);
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
) {
code
+= PRC_TIMXCEED_INTRANS
;
* Problem with datagram; advise higher level routines.
icp
->icmp_ip
.ip_len
= ntohs((u_short
)icp
->icmp_ip
.ip_len
);
if (icmplen
< ICMP_ADVLENMIN
|| icmplen
< ICMP_ADVLEN(icp
)) {
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))
if (in_netof(ip
->ip_src
) == 0 &&
(ia
= ifptoia(m
->m_pkthdr
.rcvif
)))
ip
->ip_src
= in_makeaddr(in_netof(IA_SIN(ia
)->sin_addr
),
icp
->icmp_type
= ICMP_IREQREPLY
;
if (icmplen
< ICMP_MASKLEN
||
(ia
= ifptoia(m
->m_pkthdr
.rcvif
)) == 0)
icp
->icmp_type
= ICMP_MASKREPLY
;
icp
->icmp_mask
= htonl(ia
->ia_subnetmask
);
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
)) {
* 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
,
if (code
== ICMP_REDIRECT_NET
|| code
== ICMP_REDIRECT_TOSNET
) {
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
);
icmpsrc
.sin_addr
= icp
->icmp_ip
.ip_dst
;
rtredirect((struct sockaddr
*)&icmpsrc
,
(struct sockaddr
*)&icmpdst
, RTF_GATEWAY
| RTF_HOST
,
(struct sockaddr
*)&icmpgw
);
pfctlinput(PRC_REDIRECT_HOST
,
(struct sockaddr
*)&icmpsrc
);
* No kernel processing for the following;
* just fall through to send to raw listener.
icmpsrc
.sin_addr
= ip
->ip_src
;
icmpdst
.sin_addr
= ip
->ip_dst
;
raw_input(m
, &icmproto
, (struct sockaddr
*)&icmpsrc
,
(struct sockaddr
*)&icmpdst
);
* 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 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
= ifptoia(m
->m_pkthdr
.rcvif
);
if (ia
== (struct in_ifaddr
*)0)
t
= IA_SIN(ia
)->sin_addr
;
* Retrieve any source routing from the incoming packet
* and strip out other options. Adjust the IP length.
bcopy((caddr_t
)ip
+ ip
->ip_len
, (caddr_t
)ip
+ sizeof(struct ip
),
(unsigned)m
->m_len
- ip
->ip_len
);
if (m
->m_flags
& M_PKTHDR
)
m
->m_pkthdr
.len
-= optlen
;
register struct in_ifaddr
*ia
;
for (ia
= in_ifaddr
; ia
; ia
= ia
->ia_next
)
return ((struct in_ifaddr
*)0);
* 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
, (struct route
*)0, 0);
t
= (atv
.tv_sec
% (24*60*60)) * 1000 + atv
.tv_usec
/ 1000;