libports: add lwIP tests for Pandaboard on foc

This commit is contained in:
Josef Söntgen 2012-09-03 16:16:26 +02:00 committed by Norman Feske
parent b22920c3a7
commit 6259fcf736
11 changed files with 960 additions and 0 deletions

View File

@ -0,0 +1,117 @@
#
# \brief Test for using the lwIP TCP/IP stack
# \author Norman Feske
# \date 2011-05-22
#
# This test case executes a small HTTP server on Genode running on qemu. When
# the HTTP server is up, a HTTP request to the server is performed using
# 'lynx'. The response is validated against a known pattern.
#
# The test uses qemu's "-net user" option, redirecting Genode's port 80 to the
# host's port 5555. Consequently, it cannot be executed on non-qemu test
# environments (i.e., the test won't work with the Linux version of Genode).
#
# Please make sure to include a nic driver in your build configuration. E.g.,
# on the x86 platform, you may enable the 'linux_drivers' repository.
#
#
# TODO: Add support for Linux via user-level networking (using the
# tun/tap proxy driver at os/src/drivers/nic/linux)
#
if {[have_spec linux]} {
puts "Run script does not support Linux."; exit 0 }
requires_installation_of lynx
#
# Build
#
build {
core init
drivers/pci drivers/timer drivers/nic
test/lwip/http_srv_tracing
}
create_boot_directory
#
# Generate config
#
set config {
<config verbose="yes">
<parent-provides>
<service name="ROM"/>
<service name="RAM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="CAP"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
<service name="SIGNAL"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<start name="timer">
<resource name="RAM" quantum="568K"/>
<provides> <service name="Timer"/> </provides>
</start>
<start name="nic_drv">
<resource name="RAM" quantum="2M"/>
<provides> <service name="Nic"/> </provides>
</start>
<start name="test-lwip_httpsrv_tracing">
<resource name="RAM" quantum="64M"/>
</start> }
append_if [have_spec pci] config {
<start name="pci_drv">
<resource name="RAM" quantum="1M"/>
<provides> <service name="PCI"/> </provides>
</start> }
append config {
</config>
}
install_config $config
#
# Boot modules
#
# generic modules
set boot_modules {
core init timer
nic_drv
ld.lib.so libc.lib.so libc_log.lib.so lwip.lib.so
test-lwip_httpsrv_tracing
}
# platform-specific modules
lappend_if [have_spec pci] boot_modules pci_drv
build_boot_image $boot_modules
#
# Execute test case
#
# qemu config
append qemu_args " -m 128 -nographic -serial mon:stdio "
append_if [have_spec x86] qemu_args " -net nic,model=e1000 "
append_if [have_spec lan9118] qemu_args " -net nic,model=lan9118 "
append qemu_args " -net user -redir tcp:5555::80 "
#run_genode_until {.*Sent response.*} 60
run_genode_until forever
# vi: set ft=tcl :

View File

@ -0,0 +1,75 @@
# vi: set ft=tcl :
assert_spec foc
assert_spec platform_panda
#
# Build
#
build {
core
init
drivers/timer
drivers/usb
test/lwip/http_srv_tracing_nonblocking
}
create_boot_directory
#
# Config
#
set config {
<config prio_levels="2">
<parent-provides>
<service name="ROM"/>
<service name="RAM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="CAP"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
<service name="SIGNAL"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
</start>
<start name="usb_drv">
<resource name="RAM" quantum="6M"/>
<provides>
<service name="Input"/>
<service name="Nic"/>
</provides>
<config>
<hid/>
<nic mac="2e:60:90:0c:4e:01" />
</config>
</start>
<start name="test-lwip_httpsrv_tracing_nob">
<resource name="RAM" quantum="64M"/>
</start>
</config>}
install_config $config
#
# Boot modules
#
set boot_modules {
core
init
timer
usb_drv
test-lwip_httpsrv_tracing_nob
ld.lib.so libc.lib.so libc_log.lib.so lwip.lib.so
}
build_boot_image [join $boot_modules " "]

View File

@ -0,0 +1,75 @@
# vi: set ft=tcl :
assert_spec foc
assert_spec platform_panda
#
# Build
#
build {
core
init
drivers/timer
drivers/usb
test/lwip/http_srv_tracing
}
create_boot_directory
#
# Config
#
set config {
<config prio_levels="2">
<parent-provides>
<service name="ROM"/>
<service name="RAM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="CAP"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
<service name="SIGNAL"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
</start>
<start name="usb_drv">
<resource name="RAM" quantum="12M"/>
<provides>
<service name="Input"/>
<service name="Nic"/>
</provides>
<config>
<hid/>
<nic mac="2e:60:90:0c:4e:01" />
</config>
</start>
<start name="test-lwip_httpsrv_tracing">
<resource name="RAM" quantum="64M"/>
</start>
</config>}
install_config $config
#
# Boot modules
#
set boot_modules {
core
init
timer
usb_drv
test-lwip_httpsrv_tracing
ld.lib.so libc.lib.so libc_log.lib.so lwip.lib.so
}
build_boot_image [join $boot_modules " "]

View File

@ -0,0 +1,23 @@
TARGET = httpsrv_connect
SRC = main.c net.c
CFLAGS = -Wall -Wextra -Wno-unused
ifeq ($(time),1)
CC_DEF += -DUGLY_MEASURE_TIME
endif
LDFLAGS = -pthread
OBJS = $(SRC:.c=.o)
.PHONY: clean
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@
.c.o:
$(CC) -c $(CFLAGS) $(CC_DEF) $^ -o $@
clean:
-rm -rf ./$(TARGET) ./*.o

View File

@ -0,0 +1,2 @@
This is just a simple test client for http_srv, that runs on the host
system. Therefore it does not use the Genode buildsystem.

View File

@ -0,0 +1,197 @@
/*
* \brief Simple http_srv client
* \author Josef Soentgen
* \date 2012-8-30
*/
/*
* Copyright (C) 2012 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>
/* prototypes for net.c functions */
extern int dial(struct addrinfo*);
extern struct addrinfo *lookup(const char*, const char*, const char*);
/* pthread error handling */
#define handle_error_en(en, msg) \
do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
/* send request, currently hardcoded and works with httpsrv test only */
const char req[] = "GET /";
/* thread arguments */
struct args {
struct addrinfo* ai;
int flags;
int count;
int verbose;
};
/* receive flag */
enum { F_NONE, F_RECV };
/* pthread start function */
void *
run(void *arg)
{
char buf[1 << 20]; /* 1 MiB should be enough */
struct args *ap = (struct args *) arg;
int count = ap->count;
int flags = ap->flags;
int verbose = ap->verbose;
int i, s, nbytes, sum_nbytes;
for (i = 0; i < count; i++) {
s = dial(ap->ai); /* create socket and connect */
if (s == -1)
break;
/* only send/receive if flag is set { */
if (flags & F_RECV) {
/* first send request and second get header */
write(s, req, sizeof(req));
nbytes = read(s, buf, sizeof (buf));
/* get data */
sum_nbytes = 0;
#if UGLY_MEASURE_TIME
struct timeval start, end;
gettimeofday(&start, NULL);
#endif
do {
/**
* override buffer because we don't care about
* the actual data.
*/
nbytes = read(s, buf, sizeof (buf));
sum_nbytes += nbytes;
} while (nbytes > 0 && nbytes != -1);
if (nbytes == -1)
perror("read");
#if UGLY_MEASURE_TIME
gettimeofday(&end, NULL);
int sec = end.tv_sec - start.tv_sec;
int usec = end.tv_usec - start.tv_usec;
if (usec < 0) {
usec += 1000000;
sec--;
}
printf("time: %d ms\n",
((sec) * 1000) + ((usec) / 1000));
#endif
if (verbose)
printf("bytes read: %d\n", sum_nbytes);
}
/* } */
close(s);
}
/* XXX s == -1 handling */
return NULL;
}
int
main(int argc, char *argv[])
{
struct args a;
struct addrinfo *ai;
const char *proto = "tcp", *host, *port;
int i, err, verbose = 0, flags = F_NONE, count = 1, threads = 1;
pthread_t *thread;
if (argc < 3) {
fprintf(stderr, "usage: %s [-vr] [-c count] [-t threads] "
"[-p protocol] <host> <port>\n", argv[0]);
exit(1);
}
/* argument parsing { */
while ((i = getopt(argc, argv, "c:p:t:rv")) != -1) {
switch (i) {
case 'c': count = atoi(optarg); break;
/**
* valid values are only 'tcp' and 'udp'
*/
case 'p': proto = optarg; break;
case 't': threads = atoi(optarg); break;
/**
* If receive is specified we will read from the socket,
* just connect to the host otherwise.
*/
case 'r': flags |= F_RECV; break;
case 'v': verbose = 1; break;
default: break;
}
}
if ((argc - optind) != 2) {
fprintf(stderr, "missing host and/or port\n");
exit(1);
}
host = argv[optind++];
port = argv[optind];
/* } */
/* main { */
thread = (pthread_t *) calloc(threads, sizeof (pthread_t));
if (thread == NULL) {
perror("calloc");
exit(-1);
}
/* get addrinfo once and use it for all threads */
ai = lookup(proto, host, port);
if (ai == NULL) {
exit(-1);
}
/* fill thread argument structure */
a.ai = ai;
a.count = count / threads; /* XXX check if -c > -t */
a.flags = flags;
a.verbose = verbose;
printf("connect to '%s!%s!%s' roughly %d times, %d per thread\n",
proto, host, port, count, a.count);
for (i = 0; i < threads; i++) {
err = pthread_create(&thread[i], NULL, run, (void*)&a);
if (err != 0)
handle_error_en(err, "pthread_create");
}
for (i = 0; i < threads; i++) {
err = pthread_join(thread[i], NULL);
if (err != 0)
handle_error_en(err, "pthread_create");
}
freeaddrinfo(ai);
free(thread);
/* } */
return 0;
}

View File

@ -0,0 +1,89 @@
/*
* \brief Simple http_srv client
* \author Josef Soentgen
* \date 2012-8-30
*/
/*
* Copyright (C) 2012 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
dial(struct addrinfo *ai)
{
struct addrinfo *aip;
int s, err;
for (aip = ai; aip != NULL; aip->ai_next) {
s = socket(aip->ai_family, aip->ai_socktype,
aip->ai_protocol);
if (s == -1) {
perror("socket");
continue;
}
err = connect(s, aip->ai_addr, aip->ai_addrlen);
if (err != -1)
break;
close(s);
s = -1;
}
return s;
}
struct addrinfo *
lookup(const char *proto, const char *host, const char *port)
{
struct addrinfo hints, *r;
int err, socktype, protocol;
if (proto == NULL) {
fprintf(stderr, "protocol is not set\n");
return NULL;
}
/**
* actually we can simply use protcol == 0 but we
* set it explicitly anyway.
*/
if (strcmp(proto, "tcp") == 0) {
socktype = SOCK_STREAM;
protocol = IPPROTO_TCP;
}
else if (strcmp(proto, "udp") == 0) {
socktype = SOCK_DGRAM;
protocol = IPPROTO_UDP;
}
else {
fprintf(stderr, "protocol '%s' invalid\n", proto);
return NULL;
}
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET;
hints.ai_socktype = socktype;
hints.ai_flags = 0;
hints.ai_protocol = protocol;
err = getaddrinfo(host, port, &hints, &r);
if (err != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(err));
return NULL;
}
return r;
}

View File

@ -0,0 +1,148 @@
/*
* \brief Minimal HTTP server lwIP demonstration
* \author lwIP Team
* \author Stefan Kalkowski
* \date 2009-10-23
*
* This small example shows how to use the LwIP in Genode directly.
* If you simply want to use LwIP's socket API, you might use
* Genode's libc together with its LwIP backend, especially useful
* when porting legacy code.
*/
/*
* Copyright (C) 2009-2012 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <base/printf.h>
#include <base/thread.h>
#include <util/string.h>
/* LwIP includes */
extern "C" {
#include <lwip/sockets.h>
#include <lwip/api.h>
}
#include <lwip/genode.h>
namespace Fiasco {
#include <l4/sys/ktrace.h>
}
//enum { FILE_SIZE = 1UL }; /* 1 Byte */
enum { FILE_SIZE = 3072UL }; /* 3 KiB */
//enum { FILE_SIZE = 5120UL }; /* 5 KiB */
//enum { FILE_SIZE = 8388608UL }; /* 8 MiB */
//enum { FILE_SIZE = 2097152UL }; /* 2 MiB */
//enum { FILE_SIZE = 16777216UL }; /* 16 MiB */
//enum { FILE_SIZE = 33554432UL }; /* 32 MiB */
const static char http_html_hdr[] =
"HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n"; /* HTTP response header */
static char http_file_data[FILE_SIZE];
/**
* Handle a single client's request.
*
* \param conn socket connected to the client
*/
void http_server_serve(int conn) {
char buf[1024];
ssize_t buflen;
/* Read the data from the port, blocking if nothing yet there.
We assume the request (the part we care about) is in one packet */
buflen = lwip_recv(conn, buf, 1024, 0);
//PLOG("Packet received!");
/* Ignore all receive errors */
if (buflen > 0) {
/* Is this an HTTP GET command? (only check the first 5 chars, since
there are other formats for GET, and we're keeping it very simple)*/
if (buflen >= 5 &&
buf[0] == 'G' &&
buf[1] == 'E' &&
buf[2] == 'T' &&
buf[3] == ' ' &&
buf[4] == '/' ) {
//PLOG("Will send response");
/* Send http header */
//Fiasco::fiasco_tbuf_log_3val(">> lwip_send", Genode::strlen(http_html_hdr), 0, 0);
lwip_send(conn, http_html_hdr, Genode::strlen(http_html_hdr), 0);
//Fiasco::fiasco_tbuf_log("<< lwip_send");
/*
unsigned int val = 0xdeadbeef;
Fiasco::fiasco_tbuf_log_3val(">> lwip_send", sizeof (unsigned int), 0, 0);
lwip_send(conn, &val, sizeof (unsigned int), 0);
Fiasco::fiasco_tbuf_log("<< lwip_send");
*/
lwip_send(conn, http_file_data, sizeof (http_file_data), 0);
}
}
}
#include <l4/sys/kdebug.h>
int main()
{
int s;
lwip_tcpip_init();
/* Initialize network stack and do DHCP */
if (lwip_nic_init(0, 0, 0)) {
PERR("We got no IP address!");
return -1;
}
PLOG("Create new socket ...");
if((s = lwip_socket(AF_INET, SOCK_STREAM, 0)) < 0) {
PERR("No socket available!");
return -1;
}
PLOG("Now, I will bind ...");
struct sockaddr_in in_addr;
in_addr.sin_family = AF_INET;
in_addr.sin_port = htons(80);
in_addr.sin_addr.s_addr = INADDR_ANY;
if(lwip_bind(s, (struct sockaddr*)&in_addr, sizeof(in_addr))) {
PERR("bind failed!");
return -1;
}
PLOG("Now, I will listen ...");
if(lwip_listen(s, 5)) {
PERR("listen failed!");
return -1;
}
PLOG("Start the server loop ...");
while(true) {
struct sockaddr addr;
socklen_t len = sizeof(addr);
int client = lwip_accept(s, &addr, &len);
if(client < 0) {
PWRN("Invalid socket from accept!");
continue;
}
http_server_serve(client);
// PLOG("Sent response, closing connection");
lwip_close(client);
}
return 0;
}

View File

@ -0,0 +1,6 @@
TARGET = test-lwip_httpsrv_tracing
LIBS = cxx env lwip libc libc_log
SRC_CC = main.cc
REQUIRES = foc
INC_DIR += $(REP_DIR)/src/lib/lwip/include

View File

@ -0,0 +1,222 @@
/*
* \brief Minimal HTTP server lwIP demonstration
* \author lwIP Team
* \author Stefan Kalkowski
* \date 2009-10-23
*
* This small example shows how to use the LwIP in Genode directly.
* If you simply want to use LwIP's socket API, you might use
* Genode's libc together with its LwIP backend, especially useful
* when porting legacy code.
*/
/*
* Copyright (C) 2009-2012 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <base/printf.h>
#include <base/thread.h>
#include <util/string.h>
/* LwIP includes */
extern "C" {
#include <lwip/sockets.h>
#include <lwip/api.h>
}
#include <lwip/genode.h>
#include <errno.h>
//enum { FILE_SIZE = 8388608UL }; /* 8 MiB */
//enum { FILE_SIZE = 2097152UL }; /* 2 MiB */
//enum { FILE_SIZE = 16777216UL }; /* 16 MiB */
//enum { FILE_SIZE = 33554432UL }; /* 32 MiB */
enum { FILE_SIZE = 5120UL }; /* 5 KiB */
enum { MAX_CLIENTS = 1024 };
enum { TRY_TO_CONNECT = 100 };
const static char http_html_hdr[] =
"HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n"; /* HTTP response header */
static char http_file_data[FILE_SIZE];
struct clients {
int fd;
struct sockaddr addr;
socklen_t len;
};
static struct clients c[MAX_CLIENTS];
/**
* Handle a single client's request.
*
* \param conn socket connected to the client
*/
void http_server_serve(int conn) {
char buf[1024];
ssize_t buflen;
/* Read the data from the port, blocking if nothing yet there.
We assume the request (the part we care about) is in one packet */
buflen = lwip_recv(conn, buf, 1024, 0);
PLOG("Packet received!");
/* Ignore all receive errors */
if (buflen > 0) {
/* Is this an HTTP GET command? (only check the first 5 chars, since
there are other formats for GET, and we're keeping it very simple)*/
if (buflen >= 5 &&
buf[0] == 'G' &&
buf[1] == 'E' &&
buf[2] == 'T' &&
buf[3] == ' ' &&
buf[4] == '/' ) {
PLOG("Will send response");
/* Send http header */
lwip_send(conn, http_html_hdr, Genode::strlen(http_html_hdr), 0);
lwip_send(conn, http_file_data, FILE_SIZE, 0);
}
}
}
#include <l4/sys/kdebug.h>
int main()
{
int s, c_num;
fd_set rs, ws, es;
lwip_tcpip_init();
/* Initialize network stack and do DHCP */
if (lwip_nic_init(0, 0, 0)) {
PERR("We got no IP address!");
return -1;
}
PLOG("Create new socket ...");
if((s = lwip_socket(AF_INET, SOCK_STREAM, 0)) < 0) {
PERR("No socket available!");
return -1;
}
PLOG("Now, I will bind to port 80 ...");
struct sockaddr_in in_addr;
in_addr.sin_family = AF_INET;
in_addr.sin_port = htons(80);
in_addr.sin_addr.s_addr = INADDR_ANY;
memset(&(in_addr.sin_zero), '\0', 8);
if (lwip_bind(s, (struct sockaddr*)&in_addr, sizeof(in_addr))) {
PERR("bind failed!");
return -1;
}
PLOG("Now, I will listen ...");
if (lwip_listen(s, 5)) {
PERR("listen failed!");
return -1;
}
PLOG("Make socket non-blocking ...");
if (lwip_fcntl(s, F_SETFL, O_NONBLOCK)) {
PERR("fcntl() failed!");
return -1;
}
struct timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 0;
for (int i = 0; i < MAX_CLIENTS; i++)
c[i].fd = -1;
c_num = 0;
PLOG("Start the server loop ...");
while(true) {
/* clear fds */
FD_ZERO(&rs);
FD_ZERO(&ws);
FD_ZERO(&es);
/* set fds */
FD_SET(s, &rs);
for (int i = 0, num = c_num; i < MAX_CLIENTS; i++) {
if (c[i].fd != -1) {
FD_SET(c[i].fd, &rs);
if (num > 0)
num--;
else
break;
}
}
//PLOG("before select, c_num: %d", c_num);
int ready = lwip_select(c_num + 1, &rs, &ws, &es, &timeout);
if (ready > 0) {
if (FD_ISSET(s, &rs)) {
for (int i = 0; i < TRY_TO_CONNECT; i++) {
int *fd = &c[c_num].fd;
struct sockaddr *addr = &c[c_num].addr;
socklen_t *len = &c[c_num].len;
*fd = lwip_accept(s, addr, len);
if (*fd < 0) {
/* there is currently nobody waiting */
if (errno == EWOULDBLOCK)
break;
//PWRN("Invalid socket from accept!");
continue;
}
if (lwip_fcntl(*fd, F_SETFL, O_NONBLOCK)) {
//PERR("fcntl() failed");
lwip_close(*fd);
continue;
}
c_num++;
//PLOG("add client %d", c_num);
}
}
for (int i = 0, num = c_num; i < MAX_CLIENTS; i++) {
int *fd = &c[i].fd;
if (*fd != -1) {
if (FD_ISSET(*fd, &rs)) {
http_server_serve(*fd);
//PLOG("Send response, closing connection");
lwip_close(*fd);
c_num--;
*fd = -1;
//PLOG("after close, c_num: %d", c_num);
}
if (num > 0)
num--;
else
break;
}
}
}
}
return 0;
}

View File

@ -0,0 +1,6 @@
TARGET = test-lwip_httpsrv_tracing_nob
LIBS = cxx env lwip libc libc_log
SRC_CC = main.cc
REQUIRES = foc
INC_DIR += $(REP_DIR)/src/lib/lwip/include