iPXE
icmpv6.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 #include <string.h>
27 #include <errno.h>
28 #include <byteswap.h>
29 #include <ipxe/in.h>
30 #include <ipxe/iobuf.h>
31 #include <ipxe/tcpip.h>
32 #include <ipxe/ping.h>
33 #include <ipxe/icmpv6.h>
34 
35 /** @file
36  *
37  * ICMPv6 protocol
38  *
39  */
40 
41 /* Disambiguate the various error causes */
42 #define EHOSTUNREACH_ROUTE \
43  __einfo_error ( EINFO_EHOSTUNREACH_ROUTE )
44 #define EINFO_EHOSTUNREACH_ROUTE \
45  __einfo_uniqify ( EINFO_EHOSTUNREACH, 0, \
46  "No route to destination" )
47 #define EHOSTUNREACH_PROHIBITED \
48  __einfo_error ( EINFO_EHOSTUNREACH_PROHIBITED )
49 #define EINFO_EHOSTUNREACH_PROHIBITED \
50  __einfo_uniqify ( EINFO_EHOSTUNREACH, 1, \
51  "Communication administratively prohibited" )
52 #define EHOSTUNREACH_ADDRESS \
53  __einfo_error ( EINFO_EHOSTUNREACH_ADDRESS )
54 #define EINFO_EHOSTUNREACH_ADDRESS \
55  __einfo_uniqify ( EINFO_EHOSTUNREACH, 3, \
56  "Address unreachable" )
57 #define EHOSTUNREACH_PORT \
58  __einfo_error ( EINFO_EHOSTUNREACH_PORT )
59 #define EINFO_EHOSTUNREACH_PORT \
60  __einfo_uniqify ( EINFO_EHOSTUNREACH, 4, \
61  "Port unreachable" )
62 #define EHOSTUNREACH_CODE( code ) \
63  EUNIQ ( EINFO_EHOSTUNREACH, ( (code) & 0x1f ), \
64  EHOSTUNREACH_ROUTE, EHOSTUNREACH_PROHIBITED, \
65  EHOSTUNREACH_ADDRESS, EHOSTUNREACH_PORT )
66 
67 #define ETIMEDOUT_HOP \
68  __einfo_error ( EINFO_ETIMEDOUT_HOP )
69 #define EINFO_ETIMEDOUT_HOP \
70  __einfo_uniqify ( EINFO_ETIMEDOUT, 0, \
71  "Hop limit exceeded in transit" )
72 #define ETIMEDOUT_REASSEMBLY \
73  __einfo_error ( EINFO_ETIMEDOUT_REASSEMBLY )
74 #define EINFO_ETIMEDOUT_REASSEMBLY \
75  __einfo_uniqify ( EINFO_ETIMEDOUT, 1, \
76  "Fragment reassembly time exceeded" )
77 #define ETIMEDOUT_CODE( code ) \
78  EUNIQ ( EINFO_ETIMEDOUT, ( (code) & 0x1f ), \
79  ETIMEDOUT_HOP, ETIMEDOUT_REASSEMBLY )
80 
81 #define EPROTO_BAD_HEADER \
82  __einfo_error ( EINFO_EPROTO_BAD_HEADER )
83 #define EINFO_EPROTO_BAD_HEADER \
84  __einfo_uniqify ( EINFO_EPROTO, 0, \
85  "Erroneous header field" )
86 #define EPROTO_NEXT_HEADER \
87  __einfo_error ( EINFO_EPROTO_NEXT_HEADER )
88 #define EINFO_EPROTO_NEXT_HEADER \
89  __einfo_uniqify ( EINFO_EPROTO, 1, \
90  "Unrecognised next header type" )
91 #define EPROTO_OPTION \
92  __einfo_error ( EINFO_EPROTO_OPTION )
93 #define EINFO_EPROTO_OPTION \
94  __einfo_uniqify ( EINFO_EPROTO, 2, \
95  "Unrecognised IPv6 option" )
96 #define EPROTO_CODE( code ) \
97  EUNIQ ( EINFO_EPROTO, ( (code) & 0x1f ), \
98  EPROTO_BAD_HEADER, EPROTO_NEXT_HEADER, EPROTO_OPTION )
99 
100 struct icmp_echo_protocol icmpv6_echo_protocol __icmp_echo_protocol;
101 
102 /**
103  * Process received ICMPv6 echo request packet
104  *
105  * @v iobuf I/O buffer
106  * @v netdev Network device
107  * @v sin6_src Source socket address
108  * @v sin6_dest Destination socket address
109  * @ret rc Return status code
110  */
111 static int icmpv6_rx_echo_request ( struct io_buffer *iobuf,
112  struct net_device *netdev __unused,
113  struct sockaddr_in6 *sin6_src,
114  struct sockaddr_in6 *sin6_dest __unused ) {
115  struct sockaddr_tcpip *st_src =
116  ( ( struct sockaddr_tcpip * ) sin6_src );
117 
118  return icmp_rx_echo_request ( iobuf, st_src, &icmpv6_echo_protocol );
119 }
120 
121 /** ICMPv6 echo request handler */
122 struct icmpv6_handler icmpv6_echo_request_handler __icmpv6_handler = {
125 };
126 
127 /**
128  * Process received ICMPv6 echo reply packet
129  *
130  * @v iobuf I/O buffer
131  * @v netdev Network device
132  * @v sin6_src Source socket address
133  * @v sin6_dest Destination socket address
134  * @ret rc Return status code
135  */
136 static int icmpv6_rx_echo_reply ( struct io_buffer *iobuf,
137  struct net_device *netdev __unused,
138  struct sockaddr_in6 *sin6_src,
139  struct sockaddr_in6 *sin6_dest __unused ) {
140  struct sockaddr_tcpip *st_src =
141  ( ( struct sockaddr_tcpip * ) sin6_src );
142 
143  return icmp_rx_echo_reply ( iobuf, st_src );
144 }
145 
146 /** ICMPv6 echo reply handler */
147 struct icmpv6_handler icmpv6_echo_reply_handler __icmpv6_handler = {
149  .rx = icmpv6_rx_echo_reply,
150 };
151 
152 /**
153  * Identify ICMPv6 handler
154  *
155  * @v type ICMPv6 type
156  * @ret handler ICMPv6 handler, or NULL if not found
157  */
158 static struct icmpv6_handler * icmpv6_handler ( unsigned int type ) {
159  struct icmpv6_handler *handler;
160 
162  if ( handler->type == type )
163  return handler;
164  }
165  return NULL;
166 }
167 
168 /**
169  * Process a received packet
170  *
171  * @v iobuf I/O buffer
172  * @v netdev Network device
173  * @v st_src Partially-filled source address
174  * @v st_dest Partially-filled destination address
175  * @v pshdr_csum Pseudo-header checksum
176  * @ret rc Return status code
177  */
178 static int icmpv6_rx ( struct io_buffer *iobuf, struct net_device *netdev,
179  struct sockaddr_tcpip *st_src,
180  struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum ) {
181  struct sockaddr_in6 *sin6_src = ( ( struct sockaddr_in6 * ) st_src );
182  struct sockaddr_in6 *sin6_dest = ( ( struct sockaddr_in6 * ) st_dest );
183  struct icmp_header *icmp = iobuf->data;
184  size_t len = iob_len ( iobuf );
185  struct icmpv6_handler *handler;
186  unsigned int csum;
187  int rc;
188 
189  /* Sanity check */
190  if ( len < sizeof ( *icmp ) ) {
191  DBGC ( netdev, "ICMPv6 packet too short at %zd bytes (min %zd "
192  "bytes)\n", len, sizeof ( *icmp ) );
193  rc = -EINVAL;
194  goto done;
195  }
196 
197  /* Verify checksum */
198  csum = tcpip_continue_chksum ( pshdr_csum, icmp, len );
199  if ( csum != 0 ) {
200  DBGC ( netdev, "ICMPv6 checksum incorrect (is %04x, should be "
201  "0000)\n", csum );
202  DBGC_HDA ( netdev, 0, icmp, len );
203  rc = -EINVAL;
204  goto done;
205  }
206 
207  /* Identify handler */
208  handler = icmpv6_handler ( icmp->type );
209  if ( ! handler ) {
210  switch ( icmp->type ) {
212  rc = -EHOSTUNREACH_CODE ( icmp->code );
213  break;
215  rc = -ERANGE;
216  break;
218  rc = -ETIMEDOUT_CODE ( icmp->code );
219  break;
221  rc = -EPROTO_CODE ( icmp->code );
222  break;
223  default:
224  DBGC ( netdev, "ICMPv6 unrecognised type %d code %d\n",
225  icmp->type, icmp->code );
226  rc = -ENOTSUP;
227  break;
228  };
229  goto done;
230  }
231 
232  /* Pass to handler */
233  if ( ( rc = handler->rx ( iob_disown ( iobuf ), netdev, sin6_src,
234  sin6_dest ) ) != 0 ) {
235  DBGC ( netdev, "ICMPv6 could not handle type %d: %s\n",
236  icmp->type, strerror ( rc ) );
237  goto done;
238  }
239 
240  done:
241  free_iob ( iobuf );
242  return rc;
243 }
244 
245 /** ICMPv6 TCP/IP protocol */
246 struct tcpip_protocol icmpv6_protocol __tcpip_protocol = {
247  .name = "ICMPv6",
248  .rx = icmpv6_rx,
249  .tcpip_proto = IP_ICMP6,
250 };
251 
252 /** ICMPv6 echo protocol */
253 struct icmp_echo_protocol icmpv6_echo_protocol __icmp_echo_protocol = {
254  .family = AF_INET6,
255  .request = ICMPV6_ECHO_REQUEST,
256  .reply = ICMPV6_ECHO_REPLY,
257  .tcpip_protocol = &icmpv6_protocol,
258  .net_checksum = 1,
259 };
ICMP ping protocol.
#define ETIMEDOUT_CODE(code)
Definition: icmpv6.c:77
#define EINVAL
Invalid argument.
Definition: errno.h:428
TCP/IP socket address.
Definition: tcpip.h:75
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
int(* rx)(struct io_buffer *iobuf, struct net_device *netdev, struct sockaddr_in6 *sin6_src, struct sockaddr_in6 *sin6_dest)
Process received packet.
Definition: icmpv6.h:32
unsigned short uint16_t
Definition: stdint.h:11
#define AF_INET6
IPv6 Internet addresses.
Definition: socket.h:64
Error codes.
uint8_t code
Code.
Definition: icmp.h:23
#define ICMPV6_PACKET_TOO_BIG
ICMPv6 packet too big.
Definition: icmpv6.h:47
I/O buffers.
void free_iob(struct io_buffer *iobuf)
Free I/O buffer.
Definition: iobuf.c:146
#define ICMPV6_DESTINATION_UNREACHABLE
ICMPv6 destination unreachable.
Definition: icmpv6.h:44
#define DBGC(...)
Definition: compiler.h:505
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)
Process received ICMPv6 echo reply packet.
Definition: icmpv6.c:136
#define EHOSTUNREACH_CODE(code)
Definition: icmpv6.c:62
#define EPROTO_CODE(code)
Definition: icmpv6.c:96
uint8_t type
Type.
Definition: icmp.h:21
#define ENOTSUP
Operation not supported.
Definition: errno.h:589
#define ICMPV6_ECHO_REPLY
ICMPv6 echo reply.
Definition: icmpv6.h:59
#define IP_ICMP6
Definition: in.h:15
#define iob_disown(iobuf)
Disown an I/O buffer.
Definition: iobuf.h:212
struct tcpip_protocol icmpv6_protocol __tcpip_protocol
ICMPv6 TCP/IP protocol.
Definition: icmpv6.c:246
#define DBGC_HDA(...)
Definition: compiler.h:506
static struct net_device * netdev
Definition: gdbudp.c:52
Transport-network layer interface.
static int icmpv6_rx(struct io_buffer *iobuf, struct net_device *netdev, struct sockaddr_tcpip *st_src, struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum)
Process a received packet.
Definition: icmpv6.c:178
struct icmp_echo_protocol icmpv6_echo_protocol __icmp_echo_protocol
ICMPv6 echo protocol.
Definition: icmpv6.c:100
#define ICMPV6_PARAMETER_PROBLEM
ICMPv6 parameter problem.
Definition: icmpv6.h:53
#define ICMPV6_ECHO_REQUEST
ICMPv6 echo request.
Definition: icmpv6.h:56
const char * name
Protocol name.
Definition: tcpip.h:106
#define ERANGE
Result too large.
Definition: errno.h:639
char * strerror(int errno)
Retrieve string representation of error number.
Definition: strerror.c:78
static size_t iob_len(struct io_buffer *iobuf)
Calculate length of data in an I/O buffer.
Definition: iobuf.h:155
#define for_each_table_entry(pointer, table)
Iterate through all entries within a linker table.
Definition: tables.h:385
A network device.
Definition: netdevice.h:352
unsigned int type
Type.
Definition: icmpv6.h:21
static struct icmpv6_handler * icmpv6_handler(unsigned int type)
Identify ICMPv6 handler.
Definition: icmpv6.c:158
A transport-layer protocol of the TCP/IP stack (eg.
Definition: tcpip.h:104
#define __unused
Declare a variable or data structure as unused.
Definition: compiler.h:573
An ICMP echo protocol.
Definition: icmp.h:41
struct icmpv6_handler icmpv6_echo_request_handler __icmpv6_handler
ICMPv6 echo request handler.
Definition: icmpv6.c:122
uint32_t len
Length.
Definition: ena.h:14
ICMPv6 protocol.
uint32_t type
Operating system type.
Definition: ena.h:12
An ICMPv6 handler.
Definition: icmpv6.h:19
void * data
Start of data.
Definition: iobuf.h:48
An ICMP header.
Definition: icmp.h:19
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
IPv6 socket address.
Definition: in.h:115
sa_family_t family
Address family.
Definition: icmp.h:43
int icmp_rx_echo_reply(struct io_buffer *iobuf, struct sockaddr_tcpip *st_src)
Process a received ICMP echo request.
Definition: icmp.c:196
int icmp_rx_echo_request(struct io_buffer *iobuf, struct sockaddr_tcpip *st_src, struct icmp_echo_protocol *echo_protocol)
Process a received ICMP echo request.
Definition: icmp.c:165
#define ICMPV6_HANDLERS
ICMPv6 handler table.
Definition: icmpv6.h:38
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)
Process received ICMPv6 echo request packet.
Definition: icmpv6.c:111
#define NULL
NULL pointer (VOID *)
Definition: Base.h:321
String functions.
#define ICMPV6_TIME_EXCEEDED
ICMPv6 time exceeded.
Definition: icmpv6.h:50
struct bofm_section_header done
Definition: bofm_test.c:46
uint16_t tcpip_continue_chksum(uint16_t partial, const void *data, size_t len)
Calculate continued TCP/IP checkum.
Definition: x86_tcpip.c:45
A persistent I/O buffer.
Definition: iobuf.h:33