Browse Source

[ping] Add concept of a ping socket

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 11 years ago
parent
commit
46873eda44
3 changed files with 282 additions and 0 deletions
  1. 1
    0
      src/include/ipxe/errfile.h
  2. 8
    0
      src/include/ipxe/socket.h
  3. 273
    0
      src/net/ping.c

+ 1
- 0
src/include/ipxe/errfile.h View File

@@ -217,6 +217,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
217 217
 #define ERRFILE_neighbour		( ERRFILE_NET | 0x00370000 )
218 218
 #define ERRFILE_socket			( ERRFILE_NET | 0x00380000 )
219 219
 #define ERRFILE_icmp			( ERRFILE_NET | 0x00390000 )
220
+#define ERRFILE_ping			( ERRFILE_NET | 0x003a0000 )
220 221
 
221 222
 #define ERRFILE_image		      ( ERRFILE_IMAGE | 0x00000000 )
222 223
 #define ERRFILE_elf		      ( ERRFILE_IMAGE | 0x00010000 )

+ 8
- 0
src/include/ipxe/socket.h View File

@@ -28,6 +28,11 @@ extern int udp_sock_dgram;
28 28
 #define UDP_SOCK_DGRAM 0x2
29 29
 #define SOCK_DGRAM udp_sock_dgram
30 30
 
31
+/** Echo testing streams */
32
+extern int ping_sock_echo;
33
+#define PING_SOCK_ECHO 0x3
34
+#define SOCK_ECHO ping_sock_echo
35
+
31 36
 /** @} */
32 37
 
33 38
 /**
@@ -43,6 +48,8 @@ socket_semantics_name ( int semantics ) {
43 48
 		return "SOCK_STREAM";
44 49
 	} else if ( semantics == SOCK_DGRAM ) {
45 50
 		return "SOCK_DGRAM";
51
+	} else if ( semantics == SOCK_ECHO ) {
52
+		return "SOCK_ECHO";
46 53
 	} else {
47 54
 		return "SOCK_UNKNOWN";
48 55
 	}
@@ -69,6 +76,7 @@ socket_family_name ( int family ) {
69 76
 	switch ( family ) {
70 77
 	case AF_INET:		return "AF_INET";
71 78
 	case AF_INET6:		return "AF_INET6";
79
+	case AF_FC:		return "AF_FC";
72 80
 	default:		return "AF_UNKNOWN";
73 81
 	}
74 82
 }

+ 273
- 0
src/net/ping.c View File

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

Loading…
Cancel
Save