diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index c55107a9..376f88c2 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -184,7 +184,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_slam ( ERRFILE_NET | 0x00160000 ) #define ERRFILE_ib_sma ( ERRFILE_NET | 0x00170000 ) #define ERRFILE_ib_packet ( ERRFILE_NET | 0x00180000 ) -#define ERRFILE_icmp ( ERRFILE_NET | 0x00190000 ) +#define ERRFILE_icmpv4 ( ERRFILE_NET | 0x00190000 ) #define ERRFILE_ib_qset ( ERRFILE_NET | 0x001a0000 ) #define ERRFILE_ib_gma ( ERRFILE_NET | 0x001b0000 ) #define ERRFILE_ib_pathrec ( ERRFILE_NET | 0x001c0000 ) @@ -216,6 +216,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_oncrpc_iob ( ERRFILE_NET | 0x00360000 ) #define ERRFILE_neighbour ( ERRFILE_NET | 0x00370000 ) #define ERRFILE_socket ( ERRFILE_NET | 0x00380000 ) +#define ERRFILE_icmp ( ERRFILE_NET | 0x00390000 ) #define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 ) diff --git a/src/include/ipxe/icmp.h b/src/include/ipxe/icmp.h index e402ce40..0480ddfa 100644 --- a/src/include/ipxe/icmp.h +++ b/src/include/ipxe/icmp.h @@ -9,6 +9,12 @@ FILE_LICENCE ( GPL2_OR_LATER ); +#include +#include +#include +#include +#include + /** An ICMP header */ struct icmp_header { /** Type */ @@ -19,7 +25,49 @@ struct icmp_header { uint16_t chksum; } __attribute__ (( packed )); -#define ICMP_ECHO_RESPONSE 0 +/** An ICMP echo request/reply */ +struct icmp_echo { + /** ICMPv6 header */ + struct icmp_header icmp; + /** Identifier */ + uint16_t ident; + /** Sequence number */ + uint16_t sequence; + /** Data */ + uint8_t data[0]; +} __attribute__ (( packed )); + +/** An ICMP echo protocol */ +struct icmp_echo_protocol { + /** Address family */ + sa_family_t family; + /** Request type */ + uint8_t request; + /** Reply type */ + uint8_t reply; + /** TCP/IP protocol */ + struct tcpip_protocol *tcpip_protocol; + /** Include network-layer checksum within packet */ + int net_checksum; +}; + +/** ICMP echo protocol table */ +#define ICMP_ECHO_PROTOCOLS \ + __table ( struct icmp_echo_protocol, "icmp_echo_protocols" ) + +/** Declare an ICMP echo protocol */ +#define __icmp_echo_protocol __table_entry ( ICMP_ECHO_PROTOCOLS, 01 ) + +#define ICMP_ECHO_REPLY 0 #define ICMP_ECHO_REQUEST 8 +extern int icmp_tx_echo_request ( struct io_buffer *iobuf, + struct sockaddr_tcpip *st_dest ); + +extern int icmp_rx_echo_request ( struct io_buffer *iobuf, + struct sockaddr_tcpip *st_src, + struct icmp_echo_protocol *echo_protocol ); +extern int icmp_rx_echo_reply ( struct io_buffer *iobuf, + struct sockaddr_tcpip *st_src ); + #endif /* _IPXE_ICMP_H */ diff --git a/src/include/ipxe/icmpv6.h b/src/include/ipxe/icmpv6.h index c8f0be05..c09a71a0 100644 --- a/src/include/ipxe/icmpv6.h +++ b/src/include/ipxe/icmpv6.h @@ -13,28 +13,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include - -/** An ICMPv6 header */ -struct icmpv6_header { - /** Type */ - uint8_t type; - /** Code */ - uint8_t code; - /** Checksum */ - uint16_t chksum; -} __attribute__ (( packed )); - -/** An ICMPv6 echo request/reply */ -struct icmpv6_echo { - /** ICMPv6 header */ - struct icmpv6_header icmp; - /** Identifier */ - uint16_t ident; - /** Sequence number */ - uint16_t sequence; - /** Data */ - uint8_t data[0]; -} __attribute__ (( packed )); +#include /** An ICMPv6 handler */ struct icmpv6_handler { diff --git a/src/include/ipxe/ndp.h b/src/include/ipxe/ndp.h index 7b98637f..1f05e9ab 100644 --- a/src/include/ipxe/ndp.h +++ b/src/include/ipxe/ndp.h @@ -31,7 +31,7 @@ struct ndp_option { /** An NDP header */ struct ndp_header { /** ICMPv6 header */ - struct icmpv6_header icmp; + struct icmp_header icmp; /** Flags */ uint8_t flags; /** Reserved */ diff --git a/src/include/ipxe/ping.h b/src/include/ipxe/ping.h new file mode 100644 index 00000000..6cd376b6 --- /dev/null +++ b/src/include/ipxe/ping.h @@ -0,0 +1,18 @@ +#ifndef _IPXE_PING_H +#define _IPXE_PING_H + +/** @file + * + * ICMP ping protocol + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include + +extern int ping_rx ( struct io_buffer *iobuf, + struct sockaddr_tcpip *st_src ); + +#endif /* _IPXE_PING_H */ diff --git a/src/net/icmp.c b/src/net/icmp.c index 6142b748..1bbf8bd3 100644 --- a/src/net/icmp.c +++ b/src/net/icmp.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Michael Brown . + * Copyright (C) 2013 Michael Brown . * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -20,10 +20,13 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include +#include #include #include #include #include +#include +#include #include /** @file @@ -32,76 +35,192 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ -struct tcpip_protocol icmp_protocol __tcpip_protocol; +/** + * Identify ICMP echo protocol + * + * @v st_family Address family + * @ret echo_protocol ICMP echo protocol, or NULL + */ +static struct icmp_echo_protocol * icmp_echo_protocol ( sa_family_t family ) { + struct icmp_echo_protocol *echo_protocol; + + for_each_table_entry ( echo_protocol, ICMP_ECHO_PROTOCOLS ) { + if ( echo_protocol->family == family ) + return echo_protocol; + } + return NULL; +} /** - * Process a received packet + * + * Determine debugging colour for ICMP debug messages + * + * @v st_peer Peer address + * @ret col Debugging colour (for DBGC()) + */ +static uint32_t icmpcol ( struct sockaddr_tcpip *st_peer ) { + + return crc32_le ( 0, st_peer, sizeof ( *st_peer ) ); +} + +/** + * Transmit ICMP echo packet * * @v iobuf I/O buffer - * @v netdev Network device - * @v st_src Partially-filled source address - * @v st_dest Partially-filled destination address - * @v pshdr_csum Pseudo-header checksum + * @v st_dest Destination socket address + * @v echo_protocol ICMP echo protocol * @ret rc Return status code */ -static int icmp_rx ( struct io_buffer *iobuf, - struct net_device *netdev __unused, - struct sockaddr_tcpip *st_src, - struct sockaddr_tcpip *st_dest, - uint16_t pshdr_csum __unused ) { - struct icmp_header *icmp = iobuf->data; - size_t len = iob_len ( iobuf ); - unsigned int csum; +static int icmp_tx_echo ( struct io_buffer *iobuf, + struct sockaddr_tcpip *st_dest, + struct icmp_echo_protocol *echo_protocol ) { + struct icmp_echo *echo = iobuf->data; + int rc; + + /* Set ICMP type and (re)calculate checksum */ + echo->icmp.chksum = 0; + echo->icmp.chksum = tcpip_chksum ( echo, iob_len ( iobuf ) ); + + /* Transmit packet */ + if ( ( rc = tcpip_tx ( iobuf, echo_protocol->tcpip_protocol, NULL, + st_dest, NULL, + ( echo_protocol->net_checksum ? + &echo->icmp.chksum : NULL ) ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Transmit ICMP echo request + * + * @v iobuf I/O buffer + * @v st_dest Destination socket address + * @ret rc Return status code + */ +int icmp_tx_echo_request ( struct io_buffer *iobuf, + struct sockaddr_tcpip *st_dest ) { + struct icmp_echo *echo = iobuf->data; + struct icmp_echo_protocol *echo_protocol; + int rc; + + /* Identify ICMP echo protocol */ + echo_protocol = icmp_echo_protocol ( st_dest->st_family ); + if ( ! echo_protocol ) { + DBGC ( icmpcol ( st_dest ), "ICMP TX echo request unknown " + "address family %d\n", st_dest->st_family ); + free_iob ( iobuf ); + return -ENOTSUP; + } + + /* Set type */ + echo->icmp.type = echo_protocol->request; + + /* Transmit request */ + DBGC ( icmpcol ( st_dest ), "ICMP TX echo request id %04x seq %04x\n", + ntohs ( echo->ident ), ntohs ( echo->sequence ) ); + if ( ( rc = icmp_tx_echo ( iobuf, st_dest, echo_protocol ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Transmit ICMP echo reply + * + * @v iobuf I/O buffer + * @v st_dest Destination socket address + * @ret rc Return status code + */ +static int icmp_tx_echo_reply ( struct io_buffer *iobuf, + struct sockaddr_tcpip *st_dest, + struct icmp_echo_protocol *echo_protocol ) { + struct icmp_echo *echo = iobuf->data; + int rc; + + /* Set type */ + echo->icmp.type = echo_protocol->reply; + + /* Transmit reply */ + DBGC ( icmpcol ( st_dest ), "ICMP TX echo reply id %04x seq %04x\n", + ntohs ( echo->ident ), ntohs ( echo->sequence ) ); + if ( ( rc = icmp_tx_echo ( iobuf, st_dest, echo_protocol ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Process a received ICMP echo request + * + * @v iobuf I/O buffer + * @v st_src Source socket address + * @v echo_protocol ICMP echo protocol + * @ret rc Return status code + */ +int icmp_rx_echo_request ( struct io_buffer *iobuf, + struct sockaddr_tcpip *st_src, + struct icmp_echo_protocol *echo_protocol ) { + struct icmp_echo *echo = iobuf->data; int rc; /* Sanity check */ - if ( len < sizeof ( *icmp ) ) { - DBG ( "ICMP packet too short at %zd bytes (min %zd bytes)\n", - len, sizeof ( *icmp ) ); - rc = -EINVAL; - goto done; + if ( iob_len ( iobuf ) < sizeof ( *echo ) ) { + DBGC ( icmpcol ( st_src ), "ICMP RX echo request too short at " + "%zd bytes (min %zd bytes)\n", + iob_len ( iobuf ), sizeof ( *echo ) ); + free_iob ( iobuf ); + return -EINVAL; } + DBGC ( icmpcol ( st_src ), "ICMP RX echo request id %04x seq %04x\n", + ntohs ( echo->ident ), ntohs ( echo->sequence ) ); - /* Verify checksum */ - csum = tcpip_chksum ( icmp, len ); - if ( csum != 0 ) { - DBG ( "ICMP checksum incorrect (is %04x, should be 0000)\n", - csum ); - DBG_HD ( icmp, len ); - rc = -EINVAL; - goto done; - } + /* Transmit echo reply */ + if ( ( rc = icmp_tx_echo_reply ( iobuf, st_src, echo_protocol ) ) != 0 ) + return rc; - /* We respond only to pings */ - if ( icmp->type != ICMP_ECHO_REQUEST ) { - DBG ( "ICMP ignoring type %d\n", icmp->type ); - rc = 0; - goto done; - } - - DBG ( "ICMP responding to ping\n" ); - - /* Change type to response and recalculate checksum */ - icmp->type = ICMP_ECHO_RESPONSE; - icmp->chksum = 0; - icmp->chksum = tcpip_chksum ( icmp, len ); - - /* Transmit the response */ - if ( ( rc = tcpip_tx ( iob_disown ( iobuf ), &icmp_protocol, st_dest, - st_src, NULL, NULL ) ) != 0 ) { - DBG ( "ICMP could not transmit ping response: %s\n", - strerror ( rc ) ); - goto done; - } - - done: - free_iob ( iobuf ); - return rc; + return 0; } -/** ICMP TCP/IP protocol */ -struct tcpip_protocol icmp_protocol __tcpip_protocol = { - .name = "ICMP", - .rx = icmp_rx, - .tcpip_proto = IP_ICMP, -}; +/** + * Process a received ICMP echo request + * + * @v iobuf I/O buffer + * @v st_src Source socket address + * @ret rc Return status code + */ +int icmp_rx_echo_reply ( struct io_buffer *iobuf, + struct sockaddr_tcpip *st_src ) { + struct icmp_echo *echo = iobuf->data; + int rc; + + /* Sanity check */ + if ( iob_len ( iobuf ) < sizeof ( *echo ) ) { + DBGC ( icmpcol ( st_src ), "ICMP RX echo reply too short at " + "%zd bytes (min %zd bytes)\n", + iob_len ( iobuf ), sizeof ( *echo ) ); + free_iob ( iobuf ); + return -EINVAL; + } + DBGC ( icmpcol ( st_src ), "ICMP RX echo reply id %04x seq %04x\n", + ntohs ( echo->ident ), ntohs ( echo->sequence ) ); + + /* Deliver to ping protocol */ + if ( ( rc = ping_rx ( iobuf, st_src ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Receive ping reply (when no ping protocol is present) + * + * @v iobuf I/O buffer + * @v st_src Source socket address + * @ret rc Return status code + */ +__weak int ping_rx ( struct io_buffer *iobuf, + struct sockaddr_tcpip *st_src __unused ) { + free_iob ( iobuf ); + return 0; +} diff --git a/src/net/icmpv4.c b/src/net/icmpv4.c new file mode 100644 index 00000000..996ba149 --- /dev/null +++ b/src/net/icmpv4.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2013 Michael Brown . + * + * This program 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 2 of the + * License, or any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include + +/** @file + * + * ICMPv4 protocol + * + */ + +struct icmp_echo_protocol icmpv4_echo_protocol __icmp_echo_protocol; + +/** + * Process a received packet + * + * @v iobuf I/O buffer + * @v netdev Network device + * @v st_src Partially-filled source address + * @v st_dest Partially-filled destination address + * @v pshdr_csum Pseudo-header checksum + * @ret rc Return status code + */ +static int icmpv4_rx ( struct io_buffer *iobuf, + struct net_device *netdev __unused, + struct sockaddr_tcpip *st_src, + struct sockaddr_tcpip *st_dest __unused, + uint16_t pshdr_csum __unused ) { + struct icmp_header *icmp = iobuf->data; + size_t len = iob_len ( iobuf ); + unsigned int csum; + unsigned int type; + int rc; + + /* Sanity check */ + if ( len < sizeof ( *icmp ) ) { + DBG ( "ICMP packet too short at %zd bytes (min %zd bytes)\n", + len, sizeof ( *icmp ) ); + rc = -EINVAL; + goto discard; + } + + /* Verify checksum */ + csum = tcpip_chksum ( icmp, len ); + if ( csum != 0 ) { + DBG ( "ICMP checksum incorrect (is %04x, should be 0000)\n", + csum ); + DBG_HD ( icmp, len ); + rc = -EINVAL; + goto discard; + } + + /* Handle ICMP packet */ + type = icmp->type; + switch ( type ) { + case ICMP_ECHO_REQUEST: + return icmp_rx_echo_request ( iobuf, st_src, + &icmpv4_echo_protocol ); + case ICMP_ECHO_REPLY: + return icmp_rx_echo_reply ( iobuf, st_src ); + default: + DBG ( "ICMP ignoring type %d\n", type ); + rc = 0; + break; + } + + discard: + free_iob ( iobuf ); + return rc; +} + +/** ICMPv4 TCP/IP protocol */ +struct tcpip_protocol icmpv4_protocol __tcpip_protocol = { + .name = "ICMPv4", + .rx = icmpv4_rx, + .tcpip_proto = IP_ICMP, +}; + +/** ICMPv4 echo protocol */ +struct icmp_echo_protocol icmpv4_echo_protocol __icmp_echo_protocol = { + .family = AF_INET, + .request = ICMP_ECHO_REQUEST, + .reply = ICMP_ECHO_REPLY, + .tcpip_protocol = &icmpv4_protocol, + .net_checksum = 0, +}; diff --git a/src/net/icmpv6.c b/src/net/icmpv6.c index 54426be8..479800e7 100644 --- a/src/net/icmpv6.c +++ b/src/net/icmpv6.c @@ -25,6 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include /** @file @@ -33,6 +34,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +struct icmp_echo_protocol icmpv6_echo_protocol __icmp_echo_protocol; + /** * Process received ICMPv6 echo request packet * @@ -42,50 +45,45 @@ FILE_LICENCE ( GPL2_OR_LATER ); * @v sin6_dest Destination socket address * @ret rc Return status code */ -static int icmpv6_rx_echo ( struct io_buffer *iobuf, - struct net_device *netdev, - struct sockaddr_in6 *sin6_src, - struct sockaddr_in6 *sin6_dest __unused ) { +static int icmpv6_rx_echo_request ( struct io_buffer *iobuf, + struct net_device *netdev __unused, + struct sockaddr_in6 *sin6_src, + struct sockaddr_in6 *sin6_dest __unused ) { struct sockaddr_tcpip *st_src = ( ( struct sockaddr_tcpip * ) sin6_src ); - struct icmpv6_echo *echo = iobuf->data; - size_t len = iob_len ( iobuf ); - int rc; - /* Sanity check */ - if ( iob_len ( iobuf ) < sizeof ( *echo ) ) { - DBGC ( netdev, "ICMPv6 echo request too short at %zd bytes " - "(min %zd bytes)\n", iob_len ( iobuf ), - sizeof ( *echo ) ); - rc = -EINVAL; - goto done; - } - DBGC ( netdev, "ICMPv6 echo request from %s (id %#04x seq %#04x)\n", - inet6_ntoa ( &sin6_dest->sin6_addr ), ntohs ( echo->ident ), - ntohs ( echo->sequence ) ); - - /* Convert echo request to echo reply and recalculate checksum */ - echo->icmp.type = ICMPV6_ECHO_REPLY; - echo->icmp.chksum = 0; - echo->icmp.chksum = tcpip_chksum ( echo, len ); - - /* Transmit echo reply */ - if ( ( rc = tcpip_tx ( iob_disown ( iobuf ), &icmpv6_protocol, NULL, - st_src, netdev, &echo->icmp.chksum ) ) != 0 ) { - DBGC ( netdev, "ICMPv6 could not transmit reply: %s\n", - strerror ( rc ) ); - goto done; - } - - done: - free_iob ( iobuf ); - return rc; + return icmp_rx_echo_request ( iobuf, st_src, &icmpv6_echo_protocol ); } -/** ICMPv6 echo request handlers */ -struct icmpv6_handler icmpv6_echo_handler __icmpv6_handler = { +/** ICMPv6 echo request handler */ +struct icmpv6_handler icmpv6_echo_request_handler __icmpv6_handler = { .type = ICMPV6_ECHO_REQUEST, - .rx = icmpv6_rx_echo, + .rx = icmpv6_rx_echo_request, +}; + +/** + * Process received ICMPv6 echo reply packet + * + * @v iobuf I/O buffer + * @v netdev Network device + * @v sin6_src Source socket address + * @v sin6_dest Destination socket address + * @ret rc Return status code + */ +static int icmpv6_rx_echo_reply ( struct io_buffer *iobuf, + struct net_device *netdev __unused, + struct sockaddr_in6 *sin6_src, + struct sockaddr_in6 *sin6_dest __unused ) { + struct sockaddr_tcpip *st_src = + ( ( struct sockaddr_tcpip * ) sin6_src ); + + return icmp_rx_echo_reply ( iobuf, st_src ); +} + +/** ICMPv6 echo reply handler */ +struct icmpv6_handler icmpv6_echo_reply_handler __icmpv6_handler = { + .type = ICMPV6_ECHO_REPLY, + .rx = icmpv6_rx_echo_reply, }; /** @@ -119,7 +117,7 @@ static int icmpv6_rx ( struct io_buffer *iobuf, struct net_device *netdev, struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum ) { struct sockaddr_in6 *sin6_src = ( ( struct sockaddr_in6 * ) st_src ); struct sockaddr_in6 *sin6_dest = ( ( struct sockaddr_in6 * ) st_dest ); - struct icmpv6_header *icmp = iobuf->data; + struct icmp_header *icmp = iobuf->data; size_t len = iob_len ( iobuf ); struct icmpv6_handler *handler; unsigned int csum; @@ -170,3 +168,12 @@ struct tcpip_protocol icmpv6_protocol __tcpip_protocol = { .rx = icmpv6_rx, .tcpip_proto = IP_ICMP6, }; + +/** ICMPv6 echo protocol */ +struct icmp_echo_protocol icmpv6_echo_protocol __icmp_echo_protocol = { + .family = AF_INET6, + .request = ICMPV6_ECHO_REQUEST, + .reply = ICMPV6_ECHO_REPLY, + .tcpip_protocol = &icmpv6_protocol, + .net_checksum = 1, +}; diff --git a/src/net/ipv4.c b/src/net/ipv4.c index 7956d18b..438fe9ac 100644 --- a/src/net/ipv4.c +++ b/src/net/ipv4.c @@ -669,5 +669,5 @@ struct settings_applicator ipv4_settings_applicator __settings_applicator = { .apply = ipv4_create_routes, }; -/* Drag in ICMP */ -REQUIRE_OBJECT ( icmp ); +/* Drag in ICMPv4 */ +REQUIRE_OBJECT ( icmpv4 );