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 <stdlib.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <byteswap.h>
30 #include <ipxe/refcnt.h>
31 #include <ipxe/list.h>
32 #include <ipxe/iobuf.h>
33 #include <ipxe/tcpip.h>
34 #include <ipxe/icmp.h>
35 #include <ipxe/interface.h>
36 #include <ipxe/xfer.h>
37 #include <ipxe/open.h>
38 #include <ipxe/netdevice.h>
39 #include <ipxe/ping.h>
40 
41 /** @file
42  *
43  * ICMP ping protocol
44  *
45  */
46 
47 /**
48  * A ping connection
49  *
50  */
51 struct ping_connection {
52 	/** Reference counter */
53 	struct refcnt refcnt;
54 	/** List of ping connections */
55 	struct list_head list;
56 
57 	/** Remote socket address */
58 	struct sockaddr_tcpip peer;
59 	/** Local port number */
60 	uint16_t port;
61 
62 	/** Data transfer interface */
63 	struct interface xfer;
64 };
65 
66 /** List of registered ping connections */
67 static LIST_HEAD ( ping_conns );
68 
69 /**
70  * Identify ping connection by local port number
71  *
72  * @v port		Local port number
73  * @ret ping		Ping connection, or NULL
74  */
ping_demux(unsigned int port)75 static struct ping_connection * ping_demux ( unsigned int port ) {
76 	struct ping_connection *ping;
77 
78 	list_for_each_entry ( ping, &ping_conns, list ) {
79 		if ( ping->port == port )
80 			return ping;
81 	}
82 	return NULL;
83 }
84 
85 /**
86  * Check if local port number is available
87  *
88  * @v port		Local port number
89  * @ret port		Local port number, or negative error
90  */
ping_port_available(int port)91 static int ping_port_available ( int port ) {
92 
93 	return ( ping_demux ( port ) ? -EADDRINUSE : port );
94 }
95 
96 /**
97  * Process ICMP ping reply
98  *
99  * @v iobuf		I/O buffer
100  * @v st_src		Source address
101  * @ret rc		Return status code
102  */
ping_rx(struct io_buffer * iobuf,struct sockaddr_tcpip * st_src)103 int ping_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src ) {
104 	struct icmp_echo *echo = iobuf->data;
105 	struct ping_connection *ping;
106 	struct xfer_metadata meta;
107 	int rc;
108 
109 	/* Sanity check: should already have been checked by ICMP layer */
110 	assert ( iob_len ( iobuf ) >= sizeof ( *echo ) );
111 
112 	/* Identify connection */
113 	ping = ping_demux ( ntohs ( echo->ident ) );
114 	DBGC ( ping, "PING %p reply id %#04x seq %#04x\n",
115 	       ping, ntohs ( echo->ident ), ntohs ( echo->sequence ) );
116 	if ( ! ping ) {
117 		rc = -ENOTCONN;
118 		goto discard;
119 	}
120 
121 	/* Strip header, construct metadata, and pass data to upper layer */
122 	iob_pull ( iobuf, sizeof ( *echo ) );
123 	memset ( &meta, 0, sizeof ( meta ) );
124 	meta.src = ( ( struct sockaddr * ) st_src );
125 	meta.flags = XFER_FL_ABS_OFFSET;
126 	meta.offset = ntohs ( echo->sequence );
127 	return xfer_deliver ( &ping->xfer, iob_disown ( iobuf ), &meta );
128 
129  discard:
130 	free_iob ( iobuf );
131 	return rc;
132 }
133 
134 /**
135  * Allocate I/O buffer for ping
136  *
137  * @v ping		Ping connection
138  * @v len		Payload size
139  * @ret iobuf		I/O buffer, or NULL
140  */
141 static struct io_buffer *
ping_alloc_iob(struct ping_connection * ping __unused,size_t len)142 ping_alloc_iob ( struct ping_connection *ping __unused, size_t len ) {
143 	size_t header_len;
144 	struct io_buffer *iobuf;
145 
146 	header_len = ( MAX_LL_NET_HEADER_LEN + sizeof ( struct icmp_echo ) );
147 	iobuf = alloc_iob ( header_len + len );
148 	if ( iobuf )
149 		iob_reserve ( iobuf, header_len );
150 	return iobuf;
151 }
152 
153 /**
154  * Deliver datagram as I/O buffer
155  *
156  * @v ping		Ping connection
157  * @v iobuf		I/O buffer
158  * @v meta		Data transfer metadata
159  * @ret rc		Return status code
160  */
ping_deliver(struct ping_connection * ping,struct io_buffer * iobuf,struct xfer_metadata * meta)161 static int ping_deliver ( struct ping_connection *ping, struct io_buffer *iobuf,
162 			  struct xfer_metadata *meta ) {
163 	struct icmp_echo *echo = iob_push ( iobuf, sizeof ( *echo ) );
164 	int rc;
165 
166 	/* Construct header */
167 	memset ( echo, 0, sizeof ( *echo ) );
168 	echo->ident = htons ( ping->port );
169 	echo->sequence = htons ( meta->offset );
170 
171 	/* Transmit echo request */
172 	if ( ( rc = icmp_tx_echo_request ( iob_disown ( iobuf ),
173 					   &ping->peer ) ) != 0 ) {
174 		DBGC ( ping, "PING %p could not transmit: %s\n",
175 		       ping, strerror ( rc ) );
176 		return rc;
177 	}
178 
179 	return 0;
180 }
181 
182 /**
183  * Close ping connection
184  *
185  * @v ping		Ping connection
186  * @v rc		Reason for close
187  */
ping_close(struct ping_connection * ping,int rc)188 static void ping_close ( struct ping_connection *ping, int rc ) {
189 
190 	/* Close data transfer interface */
191 	intf_shutdown ( &ping->xfer, rc );
192 
193 	/* Remove from list of connections and drop list's reference */
194 	list_del ( &ping->list );
195 	ref_put ( &ping->refcnt );
196 
197 	DBGC ( ping, "PING %p closed\n", ping );
198 }
199 
200 /** Ping data transfer interface operations */
201 static struct interface_operation ping_xfer_operations[] = {
202 	INTF_OP ( xfer_deliver, struct ping_connection *, ping_deliver ),
203 	INTF_OP ( xfer_alloc_iob, struct ping_connection *, ping_alloc_iob ),
204 	INTF_OP ( intf_close, struct ping_connection *, ping_close ),
205 };
206 
207 /** Ping data transfer interface descriptor */
208 static struct interface_descriptor ping_xfer_desc =
209 	INTF_DESC ( struct ping_connection, xfer, ping_xfer_operations );
210 
211 /**
212  * Open a ping connection
213  *
214  * @v xfer		Data transfer interface
215  * @v peer		Peer socket address
216  * @v local		Local socket address, or NULL
217  * @ret rc		Return status code
218  */
ping_open(struct interface * xfer,struct sockaddr * peer,struct sockaddr * local)219 static int ping_open ( struct interface *xfer, struct sockaddr *peer,
220 		       struct sockaddr *local ) {
221 	struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer;
222 	struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local;
223 	struct ping_connection *ping;
224 	int port;
225 	int rc;
226 
227 	/* Allocate and initialise structure */
228 	ping = zalloc ( sizeof ( *ping ) );
229 	if ( ! ping ) {
230 		rc = -ENOMEM;
231 		goto err_alloc;
232 	}
233 	DBGC ( ping, "PING %p allocated\n", ping );
234 	ref_init ( &ping->refcnt, NULL );
235 	intf_init ( &ping->xfer, &ping_xfer_desc, &ping->refcnt );
236 	memcpy ( &ping->peer, st_peer, sizeof ( ping->peer ) );
237 
238 	/* Bind to local port */
239 	port = tcpip_bind ( st_local, ping_port_available );
240 	if ( port < 0 ) {
241 		rc = port;
242 		DBGC ( ping, "PING %p could not bind: %s\n",
243 		       ping, strerror ( rc ) );
244 		goto err_bind;
245 	}
246 	ping->port = port;
247 	DBGC ( ping, "PING %p bound to id %#04x\n", ping, port );
248 
249 	/* Attach parent interface, transfer reference to connection
250 	 * list, and return
251 	 */
252 	intf_plug_plug ( &ping->xfer, xfer );
253 	list_add ( &ping->list, &ping_conns );
254 	return 0;
255 
256  err_bind:
257 	ref_put ( &ping->refcnt );
258  err_alloc:
259 	return rc;
260 }
261 
262 /** Ping socket opener */
263 struct socket_opener ping_socket_opener __socket_opener = {
264 	.semantics	= PING_SOCK_ECHO,
265 	.open		= ping_open,
266 };
267 
268 /** Linkage hack */
269 int ping_sock_echo = PING_SOCK_ECHO;
270