This is the mail archive of the ecos-discuss@sourceware.org mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [Fwd: FreeBSD network stack question]


Here are the target and host applications, with makefiles for each. For the target, edit target.mk and modify ECOS_LIB appropriately. Also edit tool names, if appropriate. The host is a (trivial) server, the target is the client.

Let me know if I can provide further information.

Barry Wealand
barry.wealand@lmco.com

Gary Thomas wrote:

On Wed, 2005-10-19 at 10:25 +0000, Barry Wealand wrote:


Hello -

We're working with a MIPS-like target, eCos 2.0, and the FreeBSD network stack. I have a simple application that sends UDP messages to a host server process, which sends back a short acknowledgement for each. Message size can be varied - for the present problem, we're using a size of 2K bytes. Of course, such messages must be fragmented before being transmitted over an ethernet link.

If we collect a packet trace with tcpdump, we see an ARP request and ARP reply, then we see the 2nd segment of the first message - the first segment of the first message is never transmitted. A little tracing with GDB has shown that:

1. udp_output calls ip_output
2. ip_output calls ether_output
3. ether_output calls arpresolve
4. arpresolve calls arprequest
5. arprequest sends the ARP request message (recurses into ether_output), then returns to arpresolve.
6. arpresolve apparently operates asynchronously, and returns 0, indicating that address resolution is not yet complete. (Meanwhile, in due time, an ARP reply is received, providing the needed remote host's ethernet address.)
7. ether_output returns 0 to ip_output, indicating no errors. In effect, the first segment has been dropped.
8. ip_output believes that all is well with the first segment and proceeds to send the second. By now, the ARP resolution process has completed, and the second segment is transmitted normally.


Is this normal? If not, do you have any idea what we might we be doing wrong that could lead to this behavior?



I don't think this is normal, but UDP is by design not guaranteed reliable.


Would it be possible to post your program(s) so they could be
tested on other systems?

n.b. eCos 2.0 is now more than 3 years old - quite a lot has happened
in the meantime :-)



/*
** udp_server.c
**
** Based on network_interface.c from the netvision program.
**
** Initiated: 9/6/05
*/

#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>

#define MAX_PACKET_SIZE 80000
#define SOCK_PORT 5691

typedef struct {
	unsigned short sequence;
	unsigned short flags;
	unsigned long params;
} MessageHeaderType;

static int sock_fd, sock_port;
static unsigned char buffer[MAX_PACKET_SIZE];

int get_packet(void *packet, int length)
/*
** Receive a packet on the datagram socket, and place the packet in <packet>.
*/
{
	int n, cli_len;
	int echo_len = sizeof(MessageHeaderType);
	struct sockaddr_in cli_addr;

	cli_len = sizeof(cli_addr);
	n = recvfrom(
		sock_fd,
		(char *) packet, length,
		0, 
		(struct sockaddr *)&cli_addr, &cli_len
	);
	if(n < 0) {
		perror("recvfrom");
		return -1;
	}
	if(sendto(
		sock_fd,
		(char *) packet, echo_len,
		0,
		(struct sockaddr *)&cli_addr, cli_len
	) != echo_len) {
		perror("sendto");
		return -1;
	}

	return n;
}

int socket_init(void)
/*
*/
{
	struct sockaddr_in serv_addr;
	int one = 1;
	int i;

	/*
	** Fill in some fields -
	*/
	sock_port = SOCK_PORT;
	sock_fd = -1;

	/*
	** Open the UDP socket -
	*/
	sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sock_fd < 0) {
		perror("socket");
		return -1;
	}

	if(setsockopt(
		sock_fd,
		SOL_SOCKET,
		SO_REUSEADDR,
		(char *)&one,
		sizeof(one)
	) < 0) {
		perror("setsockopt");
		return -1;
	}

	/*
	** Bind our local address so that the client can send to us -
	*/
	memset((char *) &serv_addr, 0, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_addr.sin_port = htons(sock_port);

	if(bind(
		sock_fd,
		(struct sockaddr *) &serv_addr,
		sizeof(serv_addr)
	) < 0) {
		perror("bind");
		return -1;
	}

	return 0;
}

int main()
{
	int i, n, total_bytes = 0;
	time_t start, run;
	double elapsed;

	if(socket_init() < 0) {
		printf("Error initializing UDP socket\n");
		return 1;
	}

	printf("Elapsed Time\tTotal Bytes\tThroughput\n");
	start = time(NULL);
	for(i = 1; ; i++) {
		n = get_packet(buffer, sizeof(buffer));
		if(n < 0) {
			printf("get_packet returned error code %d\n", n);
		}
		else {
			total_bytes += n;
		}
		if((i % 1000) == 0) {
			run = time(NULL);
			elapsed = difftime(run, start);
			printf(
				"%g\t%d\t%g\n",
				elapsed,
				total_bytes,
				(double) total_bytes / elapsed
			);
		}
	}
	return 0;
}

CC = gcc
CCFLAGS = -O2

all : udp_server

clean :
	rm udp_server

udp_server : udp_server.c
	$(CC) $(CCFLAGS) -o $@ $<

/*
** File:  udp_client.c
**
*/

#include <stdio.h>
#include <network.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <cyg/kernel/kapi.h>
#include <pkgconf/hal.h>

#define SERVER_IP_ADDRESS "10.10.1.10"
#define ServerUDPport 5691
#define MAX_MESSAGE_SIZE 80000

static double AckTimeout = 10.0;

/*
** Define some local state to be associated with the UDP socket upon
** which all data transfers will take place.
*/
static int local_sock_fd;
static struct sockaddr_in local_sockaddr;
static struct sockaddr_in remote_sockaddr;

typedef struct {
	unsigned short sequence;
	unsigned short flags;
	unsigned long params;
} MessageHeaderType;

static char buffer[MAX_MESSAGE_SIZE];

/*
** Function:   sendMessage
**
** Purpose:    This function sends a message over the
**             network to the host and receives the corresponding
**             acknowledgement.  If the acknowledgement is not
**             received within an acceptable time limit, the
**             message is retransmitted.  This is repeated until
**             the acknowledgement is received.
**
** Parameters:
**             Name           Purpose
**             ----           -------
**             message        A pointer to the message to be transferred.
**
**             length         Size of the message.
**
** Returns:    Nothing.
*/
void sendMessage(void *message, int length)
{
	MessageHeaderType ackMsg, *msg = message;
	static unsigned short sequence = 0;
	int n;
	bool xmitRequired = true;

	msg->sequence = ++sequence;
	for( ; ; ) {
		if(xmitRequired) {
			/*
			** Send the message and check for errors -
			*/
			n = length;
			if(sendto(
		  		local_sock_fd,
		  		message,
		  		n,
				0,
		  		(struct sockaddr *) &remote_sockaddr,
		  		sizeof(remote_sockaddr)
			) != n) {
				perror("image_transfer_8bpp::sendMessage: sendto");
				_exit(errno);
			}
		}

		/*
		** Receive the acknowledgement with an awareness that the operation
		** may time out.
		*/
		n = recvfrom(
			local_sock_fd,
			&ackMsg,
			sizeof(ackMsg),
			0,
			(struct sockaddr *) 0,
			(socklen_t *) 0
		);
		if(n < 0) {
			if(errno == ETIMEDOUT) {
				/*
				** The recvfrom system call timed out.  Begin the process
				** again starting with retransmission.
				*/
				xmitRequired = true;
				continue;
			}
			else {
				/*
				** The recvfrom system call failed -
				*/
				perror("image_transfer_8bpp::sendMessage: recvfrom");
				_exit(errno);
			}
		}
		else {
			if(ackMsg.sequence == sequence) {
				/*
				** We received the correct acknowledgement for the message
				** sent.  Exit the loop.
				*/
				break;
			}
			else {
				/*
				** We received a message, but the sequence field does not
				** match that of the message we sent.  This could be a
				** duplicate acknowledgement from an earlier transmission.
				** swallow this acknowledgement and attempt to receive
				** another, but don't retransmit.
				*/
				xmitRequired = false;
				continue;
			}
		}

	} // end for( ; ; )
}

/*
** Function:   initialize
**
** Purpose:    Perform all initialization required to start up the network
**             transfer service for 8-bit-per-pixel image data.  This
**             includes opening the transfer socket, creating the
**             synchronization objects, and creating and starting up the
**             server thread.
**
** Parameters: None.
**
** Returns:    Nothing.
*/
void initialize(void)
{
	struct timeval ackTimeout;

	/*
	** Bring up the TCP/IP stack -
	*/
	init_all_network_interfaces();

	/*
	** Open our UDP socket and establish the receive timeout value.
	*/
	local_sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
	if(local_sock_fd < 0) {
		perror("image_transfer_8bpp::initialize: socket");
		_exit(errno);
	}

	ackTimeout.tv_sec = (long) AckTimeout;
	ackTimeout.tv_usec =
		(long) (1000000.0 * (AckTimeout - (double) ackTimeout.tv_sec));

	if(setsockopt(
		local_sock_fd,
		SOL_SOCKET,
		SO_RCVTIMEO,
		&ackTimeout,
		sizeof(ackTimeout)
	) < 0) {
		perror("image_transfer_8bpp::initialize: setsockopt");
		_exit(errno);
	}

	/*
	** Now bind the local socket to the correct address and port.
	*/
	memset(&local_sockaddr, 0, sizeof(local_sockaddr));
	local_sockaddr.sin_family = AF_INET;
	local_sockaddr.sin_len = sizeof(local_sockaddr);
	local_sockaddr.sin_port = htons(ServerUDPport);
	local_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	if(bind(
		local_sock_fd,
		(struct sockaddr *) &local_sockaddr,
		sizeof(local_sockaddr)
	) < 0) {
		perror("image_transfer_8bpp::initialize: bind");
		_exit(errno);
	}

	/*
	** Initialize the sockaddr for the remote (server) machine.
	*/
	memset(&remote_sockaddr, 0, sizeof(remote_sockaddr));
	remote_sockaddr.sin_family = AF_INET;
	remote_sockaddr.sin_addr.s_addr = inet_addr(SERVER_IP_ADDRESS);
	remote_sockaddr.sin_port = htons(ServerUDPport);
}



#define UDP_MAIN_THREAD_PRIORITY 16

static char         udp_main_stack[CYGNUM_HAL_STACK_SIZE_TYPICAL + 0x10000];
static cyg_thread   udp_main_object;
static cyg_handle_t udp_main_handle;

void udp_main(cyg_addrword_t param)
{
	char     userInput[128];
	int      i, msg_length = 2048;

	printf("net-speed:  A simple net performance test\n\n");

	for(i = 0; i < sizeof(buffer); i++) {
		buffer[i] = (char) i;
	}

	initialize();

	while(true) {
		sendMessage(buffer, msg_length);
	}
}

void cyg_user_start(void)
{
	cyg_thread_create(
		UDP_MAIN_THREAD_PRIORITY,
		udp_main,
		0,
		"UDP main thread",
		&udp_main_stack[0],
		sizeof(udp_main_stack),
		&udp_main_handle,
		&udp_main_object
	);

	cyg_thread_resume(udp_main_handle);

	cyg_scheduler_start();
}

TESTS = udp_client

ECOS_LIB = /space/ecos-2.0/latest/nga/full/no-assert

INCLUDE  = .
SOURCE   = .

vpath %.h $(INCLUDE)
vpath %.c $(SOURCE)

CC      = mips-elf-gcc
LD      = mips-elf-gcc
DIS     = mips-elf-objdump -d -S $@ > $@.dis
SGEN    = mips-elf-objcopy -O srec $@ $@.srec

CCFLAGS = -g -D__ECOS -I$(ECOS_LIB)/include -I$(INCLUDE) \
		-ffunction-sections -fdata-sections -freg-struct-return
LDFLAGS = -g -nostartfiles -L$(ECOS_LIB)/lib -Wl,--gc-sections -Wl,--Map -Wl,$@.map
LIBS    = -Ttarget.ld -nostdlib

.C.o :
	$(CPP) $(CCFLAGS) -c $<

.c.o :
	$(CC) $(CCFLAGS) -c $<

.S.o :
	$(AS) $(ASFLAGS) -c $<

%.po : %.o
	$(PRELINK) -o $@ $<

all : $(TESTS)

clean :
	rm -f $(TESTS) *.o *.dis *.map *.srec

relink :
	rm -f $(TESTS)

udp_client : udp_client.o
	$(LD) -o $@ udp_client.o $(LDFLAGS) $(LIBS)
	$(SGEN)
	$(DIS)

udp_client.o : udp_client.c


-- 
Before posting, please read the FAQ: http://ecos.sourceware.org/fom/ecos
and search the list archive: http://ecos.sourceware.org/ml/ecos-discuss

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]