commit-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[SCM] Debian GNU Hurd packaging branch, upstream-merged, updated. upstre


From: Samuel Thibault
Subject: [SCM] Debian GNU Hurd packaging branch, upstream-merged, updated. upstream/20130707-5-g6e7f76d
Date: Sat, 27 Jul 2013 22:25:38 +0000

The following commit has been merged in the upstream-merged branch:
commit 18e5ac60b48d9158203b33f5656ee8cad8a469a2
Author: Samuel Thibault <address@hidden>
Date:   Sat Jul 27 21:08:48 2013 +0000

    New upstream snapshot

diff --git a/eth-filter.multi-thread/ChangeLog 
b/eth-filter.multi-thread/ChangeLog
new file mode 100644
index 0000000..cd92006
--- /dev/null
+++ b/eth-filter.multi-thread/ChangeLog
@@ -0,0 +1,105 @@
+2008-08-22 Zheng Da <address@hidden>
+
+       * README: Update.
+
+       * filter.c (proxy_info): Removed.
+       (proxy_pfinetpi_ht): Removed.
+       (proxy_devicepi_ht): Removed.
+       (create_proxy): Removed.
+       (destroy_proxy): Removed.
+       (proxy_device): New structure.
+       (proxy_user): New structure.
+       (proxy): New structure.
+       (create_proxy_user): New function.
+       (create_proxy_device): New function.
+       (clean_proxy_user): New function.
+       (clean_proxy_device): New function.
+       (ethernet_demuxer): Get the data from proxy object instead of from
+       proxy_info.
+       (do_mach_notify_dead_name): Likewise.
+       (ds_device_write): Likewise.
+       (ds_device_write_inband): Likewise.
+       (ds_device_read): Likewise.
+       (ds_device_read_inband): Likewise.
+       (ds_device_map): Likewise.
+       (ds_device_set_status): Likewise.
+       (ds_device_get_status): Likewise.
+       (ds_device_set_filter): Likewise. Create the proxy_device object.
+       (do_mach_notify_no_senders): Use ports_do_mach_notify_no_senders().
+       (ds_device_open): Create proxy_user and proxy objects.
+       (main): Add the cleaning routine when creating the port class.
+
+2008-08-20 Zheng Da <address@hidden>
+
+       * README: Update.
+
+       * filter.c (options): Update.
+
+2008-08-20 Zheng Da <address@hidden>
+
+       * filter.c (destroy_proxy): Test p_deliverport_hashloc before removing
+       it from proxy_deliverport_ht, and reverse the order of calling
+       ports_destroy_right and ports_port_deref.
+       (ds_device_open): Test device_file and replace "eth" with the variable
+       name.
+
+2008-08-20 Zheng Da <address@hidden>
+
+       * filter.c (device_file): Change the name of a variable.
+       (ds_device_open): Use device_file directly.
+
+2008-08-19 Zheng Da <address@hidden>
+
+       * filter.c (ds_device_open): Generate the device file name, and use it
+       to open the device.
+
+2008-08-18 Zheng Da <address@hidden>
+
+       * README: New file.
+
+
+2008-08-17 Zheng Da <address@hidden>
+
+       * filter.c (device_name): Remove its default value.
+       (options): Remove the option '-M'.
+       (create_proxy): Get the port directly from port_right field in 
port_info.
+       (ds_device_open): Remove the code of checking the device name, 
+       and open the device from the device file.
+       (parse_opt): Remove the code of handling the option '-M'.
+       (main): Remove the code of getting the master device port.
+       
+
+2008-08-14 Zheng Da <address@hidden>
+
+       * filter.c (pfinet_portclass, device_portclass, other_portclass): 
+       New variables.
+       (create_proxy): Use pfinet_portclass and device_portclass to create the
+       port.
+       (destroy_proxy): Dereference the port before destroying it.
+       (trivfs_goaway): Test the number of ports in pfinet_portclass and 
destroy
+       the master_device.
+       (main): Create pfinet_portclass, device_portclass, other_portclass.
+       (print_msg): Deleted.
+       (deliver_msg): Remove debuging print.
+
+       * ChangeLog: New file.
+       
+
+2008-08-13 Zheng Da <address@hidden>
+
+       * bpf_impl.c: New file.
+
+       * Makefile: New file.
+
+       * queue.c: New file.
+
+       * util.h: New file.
+
+       * bpf_impl.h: New file.
+
+       * filter.c: New file.
+
+       * pcap_filter.c: New file.
+
+       * queue.h: New file.
+
diff --git a/devnode/Makefile b/eth-filter.multi-thread/Makefile
similarity index 70%
copy from devnode/Makefile
copy to eth-filter.multi-thread/Makefile
index f452256..216664f 100644
--- a/devnode/Makefile
+++ b/eth-filter.multi-thread/Makefile
@@ -15,18 +15,22 @@
 # along with the GNU Hurd; see the file COPYING.  If not, write to
 # the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
-dir := devnode
+dir := eth-filter
 makemode := server
 
-SRCS = devnode.c
-LCLHDRS = util.h
-DIST_FILES = ourdevice.defs notify.defs
-HURDLIBS = ports trivfs fshelp shouldbeinlibc
-target = devnode
-MIGSTUBS = ourdeviceServer.o notifyServer.o
+SRCS = bpf_impl.c filter.c queue.c pcap_filter.c msg_queue.c
+LCLHDRS = bpf_impl.h queue.h util.h filter.h msg_queue.h
+DIST_FILES = ourdevice.defs notify.defs device_reply.defs
+HURDLIBS = ports trivfs fshelp ihash shouldbeinlibc
+OTHERLIBS = -lpthread
+target = eth-filter
+MIGSTUBS = ourdeviceServer.o notifyServer.o device_replyUser.o
 OBJS = $(SRCS:.c=.o) $(MIGSTUBS)
 
 include ../Makeconf
 
+CFLAGS += -I../pfinet/linux-src/include -I../pfinet/glue-include
+LDFLAGS += -lpcap
+
 ourdevice.defs: device.defs
        $(CPP) $(CPPFLAGS) -x c $< | sed -e '/out[      ]*device[       ]*:[    
]*device_t/s/device_t/mach_port_send_t/' > $@
diff --git a/eth-filter.multi-thread/README b/eth-filter.multi-thread/README
new file mode 100644
index 0000000..2ef16ed
--- /dev/null
+++ b/eth-filter.multi-thread/README
@@ -0,0 +1,39 @@
+[Introduce]
+
+eth-filter is a filter translator that runs on the network device. The goal of 
eth-filter is to allow the user to setup the policy to control the traffic to 
the network. For example, it can block the invalid packet or the packet with 
the wrong destination address. It can work with eth-multiplexer to control and 
reduce the traffic between eth-multiplexer and pfinet, and can also work alone, 
running directly on the real network device.
+
+
+[Usage]
+
+Usage: eth-filter [OPTION...]
+Hurd filter translator.
+
+  -i, --interface=DEVICE     Network interface to use
+  -s, --send-filter=string   The filter rule which applies to the outgoing
+                             packet
+  -r, --receive-filter=string   The filter rule which applies to the ingoing
+                             packet
+  -S, --send-ip-range=IP range   A range of IP to create the send filter
+  -R, --receive-ip-range=IP range
+                             A range of IP to create the receive filter
+  -?, --help                 Give this help list
+      --usage                Give a short usage message
+  -V, --version              Print program version
+
+Mandatory or optional arguments to long options are also mandatory or optional
+for any corresponding short options.
+
+
+The '-i' option specifies the network interface the translator sits on. 
eth-filter can only connect to one network interface and the '-i' option should 
be only used once. DEVICE is a device file created by devnode translator.
+The '-s' and '-r' options give the user a full control to specify the filter 
rules which applies to the outgoing packet and the incoming packet, 
respectively. The expression of the rule is the same as the one in TCPDUMP. 
+The '-S' and '-R' options specify a range of IP that the user wants to filter. 
They are used to create the sending filter and the receiving filter, 
respectively. The generated rule is "arp or (ip and src net addr/prefixlen)" or 
"arp or (ip and dst net addr/prefixlen)". If prefixlen is 32, it can be 
omitted. 
+NOTE: '-s' and '-S' cannot be used together. One will replace the other if 
they are both used. So are '-r' and '-R' options.
+An example: settrans -acfg /servers/feth0 /hurd/eth-filter -i /dev/eth0 -S 
192.168.8.0/24 -R 192.168.8.0/24
+
+[Internal]
+
+eth-filter works as a proxy, forwarding the packet between the user program 
and the network interface. In order to forward packets, eth-filter runs as a 
client to the network device. It opens the device and writes the packet to the 
network device as pfinet does. It calls device_set_filter() to set the filter 
rule and give its own port to the device so it can receive packets from the 
device. The rule passed to the network device is from the user program that 
connects to eth-filter.
+eth-filter works as a RPC server to communicate with the user program and 
implements the server side functions in device.defs. It gets the packet in the 
server side function ds_device_write and gets the port to deliver packets to 
the user program in ds_device_set_filter. 
+Three structures are used for one pair of the user program and the device: 
proxy_user, proxy_device, proxy. When the ds_device_open() is called, a 
proxy_user and proxy objectis created. A proxy_device object is created when 
the ds_device_set_filter() is called. The proxy_user and proxy_device extend 
port_info structure. When a packet is received from the user program or from 
the device, we have to retrieve the proxy object to get the corresponding 
information. This process is very similar as pflocal. When a user program 
exits, we need to destroy its proxy_user object and proxy object, and 
meanwhile, the proxy_device object related to the proxy object is also 
destroyed. 
+Two filters exist in eth-filter, one for outgoing packets and the other for 
incoming packets. These are BPF filters, which are ported from GNU Mach. These 
BPF filters only decide whether the packet should be forwarded, and they cannot 
decide the destination of the packet. The BPF instructions are generated by 
libpcap from the filter rules given by '-s' and '-r' or '-S' and '-R' options.
+
diff --git a/libbpf/bpf_impl.c b/eth-filter.multi-thread/bpf_impl.c
similarity index 87%
copy from libbpf/bpf_impl.c
copy to eth-filter.multi-thread/bpf_impl.c
index c8a250b..e9202a5 100644
--- a/libbpf/bpf_impl.c
+++ b/eth-filter.multi-thread/bpf_impl.c
@@ -45,7 +45,8 @@
 #include "queue.h"
 #include "util.h"
 
-static struct net_hash_header filter_hash_header[N_NET_HASH];
+queue_head_t rcv_port_list;    /* input filter list */
+queue_head_t snd_port_list;    /* output filter list */
 
 /*
  * Execute the filter program starting at pc on the packet p
@@ -58,7 +59,7 @@ static struct net_hash_header filter_hash_header[N_NET_HASH];
  */
 
 int
-bpf_do_filter(net_rcv_port_t infp, char *p,    unsigned int wirelen, 
+mach_bpf_do_filter(net_rcv_port_t infp, char *p,       unsigned int wirelen, 
                char *header, unsigned int hlen, net_hash_entry_t **hash_headpp,
                net_hash_entry_t *entpp)
 {
@@ -102,7 +103,7 @@ bpf_do_filter(net_rcv_port_t infp, char *p, unsigned int 
wirelen,
                                        A : wirelen;
 
                        case BPF_RET|BPF_MATCH_IMM:
-                               if (bpf_match ((net_hash_header_t)infp, pc->jt, 
mem,
+                               if (mach_bpf_match ((net_hash_header_t)infp, 
pc->jt, mem,
                                                        hash_headpp, entpp)) {
                                        return ((u_int)pc->k <= wirelen) ?
                                                pc->k : wirelen;
@@ -348,7 +349,7 @@ load_byte:
  * Otherwise, a bogus program could easily crash the system.
  */
 int
-bpf_validate(bpf_insn_t f, int bytes, bpf_insn_t *match)
+mach_bpf_validate(bpf_insn_t f, int bytes, bpf_insn_t *match)
 {
        register int i, j, len;
        register bpf_insn_t p;
@@ -419,7 +420,7 @@ bpf_validate(bpf_insn_t f, int bytes, bpf_insn_t *match)
 }
 
 int
-bpf_eq (bpf_insn_t f1, bpf_insn_t f2, int bytes)
+mach_bpf_eq (bpf_insn_t f1, bpf_insn_t f2, int bytes)
 {
        register int count;
 
@@ -436,7 +437,7 @@ bpf_eq (bpf_insn_t f1, bpf_insn_t f2, int bytes)
 }
 
 unsigned int
-bpf_hash (int n, unsigned int *keys)
+mach_bpf_hash (int n, unsigned int *keys)
 {
        register unsigned int hval = 0;
 
@@ -448,7 +449,7 @@ bpf_hash (int n, unsigned int *keys)
 
 
 int
-bpf_match (net_hash_header_t hash, int n_keys, unsigned int *keys,
+mach_bpf_match (net_hash_header_t hash, int n_keys, unsigned int *keys,
        net_hash_entry_t **hash_headpp, net_hash_entry_t *entpp)
 {
        register net_hash_entry_t head, entp;
@@ -457,7 +458,7 @@ bpf_match (net_hash_header_t hash, int n_keys, unsigned int 
*keys,
        if (n_keys != hash->n_keys)
                return FALSE;
 
-       *hash_headpp = &hash->table[bpf_hash(n_keys, keys)];
+       *hash_headpp = &hash->table[mach_bpf_hash(n_keys, keys)];
        head = **hash_headpp;
 
        if (head == 0)
@@ -485,7 +486,7 @@ bpf_match (net_hash_header_t hash, int n_keys, unsigned int 
*keys,
  */
 
 int
-hash_ent_remove (if_filter_list_t *ifp, net_hash_header_t hp, int used,
+hash_ent_remove (net_hash_header_t hp, int used,
                net_hash_entry_t *head, net_hash_entry_t entp, queue_entry_t 
*dead_p)
 {
        hp->ref_count--;
@@ -496,13 +497,13 @@ hash_ent_remove (if_filter_list_t *ifp, net_hash_header_t 
hp, int used,
                        ENQUEUE_DEAD(*dead_p, entp, chain);
                        if (hp->ref_count == 0 && !used) {
                                if (((net_rcv_port_t)hp)->filter[0] & NETF_IN)
-                                       queue_remove(&ifp->if_rcv_port_list,
+                                       queue_remove(&rcv_port_list,
                                                        (net_rcv_port_t)hp,
-                                                       net_rcv_port_t, input);
+                                                       net_rcv_port_t, chain);
                                if (((net_rcv_port_t)hp)->filter[0] & NETF_OUT)
-                                       queue_remove(&ifp->if_snd_port_list,
+                                       queue_remove(&snd_port_list,
                                                        (net_rcv_port_t)hp,
-                                                       net_rcv_port_t, output);
+                                                       net_rcv_port_t, chain);
                                hp->n_keys = 0;
                                return TRUE;
                        }
@@ -530,7 +531,7 @@ net_free_dead_infp (queue_entry_t dead_infp)
        register net_rcv_port_t infp, nextfp;
 
        for (infp = (net_rcv_port_t) dead_infp; infp != 0; infp = nextfp) {
-               nextfp = (net_rcv_port_t) queue_next(&infp->input);
+               nextfp = (net_rcv_port_t) queue_next(&infp->chain);
                mach_port_deallocate(mach_task_self(), infp->rcv_port);
                free(infp);
                debug ("a dead infp is freed\n");
@@ -564,8 +565,8 @@ net_free_dead_entp (queue_entry_t dead_entp)
  * We are given a naked send right for the rcv_port.
  * If we are successful, we must consume that right.
  */
-io_return_t
-net_set_filter(if_filter_list_t *ifp, mach_port_t rcv_port, int priority,
+error_t
+net_set_filter (mach_port_t rcv_port, int priority,
                filter_t *filter, unsigned int filter_count)
 {
        int               filter_bytes;
@@ -593,7 +594,7 @@ net_set_filter(if_filter_list_t *ifp, mach_port_t rcv_port, 
int priority,
        } else if (!((filter[0] & NETF_IN) || (filter[0] & NETF_OUT))) {
                return (D_INVALID_OPERATION); /* NETF_IN or NETF_OUT required */
        } else if ((filter[0] & NETF_TYPE_MASK) == NETF_BPF) {
-               ret = bpf_validate((bpf_insn_t)filter, filter_bytes, &match);
+               ret = mach_bpf_validate((bpf_insn_t)filter, filter_bytes, 
&match);
                if (!ret)
                        return (D_INVALID_OPERATION);
        } else {
@@ -630,18 +631,16 @@ net_set_filter(if_filter_list_t *ifp, mach_port_t 
rcv_port, int priority,
         */
        void check_filter_list(queue_head_t *if_port_list)
        {
-               FILTER_ITERATE(if_port_list, infp, nextfp,
-                               (if_port_list == &ifp->if_rcv_port_list)
-                               ? &infp->input : &infp->output)
-               {
+               FILTER_ITERATE(if_port_list, infp, nextfp, &infp->chain) {
                        if (infp->rcv_port == MACH_PORT_NULL) {
                                if (match != 0
                                                && infp->priority == priority
                                                && my_infp == 0
                                                && (infp->filter_end - 
infp->filter) == filter_count
-                                               && 
bpf_eq((bpf_insn_t)infp->filter,
-                                                       (bpf_insn_t)filter, 
filter_bytes))
+                                               && 
mach_bpf_eq((bpf_insn_t)infp->filter,
+                                                       (bpf_insn_t)filter, 
filter_bytes)) {
                                        my_infp = infp;
+                               }
 
                                for (i = 0; i < NET_HASH_SIZE; i++) {
                                        head = &((net_hash_header_t) 
infp)->table[i];
@@ -660,8 +659,7 @@ net_set_filter(if_filter_list_t *ifp, mach_port_t rcv_port, 
int priority,
                                                /* checked without 
                                                   ip_lock(entp->rcv_port) */
                                                if (entp->rcv_port == rcv_port) 
{
-                                                       ret = hash_ent_remove 
(ifp,
-                                                                       
(net_hash_header_t)infp,
+                                                       ret = hash_ent_remove 
((net_hash_header_t)infp,
                                                                        
(my_infp == infp),
                                                                        head,
                                                                        entp,
@@ -680,16 +678,15 @@ net_set_filter(if_filter_list_t *ifp, mach_port_t 
rcv_port, int priority,
 hash_loop_end:
                                ;
                        } else if (infp->rcv_port == rcv_port) {
-
                                /* Remove the old filter from lists */
                                if (infp->filter[0] & NETF_IN)
-                                       queue_remove(&ifp->if_rcv_port_list, 
infp,
-                                                       net_rcv_port_t, input);
+                                       queue_remove(&rcv_port_list, infp,
+                                                       net_rcv_port_t, chain);
                                if (infp->filter[0] & NETF_OUT)
-                                       queue_remove(&ifp->if_snd_port_list, 
infp,
-                                                       net_rcv_port_t, output);
+                                       queue_remove(&snd_port_list, infp,
+                                                       net_rcv_port_t, chain);
 
-                               ENQUEUE_DEAD(dead_infp, infp, input);
+                               ENQUEUE_DEAD(dead_infp, infp, chain);
                        }
                }
                FILTER_ITERATE_END
@@ -699,9 +696,9 @@ hash_loop_end:
        out = (filter[0] & NETF_OUT) != 0;
 
        if (in)
-               check_filter_list(&ifp->if_rcv_port_list);
+               check_filter_list(&rcv_port_list);
        if (out)
-               check_filter_list(&ifp->if_snd_port_list);
+               check_filter_list(&snd_port_list);
 
        if (my_infp == 0) {
                /* Allocate a dummy infp */
@@ -710,6 +707,7 @@ hash_loop_end:
                                break;
                }
                if (i == N_NET_HASH) {
+
                        mach_port_deallocate(mach_task_self() , rcv_port);
                        if (match != 0)
                                free(hash_entp);
@@ -741,24 +739,23 @@ hash_loop_end:
 
                /* Insert my_infp according to priority */
                if (in) {
-                       queue_iterate(&ifp->if_rcv_port_list, infp, 
net_rcv_port_t, input)
+                       queue_iterate(&rcv_port_list, infp, net_rcv_port_t, 
chain)
                                if (priority > infp->priority)
                                        break;
 
-                       queue_enter(&ifp->if_rcv_port_list, my_infp, 
net_rcv_port_t, input);
+                       queue_enter(&rcv_port_list, my_infp, net_rcv_port_t, 
chain);
                }
 
                if (out) {
-                       queue_iterate(&ifp->if_snd_port_list, infp, 
net_rcv_port_t, output)
+                       queue_iterate(&snd_port_list, infp, net_rcv_port_t, 
chain)
                                if (priority > infp->priority)
                                        break;
 
-                       queue_enter(&ifp->if_snd_port_list, my_infp, 
net_rcv_port_t, output);
+                       queue_enter(&snd_port_list, my_infp, net_rcv_port_t, 
chain);
                }
        }
 
-       if (match != 0)
-       {
+       if (match != 0) {
                /* Insert to hash list */
                net_hash_entry_t *p;
 
@@ -766,7 +763,7 @@ hash_loop_end:
                for (i = 0; i < match->jt; i++)     /* match->jt is n_keys */
                        hash_entp->keys[i] = match[i+1].k;
                p = &((net_hash_header_t)my_infp)->
-                       table[bpf_hash(match->jt, hash_entp->keys)];
+                       table[mach_bpf_hash(match->jt, hash_entp->keys)];
 
                /* Not checking for the same key values */
                if (*p == 0) {
@@ -790,14 +787,9 @@ clean_and_return:
        return (rval);
 }
 
+/* Remove the filter in if_port_list whose port for delivering is dead_port. */
 void
-destroy_filters (if_filter_list_t *ifp)
-{
-}
-
-void
-remove_dead_filter (if_filter_list_t *ifp, queue_head_t *if_port_list,
-               mach_port_t dead_port)
+remove_dead_filter (queue_head_t *if_port_list, mach_port_t dead_port)
 {
        net_rcv_port_t infp;
        net_rcv_port_t nextfp;
@@ -807,9 +799,7 @@ remove_dead_filter (if_filter_list_t *ifp, queue_head_t 
*if_port_list,
        int i, ret;
 
        dead_infp = dead_entp = 0;
-       FILTER_ITERATE (if_port_list, infp, nextfp,
-                       (if_port_list == &ifp->if_rcv_port_list)
-                       ? &infp->input : &infp->output) {
+       FILTER_ITERATE (if_port_list, infp, nextfp, &infp->chain) {
                if (infp->rcv_port == MACH_PORT_NULL) {
                        for (i = 0; i < NET_HASH_SIZE; i++) {
                                head = &((net_hash_header_t) infp)->table[i];
@@ -828,8 +818,7 @@ remove_dead_filter (if_filter_list_t *ifp, queue_head_t 
*if_port_list,
                                        /* checked without 
                                           ip_lock(entp->rcv_port) */
                                        if (entp->rcv_port == dead_port) {
-                                               ret = hash_ent_remove (ifp,
-                                                               
(net_hash_header_t) infp,
+                                               ret = hash_ent_remove 
((net_hash_header_t) infp,
                                                                0,
                                                                head,
                                                                entp,
@@ -850,13 +839,13 @@ hash_loop_end:
                } else if (infp->rcv_port == dead_port) {
                        /* Remove the old filter from lists */
                        if (infp->filter[0] & NETF_IN)
-                               queue_remove(&ifp->if_rcv_port_list, infp,
-                                               net_rcv_port_t, input);
+                               queue_remove(&rcv_port_list, infp,
+                                               net_rcv_port_t, chain);
                        if (infp->filter[0] & NETF_OUT)
-                               queue_remove(&ifp->if_snd_port_list, infp,
-                                               net_rcv_port_t, output);
+                               queue_remove(&snd_port_list, infp,
+                                               net_rcv_port_t, chain);
 
-                       ENQUEUE_DEAD(dead_infp, infp, input);
+                       ENQUEUE_DEAD(dead_infp, infp, chain);
                }
        }
        FILTER_ITERATE_END
@@ -866,3 +855,18 @@ hash_loop_end:
        if (dead_entp != 0)
                net_free_dead_entp(dead_entp);
 }
+
+/* Remove all filters whose port for delivering is dead_port. */
+void
+remove_dead_filter_from_all (mach_port_t dead_port)
+{
+       remove_dead_filter (&snd_port_list, dead_port);
+       remove_dead_filter (&rcv_port_list, dead_port);
+}
+
+void
+bpf_init()
+{
+       queue_init (&rcv_port_list);
+       queue_init (&snd_port_list);
+}
diff --git a/libbpf/bpf_impl.h b/eth-filter.multi-thread/bpf_impl.h
similarity index 79%
copy from libbpf/bpf_impl.h
copy to eth-filter.multi-thread/bpf_impl.h
index 9073fda..de61df1 100644
--- a/libbpf/bpf_impl.h
+++ b/eth-filter.multi-thread/bpf_impl.h
@@ -38,16 +38,12 @@
 #ifndef BPF_IMPL_H
 #define BPF_IMPL_H
 
+#include <mach.h>
+#include <hurd.h>
 #include <device/bpf.h>
 
 #include "queue.h"
 
-typedef struct
-{
-  queue_head_t if_rcv_port_list;       /* input filter list */
-  queue_head_t if_snd_port_list;       /* output filter list */
-}if_filter_list_t;
-
 typedef        unsigned short  filter_t;
 typedef filter_t       *filter_array_t;
 
@@ -98,8 +94,7 @@ typedef filter_t      *filter_array_t;
  * filter for a single session.
  */
 struct net_rcv_port {
-       queue_chain_t   input;          /* list of input open_descriptors */
-       queue_chain_t   output;         /* list of output open_descriptors */
+       queue_chain_t   chain;
        mach_port_t     rcv_port;       /* port to send packet to */
        int             rcv_count;      /* number of packets received */
        int             priority;       /* priority for filter */
@@ -134,28 +129,30 @@ struct net_hash_header {
        int n_keys;                     /* zero if not used */
        int ref_count;                  /* reference count */
        net_hash_entry_t table[NET_HASH_SIZE];
-};
+} filter_hash_header[N_NET_HASH];
 
 typedef struct net_hash_header *net_hash_header_t;
 
-int bpf_do_filter(net_rcv_port_t infp, char *p,        unsigned int wirelen, 
+int mach_bpf_do_filter(net_rcv_port_t infp, char *p,   unsigned int wirelen, 
                char *header, unsigned int hlen, net_hash_entry_t **hash_headpp,
                net_hash_entry_t *entpp);
-io_return_t net_set_filter(if_filter_list_t *ifp, mach_port_t rcv_port,
-               int priority, filter_t *filter, unsigned int filter_count);
-
-int bpf_validate(bpf_insn_t f, int bytes, bpf_insn_t *match);
-int bpf_eq (bpf_insn_t f1, bpf_insn_t f2, int bytes);
-unsigned int bpf_hash (int n, unsigned int *keys);
-int bpf_match (net_hash_header_t hash, int n_keys, unsigned int *keys,
-       net_hash_entry_t **hash_headpp, net_hash_entry_t *entpp);
-
-int hash_ent_remove (if_filter_list_t *ifp, net_hash_header_t hp, int used,
-               net_hash_entry_t *head, net_hash_entry_t entp, queue_entry_t 
*dead_p);
+int mach_bpf_validate(bpf_insn_t f, int bytes, bpf_insn_t *match);
+int mach_bpf_eq (bpf_insn_t f1, bpf_insn_t f2, int bytes);
+unsigned int mach_bpf_hash (int n, unsigned int *keys);
+int mach_bpf_match (net_hash_header_t hash, int n_keys, unsigned int *keys,
+               net_hash_entry_t **hash_headpp, net_hash_entry_t *entpp);
+
+error_t net_set_filter(mach_port_t rcv_port, int priority,
+               filter_t *filter, unsigned int filter_count);
+int hash_ent_remove (net_hash_header_t hp, int used, net_hash_entry_t *head,
+               net_hash_entry_t entp, queue_entry_t *dead_p);
 void net_free_dead_infp (queue_entry_t dead_infp);
 void net_free_dead_entp (queue_entry_t dead_entp);
-void remove_dead_filter (if_filter_list_t *ifp, 
-               queue_head_t *if_port_list, mach_port_t dead_port);
-void destroy_filters (if_filter_list_t *ifp);
+void remove_dead_filter (queue_head_t *if_port_list, mach_port_t dead_port);
+void remove_dead_filter_from_all (mach_port_t dead_port);
+void bpf_init();
+
+extern queue_head_t rcv_port_list;     /* input filter list */
+extern queue_head_t snd_port_list;     /* output filter list */
 
 #endif /* _DEVICE_BPF_H_ */
diff --git a/eth-filter.multi-thread/filter.c b/eth-filter.multi-thread/filter.c
new file mode 100644
index 0000000..ec3efe9
--- /dev/null
+++ b/eth-filter.multi-thread/filter.c
@@ -0,0 +1,877 @@
+/* 
+   Copyright (C) 2008 Free Software Foundation, Inc.
+   Written by Zheng Da.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd; see the file COPYING.  If not, write to
+   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+/*
+ * This program is a filter translator which sits on the top of the network
+ * interface. 
+ * It provides two filters: for outgoing packets and for incoming packets.
+ * Only one pfinet server are allowed to run on the top of the translator. 
+ */
+
+#include <argp.h>
+#include <errno.h>
+#include <error.h>
+#include <stddef.h>
+#include <unistd.h>
+
+#include <hurd.h>
+#include <mach.h>
+#include <pthread.h>
+#include <device/device.h>
+#include <hurd/trivfs.h>
+#include <hurd/ports.h>
+#include <hurd/ihash.h>
+
+#include "ourdevice_S.h"
+#include "notify_S.h"
+#include "device_reply_U.h"
+#include "bpf_impl.h"
+#include "util.h"
+#include "filter.h"
+#include "msg_queue.h"
+
+#ifdef DEBUG
+
+FILE *logfile;
+
+#endif 
+
+static struct hurd_ihash proxy_deliverport_ht
+= HURD_IHASH_INITIALIZER (offsetof (struct proxy, p_deliverport_hashloc));
+
+/* The name of the network interface that the filter translator sits on. */
+static char *device_file;
+const char *argp_program_version = "eth-filter 0.1";
+const char *argp_program_bug_address = "<address@hidden>";
+static const char doc[] = "Hurd filter translator.";
+static const struct argp_option options[] =
+{
+    {"interface", 'i', "DEVICE", 0,
+      "Network interface to use", 2},
+    {"send-filter", 's', "string", 0,
+      "The filter rule which applies to the outgoing packet", 4},
+    {"receive-filter", 'r', "string", 0,
+      "The filter rule which applies to the ingoing packet", 5},
+    {"send-ip-range", 'S', "IP range", 0,
+      "A range of IP to create the send filter", 6},
+    {"receive-ip-range", 'R', "IP range", 0,
+      "A range of IP to create the receive filter", 7},
+    {0}
+};
+
+/* A filter allows every packet to be sent and be received. */
+static struct bpf_insn default_snd_filter[] = 
+{
+    {NETF_OUT|NETF_BPF, 0, 0, 0},
+    {6, 0, 0, 1500}
+};
+static struct bpf_insn default_rcv_filter[] = 
+{
+    {NETF_IN|NETF_BPF, 0, 0, 0},
+    {6, 0, 0, 1500}
+};
+static struct bpf_insn *snd_filter = NULL;
+static int snd_filter_length;
+static struct bpf_insn *rcv_filter = NULL;
+static int rcv_filter_length;
+
+static pthread_t send_thread;
+static int send_thread_running = 1;
+
+/* Port bucket we service requests on.  */
+struct port_bucket *port_bucket;
+
+struct port_class *user_portclass;
+struct port_class *device_portclass;
+struct port_class *other_portclass;
+
+/* Trivfs hooks.  */
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+int trivfs_support_read = 0;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+int trivfs_allow_open = 0;
+
+struct port_class *trivfs_protid_portclasses[1];
+struct port_class *trivfs_cntl_portclasses[1];
+int trivfs_protid_nportclasses = 1;
+int trivfs_cntl_nportclasses = 1;
+
+/* For getting the notification of ports from the kernel. */
+struct port_info *notify_pi;
+
+/* Write the data from the client to the device. */
+int 
+filter_device_write (struct filter_msg *msg)
+{
+  error_t err = 0;
+  int ret_count = 0;
+  int has_filter = 0;
+  net_hash_entry_t entp, *hash_headp;
+  net_rcv_port_t infp, nextfp;
+  int bytes_written;
+  struct filter_write_msg *write_msg = (struct filter_write_msg *) msg;
+
+  /* The packet can be sent as long as it passes one filter,
+   * even thought there is usually only one filter in the list. */
+  FILTER_ITERATE (&snd_port_list, infp, nextfp, &infp->chain) 
+    {
+      has_filter = 1;
+      ret_count = mach_bpf_do_filter (infp,
+                                     write_msg->data + sizeof (struct ethhdr),
+                                     write_msg->datalen - sizeof (struct 
ethhdr),
+                                     write_msg->data, sizeof (struct ethhdr),
+                                     &hash_headp, &entp);
+      if (ret_count)
+       break;
+    }
+  FILTER_ITERATE_END
+
+  if (ret_count || !has_filter) 
+    err = device_write (msg->proxy->device_port, write_msg->mode,
+                       write_msg->recnum, write_msg->data,
+                       write_msg->datalen, &bytes_written);
+  ds_device_write_reply (write_msg->reply_port, write_msg->reply_type,
+                        err, bytes_written);
+  return 0;
+}
+
+/* Deliver the data from the device to the client. */
+int
+filter_deliver (struct filter_msg *msg)
+{
+  deliver_msg (((struct filter_deliver_msg *)msg)->net_msg,
+              &rcv_port_list, msg->proxy->deliver_port);
+  return 0;
+}
+
+error_t
+create_proxy_user (struct proxy *proxy, mach_port_t *port)
+{
+  error_t err;
+  struct proxy_user *user;
+
+  err = ports_create_port (user_portclass, port_bucket, sizeof (*user), &user);
+  if (err)
+      return err;
+  user->proxy = proxy;
+
+  *port = ports_get_right (user);
+  ports_port_deref (user);
+  return 0;
+}
+
+error_t 
+create_proxy_device (struct proxy *proxy, mach_port_t *port)
+{
+  error_t err;
+  struct proxy_device *device;
+
+  err = ports_create_port (device_portclass, port_bucket, sizeof (*device), 
&device);
+  if (err)
+      return err;
+  device->proxy = proxy;
+  proxy->device = device;
+
+  *port = ports_get_right (device);
+  ports_port_deref (device);
+  return 0;
+}
+
+void
+clean_proxy_user (void *p)
+{
+  struct proxy_user *user = p;
+  struct proxy *proxy = user->proxy;
+
+  if (proxy->p_deliverport_hashloc)
+    hurd_ihash_locp_remove (&proxy_deliverport_ht, 
proxy->p_deliverport_hashloc);
+
+  if (proxy->deliver_port != MACH_PORT_NULL)
+    mach_port_deallocate (mach_task_self (), proxy->deliver_port);
+  if (proxy->device_port != MACH_PORT_NULL)
+    mach_port_deallocate (mach_task_self (), proxy->device_port);
+
+  if (proxy->device)
+    ports_destroy_right (proxy->device);
+
+  free (proxy);
+}
+
+void
+clean_proxy_device (void *p)
+{
+  struct proxy_device *device = p;
+  if (device->proxy)
+    device->proxy->device = NULL;
+}
+
+static int
+filter_demuxer (mach_msg_header_t *inp,
+               mach_msg_header_t *outp)
+{
+  extern int device_server (mach_msg_header_t *, mach_msg_header_t *);
+  extern int notify_server (mach_msg_header_t *, mach_msg_header_t *);
+  extern int ethernet_demuxer (mach_msg_header_t *, mach_msg_header_t *);
+  return device_server (inp, outp) || notify_server (inp, outp)
+    || ethernet_demuxer (inp, outp) || trivfs_demuxer (inp, outp);
+}
+
+int
+ethernet_demuxer (mach_msg_header_t *inp,
+                 mach_msg_header_t *outp)
+{
+  struct net_rcv_msg *msg = (struct net_rcv_msg *) inp;
+  struct proxy_device *device;
+  struct proxy *proxy;
+
+  if (inp->msgh_id != NET_RCV_MSG_ID)
+    return 0;
+
+  device = ports_lookup_port (port_bucket, inp->msgh_local_port,
+                             device_portclass);
+  if (device == NULL)
+    return 0;
+
+  proxy = device->proxy;
+  ports_port_deref (device);
+
+  if (proxy && proxy->deliver_port != MACH_PORT_NULL)
+    queue_deliver (msg, proxy);
+//    deliver_msg (msg, &rcv_port_list, proxy->deliver_port);
+
+  return 1;
+}
+
+/* Implementation of notify interface */
+kern_return_t
+do_mach_notify_port_deleted (mach_port_t notify,
+                            mach_port_t name)
+{
+  return EOPNOTSUPP;
+}
+
+kern_return_t
+do_mach_notify_msg_accepted (mach_port_t notify,
+                            mach_port_t name)
+{
+  return EOPNOTSUPP;
+}
+
+kern_return_t
+do_mach_notify_port_destroyed (mach_port_t notify,
+                              mach_port_t port)
+{
+  return EOPNOTSUPP;
+}
+
+kern_return_t
+do_mach_notify_no_senders (mach_port_t notify,
+                          mach_port_mscount_t mscount)
+{
+  debug ("do_mach_notify_no_senders is called\n");
+  return ports_do_mach_notify_no_senders (notify, mscount);
+}
+
+kern_return_t
+do_mach_notify_send_once (mach_port_t notify)
+{
+  return EOPNOTSUPP;
+}
+
+kern_return_t
+do_mach_notify_dead_name (mach_port_t notify,
+                         mach_port_t name)
+{
+  struct proxy *proxy;
+
+  debug ("do_mach_notify_dead_name is called\n");
+  mach_port_deallocate (mach_task_self (), name);
+  proxy = hurd_ihash_find (&proxy_deliverport_ht, name);
+  if (proxy)
+    {
+      proxy->deliver_port = MACH_PORT_NULL;
+      return 0;
+    }
+  return EINVAL;
+}
+
+/* Implementation of device interface */
+kern_return_t 
+ds_xxx_device_set_status (device_t device, dev_flavor_t flavor,
+                         dev_status_t status, size_t statu_cnt)
+{
+  return D_INVALID_OPERATION;
+}
+
+kern_return_t
+ds_xxx_device_get_status (device_t device, dev_flavor_t flavor,
+                         dev_status_t status, size_t *statuscnt)
+{
+  return D_INVALID_OPERATION;
+}
+
+kern_return_t
+ds_xxx_device_set_filter (device_t device, mach_port_t rec,
+                         int pri, filter_array_t filt, size_t len)
+{
+  return D_INVALID_OPERATION;
+}
+
+kern_return_t
+ds_device_open (mach_port_t master_port, mach_port_t reply_port,
+               mach_msg_type_name_t reply_portPoly,
+               dev_mode_t mode, dev_name_t name, mach_port_t *device, 
+               mach_msg_type_name_t *devicetype)
+{
+  kern_return_t err;
+  mach_port_t master_device;
+  mach_port_t user_port;
+  struct proxy *proxy;
+
+  if (device_file == NULL)
+    return D_NO_SUCH_DEVICE;
+
+  master_device = file_name_lookup (device_file, 0, 0);
+  if (master_device == MACH_PORT_NULL)
+    return errno;
+
+  proxy = (struct proxy *)calloc (1, sizeof (*proxy));
+  if (proxy == NULL)
+    {
+      mach_port_deallocate (mach_task_self (), master_device);
+      return D_NO_MEMORY;
+    }
+
+  err = device_open (master_device, mode, name, &proxy->device_port);
+  mach_port_deallocate (mach_task_self (), master_device);
+  if (err != KERN_SUCCESS)
+    {
+      free (proxy);
+      return err;
+    }
+
+  err = create_proxy_user (proxy, &user_port);
+  if (err)
+    {
+      mach_port_deallocate (mach_task_self (), master_device);
+      free (proxy);
+      return err;
+    }
+
+  *device = user_port;
+  *devicetype = MACH_MSG_TYPE_MAKE_SEND;
+
+  return 0;
+}
+
+kern_return_t
+ds_device_close (device_t device)
+{
+  return 0;
+}
+
+kern_return_t
+ds_device_write (device_t device, mach_port_t reply_port,
+                mach_msg_type_name_t reply_type, dev_mode_t mode,
+                recnum_t recnum, io_buf_ptr_t data, size_t datalen,
+                int *bytes_written)
+{
+  kern_return_t err = 0;
+  struct proxy_user *user;
+  struct proxy *proxy;
+  int ret_count = 0;
+  int has_filter = 0;
+  net_hash_entry_t entp, *hash_headp;
+  net_rcv_port_t infp, nextfp;
+
+  user = ports_lookup_port (port_bucket, device, user_portclass);
+  if (user == NULL)
+    return D_INVALID_OPERATION;
+  proxy = user->proxy;
+
+  /* The packet can be sent as long as it passes one filter,
+   * even thought there is usually only one filter in the list. */
+  FILTER_ITERATE (&snd_port_list, infp, nextfp, &infp->chain) 
+    {
+      has_filter = 1;
+      ret_count = mach_bpf_do_filter (infp,
+                                     data + sizeof (struct ethhdr),
+                                     datalen - sizeof (struct ethhdr),
+                                     data, sizeof (struct ethhdr),
+                                     &hash_headp, &entp);
+      if (ret_count)
+       break;
+    }
+  FILTER_ITERATE_END
+
+  if (ret_count || !has_filter) 
+    err = device_write (proxy->device_port, mode, recnum, data,
+                       datalen, bytes_written);
+  ports_port_deref (user);
+  return err;
+}
+
+kern_return_t
+ds_device_write_inband (device_t device, mach_port_t reply_port,
+                       mach_msg_type_name_t reply_type, dev_mode_t mode,
+                       recnum_t recnum, io_buf_ptr_inband_t data,
+                       size_t datalen, int *bytes_written)
+{
+  kern_return_t ret;
+  struct proxy_user *user;
+  struct proxy *proxy;
+
+  user = ports_lookup_port (port_bucket, device, user_portclass);
+  if (user == NULL)
+    return D_INVALID_OPERATION;
+  proxy = user->proxy;
+  ports_port_deref (user);
+
+  ret = device_write_inband (proxy->device_port, mode, recnum, data,
+                            datalen, bytes_written);
+  return ret;
+}
+
+kern_return_t
+ds_device_read (device_t device, mach_port_t reply_port,
+               mach_msg_type_name_t reply_type, dev_mode_t mode,
+               recnum_t recnum, int bytes_wanted,
+               io_buf_ptr_t *data, size_t *datalen)
+{
+  kern_return_t ret;
+  struct proxy_user *user;
+  struct proxy *proxy;
+
+  user = ports_lookup_port (port_bucket, device, user_portclass);
+  if (user == NULL)
+    return D_INVALID_OPERATION;
+  proxy = user->proxy;
+  ports_port_deref (user);
+
+  ret = device_read (proxy->device_port, mode, recnum,
+                    bytes_wanted, data, datalen);
+  return ret;
+}
+
+kern_return_t
+ds_device_read_inband (device_t device, mach_port_t reply_port,
+                      mach_msg_type_name_t reply_type, dev_mode_t mode,
+                      recnum_t recnum, int bytes_wanted,
+                      io_buf_ptr_inband_t data, size_t *datalen)
+{
+  kern_return_t ret;
+  struct proxy_user *user;
+  struct proxy *proxy;
+
+  user = ports_lookup_port (port_bucket, device, user_portclass);
+  if (user == NULL)
+    return D_INVALID_OPERATION;
+  proxy = user->proxy;
+  ports_port_deref (user);
+
+  ret = device_read_inband (proxy->device_port, mode, recnum, 
+                           bytes_wanted, data, datalen);
+  return ret;
+}
+
+kern_return_t
+ds_device_map (device_t device, vm_prot_t prot, vm_offset_t offset,
+              vm_size_t size, memory_object_t *pager, int unmap)
+{
+  kern_return_t ret;
+  struct proxy_user *user;
+  struct proxy *proxy;
+
+  user = ports_lookup_port (port_bucket, device, user_portclass);
+  if (user == NULL)
+    return D_INVALID_OPERATION;
+  proxy = user->proxy;
+  ports_port_deref (user);
+
+  ret = device_map (proxy->device_port, prot, offset,
+                   size, pager, unmap);
+  return ret;
+}
+
+kern_return_t
+ds_device_set_status (device_t device, dev_flavor_t flavor,
+                     dev_status_t status, size_t statuslen)
+{
+  kern_return_t ret;
+  struct proxy_user *user;
+  struct proxy *proxy;
+
+  user = ports_lookup_port (port_bucket, device, user_portclass);
+  if (user == NULL)
+    return D_INVALID_OPERATION;
+  proxy = user->proxy;
+  ports_port_deref (user);
+
+  ret = device_set_status (proxy->device_port, flavor,
+                          status, statuslen);
+  return ret;
+}
+
+kern_return_t
+ds_device_get_status (device_t device, dev_flavor_t flavor,
+                     dev_status_t status, size_t *statuslen)
+{
+  kern_return_t ret;
+  struct proxy_user *user;
+  struct proxy *proxy;
+
+  user = ports_lookup_port (port_bucket, device, user_portclass);
+  if (user == NULL)
+    return D_INVALID_OPERATION;
+  proxy = user->proxy;
+  ports_port_deref (user);
+
+  ret = device_get_status (proxy->device_port, flavor, status, statuslen);
+  return ret;
+}
+
+kern_return_t
+ds_device_set_filter (device_t device, mach_port_t receive_port,
+                     int priority, filter_array_t filter, size_t filterlen)
+{
+  mach_port_t tmp;
+  kern_return_t err;
+  mach_port_t device_receive_port;
+  struct proxy_user *user;
+  struct proxy *proxy;
+
+  user = ports_lookup_port (port_bucket, device, user_portclass);
+  if (user == NULL)
+    return D_INVALID_OPERATION;
+  proxy = user->proxy;
+  ports_port_deref (user);
+
+  if (proxy->device == NULL)
+    {
+      error_t err;
+      err = create_proxy_device (proxy, &device_receive_port);
+      if (err)
+       return err;
+    }
+  else
+    device_receive_port = ports_get_right (proxy->device);
+
+  /* Set the filter from pfinet into the interface,
+   * but the packet will be delivered to the translator,
+   * so the translator has the chance to filter some packets. */
+  err = device_set_filter (proxy->device_port,
+                          device_receive_port,
+                          MACH_MSG_TYPE_MAKE_SEND, priority,
+                          filter, filterlen);
+  if (err)
+    return err;
+
+  proxy->deliver_port = receive_port;
+  hurd_ihash_add (&proxy_deliverport_ht, receive_port, proxy);
+
+  err = mach_port_request_notification (mach_task_self (), receive_port, 
+                                       MACH_NOTIFY_DEAD_NAME, 0,
+                                       ports_get_right (notify_pi), 
+                                       MACH_MSG_TYPE_MAKE_SEND_ONCE, &tmp);
+  if (tmp != MACH_PORT_NULL)
+    mach_port_deallocate (mach_task_self (), tmp);
+
+  return err;
+}
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, io_statbuf_t *stat)
+{
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+  int count;
+
+  /* Stop new requests.  */
+  ports_inhibit_class_rpcs (trivfs_cntl_portclasses[0]);
+  ports_inhibit_class_rpcs (trivfs_protid_portclasses[0]);
+
+  count = ports_count_class (user_portclass);
+  debug ("the number of ports alive: %d\n", count);
+
+  if (count && !(flags & FSYS_GOAWAY_FORCE)) 
+    {
+      /* We won't go away, so start things going again...  */
+      ports_enable_class (trivfs_protid_portclasses[0]);
+      ports_resume_class_rpcs (trivfs_cntl_portclasses[0]);
+      ports_resume_class_rpcs (trivfs_protid_portclasses[0]); 
+      return EBUSY;
+    } 
+
+  queue_flush ();
+  send_thread_running = 0;
+//  pthread_join (send_thread);
+
+#ifdef DEBUG
+  fclose (logfile);
+#endif
+  debug ("the translator is gone away\n");
+  exit (0);
+}
+
+/* Convert the network address input by the user into
+ * a form that is accepted by libpcap. */
+int
+correct_net_addr (char *orig, char *result, int result_len)
+{
+  char *ptr;
+  int netmask_len;
+  int remain_bits;
+  int remain_bytes;
+  char netmask;
+  char addr[4];
+  char buf[INET_ADDRSTRLEN];
+  int i;
+
+  ptr = strstr (orig, "/");
+  if (ptr == NULL)
+    {
+      strncpy (result, orig, result_len);
+      return 0;
+    }
+
+  *ptr = 0;
+  ptr++;
+  netmask_len = atoi (ptr);
+  if (inet_pton (AF_INET, orig, addr) < 0)
+    {
+      perror ("inet_pton");
+      return -1;
+    }
+  remain_bits = netmask_len % 8;
+  netmask = ~0;
+  netmask >>= 8 - remain_bits;
+  netmask <<= 8 - remain_bits;
+  remain_bytes = netmask_len / 8;
+  addr[remain_bytes] &= netmask;
+  for (i=remain_bytes+1 ; i < 4 ; i++)
+    addr[i] = 0;
+
+  snprintf (result, result_len, "%s/%s",
+           inet_ntop (AF_INET, addr, buf, INET_ADDRSTRLEN), ptr);
+  return 0;
+}
+
+static error_t
+parse_opt (int opt, char *arg, struct argp_state *state)
+{
+  struct bpf_insn *trans_filter_program (char *str, int send,
+                                        int *filter_len);
+  char buf[1024];
+  char addr[INET_ADDRSTRLEN+4];
+
+  switch (opt)
+    {
+    case 'i':
+      device_file = arg;
+      break;
+    case 's':
+      if (snd_filter) 
+       free (snd_filter);
+      snd_filter = trans_filter_program (arg, 1, &snd_filter_length);
+      break;
+    case 'r':
+      if (rcv_filter)
+       free (rcv_filter);
+      rcv_filter = trans_filter_program (arg, 0, &rcv_filter_length);
+      break;
+    case 'S':
+      if (correct_net_addr (arg, addr, INET_ADDRSTRLEN+4) < 0)
+       return 0;
+      snprintf (buf, sizeof (buf), "arp or (ip and src net %s)", addr);
+      if (snd_filter) 
+       free (snd_filter);
+      snd_filter = trans_filter_program (buf, 1, &snd_filter_length);
+      break;
+    case 'R':
+      if (correct_net_addr (arg, addr, INET_ADDRSTRLEN+4) < 0)
+       return 0;
+      snprintf (buf, sizeof (buf), "arp or (ip and dst net %s)", addr);
+      if (rcv_filter) 
+       free (rcv_filter);
+      rcv_filter = trans_filter_program (buf, 0, &rcv_filter_length);
+      break;
+    case ARGP_KEY_ERROR:
+    case ARGP_KEY_SUCCESS:
+    case ARGP_KEY_INIT:
+      break;
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+  return 0;
+}
+
+void *send_thread_func (void *arg)
+{
+  struct filter_msg *msg;
+  
+  while (send_thread_running)
+    {
+      msg = dequeue_msg ();
+      if (msg == NULL)
+       {
+//       queue_empty_wait ();
+         continue;
+       }
+      msg->forward (msg);
+      msg->destroy (msg);
+      free (msg);
+    }
+  return NULL;
+}
+
+int
+main (int argc, char *argv[])
+{
+  error_t err;
+  mach_port_t bootstrap;
+  struct trivfs_control *fsys;
+  const struct argp argp = { options, parse_opt, 0, doc };
+
+#ifdef DEBUG
+  logfile = fopen ("/root/filter.log", "a+");
+  if (logfile == NULL)
+    error (1, errno, "fopen");
+#endif
+
+  port_bucket = ports_create_bucket ();
+  user_portclass = ports_create_class (clean_proxy_user, 0);
+  device_portclass = ports_create_class (clean_proxy_device, 0);
+  other_portclass = ports_create_class (0, 0);
+  trivfs_cntl_portclasses[0] = ports_create_class (trivfs_clean_cntl, 0);
+  trivfs_protid_portclasses[0] = ports_create_class (trivfs_clean_protid, 0);
+
+  argp_parse (&argp, argc, argv, 0, 0, 0);
+
+  /* Prepare the filter. */
+  if (snd_filter == NULL) 
+    {
+      snd_filter = default_snd_filter;
+      snd_filter_length = sizeof (default_snd_filter) / sizeof (short);
+    }
+  if (rcv_filter == NULL) 
+    {
+      rcv_filter = default_rcv_filter;
+      rcv_filter_length = sizeof (default_rcv_filter) / sizeof (short);
+    }
+
+  task_get_bootstrap_port (mach_task_self (), &bootstrap);
+  if (bootstrap == MACH_PORT_NULL)
+    error (1, 0, "must be started as a translator");
+
+  err = ports_create_port (other_portclass, port_bucket, 
+                          sizeof (struct port_info), &notify_pi);
+  if (err)
+    error (1, err, "ports_create_port for notification");
+
+  /* Reply to our parent.  */
+  err = trivfs_startup (bootstrap, 0,
+                       trivfs_cntl_portclasses[0], port_bucket,
+                       trivfs_protid_portclasses[0], port_bucket, &fsys);
+  mach_port_deallocate (mach_task_self (), bootstrap);
+  if (err)
+    error (1, err, "Contacting parent");
+
+  /* Initialize the bpf, and set the filter for outgoing packets.
+   * MACH_PORT_DEAD is used because we don't need a receiving port. */
+  bpf_init ();
+  err = net_set_filter (MACH_PORT_DEAD, 0, (filter_t *) snd_filter,
+                       snd_filter_length);
+  if (err)
+    error (1, err, "set the sending filter");
+  /* Set the filter translator's own rule. */
+  err = net_set_filter (MACH_PORT_DEAD, 0, (filter_t *) rcv_filter,
+                       rcv_filter_length);
+  if (err)
+    error (1, err, "set the receiving filter");
+
+  pthread_create (&send_thread, NULL, send_thread_func, NULL);
+  /* TODO if the main thread exits,
+   * does the created thread exit if pthread_detach is called */
+  pthread_detach (send_thread);
+
+  /* Launch.  */
+  do 
+    {
+      ports_manage_port_operations_one_thread (port_bucket, 
+                                              filter_demuxer, 0);
+    } while (trivfs_goaway (fsys, 0)); 
+  return 0;
+}
+
+int
+deliver_msg (struct net_rcv_msg *msg, queue_head_t *port_list, 
+            mach_port_t dest)
+{
+  static int count = 0;
+  mach_msg_return_t err;
+  net_rcv_port_t infp, nextfp;
+
+  msg->msg_hdr.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, 0);
+  msg->msg_hdr.msgh_local_port = MACH_PORT_NULL;
+  msg->msg_hdr.msgh_kind = MACH_MSGH_KIND_NORMAL;
+
+  /* Deliver the packet to the right pfinet,
+   * actually there should be only one filter in the list. */
+  FILTER_ITERATE (port_list, infp, nextfp, &infp->chain) 
+    {
+      net_hash_entry_t entp, *hash_headp;
+      int ret_count;
+
+      entp = (net_hash_entry_t) 0;
+      ret_count = mach_bpf_do_filter (infp,
+                                     msg->packet + sizeof (struct 
packet_header),
+                                     msg->net_rcv_msg_packet_count,
+                                     msg->header,
+                                     sizeof (struct ethhdr),
+                                     &hash_headp, &entp);
+
+      if (ret_count) 
+       {
+         msg->msg_hdr.msgh_remote_port = dest;
+         debug ("before delivering the packet to port %d, count: %d\n",
+                dest, ++count);
+         err = mach_msg ((mach_msg_header_t *)msg,
+                         MACH_SEND_MSG|MACH_SEND_TIMEOUT,
+                         msg->msg_hdr.msgh_size, 0, MACH_PORT_NULL,
+                         /*MACH_MSG_TIMEOUT_NONE*/500, MACH_PORT_NULL);
+         if (err != MACH_MSG_SUCCESS)
+           {
+             mach_port_deallocate(mach_task_self (),
+                                  ((mach_msg_header_t 
*)msg)->msgh_remote_port);
+             error (0, err, "mach_msg");
+             return -1;
+           }
+         debug ("after delivering the packet\n");
+       }
+    }
+  FILTER_ITERATE_END
+
+    return 0;
+}
diff --git a/devnode/util.h b/eth-filter.multi-thread/filter.h
similarity index 57%
copy from devnode/util.h
copy to eth-filter.multi-thread/filter.h
index 2efc1ef..e1c4c8b 100644
--- a/devnode/util.h
+++ b/eth-filter.multi-thread/filter.h
@@ -18,25 +18,40 @@
    along with the GNU Hurd; see the file COPYING.  If not, write to
    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 
-#ifndef UTIL_H
-#define UTIL_H
-
-#include <stdio.h>
-
-#ifdef DEBUG 
-
-#define debug(format, ...) do                          \
-{                                                      \
-  char buf[1024];                                       \
-  snprintf (buf, 1024, "devnode: %s", format);       \
-  fprintf (stderr , buf, ## __VA_ARGS__);              \
-  fflush (stderr);                                     \
-} while (0)
-
-#else
-
-#define debug(format, ...) do {} while (0)
-
-#endif
+#ifndef FILTER_H
+#define FILTER_H
+
+#include <hurd.h>
+#include <mach.h>
+#include <hurd/ports.h>
+
+#include "bpf_impl.h"
+
+struct filter_msg;
+
+struct proxy_user
+{
+  struct port_info pi;
+  struct proxy *proxy;
+};
+
+struct proxy_device
+{
+  struct port_info pi;
+  struct proxy *proxy;
+};
+
+struct proxy
+{
+  struct proxy_device *device;
+  mach_port_t deliver_port;
+  hurd_ihash_locp_t p_deliverport_hashloc;
+  mach_port_t device_port;
+};
+
+int deliver_msg (struct net_rcv_msg *msg, queue_head_t *port_list, 
+                mach_port_t dest);
+int filter_device_write (struct filter_msg *msg);
+int filter_deliver (struct filter_msg *msg);
 
 #endif
diff --git a/eth-filter.multi-thread/msg_queue.c 
b/eth-filter.multi-thread/msg_queue.c
new file mode 100644
index 0000000..602b7fd
--- /dev/null
+++ b/eth-filter.multi-thread/msg_queue.c
@@ -0,0 +1,224 @@
+/* 
+   Copyright (C) 2008 Free Software Foundation, Inc.
+   Written by Zheng Da.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd; see the file COPYING.  If not, write to
+   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "msg_queue.h"
+#include "filter.h"
+#include "util.h"
+
+static struct filter_msg *queue_head;
+static struct filter_msg *queue_tail;
+static int queue_len = 0;
+
+static pthread_mutex_t queuelock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t condition_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t condition_cond = PTHREAD_COND_INITIALIZER;
+
+void
+queue_msg (struct filter_msg *msg)
+{
+  msg->next = NULL;
+  // TODO what kind of lock do I need?
+  pthread_mutex_lock (&queuelock);
+  if (queue_head == NULL)
+    {
+      assert (queue_tail == NULL);
+      assert (queue_len == 0);
+      /* When the queue is empty. */
+      queue_head = msg;
+      queue_tail = msg;
+    }
+  else
+    {
+      queue_tail->next = msg;
+      queue_tail = msg;
+    }
+  queue_len++;
+  debug ("queue a message, queue length: %d.\n", queue_len);
+  pthread_mutex_unlock (&queuelock);
+//  queue_wakeup ();
+}
+
+struct filter_msg *
+dequeue_msg ()
+{
+  struct filter_msg *msg;
+
+  pthread_mutex_lock (&queuelock);
+  if (queue_head == NULL)
+    {
+      assert (queue_tail == NULL);
+      assert (queue_len == 0);
+      msg = NULL;
+    }
+  else
+    {
+      msg = queue_head;
+      queue_head = msg->next;
+      if (queue_head == NULL)
+       queue_tail = NULL;
+      queue_len--;
+    }
+  debug ("dequeue a message, the queue length: %d.\n", queue_len);
+  pthread_mutex_unlock (&queuelock);
+
+  return msg;
+}
+
+/* It can be called when the translator exits. */
+void
+queue_flush ()
+{
+  struct filter_msg *msg;
+  struct filter_msg *tmp;
+
+  pthread_mutex_lock (&queuelock);
+  msg = queue_head;
+  queue_head = queue_tail = NULL;
+  queue_len = 0;
+  pthread_mutex_unlock (&queuelock);
+
+  while (msg)
+    {
+      tmp = msg;
+      msg = msg->next;
+      if (tmp->destroy)
+       tmp->destroy (tmp);
+      free (tmp);
+    }
+}
+
+void
+filter_write_destroy (struct filter_msg *msg)
+{
+  struct filter_write_msg *write_msg = (struct filter_write_msg *) msg;
+
+  free (write_msg->data);
+}
+
+void filter_deliver_destroy (struct filter_msg *msg)
+{
+  struct filter_deliver_msg *deliver_msg = (struct filter_deliver_msg *) msg;
+
+  free (deliver_msg->net_msg);
+}
+
+kern_return_t
+queue_write (char *data, size_t datalen, mach_port_t reply_port,
+            mach_msg_type_name_t reply_type, dev_mode_t mode,
+            recnum_t recnum, struct proxy *proxy)
+{
+  /* Make a copy of the data */
+  char *new_data;
+  struct filter_msg *msg;
+  struct filter_write_msg *write_msg;
+
+  if (queue_len >= MAX_QUEUE)
+    return D_NO_MEMORY;
+  
+  new_data = (char *) malloc (datalen);
+  if (new_data == NULL)
+    return D_NO_MEMORY;
+
+  memcpy (new_data, data, datalen);
+
+  write_msg = (struct filter_write_msg *) malloc (sizeof (*write_msg));
+  if (write_msg == NULL)
+    {
+      free (new_data);
+      return D_NO_MEMORY;
+    }
+  msg = (struct filter_msg *) write_msg;
+
+  msg->proxy = proxy;
+  msg->forward = filter_device_write;
+  msg->destroy = filter_write_destroy;
+
+  write_msg->data = new_data;
+  write_msg->datalen = datalen;
+  write_msg->reply_port = reply_port;
+  write_msg->reply_type = reply_type;
+  write_msg->mode = mode;
+  write_msg->recnum = recnum;
+
+  queue_msg (msg);
+  return D_SUCCESS;
+}
+
+int
+queue_deliver (struct net_rcv_msg *msg, struct proxy *proxy)
+{
+  struct net_rcv_msg *msg_copy;
+  struct filter_deliver_msg *deliver_msg;
+
+  if (queue_len >= MAX_QUEUE)
+    return D_NO_MEMORY;
+
+  /* Make a copy of the data */
+  msg_copy = (struct net_rcv_msg *) malloc (sizeof (*msg_copy));
+  if (msg_copy == NULL)
+    return D_NO_MEMORY;
+
+  deliver_msg = (struct filter_deliver_msg *) malloc (sizeof (*deliver_msg));
+  if (deliver_msg == NULL)
+    {
+      free (msg_copy);
+      return D_NO_MEMORY;
+    }
+
+  *msg_copy = *msg;
+
+  deliver_msg->msg.proxy = proxy;
+  deliver_msg->msg.forward = filter_deliver;
+  deliver_msg->msg.destroy = filter_deliver_destroy;
+
+  deliver_msg->net_msg = msg_copy;
+
+  queue_msg ((struct filter_msg *)deliver_msg);
+  return D_SUCCESS;
+}
+
+void
+queue_empty_wait ()
+{
+  pthread_mutex_lock (&condition_mutex);
+//  debug ("queue length is %d\n", queue_len);
+  while (queue_head == NULL)
+    {
+      debug ("thread waits for a signal.\n");
+      pthread_cond_wait (&condition_cond, &condition_mutex);
+    }
+  pthread_mutex_unlock (&condition_mutex);
+}
+
+void
+queue_wakeup ()
+{
+  if (queue_head)
+    {
+      debug ("wake up a thread.\n");
+      pthread_cond_signal (&condition_cond);
+    }
+}
+
diff --git a/eth-filter.multi-thread/msg_queue.h 
b/eth-filter.multi-thread/msg_queue.h
new file mode 100644
index 0000000..bc7800f
--- /dev/null
+++ b/eth-filter.multi-thread/msg_queue.h
@@ -0,0 +1,69 @@
+/* 
+   Copyright (C) 2008 Free Software Foundation, Inc.
+   Written by Zheng Da.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd; see the file COPYING.  If not, write to
+   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#ifndef MSG_QUEUE_H
+#define MSG_QUEUE_H
+
+#include <hurd.h>
+#include <mach.h>
+
+#define MAX_QUEUE 1000
+
+struct proxy;
+
+struct filter_msg 
+{
+  struct proxy *proxy;
+  struct filter_msg *next;
+
+  int (*forward) (struct filter_msg *);
+  void (*destroy) (struct filter_msg *);
+};
+
+struct filter_write_msg
+{
+  struct filter_msg msg;
+
+  char *data;
+  size_t datalen;
+  dev_mode_t mode;
+  recnum_t recnum;
+  mach_port_t reply_port;
+  mach_msg_type_name_t reply_type;
+};
+
+struct filter_deliver_msg 
+{
+  struct filter_msg msg;
+  
+  struct net_rcv_msg *net_msg;
+};
+
+void queue_msg (struct filter_msg *msg);
+struct filter_msg * dequeue_msg ();
+void queue_flush ();
+kern_return_t queue_write (char *data, size_t datalen, mach_port_t reply_port,
+            mach_msg_type_name_t reply_type, dev_mode_t mode,
+            recnum_t recnum, struct proxy *proxy);
+int queue_deliver (struct net_rcv_msg *msg, struct proxy *proxy);
+void queue_wakeup ();
+void queue_empty_wait ();
+
+#endif 
diff --git a/eth-filter.multi-thread/pcap_filter.c 
b/eth-filter.multi-thread/pcap_filter.c
new file mode 100644
index 0000000..67ee9bb
--- /dev/null
+++ b/eth-filter.multi-thread/pcap_filter.c
@@ -0,0 +1,77 @@
+/* 
+   Copyright (C) 2008 Free Software Foundation, Inc.
+   Written by Zheng Da.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd; see the file COPYING.  If not, write to
+   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+/*
+ * This file translates a string into a bpf program.
+ * The BPF structures are defined in both of bpf.h and pcap-bpf.h
+ * Hopefully, there is no conflict between them.
+ * This file uses the BPF definition in pcap-bpf.h.
+ */
+
+#include <pcap.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+
+#define NETF_IN 0x1
+#define NETF_OUT 0x2
+#define NETF_NBPA 10
+#define NETF_BPF (1 << NETF_NBPA)
+
+/* This function translates the bpf program
+ * from the string into the instructions. */
+struct bpf_insn *trans_filter_program (char *str, int send, int *filter_len)
+{
+  struct bpf_program program;
+  struct bpf_insn *insn;
+  pcap_t *pcap;
+  int err;
+
+  debug ("Compiling the bpf program: %s.\n", str);
+  pcap = pcap_open_dead (DLT_EN10MB, 1500);
+  if (pcap == NULL)
+    return NULL;
+  err = pcap_compile (pcap, &program, str, 1, 0);
+  if (err < 0) 
+    {
+      debug ("pcap_compile: %s\n", pcap_geterr (pcap));
+      pcap_close (pcap);
+      return NULL;
+    }
+
+  debug ("Finish compiling the bpf program, get %d bpf instructions.\n",
+        program.bf_len);
+  insn = (struct bpf_insn *) malloc ((program.bf_len + 1) * sizeof (*insn));
+  /* Clear the first instruction. */
+  memset (insn, 0, sizeof (*insn));
+  if (send)
+    insn->code = NETF_OUT | NETF_BPF;
+  else
+    insn->code = NETF_IN | NETF_BPF;
+  memcpy (insn + 1, program.bf_insns, program.bf_len * sizeof (*insn));
+  *filter_len = ((program.bf_len + 1) * sizeof (*insn)) / sizeof (short);
+  debug ("%d bpf instructions, the length of filters is %d words\n", 
+        program.bf_len, *filter_len);
+  pcap_freecode (&program);
+  pcap_close (pcap);
+
+  return insn;
+}
diff --git a/libbpf/queue.c b/eth-filter.multi-thread/queue.c
similarity index 100%
copy from libbpf/queue.c
copy to eth-filter.multi-thread/queue.c
diff --git a/libbpf/queue.h b/eth-filter.multi-thread/queue.h
similarity index 100%
copy from libbpf/queue.h
copy to eth-filter.multi-thread/queue.h
diff --git a/libbpf/util.h b/eth-filter.multi-thread/util.h
similarity index 62%
copy from libbpf/util.h
copy to eth-filter.multi-thread/util.h
index c90b0f8..fa7189e 100644
--- a/libbpf/util.h
+++ b/eth-filter.multi-thread/util.h
@@ -22,23 +22,24 @@
 #define UTIL_H
 
 #include <stdio.h>
-#include <execinfo.h>
 
+#include <linux/if_ether.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <arpa/inet.h>
 #include <netinet/ip.h>
 
-#include <mach.h>
+#define DEBUG
 
-#ifdef DEBUG
+#ifdef DEBUG 
 
 #define debug(format, ...) do                          \
 {                                                      \
+  extern FILE *logfile;                                        \
   char buf[1024];                                       \
-  snprintf (buf, 1024, "multiplexer: %s: %s\n", __func__, format);       \
+  snprintf (buf, 1024, "filter: %s", format);          \
   fprintf (stderr , buf, ## __VA_ARGS__);              \
-  fflush (stderr);                                     \
+  fflush (logfile);                                    \
 } while (0)
 
 #else
@@ -47,29 +48,10 @@
 
 #endif
 
-#define print_backtrace() do                           \
-{                                                      \
-  size_t size;                                         \
-  void *array[30];                                     \
-  size = backtrace (array, sizeof (array));            \
-  debug ("the depth of the stack: %d", size);          \
-  backtrace_symbols_fd(array, size, fileno (stderr));  \
-} while (0)
-
-#define ETH_ALEN 6             /* Octets in one ethernet addr   */
-
-struct ethhdr 
-{
-  unsigned char        h_dest[ETH_ALEN];       /* destination eth addr */
-  unsigned char        h_source[ETH_ALEN];     /* source ether addr    */
-  unsigned short h_proto;              /* packet type ID field */
-};
-
 static inline void
 print_pack (char *packet, int len)
 {
 #ifdef DEBUG
-#define ETH_P_IP 0x0800
   struct ethhdr *ethh = (struct ethhdr *) packet;
   struct iphdr *iph = (struct iphdr *)(ethh + 1);
   char src_str[INET_ADDRSTRLEN];
@@ -77,13 +59,13 @@ print_pack (char *packet, int len)
   if (ntohs (ethh->h_proto) == ETH_P_IP
       && len >= sizeof (struct ethhdr) + sizeof (struct iphdr)) 
     {
-      debug ("multiplexer: get a IP packet from %s to %s\n",
-            inet_ntop (AF_INET, &iph->saddr, src_str, INET_ADDRSTRLEN),
-            inet_ntop (AF_INET, &iph->daddr, dst_str, INET_ADDRSTRLEN));
+      debug ("pack: get a IP packet from %s to %s\n",
+             inet_ntop (AF_INET, &iph->saddr, src_str, INET_ADDRSTRLEN),
+             inet_ntop (AF_INET, &iph->daddr, dst_str, INET_ADDRSTRLEN));
     }
   else 
     {
-      debug ("multiplexer: get a non-IP packet\n");
+      debug ("pack: get a non-IP packet: %x\n", ntohs (ethh->h_proto));
     }
 #endif
 }
diff --git a/eth-filter/ChangeLog b/eth-filter/ChangeLog
new file mode 100644
index 0000000..1b3eb8a
--- /dev/null
+++ b/eth-filter/ChangeLog
@@ -0,0 +1,127 @@
+2009-04-18  Zheng Da  <address@hidden>
+
+       * filter.c (trivfs_allow_open): Change its value.
+       (ds_device_open): Change the mode for file_name_lookup().
+
+2009-01-02  Zheng Da  <address@hidden>
+
+       * filter.c (ds_device_write): Deallocate the out-of-line data.
+
+2008-10-03  Zheng Da  <address@hidden>
+
+       * Makefile: Remove the include paths from pfinet.
+
+       * util.h: Remove the line of including linux/if_ether.h
+       (ETH_ALEN): New macro.
+       (ethhdr): New structure.
+
+2008-09-26  Zheng Da  <address@hidden>
+
+       * filter.c (ethernet_demuxer): Save and restore the message header.
+       (deliver_msg): Use the non-block send.
+
+2008-08-22 Zheng Da <address@hidden>
+
+       * README: Update.
+
+       * filter.c (proxy_info): Removed.
+       (proxy_pfinetpi_ht): Removed.
+       (proxy_devicepi_ht): Removed.
+       (create_proxy): Removed.
+       (destroy_proxy): Removed.
+       (proxy_device): New structure.
+       (proxy_user): New structure.
+       (proxy): New structure.
+       (create_proxy_user): New function.
+       (create_proxy_device): New function.
+       (clean_proxy_user): New function.
+       (clean_proxy_device): New function.
+       (ethernet_demuxer): Get the data from proxy object instead of from
+       proxy_info.
+       (do_mach_notify_dead_name): Likewise.
+       (ds_device_write): Likewise.
+       (ds_device_write_inband): Likewise.
+       (ds_device_read): Likewise.
+       (ds_device_read_inband): Likewise.
+       (ds_device_map): Likewise.
+       (ds_device_set_status): Likewise.
+       (ds_device_get_status): Likewise.
+       (ds_device_set_filter): Likewise. Create the proxy_device object.
+       (do_mach_notify_no_senders): Use ports_do_mach_notify_no_senders().
+       (ds_device_open): Create proxy_user and proxy objects.
+       (main): Add the cleaning routine when creating the port class.
+
+2008-08-20 Zheng Da <address@hidden>
+
+       * README: Update.
+
+       * filter.c (options): Update.
+
+2008-08-20 Zheng Da <address@hidden>
+
+       * filter.c (destroy_proxy): Test p_deliverport_hashloc before removing
+       it from proxy_deliverport_ht, and reverse the order of calling
+       ports_destroy_right and ports_port_deref.
+       (ds_device_open): Test device_file and replace "eth" with the variable
+       name.
+
+2008-08-20 Zheng Da <address@hidden>
+
+       * filter.c (device_file): Change the name of a variable.
+       (ds_device_open): Use device_file directly.
+
+2008-08-19 Zheng Da <address@hidden>
+
+       * filter.c (ds_device_open): Generate the device file name, and use it
+       to open the device.
+
+2008-08-18 Zheng Da <address@hidden>
+
+       * README: New file.
+
+
+2008-08-17 Zheng Da <address@hidden>
+
+       * filter.c (device_name): Remove its default value.
+       (options): Remove the option '-M'.
+       (create_proxy): Get the port directly from port_right field in 
port_info.
+       (ds_device_open): Remove the code of checking the device name, 
+       and open the device from the device file.
+       (parse_opt): Remove the code of handling the option '-M'.
+       (main): Remove the code of getting the master device port.
+       
+
+2008-08-14 Zheng Da <address@hidden>
+
+       * filter.c (pfinet_portclass, device_portclass, other_portclass): 
+       New variables.
+       (create_proxy): Use pfinet_portclass and device_portclass to create the
+       port.
+       (destroy_proxy): Dereference the port before destroying it.
+       (trivfs_goaway): Test the number of ports in pfinet_portclass and 
destroy
+       the master_device.
+       (main): Create pfinet_portclass, device_portclass, other_portclass.
+       (print_msg): Deleted.
+       (deliver_msg): Remove debuging print.
+
+       * ChangeLog: New file.
+       
+
+2008-08-13 Zheng Da <address@hidden>
+
+       * bpf_impl.c: New file.
+
+       * Makefile: New file.
+
+       * queue.c: New file.
+
+       * util.h: New file.
+
+       * bpf_impl.h: New file.
+
+       * filter.c: New file.
+
+       * pcap_filter.c: New file.
+
+       * queue.h: New file.
+
diff --git a/devnode/Makefile b/eth-filter/Makefile
similarity index 80%
copy from devnode/Makefile
copy to eth-filter/Makefile
index f452256..16f5d73 100644
--- a/devnode/Makefile
+++ b/eth-filter/Makefile
@@ -15,18 +15,21 @@
 # along with the GNU Hurd; see the file COPYING.  If not, write to
 # the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
-dir := devnode
+dir := eth-filter
 makemode := server
 
-SRCS = devnode.c
-LCLHDRS = util.h
+SRCS = bpf_impl.c filter.c queue.c pcap_filter.c
+LCLHDRS = bpf_impl.h queue.h util.h
 DIST_FILES = ourdevice.defs notify.defs
-HURDLIBS = ports trivfs fshelp shouldbeinlibc
-target = devnode
+HURDLIBS = ports trivfs fshelp ihash shouldbeinlibc
+target = eth-filter
 MIGSTUBS = ourdeviceServer.o notifyServer.o
 OBJS = $(SRCS:.c=.o) $(MIGSTUBS)
 
 include ../Makeconf
 
+#CFLAGS += -I../pfinet/linux-src/include -I../pfinet/glue-include
+LDFLAGS += -lpcap
+
 ourdevice.defs: device.defs
        $(CPP) $(CPPFLAGS) -x c $< | sed -e '/out[      ]*device[       ]*:[    
]*device_t/s/device_t/mach_port_send_t/' > $@
diff --git a/eth-filter/README b/eth-filter/README
new file mode 100644
index 0000000..2ef16ed
--- /dev/null
+++ b/eth-filter/README
@@ -0,0 +1,39 @@
+[Introduce]
+
+eth-filter is a filter translator that runs on the network device. The goal of 
eth-filter is to allow the user to setup the policy to control the traffic to 
the network. For example, it can block the invalid packet or the packet with 
the wrong destination address. It can work with eth-multiplexer to control and 
reduce the traffic between eth-multiplexer and pfinet, and can also work alone, 
running directly on the real network device.
+
+
+[Usage]
+
+Usage: eth-filter [OPTION...]
+Hurd filter translator.
+
+  -i, --interface=DEVICE     Network interface to use
+  -s, --send-filter=string   The filter rule which applies to the outgoing
+                             packet
+  -r, --receive-filter=string   The filter rule which applies to the ingoing
+                             packet
+  -S, --send-ip-range=IP range   A range of IP to create the send filter
+  -R, --receive-ip-range=IP range
+                             A range of IP to create the receive filter
+  -?, --help                 Give this help list
+      --usage                Give a short usage message
+  -V, --version              Print program version
+
+Mandatory or optional arguments to long options are also mandatory or optional
+for any corresponding short options.
+
+
+The '-i' option specifies the network interface the translator sits on. 
eth-filter can only connect to one network interface and the '-i' option should 
be only used once. DEVICE is a device file created by devnode translator.
+The '-s' and '-r' options give the user a full control to specify the filter 
rules which applies to the outgoing packet and the incoming packet, 
respectively. The expression of the rule is the same as the one in TCPDUMP. 
+The '-S' and '-R' options specify a range of IP that the user wants to filter. 
They are used to create the sending filter and the receiving filter, 
respectively. The generated rule is "arp or (ip and src net addr/prefixlen)" or 
"arp or (ip and dst net addr/prefixlen)". If prefixlen is 32, it can be 
omitted. 
+NOTE: '-s' and '-S' cannot be used together. One will replace the other if 
they are both used. So are '-r' and '-R' options.
+An example: settrans -acfg /servers/feth0 /hurd/eth-filter -i /dev/eth0 -S 
192.168.8.0/24 -R 192.168.8.0/24
+
+[Internal]
+
+eth-filter works as a proxy, forwarding the packet between the user program 
and the network interface. In order to forward packets, eth-filter runs as a 
client to the network device. It opens the device and writes the packet to the 
network device as pfinet does. It calls device_set_filter() to set the filter 
rule and give its own port to the device so it can receive packets from the 
device. The rule passed to the network device is from the user program that 
connects to eth-filter.
+eth-filter works as a RPC server to communicate with the user program and 
implements the server side functions in device.defs. It gets the packet in the 
server side function ds_device_write and gets the port to deliver packets to 
the user program in ds_device_set_filter. 
+Three structures are used for one pair of the user program and the device: 
proxy_user, proxy_device, proxy. When the ds_device_open() is called, a 
proxy_user and proxy objectis created. A proxy_device object is created when 
the ds_device_set_filter() is called. The proxy_user and proxy_device extend 
port_info structure. When a packet is received from the user program or from 
the device, we have to retrieve the proxy object to get the corresponding 
information. This process is very similar as pflocal. When a user program 
exits, we need to destroy its proxy_user object and proxy object, and 
meanwhile, the proxy_device object related to the proxy object is also 
destroyed. 
+Two filters exist in eth-filter, one for outgoing packets and the other for 
incoming packets. These are BPF filters, which are ported from GNU Mach. These 
BPF filters only decide whether the packet should be forwarded, and they cannot 
decide the destination of the packet. The BPF instructions are generated by 
libpcap from the filter rules given by '-s' and '-r' or '-S' and '-R' options.
+
diff --git a/libbpf/bpf_impl.c b/eth-filter/bpf_impl.c
similarity index 87%
copy from libbpf/bpf_impl.c
copy to eth-filter/bpf_impl.c
index c8a250b..e9202a5 100644
--- a/libbpf/bpf_impl.c
+++ b/eth-filter/bpf_impl.c
@@ -45,7 +45,8 @@
 #include "queue.h"
 #include "util.h"
 
-static struct net_hash_header filter_hash_header[N_NET_HASH];
+queue_head_t rcv_port_list;    /* input filter list */
+queue_head_t snd_port_list;    /* output filter list */
 
 /*
  * Execute the filter program starting at pc on the packet p
@@ -58,7 +59,7 @@ static struct net_hash_header filter_hash_header[N_NET_HASH];
  */
 
 int
-bpf_do_filter(net_rcv_port_t infp, char *p,    unsigned int wirelen, 
+mach_bpf_do_filter(net_rcv_port_t infp, char *p,       unsigned int wirelen, 
                char *header, unsigned int hlen, net_hash_entry_t **hash_headpp,
                net_hash_entry_t *entpp)
 {
@@ -102,7 +103,7 @@ bpf_do_filter(net_rcv_port_t infp, char *p, unsigned int 
wirelen,
                                        A : wirelen;
 
                        case BPF_RET|BPF_MATCH_IMM:
-                               if (bpf_match ((net_hash_header_t)infp, pc->jt, 
mem,
+                               if (mach_bpf_match ((net_hash_header_t)infp, 
pc->jt, mem,
                                                        hash_headpp, entpp)) {
                                        return ((u_int)pc->k <= wirelen) ?
                                                pc->k : wirelen;
@@ -348,7 +349,7 @@ load_byte:
  * Otherwise, a bogus program could easily crash the system.
  */
 int
-bpf_validate(bpf_insn_t f, int bytes, bpf_insn_t *match)
+mach_bpf_validate(bpf_insn_t f, int bytes, bpf_insn_t *match)
 {
        register int i, j, len;
        register bpf_insn_t p;
@@ -419,7 +420,7 @@ bpf_validate(bpf_insn_t f, int bytes, bpf_insn_t *match)
 }
 
 int
-bpf_eq (bpf_insn_t f1, bpf_insn_t f2, int bytes)
+mach_bpf_eq (bpf_insn_t f1, bpf_insn_t f2, int bytes)
 {
        register int count;
 
@@ -436,7 +437,7 @@ bpf_eq (bpf_insn_t f1, bpf_insn_t f2, int bytes)
 }
 
 unsigned int
-bpf_hash (int n, unsigned int *keys)
+mach_bpf_hash (int n, unsigned int *keys)
 {
        register unsigned int hval = 0;
 
@@ -448,7 +449,7 @@ bpf_hash (int n, unsigned int *keys)
 
 
 int
-bpf_match (net_hash_header_t hash, int n_keys, unsigned int *keys,
+mach_bpf_match (net_hash_header_t hash, int n_keys, unsigned int *keys,
        net_hash_entry_t **hash_headpp, net_hash_entry_t *entpp)
 {
        register net_hash_entry_t head, entp;
@@ -457,7 +458,7 @@ bpf_match (net_hash_header_t hash, int n_keys, unsigned int 
*keys,
        if (n_keys != hash->n_keys)
                return FALSE;
 
-       *hash_headpp = &hash->table[bpf_hash(n_keys, keys)];
+       *hash_headpp = &hash->table[mach_bpf_hash(n_keys, keys)];
        head = **hash_headpp;
 
        if (head == 0)
@@ -485,7 +486,7 @@ bpf_match (net_hash_header_t hash, int n_keys, unsigned int 
*keys,
  */
 
 int
-hash_ent_remove (if_filter_list_t *ifp, net_hash_header_t hp, int used,
+hash_ent_remove (net_hash_header_t hp, int used,
                net_hash_entry_t *head, net_hash_entry_t entp, queue_entry_t 
*dead_p)
 {
        hp->ref_count--;
@@ -496,13 +497,13 @@ hash_ent_remove (if_filter_list_t *ifp, net_hash_header_t 
hp, int used,
                        ENQUEUE_DEAD(*dead_p, entp, chain);
                        if (hp->ref_count == 0 && !used) {
                                if (((net_rcv_port_t)hp)->filter[0] & NETF_IN)
-                                       queue_remove(&ifp->if_rcv_port_list,
+                                       queue_remove(&rcv_port_list,
                                                        (net_rcv_port_t)hp,
-                                                       net_rcv_port_t, input);
+                                                       net_rcv_port_t, chain);
                                if (((net_rcv_port_t)hp)->filter[0] & NETF_OUT)
-                                       queue_remove(&ifp->if_snd_port_list,
+                                       queue_remove(&snd_port_list,
                                                        (net_rcv_port_t)hp,
-                                                       net_rcv_port_t, output);
+                                                       net_rcv_port_t, chain);
                                hp->n_keys = 0;
                                return TRUE;
                        }
@@ -530,7 +531,7 @@ net_free_dead_infp (queue_entry_t dead_infp)
        register net_rcv_port_t infp, nextfp;
 
        for (infp = (net_rcv_port_t) dead_infp; infp != 0; infp = nextfp) {
-               nextfp = (net_rcv_port_t) queue_next(&infp->input);
+               nextfp = (net_rcv_port_t) queue_next(&infp->chain);
                mach_port_deallocate(mach_task_self(), infp->rcv_port);
                free(infp);
                debug ("a dead infp is freed\n");
@@ -564,8 +565,8 @@ net_free_dead_entp (queue_entry_t dead_entp)
  * We are given a naked send right for the rcv_port.
  * If we are successful, we must consume that right.
  */
-io_return_t
-net_set_filter(if_filter_list_t *ifp, mach_port_t rcv_port, int priority,
+error_t
+net_set_filter (mach_port_t rcv_port, int priority,
                filter_t *filter, unsigned int filter_count)
 {
        int               filter_bytes;
@@ -593,7 +594,7 @@ net_set_filter(if_filter_list_t *ifp, mach_port_t rcv_port, 
int priority,
        } else if (!((filter[0] & NETF_IN) || (filter[0] & NETF_OUT))) {
                return (D_INVALID_OPERATION); /* NETF_IN or NETF_OUT required */
        } else if ((filter[0] & NETF_TYPE_MASK) == NETF_BPF) {
-               ret = bpf_validate((bpf_insn_t)filter, filter_bytes, &match);
+               ret = mach_bpf_validate((bpf_insn_t)filter, filter_bytes, 
&match);
                if (!ret)
                        return (D_INVALID_OPERATION);
        } else {
@@ -630,18 +631,16 @@ net_set_filter(if_filter_list_t *ifp, mach_port_t 
rcv_port, int priority,
         */
        void check_filter_list(queue_head_t *if_port_list)
        {
-               FILTER_ITERATE(if_port_list, infp, nextfp,
-                               (if_port_list == &ifp->if_rcv_port_list)
-                               ? &infp->input : &infp->output)
-               {
+               FILTER_ITERATE(if_port_list, infp, nextfp, &infp->chain) {
                        if (infp->rcv_port == MACH_PORT_NULL) {
                                if (match != 0
                                                && infp->priority == priority
                                                && my_infp == 0
                                                && (infp->filter_end - 
infp->filter) == filter_count
-                                               && 
bpf_eq((bpf_insn_t)infp->filter,
-                                                       (bpf_insn_t)filter, 
filter_bytes))
+                                               && 
mach_bpf_eq((bpf_insn_t)infp->filter,
+                                                       (bpf_insn_t)filter, 
filter_bytes)) {
                                        my_infp = infp;
+                               }
 
                                for (i = 0; i < NET_HASH_SIZE; i++) {
                                        head = &((net_hash_header_t) 
infp)->table[i];
@@ -660,8 +659,7 @@ net_set_filter(if_filter_list_t *ifp, mach_port_t rcv_port, 
int priority,
                                                /* checked without 
                                                   ip_lock(entp->rcv_port) */
                                                if (entp->rcv_port == rcv_port) 
{
-                                                       ret = hash_ent_remove 
(ifp,
-                                                                       
(net_hash_header_t)infp,
+                                                       ret = hash_ent_remove 
((net_hash_header_t)infp,
                                                                        
(my_infp == infp),
                                                                        head,
                                                                        entp,
@@ -680,16 +678,15 @@ net_set_filter(if_filter_list_t *ifp, mach_port_t 
rcv_port, int priority,
 hash_loop_end:
                                ;
                        } else if (infp->rcv_port == rcv_port) {
-
                                /* Remove the old filter from lists */
                                if (infp->filter[0] & NETF_IN)
-                                       queue_remove(&ifp->if_rcv_port_list, 
infp,
-                                                       net_rcv_port_t, input);
+                                       queue_remove(&rcv_port_list, infp,
+                                                       net_rcv_port_t, chain);
                                if (infp->filter[0] & NETF_OUT)
-                                       queue_remove(&ifp->if_snd_port_list, 
infp,
-                                                       net_rcv_port_t, output);
+                                       queue_remove(&snd_port_list, infp,
+                                                       net_rcv_port_t, chain);
 
-                               ENQUEUE_DEAD(dead_infp, infp, input);
+                               ENQUEUE_DEAD(dead_infp, infp, chain);
                        }
                }
                FILTER_ITERATE_END
@@ -699,9 +696,9 @@ hash_loop_end:
        out = (filter[0] & NETF_OUT) != 0;
 
        if (in)
-               check_filter_list(&ifp->if_rcv_port_list);
+               check_filter_list(&rcv_port_list);
        if (out)
-               check_filter_list(&ifp->if_snd_port_list);
+               check_filter_list(&snd_port_list);
 
        if (my_infp == 0) {
                /* Allocate a dummy infp */
@@ -710,6 +707,7 @@ hash_loop_end:
                                break;
                }
                if (i == N_NET_HASH) {
+
                        mach_port_deallocate(mach_task_self() , rcv_port);
                        if (match != 0)
                                free(hash_entp);
@@ -741,24 +739,23 @@ hash_loop_end:
 
                /* Insert my_infp according to priority */
                if (in) {
-                       queue_iterate(&ifp->if_rcv_port_list, infp, 
net_rcv_port_t, input)
+                       queue_iterate(&rcv_port_list, infp, net_rcv_port_t, 
chain)
                                if (priority > infp->priority)
                                        break;
 
-                       queue_enter(&ifp->if_rcv_port_list, my_infp, 
net_rcv_port_t, input);
+                       queue_enter(&rcv_port_list, my_infp, net_rcv_port_t, 
chain);
                }
 
                if (out) {
-                       queue_iterate(&ifp->if_snd_port_list, infp, 
net_rcv_port_t, output)
+                       queue_iterate(&snd_port_list, infp, net_rcv_port_t, 
chain)
                                if (priority > infp->priority)
                                        break;
 
-                       queue_enter(&ifp->if_snd_port_list, my_infp, 
net_rcv_port_t, output);
+                       queue_enter(&snd_port_list, my_infp, net_rcv_port_t, 
chain);
                }
        }
 
-       if (match != 0)
-       {
+       if (match != 0) {
                /* Insert to hash list */
                net_hash_entry_t *p;
 
@@ -766,7 +763,7 @@ hash_loop_end:
                for (i = 0; i < match->jt; i++)     /* match->jt is n_keys */
                        hash_entp->keys[i] = match[i+1].k;
                p = &((net_hash_header_t)my_infp)->
-                       table[bpf_hash(match->jt, hash_entp->keys)];
+                       table[mach_bpf_hash(match->jt, hash_entp->keys)];
 
                /* Not checking for the same key values */
                if (*p == 0) {
@@ -790,14 +787,9 @@ clean_and_return:
        return (rval);
 }
 
+/* Remove the filter in if_port_list whose port for delivering is dead_port. */
 void
-destroy_filters (if_filter_list_t *ifp)
-{
-}
-
-void
-remove_dead_filter (if_filter_list_t *ifp, queue_head_t *if_port_list,
-               mach_port_t dead_port)
+remove_dead_filter (queue_head_t *if_port_list, mach_port_t dead_port)
 {
        net_rcv_port_t infp;
        net_rcv_port_t nextfp;
@@ -807,9 +799,7 @@ remove_dead_filter (if_filter_list_t *ifp, queue_head_t 
*if_port_list,
        int i, ret;
 
        dead_infp = dead_entp = 0;
-       FILTER_ITERATE (if_port_list, infp, nextfp,
-                       (if_port_list == &ifp->if_rcv_port_list)
-                       ? &infp->input : &infp->output) {
+       FILTER_ITERATE (if_port_list, infp, nextfp, &infp->chain) {
                if (infp->rcv_port == MACH_PORT_NULL) {
                        for (i = 0; i < NET_HASH_SIZE; i++) {
                                head = &((net_hash_header_t) infp)->table[i];
@@ -828,8 +818,7 @@ remove_dead_filter (if_filter_list_t *ifp, queue_head_t 
*if_port_list,
                                        /* checked without 
                                           ip_lock(entp->rcv_port) */
                                        if (entp->rcv_port == dead_port) {
-                                               ret = hash_ent_remove (ifp,
-                                                               
(net_hash_header_t) infp,
+                                               ret = hash_ent_remove 
((net_hash_header_t) infp,
                                                                0,
                                                                head,
                                                                entp,
@@ -850,13 +839,13 @@ hash_loop_end:
                } else if (infp->rcv_port == dead_port) {
                        /* Remove the old filter from lists */
                        if (infp->filter[0] & NETF_IN)
-                               queue_remove(&ifp->if_rcv_port_list, infp,
-                                               net_rcv_port_t, input);
+                               queue_remove(&rcv_port_list, infp,
+                                               net_rcv_port_t, chain);
                        if (infp->filter[0] & NETF_OUT)
-                               queue_remove(&ifp->if_snd_port_list, infp,
-                                               net_rcv_port_t, output);
+                               queue_remove(&snd_port_list, infp,
+                                               net_rcv_port_t, chain);
 
-                       ENQUEUE_DEAD(dead_infp, infp, input);
+                       ENQUEUE_DEAD(dead_infp, infp, chain);
                }
        }
        FILTER_ITERATE_END
@@ -866,3 +855,18 @@ hash_loop_end:
        if (dead_entp != 0)
                net_free_dead_entp(dead_entp);
 }
+
+/* Remove all filters whose port for delivering is dead_port. */
+void
+remove_dead_filter_from_all (mach_port_t dead_port)
+{
+       remove_dead_filter (&snd_port_list, dead_port);
+       remove_dead_filter (&rcv_port_list, dead_port);
+}
+
+void
+bpf_init()
+{
+       queue_init (&rcv_port_list);
+       queue_init (&snd_port_list);
+}
diff --git a/libbpf/bpf_impl.h b/eth-filter/bpf_impl.h
similarity index 79%
copy from libbpf/bpf_impl.h
copy to eth-filter/bpf_impl.h
index 9073fda..de61df1 100644
--- a/libbpf/bpf_impl.h
+++ b/eth-filter/bpf_impl.h
@@ -38,16 +38,12 @@
 #ifndef BPF_IMPL_H
 #define BPF_IMPL_H
 
+#include <mach.h>
+#include <hurd.h>
 #include <device/bpf.h>
 
 #include "queue.h"
 
-typedef struct
-{
-  queue_head_t if_rcv_port_list;       /* input filter list */
-  queue_head_t if_snd_port_list;       /* output filter list */
-}if_filter_list_t;
-
 typedef        unsigned short  filter_t;
 typedef filter_t       *filter_array_t;
 
@@ -98,8 +94,7 @@ typedef filter_t      *filter_array_t;
  * filter for a single session.
  */
 struct net_rcv_port {
-       queue_chain_t   input;          /* list of input open_descriptors */
-       queue_chain_t   output;         /* list of output open_descriptors */
+       queue_chain_t   chain;
        mach_port_t     rcv_port;       /* port to send packet to */
        int             rcv_count;      /* number of packets received */
        int             priority;       /* priority for filter */
@@ -134,28 +129,30 @@ struct net_hash_header {
        int n_keys;                     /* zero if not used */
        int ref_count;                  /* reference count */
        net_hash_entry_t table[NET_HASH_SIZE];
-};
+} filter_hash_header[N_NET_HASH];
 
 typedef struct net_hash_header *net_hash_header_t;
 
-int bpf_do_filter(net_rcv_port_t infp, char *p,        unsigned int wirelen, 
+int mach_bpf_do_filter(net_rcv_port_t infp, char *p,   unsigned int wirelen, 
                char *header, unsigned int hlen, net_hash_entry_t **hash_headpp,
                net_hash_entry_t *entpp);
-io_return_t net_set_filter(if_filter_list_t *ifp, mach_port_t rcv_port,
-               int priority, filter_t *filter, unsigned int filter_count);
-
-int bpf_validate(bpf_insn_t f, int bytes, bpf_insn_t *match);
-int bpf_eq (bpf_insn_t f1, bpf_insn_t f2, int bytes);
-unsigned int bpf_hash (int n, unsigned int *keys);
-int bpf_match (net_hash_header_t hash, int n_keys, unsigned int *keys,
-       net_hash_entry_t **hash_headpp, net_hash_entry_t *entpp);
-
-int hash_ent_remove (if_filter_list_t *ifp, net_hash_header_t hp, int used,
-               net_hash_entry_t *head, net_hash_entry_t entp, queue_entry_t 
*dead_p);
+int mach_bpf_validate(bpf_insn_t f, int bytes, bpf_insn_t *match);
+int mach_bpf_eq (bpf_insn_t f1, bpf_insn_t f2, int bytes);
+unsigned int mach_bpf_hash (int n, unsigned int *keys);
+int mach_bpf_match (net_hash_header_t hash, int n_keys, unsigned int *keys,
+               net_hash_entry_t **hash_headpp, net_hash_entry_t *entpp);
+
+error_t net_set_filter(mach_port_t rcv_port, int priority,
+               filter_t *filter, unsigned int filter_count);
+int hash_ent_remove (net_hash_header_t hp, int used, net_hash_entry_t *head,
+               net_hash_entry_t entp, queue_entry_t *dead_p);
 void net_free_dead_infp (queue_entry_t dead_infp);
 void net_free_dead_entp (queue_entry_t dead_entp);
-void remove_dead_filter (if_filter_list_t *ifp, 
-               queue_head_t *if_port_list, mach_port_t dead_port);
-void destroy_filters (if_filter_list_t *ifp);
+void remove_dead_filter (queue_head_t *if_port_list, mach_port_t dead_port);
+void remove_dead_filter_from_all (mach_port_t dead_port);
+void bpf_init();
+
+extern queue_head_t rcv_port_list;     /* input filter list */
+extern queue_head_t snd_port_list;     /* output filter list */
 
 #endif /* _DEVICE_BPF_H_ */
diff --git a/eth-filter/filter.c b/eth-filter/filter.c
new file mode 100644
index 0000000..7720b0c
--- /dev/null
+++ b/eth-filter/filter.c
@@ -0,0 +1,901 @@
+/* 
+   Copyright (C) 2008 Free Software Foundation, Inc.
+   Written by Zheng Da.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd; see the file COPYING.  If not, write to
+   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+/*
+ * This program is a filter translator which sits on the top of the network
+ * interface. 
+ * It provides two filters: for outgoing packets and for incoming packets.
+ * Only one pfinet server are allowed to run on the top of the translator. 
+ */
+
+#include <argp.h>
+#include <errno.h>
+#include <error.h>
+#include <stddef.h>
+
+#include <fcntl.h>
+#include <hurd.h>
+#include <mach.h>
+#include <device/device.h>
+#include <hurd/trivfs.h>
+#include <hurd/ports.h>
+#include <hurd/ihash.h>
+
+#include "ourdevice_S.h"
+#include "notify_S.h"
+#include "bpf_impl.h"
+#include "util.h"
+
+struct proxy_user
+{
+  struct port_info pi;
+  struct proxy *proxy;
+};
+
+struct proxy_device
+{
+  struct port_info pi;
+  struct proxy *proxy;
+};
+
+struct proxy
+{
+  struct proxy_device *device;
+  mach_port_t deliver_port;
+  hurd_ihash_locp_t p_deliverport_hashloc;
+  mach_port_t device_port;
+};
+
+int deliver_msg (struct net_rcv_msg *msg, queue_head_t *port_list, 
+                mach_port_t dest);
+
+static struct hurd_ihash proxy_deliverport_ht
+= HURD_IHASH_INITIALIZER (offsetof (struct proxy, p_deliverport_hashloc));
+
+/* The name of the network interface that the filter translator sits on. */
+static char *device_file;
+const char *argp_program_version = "eth-filter 0.1";
+const char *argp_program_bug_address = "<address@hidden>";
+static const char doc[] = "Hurd filter translator.";
+static const struct argp_option options[] =
+{
+    {"interface", 'i', "DEVICE", 0,
+      "Network interface to use", 2},
+    {"send-filter", 's', "string", 0,
+      "The filter rule which applies to the outgoing packet", 4},
+    {"receive-filter", 'r', "string", 0,
+      "The filter rule which applies to the ingoing packet", 5},
+    {"send-ip-range", 'S', "IP range", 0,
+      "A range of IP to create the send filter", 6},
+    {"receive-ip-range", 'R', "IP range", 0,
+      "A range of IP to create the receive filter", 7},
+    {0}
+};
+
+/* A filter allows every packet to be sent and be received. */
+static struct bpf_insn default_snd_filter[] = 
+{
+    {NETF_OUT|NETF_BPF, 0, 0, 0},
+    {6, 0, 0, 1500}
+};
+static struct bpf_insn default_rcv_filter[] = 
+{
+    {NETF_IN|NETF_BPF, 0, 0, 0},
+    {6, 0, 0, 1500}
+};
+static char *snd_string = NULL;
+static struct bpf_insn *snd_filter = NULL;
+static int snd_filter_length;
+static char *rcv_string = NULL;
+static struct bpf_insn *rcv_filter = NULL;
+static int rcv_filter_length;
+
+/* Port bucket we service requests on.  */
+struct port_bucket *port_bucket;
+
+struct port_class *user_portclass;
+struct port_class *device_portclass;
+struct port_class *other_portclass;
+
+/* Trivfs hooks.  */
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+int trivfs_support_read = 0;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+int trivfs_allow_open = O_READ | O_WRITE;
+
+struct port_class *trivfs_protid_portclasses[1];
+struct port_class *trivfs_cntl_portclasses[1];
+int trivfs_protid_nportclasses = 1;
+int trivfs_cntl_nportclasses = 1;
+
+/* For getting the notification of ports from the kernel. */
+struct port_info *notify_pi;
+
+error_t
+create_proxy_user (struct proxy *proxy, mach_port_t *port)
+{
+  error_t err;
+  struct proxy_user *user;
+
+  err = ports_create_port (user_portclass, port_bucket, sizeof (*user), &user);
+  if (err)
+      return err;
+  user->proxy = proxy;
+
+  *port = ports_get_right (user);
+  ports_port_deref (user);
+  return 0;
+}
+
+error_t 
+create_proxy_device (struct proxy *proxy, mach_port_t *port)
+{
+  error_t err;
+  struct proxy_device *device;
+
+  err = ports_create_port (device_portclass, port_bucket, sizeof (*device), 
&device);
+  if (err)
+      return err;
+  device->proxy = proxy;
+  proxy->device = device;
+
+  *port = ports_get_right (device);
+  ports_port_deref (device);
+  return 0;
+}
+
+void
+clean_proxy_user (void *p)
+{
+  struct proxy_user *user = p;
+  struct proxy *proxy = user->proxy;
+
+  if (proxy->p_deliverport_hashloc)
+    hurd_ihash_locp_remove (&proxy_deliverport_ht, 
proxy->p_deliverport_hashloc);
+
+  if (proxy->deliver_port != MACH_PORT_NULL)
+    mach_port_deallocate (mach_task_self (), proxy->deliver_port);
+  if (proxy->device_port != MACH_PORT_NULL)
+    mach_port_deallocate (mach_task_self (), proxy->device_port);
+
+  if (proxy->device)
+    ports_destroy_right (proxy->device);
+
+  free (proxy);
+}
+
+void
+clean_proxy_device (void *p)
+{
+  struct proxy_device *device = p;
+  if (device->proxy)
+    device->proxy->device = NULL;
+}
+
+static int
+filter_demuxer (mach_msg_header_t *inp,
+               mach_msg_header_t *outp)
+{
+  extern int device_server (mach_msg_header_t *, mach_msg_header_t *);
+  extern int notify_server (mach_msg_header_t *, mach_msg_header_t *);
+  extern int ethernet_demuxer (mach_msg_header_t *, mach_msg_header_t *);
+  return device_server (inp, outp) || notify_server (inp, outp)
+    || ethernet_demuxer (inp, outp) || trivfs_demuxer (inp, outp);
+}
+
+int
+ethernet_demuxer (mach_msg_header_t *inp,
+                 mach_msg_header_t *outp)
+{
+  struct net_rcv_msg *msg = (struct net_rcv_msg *) inp;
+  struct proxy_device *device;
+  struct proxy *proxy;
+  mach_msg_header_t header;
+
+  if (inp->msgh_id != NET_RCV_MSG_ID)
+    return 0;
+
+  device = ports_lookup_port (port_bucket, inp->msgh_local_port, 
device_portclass);
+  if (device == NULL)
+    return 0;
+  proxy = device->proxy;
+  ports_port_deref (device);
+
+  if (proxy && proxy->deliver_port != MACH_PORT_NULL)
+    {
+      header = msg->msg_hdr;
+      deliver_msg (msg, &rcv_port_list, proxy->deliver_port);
+      msg->msg_hdr = header;
+    }
+
+  return 1;
+}
+
+/* Implementation of notify interface */
+kern_return_t
+do_mach_notify_port_deleted (mach_port_t notify,
+                            mach_port_t name)
+{
+  return EOPNOTSUPP;
+}
+
+kern_return_t
+do_mach_notify_msg_accepted (mach_port_t notify,
+                            mach_port_t name)
+{
+  return EOPNOTSUPP;
+}
+
+kern_return_t
+do_mach_notify_port_destroyed (mach_port_t notify,
+                              mach_port_t port)
+{
+  return EOPNOTSUPP;
+}
+
+kern_return_t
+do_mach_notify_no_senders (mach_port_t notify,
+                          mach_port_mscount_t mscount)
+{
+  debug ("do_mach_notify_no_senders is called\n");
+  return ports_do_mach_notify_no_senders (notify, mscount);
+}
+
+kern_return_t
+do_mach_notify_send_once (mach_port_t notify)
+{
+  return EOPNOTSUPP;
+}
+
+kern_return_t
+do_mach_notify_dead_name (mach_port_t notify,
+                         mach_port_t name)
+{
+  struct proxy *proxy;
+
+  debug ("do_mach_notify_dead_name is called\n");
+  mach_port_deallocate (mach_task_self (), name);
+  proxy = hurd_ihash_find (&proxy_deliverport_ht, name);
+  if (proxy)
+    {
+      proxy->deliver_port = MACH_PORT_NULL;
+      return 0;
+    }
+  return EINVAL;
+}
+
+/* Implementation of device interface */
+kern_return_t 
+ds_xxx_device_set_status (device_t device, dev_flavor_t flavor,
+                         dev_status_t status, size_t statu_cnt)
+{
+  return D_INVALID_OPERATION;
+}
+
+kern_return_t
+ds_xxx_device_get_status (device_t device, dev_flavor_t flavor,
+                         dev_status_t status, size_t *statuscnt)
+{
+  return D_INVALID_OPERATION;
+}
+
+kern_return_t
+ds_xxx_device_set_filter (device_t device, mach_port_t rec,
+                         int pri, filter_array_t filt, size_t len)
+{
+  return D_INVALID_OPERATION;
+}
+
+kern_return_t
+ds_device_open (mach_port_t master_port, mach_port_t reply_port,
+               mach_msg_type_name_t reply_portPoly,
+               dev_mode_t mode, dev_name_t name, mach_port_t *device, 
+               mach_msg_type_name_t *devicetype)
+{
+  kern_return_t err;
+  mach_port_t master_device;
+  mach_port_t user_port;
+  int file_mode = 0;
+  struct proxy *proxy;
+
+  if (device_file == NULL)
+    return D_NO_SUCH_DEVICE;
+
+  if (mode | D_WRITE)
+    file_mode |= O_WRITE;
+  if (mode | D_READ)
+    file_mode |= O_READ;
+  master_device = file_name_lookup (device_file, file_mode, 0);
+  if (master_device == MACH_PORT_NULL)
+    return errno;
+
+  proxy = (struct proxy *)calloc (1, sizeof (*proxy));
+  if (proxy == NULL)
+    {
+      mach_port_deallocate (mach_task_self (), master_device);
+      return D_NO_MEMORY;
+    }
+
+  err = device_open (master_device, mode, name, &proxy->device_port);
+  mach_port_deallocate (mach_task_self (), master_device);
+  if (err != KERN_SUCCESS)
+    {
+      free (proxy);
+      return err;
+    }
+
+  err = create_proxy_user (proxy, &user_port);
+  if (err)
+    {
+      mach_port_deallocate (mach_task_self (), master_device);
+      free (proxy);
+      return err;
+    }
+
+  *device = user_port;
+  *devicetype = MACH_MSG_TYPE_MAKE_SEND;
+
+  return 0;
+}
+
+kern_return_t
+ds_device_close (device_t device)
+{
+  return 0;
+}
+
+kern_return_t
+ds_device_write (device_t device, mach_port_t reply_port,
+                mach_msg_type_name_t reply_type, dev_mode_t mode,
+                recnum_t recnum, io_buf_ptr_t data, size_t datalen,
+                int *bytes_written)
+{
+  int ret_count = 0;
+  int has_filter = 0;
+  net_hash_entry_t entp, *hash_headp;
+  net_rcv_port_t infp, nextfp;
+  struct proxy_user *user;
+  struct proxy *proxy;
+
+  user = ports_lookup_port (port_bucket, device, user_portclass);
+  if (user == NULL)
+    {
+      vm_deallocate (mach_task_self (), data, datalen);
+      return D_INVALID_OPERATION;
+    }
+  proxy = user->proxy;
+  ports_port_deref (user);
+
+  /* The packet can be sent as long as it passes one filter,
+   * even thought there is usually only one filter in the list. */
+  FILTER_ITERATE (&snd_port_list, infp, nextfp, &infp->chain) 
+    {
+      has_filter = 1;
+      ret_count = mach_bpf_do_filter (infp,
+                                     data + sizeof (struct ethhdr),
+                                     datalen - sizeof (struct ethhdr),
+                                     data, sizeof (struct ethhdr),
+                                     &hash_headp, &entp);
+      if (ret_count)
+       break;
+    }
+  FILTER_ITERATE_END
+
+  error_t err;
+  if (ret_count || !has_filter) 
+    {
+      print_pack (data, datalen);
+      debug ("before writing a packet from the device.\n");
+      err = device_write (proxy->device_port, mode , recnum ,
+                         data, datalen, bytes_written);
+      debug ("after writing a packet from the device.\n");
+    }
+  else 
+    {
+      *bytes_written = datalen;
+      err = 0;
+    }
+  vm_deallocate (mach_task_self (), data, datalen);
+  return err;
+}
+
+kern_return_t
+ds_device_write_inband (device_t device, mach_port_t reply_port,
+                       mach_msg_type_name_t reply_type, dev_mode_t mode,
+                       recnum_t recnum, io_buf_ptr_inband_t data,
+                       size_t datalen, int *bytes_written)
+{
+  kern_return_t ret;
+  struct proxy_user *user;
+  struct proxy *proxy;
+
+  user = ports_lookup_port (port_bucket, device, user_portclass);
+  if (user == NULL)
+    return D_INVALID_OPERATION;
+  proxy = user->proxy;
+  ports_port_deref (user);
+
+  ret = device_write_inband (proxy->device_port, mode, recnum, data,
+                            datalen, bytes_written);
+  return ret;
+}
+
+kern_return_t
+ds_device_read (device_t device, mach_port_t reply_port,
+               mach_msg_type_name_t reply_type, dev_mode_t mode,
+               recnum_t recnum, int bytes_wanted,
+               io_buf_ptr_t *data, size_t *datalen)
+{
+  kern_return_t ret;
+  struct proxy_user *user;
+  struct proxy *proxy;
+
+  user = ports_lookup_port (port_bucket, device, user_portclass);
+  if (user == NULL)
+    return D_INVALID_OPERATION;
+  proxy = user->proxy;
+  ports_port_deref (user);
+
+  ret = device_read (proxy->device_port, mode, recnum,
+                    bytes_wanted, data, datalen);
+  return ret;
+}
+
+kern_return_t
+ds_device_read_inband (device_t device, mach_port_t reply_port,
+                      mach_msg_type_name_t reply_type, dev_mode_t mode,
+                      recnum_t recnum, int bytes_wanted,
+                      io_buf_ptr_inband_t data, size_t *datalen)
+{
+  kern_return_t ret;
+  struct proxy_user *user;
+  struct proxy *proxy;
+
+  user = ports_lookup_port (port_bucket, device, user_portclass);
+  if (user == NULL)
+    return D_INVALID_OPERATION;
+  proxy = user->proxy;
+  ports_port_deref (user);
+
+  ret = device_read_inband (proxy->device_port, mode, recnum, 
+                           bytes_wanted, data, datalen);
+  return ret;
+}
+
+kern_return_t
+ds_device_map (device_t device, vm_prot_t prot, vm_offset_t offset,
+              vm_size_t size, memory_object_t *pager, int unmap)
+{
+  kern_return_t ret;
+  struct proxy_user *user;
+  struct proxy *proxy;
+
+  user = ports_lookup_port (port_bucket, device, user_portclass);
+  if (user == NULL)
+    return D_INVALID_OPERATION;
+  proxy = user->proxy;
+  ports_port_deref (user);
+
+  ret = device_map (proxy->device_port, prot, offset,
+                   size, pager, unmap);
+  return ret;
+}
+
+kern_return_t
+ds_device_set_status (device_t device, dev_flavor_t flavor,
+                     dev_status_t status, size_t statuslen)
+{
+  kern_return_t ret;
+  struct proxy_user *user;
+  struct proxy *proxy;
+
+  user = ports_lookup_port (port_bucket, device, user_portclass);
+  if (user == NULL)
+    return D_INVALID_OPERATION;
+  proxy = user->proxy;
+  ports_port_deref (user);
+
+  ret = device_set_status (proxy->device_port, flavor,
+                          status, statuslen);
+  return ret;
+}
+
+kern_return_t
+ds_device_get_status (device_t device, dev_flavor_t flavor,
+                     dev_status_t status, size_t *statuslen)
+{
+  kern_return_t ret;
+  struct proxy_user *user;
+  struct proxy *proxy;
+
+  user = ports_lookup_port (port_bucket, device, user_portclass);
+  if (user == NULL)
+    return D_INVALID_OPERATION;
+  proxy = user->proxy;
+  ports_port_deref (user);
+
+  ret = device_get_status (proxy->device_port, flavor, status, statuslen);
+  return ret;
+}
+
+kern_return_t
+ds_device_set_filter (device_t device, mach_port_t receive_port,
+                     int priority, filter_array_t filter, size_t filterlen)
+{
+  mach_port_t tmp;
+  kern_return_t err;
+  mach_port_t device_receive_port;
+  struct proxy_user *user;
+  struct proxy *proxy;
+
+  user = ports_lookup_port (port_bucket, device, user_portclass);
+  if (user == NULL)
+    return D_INVALID_OPERATION;
+  proxy = user->proxy;
+  ports_port_deref (user);
+
+  if (proxy->device == NULL)
+    {
+      error_t err;
+      err = create_proxy_device (proxy, &device_receive_port);
+      if (err)
+       return err;
+    }
+  else
+    device_receive_port = ports_get_right (proxy->device);
+
+  /* Set the filter from pfinet into the interface,
+   * but the packet will be delivered to the translator,
+   * so the translator has the chance to filter some packets. */
+  err = device_set_filter (proxy->device_port,
+                          device_receive_port,
+                          MACH_MSG_TYPE_MAKE_SEND, priority,
+                          filter, filterlen);
+  if (err)
+    return err;
+
+  proxy->deliver_port = receive_port;
+  hurd_ihash_add (&proxy_deliverport_ht, receive_port, proxy);
+
+  err = mach_port_request_notification (mach_task_self (), receive_port, 
+                                       MACH_NOTIFY_DEAD_NAME, 0,
+                                       ports_get_right (notify_pi), 
+                                       MACH_MSG_TYPE_MAKE_SEND_ONCE, &tmp);
+  if (tmp != MACH_PORT_NULL)
+    mach_port_deallocate (mach_task_self (), tmp);
+
+  return err;
+}
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, io_statbuf_t *stat)
+{
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+  int count;
+
+  /* Stop new requests.  */
+  ports_inhibit_class_rpcs (trivfs_cntl_portclasses[0]);
+  ports_inhibit_class_rpcs (trivfs_protid_portclasses[0]);
+
+  count = ports_count_class (user_portclass);
+  debug ("the number of ports alive: %d\n", count);
+
+  if (count && !(flags & FSYS_GOAWAY_FORCE)) 
+    {
+      /* We won't go away, so start things going again...  */
+      ports_enable_class (trivfs_protid_portclasses[0]);
+      ports_resume_class_rpcs (trivfs_cntl_portclasses[0]);
+      ports_resume_class_rpcs (trivfs_protid_portclasses[0]); 
+      return EBUSY;
+    } 
+
+  debug ("the translator is gone away\n");
+  exit (0);
+}
+
+/* Convert the network address input by the user into
+ * a form that is accepted by libpcap. */
+int
+correct_net_addr (char *orig, char *result, int result_len)
+{
+  char *ptr;
+  int netmask_len;
+  int remain_bits;
+  int remain_bytes;
+  char netmask;
+  char addr[4];
+  char buf[INET_ADDRSTRLEN];
+  int i;
+
+  ptr = strstr (orig, "/");
+  if (ptr == NULL)
+    {
+      strncpy (result, orig, result_len);
+      return 0;
+    }
+
+  *ptr = 0;
+  ptr++;
+  netmask_len = atoi (ptr);
+  if (inet_pton (AF_INET, orig, addr) < 0)
+    {
+      perror ("inet_pton");
+      return -1;
+    }
+  remain_bits = netmask_len % 8;
+  netmask = ~0;
+  netmask >>= 8 - remain_bits;
+  netmask <<= 8 - remain_bits;
+  remain_bytes = netmask_len / 8;
+  addr[remain_bytes] &= netmask;
+  for (i=remain_bytes+1 ; i < 4 ; i++)
+    addr[i] = 0;
+
+  snprintf (result, result_len, "%s/%s",
+           inet_ntop (AF_INET, addr, buf, INET_ADDRSTRLEN), ptr);
+  return 0;
+}
+
+static error_t
+parse_opt (int opt, char *arg, struct argp_state *state)
+{
+  struct bpf_insn *trans_filter_program (char *str, int send,
+                                        int *filter_len);
+  char buf[1024];
+  char addr[INET_ADDRSTRLEN+4];
+
+  switch (opt)
+    {
+    case 'i':
+      device_file = arg;
+      break;
+    case 's':
+      if (snd_filter && snd_filter != default_snd_filter)
+       free (snd_filter);
+      if (snd_string)
+       free (snd_string);
+      snd_string = strdup (arg);
+      snd_filter = trans_filter_program (arg, 1, &snd_filter_length);
+      if (!snd_filter)
+       return ARGP_ERR_UNKNOWN;
+      break;
+    case 'r':
+      if (rcv_filter && rcv_filter != default_rcv_filter)
+       free (rcv_filter);
+      if (rcv_string)
+       free (rcv_string);
+      rcv_string = strdup (arg);
+      rcv_filter = trans_filter_program (arg, 0, &rcv_filter_length);
+      if (!rcv_filter)
+       return ARGP_ERR_UNKNOWN;
+      break;
+    case 'S':
+      if (correct_net_addr (arg, addr, INET_ADDRSTRLEN+4) < 0)
+       return 0;
+      snprintf (buf, sizeof (buf), "arp or (ip and src net %s)", addr);
+      if (snd_filter && snd_filter != default_snd_filter) 
+       free (snd_filter);
+      if (snd_string)
+       free (snd_string);
+      snd_string = strdup (buf);
+      snd_filter = trans_filter_program (buf, 1, &snd_filter_length);
+      if (!snd_filter)
+       return ARGP_ERR_UNKNOWN;
+      break;
+    case 'R':
+      if (correct_net_addr (arg, addr, INET_ADDRSTRLEN+4) < 0)
+       return 0;
+      snprintf (buf, sizeof (buf), "arp or (ip and dst net %s)", addr);
+      if (rcv_filter && rcv_filter != default_rcv_filter) 
+       free (rcv_filter);
+      if (rcv_string)
+       free (rcv_string);
+      rcv_string = strdup (buf);
+      rcv_filter = trans_filter_program (buf, 0, &rcv_filter_length);
+      if (!rcv_filter)
+       return ARGP_ERR_UNKNOWN;
+      break;
+    case ARGP_KEY_ERROR:
+    case ARGP_KEY_SUCCESS:
+    case ARGP_KEY_INIT:
+      break;
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+  return 0;
+}
+
+static const struct argp argp = { options, parse_opt, 0, doc };
+
+error_t
+trivfs_append_args (struct trivfs_control *fsys,
+                   char **argz, size_t *argz_len)
+{
+  error_t err;
+  char *opt;
+
+  if (device_file)
+    {
+      asprintf(&opt, "--interface=\"%s\"", device_file);
+      argz_add (argz, argz_len, opt);
+      free (opt);
+    }
+
+  if (snd_string)
+    {
+      asprintf(&opt, "--send-filter=\"%s\"", snd_string);
+      argz_add (argz, argz_len, opt);
+      free (opt);
+    }
+
+  if (rcv_string)
+    {
+      asprintf(&opt, "--receive-filter=\"%s\"", rcv_string);
+      argz_add (argz, argz_len, opt);
+      free (opt);
+    }
+
+  return 0;
+}
+
+error_t
+trivfs_set_options (struct trivfs_control *fsys, char *argz, size_t argz_len)
+{
+  error_t err;
+
+  err = fshelp_set_options (&argp, 0, argz, argz_len, fsys);
+  if (err)
+    return err;
+
+  err = net_set_filter (MACH_PORT_DEAD, 0, (filter_t *) snd_filter,
+                       snd_filter_length);
+  if (err)
+    return err;
+
+  err = net_set_filter (MACH_PORT_DEAD, 0, (filter_t *) rcv_filter,
+                       rcv_filter_length);
+  if (err)
+    return err;
+}
+
+int
+main (int argc, char *argv[])
+{
+  error_t err;
+  mach_port_t bootstrap;
+  struct trivfs_control *fsys;
+
+  port_bucket = ports_create_bucket ();
+  user_portclass = ports_create_class (clean_proxy_user, 0);
+  device_portclass = ports_create_class (clean_proxy_device, 0);
+  other_portclass = ports_create_class (0, 0);
+  trivfs_cntl_portclasses[0] = ports_create_class (trivfs_clean_cntl, 0);
+  trivfs_protid_portclasses[0] = ports_create_class (trivfs_clean_protid, 0);
+
+  argp_parse (&argp, argc, argv, 0, 0, 0);
+
+  /* Prepare the filter. */
+  if (snd_filter == NULL) 
+    {
+      snd_filter = default_snd_filter;
+      snd_filter_length = sizeof (default_snd_filter) / sizeof (short);
+    }
+  if (rcv_filter == NULL) 
+    {
+      rcv_filter = default_rcv_filter;
+      rcv_filter_length = sizeof (default_rcv_filter) / sizeof (short);
+    }
+
+  task_get_bootstrap_port (mach_task_self (), &bootstrap);
+  if (bootstrap == MACH_PORT_NULL)
+    error (1, 0, "must be started as a translator");
+
+  err = ports_create_port (other_portclass, port_bucket, 
+                          sizeof (struct port_info), &notify_pi);
+  if (err)
+    error (1, err, "ports_create_port for notification");
+
+  /* Reply to our parent.  */
+  err = trivfs_startup (bootstrap, 0,
+                       trivfs_cntl_portclasses[0], port_bucket,
+                       trivfs_protid_portclasses[0], port_bucket, &fsys);
+  mach_port_deallocate (mach_task_self (), bootstrap);
+  if (err)
+    error (1, err, "Contacting parent");
+
+  /* Initialize the bpf, and set the filter for outgoing packets.
+   * MACH_PORT_DEAD is used because we don't need a receiving port. */
+  bpf_init ();
+  err = net_set_filter (MACH_PORT_DEAD, 0, (filter_t *) snd_filter,
+                       snd_filter_length);
+  if (err)
+    error (1, err, "set the sending filter");
+  /* Set the filter translator's own rule. */
+  err = net_set_filter (MACH_PORT_DEAD, 0, (filter_t *) rcv_filter,
+                       rcv_filter_length);
+  if (err)
+    error (1, err, "set the receiving filter");
+
+  /* Launch.  */
+  do 
+    {
+      ports_manage_port_operations_one_thread (port_bucket, 
+                                              filter_demuxer, 0);
+    } while (trivfs_goaway (fsys, 0)); 
+  return 0;
+}
+
+int
+deliver_msg (struct net_rcv_msg *msg, queue_head_t *port_list, 
+            mach_port_t dest)
+{
+  int ret_count = 0;
+  int has_filter = 0;
+  mach_msg_return_t err;
+  net_rcv_port_t infp, nextfp;
+
+  msg->msg_hdr.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, 0);
+  msg->msg_hdr.msgh_local_port = MACH_PORT_NULL;
+  msg->msg_hdr.msgh_kind = MACH_MSGH_KIND_NORMAL;
+
+  /* Deliver the packet to the right pfinet,
+   * actually there should be only one filter in the list. */
+  FILTER_ITERATE (port_list, infp, nextfp, &infp->chain) 
+    {
+      net_hash_entry_t entp, *hash_headp;
+
+      entp = (net_hash_entry_t) 0;
+      ret_count = mach_bpf_do_filter (infp,
+                                     msg->packet + sizeof (struct 
packet_header),
+                                     msg->net_rcv_msg_packet_count,
+                                     msg->header,
+                                     sizeof (struct ethhdr),
+                                     &hash_headp, &entp);
+
+      if (ret_count) 
+       break;
+    }
+  FILTER_ITERATE_END
+    
+    if (ret_count || !has_filter)
+      {
+       msg->msg_hdr.msgh_remote_port = dest;
+       debug ("before delivering the message\n");
+       err = mach_msg ((mach_msg_header_t *)msg,
+                       MACH_SEND_MSG|MACH_SEND_TIMEOUT,
+                       msg->msg_hdr.msgh_size, 0, MACH_PORT_NULL,
+                       0, MACH_PORT_NULL);
+       if (err != MACH_MSG_SUCCESS)
+         {
+           fprintf (stderr, "deliver msg: mach_msg: %s\n",
+                    strerror (err));
+           return -1;
+         }
+       debug ("after delivering the message\n");
+      }
+
+    return 0;
+}
diff --git a/eth-filter/pcap_filter.c b/eth-filter/pcap_filter.c
new file mode 100644
index 0000000..67ee9bb
--- /dev/null
+++ b/eth-filter/pcap_filter.c
@@ -0,0 +1,77 @@
+/* 
+   Copyright (C) 2008 Free Software Foundation, Inc.
+   Written by Zheng Da.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd; see the file COPYING.  If not, write to
+   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+/*
+ * This file translates a string into a bpf program.
+ * The BPF structures are defined in both of bpf.h and pcap-bpf.h
+ * Hopefully, there is no conflict between them.
+ * This file uses the BPF definition in pcap-bpf.h.
+ */
+
+#include <pcap.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+
+#define NETF_IN 0x1
+#define NETF_OUT 0x2
+#define NETF_NBPA 10
+#define NETF_BPF (1 << NETF_NBPA)
+
+/* This function translates the bpf program
+ * from the string into the instructions. */
+struct bpf_insn *trans_filter_program (char *str, int send, int *filter_len)
+{
+  struct bpf_program program;
+  struct bpf_insn *insn;
+  pcap_t *pcap;
+  int err;
+
+  debug ("Compiling the bpf program: %s.\n", str);
+  pcap = pcap_open_dead (DLT_EN10MB, 1500);
+  if (pcap == NULL)
+    return NULL;
+  err = pcap_compile (pcap, &program, str, 1, 0);
+  if (err < 0) 
+    {
+      debug ("pcap_compile: %s\n", pcap_geterr (pcap));
+      pcap_close (pcap);
+      return NULL;
+    }
+
+  debug ("Finish compiling the bpf program, get %d bpf instructions.\n",
+        program.bf_len);
+  insn = (struct bpf_insn *) malloc ((program.bf_len + 1) * sizeof (*insn));
+  /* Clear the first instruction. */
+  memset (insn, 0, sizeof (*insn));
+  if (send)
+    insn->code = NETF_OUT | NETF_BPF;
+  else
+    insn->code = NETF_IN | NETF_BPF;
+  memcpy (insn + 1, program.bf_insns, program.bf_len * sizeof (*insn));
+  *filter_len = ((program.bf_len + 1) * sizeof (*insn)) / sizeof (short);
+  debug ("%d bpf instructions, the length of filters is %d words\n", 
+        program.bf_len, *filter_len);
+  pcap_freecode (&program);
+  pcap_close (pcap);
+
+  return insn;
+}
diff --git a/libbpf/queue.c b/eth-filter/queue.c
similarity index 100%
copy from libbpf/queue.c
copy to eth-filter/queue.c
diff --git a/libbpf/queue.h b/eth-filter/queue.h
similarity index 100%
copy from libbpf/queue.h
copy to eth-filter/queue.h
diff --git a/libbpf/util.h b/eth-filter/util.h
similarity index 73%
copy from libbpf/util.h
copy to eth-filter/util.h
index c90b0f8..de23710 100644
--- a/libbpf/util.h
+++ b/eth-filter/util.h
@@ -22,21 +22,18 @@
 #define UTIL_H
 
 #include <stdio.h>
-#include <execinfo.h>
 
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <arpa/inet.h>
 #include <netinet/ip.h>
 
-#include <mach.h>
-
-#ifdef DEBUG
+#ifdef DEBUG 
 
 #define debug(format, ...) do                          \
 {                                                      \
   char buf[1024];                                       \
-  snprintf (buf, 1024, "multiplexer: %s: %s\n", __func__, format);       \
+  snprintf (buf, 1024, "filter: %s", format);       \
   fprintf (stderr , buf, ## __VA_ARGS__);              \
   fflush (stderr);                                     \
 } while (0)
@@ -47,15 +44,6 @@
 
 #endif
 
-#define print_backtrace() do                           \
-{                                                      \
-  size_t size;                                         \
-  void *array[30];                                     \
-  size = backtrace (array, sizeof (array));            \
-  debug ("the depth of the stack: %d", size);          \
-  backtrace_symbols_fd(array, size, fileno (stderr));  \
-} while (0)
-
 #define ETH_ALEN 6             /* Octets in one ethernet addr   */
 
 struct ethhdr 
@@ -69,7 +57,6 @@ static inline void
 print_pack (char *packet, int len)
 {
 #ifdef DEBUG
-#define ETH_P_IP 0x0800
   struct ethhdr *ethh = (struct ethhdr *) packet;
   struct iphdr *iph = (struct iphdr *)(ethh + 1);
   char src_str[INET_ADDRSTRLEN];
@@ -77,13 +64,13 @@ print_pack (char *packet, int len)
   if (ntohs (ethh->h_proto) == ETH_P_IP
       && len >= sizeof (struct ethhdr) + sizeof (struct iphdr)) 
     {
-      debug ("multiplexer: get a IP packet from %s to %s\n",
-            inet_ntop (AF_INET, &iph->saddr, src_str, INET_ADDRSTRLEN),
-            inet_ntop (AF_INET, &iph->daddr, dst_str, INET_ADDRSTRLEN));
+      debug ("pack: get a IP packet from %s to %s\n",
+             inet_ntop (AF_INET, &iph->saddr, src_str, INET_ADDRSTRLEN),
+             inet_ntop (AF_INET, &iph->daddr, dst_str, INET_ADDRSTRLEN));
     }
   else 
     {
-      debug ("multiplexer: get a non-IP packet\n");
+      debug ("pack: get a non-IP packet: %x\n", ntohs (ethh->h_proto));
     }
 #endif
 }
diff --git a/eth-multiplexer/ChangeLog b/eth-multiplexer/ChangeLog
new file mode 100644
index 0000000..3737118
--- /dev/null
+++ b/eth-multiplexer/ChangeLog
@@ -0,0 +1,341 @@
+2009-04-18  Zheng Da  <address@hidden>
+
+       * device_impl.c (ds_device_open): Create a virtual device if it
+                                         doesn't exist.
+
+       * netfs_impl.c (new_node): Test if the lnode structure exists
+                                  before setting its field.
+       (lookup): Copy the device name and don't create the virtual device.
+       (netfs_validate_stat): Set the status with the one of the underlying
+                              node if the node has no lnode structure.
+       (netfs_attempt_chmod): chmod isn't supported if the node has no lnode
+                              structure.
+       (netfs_node_norefs): Free the name in netnode.
+
+       * netfs_impl.h (net_node): Add a new field 'name'.
+
+2009-04-18  Zheng Da  <address@hidden>
+
+       * device_impl.c (ds_device_open): Check the mode for opening the file.
+
+       * multiplexer.c
+       (underlying_node_stat): New variable.
+       (main): Get the mapped time from Mach and set the time of the underlying
+               node of the translator.
+
+       * netfs_impl.c (lookup): Set the new created node with the same 
permission
+                                as the underlying node of the translator and 
its time.
+       (netfs_check_open_permissions): Check the open permission of a node
+                                       in the same way.
+       (netfs_attempt_unlink): Change the return value.
+       (netfs_attempt_rename): Likewise.
+       (netfs_attempt_mkdir): Likewise.
+       (netfs_attempt_rmdir): Likewise.
+       (netfs_attempt_chown): Likewise.
+       (netfs_attempt_chauthor): Likewise.
+       (netfs_attempt_mksymlink): Likewise.
+       (netfs_attempt_mkdev): Likewise.
+       (netfs_set_translator): Likewise.
+       (netfs_attempt_chflags): Likewise.
+       (netfs_attempt_set_size): Likewise.
+       (netfs_attempt_link): Likewise.
+       (netfs_attempt_mkfile): Likewise.
+       (netfs_attempt_write): Likewise.
+       (netfs_attempt_chmod): Write the code to support the change of the mode.
+
+       * netfs_impl.h (multiplexer_maptime): Add the declaration.
+
+2009-01-03  Zheng Da  <address@hidden>
+
+       * device_impl.c (ds_device_write): Deallocate the out-of-line data.
+
+2008-12-12  Zheng Da  <address@hidden>
+
+       * multiplexer.c (main): Initialize the file status of the root node.
+       
+       * netfs_impl.c (netfs_validate_stat): Set the file status of the node
+                                             with the one in the light node.
+
+       * vdev.h (dev_act_func): Define a new type.
+       (foreach_dev_do): Declare the function.
+
+2008-11-18  Zheng Da  <address@hidden>
+
+       * netfs_impl.c (netfs_get_dirents): Use foreach_dev_do.
+
+       * vdev.c (dev_head, dev_num): Hide in the file.
+       (dev_list_lock): New variable.
+       (get_dev_num): New function.
+       (lookup_dev_by_name): Use lock.
+       (foreach_dev_do): New function.
+       (remove_dead_port_from_dev): Use lock.
+       (broadcast_pack, broadcast_msg): Use foreach_dev_do.
+
+       * vdev.h (dev_num): Remove declaration. 
+       (get_dev_num): Add declaration.
+
+2008-11-13  Zheng Da  <address@hidden>
+
+       * device_impl.c (ds_device_open): Use dev_port, dereference pi.
+
+       * util.h (print_backtrace): New macro.
+
+       * vdev.c (add_vdev): Set dev_port.
+
+       * vdev.h (vether_device): Add dev_port.
+
+2008-11-12  Zheng Da  <address@hidden>
+
+       * Makefile (SRCS): Updated.
+
+       * demuxer.c: New file.
+
+       * device_impl.c (ds_device_open): Use netfs_port_bucket.
+
+       * make-protid.c: Deleted.
+
+2008-11-02  Zheng Da  <address@hidden>
+
+       * Makefile (CFLAGS): Add a macro.
+       (SRCS): Add new C files.
+       (LCLHDRS): Add new H files.
+       (HURDLIBS): Change libraries.
+       
+       * demuxer.c: New file.
+
+       * device_impl.c: New file.
+
+       * make-protid.c: New file.
+
+       * netfs_impl.c: New file.
+
+       * netfs_impl.h: New file.
+
+       * notify_impl.c: New file.
+
+       * multiplexer.c: Remove the trivfs variables. Move the implementation of
+       notify interface. Move the implementation of device interface.
+       (multiplexer_thread): New functions.
+       (main): Run the libnetfs translator.
+
+       * util.h (debug): Update.
+
+       * vdev.c (lookup_dev_by_name): Use strncmp.
+       (add_vdev): Change its interface.
+
+2008-10-27  Zheng Da  <address@hidden>
+
+       * README: Update.
+
+       * bpf_impl.c (destroy_filters): New function.
+       
+       * multiplexer.c (nb_dev): Deleted.
+       (options): Remove the option '-v'.
+       (do_mach_notify_no_senders): Remove all port_info in the same way.
+       (ds_device_open): Create new devices if they don't exist, and decrease
+                         their reference counts.
+       (ds_device_set_filter): Fix a bug.
+       (trivfs_goaway): Use has_vdev() to test.
+       (parse_opt): Remove the code of handling '-v'.
+       (main): Remove the code of creating virtual devices.
+
+       * util.h (ETH_P_IP): New macro.
+       
+       * vdev.c (all_dev_close): Deleted.
+       (add_vdev): Link virtual device.
+       (destroy_vdev): New function.
+
+       * vdev.h (vether_device): Changed.
+       
+
+2008-10-03  Zheng Da  <address@hidden>
+
+       * Makefile (CFLAGS): Remove the include paths from pfinet.
+
+       * util.h: Remove the line of including linux/if_ether.h.
+       (ETH_ALEN): New macro.
+       (ethhdr): New structure.
+
+       * vdev.c (ETH_HLEN): New macro.
+
+       * vdev.h: Remove the line of including linux/etherdevice.h and include 
util.h
+
+2008-10-03  Zheng Da  <address@hidden>
+
+       * multiplexer.c (parse_opt): Don't create the virtual devices in case 
'v'.
+       (main): Create the virtual devices.
+
+       * README: Update.
+
+2008-10-03  Zheng Da  <address@hidden>
+
+       * multiplexer.c (ds_device_write): Don't call device_write when 
ether_port is NULL.
+       (ds_device_get_status): Call dev_getstat when ether_port is NULL.
+       (main): If device_file isn't specified, don't open the underlying 
device.
+
+2008-09-26  Zheng Da  <address@hidden>
+
+       * vdev.c (deliver_msg): Use non-block send.
+
+2008-09-21  Zheng Da  <address@hidden>
+
+       * README: Update.
+
+2008-09-02  Zheng Da  <address@hidden>
+
+       * ethernet.c (ether_filter): Use the original NPF filter.
+
+2008-9-01  Zheng Da  <address@hidden>
+
+       * multiplexer.c (ds_device_write): Reverse the calling of functions.
+       (ds_device_get_status): Call device_get_status.
+
+       * vdev.c (broadcast_pack): Change its function prototype. Broadcast to
+       all other interface.
+       (deliver_pack): Don't set the message header.
+       (broadcast_msg): Save the original message header and restore it.
+       (deliver_msg): Deallocate the port if mach_msg fails.
+
+       * vdev.h (broadcast_pack): Change its function prototype.
+
+2008-8-29  Zheng Da <address@hidden>
+
+       * ethernet.c (ethernet_open): Use error instead of assert_perror.
+
+       * multiplexer.c (ds_device_set_filter): Return the error.
+
+2008-8-28 Zheng Da <address@hidden>
+
+       * ethernet.c (NET_FLAGS): New macro.
+
+2008-8-22 Zheng Da <address@hidden>
+
+       * README: Update.
+
+       * Makefile: Remove list.h.
+
+       * multiplexer.c (do_mach_notify_no_senders): Get vether_device object
+       with ports_lookup_port().
+       (ds_xxx_device_set_status): Likewise.
+       (ds_xxx_device_get_status): Likewise.
+       (ds_xxx_device_set_filter): Likewise.
+       (ds_device_write): Likewise.
+       (ds_device_write_inband): Likewise.
+       (ds_device_read): Likewise.
+       (ds_device_read_inband): Likewise.
+       (ds_device_map): Likewise.
+       (ds_device_set_status): Likewise.
+       (ds_device_get_status): Likewise.
+       (ds_device_set_filter): Likewise.
+       (do_mach_notify_dead_name): Deallocate the port.
+       (ds_device_open): Get the name directly from the vether_device object.
+       (ds_device_close): Return 0 immediately.
+
+       * vdev.c (dev_head): Point to the head of the device list.
+       (print_eth_addr): Removed.
+       (lookup_dev_by_devport): Likewise.
+       (lookup_dev_by_name): Use the for loop to replace list_for_each_entry.
+       (remove_dead_port_from_dev): Likewise.
+       (all_dev_close): Likewise.
+       (broadcast_pack): Likewise.
+       (broadcast_msg): Likewise.
+       (add_vdev): Create the vether_device object with ports_create_port.
+       (has_vdev): Test if the device list is empty.
+
+       * vdev.h: Don't include list.h.
+       (vether_device): Include the port_info object instead of its pointer.
+       (next): Replace dev_list.
+
+       * list.h: Removed.
+
+2008-8-20 Zheng Da <address@hidden>
+
+       * README: Update.
+
+       * multiplexer.c (options): Update.
+
+2008-8-20 Zheng Da <address@hidden>
+
+       * multiplexer.c (main): Test device_file before using it.
+
+2008-8-20 Zheng Da <address@hidden>
+
+       * multiplexer.c (device_file): Rename a variable.
+       (main): Use device_file directly.
+
+2008-8-19 Zheng Da <address@hidden>
+
+       * multiplexer.c (main): Generate the device file name, and use it
+       to open the device.
+
+2008-8-18 Zheng Da <address@hidden>
+
+       * README: New file.
+
+       * multiplexer.c (options): Change the meaning of '-v' option.
+       (parse_opt): Change the way of handling '-v' option.
+       
+       * vdev.c (has_vdev): New function.
+
+       * vdev.h (has_vdev): New declaration. 
+
+2008-8-17 Zheng Da <address@hidden>
+
+       * ethernet.c (ethernet_open): Use a hard coded string for the device 
name.
+
+       * multiplexer.c: Remove the option '-M'.
+       (parse_opt): Remove the code of handling '-M' option.
+       (main): Get the master device port from the device file.
+
+2008-8-14 Zheng Da <address@hidden>
+
+       * ChangeLog: New file.
+
+       * multiplexer.c (vdev_portclass, other_portclass):
+       New variables.
+       (do_mach_notify_no_senders): Mark the device unused when there are no
+       senders for the device.
+       (do_mach_notify_dead_name): Return 0.
+       (ds_device_open): Mark the device used.
+       (ds_device_close): Remove the code of decreasing the count of the 
device.
+       (trivfs_goaway): Only test if all devices aren't used, and delete the 
code
+       of closing the device.
+       (parse_opt): Use vdev_portclass to create the virtual device.
+       (main): Create vdev_portclass and other_portclass, open the ethernet
+       device with other_portclass and create notify_pi with other_portclass.
+
+       * vdev.c (all_dev_close): Change the way of testing if all devices are
+       closed.
+       
+       * vdev.h (vether_device): Replace count field with used.
+
+2008-8-13 Zheng Da <address@hidden>
+
+       * bpf_impl.c: New file.
+
+       * bpf_impl.h: New file.
+
+       * dev_stat.c: New file.
+
+       * ethernet.c: New file.
+
+       * ethernet.h: New file.
+
+       * list.h: New file.
+
+       * Makefile: New file.
+
+       * multiplexer.c: New file.
+
+       * queue.c: New file.
+
+       * queue.h: New file.
+
+       * test.c: New file.
+
+       * util.h: New file.
+
+       * vdev.c: New file.
+
+       * vdev.h: New file.
+
diff --git a/devnode/Makefile b/eth-multiplexer/Makefile
similarity index 77%
copy from devnode/Makefile
copy to eth-multiplexer/Makefile
index f452256..a96619c 100644
--- a/devnode/Makefile
+++ b/eth-multiplexer/Makefile
@@ -15,18 +15,23 @@
 # along with the GNU Hurd; see the file COPYING.  If not, write to
 # the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
-dir := devnode
+dir := eth-multiplexer
 makemode := server
+target = eth-multiplexer
 
-SRCS = devnode.c
-LCLHDRS = util.h
-DIST_FILES = ourdevice.defs notify.defs
-HURDLIBS = ports trivfs fshelp shouldbeinlibc
-target = devnode
+#CFLAGS += -DDEBUG
+SRCS = ethernet.c vdev.c multiplexer.c dev_stat.c netfs_impl.c notify_impl.c 
device_impl.c demuxer.c
 MIGSTUBS = ourdeviceServer.o notifyServer.o
 OBJS = $(SRCS:.c=.o) $(MIGSTUBS)
+LCLHDRS = ethernet.h util.h vdev.h netfs_impl.h
+DIST_FILES = ourdevice.defs notify.defs
+HURDLIBS=ports fshelp shouldbeinlibc netfs bpf
+OTHERLIBS = -lpthread
+
+CFLAGS += -I$(top_srcdir)/libbpf
 
 include ../Makeconf
 
 ourdevice.defs: device.defs
        $(CPP) $(CPPFLAGS) -x c $< | sed -e '/out[      ]*device[       ]*:[    
]*device_t/s/device_t/mach_port_send_t/' > $@
+
diff --git a/eth-multiplexer/README b/eth-multiplexer/README
new file mode 100644
index 0000000..0024a93
--- /dev/null
+++ b/eth-multiplexer/README
@@ -0,0 +1,27 @@
+[Introduction]
+
+eth-multiplexer is a network multiplexer. It creates virtual ethernet 
interface and dispatches the packet to the right user program that opens its 
virtual interface. It also works as a bridge to connect the real ethernet 
interface and the virtual ones. 
+
+
+[Usage]
+
+Usage: eth-multiplexer [OPTION...]
+Hurd multiplexer server.
+
+  -i, --interface=DEVICE     Network interface to use
+  -?, --help                 Give this help list
+      --usage                Give a short usage message
+  -V, --version              Print program version
+
+Mandatory or optional arguments to long options are also mandatory or optional
+for any corresponding short options.
+
+
+The '-i' option specifies the network interface the translator sits on. 
eth-multiplexer can only connect to one network interface and the '-i' option 
should be only used once. DEVICE is a device file that is created by the 
devnode translator.
+
+
+[Internal]
+
+eth-multiplexer implements the server side functions in device.defs, so other 
programs can access the virtual device as other devices. All information about 
the virtual interface is kept in the vether_device structure.
+When eth-multiplexer gets a packet from a virtual interface (which happens in 
ds_device_write) or from the real interface (which happens in 
ethernet_demuxer), it sends the packet to all other interfaces. eth-multipexer 
has BPF filters for each client. The BPF filter decides whether to deliver the 
packet. The packet delivery is done by deliver_pack(). There is no filter for 
the real network interface in eth-multiplexer, so every packet from the virtual 
interface will be sent to the real interface whose filter will decide the 
destination of the packet.
+eth-multiplexer sets the real interface into the promiscuous mode, so 
eth-multiplexer can receive the packet with the virtual interface's hardware 
address from the real interface.
diff --git a/libnetfs/demuxer.c b/eth-multiplexer/demuxer.c
similarity index 89%
copy from libnetfs/demuxer.c
copy to eth-multiplexer/demuxer.c
index 6b4f727..1f671b2 100644
--- a/libnetfs/demuxer.c
+++ b/eth-multiplexer/demuxer.c
@@ -18,7 +18,7 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA. */
 
-#include "netfs.h"
+#include <hurd/netfs.h>
 
 int
 netfs_demuxer (mach_msg_header_t *inp,
@@ -28,12 +28,14 @@ netfs_demuxer (mach_msg_header_t *inp,
   int netfs_io_server (mach_msg_header_t *, mach_msg_header_t *);
   int netfs_fsys_server (mach_msg_header_t *, mach_msg_header_t *);
   int netfs_ifsock_server (mach_msg_header_t *, mach_msg_header_t *);
+  int device_server (mach_msg_header_t *, mach_msg_header_t *);
 
   return (netfs_io_server (inp, outp)
           || netfs_fs_server (inp, outp)
           || ports_notify_server (inp, outp)
           || netfs_fsys_server (inp, outp)
           || ports_interrupt_server (inp, outp)
-          || netfs_ifsock_server (inp, outp));
+          || netfs_ifsock_server (inp, outp)
+         || device_server (inp, outp));
 }
 
diff --git a/eth-multiplexer/dev_stat.c b/eth-multiplexer/dev_stat.c
new file mode 100644
index 0000000..43c68d6
--- /dev/null
+++ b/eth-multiplexer/dev_stat.c
@@ -0,0 +1,101 @@
+ /* 
+  * Mach Operating System
+  * Copyright (c) 1993-1989 Carnegie Mellon University
+  * All Rights Reserved.
+  * 
+  * Permission to use, copy, modify and distribute this software and its
+  * documentation is hereby granted, provided that both the copyright
+  * notice and this permission notice appear in all copies of the
+  * software, derivative works or modified versions, and any portions
+  * thereof, and that both notices appear in supporting documentation.
+  * 
+  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+  * 
+  * Carnegie Mellon requests users of this software to return to
+  * 
+  *  Software Distribution Coordinator  or  address@hidden
+  *  School of Computer Science
+  *  Carnegie Mellon University
+  *  Pittsburgh PA 15213-3890
+  * 
+  * any improvements or extensions that they make and grant Carnegie Mellon
+  * the rights to redistribute these changes.
+  */
+/*
+ *     Author: David B. Golub, Carnegie Mellon University
+ *     Date:   3/98
+ *
+ *     Network IO.
+ *
+ *     Packet filter code taken from vaxif/enet.c written               
+ *             CMU and Stanford. 
+ */
+
+/* the code copied from device/net_io.c in Mach */
+
+#include <string.h>
+#include <arpa/inet.h>
+
+#include <mach.h>
+
+#include "vdev.h"
+
+io_return_t
+dev_getstat(struct vether_device *ifp, dev_flavor_t flavor,
+               dev_status_t status, natural_t *count)
+{
+       switch (flavor) {
+               case NET_STATUS:
+                       {
+                               register struct net_status *ns = (struct 
net_status *)status;
+
+                               if (*count < NET_STATUS_COUNT)
+                                       return (D_INVALID_OPERATION);
+
+                               ns->min_packet_size = ifp->if_header_size;
+                               ns->max_packet_size = ifp->if_header_size + 
ifp->if_mtu;
+                               ns->header_format = ifp->if_header_format;
+                               ns->header_size = ifp->if_header_size;
+                               ns->address_size = ifp->if_address_size;
+                               ns->flags = ifp->if_flags;
+                               ns->mapped_size = 0;
+
+                               *count = NET_STATUS_COUNT;
+                               break;
+                       }
+               case NET_ADDRESS:
+                       {
+                               register int    addr_byte_count;
+                               register int    addr_int_count;
+                               register int    i;
+
+                               addr_byte_count = ifp->if_address_size;
+                               addr_int_count = (addr_byte_count + 
(sizeof(int)-1))
+                                       / sizeof(int);
+
+                               if (*count < addr_int_count) {
+                                       return (D_INVALID_OPERATION);
+                               }
+
+                               memcpy(status, ifp->if_address, 
addr_byte_count);
+                               if (addr_byte_count < addr_int_count * 
sizeof(int))
+                                       memset((char *)status + 
addr_byte_count, 0, 
+                                                       (addr_int_count * 
sizeof(int)
+                                                        - addr_byte_count));
+
+                               for (i = 0; i < addr_int_count; i++) {
+                                       register int word;
+
+                                       word = status[i];
+                                       status[i] = htonl(word);
+                               }
+                               *count = addr_int_count;
+                               break;
+                       }
+               default:
+                       return (D_INVALID_OPERATION);
+       }
+       return (D_SUCCESS);
+}
diff --git a/eth-multiplexer/device.h b/eth-multiplexer/device.h
new file mode 100644
index 0000000..db0798d
--- /dev/null
+++ b/eth-multiplexer/device.h
@@ -0,0 +1,336 @@
+#ifndef        _device_user_
+#define        _device_user_
+
+/* Module device */
+
+#include <mach/kern_return.h>
+#include <mach/port.h>
+#include <mach/message.h>
+
+#include <mach/std_types.h>
+#include <mach/mach_types.h>
+#include <device/device_types.h>
+#include <device/net_status.h>
+
+/* Routine device_open */
+#ifdef mig_external
+mig_external
+#else
+extern
+#endif
+kern_return_t device_open
+#if    defined(LINTLIBRARY)
+    (master_port, mode, name, device)
+       mach_port_t master_port;
+       dev_mode_t mode;
+       dev_name_t name;
+       mach_port_t *device;
+{ return device_open(master_port, mode, name, device); }
+#else
+(
+       mach_port_t master_port,
+       dev_mode_t mode,
+       dev_name_t name,
+       mach_port_t *device
+);
+#endif
+
+/* Routine device_close */
+#ifdef mig_external
+mig_external
+#else
+extern
+#endif
+kern_return_t device_close
+#if    defined(LINTLIBRARY)
+    (device)
+       mach_port_t device;
+{ return device_close(device); }
+#else
+(
+       mach_port_t device
+);
+#endif
+
+/* Routine device_write */
+#ifdef mig_external
+mig_external
+#else
+extern
+#endif
+kern_return_t device_write
+#if    defined(LINTLIBRARY)
+    (device, mode, recnum, data, dataCnt, bytes_written)
+       mach_port_t device;
+       dev_mode_t mode;
+       recnum_t recnum;
+       io_buf_ptr_t data;
+       mach_msg_type_number_t dataCnt;
+       int *bytes_written;
+{ return device_write(device, mode, recnum, data, dataCnt, bytes_written); }
+#else
+(
+       mach_port_t device,
+       dev_mode_t mode,
+       recnum_t recnum,
+       io_buf_ptr_t data,
+       mach_msg_type_number_t dataCnt,
+       int *bytes_written
+);
+#endif
+
+/* Routine device_write_inband */
+#ifdef mig_external
+mig_external
+#else
+extern
+#endif
+kern_return_t device_write_inband
+#if    defined(LINTLIBRARY)
+    (device, mode, recnum, data, dataCnt, bytes_written)
+       mach_port_t device;
+       dev_mode_t mode;
+       recnum_t recnum;
+       io_buf_ptr_inband_t data;
+       mach_msg_type_number_t dataCnt;
+       int *bytes_written;
+{ return device_write_inband(device, mode, recnum, data, dataCnt, 
bytes_written); }
+#else
+(
+       mach_port_t device,
+       dev_mode_t mode,
+       recnum_t recnum,
+       io_buf_ptr_inband_t data,
+       mach_msg_type_number_t dataCnt,
+       int *bytes_written
+);
+#endif
+
+/* Routine device_read */
+#ifdef mig_external
+mig_external
+#else
+extern
+#endif
+kern_return_t device_read
+#if    defined(LINTLIBRARY)
+    (device, mode, recnum, bytes_wanted, data, dataCnt)
+       mach_port_t device;
+       dev_mode_t mode;
+       recnum_t recnum;
+       int bytes_wanted;
+       io_buf_ptr_t *data;
+       mach_msg_type_number_t *dataCnt;
+{ return device_read(device, mode, recnum, bytes_wanted, data, dataCnt); }
+#else
+(
+       mach_port_t device,
+       dev_mode_t mode,
+       recnum_t recnum,
+       int bytes_wanted,
+       io_buf_ptr_t *data,
+       mach_msg_type_number_t *dataCnt
+);
+#endif
+
+/* Routine device_read_inband */
+#ifdef mig_external
+mig_external
+#else
+extern
+#endif
+kern_return_t device_read_inband
+#if    defined(LINTLIBRARY)
+    (device, mode, recnum, bytes_wanted, data, dataCnt)
+       mach_port_t device;
+       dev_mode_t mode;
+       recnum_t recnum;
+       int bytes_wanted;
+       io_buf_ptr_inband_t data;
+       mach_msg_type_number_t *dataCnt;
+{ return device_read_inband(device, mode, recnum, bytes_wanted, data, 
dataCnt); }
+#else
+(
+       mach_port_t device,
+       dev_mode_t mode,
+       recnum_t recnum,
+       int bytes_wanted,
+       io_buf_ptr_inband_t data,
+       mach_msg_type_number_t *dataCnt
+);
+#endif
+
+/* Routine xxx_device_set_status */
+#ifdef mig_external
+mig_external
+#else
+extern
+#endif
+kern_return_t xxx_device_set_status
+#if    defined(LINTLIBRARY)
+    (device, flavor, status, statusCnt)
+       mach_port_t device;
+       dev_flavor_t flavor;
+       dev_status_t status;
+       mach_msg_type_number_t statusCnt;
+{ return xxx_device_set_status(device, flavor, status, statusCnt); }
+#else
+(
+       mach_port_t device,
+       dev_flavor_t flavor,
+       dev_status_t status,
+       mach_msg_type_number_t statusCnt
+);
+#endif
+
+/* Routine xxx_device_get_status */
+#ifdef mig_external
+mig_external
+#else
+extern
+#endif
+kern_return_t xxx_device_get_status
+#if    defined(LINTLIBRARY)
+    (device, flavor, status, statusCnt)
+       mach_port_t device;
+       dev_flavor_t flavor;
+       dev_status_t status;
+       mach_msg_type_number_t *statusCnt;
+{ return xxx_device_get_status(device, flavor, status, statusCnt); }
+#else
+(
+       mach_port_t device,
+       dev_flavor_t flavor,
+       dev_status_t status,
+       mach_msg_type_number_t *statusCnt
+);
+#endif
+
+/* Routine xxx_device_set_filter */
+#ifdef mig_external
+mig_external
+#else
+extern
+#endif
+kern_return_t xxx_device_set_filter
+#if    defined(LINTLIBRARY)
+    (device, receive_port, receive_portPoly, priority, filter, filterCnt)
+       mach_port_t device;
+       mach_port_t receive_port;
+       mach_msg_type_name_t receive_portPoly;
+       int priority;
+       filter_array_t filter;
+       mach_msg_type_number_t filterCnt;
+{ return xxx_device_set_filter(device, receive_port, receive_portPoly, 
priority, filter, filterCnt); }
+#else
+(
+       mach_port_t device,
+       mach_port_t receive_port,
+       mach_msg_type_name_t receive_portPoly,
+       int priority,
+       filter_array_t filter,
+       mach_msg_type_number_t filterCnt
+);
+#endif
+
+/* Routine device_map */
+#ifdef mig_external
+mig_external
+#else
+extern
+#endif
+kern_return_t device_map
+#if    defined(LINTLIBRARY)
+    (device, prot, offset, size, pager, unmap)
+       mach_port_t device;
+       vm_prot_t prot;
+       vm_offset_t offset;
+       vm_size_t size;
+       mach_port_t *pager;
+       int unmap;
+{ return device_map(device, prot, offset, size, pager, unmap); }
+#else
+(
+       mach_port_t device,
+       vm_prot_t prot,
+       vm_offset_t offset,
+       vm_size_t size,
+       mach_port_t *pager,
+       int unmap
+);
+#endif
+
+/* Routine device_set_status */
+#ifdef mig_external
+mig_external
+#else
+extern
+#endif
+kern_return_t device_set_status
+#if    defined(LINTLIBRARY)
+    (device, flavor, status, statusCnt)
+       mach_port_t device;
+       dev_flavor_t flavor;
+       dev_status_t status;
+       mach_msg_type_number_t statusCnt;
+{ return device_set_status(device, flavor, status, statusCnt); }
+#else
+(
+       mach_port_t device,
+       dev_flavor_t flavor,
+       dev_status_t status,
+       mach_msg_type_number_t statusCnt
+);
+#endif
+
+/* Routine device_get_status */
+#ifdef mig_external
+mig_external
+#else
+extern
+#endif
+kern_return_t device_get_status
+#if    defined(LINTLIBRARY)
+    (device, flavor, status, statusCnt)
+       mach_port_t device;
+       dev_flavor_t flavor;
+       dev_status_t status;
+       mach_msg_type_number_t *statusCnt;
+{ return device_get_status(device, flavor, status, statusCnt); }
+#else
+(
+       mach_port_t device,
+       dev_flavor_t flavor,
+       dev_status_t status,
+       mach_msg_type_number_t *statusCnt
+);
+#endif
+
+/* Routine device_set_filter */
+#ifdef mig_external
+mig_external
+#else
+extern
+#endif
+kern_return_t device_set_filter
+#if    defined(LINTLIBRARY)
+    (device, receive_port, receive_portPoly, priority, filter, filterCnt)
+       mach_port_t device;
+       mach_port_t receive_port;
+       mach_msg_type_name_t receive_portPoly;
+       int priority;
+       filter_array_t filter;
+       mach_msg_type_number_t filterCnt;
+{ return device_set_filter(device, receive_port, receive_portPoly, priority, 
filter, filterCnt); }
+#else
+(
+       mach_port_t device,
+       mach_port_t receive_port,
+       mach_msg_type_name_t receive_portPoly,
+       int priority,
+       filter_array_t filter,
+       mach_msg_type_number_t filterCnt
+);
+#endif
+
+#endif /* not defined(_device_user_) */
diff --git a/eth-multiplexer/device_impl.c b/eth-multiplexer/device_impl.c
new file mode 100644
index 0000000..953fca7
--- /dev/null
+++ b/eth-multiplexer/device_impl.c
@@ -0,0 +1,284 @@
+/* 
+   Copyright (C) 2008 Free Software Foundation, Inc.
+   Written by Zheng Da.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd; see the file COPYING.  If not, write to
+   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include <fcntl.h>
+#include <mach.h>
+#include <hurd.h>
+#include <hurd/ports.h>
+#include <hurd/netfs.h>
+#include <device/device.h>
+
+#include "ethernet.h"
+#include "vdev.h"
+#include "ourdevice_S.h"
+#include "notify_S.h"
+#include "bpf_impl.h"
+#include "netfs_impl.h"
+#include "util.h"
+
+extern struct port_bucket *port_bucket;
+extern struct port_class *vdev_portclass;
+extern struct port_class *other_portclass;
+extern struct port_info *notify_pi;
+
+/* Implementation of device interface */
+kern_return_t
+ds_xxx_device_set_status (device_t device, dev_flavor_t flavor,
+                         dev_status_t status, size_t statu_cnt)
+{
+  struct vether_device *vdev = ports_lookup_port (port_bucket, device,
+                                                 vdev_portclass);
+  if (vdev == NULL)
+    return D_NO_SUCH_DEVICE;
+  ports_port_deref (vdev);
+  return D_INVALID_OPERATION;
+}
+
+kern_return_t
+ds_xxx_device_get_status (device_t device, dev_flavor_t flavor,
+                         dev_status_t status, size_t *statuscnt)
+{
+  struct vether_device *vdev = ports_lookup_port (port_bucket, device,
+                                                 vdev_portclass);
+  if (vdev == NULL)
+    return D_NO_SUCH_DEVICE;
+  ports_port_deref (vdev);
+  return D_INVALID_OPERATION;
+}
+
+kern_return_t
+ds_xxx_device_set_filter (device_t device, mach_port_t rec,
+                         int pri, filter_array_t filt, size_t len)
+{
+  struct vether_device *vdev = ports_lookup_port (port_bucket, device,
+                                                 vdev_portclass);
+  if (vdev == NULL)
+    return D_NO_SUCH_DEVICE;
+  ports_port_deref (vdev);
+  return D_INVALID_OPERATION;
+}
+
+/*
+ * This function is currently running in the multithread environment,
+ * it should be protected by locks.
+ */
+kern_return_t
+ds_device_open (mach_port_t master_port, mach_port_t reply_port,
+               mach_msg_type_name_t reply_portPoly,
+               dev_mode_t mode, dev_name_t name, mach_port_t *device, 
+               mach_msg_type_name_t *devicetype)
+{
+  struct vether_device *dev;
+  int openstat; 
+  int right_mode = 1;
+  struct protid *pi = ports_lookup_port (netfs_port_bucket, master_port, 0);
+  if (pi == NULL)
+    return D_NO_SUCH_DEVICE;
+
+  /* If the virtual device hasn't been created yet,
+   * create it now. */
+  if (pi->po->np->nn->ln == NULL)
+    {
+      extern struct port_bucket *port_bucket;
+      extern struct port_class *vdev_portclass;
+      extern struct stat underlying_node_stat;
+      static int ino_count = 0;
+      /* Create a new light node (virtual device). */
+      struct lnode *ln = (struct lnode *) add_vdev (pi->po->np->nn->name,
+                                                   sizeof (*ln),
+                                                   vdev_portclass,
+                                                   port_bucket);
+      if (ln == NULL)
+       {
+         ports_port_deref (pi);
+         return D_NO_MEMORY;
+       }
+      memset (&ln->st, 0, sizeof (ln->st));
+      ln->st.st_ino = ++ino_count;
+      ln->st.st_mode = S_IFCHR | (underlying_node_stat.st_mode & ~S_IFMT);
+      ln->st.st_ctime = ln->st.st_mtime = ln->st.st_atime = time (NULL);
+      fshelp_touch (&ln->st, TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME,
+                   multiplexer_maptime);
+      pi->po->np->nn->ln = ln;
+    }
+
+  dev = (struct vether_device *) pi->po->np->nn->ln;
+  /* check the mode */
+  openstat = pi->po->openstat;
+  if (mode & D_READ && !(openstat & O_READ))
+    right_mode = 0;
+  if (mode & D_WRITE && !(openstat & O_WRITE))
+    right_mode = 0;
+  ports_port_deref (pi);
+
+  if (dev)
+    {
+      if (!right_mode)
+       return EBADF;
+      *device = dev->dev_port;
+      *devicetype = MACH_MSG_TYPE_MAKE_SEND;
+      return 0;
+    }
+  return D_NO_SUCH_DEVICE;
+}
+
+kern_return_t
+ds_device_close (device_t device)
+{
+  return 0;
+}
+
+kern_return_t
+ds_device_write (device_t device, mach_port_t reply_port,
+                mach_msg_type_name_t reply_type, dev_mode_t mode,
+                recnum_t recnum, io_buf_ptr_t data, size_t datalen,
+                int *bytes_written)
+{
+  kern_return_t ret = 0;
+  struct vether_device *vdev = ports_lookup_port (port_bucket, device,
+                                                 vdev_portclass);
+  if (vdev == NULL)
+    {
+      vm_deallocate (mach_task_self (), data, datalen);
+      return D_NO_SUCH_DEVICE;
+    }
+  /* The packet is forwarded to all virtual interfaces and
+   * the interface which the multiplexer connects to. */
+  broadcast_pack (data, datalen, vdev);
+  *bytes_written = datalen;
+  if(ether_port != MACH_PORT_NULL)
+    ret = device_write (ether_port, mode , recnum ,
+                       data, datalen, bytes_written);
+  /* The data in device_write() is transmifered out of line,
+   * so the server-side function has to deallocate it. */
+  vm_deallocate (mach_task_self (), data, datalen);
+  ports_port_deref (vdev);
+  return ret;
+}
+
+kern_return_t
+ds_device_write_inband (device_t device, mach_port_t reply_port,
+                       mach_msg_type_name_t reply_type, dev_mode_t mode,
+                       recnum_t recnum, io_buf_ptr_inband_t data,
+                       size_t datalen, int *bytes_written)
+{
+  struct vether_device *vdev = ports_lookup_port (port_bucket, device,
+                                                 vdev_portclass);
+  if (vdev == NULL)
+    return D_NO_SUCH_DEVICE;
+  ports_port_deref (vdev);
+  return D_INVALID_OPERATION;
+}
+
+kern_return_t
+ds_device_read (device_t device, mach_port_t reply_port,
+               mach_msg_type_name_t reply_type, dev_mode_t mode,
+               recnum_t recnum, int bytes_wanted,
+               io_buf_ptr_t *data, size_t *datalen)
+{
+  struct vether_device *vdev = ports_lookup_port (port_bucket, device,
+                                                 vdev_portclass);
+  if (vdev == NULL)
+    return D_NO_SUCH_DEVICE;
+  ports_port_deref (vdev);
+  return D_INVALID_OPERATION;
+}
+
+kern_return_t
+ds_device_read_inband (device_t device, mach_port_t reply_port,
+                      mach_msg_type_name_t reply_type, dev_mode_t mode,
+                      recnum_t recnum, int bytes_wanted,
+                      io_buf_ptr_inband_t data, size_t *datalen)
+{
+  struct vether_device *vdev = ports_lookup_port (port_bucket, device,
+                                                 vdev_portclass);
+  if (vdev == NULL)
+    return D_NO_SUCH_DEVICE;
+  ports_port_deref (vdev);
+  return D_INVALID_OPERATION;
+}
+
+kern_return_t
+ds_device_map (device_t device, vm_prot_t prot, vm_offset_t offset,
+              vm_size_t size, memory_object_t *pager, int unmap)
+{
+  struct vether_device *vdev = ports_lookup_port (port_bucket, device,
+                                                 vdev_portclass);
+  if (vdev == NULL)
+    return D_NO_SUCH_DEVICE;
+  ports_port_deref (vdev);
+  return D_INVALID_OPERATION;
+}
+
+kern_return_t
+ds_device_set_status (device_t device, dev_flavor_t flavor,
+                     dev_status_t status, size_t statuslen)
+{
+  struct vether_device *vdev = ports_lookup_port (port_bucket, device,
+                                                 vdev_portclass);
+  if (vdev == NULL)
+    return D_NO_SUCH_DEVICE;
+  ports_port_deref (vdev);
+  return D_INVALID_OPERATION;
+}
+
+kern_return_t
+ds_device_get_status (device_t device, dev_flavor_t flavor,
+                     dev_status_t status, size_t *statuslen)
+{
+  extern io_return_t dev_getstat (struct vether_device *, dev_flavor_t,
+                                 dev_status_t, natural_t *);
+  kern_return_t ret = 0;
+  struct vether_device *vdev = ports_lookup_port (port_bucket, device,
+                                                 vdev_portclass);
+  if (vdev == NULL)
+    return D_NO_SUCH_DEVICE;
+  if(ether_port != MACH_PORT_NULL)
+    ret = device_get_status (ether_port, flavor, status, statuslen);
+  else 
+    ret = dev_getstat (vdev, flavor, status, statuslen);
+  ports_port_deref (vdev);
+  return ret;
+}
+
+kern_return_t
+ds_device_set_filter (device_t device, mach_port_t receive_port,
+                     int priority, filter_array_t filter, size_t filterlen)
+{
+  mach_port_t tmp;
+  kern_return_t err;
+  struct vether_device *vdev = ports_lookup_port (port_bucket, device,
+                                                 vdev_portclass);
+  if (vdev == NULL)
+    return D_NO_SUCH_DEVICE;
+  err = mach_port_request_notification (mach_task_self (), receive_port, 
+                                       MACH_NOTIFY_DEAD_NAME, 0,
+                                       ports_get_right (notify_pi), 
+                                       MACH_MSG_TYPE_MAKE_SEND_ONCE, &tmp);
+  if (err != KERN_SUCCESS)
+    goto out;
+  if (tmp != MACH_PORT_NULL)
+    mach_port_deallocate (mach_task_self (), tmp);
+  err = net_set_filter (&vdev->port_list, receive_port,
+                       priority, filter, filterlen);
+out:
+  ports_port_deref (vdev);
+  return err;
+}
diff --git a/eth-multiplexer/ethernet.c b/eth-multiplexer/ethernet.c
new file mode 100644
index 0000000..32c5589
--- /dev/null
+++ b/eth-multiplexer/ethernet.c
@@ -0,0 +1,144 @@
+/*
+   Copyright (C) 1995, 1996, 1998, 1999, 2000, 2002, 2007, 2008
+   Free Software Foundation, Inc.
+
+   Written by Zheng Da 
+
+   Based on pfinet/ethernet.c, written by Michael I. Bushnell, p/BSG.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA. */
+
+#include <string.h>
+#include <error.h>
+#include <assert.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+
+#include <hurd/ports.h>
+#include <device/device.h>
+#include <device/net_status.h>
+
+#include "ethernet.h"
+#include "vdev.h"
+#include "util.h"
+
+#define ETH_HLEN 14
+
+static struct port_info *readpt;
+
+/* Port for writing message to the real network interface. */
+mach_port_t ether_port;
+/* Port for receiving messages from the interface. */
+static mach_port_t readptname;
+
+/* The BPF instruction allows IP and ARP packets */
+static struct bpf_insn ether_filter[] =
+{
+    {NETF_IN|NETF_BPF, /* Header. */ 0, 0, 0},
+    {40, 0, 0, 12},
+    {21, 1, 0, 2054},
+    {21, 0, 1, 2048},
+    {6, 0, 0, 1500},
+    {6, 0, 0, 0}
+};
+static int ether_filter_len = sizeof (ether_filter) / sizeof (short);
+
+int ethernet_demuxer (mach_msg_header_t *inp,
+                     mach_msg_header_t *outp)
+{
+  struct net_rcv_msg *msg = (struct net_rcv_msg *) inp;
+
+  if (inp->msgh_id != NET_RCV_MSG_ID)
+    return 0;
+
+  broadcast_msg (msg);
+  /* The data from the underlying network is inside the message,
+   * so we don't need to deallocate the data. */
+  return 1;
+}
+
+int set_promisc (char *dev_name, mach_port_t ether_port, int is_promisc)
+{
+#ifndef NET_FLAGS
+#define NET_FLAGS (('n'<<16) + 4)
+#endif
+  short flags;
+  int ret;
+  size_t count;
+
+  debug ("set_promisc is called, is_promisc: %d\n", is_promisc);
+  count = sizeof (flags);
+  ret = device_get_status (ether_port, NET_FLAGS, (dev_status_t) &flags, 
+                           &count);
+  if (ret) 
+    {
+      error (0, ret, "device_get_status");  
+      return -1;
+    }  
+  if (is_promisc)
+    flags |= IFF_PROMISC;
+  else
+    flags &= ~IFF_PROMISC;
+  ret = device_set_status(ether_port, NET_FLAGS, (dev_status_t) &flags,
+                          sizeof (flags));
+  if (ret) 
+    {
+      error (0, ret, "device_set_status");
+      return -1;
+    }  
+  return 0;  
+} 
+
+int ethernet_open (char *dev_name, device_t master_device, 
+                  struct port_bucket *etherport_bucket,
+                  struct port_class *etherreadclass)
+{
+  error_t err;
+
+  assert (ether_port == MACH_PORT_NULL);
+
+  err = ports_create_port (etherreadclass, etherport_bucket,
+                          sizeof (struct port_info), &readpt);
+  if (err)
+    error (2, err, "ports_create_port");
+  readptname = ports_get_right (readpt);
+  mach_port_insert_right (mach_task_self (), readptname, readptname,
+                         MACH_MSG_TYPE_MAKE_SEND);
+
+  mach_port_set_qlimit (mach_task_self (), readptname, MACH_PORT_QLIMIT_MAX);
+
+  err = device_open (master_device, D_WRITE | D_READ, "eth", &ether_port);
+  mach_port_deallocate (mach_task_self (), master_device);
+  if (err)
+    error (2, err, "device_open: %s", dev_name);
+
+  err = device_set_filter (ether_port, ports_get_right (readpt),
+                          MACH_MSG_TYPE_MAKE_SEND, 0,
+                          (unsigned short *)ether_filter, ether_filter_len);
+  if (err)
+    error (2, err, "device_set_filter: %s", dev_name);
+
+  set_promisc (dev_name, ether_port, 1);
+  return 0;
+}
+
+int ethernet_close (char *dev_name)
+{
+  set_promisc (dev_name, ether_port, 0);
+  return 0;
+}
+
diff --git a/version.h.in b/eth-multiplexer/ethernet.h
similarity index 61%
copy from version.h.in
copy to eth-multiplexer/ethernet.h
index 80e75b5..04b41e3 100644
--- a/version.h.in
+++ b/eth-multiplexer/ethernet.h
@@ -1,6 +1,8 @@
-/* Hurd version
-   Copyright (C) 1996, 1997, 1999 Free Software Foundation, Inc.
-   Written by Thomas Bushnell, n/BSG.
+/*
+   Copyright (C) 2008
+   Free Software Foundation, Inc.
+
+   Written by Zheng Da.
 
    This file is part of the GNU Hurd.
 
@@ -18,12 +20,20 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA. */
 
-#ifndef HURD_VERSION
-#define HURD_VERSION MASTER_HURD_VERSION
+#ifndef ETHERNET_H
+#define ETHERNET_H
+
+#include <netinet/in.h>
+#include <stdlib.h>
+
+extern mach_port_t ether_port;
+
+int ethernet_open (char *dev_name, device_t master_device, 
+                  struct port_bucket *etherport_bucket,
+                  struct port_class *etherreadclass);
+int ethernet_close (char *dev_name);
+int ethernet_demuxer (mach_msg_header_t *inp,
+                     mach_msg_header_t *outp);
+
 #endif
 
-/* The standard way to print versions for --version.  */
-#define STANDARD_HURD_VERSION(s) \
-  #s " (GNU Hurd) " HURD_VERSION
-#define STANDARD_HURD_VERSION_EXTRA(s, extra) \
-  #s " (GNU Hurd; " extra ") " HURD_VERSION
diff --git a/eth-multiplexer/multiplexer.c b/eth-multiplexer/multiplexer.c
new file mode 100644
index 0000000..87578ec
--- /dev/null
+++ b/eth-multiplexer/multiplexer.c
@@ -0,0 +1,208 @@
+/* 
+   Copyright (C) 2008 Free Software Foundation, Inc.
+   Written by Zheng Da.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd; see the file COPYING.  If not, write to
+   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+/*
+ * The multiplexer server provides the virtual network interface.
+ * When it gets a packet, it forwards it to every other network interface,
+ * the ones that are created by itself or that it connects to.
+ * BPF is ported to the multiplexer to help deliver packets
+ * to the right pfinet.
+ */
+
+#include <argz.h>
+#include <argp.h>
+#include <errno.h>
+#include <error.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#include <hurd.h>
+#include <mach.h>
+#include <version.h>
+#include <device/device.h>
+#include <hurd/ports.h>
+#include <hurd/netfs.h>
+
+#include "ethernet.h"
+#include "vdev.h"
+#include "ourdevice_S.h"
+#include "notify_S.h"
+#include "bpf_impl.h"
+#include "netfs_impl.h"
+#include "util.h"
+
+/* The device which the multiplexer connects to */
+static char *device_file;
+
+const char *argp_program_version = "eth-multiplexer 0.1";
+const char *argp_program_bug_address = "<address@hidden>";
+static const char doc[] = "Hurd multiplexer server.";
+static const struct argp_option options[] =
+{
+    {"interface", 'i', "DEVICE", 0,
+      "Network interface to use", 2},
+    {0}
+};
+
+/* Port bucket we service requests on.  */
+struct port_bucket *port_bucket;
+struct port_class *other_portclass;
+struct port_class *vdev_portclass;
+struct port_info *notify_pi;
+
+int netfs_maxsymlinks = 12;
+char *netfs_server_name = "multiplexer";
+char *netfs_server_version = HURD_VERSION;
+file_t root_file;
+struct lnode root;
+struct stat underlying_node_stat;
+
+static int
+multiplexer_demuxer (mach_msg_header_t *inp,
+                 mach_msg_header_t *outp)
+{
+  int device_server (mach_msg_header_t *, mach_msg_header_t *);
+  int notify_server (mach_msg_header_t *, mach_msg_header_t *);
+
+  return (device_server (inp, outp)
+          || notify_server (inp, outp)
+          || ethernet_demuxer (inp, outp));
+}
+
+static void *
+multiplexer_thread (void *arg)
+{
+  ports_manage_port_operations_one_thread (port_bucket,
+                                          multiplexer_demuxer,
+                                          0);
+  return 0;
+}
+
+static error_t
+parse_opt (int opt, char *arg, struct argp_state *state)
+{
+  switch (opt)
+    {
+    case 'i':
+      device_file = arg;
+      break;
+    case ARGP_KEY_ERROR:
+    case ARGP_KEY_SUCCESS:
+    case ARGP_KEY_INIT:
+      break;
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+  return 0;
+}
+
+int
+main (int argc, char *argv[])
+{
+  error_t err;
+  mach_port_t bootstrap;
+  mach_port_t master_device;
+  const struct argp argp = { options, parse_opt, 0, doc };
+  pthread_t t;
+
+  port_bucket = ports_create_bucket ();
+  other_portclass = ports_create_class (0, 0);
+  vdev_portclass = ports_create_class (destroy_vdev, 0);
+
+  argp_parse (&argp, argc, argv, 0, 0, 0);
+
+  /* Open the network interface. */
+  if (device_file)
+    {
+      master_device = file_name_lookup (device_file, 0, 0);
+      if (master_device == MACH_PORT_NULL)
+       error (1, errno, "file_name_lookup");
+
+      ethernet_open (device_file, master_device, port_bucket,
+                    other_portclass);
+    }
+
+  /* Prepare for the notification. */
+  err = ports_create_port (other_portclass, port_bucket, 
+                          sizeof (struct port_info), &notify_pi);
+  if (err)
+    error (1, err, "ports_create_port for notification");
+
+  task_get_bootstrap_port (mach_task_self (), &bootstrap);
+  if (bootstrap == MACH_PORT_NULL)
+    error (1, 0, "must be started as a translator");
+
+  /* Run the multiplexer server in another thread. */
+  pthread_create (&t, NULL, multiplexer_thread, NULL);
+  pthread_detach (t);
+
+  err = maptime_map (0, 0, &multiplexer_maptime);
+  if (err)
+    error (4, err, "Cannot map time");
+
+  /* Initialize netfs and start the translator. */
+  netfs_init ();
+
+  root_file = netfs_startup (bootstrap, O_READ);
+  err = new_node (&root, &netfs_root_node);
+  if (err)
+    error (5, err, "Cannot create root node");
+
+  err = io_stat (root_file, &underlying_node_stat);
+  if (err) 
+    error (6, err, "Cannot stat underlying node");
+
+  struct stat stat = underlying_node_stat;
+  /* If the underlying node is not a directory, increase its permissions */
+  if(!S_ISDIR(stat.st_mode))
+    {
+      if(stat.st_mode & S_IRUSR)
+       stat.st_mode |= S_IXUSR;
+      if(stat.st_mode & S_IRGRP)
+       stat.st_mode |= S_IXGRP;
+      if(stat.st_mode & S_IROTH)
+       stat.st_mode |= S_IXOTH;
+    }
+
+  stat.st_mode &= ~(S_ITRANS | S_IFMT);
+  stat.st_mode |= S_IFDIR;
+  netfs_root_node->nn->ln->st = stat;
+  fshelp_touch (&netfs_root_node->nn_stat, TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME,
+               multiplexer_maptime);
+
+  netfs_server_loop ();         /* Never returns.  */
+  return 0;
+}
+
+error_t
+netfs_append_args (char **argz, size_t *argz_len)
+{
+  error_t err = 0;
+
+#define ADD_OPT(fmt, args...)                                          \
+  do { char buf[100];                                                  \
+       if (! err) {                                                    \
+         snprintf (buf, sizeof buf, fmt , ##args);                     \
+         err = argz_add (argz, argz_len, buf); } } while (0)
+  if (device_file)
+    ADD_OPT ("--interface=%s", device_file);
+#undef ADD_OPT
+  return err;
+}
diff --git a/ftpfs/netfs.c b/eth-multiplexer/netfs_impl.c
similarity index 54%
copy from ftpfs/netfs.c
copy to eth-multiplexer/netfs_impl.c
index 5359acb..29ae072 100644
--- a/ftpfs/netfs.c
+++ b/eth-multiplexer/netfs_impl.c
@@ -1,38 +1,107 @@
-/* ftpfs interface to libnetfs
-
-   Copyright (C) 1997, 1998, 1999, 2001, 2007 Free Software Foundation, Inc.
-
-   Written by Miles Bader <address@hidden>
+/* 
+   Copyright (C) 2008, 2009 Free Software Foundation, Inc.
+   Written by Zheng Da.
 
    This file is part of the GNU Hurd.
 
-   The GNU Hurd is free software; you can redistribute it and/or
-   modify it under the terms of the GNU General Public License as
-   published by the Free Software Foundation; either version 2, or (at
-   your option) any later version.
+   The GNU Hurd is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
 
-   The GNU Hurd is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   General Public License for more details.
+   The GNU Hurd is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA. */
+   along with the GNU Hurd; see the file COPYING.  If not, write to
+   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 
-#include <stddef.h>
-#include <stdlib.h>
-#include <dirent.h>
-#include <string.h>
 #include <fcntl.h>
-#include <unistd.h>
+#include <dirent.h>
+#include <stddef.h>
 #include <sys/mman.h>
+#include <stdlib.h>
+#include <ctype.h>
 
 #include <hurd/netfs.h>
 
-#include "ftpfs.h"
-#include "ccache.h"
-
+#include "netfs_impl.h"
+#include "vdev.h"
+#include "util.h"
+
+#define DIRENTS_CHUNK_SIZE      (8*1024)
+/* Returned directory entries are aligned to blocks this many bytes long.
+ * Must be a power of two.  */
+#define DIRENT_ALIGN 4
+#define DIRENT_NAME_OFFS offsetof (struct dirent, d_name)
+
+/* Length is structure before the name + the name + '\0', all
+ *    padded to a four-byte alignment.  */
+#define DIRENT_LEN(name_len)                                                  \
+  ((DIRENT_NAME_OFFS + (name_len) + 1 + (DIRENT_ALIGN - 1))                   \
+   & ~(DIRENT_ALIGN - 1))
+
+extern struct stat underlying_node_stat;
+
+int 
+is_num (char *str)
+{
+  for (; *str; str++)
+    {
+      if (!isdigit (*str))
+       return 0;
+    }
+  return 1;
+}
+
+/* Make a new virtual node.  Always consumes the ports.  */
+error_t
+new_node (struct lnode *ln, struct node **np)
+{
+  error_t err = 0;
+  struct netnode *nn = calloc (1, sizeof *nn);
+  struct node *node;
+
+  if (nn == 0)
+    return ENOMEM;
+  node = netfs_make_node (nn);
+  if (node == 0)
+    {
+      free (nn);
+      *np = NULL;
+      return ENOMEM;
+    }
+  if (ln)
+    ln->n = node;
+  nn->ln = ln;
+  *np = node;
+  return err;
+}
+
+struct node *
+lookup (char *name)
+{
+  struct lnode *ln = (struct lnode *) lookup_dev_by_name (name);
+
+  char *copied_name = malloc (strlen (name) + 1);
+  strcpy (copied_name, name);
+  if (ln)
+    {
+      new_node (ln, &ln->n);
+      ln->n->nn->name = copied_name;
+      return ln->n;
+    }
+  else
+    {
+      struct node *n;
+      new_node (ln, &n);
+      n->nn->name = copied_name;
+      return n;
+    }
+}
+
 /* Attempt to create a file named NAME in DIR for USER with MODE.  Set *NODE
    to the new node upon return.  On any error, clear *NODE.  *NODE should be
    locked on success; no matter what, unlock DIR before returning.  */
@@ -40,6 +109,7 @@ error_t
 netfs_attempt_create_file (struct iouser *user, struct node *dir,
                           char *name, mode_t mode, struct node **node)
 {
+  debug("");
   *node = 0;
   pthread_mutex_unlock (&dir->lock);
   return EOPNOTSUPP;
@@ -52,13 +122,19 @@ error_t
 netfs_check_open_permissions (struct iouser *user, struct node *node,
                              int flags, int newnode)
 {
-  error_t err = ftpfs_refresh_node (node);
-  if (!err && (flags & O_READ))
-    err = fshelp_access (&node->nn_stat, S_IREAD, user);
-  if (!err && (flags & O_WRITE))
-    err = fshelp_access (&node->nn_stat, S_IWRITE, user);
-  if (!err && (flags & O_EXEC))
-    err = fshelp_access (&node->nn_stat, S_IEXEC, user);
+  error_t err = 0;
+
+  /*Cheks user's permissions*/
+  if(flags & O_READ)
+    err = fshelp_access(&node->nn_stat, S_IREAD, user);
+  if(!err && (flags & O_WRITE))
+    err = fshelp_access(&node->nn_stat, S_IWRITE, user);
+  if(!err && (flags & O_EXEC))
+    err = fshelp_access(&node->nn_stat, S_IEXEC, user);
+
+  debug("the mode of node: %o, return result: %d",
+       (node->nn_stat.st_mode & ~S_IFMT), err);
+  /*Return the result of the check*/
   return err;
 }
 
@@ -68,28 +144,8 @@ error_t
 netfs_attempt_utimes (struct iouser *cred, struct node *node,
                      struct timespec *atime, struct timespec *mtime)
 {
-  error_t err = ftpfs_refresh_node (node);
-  int flags = TOUCH_CTIME;
-
-  if (! err)
-    err = fshelp_isowner (&node->nn_stat, cred);
-
-  if (! err)
-    {
-      if (atime)
-       node->nn_stat.st_atim = *atime;
-      else
-       flags |= TOUCH_ATIME;
-
-      if (mtime)
-       node->nn_stat.st_mtim = *mtime;
-      else
-       flags |= TOUCH_MTIME;
-
-      fshelp_touch (&node->nn_stat, flags, ftpfs_maptime);
-    }
-
-  return err;
+  debug("");
+  return EOPNOTSUPP;
 }
 
 /* Return the valid access types (bitwise OR of O_READ, O_WRITE, and O_EXEC)
@@ -97,30 +153,27 @@ netfs_attempt_utimes (struct iouser *cred, struct node 
*node,
 error_t
 netfs_report_access (struct iouser *cred, struct node *node, int *types)
 {
-  error_t err = ftpfs_refresh_node (node);
-
-  if (! err)
-    {
-      *types = 0;
-      if (fshelp_access (&node->nn_stat, S_IREAD, cred) == 0)
-       *types |= O_READ;
-      if (fshelp_access (&node->nn_stat, S_IWRITE, cred) == 0)
-       *types |= O_WRITE;
-      if (fshelp_access (&node->nn_stat, S_IEXEC, cred) == 0)
-       *types |= O_EXEC;
-    }
-
-  return err;
+  debug("");
+  *types = 0;
+  return 0;
 }
 
-/* Trivial definitions.  */
-
 /* Make sure that NP->nn_stat is filled with current information.  CRED
    identifies the user responsible for the operation.  */
 error_t
 netfs_validate_stat (struct node *node, struct iouser *cred)
 {
-  return ftpfs_refresh_node (node);
+  struct stat st;
+  
+  if (node->nn->ln)
+    st = node->nn->ln->st;
+  else
+    st = underlying_node_stat;
+  
+  debug("node: %p", node);
+  node->nn_translated = S_ISLNK (st.st_mode) ? S_IFLNK : 0;
+  node->nn_stat = st;
+  return 0;
 }
 
 /* This should sync the file NODE completely to disk, for the user CRED.  If
@@ -128,123 +181,9 @@ netfs_validate_stat (struct node *node, struct iouser 
*cred)
 error_t
 netfs_attempt_sync (struct iouser *cred, struct node *node, int wait)
 {
+  debug("");
   return 0;
 }
-
-/* The granularity with which we allocate space to return our result.  */
-#define DIRENTS_CHUNK_SIZE     (8*1024)
-
-/* Returned directory entries are aligned to blocks this many bytes long.
-   Must be a power of two.  */
-#define DIRENT_ALIGN 4
-#define DIRENT_NAME_OFFS offsetof (struct dirent, d_name)
-
-/* Length is structure before the name + the name + '\0', all
-   padded to a four-byte alignment.  */
-#define DIRENT_LEN(name_len)                                                 \
-  ((DIRENT_NAME_OFFS + (name_len) + 1 + (DIRENT_ALIGN - 1))                  \
-   & ~(DIRENT_ALIGN - 1))
-
-/* Fetch a directory, as for netfs_get_dirents.  */
-static error_t
-get_dirents (struct ftpfs_dir *dir,
-            int first_entry, int max_entries, char **data,
-            mach_msg_type_number_t *data_len,
-            vm_size_t max_data_len, int *data_entries)
-{
-  struct ftpfs_dir_entry *e;
-  error_t err = 0;
-
-  if (! dir)
-    return ENOTDIR;
-
-  e = dir->ordered;
-
-  /* Find the first entry.  */
-  while (first_entry-- > 0)
-    if (! e)
-      {
-       max_entries = 0;
-       break;
-      }
-    else
-      e = e->ordered_next;
-
-  if (max_entries != 0)
-    {
-      size_t size =
-       (max_data_len == 0 || max_data_len > DIRENTS_CHUNK_SIZE
-        ? DIRENTS_CHUNK_SIZE
-        : max_data_len);
-
-      *data = mmap (0, size, PROT_READ|PROT_WRITE,
-                                  MAP_ANON, 0, 0);
-      err = ((void *) *data == (void *) -1) ? errno : 0;
-
-      if (! err)
-       {
-         char *p = *data;
-         int count = 0;
-
-         /* See how much space we need for the result.  */
-         while ((max_entries == -1 || count < max_entries) && e)
-           {
-             struct dirent hdr;
-             size_t name_len = strlen (e->name);
-             size_t sz = DIRENT_LEN (name_len);
-             int entry_type =
-               e->stat_timestamp ? IFTODT (e->stat.st_mode) : DT_UNKNOWN;
-
-             if ((p - *data) + sz > size)
-               {
-                 if (max_data_len > 0)
-                   break;
-                 else
-                   /* Try to grow our return buffer.  */
-                   {
-                     vm_address_t extension = (vm_address_t)(*data + size);
-                     err = vm_allocate (mach_task_self (), &extension,
-                                        DIRENTS_CHUNK_SIZE, 0);
-                     if (err)
-                       break;
-                     size += DIRENTS_CHUNK_SIZE;
-                   }
-               }
-
-             hdr.d_namlen = name_len;
-             hdr.d_fileno = e->stat.st_ino;
-             hdr.d_reclen = sz;
-             hdr.d_type = entry_type;
-
-             memcpy (p, &hdr, DIRENT_NAME_OFFS);
-             strcpy (p + DIRENT_NAME_OFFS, e->name);
-             p += sz;
-
-             count++;
-             e = e->ordered_next;
-           }
-
-         if (err)
-           munmap (*data, size);
-         else
-           {
-             vm_address_t alloc_end = (vm_address_t)(*data + size);
-             vm_address_t real_end = round_page (p);
-             if (alloc_end > real_end)
-               munmap ((caddr_t) real_end, alloc_end - real_end);
-             *data_len = p - *data;
-             *data_entries = count;
-           }
-       }
-    }
-  else
-    {
-      *data_len = 0;
-      *data_entries = 0;
-    }
-
-  return err;
-}
 
 error_t
 netfs_get_dirents (struct iouser *cred, struct node *dir,
@@ -252,24 +191,89 @@ netfs_get_dirents (struct iouser *cred, struct node *dir,
                   mach_msg_type_number_t *data_len,
                   vm_size_t max_data_len, int *data_entries)
 {
-  error_t err = ftpfs_refresh_node (dir);
+  error_t err;
+  int count = 0;
+  char *data_p;
+  size_t size = (max_data_len == 0 || max_data_len > DIRENTS_CHUNK_SIZE
+     ? DIRENTS_CHUNK_SIZE : max_data_len);
+  debug ("");
+  int
+    add_dirent (const char * name, ino_t ino, int type)
+      {
+       /*If the required number of dirents has not been listed yet*/
+       if((max_entries == -1) || (count < max_entries))
+         {
+           struct dirent hdr;
+           size_t name_len = strlen(name);
+           size_t sz = DIRENT_LEN(name_len);
+
+           /*If there is no room for this dirent*/
+           if ((data_p - *data) + sz > size)
+             {
+               if (max_data_len > 0)
+                 return 1;
+               else
+                 /* Try to grow our return buffer.  */
+                 {
+                   error_t err;
+                   vm_address_t extension = (vm_address_t)(*data + size);
+                   err = vm_allocate (mach_task_self (), &extension,
+                                      DIRENTS_CHUNK_SIZE, 0);
+                   if (err)
+                     {
+                       munmap (*data, size);
+                       return 1;
+                     }
+                   size += DIRENTS_CHUNK_SIZE;
+                 }
+             }
+
+           /*setup the dirent*/
+           hdr.d_ino = ino;
+           hdr.d_reclen = sz;
+           hdr.d_type = type;
+           hdr.d_namlen = name_len;
+           memcpy(data_p, &hdr, DIRENT_NAME_OFFS);
+           strcpy(data_p + DIRENT_NAME_OFFS, name);
+           data_p += sz;
+
+           /*count the new dirent*/
+           ++count;
+         }
+       return 0;
+      }
+  int add_each_dev (struct vether_device *dev)
+    {
+      struct lnode *ln = (struct lnode *) dev;
+      add_dirent (ln->vdev.name, ln->st.st_ino, DT_CHR);
+      return 0;
+    }
+  if (dir != netfs_root_node)
+    return ENOTDIR;
 
-  if (! err)
+  *data = mmap (0, size, PROT_READ | PROT_WRITE, MAP_ANON, 0, 0);
+  err = ((void *) *data == (void *) -1) ? errno : 0;
+  if (!err)
     {
-      if (dir->nn->dir)
+      data_p = *data;
+      if (first_entry < 2 + get_dev_num ())
        {
-         err = ftpfs_dir_refresh (dir->nn->dir);
-         if (! err)
-           err = get_dirents (dir->nn->dir, first_entry, max_entries,
-                              data, data_len, max_entries, data_entries);
+         add_dirent (".", 2, DT_DIR);
+         add_dirent ("..", 2, DT_DIR);
+         foreach_dev_do (add_each_dev);
        }
-      else
-       err = ENOTDIR;
-    }
 
+      vm_address_t alloc_end = (vm_address_t)(*data + size);
+      vm_address_t real_end = round_page (data_p);
+      if (alloc_end > real_end)
+       munmap ((caddr_t) real_end, alloc_end - real_end);
+      *data_entries = count;
+      debug ("first_entry is %d, count is %d", first_entry, count);
+      *data_len = data_p - *data;
+    }
   return err;
 }
-
+
 /* Lookup NAME in DIR for USER; set *NODE to the found name upon return.  If
    the name was not found, then return ENOENT.  On any error, clear *NODE.
    (*NODE, if found, should be locked, this call should unlock DIR no matter
@@ -277,17 +281,41 @@ netfs_get_dirents (struct iouser *cred, struct node *dir,
 error_t netfs_attempt_lookup (struct iouser *user, struct node *dir,
                              char *name, struct node **node)
 {
-  error_t err = ftpfs_refresh_node (dir);
-  if (! err)
-    err = ftpfs_dir_lookup (dir->nn->dir, name, node);
-  return err;
+  error_t err = 0;
+
+  debug ("dir: %p, file name: %s", dir, name);
+
+  if (strcmp(name, ".") == 0)
+    {
+      netfs_nref(dir);
+      *node = dir;
+      return 0;
+    }
+  else if (strcmp(name, "..") == 0)
+    {
+      /*The supplied node is always root*/
+      err = ENOENT;
+      *node = NULL;
+
+      /*unlock the directory*/
+      pthread_mutex_unlock (&dir->lock);
+
+      /*stop here*/
+      return err;
+    }
+
+  *node = lookup (name);
+  pthread_mutex_lock (&(*node)->lock);
+  pthread_mutex_unlock (&dir->lock);
+  return 0;
 }
 
 /* Delete NAME in DIR for USER. */
 error_t netfs_attempt_unlink (struct iouser *user, struct node *dir,
                              char *name)
 {
-  return EROFS;
+  debug("");
+  return EOPNOTSUPP;
 }
 
 /* Note that in this one call, neither of the specific nodes are locked. */
@@ -295,7 +323,8 @@ error_t netfs_attempt_rename (struct iouser *user, struct 
node *fromdir,
                              char *fromname, struct node *todir,
                              char *toname, int excl)
 {
-  return EROFS;
+  debug("");
+  return EOPNOTSUPP;
 }
 
 /* Attempt to create a new directory named NAME in DIR for USER with mode
@@ -303,22 +332,25 @@ error_t netfs_attempt_rename (struct iouser *user, struct 
node *fromdir,
 error_t netfs_attempt_mkdir (struct iouser *user, struct node *dir,
                             char *name, mode_t mode)
 {
-  return EROFS;
+  debug("");
+  return EOPNOTSUPP;
 }
 
 /* Attempt to remove directory named NAME in DIR for USER. */
 error_t netfs_attempt_rmdir (struct iouser *user,
                             struct node *dir, char *name)
 {
-  return EROFS;
+  debug("");
+  return EOPNOTSUPP;
 }
-
+
 /* This should attempt a chmod call for the user specified by CRED on node
    NODE, to change the owner to UID and the group to GID. */
 error_t netfs_attempt_chown (struct iouser *cred, struct node *node,
                             uid_t uid, uid_t gid)
 {
-  return EROFS;
+  debug("");
+  return EOPNOTSUPP;
 }
 
 /* This should attempt a chauthor call for the user specified by CRED on node
@@ -326,7 +358,8 @@ error_t netfs_attempt_chown (struct iouser *cred, struct 
node *node,
 error_t netfs_attempt_chauthor (struct iouser *cred, struct node *node,
                                uid_t author)
 {
-  return EROFS;
+  debug("");
+  return EOPNOTSUPP;
 }
 
 /* This should attempt a chmod call for the user specified by CRED on node
@@ -337,14 +370,27 @@ error_t netfs_attempt_chauthor (struct iouser *cred, 
struct node *node,
 error_t netfs_attempt_chmod (struct iouser *cred, struct node *node,
                             mode_t mode)
 {
-  return EROFS;
+  error_t err = 0;
+  debug("");
+  if (node->nn->ln == NULL)
+    return EOPNOTSUPP;
+
+  mode &= ~S_ITRANS;
+  err = fshelp_isowner (&node->nn->ln->st, cred);
+  if (err)
+    return err;
+  mode |= node->nn->ln->st.st_mode & S_IFMT;
+  node->nn->ln->st.st_mode = mode;
+  fshelp_touch (&node->nn_stat, TOUCH_CTIME, multiplexer_maptime);
+  return err;
 }
 
 /* Attempt to turn NODE (user CRED) into a symlink with target NAME. */
 error_t netfs_attempt_mksymlink (struct iouser *cred, struct node *node,
                                 char *name)
 {
-  return EROFS;
+  debug("");
+  return EOPNOTSUPP;
 }
 
 /* Attempt to turn NODE (user CRED) into a device.  TYPE is either S_IFBLK or
@@ -352,7 +398,8 @@ error_t netfs_attempt_mksymlink (struct iouser *cred, 
struct node *node,
 error_t netfs_attempt_mkdev (struct iouser *cred, struct node *node,
                             mode_t type, dev_t indexes)
 {
-  return EROFS;
+  debug("");
+  return EOPNOTSUPP;
 }
 
 /* Attempt to set the passive translator record for FILE to ARGZ (of length
@@ -360,7 +407,8 @@ error_t netfs_attempt_mkdev (struct iouser *cred, struct 
node *node,
 error_t netfs_set_translator (struct iouser *cred, struct node *node,
                              char *argz, size_t argzlen)
 {
-  return EROFS;
+  debug("");
+  return EOPNOTSUPP;
 }
 
 /* This should attempt a chflags call for the user specified by CRED on node
@@ -368,7 +416,8 @@ error_t netfs_set_translator (struct iouser *cred, struct 
node *node,
 error_t netfs_attempt_chflags (struct iouser *cred, struct node *node,
                               int flags)
 {
-  return EROFS;
+  debug("");
+  return EOPNOTSUPP;
 }
 
 /* This should attempt to set the size of the file NODE (for user CRED) to
@@ -376,25 +425,24 @@ error_t netfs_attempt_chflags (struct iouser *cred, 
struct node *node,
 error_t netfs_attempt_set_size (struct iouser *cred, struct node *node,
                                off_t size)
 {
-  return EROFS;
+  debug("");
+  return EOPNOTSUPP;
 }
 
-/* This should attempt to fetch filesystem status information for the remote
-   filesystem, for the user CRED. */
+/*Fetches the filesystem status information*/
 error_t
 netfs_attempt_statfs (struct iouser *cred, struct node *node,
                      struct statfs *st)
 {
-  bzero (st, sizeof *st);
-  st->f_type = FSTYPE_FTP;
-  st->f_fsid = getpid ();
-  return 0;
+  debug("");
+  return EOPNOTSUPP;
 }
 
 /* This should sync the entire remote filesystem.  If WAIT is set, return
    only after sync is completely finished.  */
 error_t netfs_attempt_syncfs (struct iouser *cred, int wait)
 {
+  debug("");
   return 0;
 }
 
@@ -404,7 +452,8 @@ error_t netfs_attempt_syncfs (struct iouser *cred, int wait)
 error_t netfs_attempt_link (struct iouser *user, struct node *dir,
                            struct node *file, char *name, int excl)
 {
-  return EROFS;
+  debug("");
+  return EOPNOTSUPP;
 }
 
 /* Attempt to create an anonymous file related to DIR for USER with MODE.
@@ -412,24 +461,17 @@ error_t netfs_attempt_link (struct iouser *user, struct 
node *dir,
 error_t netfs_attempt_mkfile (struct iouser *user, struct node *dir,
                              mode_t mode, struct node **node)
 {
+  debug("");
   *node = 0;
   pthread_mutex_unlock (&dir->lock);
-  return EROFS;
+  return EOPNOTSUPP;
 }
 
 /* Read the contents of NODE (a symlink), for USER, into BUF. */
 error_t netfs_attempt_readlink (struct iouser *user, struct node *node, char 
*buf)
 {
-  error_t err = ftpfs_refresh_node (node);
-  if (! err)
-    {
-      struct ftpfs_dir_entry *e = node->nn->dir_entry;
-      if (e)
-       bcopy (e->symlink_target, buf, node->nn_stat.st_size);
-      else
-       err = EINVAL;
-    }
-  return err;
+  debug("");
+  return EOPNOTSUPP;
 }
 
 /* Read from the file NODE for user CRED starting at OFFSET and continuing for
@@ -438,19 +480,8 @@ error_t netfs_attempt_readlink (struct iouser *user, 
struct node *node, char *bu
 error_t netfs_attempt_read (struct iouser *cred, struct node *node,
                            off_t offset, size_t *len, void *data)
 {
-  error_t err = 0;
-
-  if (! node->nn->contents)
-    err = ccache_create (node, &node->nn->contents);
-  if (! err)
-    {
-      if (*len > node->nn_stat.st_size - offset)
-       *len = node->nn_stat.st_size - offset;
-      if (*len > 0)
-       err = ccache_read (node->nn->contents, offset, *len, data);
-    }
-
-  return err;
+  debug("");
+  return EOPNOTSUPP;
 }
 
 /* Write to the file NODE for user CRED starting at OFSET and continuing for up
@@ -459,5 +490,19 @@ error_t netfs_attempt_read (struct iouser *cred, struct 
node *node,
 error_t netfs_attempt_write (struct iouser *cred, struct node *node,
                             off_t offset, size_t *len, void *data)
 {
-  return EROFS;
+  debug("");
+  return EOPNOTSUPP;
 }
+
+/* Node NP is all done; free all its associated storage. */
+void
+netfs_node_norefs (struct node *node)
+{
+  debug("node: %p", node);
+  if (node->nn->ln)
+    node->nn->ln->n = NULL;
+  free (node->nn->name);
+  free (node->nn);
+  free (node);
+}
+
diff --git a/devnode/util.h b/eth-multiplexer/netfs_impl.h
similarity index 64%
copy from devnode/util.h
copy to eth-multiplexer/netfs_impl.h
index 2efc1ef..17c66f6 100644
--- a/devnode/util.h
+++ b/eth-multiplexer/netfs_impl.h
@@ -1,5 +1,5 @@
 /* 
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009 Free Software Foundation, Inc.
    Written by Zheng Da.
 
    This file is part of the GNU Hurd.
@@ -18,25 +18,30 @@
    along with the GNU Hurd; see the file COPYING.  If not, write to
    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 
-#ifndef UTIL_H
-#define UTIL_H
+#ifndef NETFS_IMPL
+#define NETFS_IMPL
 
-#include <stdio.h>
+#include <hurd.h>
+#include <mach.h>
 
-#ifdef DEBUG 
+#include "vdev.h"
 
-#define debug(format, ...) do                          \
-{                                                      \
-  char buf[1024];                                       \
-  snprintf (buf, 1024, "devnode: %s", format);       \
-  fprintf (stderr , buf, ## __VA_ARGS__);              \
-  fflush (stderr);                                     \
-} while (0)
+struct netnode
+{
+  struct lnode *ln;
+  char *name;
+};
 
-#else
+struct lnode
+{
+  struct vether_device vdev;
+  struct stat st;
+  struct node *n;
+};
 
-#define debug(format, ...) do {} while (0)
+extern file_t root_file;
+volatile struct mapped_time_value *multiplexer_maptime;
 
-#endif
+error_t new_node (struct lnode *ln, struct node **np);
 
 #endif
diff --git a/eth-multiplexer/notify_impl.c b/eth-multiplexer/notify_impl.c
new file mode 100644
index 0000000..33725bb
--- /dev/null
+++ b/eth-multiplexer/notify_impl.c
@@ -0,0 +1,69 @@
+/* 
+   Copyright (C) 2008 Free Software Foundation, Inc.
+   Written by Zheng Da.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd; see the file COPYING.  If not, write to
+   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include <hurd.h>
+#include <mach.h>
+
+#include "vdev.h"
+
+/* Implementation of notify interface */
+kern_return_t
+do_mach_notify_port_deleted (mach_port_t notify,
+                            mach_port_t name)
+{
+  return EOPNOTSUPP;
+}
+
+kern_return_t
+do_mach_notify_msg_accepted (mach_port_t notify,
+                            mach_port_t name)
+{
+  return EOPNOTSUPP;
+}
+
+kern_return_t
+do_mach_notify_port_destroyed (mach_port_t notify,
+                              mach_port_t port)
+{
+  return EOPNOTSUPP;
+}
+
+kern_return_t
+do_mach_notify_no_senders (mach_port_t notify,
+                          mach_port_mscount_t mscount)
+{
+  return ports_do_mach_notify_no_senders (notify, mscount);
+}
+
+kern_return_t
+do_mach_notify_send_once (mach_port_t notify)
+{
+  return EOPNOTSUPP;
+}
+
+kern_return_t
+do_mach_notify_dead_name (mach_port_t notify,
+                         mach_port_t name)
+{
+  debug ("do_mach_notify_dead_name is called\n");
+  mach_port_deallocate (mach_task_self (), name);
+  remove_dead_port_from_dev (name);
+  return 0;
+}
diff --git a/devnode/util.h b/eth-multiplexer/test.c
similarity index 55%
copy from devnode/util.h
copy to eth-multiplexer/test.c
index 2efc1ef..bf80583 100644
--- a/devnode/util.h
+++ b/eth-multiplexer/test.c
@@ -18,25 +18,36 @@
    along with the GNU Hurd; see the file COPYING.  If not, write to
    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 
-#ifndef UTIL_H
-#define UTIL_H
+#define _GNU_SOURCE
 
 #include <stdio.h>
-
-#ifdef DEBUG 
-
-#define debug(format, ...) do                          \
-{                                                      \
-  char buf[1024];                                       \
-  snprintf (buf, 1024, "devnode: %s", format);       \
-  fprintf (stderr , buf, ## __VA_ARGS__);              \
-  fflush (stderr);                                     \
-} while (0)
-
-#else
-
-#define debug(format, ...) do {} while (0)
-
-#endif
-
-#endif
+#include <string.h>
+#include <error.h>
+
+#include <hurd.h>
+#include <mach.h>
+#include <device/device.h>
+
+int
+main(int argc , char *argv[])
+{
+  mach_port_t device;
+  mach_port_t master_device;
+  error_t err;
+
+  err = get_privileged_ports (0, &master_device);
+  if (err)
+    error (2, err, "cannot get device master port");
+
+  err = device_open (master_device, D_READ | D_WRITE, "eth0", &device);
+  if (err)
+    error (1, err, "device_open");
+  printf ("the device port is %d\n", device);
+
+  err = device_open (master_device, D_READ | D_WRITE, "eth0", &device);
+  if (err)
+    error (1, err, "device_open");
+  printf ("the device port is %d\n", device);
+
+  return 0;
+}
diff --git a/libbpf/util.h b/eth-multiplexer/util.h
similarity index 100%
copy from libbpf/util.h
copy to eth-multiplexer/util.h
diff --git a/eth-multiplexer/vdev.c b/eth-multiplexer/vdev.c
new file mode 100644
index 0000000..fd88661
--- /dev/null
+++ b/eth-multiplexer/vdev.c
@@ -0,0 +1,309 @@
+/*
+   Copyright (C) 2008 Free Software Foundation, Inc.
+   Written by Zheng Da.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA. */
+
+/* This file implement the virtual network interface */
+
+#include <string.h>
+#include <stdio.h>
+#include <netinet/ip.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <error.h>
+
+#include <pthread.h>
+
+#include "vdev.h"
+#include "queue.h"
+#include "bpf_impl.h"
+#include "util.h"
+
+#define ETH_HLEN sizeof (struct ethhdr)
+
+static struct vether_device *dev_head;
+static int dev_num;
+
+/* This lock is only used to protected the virtual device list.
+ * TODO every device structure should has its own lock to protect itself. */
+static pthread_mutex_t dev_list_lock = PTHREAD_MUTEX_INITIALIZER;
+
+mach_msg_type_t header_type = 
+{
+  MACH_MSG_TYPE_BYTE,
+  8,
+  NET_HDW_HDR_MAX,
+  TRUE,
+  FALSE,
+  FALSE,
+  0
+};
+
+mach_msg_type_t packet_type = 
+{
+  MACH_MSG_TYPE_BYTE,  /* name */
+  8,                   /* size */
+  0,                   /* number */
+  TRUE,                        /* inline */
+  FALSE,                       /* longform */
+  FALSE                        /* deallocate */
+};
+
+int
+get_dev_num ()
+{
+  return dev_num;
+}
+
+struct vether_device *
+lookup_dev_by_name (char *name)
+{
+  struct vether_device *vdev;
+  pthread_mutex_lock (&dev_list_lock);
+  for (vdev = dev_head; vdev; vdev = vdev->next)
+    {
+      if (strncmp (vdev->name, name, IFNAMSIZ) == 0)
+       break;
+    }
+  pthread_mutex_unlock (&dev_list_lock);
+  return vdev;
+}
+
+int
+foreach_dev_do (int (func) (struct vether_device *))
+{
+  struct vether_device *vdev;
+  int rval = 0;
+  pthread_mutex_lock (&dev_list_lock);
+  for (vdev = dev_head; vdev; vdev = vdev->next)
+    {
+      pthread_mutex_unlock (&dev_list_lock);
+      /* func() can stop the loop by returning <> 0 */
+      rval = func (vdev);
+      pthread_mutex_lock (&dev_list_lock);
+      if (rval)
+       break;
+    }
+  pthread_mutex_unlock (&dev_list_lock);
+  return rval;
+}
+
+/* Remove all filters with the dead name. */
+int 
+remove_dead_port_from_dev (mach_port_t dead_port)
+{
+  struct vether_device *vdev;
+  pthread_mutex_lock (&dev_list_lock);
+  for (vdev = dev_head; vdev; vdev = vdev->next)
+    {
+      remove_dead_filter (&vdev->port_list,
+                         &vdev->port_list.if_rcv_port_list, dead_port);
+      remove_dead_filter (&vdev->port_list,
+                         &vdev->port_list.if_snd_port_list, dead_port);
+    }
+  pthread_mutex_unlock (&dev_list_lock);
+  return 0;
+}
+
+/* Add a new virtual interface to the multiplexer. */
+struct vether_device *
+add_vdev (char *name, int size, 
+         struct port_class *class, struct port_bucket *bucket)
+{
+  error_t err;
+  struct vether_device *vdev;
+
+  if (size < sizeof (*vdev))
+    size = sizeof (*vdev);
+  err = ports_create_port (class, bucket, size, &vdev);
+  if (err)
+    return NULL;
+
+  vdev->dev_port = ports_get_right (vdev);
+  ports_port_deref (vdev);
+  strncpy (vdev->name, name, IFNAMSIZ);
+  vdev->if_header_size = ETH_HLEN;
+  vdev->if_mtu = ETH_MTU;
+  vdev->if_header_format = HDR_ETHERNET;
+  vdev->if_address_size = ETH_ALEN;
+  vdev->if_flags = 0;
+  vdev->if_address[0] = 0x52;
+  vdev->if_address[1] = 0x54;
+  *(int *)(vdev->if_address + 2) = random ();
+
+  queue_init (&vdev->port_list.if_rcv_port_list);
+  queue_init (&vdev->port_list.if_snd_port_list);
+
+  pthread_mutex_lock (&dev_list_lock);
+  vdev->next = dev_head;
+  dev_head = vdev;
+  vdev->pprev = &dev_head;
+  if (vdev->next)
+    vdev->next->pprev = &vdev->next;
+  dev_num++;
+  pthread_mutex_unlock (&dev_list_lock);
+
+  debug ("initialize the virtual device\n");
+  return vdev;
+}
+
+void
+destroy_vdev (void *port)
+{
+  struct vether_device *vdev = (struct vether_device *)port;
+
+  debug ("device %s is going to be destroyed\n", vdev->name);
+  /* Delete it from the virtual device list */
+  pthread_mutex_lock (&dev_list_lock);
+  *vdev->pprev = vdev->next;
+  if (vdev->next)
+    vdev->next->pprev = vdev->pprev;
+  dev_num--;
+  pthread_mutex_unlock (&dev_list_lock);
+
+  /* TODO Delete all filters in the interface,
+   * there shouldn't be any filters left */
+  destroy_filters (&vdev->port_list);
+}
+
+/* Test if there are devices existing in the list */
+int
+has_vdev ()
+{
+  return dev_head != NULL;
+}
+
+/* Broadcast the packet to all virtual interfaces
+ * except the one the packet is from */
+int 
+broadcast_pack (char *data, int datalen, struct vether_device *from_vdev)
+{
+  int internal_deliver_pack (struct vether_device *vdev)
+    {
+      if (from_vdev == vdev)
+       return 0;
+      return deliver_pack (data, datalen, vdev);
+    }
+
+  return foreach_dev_do (internal_deliver_pack);
+}
+
+/* Create a message, and deliver it. */
+int 
+deliver_pack (char *data, int datalen, struct vether_device *vdev)
+{
+  struct net_rcv_msg msg;
+  int pack_size;
+  struct ethhdr *header;
+  struct packet_header *packet;
+
+  pack_size = datalen - sizeof (struct ethhdr);
+  /* remember message sizes must be rounded up */
+  msg.msg_hdr.msgh_size = (((mach_msg_size_t) (sizeof(struct net_rcv_msg)
+                                              - NET_RCV_MAX + pack_size)) + 3) 
& ~3;
+
+  header = (struct ethhdr *) msg.header;
+  packet = (struct packet_header *) msg.packet;
+  msg.header_type = header_type;
+  memcpy (header, data, sizeof (struct ethhdr));
+  msg.packet_type = packet_type;
+  memcpy (packet + 1, data + sizeof (struct ethhdr), pack_size);
+  packet->type = header->h_proto;
+  packet->length = pack_size + sizeof (struct packet_header);
+  msg.packet_type.msgt_number = packet->length;
+
+  return deliver_msg (&msg, vdev);
+}
+
+/* Broadcast the message to all virtual interfaces. */
+int 
+broadcast_msg (struct net_rcv_msg *msg)
+{
+  int rval = 0;
+  mach_msg_header_t header;
+
+  int internal_deliver_msg (struct vether_device *vdev)
+    {
+      return deliver_msg (msg, vdev);
+    }
+
+  /* Save the message header because deliver_msg will change it. */
+  header = msg->msg_hdr;
+  rval = foreach_dev_do (internal_deliver_msg);
+  msg->msg_hdr = header;
+  return rval;
+}
+
+/*
+ * Deliver the message to all right pfinet servers that
+ * connects to the virtual network interface.
+ */
+int
+deliver_msg(struct net_rcv_msg *msg, struct vether_device *vdev)
+{
+  mach_msg_return_t err;
+  queue_head_t *if_port_list;
+  net_rcv_port_t infp, nextfp;
+
+  msg->msg_hdr.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, 0);
+  /* remember message sizes must be rounded up */
+  msg->msg_hdr.msgh_local_port = MACH_PORT_NULL;
+  msg->msg_hdr.msgh_kind = MACH_MSGH_KIND_NORMAL;
+  msg->msg_hdr.msgh_id = NET_RCV_MSG_ID;
+
+  if_port_list = &vdev->port_list.if_rcv_port_list;
+  FILTER_ITERATE (if_port_list, infp, nextfp, &infp->input) 
+    {
+      mach_port_t dest;
+      net_hash_entry_t entp, *hash_headp;
+      int ret_count;
+
+      entp = (net_hash_entry_t) 0;
+      ret_count = bpf_do_filter (infp,
+                                msg->packet + sizeof (struct packet_header),
+                                msg->net_rcv_msg_packet_count, msg->header,
+                                sizeof (struct ethhdr), &hash_headp, &entp);
+      if (entp == (net_hash_entry_t) 0)
+       dest = infp->rcv_port;
+      else
+       dest = entp->rcv_port;
+
+      if (ret_count) 
+       {
+         debug ("before delivering the packet\n");
+         msg->msg_hdr.msgh_remote_port = dest;
+         err = mach_msg ((mach_msg_header_t *)msg,
+                         MACH_SEND_MSG|MACH_SEND_TIMEOUT,
+                         msg->msg_hdr.msgh_size, 0, MACH_PORT_NULL,
+                         0, MACH_PORT_NULL);
+         if (err != MACH_MSG_SUCCESS)
+           {
+             mach_port_deallocate(mach_task_self (),
+                                  ((mach_msg_header_t 
*)msg)->msgh_remote_port);
+             error (0, err, "mach_msg");
+             return -1;
+           }
+         debug ("after delivering the packet\n");
+       }
+    }
+  FILTER_ITERATE_END
+
+    return 0;
+}
+
diff --git a/eth-multiplexer/vdev.h b/eth-multiplexer/vdev.h
new file mode 100644
index 0000000..c869678
--- /dev/null
+++ b/eth-multiplexer/vdev.h
@@ -0,0 +1,79 @@
+/*
+   Copyright (C) 2008 Free Software Foundation, Inc.
+   Written by Zheng Da.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA. */
+
+#ifndef VDEV_H
+#define VDEV_H
+
+#include <net/if.h>
+
+#include <hurd.h>
+#include <mach.h>
+#include <hurd/ports.h>
+#include <device/net_status.h>
+
+#include <bpf_impl.h>
+
+#include "queue.h"
+#include "util.h"
+
+#define MAX_SERVERS 10
+#define ETH_MTU 1500
+
+struct vether_device
+{
+  /* The ports used by the socket server to send packets to the interface. */
+  struct port_info dev_pi;
+  mach_port_t dev_port;
+
+  char name[IFNAMSIZ];
+
+  short if_header_size;
+  short if_mtu;
+  short if_header_format;
+  short if_address_size;
+  short if_flags;
+  char if_address[ETH_ALEN];
+
+  struct vether_device *next;
+  struct vether_device **pprev;
+
+  if_filter_list_t port_list;
+};
+
+typedef int (*dev_act_func) (struct vether_device *);
+
+int serv_connect (mach_port_t port);
+int serv_disconnect ();
+struct vether_device *lookup_dev_by_name (char *name);
+int remove_dead_port_from_dev (mach_port_t dead_port);
+struct vether_device *add_vdev (char *name, int size,
+                               struct port_class *class,
+                               struct port_bucket *bucket);
+void destroy_vdev (void *port);
+int has_vdev ();
+int deliver_msg (struct net_rcv_msg *msg, struct vether_device *vdev);
+int deliver_pack (char *data, int datalen, struct vether_device *vdev);
+boolean_t all_dev_close ();
+int broadcast_pack (char *data, int datalen, struct vether_device *from_vdev);
+int broadcast_msg (struct net_rcv_msg *msg);
+int get_dev_num ();
+int foreach_dev_do (dev_act_func func);
+
+#endif
diff --git a/libdiskfs/file-set-trans.c b/libdiskfs/file-set-trans.c
index 3798001..49303e7 100644
--- a/libdiskfs/file-set-trans.c
+++ b/libdiskfs/file-set-trans.c
@@ -43,7 +43,7 @@ diskfs_S_file_set_translator (struct protid *cred,
   if ((passive_flags & FS_TRANS_SET) && diskfs_check_readonly ())
     return EROFS;
 
-  if (passive && passive[passivelen - 1])
+  if (passivelen && passive[passivelen - 1])
     return EINVAL;
 
   np = cred->po->np;
diff --git a/libiohelp/iouser-create.c b/libiohelp/iouser-create.c
index f1dd2f0..980c8a1 100644
--- a/libiohelp/iouser-create.c
+++ b/libiohelp/iouser-create.c
@@ -33,8 +33,9 @@ iohelp_create_iouser (struct iouser **user, struct idvec 
*uids,
   return 0;
 }
 
-#define E(err)                         \
+#define E(err_)                                \
        do {                            \
+         error_t err = err_;           \
          if (err)                      \
            {                           \
              *user = 0;                \
diff --git a/libnetfs/file-set-translator.c b/libnetfs/file-set-translator.c
index 4c29d95..b107ccd 100644
--- a/libnetfs/file-set-translator.c
+++ b/libnetfs/file-set-translator.c
@@ -39,7 +39,7 @@ netfs_S_file_set_translator (struct protid *user,
   if (!(passive_flags & FS_TRANS_SET) && !(active_flags & FS_TRANS_SET))
     return 0;
 
-  if (passive && passive[passivelen - 1])
+  if (passivelen && passive[passivelen - 1])
     return EINVAL;
 
   np = user->po->np;
diff --git a/pfinet/linux-src/net/ipv4/ipmr.c b/pfinet/linux-src/net/ipv4/ipmr.c
index cd51cd9..08d1a36 100644
--- a/pfinet/linux-src/net/ipv4/ipmr.c
+++ b/pfinet/linux-src/net/ipv4/ipmr.c
@@ -332,7 +332,7 @@ static void ipmr_cache_delete(struct mfc_cache *cache)
                        kfree_skb(skb);
                }
        }
-       kfree_s(cache,sizeof(cache));
+       kfree_s(cache,sizeof(*cache));
 }
 
 /*
diff --git a/procfs/Makefile b/procfs/Makefile
index c69cb20..5c51c1d 100644
--- a/procfs/Makefile
+++ b/procfs/Makefile
@@ -1,7 +1,7 @@
 TARGET = procfs
 OBJS = procfs.o netfs.o procfs_dir.o \
        process.o proclist.o rootdir.o dircat.o main.o
-LIBS = -lnetfs -lps -lfshelp
+LIBS = -lnetfs -lps -lfshelp -lpthread
 
 CC = gcc
 CFLAGS = -Wall -g
@@ -12,7 +12,7 @@ ifdef PROFILE
 CFLAGS= -g -pg
 CPPFLAGS= -DPROFILE
 LDFLAGS= -static
-LIBS= -lnetfs -lfshelp -liohelp -lps -lports -lthreads -lihash -lshouldbeinlibc
+LIBS= -lnetfs -lfshelp -liohelp -lps -lports -lpthread -lihash -lshouldbeinlibc
 endif
 
 CPPFLAGS += -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64
diff --git a/procfs/main.c b/procfs/main.c
index 1b19c01..90b3e92 100644
--- a/procfs/main.c
+++ b/procfs/main.c
@@ -37,6 +37,10 @@ pid_t opt_fake_self;
 pid_t opt_kernel_pid;
 uid_t opt_anon_owner;
 
+#define NODEV_KEY  -1 /* <= 0, so no short option. */
+#define NOEXEC_KEY -2 /* Likewise. */
+#define NOSUID_KEY -3 /* Likewise. */
+
 static error_t
 argp_parser (int key, char *arg, struct argp_state *state)
 {
@@ -104,6 +108,18 @@ argp_parser (int key, char *arg, struct argp_state *state)
       else
        opt_anon_owner = v;
       break;
+
+    case NODEV_KEY:
+      /* Ignored for compatibility with Linux' procfs. */
+      ;;
+
+    case NOEXEC_KEY:
+      /* Ignored for compatibility with Linux' procfs. */
+      ;;
+
+    case NOSUID_KEY:
+      /* Ignored for compatibility with Linux' procfs. */
+      ;;
   }
 
   return 0;
@@ -136,6 +152,12 @@ struct argp argp = {
        "Be aware that USER will be granted access to the environment and "
        "other sensitive information about the processes in question.  "
        "(default: use uid 0)" },
+    { "nodev", NODEV_KEY, NULL, 0,
+       "Ignored for compatibility with Linux' procfs." },
+    { "noexec", NOEXEC_KEY, NULL, 0,
+       "Ignored for compatibility with Linux' procfs." },
+    { "nosuid", NOSUID_KEY, NULL, 0,
+       "Ignored for compatibility with Linux' procfs." },
     {}
   },
   .parser = argp_parser,
diff --git a/procfs/netfs.c b/procfs/netfs.c
index c139d11..276c57c 100644
--- a/procfs/netfs.c
+++ b/procfs/netfs.c
@@ -230,6 +230,17 @@ void netfs_node_norefs (struct node *np)
   pthread_spin_lock (&netfs_node_refcnt_lock);
 }
 
+/* The user may define this function (but should define it together
+   with netfs_set_translator).  For locked node NODE with S_IPTRANS
+   set in its mode, look up the name of its translator.  Store the
+   name into newly malloced storage, and return it in *ARGZ; set
+   *ARGZ_LEN to the total length.  */
+error_t netfs_get_translator (struct node *np, char **argz,
+                             size_t *argz_len)
+{
+  return procfs_get_translator (np, argz, argz_len);
+}
+
 
 /* Libnetfs callbacks managed with libfshelp. */
 
diff --git a/procfs/procfs.c b/procfs/procfs.c
index ae5a676..cae4a51 100644
--- a/procfs/procfs.c
+++ b/procfs/procfs.c
@@ -19,6 +19,7 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 #include <fcntl.h>
 #include <mach.h>
 #include <hurd/netfs.h>
@@ -76,6 +77,9 @@ struct node *procfs_make_node (const struct procfs_node_ops 
*ops, void *hook)
   else
     np->nn_stat.st_mode = S_IFREG | 0444;
 
+  np->nn_stat.st_uid = getuid ();
+  np->nn_stat.st_gid = getgid ();
+
   return np;
 
 fail:
@@ -93,7 +97,7 @@ void procfs_node_chown (struct node *np, uid_t owner)
 
 void procfs_node_chmod (struct node *np, mode_t mode)
 {
-  np->nn_stat.st_mode = (np->nn_stat.st_mode & S_IFMT) | mode;
+  np->nn_stat.st_mode = (np->nn_stat.st_mode & ~ALLPERMS) | mode;
   np->nn_translated = np->nn_stat.st_mode;
 }
 
@@ -201,3 +205,15 @@ void procfs_cleanup (struct node *np)
 
   free (np->nn);
 }
+
+error_t procfs_get_translator (struct node *np,
+                               char **argz,
+                               size_t *argz_len)
+{
+  if (np->nn->ops->get_translator)
+    return np->nn->ops->get_translator (np->nn->hook, argz, argz_len);
+
+  *argz = NULL;
+  *argz_len = 0;
+  return 0;
+}
diff --git a/procfs/procfs.h b/procfs/procfs.h
index 64782ec..d04bbad 100644
--- a/procfs/procfs.h
+++ b/procfs/procfs.h
@@ -51,6 +51,9 @@ struct procfs_node_ops
 
   /* Destroy this node.  */
   void (*cleanup) (void *hook);
+
+  /* Get the passive translator record.  */
+  error_t (*get_translator) (void *hook, char **argz, size_t *argz_len);
 };
 
 /* These helper functions can be used as procfs_node_ops.cleanup_contents. */
@@ -91,3 +94,6 @@ error_t procfs_get_contents (struct node *np, char **data, 
ssize_t *data_len);
 error_t procfs_lookup (struct node *np, const char *name, struct node **npp);
 void procfs_cleanup (struct node *np);
 
+/* Get the passive translator record if any.  */
+error_t procfs_get_translator (struct node *np, char **argz, size_t *argz_len);
+
diff --git a/procfs/rootdir.c b/procfs/rootdir.c
index 31e2d8c..f234dd0 100644
--- a/procfs/rootdir.c
+++ b/procfs/rootdir.c
@@ -300,7 +300,7 @@ rootdir_gc_meminfo (void *hook, char **contents, ssize_t 
*contents_len)
       ,
       (long unsigned) hbi.memory_size / 1024,
       (long unsigned) vmstats.free_count * PAGE_SIZE / 1024,
-      0,
+      0UL,
       (long unsigned) cache_stats.cache_count * PAGE_SIZE / 1024,
       (long unsigned) vmstats.active_count * PAGE_SIZE / 1024,
       (long unsigned) vmstats.inactive_count * PAGE_SIZE / 1024,
@@ -392,7 +392,7 @@ out:
 }
 
 static int
-rootdir_fakeself_exists ()
+rootdir_fakeself_exists (void *dir_hook, const void *entry_hook)
 {
   return opt_fake_self >= 0;
 }

-- 
Debian GNU Hurd packaging



reply via email to

[Prev in Thread] Current Thread [Next in Thread]