Goto sanos source index
//
// icmp.c
//
// Internet Control Message Protocol (ICMP)
//
// Some ICMP messages should be passed to the transport protocols. This
// is not implemented.
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
// Portions Copyright (C) 2001, Swedish Institute of Computer Science.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the project nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
#include <net/net.h>
err_t icmp_input(struct pbuf *p, struct netif *inp) {
unsigned char type;
unsigned char code;
struct icmp_echo_hdr *iecho;
struct icmp_dur_hdr *idur;
struct ip_hdr *iphdr;
struct ip_addr tmpaddr;
int hlen;
stats.icmp.recv++;
iphdr = p->payload;
hlen = IPH_HL(iphdr) * 4;
if (pbuf_header(p, -hlen) < 0 || p->tot_len < sizeof(unsigned short) * 2) {
kprintf("icmp_input: short ICMP (%u bytes) received\n", p->tot_len);
stats.icmp.lenerr++;
return -EPROTO;
}
type = *((unsigned char *) p->payload);
//kprintf("icmp: recv type %d\n", type);
switch (type) {
case ICMP_ECHO:
if (ip_addr_isbroadcast(&iphdr->dest, &inp->netmask) || ip_addr_ismulticast(&iphdr->dest)) {
stats.icmp.err++;
return -EPROTO;
}
if (!ip_ownaddr(&iphdr->dest)) {
stats.icmp.err++;
return -EPROTO;
}
//kprintf("icmp_input: ping src %a dest %a\n", &iphdr->src, &iphdr->dest);
if (p->tot_len < sizeof(struct icmp_echo_hdr)) {
kprintf("icmp_input: bad ICMP echo received\n");
stats.icmp.lenerr++;
return -EPROTO;
}
iecho = p->payload;
if (inet_chksum_pbuf(p) != 0) {
kprintf("icmp_input: checksum failed for received ICMP echo\n");
stats.icmp.chkerr++;
return -ECHKSUM;
}
tmpaddr.addr = iphdr->src.addr;
iphdr->src.addr = iphdr->dest.addr;
iphdr->dest.addr = tmpaddr.addr;
ICMPH_TYPE_SET(iecho, ICMP_ER);
// Adjust the checksum
if (iecho->chksum >= htons(0xFFFF - (ICMP_ECHO << 8))) {
iecho->chksum += htons(ICMP_ECHO << 8) + 1;
} else {
iecho->chksum += htons(ICMP_ECHO << 8);
}
stats.icmp.xmit++;
pbuf_header(p, hlen);
return ip_output_if(p, &iphdr->src, IP_HDRINCL, IPH_TTL(iphdr), IP_PROTO_ICMP, inp);
case ICMP_DUR:
if (p->tot_len < ICMP_HLEN) {
kprintf("icmp_input: ICMP message too short\n");
stats.icmp.lenerr++;
return -EPROTO;
}
idur = (struct icmp_dur_hdr *) p->payload;
code = ICMPH_CODE(idur);
pbuf_header(p, -ICMP_HLEN);
return ip_input_dur(code, p);
default:
kprintf("icmp_input: ICMP type %d not supported\n", type);
stats.icmp.proterr++;
stats.icmp.drop++;
return -EPROTO;
}
pbuf_free(p);
return 0;
}
void icmp_dest_unreach(struct pbuf *p, int t) {
struct pbuf *q;
struct ip_hdr *iphdr;
struct icmp_dur_hdr *idur;
// ICMP header + IP header + 8 bytes of data
q = pbuf_alloc(PBUF_IP, 8 + IP_HLEN + 8, PBUF_RW);
if (!q) return;
iphdr = p->payload;
idur = q->payload;
ICMPH_TYPE_SET(idur, ICMP_DUR);
ICMPH_CODE_SET(idur, t);
memcpy((char *) q->payload + 8, p->payload, IP_HLEN + 8);
// Calculate checksum
idur->chksum = 0;
idur->chksum = inet_chksum(idur, q->len);
stats.icmp.xmit++;
if (ip_output(q, NULL, &iphdr->src, ICMP_TTL, IP_PROTO_ICMP) < 0) pbuf_free(q);
}
void icmp_time_exceeded(struct pbuf *p, int t) {
struct pbuf *q;
struct ip_hdr *iphdr;
struct icmp_te_hdr *tehdr;
q = pbuf_alloc(PBUF_IP, 8 + IP_HLEN + 8, PBUF_RW);
if (!q) return;
iphdr = p->payload;
tehdr = q->payload;
ICMPH_TYPE_SET(tehdr, ICMP_TE);
ICMPH_CODE_SET(tehdr, t);
// Copy fields from original packet
memcpy((char *) q->payload + 8, (char *) p->payload, IP_HLEN + 8);
// Calculate checksum
tehdr->chksum = 0;
tehdr->chksum = inet_chksum(tehdr, q->len);
stats.icmp.xmit++;
if (ip_output(q, NULL, &iphdr->src, ICMP_TTL, IP_PROTO_ICMP) < 0) pbuf_free(q);
}