[ath5k] Update for changes in kernel 2.6.31
[gpxe.git] / contrib / mini-slamd / mini-slamd.c
blob7c33e22be0c69e88c1adb84dad535453e225d1c1
1 /*
2 * mini-slamd
3 * (c) 2002 Eric Biederman
4 */
6 #include <string.h>
7 #include <errno.h>
8 #include <stdio.h>
9 #include <stdint.h>
10 #include <stdlib.h>
11 #include <sys/types.h>
12 #include <sys/poll.h>
13 #include <sys/socket.h>
14 #include <sys/stat.h>
15 #include <netinet/ip.h>
16 #include <netinet/in.h>
17 #include <fcntl.h>
18 #include <unistd.h>
19 #include <arpa/inet.h>
22 * To specify the default interface for multicast packets use:
23 * route add -net 224.0.0.0 netmask 240.0.0.0 dev eth1
24 * This server is stupid and does not override the default.
27 /* Sever states.
29 * Waiting for clients.
30 * Sending data to clients.
31 * Pinging clients for data.
34 #define SLAM_PORT 10000
35 #define SLAM_MULTICAST_IP ((239<<24)|(255<<16)|(1<<8)|(1<<0))
36 #define SLAM_MULTICAST_PORT 10000
37 #define SLAM_MULTICAST_TTL 1
38 #define SLAM_MULTICAST_LOOPBACK 1
39 #define SLAM_MAX_CLIENTS 10
41 #define SLAM_PING_TIMEOUT 100 /* ms */
43 /*** Packets Formats ***
44 * Data Packet:
45 * transaction
46 * total bytes
47 * block size
48 * packet #
49 * data
51 * Status Request Packet
52 * transaction
53 * total bytes
54 * block packets
56 * Status Packet
57 * received packets
58 * requested packets
59 * received packets
60 * requested packets
61 * ...
62 * received packets
63 * requested packtes
64 * 0
67 #define MAX_HDR (7 + 7 + 7) /* transaction, total size, block size */
68 #define MIN_HDR (1 + 1 + 1) /* transaction, total size, block size */
70 #define MAX_DATA_HDR (MAX_HDR + 7) /* header, packet # */
71 #define MIN_DATA_HDR (MAX_HDR + 1) /* header, packet # */
73 /* ETH_MAX_MTU 1500 - sizeof(iphdr) 20 - sizeof(udphdr) 8 = 1472 */
74 #define SLAM_MAX_NACK (1500 - (20 + 8))
75 /* ETH_MAX_MTU 1500 - sizeof(iphdr) 20 - sizeof(udphdr) 8 - MAX_HDR = 1451 */
76 #define SLAM_BLOCK_SIZE (1500 - (20 + 8 + MAX_HDR))
79 /* Define how many debug messages you want
80 * 1 - sparse but useful
81 * 2 - everything
83 #ifndef DEBUG
84 #define DEBUG 0
85 #endif
87 static int slam_encode(
88 unsigned char **ptr, unsigned char *end, unsigned long value)
90 unsigned char *data = *ptr;
91 int bytes;
92 bytes = sizeof(value);
93 while ((bytes > 0) && ((0xff & (value >> ((bytes -1)<<3))) == 0)) {
94 bytes--;
96 if (bytes <= 0) {
97 bytes = 1;
99 if (data + bytes >= end) {
100 return -1;
102 if ((0xe0 & (value >> ((bytes -1)<<3))) == 0) {
103 /* packed together */
104 *data = (bytes << 5) | (value >> ((bytes -1)<<3));
105 } else {
106 bytes++;
107 *data = (bytes << 5);
109 bytes--;
110 data++;
111 while(bytes) {
112 *(data++) = 0xff & (value >> ((bytes -1)<<3));
113 bytes--;
115 *ptr = data;
116 return 0;
119 static unsigned long slam_decode(unsigned char **ptr, unsigned char *end, int *err)
121 unsigned long value;
122 unsigned bytes;
123 if (*ptr >= end) {
124 *err = -1;
126 bytes = ((**ptr) >> 5) & 7;
127 if ((bytes == 0) || (bytes > sizeof(unsigned long))) {
128 *err = -1;
129 return 0;
131 if ((*ptr) + bytes >= end) {
132 *err = -1;
134 value = (**ptr) & 0x1f;
135 bytes--;
136 (*ptr)++;
137 while(bytes) {
138 value <<= 8;
139 value |= **ptr;
140 (*ptr)++;
141 bytes--;
143 return value;
147 static struct sockaddr_in client[SLAM_MAX_CLIENTS];
148 static int clients;
151 void del_client(struct sockaddr_in *old)
153 int i;
154 for(i = 0; i < clients; i++) {
155 if ((client[i].sin_family == old->sin_family) &&
156 (client[i].sin_addr.s_addr == old->sin_addr.s_addr) &&
157 (client[i].sin_port == old->sin_port)) {
158 memmove(&client[i], &client[i+1],
159 (clients - (i+1))*sizeof(client[0]));
160 clients--;
165 void add_client(struct sockaddr_in *new)
167 del_client(new);
168 if (clients >= SLAM_MAX_CLIENTS)
169 return;
170 memcpy(&client[clients], new, sizeof(*new));
171 clients++;
174 void push_client(struct sockaddr_in *new)
176 del_client(new);
177 if (clients >= SLAM_MAX_CLIENTS) {
178 clients--;
180 memmove(&client[1], &client[0], clients*sizeof(*new));
181 memcpy(&client[0], new, sizeof(*new));
182 clients++;
186 void next_client(struct sockaddr_in *next)
188 /* Find the next client we want to ping next */
189 if (!clients) {
190 next->sin_family = AF_UNSPEC;
191 return;
193 /* Return the first client */
194 memcpy(next, &client[0], sizeof(*next));
197 int main(int argc, char **argv)
199 char *filename;
200 uint8_t nack_packet[SLAM_MAX_NACK];
201 int nack_len;
202 uint8_t request_packet[MAX_HDR];
203 int request_len;
204 uint8_t data_packet[MAX_DATA_HDR + SLAM_BLOCK_SIZE];
205 int data_len;
206 uint8_t *ptr, *end;
207 struct sockaddr_in master_client;
208 struct sockaddr_in sa_src;
209 struct sockaddr_in sa_mcast;
210 uint8_t mcast_ttl;
211 uint8_t mcast_loop;
212 int sockfd, filefd;
213 int result;
214 struct pollfd fds[1];
215 int state;
216 #define STATE_PINGING 1
217 #define STATE_WAITING 2
218 #define STATE_RECEIVING 3
219 #define STATE_TRANSMITTING 4
220 off_t size;
221 struct stat st;
222 uint64_t transaction;
223 unsigned long packet;
224 unsigned long packet_count;
225 unsigned slam_port, slam_multicast_port;
226 struct in_addr slam_multicast_ip;
228 slam_port = SLAM_PORT;
229 slam_multicast_port = SLAM_MULTICAST_PORT;
230 slam_multicast_ip.s_addr = htonl(SLAM_MULTICAST_IP);
232 if (argc != 2) {
233 fprintf(stderr, "Bad argument count\n");
234 fprintf(stderr, "Usage: mini-slamd filename\n");
235 exit(EXIT_FAILURE);
237 filename = argv[1];
238 filefd = -1;
239 size = 0;
240 transaction = 0;
242 /* Setup the udp socket */
243 sockfd = socket(PF_INET, SOCK_DGRAM, 0);
244 if (sockfd < 0) {
245 fprintf(stderr, "Cannot create socket\n");
246 exit(EXIT_FAILURE);
248 memset(&sa_src, 0, sizeof(sa_src));
249 sa_src.sin_family = AF_INET;
250 sa_src.sin_port = htons(slam_port);
251 sa_src.sin_addr.s_addr = INADDR_ANY;
253 result = bind(sockfd, &sa_src, sizeof(sa_src));
254 if (result < 0) {
255 fprintf(stderr, "Cannot bind socket to port %d\n",
256 ntohs(sa_src.sin_port));
257 exit(EXIT_FAILURE);
260 /* Setup the multicast transmission address */
261 memset(&sa_mcast, 0, sizeof(sa_mcast));
262 sa_mcast.sin_family = AF_INET;
263 sa_mcast.sin_port = htons(slam_multicast_port);
264 sa_mcast.sin_addr.s_addr = slam_multicast_ip.s_addr;
265 if (!IN_MULTICAST(ntohl(sa_mcast.sin_addr.s_addr))) {
266 fprintf(stderr, "Not a multicast ip\n");
267 exit(EXIT_FAILURE);
270 /* Set the multicast ttl */
271 mcast_ttl = SLAM_MULTICAST_TTL;
272 setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL,
273 &mcast_ttl, sizeof(mcast_ttl));
275 /* Set the multicast loopback status */
276 mcast_loop = SLAM_MULTICAST_LOOPBACK;
277 setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, &mcast_loop, sizeof(mcast_loop));
280 state = STATE_WAITING;
281 packet = 0;
282 packet_count = 0;
283 fds[0].fd = sockfd;
284 fds[0].events = POLLIN;
285 fds[0].revents = 0;
286 for(;;) {
287 switch(state) {
288 case STATE_PINGING:
289 state = STATE_WAITING;
290 next_client(&master_client);
291 if (master_client.sin_family == AF_UNSPEC) {
292 break;
294 #if DEBUG
295 printf("Pinging %s:%d\n",
296 inet_ntoa(master_client.sin_addr),
297 ntohs(master_client.sin_port));
298 fflush(stdout);
299 #endif
301 /* Prepare the request packet, it is all header */
302 ptr = request_packet;
303 end = &request_packet[sizeof(request_packet) -1];
304 slam_encode(&ptr, end, transaction);
305 slam_encode(&ptr, end, size);
306 slam_encode(&ptr, end, SLAM_BLOCK_SIZE);
307 request_len = ptr - request_packet;
309 result = sendto(sockfd, request_packet, request_len, 0,
310 &master_client, sizeof(master_client));
311 /* Forget the client I just asked, when the reply
312 * comes in we will remember it again.
314 del_client(&master_client);
315 break;
316 case STATE_WAITING:
318 int timeout;
319 int from_len;
320 timeout = -1;
321 if (master_client.sin_family != AF_UNSPEC) {
322 timeout = SLAM_PING_TIMEOUT;
324 result = poll(fds, sizeof(fds)/sizeof(fds[0]), timeout);
325 if (result == 0) {
326 /* On a timeout try the next client */
327 state = STATE_PINGING;
328 break;
330 if (result > 0) {
331 from_len = sizeof(master_client);
332 result = recvfrom(sockfd,
333 nack_packet, sizeof(nack_packet), 0,
334 &master_client, &from_len);
335 if (result < 0)
336 break;
337 nack_len = result;
338 #if DEBUG
339 printf("Received Nack from %s:%d\n",
340 inet_ntoa(master_client.sin_addr),
341 ntohs(master_client.sin_port));
342 fflush(stdout);
343 #endif
344 #if DEBUG
346 ptr = nack_packet;
347 end = ptr + result;
348 packet = 0;
349 result = 0;
350 while(ptr < end) {
351 packet += slam_decode(&ptr, end, &result);
352 if (result < 0) break;
353 packet_count = slam_decode(&ptr, end, &result);
354 if (result < 0) break;
355 printf("%d-%d ",
356 packet, packet + packet_count -1);
358 printf("\n");
359 fflush(stdout);
361 #endif
362 /* Forget this client temporarily.
363 * If the packet appears good they will be
364 * readded.
366 del_client(&master_client);
367 ptr = nack_packet;
368 end = ptr + nack_len;
369 result = 0;
370 packet = slam_decode(&ptr, end, &result);
371 if (result < 0)
372 break;
373 packet_count = slam_decode(&ptr, end, &result);
374 if (result < 0)
375 break;
376 /* We appear to have a good packet, keep
377 * this client.
379 push_client(&master_client);
381 /* Reopen the file to transmit */
382 if (filefd != -1) {
383 close(filefd);
385 filefd = open(filename, O_RDONLY);
386 if (filefd < 0) {
387 fprintf(stderr, "Cannot open %s: %s\n",
388 filename, strerror(errno));
389 break;
391 size = lseek(filefd, 0, SEEK_END);
392 if (size < 0) {
393 fprintf(stderr, "Seek failed on %s: %s\n",
394 filename, strerror(errno));
395 break;
397 result = fstat(filefd, &st);
398 if (result < 0) {
399 fprintf(stderr, "Stat failed on %s: %s\n",
400 filename, strerror(errno));
401 break;
403 transaction = st.st_mtime;
405 state = STATE_TRANSMITTING;
406 break;
408 break;
410 case STATE_RECEIVING:
411 /* Now clear the queue of received packets */
413 struct sockaddr_in from;
414 int from_len;
415 uint8_t dummy_packet[SLAM_MAX_NACK];
416 state = STATE_TRANSMITTING;
417 result = poll(fds, sizeof(fds)/sizeof(fds[0]), 0);
418 if (result < 1)
419 break;
420 from_len = sizeof(from);
421 result = recvfrom(sockfd,
422 dummy_packet, sizeof(dummy_packet), 0,
423 &from, &from_len);
424 if (result <= 0)
425 break;
426 #if DEBUG
427 printf("Received Nack from %s:%d\n",
428 inet_ntoa(from.sin_addr),
429 ntohs(from.sin_port));
430 fflush(stdout);
431 #endif
432 /* Receive packets until I don't get any more */
433 state = STATE_RECEIVING;
434 /* Process a packet */
435 if (dummy_packet[0] == '\0') {
436 /* If the first byte is null it is a disconnect
437 * packet.
439 del_client(&from);
441 else {
442 /* Otherwise attempt to add the client. */
443 add_client(&from);
445 break;
447 case STATE_TRANSMITTING:
449 off_t off;
450 off_t offset;
451 ssize_t bytes;
452 uint8_t *ptr2, *end2;
454 /* After I transmit a packet check for packets to receive. */
455 state = STATE_RECEIVING;
457 /* Find the packet to transmit */
458 offset = packet * SLAM_BLOCK_SIZE;
460 /* Seek to the desired packet */
461 off = lseek(filefd, offset, SEEK_SET);
462 if ((off < 0) || (off != offset)) {
463 fprintf(stderr, "Seek failed on %s:%s\n",
464 filename, strerror(errno));
465 break;
467 /* Encode the packet header */
468 ptr2 = data_packet;
469 end2 = data_packet + sizeof(data_packet);
470 slam_encode(&ptr2, end2, transaction);
471 slam_encode(&ptr2, end2, size);
472 slam_encode(&ptr2, end2, SLAM_BLOCK_SIZE);
473 slam_encode(&ptr2, end2, packet);
474 data_len = ptr2 - data_packet;
476 /* Read in the data */
477 bytes = read(filefd, &data_packet[data_len],
478 SLAM_BLOCK_SIZE);
479 if (bytes <= 0) {
480 fprintf(stderr, "Read failed on %s:%s\n",
481 filename, strerror(errno));
482 break;
484 data_len += bytes;
485 /* Write out the data */
486 result = sendto(sockfd, data_packet, data_len, 0,
487 &sa_mcast, sizeof(sa_mcast));
488 if (result != data_len) {
489 fprintf(stderr, "Send failed %s\n",
490 strerror(errno));
491 break;
493 #if DEBUG > 1
494 printf("Transmitted: %d\n", packet);
495 fflush(stdout);
496 #endif
497 /* Compute the next packet */
498 packet++;
499 packet_count--;
500 if (packet_count == 0) {
501 packet += slam_decode(&ptr, end, &result);
502 if (result >= 0)
503 packet_count = slam_decode(&ptr, end, &result);
504 if (result < 0) {
505 /* When a transmission is done close the file,
506 * so it may be updated. And then ping then start
507 * pinging clients to get the transmission started
508 * again.
510 state = STATE_PINGING;
511 close(filefd);
512 filefd = -1;
513 break;
516 break;
520 return EXIT_SUCCESS;