This is the mail archive of the
ecos-discuss@sourceware.org
mailing list for the eCos project.
Re: [Fwd: FreeBSD network stack question]
- From: Barry Wealand <barry dot wealand at lmco dot com>
- To: Gary Thomas <gary at mlbassoc dot com>
- Cc: eCos Discussion <ecos-discuss at ecos dot sourceware dot org>, "Mills, Ted" <ted dot mills at lmco dot com>, "Reagan, John T" <john dot t dot reagan at lmco dot com>
- Date: Wed, 19 Oct 2005 12:04:46 +0000
- Subject: Re: [ECOS] [Fwd: FreeBSD network stack question]
- References: <43561EFF.8030200@lmco.com> <1129744855.9769.7.camel@hermes>
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