Revision: 527
          http://vde.svn.sourceforge.net/vde/?rev=527&view=rev
Author:   danielel
Date:     2011-12-27 14:45:35 +0000 (Tue, 27 Dec 2011)
Log Message:
-----------
Added support for DHCP server
some minor bugfixes

Modified Paths:
--------------
    branches/vde-router/vde-2/src/vde_router/Makefile.am
    branches/vde-router/vde-2/src/vde_router/vde_headers.h
    branches/vde-router/vde-2/src/vde_router/vde_router.c
    branches/vde-router/vde-2/src/vde_router/vde_router.h
    branches/vde-router/vde-2/src/vde_router/vder_arp.c
    branches/vde-router/vde-2/src/vde_router/vder_arp.h
    branches/vde-router/vde-2/src/vde_router/vder_datalink.c
    branches/vde-router/vde-2/src/vde_router/vder_datalink.h
    branches/vde-router/vde-2/src/vde_router/vder_packet.c
    branches/vde-router/vde-2/src/vde_router/vder_packet.h
    branches/vde-router/vde-2/src/vde_router/vder_queue.c
    branches/vde-router/vde-2/src/vde_router/vder_queue.h

Added Paths:
-----------
    branches/vde-router/vde-2/src/vde_router/vder_dhcpd.c
    branches/vde-router/vde-2/src/vde_router/vder_dhcpd.h
    branches/vde-router/vde-2/src/vde_router/vder_udp.c
    branches/vde-router/vde-2/src/vde_router/vder_udp.h

Modified: branches/vde-router/vde-2/src/vde_router/Makefile.am
===================================================================
--- branches/vde-router/vde-2/src/vde_router/Makefile.am        2011-12-21 
17:28:47 UTC (rev 526)
+++ branches/vde-router/vde-2/src/vde_router/Makefile.am        2011-12-27 
14:45:35 UTC (rev 527)
@@ -13,6 +13,6 @@
 bin_PROGRAMS = vde_router
 vde_router_SOURCES = rbtree.h  vde_headers.h  vder_arp.h  vder_datalink.h  
vder_icmp.h \
     vde_router.h  vder_packet.h        vder_queue.h rbtree.c  vder_arp.c  
vder_datalink.c \
-    vder_icmp.c  vde_router.c  vder_packet.c  vder_queue.c
+    vder_icmp.c  vde_router.c  vder_packet.c  vder_queue.c vder_udp.c 
vder_dhcpd.c
 
 vde_router_LDADD = $(top_builddir)/src/common/libvdecommon.la 
$(top_builddir)/src/lib/libvdeplug.la -lpthread

Modified: branches/vde-router/vde-2/src/vde_router/vde_headers.h
===================================================================
--- branches/vde-router/vde-2/src/vde_router/vde_headers.h      2011-12-21 
17:28:47 UTC (rev 526)
+++ branches/vde-router/vde-2/src/vde_router/vde_headers.h      2011-12-27 
14:45:35 UTC (rev 527)
@@ -88,8 +88,9 @@
 #define check_destination(vb,mac) ( strncmp((ethhead(vb))->dst, mac) == 0)
 
 #define iphead(vb) ((struct iphdr *)(vb->data + 14))
+#define udp_pseudohead(vb) ((uint8_t *)(vb->data + 14 + sizeof(struct iphdr) - 
(2 * sizeof(uint32_t))))
 #define footprint(vb) ((uint8_t *)(vb->data + 14))
 #define arphead(vb) ((struct vde_arp_header *)(vb->data + 14))
-#define payload(vb) ((struct vde_ethernet_header *)(vb->data + 14 + 
sizeof(struct iphdr)))
+#define payload(vb) ((uint8_t *)(vb->data + 14 + sizeof(struct iphdr)))
 
 #endif

Modified: branches/vde-router/vde-2/src/vde_router/vde_router.c
===================================================================
--- branches/vde-router/vde-2/src/vde_router/vde_router.c       2011-12-21 
17:28:47 UTC (rev 526)
+++ branches/vde-router/vde-2/src/vde_router/vde_router.c       2011-12-27 
14:45:35 UTC (rev 527)
@@ -11,6 +11,7 @@
 #include "vde_router.h"
 #include "vder_queue.h"
 #include "vder_packet.h"
+#include "vder_dhcpd.h"
 #include <stdlib.h>
 #include <unistd.h>
 #include <stdio.h>
@@ -61,6 +62,7 @@
                printoutc(fd, "help         print a summary of mgmt commands. 
Use \"help <command>\" for details.");
                printoutc(fd, "connect      create a new interface connect it 
to vde socket");
                printoutc(fd, "ifconfig     show/change interface addresses 
configuration");
+               printoutc(fd, "dhcpd        start/stop dhcp server on a 
specific interface");
                printoutc(fd, "route        show/change routing table");
                printoutc(fd, "queue        show/change outgoing frames 
queues");
                printoutc(fd, "ipfilter     show/change ip filtering 
configuration");
@@ -98,6 +100,18 @@
                printoutc(fd, "ifconfig eth1 add 10.0.0.1 255.0.0.0");
                printoutc(fd, "ifconfig eth1 del 10.0.0.1");
                return 0;
+       } else if (match_input("dhcpd",arg)) {
+               printoutc(fd, "Syntax:");
+               printoutc(fd, "\tdhcpd start <devname> <dhcp_pool_start> 
<dhcp_pool_end>");
+               printoutc(fd, "--or--");
+               printoutc(fd, "\tdhcpd stop <devname>");
+               printoutc(fd, "Start/stop DHCP server on a specific interface. 
Devices/machines connected to the router");
+               printoutc(fd, "will be provided with a dynamic IP address on 
request.");
+               printoutc(fd, "");
+               printoutc(fd, "Examples:");
+               printoutc(fd, "dhcpd start eth0 10.0.0.101 10.0.0.120");
+               printoutc(fd, "dhcpd stop eth0");
+               return 0;
        } else if (match_input("route",arg)) {
                printoutc(fd, "Syntax:");
                printoutc(fd, "\troute [<action> <address> <netmask> [gw 
<gateway>] [via <interface>] [metric <metric>]]");
@@ -213,14 +227,6 @@
        exit(0);
 }
 
-static char *vder_ntoa(uint32_t addr)
-{
-       struct in_addr a;
-       char *res;
-       a.s_addr = addr;
-       res = inet_ntoa(a);
-       return res;
-}
 
 static int not_understood(int fd, char *s)
 {
@@ -625,7 +631,7 @@
                                        return EINVAL;
                                }
                        }
-               } else if (match_input("from",arg)){
+               } else if (match_input("from",arg)) {
                        arg = strtok_r(NULL, " ", &nextargs);
                        if (!arg)
                                return EINVAL;
@@ -642,7 +648,7 @@
                                printoutc(fd, "Invalid netmask \"%s\"", arg);
                                return EINVAL;
                        }
-               } else if (match_input("to",arg)){
+               } else if (match_input("to",arg)) {
                        arg = strtok_r(NULL, " ", &nextargs);
                        if (!arg)
                                return EINVAL;
@@ -659,7 +665,7 @@
                                printoutc(fd, "Invalid netmask \"%s\"", arg);
                                return EINVAL;
                        }
-               } else if (match_input("tos",arg)){
+               } else if (match_input("tos",arg)) {
                        arg = strtok_r(NULL, " ", &nextargs);
                        if (!arg)
                                return EINVAL;
@@ -668,7 +674,7 @@
                                printoutc(fd, "Invalid tos %s", arg);
                                return EINVAL;
                        }
-               } else if (match_input("sport",arg)){
+               } else if (match_input("sport",arg)) {
                        arg = strtok_r(NULL, " ", &nextargs);
                        if (!arg)
                                return EINVAL;
@@ -677,7 +683,7 @@
                                return EINVAL;
                        }
                        sport = htons(atoi(arg));
-               } else if (match_input("dport",arg)){
+               } else if (match_input("dport",arg)) {
                        arg = strtok_r(NULL, " ", &nextargs);
                        if (!arg)
                                return EINVAL;
@@ -686,7 +692,7 @@
                                return EINVAL;
                        }
                        dport = htons(atoi(arg));
-               } else if (match_input("prio",arg)){
+               } else if (match_input("prio",arg)) {
                        if (filter_action != filter_invalid) {
                                printoutc(fd, "Invalid double action for 
filter");
                        }
@@ -804,6 +810,8 @@
        return strtod(arg, NULL);
 }
 
+
+
 static int queue(int fd, char *s)
 {
        struct vder_iface *cur = Router.iflist, *selected = NULL;
@@ -974,6 +982,86 @@
        return 0;
 }
 
+#define DEFAULT_LEASE_TIME htonl(0xa8c0)
+static int dhcpd(int fd,char *s)
+{
+       char *nextargs = NULL, *arg;
+       struct vder_dhcpd_settings *dhcpd_settings;
+       struct vder_iface *selected = NULL;
+       struct in_addr temp_pool_start, temp_pool_end;
+       enum command_action_enum action = -1;
+
+       arg = strtok_r(s, " ", &nextargs);
+       if(!arg) {
+               printoutc(fd, "Error: arguments required");
+               return EINVAL;
+       }
+       if ((!arg) || (strlen(arg) < 4) || ((strncmp(arg, "start", 5) != 0) && 
(strncmp(arg, "stop", 4) != 0))) {
+               printoutc(fd, "Invalid action \"%s\".", arg);
+               return EINVAL;
+       }
+       if (strncmp(arg, "start", 5) == 0)
+               action = ACTION_ADD;
+       else
+               action = ACTION_DELETE;
+
+
+       arg = strtok_r(NULL, " ", &nextargs);
+       if (!arg) {
+               not_understood(fd, "");
+               return EINVAL;
+       }
+       if ((strlen(arg) < 4) || (strncmp(arg, "eth", 3)!= 0)) {
+               printoutc(fd, "Invalid interface \"%s\".", arg);
+               return EINVAL;
+       }
+       selected = select_interface(arg);
+       if (!selected)
+               return ENXIO;
+
+       if (action == ACTION_ADD) {
+               arg = strtok_r(NULL, " ", &nextargs);
+               if (!arg) {
+                       not_understood(fd, "");
+                       return EINVAL;
+               }
+
+               if (!inet_aton(arg, &temp_pool_start) || 
!is_unicast(temp_pool_start.s_addr)) {
+                       printoutc(fd, "Invalid pool start address \"%s\"", arg);
+                       return EINVAL;
+               }
+
+               arg = strtok_r(NULL, " ", &nextargs);
+               if (!arg) {
+                       not_understood(fd, "");
+                       return EINVAL;
+               }
+               if (!inet_aton(arg, &temp_pool_end) || 
!is_unicast(temp_pool_end.s_addr)) {
+                       printoutc(fd, "Invalid pool end address \"%s\"", arg);
+                       return EINVAL;
+               }
+
+               dhcpd_settings = malloc(sizeof(struct vder_dhcpd_settings));
+               if (!dhcpd_settings)
+                       return ENOMEM;
+
+               dhcpd_settings->iface = selected;
+               dhcpd_settings->my_ip = vder_get_right_localip(selected, 
temp_pool_start.s_addr);
+               dhcpd_settings->netmask = vder_get_netmask(selected, 
dhcpd_settings->my_ip);
+               dhcpd_settings->pool_start = temp_pool_start.s_addr;
+               dhcpd_settings->pool_end = temp_pool_end.s_addr;
+               dhcpd_settings->lease_time = DEFAULT_LEASE_TIME;
+               dhcpd_settings->flags = 0;
+               selected->dhcpd_started = 1;
+               pthread_create(&selected->dhcpd, 0, dhcp_server_loop, 
dhcpd_settings); 
+       } else if (selected->dhcpd_started) {
+               pthread_cancel(selected->dhcpd);
+               selected->dhcpd_started = 0;
+       }
+       return 0;
+}
+
+
 #define WITHFILE 0x80
 static struct comlist {
        char *tag;
@@ -987,6 +1075,7 @@
        {"stats", stats, WITHFILE},
        {"ipfilter", filter, WITHFILE},
        {"queue", queue, WITHFILE},
+       {"dhcpd", dhcpd, 0 },
        {"logout",logout, 0},
        {"shutdown",doshutdown, 0}
 };
@@ -1276,6 +1365,7 @@
                npfd++;
        }
 
+
        while(1) {
                n = poll(pfd, npfd, -1);
                if (n>0) {

Modified: branches/vde-router/vde-2/src/vde_router/vde_router.h
===================================================================
--- branches/vde-router/vde-2/src/vde_router/vde_router.h       2011-12-21 
17:28:47 UTC (rev 526)
+++ branches/vde-router/vde-2/src/vde_router/vde_router.h       2011-12-27 
14:45:35 UTC (rev 527)
@@ -163,6 +163,8 @@
        pthread_t sender;
        pthread_t receiver;
        pthread_t queue_manager;
+       pthread_t dhcpd;
+       int dhcpd_started;
        struct {
                uint32_t sent;
                uint32_t recvd;

Modified: branches/vde-router/vde-2/src/vde_router/vder_arp.c
===================================================================
--- branches/vde-router/vde-2/src/vde_router/vder_arp.c 2011-12-21 17:28:47 UTC 
(rev 526)
+++ branches/vde-router/vde-2/src/vde_router/vder_arp.c 2011-12-27 14:45:35 UTC 
(rev 527)
@@ -12,7 +12,7 @@
 #include <errno.h>
 #include "rbtree.h"
 
-static void add_arp_entry(struct vder_iface *vif, struct vder_arp_entry *p)
+void vder_add_arp_entry(struct vder_iface *vif, struct vder_arp_entry *p)
 {
        struct rb_node **link, *parent;
        uint32_t hostorder_ip = ntohl(p->ipaddr);
@@ -119,9 +119,36 @@
        memcpy(ae->macaddr,ah->s_mac,6);
        ae->ipaddr = ah->s_addr;
 
-       add_arp_entry(vif, ae);
+       vder_add_arp_entry(vif, ae);
 
        if(ntohs(ah->opcode) == ARP_REQUEST)
                vder_arp_reply(vif, vdb);
        return 0;
 }
+
+struct vder_arp_entry *vder_arp_get_record_by_macaddr(struct vder_iface *vif, 
uint8_t *mac)
+{
+       struct rb_node *node;
+       struct vder_arp_entry *found=NULL;
+       node = vif->arp_table.rb_node;
+       while(node) {
+               struct vder_arp_entry *entry = rb_entry(node, struct 
vder_arp_entry, rb_node);
+               if (memcmp(entry->macaddr, mac, ETHERNET_ADDRESS_SIZE) == 0) {
+                       found = entry;
+                       break;
+               }
+               node = node->rb_left;
+       }
+       if (found)
+               return found;
+       node = vif->arp_table.rb_node;
+       while(node) {
+               struct vder_arp_entry *entry = rb_entry(node, struct 
vder_arp_entry, rb_node);
+               if (memcmp(entry->macaddr, mac, ETHERNET_ADDRESS_SIZE) == 0) {
+                       found = entry;
+                       break;
+               }
+               node = node->rb_right;
+       }
+       return found;
+}

Modified: branches/vde-router/vde-2/src/vde_router/vder_arp.h
===================================================================
--- branches/vde-router/vde-2/src/vde_router/vder_arp.h 2011-12-21 17:28:47 UTC 
(rev 526)
+++ branches/vde-router/vde-2/src/vde_router/vder_arp.h 2011-12-27 14:45:35 UTC 
(rev 527)
@@ -5,10 +5,16 @@
  */
 #ifndef __VDER_ARP
 #define __VDER_ARP
+void vder_add_arp_entry(struct vder_iface *vif, struct vder_arp_entry *p);
 struct vder_arp_entry *vder_get_arp_entry(struct vder_iface *vif, uint32_t 
addr);
 size_t vder_arp_query(struct vder_iface *oif, uint32_t tgt);
 size_t vder_arp_reply(struct vder_iface *oif, struct vde_buff *vdb);
 /* Parse an incoming arp packet */;
 int vder_parse_arp(struct vder_iface *vif, struct vde_buff *vdb);
+
+
+/* O(N) search by macaddr (required by dhcp server) */
+struct vder_arp_entry *vder_arp_get_record_by_macaddr(struct vder_iface *vif, 
uint8_t *mac);
+
 #endif
 

Modified: branches/vde-router/vde-2/src/vde_router/vder_datalink.c
===================================================================
--- branches/vde-router/vde-2/src/vde_router/vder_datalink.c    2011-12-21 
17:28:47 UTC (rev 526)
+++ branches/vde-router/vde-2/src/vde_router/vder_datalink.c    2011-12-27 
14:45:35 UTC (rev 527)
@@ -15,8 +15,8 @@
 #include <errno.h>
 #include <pthread.h>
 #include <semaphore.h>
-#include <stdio.h>
 #include <sys/time.h>
+#include <stdio.h>
 
 struct vde_router Router = {};
 
@@ -46,11 +46,7 @@
        vif->macaddr[5] = vif->interface_id;
 }
 
-/* Get TCP/UDP header ports */
 
-#define transport_sport(vdb) *((uint16_t *)((unsigned char*)(payload(vdb)) + 
0))
-#define transport_dport(vdb) *((uint16_t *)((unsigned char*)(payload(vdb)) + 
2))
-
 /* Queue management */
 
 static void queue_init(struct vder_queue *q)
@@ -60,87 +56,6 @@
        qunlimited_setup(q);
 }
 
-static void enqueue(struct vder_queue *q, struct vde_buff *b)
-{
-       pthread_mutex_lock(&q->lock);
-
-       if (!q->may_enqueue(q, b)) {
-               free(b);
-               pthread_mutex_unlock(&q->lock);
-               return;
-       }
-
-       b->next = NULL;
-       if (!q->head) {
-               q->head = b;
-               q->tail = b;
-       } else {
-               q->tail->next = b;
-               q->tail = b;
-       }
-       q->size += b->len;
-       q->n++;
-       pthread_mutex_unlock(&q->lock);
-       if (q->policy != QPOLICY_TOKEN) {
-               if (q->type == QTYPE_OUT)
-                       sem_post(&q->semaphore);
-               else
-                       sem_post(q->prio_semaphore);
-       }
-}
-
-static struct vde_buff *prio_dequeue(struct vder_iface *vif)
-{
-       struct vder_queue *q;
-       int i;
-       struct vde_buff *ret = NULL;
-       sem_wait(&vif->prio_semaphore);
-       for (i = 0; i < PRIO_NUM; i++) {
-               q = &(vif->prio_q[i]);
-               pthread_mutex_lock(&q->lock);
-               if (q->size == 0){
-                       pthread_mutex_unlock(&q->lock);
-                       continue;
-               }
-               if (q->n) {
-                       ret = q->head;
-                       q->head = ret->next;
-                       q->n--;
-                       q->size -= ret->len;
-                       if (q->n == 0) {
-                               q->tail = NULL;
-                               q->head = NULL;
-                       }
-                       pthread_mutex_unlock(&q->lock);
-                       break;
-               }
-               pthread_mutex_unlock(&q->lock);
-       }
-       return ret;
-}
-
-static struct vde_buff *dequeue(struct vder_queue *q)
-{
-       struct vde_buff *ret = NULL;
-       if (q->type == QTYPE_OUT)
-               sem_wait(&q->semaphore);
-       else
-               return NULL;
-       pthread_mutex_lock(&q->lock);
-       if (q->n) {
-               ret = q->head;
-               q->head = ret->next;
-               q->n--;
-               q->size -= ret->len;
-               if (q->n == 0) {
-                       q->tail = NULL;
-                       q->head = NULL;
-               }
-       }
-       pthread_mutex_unlock(&q->lock);
-       return ret;
-}
-
 #define microseconds(tv) (unsigned long long)((tv.tv_sec * 1000000) + 
(tv.tv_usec));
 
 static void *vder_timer_loop(void *arg)
@@ -241,6 +156,27 @@
        return 0U;
 }
 
+uint32_t vder_get_netmask(struct vder_iface *vif, uint32_t localip)
+{
+       struct vder_ip4address *cur = vif->address_list;
+       while(cur) {
+               if (cur->address == localip)
+                       return cur->netmask;
+               cur = cur->next;
+       }
+       return 0U;
+}
+
+uint32_t vder_get_network(uint32_t localip, uint32_t netmask)
+{
+       return (localip & netmask);
+}
+
+uint32_t vder_get_broadcast(uint32_t localip, uint32_t netmask)
+{
+       return (localip | (~netmask));
+}
+
 /* insert route, ordered by netmask, metric.
  *  Default gw will be the last ones.
  */
@@ -543,8 +479,26 @@
        return 0;
 }
 
+int vder_ipaddress_is_broadcast(uint32_t addr) 
+{
+       struct vder_iface *iface = Router.iflist;
+       if (addr == (uint32_t)(-1))
+               return 1;
+       while (iface) {
+               struct vder_ip4address *cur = iface->address_list;
+               while(cur) {
+                       if (((cur->address & cur->netmask) == (addr & 
cur->netmask)) && ((cur->netmask | addr) == 0xFFFFFFFF)) {
+                               return 1;
+                       }
+                       cur = cur->next;
+               }
+               iface = iface->next;
+       }
+       return 0;
+}
 
 
+
 /* IP filter management */
 int vder_filter_del(struct vder_iface *src, uint8_t proto,
                uint32_t saddr_address, uint32_t saddr_netmask,

Modified: branches/vde-router/vde-2/src/vde_router/vder_datalink.h
===================================================================
--- branches/vde-router/vde-2/src/vde_router/vder_datalink.h    2011-12-21 
17:28:47 UTC (rev 526)
+++ branches/vde-router/vde-2/src/vde_router/vder_datalink.h    2011-12-27 
14:45:35 UTC (rev 527)
@@ -9,19 +9,26 @@
 #include "vde_headers.h"
 #include "vde_router.h"
 
+
 /* Global router initialization */
 void vderouter_init(void);
 
 /* Route management */
 
 uint32_t vder_get_right_localip(struct vder_iface *vif, uint32_t dst);
+uint32_t vder_get_netmask(struct vder_iface *vif, uint32_t localip);
+uint32_t vder_get_network(uint32_t localip, uint32_t netmask);
+uint32_t vder_get_broadcast(uint32_t localip, uint32_t netmask);
+
 int vder_route_add(uint32_t address, uint32_t netmask, uint32_t gateway, 
uint16_t metric, struct vder_iface *dst);
 int vder_route_del(uint32_t address, uint32_t netmask, int metric);
 struct vder_route * vder_get_route(uint32_t address);
 int vder_default_route(uint32_t gateway, int metric);
 uint32_t vder_get_right_localip(struct vder_iface *vif, uint32_t dst);
 int vder_ipaddress_is_local(uint32_t addr);
+int vder_ipaddress_is_broadcast(uint32_t addr);
 
+
 /* Interface management */
 
 struct vder_iface *vder_iface_new(char *sock, uint8_t *macaddr);
@@ -59,4 +66,9 @@
                enum filter_action action, uint8_t priority);
 
 int vder_filter(struct vde_buff *buf);
+
+/* Get TCP/UDP header ports */
+#define transport_sport(vdb) *((uint16_t *)((unsigned char*)(payload(vdb)) + 
0))
+#define transport_dport(vdb) *((uint16_t *)((unsigned char*)(payload(vdb)) + 
2))
+
 #endif

Added: branches/vde-router/vde-2/src/vde_router/vder_dhcpd.c
===================================================================
--- branches/vde-router/vde-2/src/vde_router/vder_dhcpd.c                       
        (rev 0)
+++ branches/vde-router/vde-2/src/vde_router/vder_dhcpd.c       2011-12-27 
14:45:35 UTC (rev 527)
@@ -0,0 +1,232 @@
+#include "vder_udp.h"
+#include "vder_arp.h"
+#include "vder_dhcpd.h"
+#include <stdio.h>
+
+static struct vder_dhcp_negotiation *Negotiation_list;
+static struct vder_udp_socket *udpsock;
+static struct vder_dhcpd_settings Settings;
+
+static struct vder_dhcp_negotiation *
+get_negotiation_by_xid(uint32_t xid)
+{
+       struct vder_dhcp_negotiation *cur = Negotiation_list;
+       while (cur) {
+               if (cur->xid == xid)
+                       return cur;
+               cur = cur->next;
+       }
+       return NULL;
+}
+
+static uint8_t dhcp_get_next_option(uint8_t *begin, uint8_t *data, int *len, 
uint8_t **nextopt)
+{
+       uint8_t *p;
+       uint8_t type;
+       uint8_t opt_len;
+
+       if (!begin)
+               p = *nextopt;
+       else
+               p = begin;
+
+       type = *p;
+       *nextopt = ++p;
+       if ((type == DHCPOPT_END) || (type == DHCPOPT_PAD)) {
+               memset(data, 0, *len);
+               len = 0;
+               return type;
+       }
+       opt_len = *p;
+       p++;
+       if (*len > opt_len)
+               *len = opt_len;
+       memcpy(data, p, *len);
+       *nextopt = p + opt_len;
+       return type;
+}
+
+static int is_options_valid(uint8_t *opt_buffer, int len)
+{
+       uint8_t *p = opt_buffer;
+       while (len > 0) {
+               if (*p == DHCPOPT_END)
+                       return 1;
+               else if (*p == DHCPOPT_PAD) {
+                       p++;
+                       len--;
+               } else {
+                       uint8_t opt_len;
+                       p++;
+                       len--;
+                       opt_len = *p;
+                       p += opt_len + 1;
+                       len -= opt_len;
+               }
+       }
+       return 0;
+}
+
+#define DHCP_OFFER_SIZE 308
+#define OPENDNS (htonl(0xd043dede))
+
+static void dhcpd_make_reply(struct vder_dhcp_negotiation *dn, uint8_t 
reply_type)
+{
+
+       uint8_t buf_out[DHCP_OFFER_SIZE] = {0};
+       struct dhcphdr *dh_out = (struct dhcphdr *) buf_out;
+       uint32_t server_address = vder_get_right_localip(Settings.iface, 
Settings.pool_next);
+       uint32_t netmask = vder_get_netmask(Settings.iface, server_address);
+       uint32_t bcast = vder_get_broadcast(server_address, netmask);
+       uint32_t dns_server = OPENDNS;
+
+       int sent = 0;
+
+
+       memcpy(dh_out->hwaddr, dn->hwaddr, HLEN_ETHER);
+       dh_out->op = DHCP_OP_REPLY;
+       dh_out->htype = HTYPE_ETHER;
+       dh_out->hlen = HLEN_ETHER;
+       dh_out->xid = dn->xid;
+       dh_out->yiaddr = dn->arp->ipaddr;
+       dh_out->siaddr = server_address;
+       dh_out->dhcp_magic = DHCPD_MAGIC_COOKIE;
+
+       /* Option: msg type, len 1 */
+       dh_out->options[0] = DHCPOPT_MSGTYPE;
+       dh_out->options[1] = 1;
+       dh_out->options[2] = reply_type;
+
+       /* Option: server id, len 4 */
+       dh_out->options[3] = DHCPOPT_SERVERID;
+       dh_out->options[4] = 4;
+       memcpy(dh_out->options + 5, &server_address, 4);
+
+       /* Option: Lease time, len 4 */
+       dh_out->options[9] = DHCPOPT_LEASETIME;
+       dh_out->options[10] = 4;
+       memcpy(dh_out->options + 11, &Settings.lease_time, 4);
+
+       /* Option: Netmask, len 4 */
+       dh_out->options[15] = DHCPOPT_NETMASK;
+       dh_out->options[16] = 4;
+       memcpy(dh_out->options + 17, &netmask, 4);
+
+       /* Option: Router, len 4 */
+       dh_out->options[21] = DHCPOPT_ROUTER;
+       dh_out->options[22] = 4;
+       memcpy(dh_out->options + 23, &server_address, 4);
+
+       /* Option: Broadcast, len 4 */
+       dh_out->options[27] = DHCPOPT_BCAST;
+       dh_out->options[28] = 4;
+       memcpy(dh_out->options + 29, &bcast, 4);
+
+       /* Option: DNS, len 4 */
+       dh_out->options[33] = DHCPOPT_DNS;
+       dh_out->options[34] = 4;
+       memcpy(dh_out->options + 35, &dns_server, 4);
+
+       dh_out->options[40] = DHCPOPT_END;
+
+       sent = vder_udpsocket_sendto(udpsock, buf_out, DHCP_OFFER_SIZE, 
dh_out->yiaddr, DHCP_CLIENT_PORT);
+       if (sent < 0) {
+               perror("udp sendto");
+       }
+}
+
+#define dhcpd_make_offer(x) dhcpd_make_reply(x, DHCP_MSG_OFFER)
+#define dhcpd_make_ack(x) dhcpd_make_reply(x, DHCP_MSG_ACK)
+
+#define ip_inrange(x) ((ntohl(x) >= ntohl(Settings.pool_start)) && (ntohl(x) 
<= ntohl(Settings.pool_end)))
+
+static void dhcp_recv(uint8_t *buffer, int len)
+{
+       struct dhcphdr *dhdr = (struct dhcphdr *) buffer;
+       struct vder_dhcp_negotiation *dn = get_negotiation_by_xid(dhdr->xid);
+       uint8_t *nextopt, opt_data[20], opt_type;
+       int opt_len = 20;
+
+
+       if (!is_options_valid(dhdr->options, len - sizeof(struct dhcphdr)))
+               return;
+
+
+
+       if (!dn) {
+               dn = malloc(sizeof(struct vder_dhcp_negotiation));
+               memset(dn, 0, sizeof(struct vder_dhcp_negotiation));
+               dn->xid = dhdr->xid;
+               dn->state = DHCPSTATE_DISCOVER;
+               memcpy(dn->hwaddr, dhdr->hwaddr, HLEN_ETHER);
+               dn->next = Negotiation_list;
+               Negotiation_list = dn;
+               dn->arp = vder_arp_get_record_by_macaddr(Settings.iface, 
dn->hwaddr);
+               if (!dn->arp) {
+                       dn->arp = malloc(sizeof(struct vder_arp_entry));
+                       if (!dn->arp)
+                               return;
+                       memcpy(dn->arp->macaddr, dn->hwaddr, HLEN_ETHER);
+                       dn->arp->ipaddr = Settings.pool_next;
+                       Settings.pool_next = htonl(ntohl(Settings.pool_next) + 
1);
+                       vder_add_arp_entry(Settings.iface, dn->arp);
+               }
+       }
+
+       if (!ip_inrange(dn->arp->ipaddr))
+               return;
+
+
+       opt_type = dhcp_get_next_option(dhdr->options, opt_data, &opt_len, 
&nextopt);
+       while (opt_type != DHCPOPT_END) {
+               /* parse interesting options here */
+               if (opt_type == DHCPOPT_MSGTYPE) {
+
+                       /* server simple state machine */
+                       uint8_t msg_type = opt_data[0];
+                       if (msg_type == DHCP_MSG_DISCOVER) {
+                               dhcpd_make_offer(dn);
+                               dn->state = DHCPSTATE_OFFER;
+                               return;
+                       } else if (msg_type == DHCP_MSG_REQUEST) {
+                               dhcpd_make_ack(dn);
+                               return;
+                       }
+               }
+               opt_len = 20;
+               opt_type = dhcp_get_next_option(NULL, opt_data, &opt_len, 
&nextopt);
+       }
+}
+
+
+void *dhcp_server_loop(void *ptr_settings)
+{
+       uint32_t from_ip;
+       uint16_t from_port;
+
+       unsigned char buffer[2000];
+       int len;
+
+       memcpy(&Settings, ptr_settings, sizeof(struct vder_dhcpd_settings));
+       Settings.pool_next = Settings.pool_start;
+       free(ptr_settings);
+
+
+       if(!Settings.iface)
+               return NULL;
+       udpsock = vder_udpsocket_open(DHCPD_PORT);
+       if (!udpsock) {
+               return NULL;
+       }
+
+       while(1) {
+               len = vder_udpsocket_recvfrom(udpsock, buffer, 2000, &from_ip, 
&from_port);
+               if (len < 0) {
+                       perror("udp recv");
+                       return NULL;
+               }
+               if ((from_ip == 0) && (from_port == DHCP_CLIENT_PORT)) {
+                       dhcp_recv(buffer, len);
+               }
+       }
+}

Added: branches/vde-router/vde-2/src/vde_router/vder_dhcpd.h
===================================================================
--- branches/vde-router/vde-2/src/vde_router/vder_dhcpd.h                       
        (rev 0)
+++ branches/vde-router/vde-2/src/vde_router/vder_dhcpd.h       2011-12-27 
14:45:35 UTC (rev 527)
@@ -0,0 +1,100 @@
+#ifndef __VDER_DHCPD
+#define __VDER_DHCPD
+
+#include "vder_arp.h"
+
+#define DHCPD_PORT (htons(67))
+#define DHCP_CLIENT_PORT (htons(68))
+
+
+#define DHCP_GATEWAY 0x01
+#define DHCP_DNS 0x02
+
+struct vder_dhcpd_settings
+{
+       struct vder_iface *iface;
+       uint32_t my_ip;
+       uint32_t netmask;
+       uint32_t pool_start;
+       uint32_t pool_next;
+       uint32_t pool_end;
+       unsigned long lease_time;
+       uint8_t flags;
+};
+
+#define DHCP_OP_REQUEST 1
+#define DHCP_OP_REPLY   2
+
+#define HTYPE_ETHER 1
+#define HLEN_ETHER 6
+
+#define FLAG_BROADCAST (htons(0xF000))
+
+#define DHCPD_MAGIC_COOKIE (htonl(0x63825363))
+
+/* DHCP OPTIONS, RFC2132 */
+#define DHCPOPT_PAD                    0x00
+#define DHCPOPT_NETMASK                0x01
+#define DHCPOPT_ROUTER                         0x03
+#define DHCPOPT_DNS                            0x06
+#define DHCPOPT_BCAST                  0x1c
+#define DHCPOPT_REQIP                  0x32
+#define DHCPOPT_LEASETIME              0x33
+#define DHCPOPT_MSGTYPE                        0x35
+#define DHCPOPT_SERVERID               0x36
+#define DHCPOPT_PARMLIST               0x37
+#define DHCPOPT_RENEWALTIME    0x3a
+#define DHCPOPT_REBINDINGTIME  0x3b
+#define DHCPOPT_END                    0xFF
+
+/* DHCP MESSAGE TYPE */
+#define DHCP_MSG_DISCOVER              1
+#define DHCP_MSG_OFFER                         2
+#define DHCP_MSG_REQUEST               3
+#define DHCP_MSG_DECLINE               4
+#define DHCP_MSG_ACK                   5
+#define DHCP_MSG_NAK                   6
+#define DHCP_MSG_RELEASE               7
+#define DHCP_MSG_INFORM                        8
+
+
+struct __attribute__((packed)) dhcphdr
+{
+       uint8_t op;
+       uint8_t htype;
+       uint8_t hlen;
+       uint8_t hops; //zero
+       uint32_t xid; //store this in the request
+       uint16_t secs; // ignore
+       uint16_t flags;
+       uint32_t ciaddr; // client address - if asking for renewal
+       uint32_t yiaddr; // your address (client)
+       uint32_t siaddr; // dhcp offered address
+       uint32_t giaddr; // relay agent, bootp.
+       uint8_t hwaddr[6];
+       uint8_t hwaddr_padding[10];
+       char    hostname[64];
+       char    bootp_filename[128]; 
+       uint32_t dhcp_magic;
+       uint8_t options[0];
+};
+
+enum dhcp_negotiation_state {
+       DHCPSTATE_DISCOVER = 0,
+       DHCPSTATE_OFFER,
+       DHCPSTATE_REQUEST,
+       DHCPSTATE_ACK
+};
+
+struct vder_dhcp_negotiation {
+       struct vder_dhcp_negotiation *next;
+       uint32_t xid;
+       uint8_t hwaddr[6];
+       uint32_t assigned_address;
+       enum dhcp_negotiation_state state;
+       struct vder_arp_entry *arp;
+};
+
+void *dhcp_server_loop(void *ptr_iface);
+
+#endif

Modified: branches/vde-router/vde-2/src/vde_router/vder_packet.c
===================================================================
--- branches/vde-router/vde-2/src/vde_router/vder_packet.c      2011-12-21 
17:28:47 UTC (rev 526)
+++ branches/vde-router/vde-2/src/vde_router/vder_packet.c      2011-12-27 
14:45:35 UTC (rev 527)
@@ -6,14 +6,26 @@
 #include "vder_datalink.h"
 #include "vder_arp.h"
 #include "vder_icmp.h"
+#include "vder_udp.h"
 #include <sys/poll.h>
 #include <unistd.h>
 #include <string.h>
 #include <stdlib.h>
 #include <errno.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
 
 #define MAX_PACKET_SIZE 2000
 
+char *vder_ntoa(uint32_t addr)
+{
+       struct in_addr a;
+       char *res;
+       a.s_addr = addr;
+       res = inet_ntoa(a);
+       return res;
+}
+
 /*
  * Forward the ip packet to next hop. TTL is decreased,
  * checksum is set again for coherence, and TTL overdue
@@ -33,7 +45,7 @@
  */
 uint16_t net_checksum(void *inbuf, int len)
 {
-       uint8_t *buf = (uint8_t *) buf;
+       uint8_t *buf = (uint8_t *) inbuf;
        uint32_t sum = 0, carry=0;
        int i=0;
        for(i=0; i<len; i++){
@@ -62,14 +74,25 @@
 int vder_ip_input(struct vde_buff *vb)
 {
        struct iphdr *iph = iphead(vb);
-       if (vder_ipaddress_is_local(iph->daddr)) {
-               if (iph->protocol == PROTO_ICMP)
+       int recvd = 0;
+       int is_broadcast = vder_ipaddress_is_broadcast(iph->daddr);
+
+
+       if (!vder_ipaddress_is_local(iph->daddr) && !is_broadcast)
+               return 0;
+       switch(iph->protocol) {
+               case PROTO_ICMP:
                        vder_icmp_recv(vb);
-               else
-                       vder_icmp_service_unreachable((uint32_t)iph->saddr, 
footprint(vb));
-               return 1;
+                       recvd=1;
+                       break;
+               case PROTO_UDP:
+                       if (vder_udp_recv(vb) == 1)
+                               recvd=1;
+                       break;
        }
-       return 0;
+       if (!recvd && !is_broadcast)
+               vder_icmp_service_unreachable((uint32_t)iph->saddr, 
footprint(vb));
+       return 1;
 }
 
 int vder_packet_send(struct vde_buff *vdb, uint32_t dst_ip, uint8_t protocol)
@@ -142,7 +165,7 @@
 
                        if (vder_ip_input(packet)) {
                                /* If the packet is for us, process it here. */
-                               free(packet);
+                               //free(packet);
                                return;
                        } else {
                                struct iphdr *hdr = iphead(packet);

Modified: branches/vde-router/vde-2/src/vde_router/vder_packet.h
===================================================================
--- branches/vde-router/vde-2/src/vde_router/vder_packet.h      2011-12-21 
17:28:47 UTC (rev 526)
+++ branches/vde-router/vde-2/src/vde_router/vder_packet.h      2011-12-27 
14:45:35 UTC (rev 527)
@@ -8,8 +8,9 @@
 
 #define DEFAULT_TTL 64
 uint16_t vder_ip_checksum(struct iphdr *iph);
-struct vde_buff *vder_packet_recv(struct vder_iface *vif, int timeout);
+void vder_packet_recv(struct vder_iface *vif, int timeout);
 uint16_t net_checksum(void *inbuf, int len);
 int vder_packet_send(struct vde_buff *vdb, uint32_t dst_ip, uint8_t protocol);
+char *vder_ntoa(uint32_t addr);
 
 #endif

Modified: branches/vde-router/vde-2/src/vde_router/vder_queue.c
===================================================================
--- branches/vde-router/vde-2/src/vde_router/vder_queue.c       2011-12-21 
17:28:47 UTC (rev 526)
+++ branches/vde-router/vde-2/src/vde_router/vder_queue.c       2011-12-27 
14:45:35 UTC (rev 527)
@@ -6,6 +6,88 @@
 #include "vder_queue.h"
 #include "vde_router.h"
 #include <stdlib.h>
+
+void enqueue(struct vder_queue *q, struct vde_buff *b)
+{
+       pthread_mutex_lock(&q->lock);
+
+       if (!q->may_enqueue(q, b)) {
+               free(b);
+               pthread_mutex_unlock(&q->lock);
+               return;
+       }
+
+       b->next = NULL;
+       if (!q->head) {
+               q->head = b;
+               q->tail = b;
+       } else {
+               q->tail->next = b;
+               q->tail = b;
+       }
+       q->size += b->len;
+       q->n++;
+       pthread_mutex_unlock(&q->lock);
+       if (q->policy != QPOLICY_TOKEN) {
+               if (q->type != QTYPE_PRIO)
+                       sem_post(&q->semaphore);
+               else
+                       sem_post(q->prio_semaphore);
+       }
+}
+
+struct vde_buff *prio_dequeue(struct vder_iface *vif)
+{
+       struct vder_queue *q;
+       int i;
+       struct vde_buff *ret = NULL;
+       sem_wait(&vif->prio_semaphore);
+       for (i = 0; i < PRIO_NUM; i++) {
+               q = &(vif->prio_q[i]);
+               pthread_mutex_lock(&q->lock);
+               if (q->size == 0){
+                       pthread_mutex_unlock(&q->lock);
+                       continue;
+               }
+               if (q->n) {
+                       ret = q->head;
+                       q->head = ret->next;
+                       q->n--;
+                       q->size -= ret->len;
+                       if (q->n == 0) {
+                               q->tail = NULL;
+                               q->head = NULL;
+                       }
+                       pthread_mutex_unlock(&q->lock);
+                       break;
+               }
+               pthread_mutex_unlock(&q->lock);
+       }
+       return ret;
+}
+
+struct vde_buff *dequeue(struct vder_queue *q)
+{
+       struct vde_buff *ret = NULL;
+       if (q->type != QTYPE_PRIO)
+               sem_wait(&q->semaphore);
+       else
+               return NULL;
+       pthread_mutex_lock(&q->lock);
+       if (q->n) {
+               ret = q->head;
+               q->head = ret->next;
+               q->n--;
+               q->size -= ret->len;
+               if (q->n == 0) {
+                       q->tail = NULL;
+                       q->head = NULL;
+               }
+       }
+       pthread_mutex_unlock(&q->lock);
+       return ret;
+}
+
 /* Unlimited policy */
 int qunlimited_may_enqueue(struct vder_queue *q, struct vde_buff *b)
 {

Modified: branches/vde-router/vde-2/src/vde_router/vder_queue.h
===================================================================
--- branches/vde-router/vde-2/src/vde_router/vder_queue.h       2011-12-21 
17:28:47 UTC (rev 526)
+++ branches/vde-router/vde-2/src/vde_router/vder_queue.h       2011-12-27 
14:45:35 UTC (rev 527)
@@ -8,6 +8,9 @@
 #include <stdint.h>
 #include "vde_router.h"
 #include "vder_datalink.h"
+void enqueue(struct vder_queue *q, struct vde_buff *b);
+struct vde_buff *prio_dequeue(struct vder_iface *vif);
+struct vde_buff *dequeue(struct vder_queue *q);
 
 void qunlimited_setup(struct vder_queue *q);
 void qfifo_setup(struct vder_queue *q, uint32_t limit);

Added: branches/vde-router/vde-2/src/vde_router/vder_udp.c
===================================================================
--- branches/vde-router/vde-2/src/vde_router/vder_udp.c                         
(rev 0)
+++ branches/vde-router/vde-2/src/vde_router/vder_udp.c 2011-12-27 14:45:35 UTC 
(rev 527)
@@ -0,0 +1,140 @@
+#include "vder_udp.h"
+#include <stdio.h>
+
+/* UDP header, rfc 768 */
+
+struct __attribute__((packed)) udphdr {
+       uint16_t sport, dport, len, crc;
+};
+
+static struct vder_udp_socket *socket_list = NULL;
+
+
+/* interface toward the router */
+int vder_udp_recv(struct vde_buff *buf)
+{
+       struct vder_udp_socket *cur = socket_list;
+       int found = 0;
+       struct vde_buff *copy = NULL;
+       uint16_t port = transport_dport(buf);
+       while(cur) {
+               if (cur->port == port) {
+                       if (!found) {
+                               enqueue(&cur->inq, buf);
+                               found = 1;
+                       } else {
+                               copy = malloc(sizeof(struct vde_buff) + 
buf->len);
+                               if (!copy)
+                                       break;
+                               memcpy(copy, buf, sizeof(struct vde_buff) + 
buf->len);
+                               enqueue(&cur->inq, copy);
+                       }
+               }
+               cur = cur->next;
+       }
+       return found;
+}
+
+struct vder_udp_socket *vder_udpsocket_open(uint16_t port)
+{
+
+       struct vder_udp_socket *vu;
+
+       if (port == 0) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       vu = malloc(sizeof(struct vder_udp_socket));
+       if (!vu)
+               return NULL;
+
+       memset(&vu->inq, 0, sizeof(struct vder_queue));
+       pthread_mutex_init(&vu->inq.lock, NULL);
+       qfifo_setup(&vu->inq, UDPSOCK_BUFFER_SIZE);
+       vu->port = port;
+       vu->next = socket_list;
+       socket_list = vu;
+       return vu;
+}
+
+void vder_udp_close(struct vder_udp_socket *sock)
+{
+       struct vder_udp_socket *prev = NULL, *cur = socket_list;
+       while(cur) {
+               if (cur == sock) {
+                       if (!prev) {
+                               socket_list = cur->next;
+                       } else {
+                               prev->next = cur->next;
+                       }
+                       free(sock);
+                       return;
+               }
+               prev = cur;
+               cur = cur->next;
+       }
+}
+
+
+int vder_udpsocket_sendto(struct vder_udp_socket *sock, void *data, size_t 
len, uint32_t dst, uint16_t dstport)
+{
+       struct vde_buff *b;
+       struct udphdr *uh;
+       uint8_t *datagram;
+       struct vder_route *ro;
+       int bufsize;
+       if (len <= 0) {
+               errno = EINVAL;
+               return -1;
+       }
+       ro = vder_get_route(dst);
+       if (!ro) {
+               errno = EHOSTUNREACH;
+               return -1;
+       }
+
+       bufsize = sizeof(struct vde_buff) + sizeof(struct vde_ethernet_header) 
+ sizeof(struct iphdr) + sizeof(struct udphdr) + len;
+       b = malloc(bufsize);
+       if (!b)
+               return -1;
+       b->len = bufsize - sizeof(struct vde_buff);
+       b->src = NULL;
+       b->priority = PRIO_BESTEFFORT;
+       uh = (struct udphdr *) payload(b);
+       datagram = (uint8_t *)((payload(b) + sizeof(struct udphdr)));
+       memcpy(datagram, data, len);
+
+       uh->sport = sock->port;
+       uh->dport = dstport;
+       uh->len = htons(len);
+       uh->crc = 0;
+       vder_packet_send(b, dst, PROTO_UDP);
+       return len;
+}
+
+
+int vder_udpsocket_recvfrom(struct vder_udp_socket *sock, void *data, size_t 
len, uint32_t *from, uint16_t *fromport)
+{
+       struct vde_buff *b;
+       struct udphdr *uh;
+       uint8_t *datagram;
+
+       if (len <= 0) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       do {
+               b = dequeue(&sock->inq);
+       } while(!b);
+       uh = (struct udphdr *) payload(b);
+       datagram = (uint8_t *)(payload(b) + sizeof(struct udphdr));
+       if (ntohs(uh->len) < len)
+               len = ntohs(uh->len);
+       memcpy(data, datagram, len);
+       *fromport = uh->sport;
+       return len;
+}
+
+

Added: branches/vde-router/vde-2/src/vde_router/vder_udp.h
===================================================================
--- branches/vde-router/vde-2/src/vde_router/vder_udp.h                         
(rev 0)
+++ branches/vde-router/vde-2/src/vde_router/vder_udp.h 2011-12-27 14:45:35 UTC 
(rev 527)
@@ -0,0 +1,32 @@
+#include "vde_headers.h"
+#include "vde_router.h"
+#include "vder_queue.h"
+#include "vder_datalink.h"
+#include "vder_packet.h"
+#include <stdint.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef __VDER_UDP_H
+#define __VDER_UDP_H
+
+
+struct vder_udp_socket {
+       struct vder_udp_socket *next;
+       uint16_t port;
+       struct vder_queue inq;
+};
+
+#define UDPSOCK_BUFFER_SIZE 1024 * 16
+
+struct vder_udp_socket *get_by_port(uint16_t port);
+
+
+/* interface toward the router */
+int vder_udp_recv(struct vde_buff *buf);
+struct vder_udp_socket *vder_udpsocket_open(uint16_t port);
+void vder_udp_close(struct vder_udp_socket *sock);
+int vder_udpsocket_sendto(struct vder_udp_socket *sock, void *data, size_t 
len, uint32_t dst, uint16_t dstport);
+int vder_udpsocket_recvfrom(struct vder_udp_socket *sock, void *data, size_t 
len, uint32_t *from, uint16_t *fromport);
+
+#endif

This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.


------------------------------------------------------------------------------
Write once. Port to many.
Get the SDK and tools to simplify cross-platform app development. Create 
new or port existing apps to sell to consumers worldwide. Explore the 
Intel AppUpSM program developer opportunity. appdeveloper.intel.com/join
http://p.sf.net/sfu/intel-appdev
_______________________________________________
vde-users mailing list
vde-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/vde-users

Reply via email to