grub/grub-core/net/icmp.c
Vladimir Serbinenko 63034d3261 arp, icmp: Fix handling in case of oversized or invalid packets.
This restrict ARP handling to MAC and IP addresses but in practice we need
only this case anyway and other cases are very rar if exist at all. It makes
code much simpler and less error-prone.
2015-03-27 12:18:25 +01:00

112 lines
2.7 KiB
C

/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2010,2011 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/net.h>
#include <grub/net/ip.h>
#include <grub/net/netbuff.h>
struct icmp_header
{
grub_uint8_t type;
grub_uint8_t code;
grub_uint16_t checksum;
} GRUB_PACKED;
struct ping_header
{
grub_uint16_t id;
grub_uint16_t seq;
} GRUB_PACKED;
enum
{
ICMP_ECHO_REPLY = 0,
ICMP_ECHO = 8,
};
grub_err_t
grub_net_recv_icmp_packet (struct grub_net_buff *nb,
struct grub_net_network_level_interface *inf,
const grub_net_link_level_address_t *ll_src,
const grub_net_network_level_address_t *src)
{
struct icmp_header *icmph;
grub_err_t err;
grub_uint16_t checksum;
/* Ignore broadcast. */
if (!inf)
{
grub_netbuff_free (nb);
return GRUB_ERR_NONE;
}
icmph = (struct icmp_header *) nb->data;
if (nb->tail - nb->data < (grub_ssize_t) sizeof (*icmph))
{
grub_netbuff_free (nb);
return GRUB_ERR_NONE;
}
checksum = icmph->checksum;
icmph->checksum = 0;
if (checksum != grub_net_ip_chksum (nb->data, nb->tail - nb->data))
{
icmph->checksum = checksum;
return GRUB_ERR_NONE;
}
icmph->checksum = checksum;
err = grub_netbuff_pull (nb, sizeof (*icmph));
if (err)
return err;
switch (icmph->type)
{
case ICMP_ECHO:
{
struct grub_net_buff *nb_reply;
struct icmp_header *icmphr;
if (icmph->code)
break;
nb_reply = grub_netbuff_make_pkt (nb->tail - nb->data + sizeof (*icmphr));
if (!nb_reply)
{
grub_netbuff_free (nb);
return grub_errno;
}
grub_memcpy (nb_reply->data + sizeof (*icmphr), nb->data, nb->tail - nb->data);
icmphr = (struct icmp_header *) nb_reply->data;
icmphr->type = ICMP_ECHO_REPLY;
icmphr->code = 0;
icmphr->checksum = 0;
icmphr->checksum = grub_net_ip_chksum ((void *) nb_reply->data,
nb_reply->tail - nb_reply->data);
err = grub_net_send_ip_packet (inf, src, ll_src,
nb_reply, GRUB_NET_IP_ICMP);
grub_netbuff_free (nb);
grub_netbuff_free (nb_reply);
return err;
}
};
grub_netbuff_free (nb);
return GRUB_ERR_NONE;
}