diff options
| -rw-r--r-- | Makefile | 1 | ||||
| -rw-r--r-- | mcast_image.h | 18 | ||||
| -rw-r--r-- | recv_image.c | 356 | ||||
| -rw-r--r-- | serve_image.c | 191 | 
4 files changed, 566 insertions, 0 deletions
| @@ -25,6 +25,7 @@ RAWTARGETS = ftl_format flash_erase flash_eraseall nanddump doc_loadbios \  	jffs2dump \  	nftldump nftl_format docfdisk \  	rfddump rfdformat \ +	serve_image recv_image \  	sumtool #jffs2reader  TARGETS = $(foreach target,$(RAWTARGETS),$(BUILDDIR)/$(target)) diff --git a/mcast_image.h b/mcast_image.h new file mode 100644 index 0000000..e2048b6 --- /dev/null +++ b/mcast_image.h @@ -0,0 +1,18 @@ +#include <stdint.h> + +#define PKT_SIZE 1400 + +struct image_pkt_hdr { +	uint32_t totcrc; +	uint32_t nr_blocks; +	uint32_t blocksize; +	uint32_t block_nr; +	uint32_t block_ofs; +	uint32_t thislen; +	uint32_t thiscrc; +}; + +struct image_pkt { +	struct image_pkt_hdr hdr; +	unsigned char data[PKT_SIZE]; +}; diff --git a/recv_image.c b/recv_image.c new file mode 100644 index 0000000..d2b8813 --- /dev/null +++ b/recv_image.c @@ -0,0 +1,356 @@ + +#define _XOPEN_SOURCE 500 + +#include <errno.h>  	 +#include <error.h> 	 +#include <netdb.h> 	 +#include <stdio.h> 	 +#include <stdlib.h> 	 +#include <string.h> +#include <unistd.h> 	 +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <sys/ioctl.h> +#include "crc32.h" +#include "mtd/mtd-user.h" +#include "mcast_image.h" + +#define min(x,y) (  (x)>(y)?(y):(x) ) + +int main(int argc, char **argv) +{ +	struct sockaddr_storage server_addr; +	socklen_t server_addrlen = sizeof(server_addr); +	struct addrinfo *ai; +	struct addrinfo hints; +	struct addrinfo *runp; +	int ret; +	int sock; +	struct image_pkt pktbuf; +	size_t len; +	int flfd; +	struct mtd_info_user meminfo; +	unsigned char *eb_buf; +	unsigned char *blockmap = NULL; +	unsigned char *subblockmap; +	int nr_blocks = 0; +	int nr_subblocks = 0; +	int pkts_per_block; +	int block_nr = -1; +	uint32_t image_crc; +	uint32_t blocks_received = 0; +	uint32_t block_ofs; +	loff_t mtdoffset = 0; +	int *stats; +	int badcrcs = 0; +	int duplicates = 0; +	int missing = -1; +	int file_mode = 0; + +	if (argc != 4) { +		fprintf(stderr, "usage: %s <host> <port> <mtddev>\n", +			(strrchr(argv[0], '/')?:argv[0]-1)+1); +		exit(1); +	} +	/* Open the device */ +	flfd = open(argv[3], O_RDWR); + +	if (flfd >= 0) { +		/* Fill in MTD device capability structure */ +		if (ioctl(flfd, MEMGETINFO, &meminfo) != 0) { +			perror("MEMGETINFO"); +			close(flfd); +			flfd = -1; +		} else { +			printf("Receive to MTD device %s with erasesize %d\n", +			       argv[3], meminfo.erasesize); +		} +	} +	if (flfd == -1) { +		/* Try again, as if it's a file */ +		flfd = open(argv[3], O_CREAT|O_TRUNC|O_WRONLY, 0644); +		if (flfd < 0) { +			perror("open"); +			exit(1); +		} +		meminfo.erasesize = 131072; +		file_mode = 1; +		printf("Receive to file %s with (assumed) erasesize %d\n", +		       argv[3], meminfo.erasesize); +	} + +	pkts_per_block = (meminfo.erasesize + PKT_SIZE - 1) / PKT_SIZE; + +	stats = malloc(pkts_per_block + 1); +	if (!stats) { +		fprintf(stderr, "No memory for statistics\n"); +		exit(1); +	} +	memset(stats, 0, sizeof(int) * (pkts_per_block + 1)); + +	eb_buf = malloc(pkts_per_block * PKT_SIZE); +	if (!eb_buf) { +		fprintf(stderr, "No memory for eraseblock buffer\n"); +		exit(1); +	} +	memset(eb_buf, 0, pkts_per_block * PKT_SIZE); + +	subblockmap = malloc(pkts_per_block + 1); +	if (!subblockmap) { +		fprintf(stderr, "No memory for subblock map\n"); +		exit(1); +	} +	memset(subblockmap, 0, pkts_per_block + 1); + +	memset(&hints, 0, sizeof(hints)); +	hints.ai_flags = AI_ADDRCONFIG; +	hints.ai_socktype = SOCK_DGRAM; +	 +	ret = getaddrinfo(argv[1], argv[2], &hints, &ai); +	if (ret) { +		fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret)); +		exit(1); +	} +	runp = ai; +	for (runp = ai; runp; runp = runp->ai_next) { +		sock = socket(runp->ai_family, runp->ai_socktype, +			      runp->ai_protocol); +		if (sock == -1) { +			perror("socket"); +			continue; +		} +		if (runp->ai_family == AF_INET && +		    IN_MULTICAST( ntohl(((struct sockaddr_in *)runp->ai_addr)->sin_addr.s_addr))) { +			struct ip_mreq rq; +			rq.imr_multiaddr = ((struct sockaddr_in *)runp->ai_addr)->sin_addr; +			rq.imr_interface.s_addr = INADDR_ANY; +			if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &rq, sizeof(rq))) { +				perror("IP_ADD_MEMBERSHIP");  +				close(sock); +				continue; +			} +			 +		} else if (runp->ai_family == AF_INET6 && +			   ((struct sockaddr_in6 *)runp->ai_addr)->sin6_addr.s6_addr[0] == 0xff) { +			struct ipv6_mreq rq; +			rq.ipv6mr_multiaddr =  ((struct sockaddr_in6 *)runp->ai_addr)->sin6_addr; +			rq.ipv6mr_interface = 0; +			if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &rq, sizeof(rq))) { +				perror("IPV6_ADD_MEMBERSHIP");  +				close(sock); +				continue; +			} +		} else printf("not multicast?\n"); +		if (bind(sock, runp->ai_addr, runp->ai_addrlen)) { +			perror("bind"); +			close(sock); +			continue; +		} +		break; +	} +	if (!runp) +		exit(1); + +	while ((len = read(sock, &pktbuf, sizeof(pktbuf))) >= 0) { +		if (len < sizeof(pktbuf.hdr)) { +			fprintf(stderr, "Short read %d bytes\n", len); +			continue; +		} +		if (len != sizeof(pktbuf.hdr) + ntohl(pktbuf.hdr.thislen)) { +			fprintf(stderr, "Wrong length %d bytes (expected %d+%d)\n", +				len, sizeof(pktbuf.hdr), ntohl(pktbuf.hdr.thislen)); +			continue; +		} +		/* Holds _data_ length */ +		len -= sizeof(pktbuf.hdr); +			 +		if (!blockmap) { +			image_crc = pktbuf.hdr.totcrc; +			if (meminfo.erasesize != ntohl(pktbuf.hdr.blocksize)) { +				fprintf(stderr, "Erasesize mismatch (0x%x not 0x%x)\n", +					ntohl(pktbuf.hdr.blocksize), meminfo.erasesize); +				exit(1); +			} +			nr_blocks = ntohl(pktbuf.hdr.nr_blocks); +			nr_subblocks = pkts_per_block + 2; +			blockmap = malloc(nr_blocks); +			if (!blockmap) { +				fprintf(stderr, "No memory for block map\n"); +				exit(1); +			} +			memset(blockmap, 0, nr_blocks); +		} +		if (image_crc != pktbuf.hdr.totcrc) { +			fprintf(stderr, "Image CRC changed from 0x%x to 0x%x. Aborting\n", +				ntohl(image_crc), ntohl(pktbuf.hdr.totcrc)); +			exit(1); +		} +		if (ntohl(pktbuf.hdr.block_nr) != block_nr) { +			/* Hm, new block */ +			if (nr_subblocks < pkts_per_block && +			    block_nr != -1)  +				printf("Lost image block at %08x with only %d/%d packets\n", +				       block_nr * meminfo.erasesize, nr_subblocks, +				       pkts_per_block + 1); + + +			if (nr_subblocks < pkts_per_block + 2) +				stats[nr_subblocks]++; + +			nr_subblocks = 0; +			missing = -1; +			memset(subblockmap, 0, pkts_per_block + 1); +			block_nr = ntohl(pktbuf.hdr.block_nr); +			if (block_nr > nr_blocks) { +				fprintf(stderr, "Erroneous block_nr %d (> %d)\n", +					block_nr, nr_blocks); +				exit(1); +			} +			if (blockmap[block_nr]) { +				printf("Discard chunk at 0x%08x for already-flashed eraseblock (%d to go)\n", +				       block_nr * meminfo.erasesize, nr_blocks - blocks_received); +				nr_subblocks = pkts_per_block + 2; +				continue; +			} +		} +		if (nr_subblocks == pkts_per_block) { +			/* We have a parity block but we didn't need it */ +			nr_subblocks++; +			continue; +		} +		if (blockmap[block_nr]) +			continue; + +		block_ofs = ntohl(pktbuf.hdr.block_ofs); +		if (block_ofs == meminfo.erasesize) +			block_ofs = PKT_SIZE * pkts_per_block; + +		if (len != PKT_SIZE && len + block_ofs != meminfo.erasesize) { +			fprintf(stderr, "Bogus packet size 0x%x (expected 0x%x)\n", +				ntohl(pktbuf.hdr.thislen), +				min(PKT_SIZE, meminfo.erasesize - block_ofs)); +			exit(1); +		} + +		if (crc32(-1, pktbuf.data, len) != ntohl(pktbuf.hdr.thiscrc)) { +			printf("Discard chunk %08x with bad CRC (%08x not %08x)\n", +			       block_nr * meminfo.erasesize + block_ofs, +			       crc32(-1, pktbuf.data, pktbuf.hdr.thislen), +			       ntohl(pktbuf.hdr.thiscrc)); +			badcrcs++; +			continue; +		} +		if (subblockmap[block_ofs / PKT_SIZE]) { +			printf("Discarding duplicate packet at %08x\n", +			       block_nr * meminfo.erasesize + block_ofs); +			duplicates++; +			continue; +		} +		subblockmap[block_ofs / PKT_SIZE] = 1; +		nr_subblocks++; +		if (block_ofs < meminfo.erasesize) { +			/* Normal data packet */ +			memcpy(eb_buf + block_ofs, pktbuf.data, len); +//			printf("Received data block at %08x\n", block_nr * meminfo.erasesize + block_ofs); +		} else { +			/* Parity block */ +			int i; + +			/* If we don't have enough to recover, skip */ +			if (nr_subblocks < pkts_per_block) +				continue; + +			for (i = 0; i<pkts_per_block; i++) { +				if (subblockmap[i]) { +					int j; +					for (j=0; j<PKT_SIZE; j++) +						pktbuf.data[j] ^= eb_buf[i*PKT_SIZE + j]; +				} else +					missing = i; +			} + +			if (missing == -1) { +				fprintf(stderr, "dwmw2 is stupid\n"); +				exit(1); +			} +//			printf("Recover missing packet at %08x from parity\n", +//			       block_nr * meminfo.erasesize + missing * PKT_SIZE); +			memcpy(eb_buf + (missing * PKT_SIZE), pktbuf.data, PKT_SIZE); +		} + +		if (nr_subblocks == pkts_per_block) { + +			blockmap[block_nr] = 1; +			blocks_received++; + +			if (file_mode) { +				printf("Received image block %08x%s (%d/%d)\n", +				       block_nr * meminfo.erasesize, +				       (missing==-1)?"":" (parity)", +				       blocks_received, nr_blocks); +				pwrite(flfd, eb_buf, meminfo.erasesize, block_nr * meminfo.erasesize); +			} else { +				ssize_t wrotelen; +			again: +				if (mtdoffset >= meminfo.size) { +					fprintf(stderr, "Run out of space on flash\n"); +					exit(1); +				} +				while (ioctl(flfd, MEMGETBADBLOCK, &mtdoffset) > 0) { +					printf("Skipping flash bad block at %08x\n", (uint32_t)mtdoffset); +					mtdoffset += meminfo.erasesize; +				} +				wrotelen = pwrite(flfd, eb_buf, meminfo.erasesize, mtdoffset); +				if (wrotelen != meminfo.erasesize) { +					struct erase_info_user erase; +					 +					if (wrotelen < 0) +						perror("flash write"); +					else +						fprintf(stderr, "Short write to flash at %08x: %zd bytes\n", +							(uint32_t)mtdoffset, wrotelen); +					 +					erase.start = mtdoffset; +					erase.length = meminfo.erasesize; +					 +					if (ioctl(flfd, MEMERASE, erase)) { +						perror("MEMERASE"); +						exit(1); +					} +					/* skip it */ +					//				ioctl(flfd, MEMSETBADBLOCK, &mtdoffset); +					mtdoffset += meminfo.erasesize; +					goto again; +				} +				printf("Wrote image block %08x (%d/%d) to flash offset %08x%s\n", +				       block_nr * meminfo.erasesize,  +				       blocks_received, nr_blocks, +				       (uint32_t)mtdoffset, +				       (missing==-1)?"":" (parity)"); +				mtdoffset += meminfo.erasesize; +			} +			if (!(blocks_received%100) || blocks_received == nr_blocks) { +				int i, printed = 0; +				for (i=0; i <= pkts_per_block + 1; i++) { +					if (printed || stats[i]) { +						printf("Number of blocks with %d packets received: %d\n", +						       i, stats[i]); +						printed = 1; +					} +				} +				printf("Bad CRC: %d\n", badcrcs); +				printf("Duplicate: %d\n", duplicates); + +			} +			if (blocks_received == nr_blocks) { +				printf("Got all %08x bytes of image. Bye!\n", +				       nr_blocks * meminfo.erasesize); +				exit(0); +			} +		} +	} +	close(sock); +} diff --git a/serve_image.c b/serve_image.c new file mode 100644 index 0000000..b016d63 --- /dev/null +++ b/serve_image.c @@ -0,0 +1,191 @@ +#include <errno.h>  	 +#include <error.h> 	 +#include <netdb.h> 	 +#include <stdio.h> 	 +#include <stdlib.h> 	 +#include <string.h> +#include <unistd.h> 	 +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/mman.h> +#include <netinet/in.h> +#include "crc32.h" +#include "mcast_image.h" + +int tx_rate = 80000; +int pkt_delay = 12500 * PKT_SIZE / 1024; + +int main(int argc, char **argv) +{ +	struct addrinfo *ai; +	struct addrinfo hints; +	struct addrinfo *runp; +	int ret; +	int sock; +	struct image_pkt pktbuf; +	int rfd; +	struct stat st; +	int writeerrors = 0; +	long usec_per_tick = 1000000 / sysconf(_SC_CLK_TCK); +	long delay_accum = 0; +	uint32_t erasesize; +	unsigned char parbuf[PKT_SIZE]; +	unsigned char *image, *blockptr; +	uint32_t block_nr; +	uint32_t block_ofs; +	int nr_blocks; +	uint32_t droppoint = -1; + +	if (argc == 6) { +		tx_rate = atol(argv[5]) * 1024; +		if (tx_rate < PKT_SIZE || tx_rate > 20000000) { +			fprintf(stderr, "Bogus TX rate %d KiB/s\n", tx_rate); +			exit(1); +		} +		argc = 5; +	} +	        +	if (argc != 5) { +		fprintf(stderr, "usage: %s <host> <port> <image> <erasesize> [<tx_rate>]\n", +			(strrchr(argv[0], '/')?:argv[0]-1)+1); +		exit(1); +	} +	pkt_delay = (PKT_SIZE * 1000000) / tx_rate; +	printf("Inter-packet delay (avg): %dµs\n", pkt_delay); +	printf("Transmit rate: %d KiB/s\n", tx_rate / 1024); + +	erasesize = atol(argv[4]); +	if (!erasesize) { +		fprintf(stderr, "erasesize cannot be zero\n"); +		exit(1); +	} +	memset(&hints, 0, sizeof(hints)); +	hints.ai_flags = AI_ADDRCONFIG; +	hints.ai_socktype = SOCK_DGRAM; +	 +	ret = getaddrinfo(argv[1], argv[2], &hints, &ai); +	if (ret) { +		fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret)); +		exit(1); +	} +	runp = ai; +	for (runp = ai; runp; runp = runp->ai_next) { +		sock = socket(runp->ai_family, runp->ai_socktype, +			      runp->ai_protocol); +		if (sock == -1) { +			perror("socket"); +			continue; +		} +		if (connect(sock, runp->ai_addr, runp->ai_addrlen) == 0) +			break; +		perror("connect"); +		close(sock); +	} +	if (!runp) +		exit(1); + +	rfd = open(argv[3], O_RDONLY); +	if (rfd < 0) { +		perror("open"); +		exit(1); +	} + +	if (fstat(rfd, &st)) { +		perror("fstat"); +		exit(1); +	} + +	if (st.st_size % erasesize) { +		fprintf(stderr, "Image size %ld bytes is not a multiple of erasesize %d bytes\n", +			st.st_size, erasesize); +		exit(1); +	} +	image = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, rfd, 0); +	if (image == MAP_FAILED) { +		perror("mmap"); +		exit(1); +	} + + +	nr_blocks = st.st_size / erasesize; + +	pktbuf.hdr.totcrc = htonl(crc32(-1, image, st.st_size)); +	pktbuf.hdr.nr_blocks = htonl(nr_blocks); +	pktbuf.hdr.blocksize = htonl(erasesize); + +	blockptr = image; + +	for (block_nr = 0; block_nr < nr_blocks; block_nr++) { +		int len; +		int dropped = 0; + +		pktbuf.hdr.block_nr = htonl(block_nr); + +		for (block_ofs = 0; block_ofs <= erasesize; block_ofs += len) { +			int i; +			 +			if (block_ofs + PKT_SIZE > erasesize) +				len = erasesize - block_ofs; +			else +				len = PKT_SIZE; + +			if (block_ofs == erasesize) { +				printf("\rSending parity block: %08x", block_nr * erasesize); +				len = PKT_SIZE; +				memcpy(pktbuf.data, parbuf, PKT_SIZE); +			} else { +				if (!block_ofs) +					memcpy(parbuf, blockptr, PKT_SIZE); +				else for (i=0; i < len; i++) +					     parbuf[i] ^= blockptr[i]; + +				memcpy(pktbuf.data, blockptr, len); +				printf("\rSending data block at %08x", +				       block_nr * erasesize + block_ofs); +				blockptr += len; +			} + +			fflush(stdout); +			pktbuf.hdr.thislen = htonl(len); +			pktbuf.hdr.block_ofs = htonl(block_ofs); +			pktbuf.hdr.thiscrc = htonl(crc32(-1, pktbuf.data, len)); + +			if (droppoint == block_ofs && !dropped) { +				dropped = 1; +				if (droppoint == 0) +					droppoint = erasesize; +				else if (droppoint == erasesize) +					droppoint = ((erasesize - 1) / PKT_SIZE) * PKT_SIZE; +				else droppoint -= PKT_SIZE; +				printf("\nDropping data block at %08x\n", block_ofs); +				continue; +			} + + +			if (write(sock, &pktbuf, sizeof(pktbuf.hdr)+len) < 0) { +				perror("write"); +				writeerrors++; +				if (writeerrors > 10) { +					fprintf(stderr, "Too many consecutive write errors\n"); +					exit(1); +				} +			} else +				writeerrors = 0; + +			/* Delay, if we are so far ahead of ourselves that we have at +			   least one tick to wait. */ +			delay_accum += pkt_delay; +			if (delay_accum >= usec_per_tick) { +				usleep(delay_accum); +				delay_accum = 0; +			} +		} +	} +	munmap(image, st.st_size); +	close(rfd); +	close(sock); +	printf("\n"); +	return 0; +} | 
