static int icmp_send(uint8_t type, uint8_t code, const void *body, size_t body_sz, struct sk_buff *skb) { int ret; size_t size; if (ip_out_ops == NULL) { skb_free(skb); return -ENOSYS; /* error: not implemented */ } size = sizeof *icmp_hdr(skb) + body_sz; assert(ip_out_ops->make_pack != NULL); ret = ip_out_ops->make_pack(NULL, NULL, &size, &skb); if (ret != 0) { skb_free(skb); return ret; /* error: see ret */ } else if (size != sizeof *icmp_hdr(skb) + body_sz) { skb_free(skb); return -EMSGSIZE; /* error: message is too big */ } icmp_build(icmp_hdr(skb), type, code, body, body_sz); icmp_set_check_field(icmp_hdr(skb), ip_hdr(skb)); assert(ip_out_ops->snd_pack != NULL); return ip_out_ops->snd_pack(skb); }
static int icmp_rcv(struct sk_buff *skb) { struct icmphdr *icmph; uint16_t old_check; log_debug("%p len %zu", skb, skb->len); if (sizeof *icmph > ip_data_length(ip_hdr(skb))) { log_error("invalid length (%zu > %zu)", sizeof *icmph, ip_data_length(ip_hdr(skb))); skb_free(skb); return 0; /* error: invalid length */ } if (NULL == skb_declone(skb)) { log_error("can't declone data"); skb_free(skb); return -ENOMEM; /* error: can't declone data */ } icmph = icmp_hdr(skb); assert(icmph != NULL); old_check = icmph->check; icmp_set_check_field(icmph, ip_hdr(skb)); if (old_check != icmph->check) { log_error("bad checksum"); skb_free(skb); return 0; /* error: bad checksum */ } switch (icmph->type) { default: log_error("icmp_rcv: unknown type: %hhu\n", icmph->type); break; /* error: unknown type */ case ICMP_ECHO_REPLY: case ICMP_TIMESTAMP_REPLY: case ICMP_INFO_REPLY: break; case ICMP_DEST_UNREACH: return icmp_hnd_dest_unreach(icmph, &icmph->body[0].dest_unreach, skb); case ICMP_SOURCE_QUENCH: return icmp_hnd_source_quench(icmph, &icmph->body[0].source_quench, skb); case ICMP_REDIRECT: case ICMP_TIME_EXCEED: case ICMP_INFO_REQUEST: break; /* error: not implemented */ case ICMP_ECHO_REQUEST: return icmp_hnd_echo_request(icmph, &icmph->body[0].echo, skb); case ICMP_PARAM_PROB: return icmp_hnd_param_prob(icmph, &icmph->body[0].param_prob, skb); case ICMP_TIMESTAMP_REQUEST: return icmp_hnd_timestamp_request(icmph, &icmph->body[0].timestamp, skb); } skb_free(skb); return 0; }