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 <byteswap.h>
28 #include <errno.h>
29 #include <ipxe/iobuf.h>
30 #include <ipxe/in.h>
31 #include <ipxe/tcpip.h>
32 #include <ipxe/ping.h>
33 #include <ipxe/crc32.h>
34 #include <ipxe/icmp.h>
35 
36 /** @file
37  *
38  * ICMP protocol
39  *
40  */
41 
42 /**
43  * Identify ICMP echo protocol
44  *
45  * @v st_family		Address family
46  * @ret echo_protocol	ICMP echo protocol, or NULL
47  */
icmp_echo_protocol(sa_family_t family)48 static struct icmp_echo_protocol * icmp_echo_protocol ( sa_family_t family ) {
49 	struct icmp_echo_protocol *echo_protocol;
50 
51 	for_each_table_entry ( echo_protocol, ICMP_ECHO_PROTOCOLS ) {
52 		if ( echo_protocol->family == family )
53 			return echo_protocol;
54 	}
55 	return NULL;
56 }
57 
58 /**
59  *
60  * Determine debugging colour for ICMP debug messages
61  *
62  * @v st_peer		Peer address
63  * @ret col		Debugging colour (for DBGC())
64  */
icmpcol(struct sockaddr_tcpip * st_peer)65 static uint32_t icmpcol ( struct sockaddr_tcpip *st_peer ) {
66 
67 	return crc32_le ( 0, st_peer, sizeof ( *st_peer ) );
68 }
69 
70 /**
71  * Transmit ICMP echo packet
72  *
73  * @v iobuf		I/O buffer
74  * @v st_dest		Destination socket address
75  * @v echo_protocol	ICMP echo protocol
76  * @ret rc		Return status code
77  */
icmp_tx_echo(struct io_buffer * iobuf,struct sockaddr_tcpip * st_dest,struct icmp_echo_protocol * echo_protocol)78 static int icmp_tx_echo ( struct io_buffer *iobuf,
79 			  struct sockaddr_tcpip *st_dest,
80 			  struct icmp_echo_protocol *echo_protocol ) {
81 	struct icmp_echo *echo = iobuf->data;
82 	int rc;
83 
84 	/* Set ICMP type and (re)calculate checksum */
85 	echo->icmp.chksum = 0;
86 	echo->icmp.chksum = tcpip_chksum ( echo, iob_len ( iobuf ) );
87 
88 	/* Transmit packet */
89 	if ( ( rc = tcpip_tx ( iobuf, echo_protocol->tcpip_protocol, NULL,
90 			       st_dest, NULL,
91 			       ( echo_protocol->net_checksum ?
92 				 &echo->icmp.chksum : NULL ) ) ) != 0 )
93 		return rc;
94 
95 	return 0;
96 }
97 
98 /**
99  * Transmit ICMP echo request
100  *
101  * @v iobuf		I/O buffer
102  * @v st_dest		Destination socket address
103  * @ret rc		Return status code
104  */
icmp_tx_echo_request(struct io_buffer * iobuf,struct sockaddr_tcpip * st_dest)105 int icmp_tx_echo_request ( struct io_buffer *iobuf,
106 			   struct sockaddr_tcpip *st_dest ) {
107 	struct icmp_echo *echo = iobuf->data;
108 	struct icmp_echo_protocol *echo_protocol;
109 	int rc;
110 
111 	/* Identify ICMP echo protocol */
112 	echo_protocol = icmp_echo_protocol ( st_dest->st_family );
113 	if ( ! echo_protocol ) {
114 		DBGC ( icmpcol ( st_dest ), "ICMP TX echo request unknown "
115 		       "address family %d\n", st_dest->st_family );
116 		free_iob ( iobuf );
117 		return -ENOTSUP;
118 	}
119 
120 	/* Set type */
121 	echo->icmp.type = echo_protocol->request;
122 
123 	/* Transmit request */
124 	DBGC ( icmpcol ( st_dest ), "ICMP TX echo request id %04x seq %04x\n",
125 	       ntohs ( echo->ident ), ntohs ( echo->sequence ) );
126 	if ( ( rc = icmp_tx_echo ( iobuf, st_dest, echo_protocol ) ) != 0 )
127 		return rc;
128 
129 	return 0;
130 }
131 
132 /**
133  * Transmit ICMP echo reply
134  *
135  * @v iobuf		I/O buffer
136  * @v st_dest		Destination socket address
137  * @ret rc		Return status code
138  */
icmp_tx_echo_reply(struct io_buffer * iobuf,struct sockaddr_tcpip * st_dest,struct icmp_echo_protocol * echo_protocol)139 static int icmp_tx_echo_reply ( struct io_buffer *iobuf,
140 				struct sockaddr_tcpip *st_dest,
141 				struct icmp_echo_protocol *echo_protocol ) {
142 	struct icmp_echo *echo = iobuf->data;
143 	int rc;
144 
145 	/* Set type */
146 	echo->icmp.type = echo_protocol->reply;
147 
148 	/* Transmit reply */
149 	DBGC ( icmpcol ( st_dest ), "ICMP TX echo reply id %04x seq %04x\n",
150 	       ntohs ( echo->ident ), ntohs ( echo->sequence ) );
151 	if ( ( rc = icmp_tx_echo ( iobuf, st_dest, echo_protocol ) ) != 0 )
152 		return rc;
153 
154 	return 0;
155 }
156 
157 /**
158  * Process a received ICMP echo request
159  *
160  * @v iobuf		I/O buffer
161  * @v st_src		Source socket address
162  * @v echo_protocol	ICMP echo protocol
163  * @ret rc		Return status code
164  */
icmp_rx_echo_request(struct io_buffer * iobuf,struct sockaddr_tcpip * st_src,struct icmp_echo_protocol * echo_protocol)165 int icmp_rx_echo_request ( struct io_buffer *iobuf,
166 			   struct sockaddr_tcpip *st_src,
167 			   struct icmp_echo_protocol *echo_protocol ) {
168 	struct icmp_echo *echo = iobuf->data;
169 	int rc;
170 
171 	/* Sanity check */
172 	if ( iob_len ( iobuf ) < sizeof ( *echo ) ) {
173 		DBGC ( icmpcol ( st_src ), "ICMP RX echo request too short at "
174 		       "%zd bytes (min %zd bytes)\n",
175 		       iob_len ( iobuf ), sizeof ( *echo ) );
176 		free_iob ( iobuf );
177 		return -EINVAL;
178 	}
179 	DBGC ( icmpcol ( st_src ), "ICMP RX echo request id %04x seq %04x\n",
180 	       ntohs ( echo->ident ), ntohs ( echo->sequence ) );
181 
182 	/* Transmit echo reply */
183 	if ( ( rc = icmp_tx_echo_reply ( iobuf, st_src, echo_protocol ) ) != 0 )
184 		return rc;
185 
186 	return 0;
187 }
188 
189 /**
190  * Process a received ICMP echo request
191  *
192  * @v iobuf		I/O buffer
193  * @v st_src		Source socket address
194  * @ret rc		Return status code
195  */
icmp_rx_echo_reply(struct io_buffer * iobuf,struct sockaddr_tcpip * st_src)196 int icmp_rx_echo_reply ( struct io_buffer *iobuf,
197 			 struct sockaddr_tcpip *st_src ) {
198 	struct icmp_echo *echo = iobuf->data;
199 	int rc;
200 
201 	/* Sanity check */
202 	if ( iob_len ( iobuf ) < sizeof ( *echo ) ) {
203 		DBGC ( icmpcol ( st_src ), "ICMP RX echo reply too short at "
204 		       "%zd bytes (min %zd bytes)\n",
205 		       iob_len ( iobuf ), sizeof ( *echo ) );
206 		free_iob ( iobuf );
207 		return -EINVAL;
208 	}
209 	DBGC ( icmpcol ( st_src ), "ICMP RX echo reply id %04x seq %04x\n",
210 	       ntohs ( echo->ident ), ntohs ( echo->sequence ) );
211 
212 	/* Deliver to ping protocol */
213 	if ( ( rc = ping_rx ( iobuf, st_src ) ) != 0 )
214 		return rc;
215 
216 	return 0;
217 }
218 
219 /**
220  * Receive ping reply (when no ping protocol is present)
221  *
222  * @v iobuf		I/O buffer
223  * @v st_src		Source socket address
224  * @ret rc		Return status code
225  */
ping_rx(struct io_buffer * iobuf,struct sockaddr_tcpip * st_src __unused)226 __weak int ping_rx ( struct io_buffer *iobuf,
227 		     struct sockaddr_tcpip *st_src __unused ) {
228 	free_iob ( iobuf );
229 	return 0;
230 }
231