network.c 8.01 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1
2
/*
 * EMULAB-COPYRIGHT
3
 * Copyright (c) 2000-2004 University of Utah and the Flux Group.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
4
5
6
 * All rights reserved.
 */

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
 * Network routines.
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include "decls.h"
Mike Hibler's avatar
Mike Hibler committed
23
24
#include "utils.h"

25
26
27
28
29
30
31
#ifdef STATS
unsigned long nonetbufs;
#define DOSTAT(x)	(x)
#else
#define DOSTAT(x)
#endif

32
33
34
35
36
37
38
/* Max number of times to attempt bind to port before failing. */
#define MAXBINDATTEMPTS		10

/* Max number of hops multicast hops. */
#define MCAST_TTL		5

static int		sock;
39
struct in_addr		myipaddr;
40
static int		nobufdelay = -1;
41
int			broadcast = 0;
42
43
44
45
46
47
48
49
50
51
52
53
54

static void
CommonInit(void)
{
	struct sockaddr_in	name;
	struct timeval		timeout;
	int			i;
	char			buf[BUFSIZ];
	struct hostent		*he;
	
	if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
		pfatal("Could not allocate a socket");

55
	i = SOCKBUFSIZE;
Mike Hibler's avatar
Mike Hibler committed
56
57
58
	if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &i, sizeof(i)) < 0)
		pwarning("Could not increase send socket buffer size to %d",
			 SOCKBUFSIZE);
59
    
60
	i = SOCKBUFSIZE;
Mike Hibler's avatar
Mike Hibler committed
61
62
63
	if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &i, sizeof(i)) < 0)
		pwarning("Could not increase recv socket buffer size to %d",
			 SOCKBUFSIZE);
64
65
66
67
68
69
70
71
72
73

	name.sin_family      = AF_INET;
	name.sin_port	     = htons(portnum);
	name.sin_addr.s_addr = htonl(INADDR_ANY);

	i = MAXBINDATTEMPTS;
	while (i) {
		if (bind(sock, (struct sockaddr *)&name, sizeof(name)) == 0)
			break;

74
75
76
		if (--i == 0)
			pfatal("Could not bind to port %d!", portnum);

77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
		pwarning("Bind to port %d failed. Will try %d more times!",
			 portnum, i);
		sleep(5);
	}
	log("Bound to port %d", portnum);

	/*
	 * At present, we use a multicast address in both directions.
	 */
	if ((ntohl(mcastaddr.s_addr) >> 28) == 14) {
		unsigned int loop = 0, ttl = MCAST_TTL;
		struct ip_mreq mreq;

		log("Using Multicast");

		mreq.imr_multiaddr.s_addr = mcastaddr.s_addr;

		if (mcastif.s_addr)
			mreq.imr_interface.s_addr = mcastif.s_addr;
		else
			mreq.imr_interface.s_addr = htonl(INADDR_ANY);

		if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
			       &mreq, sizeof(mreq)) < 0)
			pfatal("setsockopt(IPPROTO_IP, IP_ADD_MEMBERSHIP)");

		if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL,
			       &ttl, sizeof(ttl)) < 0) 
			pfatal("setsockopt(IPPROTO_IP, IP_MULTICAST_TTL)");

		/* Disable local echo */
		if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP,
			       &loop, sizeof(loop)) < 0)
			pfatal("setsockopt(IPPROTO_IP, IP_MULTICAST_LOOP)");

		if (mcastif.s_addr &&
		    setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
			       &mcastif, sizeof(mcastif)) < 0) {
			pfatal("setsockopt(IPPROTO_IP, IP_MULTICAST_IF)");
		}
	}
118
	else if (broadcast) {
119
120
121
122
		/*
		 * Otherwise, we use a broadcast addr. 
		 */
		i = 1;
123
124

		log("Setting broadcast mode\n");
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
		
		if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST,
			       &i, sizeof(i)) < 0)
			pfatal("setsockopt(SOL_SOCKET, SO_BROADCAST)");
	}

	/*
	 * We use a socket level timeout instead of polling for data.
	 */
	timeout.tv_sec  = 0;
	timeout.tv_usec = PKTRCV_TIMEOUT;
	
	if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
		       &timeout, sizeof(timeout)) < 0)
		pfatal("setsockopt(SOL_SOCKET, SO_RCVTIMEO)");

	/*
Mike Hibler's avatar
Mike Hibler committed
142
143
144
	 * If a specific interface IP is specified, use that to
	 * tag our outgoing packets.  Otherwise we use the IP address
	 * associated with our hostname.
145
	 */
Mike Hibler's avatar
Mike Hibler committed
146
147
148
	if (mcastif.s_addr)
		myipaddr.s_addr = mcastif.s_addr;
	else {
149
150
		if (gethostname(buf, sizeof(buf)) < 0)
			pfatal("gethostname failed");
151

152
153
		if ((he = gethostbyname(buf)) == 0)
			fatal("gethostbyname: %s", hstrerror(h_errno));
154

155
156
		memcpy((char *)&myipaddr, he->h_addr, sizeof(myipaddr));
	}
157
158

	/*
Mike Hibler's avatar
Mike Hibler committed
159
	 * Compute the out of buffer space delay.
160
161
	 */
	if (nobufdelay < 0)
Mike Hibler's avatar
Mike Hibler committed
162
		nobufdelay = sleeptime(100, NULL, 1);
163
164
165
166
167
168
169
170
171
172
}

int
ClientNetInit(void)
{
	CommonInit();
	
	return 1;
}

173
174
175
176
177
178
unsigned long
ClientNetID(void)
{
	return ntohl(myipaddr.s_addr);
}

179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
int
ServerNetInit(void)
{
	CommonInit();

	return 1;
}

/*
 * Look for a packet on the socket. Propogate the errors back to the caller
 * exactly as the system call does. Remember that we set up a socket timeout
 * above, so we will get EWOULDBLOCK errors when no data is available. 
 *
 * The amount of data received is determined from the datalen of the hdr.
 * All packets are actually the same size/structure. 
Mike Hibler's avatar
Mike Hibler committed
194
195
 *
 * Returns 0 for a good packet, 1 for a back packet, -1 on timeout.
196
197
198
199
200
 */
int
PacketReceive(Packet_t *p)
{
	struct sockaddr_in from;
201
	int		   mlen, alen;
202

203
204
	alen = sizeof(from);
	bzero(&from, alen);
205
206
207
208
209
210
	if ((mlen = recvfrom(sock, p, sizeof(*p), 0,
			     (struct sockaddr *)&from, &alen)) < 0) {
		if (errno == EWOULDBLOCK)
			return -1;
		pfatal("PacketReceive(recvfrom)");
	}
211

Mike Hibler's avatar
Mike Hibler committed
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
	/*
	 * Basic integrity checks
	 */
	if (mlen < sizeof(p->hdr) + p->hdr.datalen) {
		log("Bad message length (%d != %d)",
		    mlen, p->hdr.datalen);
		return 1;
	}
	if (p->hdr.srcip != from.sin_addr.s_addr) {
		log("Bad message source (%x != %x)",
		    ntohl(from.sin_addr.s_addr), ntohl(p->hdr.srcip));
		return 1;
	}

	return 0;
227
228
229
230
231
232
233
234
235
}

/*
 * We use blocking sends since there is no point in giving up. All packets
 * go to the same place, whether client or server.
 *
 * The amount of data sent is determined from the datalen of the packet hdr.
 * All packets are actually the same size/structure. 
 */
236
237
void
PacketSend(Packet_t *p, int *resends)
238
239
{
	struct sockaddr_in to;
240
241
	int		   len, delays;

242
	len = sizeof(p->hdr) + p->hdr.datalen;
243
	p->hdr.srcip = myipaddr.s_addr;
244
245
246
247
248

	to.sin_family      = AF_INET;
	to.sin_port        = htons(portnum);
	to.sin_addr.s_addr = mcastaddr.s_addr;

249
	delays = 0;
250
251
252
253
254
255
256
257
258
	while (sendto(sock, (void *)p, len, 0, 
		      (struct sockaddr *)&to, sizeof(to)) < 0) {
		if (errno != ENOBUFS)
			pfatal("PacketSend(sendto)");

		/*
		 * ENOBUFS means we ran out of mbufs. Okay to sleep a bit
		 * to let things drain.
		 */
259
260
		delays++;
		fsleep(nobufdelay);
261
262
	}

263
264
265
	DOSTAT(nonetbufs += delays);
	if (resends != 0)
		*resends = delays;
266
267
268
269
270
271
272
273
}

/*
 * Basically the same as above, but instead of sending to the multicast
 * group, send to the (unicast) IP in the packet header. This simplifies
 * the logic in a number of places, by avoiding having to deal with
 * multicast packets that are not destined for us, but for someone else.
 */
274
void
275
276
277
278
279
280
281
282
283
284
PacketReply(Packet_t *p)
{
	struct sockaddr_in to;
	int		   len;

	len = sizeof(p->hdr) + p->hdr.datalen;

	to.sin_family      = AF_INET;
	to.sin_port        = htons(portnum);
	to.sin_addr.s_addr = p->hdr.srcip;
285
	p->hdr.srcip       = myipaddr.s_addr;
286
287
288
289
290
291
292
293
294
295

	while (sendto(sock, (void *)p, len, 0, 
		      (struct sockaddr *)&to, sizeof(to)) < 0) {
		if (errno != ENOBUFS)
			pfatal("PacketSend(sendto)");

		/*
		 * ENOBUFS means we ran out of mbufs. Okay to sleep a bit
		 * to let things drain.
		 */
296
297
		DOSTAT(nonetbufs++);
		fsleep(nobufdelay);
298
	}
299
}
300

301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
int
PacketValid(Packet_t *p, int nchunks)
{
	switch (p->hdr.type) {
	case PKTTYPE_REQUEST:
	case PKTTYPE_REPLY:
		break;
	default:
		return 0;
	}

	switch (p->hdr.subtype) {
	case PKTSUBTYPE_BLOCK:
		if (p->hdr.datalen < sizeof(p->msg.block))
			return 0;
		if (p->msg.block.chunk < 0 ||
		    p->msg.block.chunk >= nchunks ||
		    p->msg.block.block < 0 ||
		    p->msg.block.block >= CHUNKSIZE)
			return 0;
		break;
	case PKTSUBTYPE_REQUEST:
		if (p->hdr.datalen < sizeof(p->msg.request))
			return 0;
		if (p->msg.request.chunk < 0 ||
		    p->msg.request.chunk >= nchunks ||
		    p->msg.request.block < 0 ||
		    p->msg.request.block >= CHUNKSIZE ||
		    p->msg.request.count < 0 ||
		    p->msg.request.block+p->msg.request.count > CHUNKSIZE)
			return 0;
		break;
Mike Hibler's avatar
Mike Hibler committed
333
334
335
336
337
338
339
	case PKTSUBTYPE_PREQUEST:
		if (p->hdr.datalen < sizeof(p->msg.prequest))
			return 0;
		if (p->msg.prequest.chunk < 0 ||
		    p->msg.prequest.chunk >= nchunks)
			return 0;
		break;
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
	case PKTSUBTYPE_JOIN:
		if (p->hdr.datalen < sizeof(p->msg.join))
			return 0;
		break;
	case PKTSUBTYPE_LEAVE:
		if (p->hdr.datalen < sizeof(p->msg.leave))
			return 0;
		break;
	case PKTSUBTYPE_LEAVE2:
		if (p->hdr.datalen < sizeof(p->msg.leave2))
			return 0;
		break;
	default:
		return 0;
	}

	return 1;
357
}