commit-hurd
[Top][All Lists]
Advanced

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

[hurd] 03/06: New upstream snapshot


From: Samuel Thibault
Subject: [hurd] 03/06: New upstream snapshot
Date: Tue, 09 Jan 2018 01:35:28 +0000

This is an automated email from the git hooks/post-receive script.

sthibault pushed a commit to branch master
in repository hurd.

commit f757c22a11973834164b5f266a7a1301f88446d2
Author: Samuel Thibault <address@hidden>
Date:   Tue Jan 9 00:16:43 2018 +0000

    New upstream snapshot
---
 Makefile                             |   4 +
 TODO                                 |   2 +-
 config.make.in                       |   7 +
 configure.ac                         |   7 +
 doc/hurd.texi                        |  22 +-
 exec/elfcore.c                       |  13 +
 exec/exec.c                          | 146 ++++++-
 exec/hashexec.c                      | 129 ++++---
 exec/priv.h                          |   4 +-
 hurd/exec.defs                       |  19 +-
 hurd/fs.defs                         |  29 +-
 hurd/hurd_types.h                    |   9 +-
 hurd/process.defs                    |  21 +
 hurd/process_reply.defs              |   6 +
 hurd/process_request.defs            |   6 +
 libdiskfs/boot-start.c               |   2 +-
 libdiskfs/file-exec.c                |  79 +++-
 libfshelp/get-identity.c             |  49 ++-
 libfshelp/start-translator-long.c    |  23 +-
 libihash/Makefile                    |   1 +
 libihash/ihash.c                     |   2 +
 libnetfs/file-exec.c                 |  71 +++-
 libps/procstat.c                     |  17 +
 libps/ps.h                           |  11 +-
 libps/spec.c                         |  10 +
 libtrivfs/file-exec.c                |  28 +-
 lwip/Makefile                        |  50 +++
 lwip/iioctl-ops.c                    | 410 ++++++++++++++++++++
 lwip/io-ops.c                        | 554 +++++++++++++++++++++++++++
 lwip/lwip-hurd.h                     | 102 +++++
 lwip/lwip-util.c                     | 343 +++++++++++++++++
 lwip/lwip-util.h                     |  41 ++
 lwip/main.c                          | 272 +++++++++++++
 lwip/mig-decls.h                     |  68 ++++
 lwip/mig-mutate.h                    |  44 +++
 lwip/options.c                       | 342 +++++++++++++++++
 lwip/options.h                       |  81 ++++
 lwip/pfinet-ops.c                    | 113 ++++++
 lwip/port-objs.c                     | 144 +++++++
 lwip/port/include/netif/hurdethif.h  |  39 ++
 lwip/port/include/netif/hurdloopif.h |  36 ++
 lwip/port/include/netif/hurdtunif.h  |  65 ++++
 lwip/port/include/netif/ifcommon.h   |  60 +++
 lwip/port/netif/hurdethif.c          | 573 ++++++++++++++++++++++++++++
 lwip/port/netif/hurdloopif.c         | 112 ++++++
 lwip/port/netif/hurdtunif.c          | 721 +++++++++++++++++++++++++++++++++++
 lwip/port/netif/ifcommon.c           | 121 ++++++
 lwip/socket-ops.c                    | 451 ++++++++++++++++++++++
 lwip/startup-ops.c                   |  39 ++
 lwip/startup.c                       |  69 ++++
 lwip/startup.h                       |  26 ++
 proc/info.c                          |  41 ++
 proc/mgt.c                           |  22 ++
 proc/proc.h                          |   2 +
 procfs/process.c                     |  32 ++
 startup/startup.c                    |  94 +++--
 trans/fakeroot.c                     |  97 +++--
 utils/fakeauth.c                     |   9 +-
 utils/login.c                        |  25 +-
 utils/rpctrace.c                     |   9 +-
 utils/shd.c                          |   9 +-
 61 files changed, 5759 insertions(+), 174 deletions(-)

diff --git a/Makefile b/Makefile
index 119f130..2d9d3f8 100644
--- a/Makefile
+++ b/Makefile
@@ -50,6 +50,10 @@ ifeq ($(HAVE_SUN_RPC),yes)
 prog-subdirs += nfs nfsd
 endif
 
+ifeq ($(HAVE_LIBLWIP),yes)
+prog-subdirs += lwip
+endif
+
 # Other directories
 other-subdirs = hurd doc config release include
 
diff --git a/TODO b/TODO
index de2a199..4361f92 100644
--- a/TODO
+++ b/TODO
@@ -131,7 +131,7 @@ See `tasks', the exported task list.
 
 ** libtrivfs
 *** Allow for read/write/exec to be passed down.
-*** Implement file_exec when appropriate. !!
+*** Implement file_exec_paths when appropriate. !!
 *** Provide for the visible owner, etc., to be held in command-line args
     instead of the underlying node, when it's important. !!
 
diff --git a/config.make.in b/config.make.in
index dfbf0c1..7b62e85 100644
--- a/config.make.in
+++ b/config.make.in
@@ -99,6 +99,13 @@ HAVE_SUN_RPC = @HAVE_SUN_RPC@
 # Whether we found libgcrypt.
 HAVE_LIBGCRYPT = @HAVE_LIBGCRYPT@
 
+# Whether we found liblwip.
+HAVE_LIBLWIP = @HAVE_LIBLWIP@
+
+# How to compile and link against liblwip.
+liblwip_CFLAGS = @liblwip_CFLAGS@
+liblwip_LIBS = @liblwip_LIBS@
+
 # Installation tools.
 INSTALL = @INSTALL@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
diff --git a/configure.ac b/configure.ac
index 54aa72b..89107a9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -162,6 +162,8 @@ else
   VERSIONING=no
 fi
 AC_SUBST(VERSIONING)
+# Check if libc contains these functions.
+AC_CHECK_FUNCS(file_exec_paths exec_exec_paths _hurd_exec_paths)
 
 
 # From glibc HEAD, 2007-11-07.
@@ -344,6 +346,11 @@ AC_SUBST([libblkid_CFLAGS])
 AM_PATH_LIBGCRYPT("1:1.6.0",[HAVE_LIBGCRYPT=yes], [HAVE_LIBGCRYPT=no])
 AC_SUBST([HAVE_LIBGCRYPT])
 
+PKG_CHECK_MODULES([liblwip], [lwip], [HAVE_LIBLWIP=yes], [HAVE_LIBLWIP=no])
+AC_SUBST([HAVE_LIBLWIP])
+AC_SUBST([liblwip_CFLAGS])
+AC_SUBST([liblwip_LIBS])
+
 AC_CONFIG_FILES([config.make ${makefiles}])
 AC_OUTPUT
 
diff --git a/doc/hurd.texi b/doc/hurd.texi
index 8428a77..e77894a 100644
--- a/doc/hurd.texi
+++ b/doc/hurd.texi
@@ -102,7 +102,7 @@ This file documents the GNU Hurd kernel component.  This 
edition of the
 documentation was last updated for version @value{VERSION} of the Hurd.
 
 Copyright @copyright{} 1994, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
-2004, 2005, 2007, 2008, 2009 Free Software Foundation, Inc.
+2004, 2005, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
 
 @quotation
 Permission is granted to make and distribute verbatim copies of
@@ -2736,10 +2736,10 @@ write the file.
 @node Program Execution
 @subsection Program Execution
 
address@hidden file_exec
address@hidden file_exec_paths
 Execution of programs on the Hurd is done through fileservers with the
address@hidden RPC.  The fileserver is expected to verify that the
-user is allowed to execute the file, make whatever modifications to the
address@hidden RPC.  The fileserver is expected to verify that
+the user is allowed to execute the file, make whatever modifications to the
 ports are necessary for setuid execution, and then invoke the standard
 execserver found on @file{/servers/exec}.
 
@@ -2751,13 +2751,13 @@ The file must be opened for execution; if it is not, 
@code{EBADF} should
 be returned.  In addition, at least one of the execute bits must be on.  A
 failure of this check should result in @code{EACCES}---not
 @code{ENOEXEC}.  It is not proper for the fileserver ever to respond to
-the @code{file_exec} RPC with @code{ENOEXEC}.
+the @code{file_exec_paths} RPC with @code{ENOEXEC}.
 
 If either the setuid or setgid bits are set, the server needs to
 construct a new authentication handle with the additional new ID's.
-Then all the ports passed to @code{file_exec} need to be reauthenticated
-with the new handle.  If the fileserver is unable to make the new
-authentication handle (for example, because it is not running as root)
+Then all the ports passed to @code{file_exec_paths} need to be
+reauthenticated with the new handle.  If the fileserver is unable to make the
+new authentication handle (for example, because it is not running as root)
 it is not acceptable to return an error; in such a case the server
 should simply silently fail to implement the setuid/setgid semantics.
 
@@ -2765,14 +2765,14 @@ If the setuid/setgid transformation adds a new uid or 
gid to the user's
 authentication handle that was not previously present (as opposed to
 merely reordering them), then the @code{EXEC_SECURE} and
 @code{EXEC_NEWTASK} flags should both be added in the call to
address@hidden
address@hidden
 
 The server then needs to open a new port onto the executed file which
 will not share any file pointers with the port the user passed in,
 opened with @code{O_READ}.  Finally, all the information (mutated
 appropriately for setuid/setgid) should be sent to the execserver with
address@hidden  Whatever error code @code{exec_exec} returns should
-returned to the caller of @code{file_exec}.
address@hidden  Whatever error code @code{exec_exec_paths}
+returns should be returned to the caller of @code{file_exec_paths}.
 
 @node File Locking
 @subsection File Locking
diff --git a/exec/elfcore.c b/exec/elfcore.c
index 12ecf34..2dd499b 100644
--- a/exec/elfcore.c
+++ b/exec/elfcore.c
@@ -331,6 +331,7 @@ dump_core (task_t task, file_t file, off_t corelimit,
   {
     DEFINE_NOTE (psinfo_t) psinfo;
     DEFINE_NOTE (pstatus_t) pstatus;
+    DEFINE_NOTE (ElfW(auxv_t)) at_entry;
     int flags = PI_FETCH_TASKINFO | PI_FETCH_THREADS | PI_FETCH_THREAD_BASIC;
     char *waits = 0;
     mach_msg_type_number_t num_waits = 0;
@@ -410,6 +411,18 @@ dump_core (task_t task, file_t file, off_t corelimit,
            err = proc_get_arg_locations (proc,
                                          &psinfo.data.pr_argv,
                                          &psinfo.data.pr_envp);
+           if (err == 0)
+             {
+               /* Write position of executable.  */
+               vm_address_t addr;
+               err = proc_get_entry (proc, &addr);
+               if (err == 0)
+                 {
+                   at_entry.data.a_type = AT_ENTRY;
+                   at_entry.data.a_un.a_val = addr;
+                   err = WRITE_NOTE (NT_AUXV, at_entry);
+                 }
+             }
            mach_port_deallocate (mach_task_self (), proc);
          }
        {
diff --git a/exec/exec.c b/exec/exec.c
index d78c54c..e959091 100644
--- a/exec/exec.c
+++ b/exec/exec.c
@@ -1,6 +1,6 @@
 /* GNU Hurd standard exec server.
-   Copyright (C) 1992,93,94,95,96,98,99,2000,01,02,04
-       Free Software Foundation, Inc.
+   Copyright (C) 1992 ,1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+   2002, 2004, 2010 Free Software Foundation, Inc.
    Written by Roland McGrath.
 
    Can exec ELF format directly.
@@ -793,6 +793,8 @@ static error_t
 do_exec (file_t file,
         task_t oldtask,
         int flags,
+        char *path,
+        char *abspath,
         char *argv, mach_msg_type_number_t argvlen, boolean_t argv_copy,
         char *envp, mach_msg_type_number_t envplen, boolean_t envp_copy,
         mach_port_t *dtable, mach_msg_type_number_t dtablesize,
@@ -852,7 +854,7 @@ do_exec (file_t file,
     {
       /* Check for a #! executable file.  */
       check_hashbang (&e,
-                     file, oldtask, flags,
+                     file, oldtask, flags, path,
                      argv, argvlen, argv_copy,
                      envp, envplen, envp_copy,
                      dtable, dtablesize, dtable_copy,
@@ -949,7 +951,7 @@ do_exec (file_t file,
     secure = (flags & EXEC_SECURE);
     defaults = (flags & EXEC_DEFAULTS);
 
-    /* Now record the big blocks of data we shuffle around unchanged.
+    /* Now record the big blocks of data we shuffle around.
        Whatever arrived inline, we must allocate space for so it can
        survive after this RPC returns.  */
 
@@ -960,11 +962,90 @@ do_exec (file_t file,
       goto stdout;
     boot->argv = argv;
     boot->argvlen = argvlen;
-    envp = servercopy (envp, envplen, envp_copy, &e.error);
-    if (e.error)
-      goto stdout;
+
+    if (abspath && abspath[0] == '/')
+      {
+       /* Explicit absolute filename, put its dirname in the LD_ORIGIN_PATH
+          environment variable for $ORIGIN rpath expansion. */
+       const char *end = strrchr (abspath, '/');
+       size_t pathlen;
+       const char ld_origin_s[] = "\0LD_ORIGIN_PATH=";
+       const char *existing;
+       size_t existing_len = 0;
+       size_t new_envplen;
+       char *new_envp;
+
+       /* Drop trailing slashes.  */
+       while (end > abspath && end[-1] == '/')
+         end--;
+
+       if (end == abspath)
+         /* Root, keep explicit heading/trailing slash.   */
+         end++;
+
+       pathlen = end - abspath;
+
+       if (memcmp (envp, ld_origin_s + 1, sizeof (ld_origin_s) - 2) == 0)
+         /* Existing variable at the beginning of envp.  */
+         existing = envp - 1;
+       else
+         /* Look for the definition.  */
+         existing = memmem (envp, envplen, ld_origin_s, sizeof (ld_origin_s) - 
1);
+
+       if (existing)
+         {
+           /* Definition already exists, just replace the content.  */
+           existing += sizeof (ld_origin_s) - 1;
+           existing_len = strnlen (existing, envplen - (existing - envp));
+
+           /* Allocate room for the new content.  */
+           new_envplen = envplen - existing_len + pathlen;
+           new_envp = mmap (0, new_envplen,
+                            PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+           if (new_envp == MAP_FAILED)
+             {
+               e.error = errno;
+               goto stdout;
+             }
+
+           /* And copy.  */
+           memcpy (new_envp, envp, existing - envp);
+           memcpy (new_envp + (existing - envp), abspath, pathlen);
+           memcpy (new_envp + (existing - envp) + pathlen,
+                   existing + existing_len,
+                   envplen - ((existing - envp) + existing_len));
+         }
+       else
+         {
+           /* No existing definition, prepend one.  */
+           new_envplen = sizeof (ld_origin_s) - 1 + pathlen + envplen;
+           new_envp = mmap (0, new_envplen,
+                            PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+
+           memcpy (new_envp, ld_origin_s + 1, sizeof (ld_origin_s) - 2);
+           memcpy (new_envp + sizeof (ld_origin_s) - 2, abspath, pathlen);
+           new_envp [sizeof (ld_origin_s) - 2 + pathlen] = 0;
+           memcpy (new_envp + sizeof (ld_origin_s) - 2 + pathlen + 1, envp, 
envplen);
+         }
+
+       if (! envp_copy)
+         /* Deallocate original environment */
+         munmap (envp, envplen);
+
+       envp = new_envp;
+       envplen = new_envplen;
+      }
+    else
+      {
+       /* No explicit abspath, just copy the existing environment */
+       envp = servercopy (envp, envplen, envp_copy, &e.error);
+       if (e.error)
+         goto stdout;
+      }
+
     boot->envp = envp;
     boot->envplen = envplen;
+
     dtable = servercopy (dtable, dtablesize * sizeof (mach_port_t),
                         dtable_copy, &e.error);
     if (e.error)
@@ -1233,7 +1314,15 @@ do_exec (file_t file,
       if (e.error)
        goto out;
 
+      if (abspath)
+       proc_set_exe (boot->portarray[INIT_PORT_PROC], abspath);
+
       set_name (newtask, argv, pid);
+
+      e.error = proc_set_entry (boot->portarray[INIT_PORT_PROC],
+                               e.entry);
+      if (e.error)
+       goto out;
     }
   else
     set_name (newtask, argv, 0);
@@ -1435,6 +1524,7 @@ do_exec (file_t file,
   return e.error;
 }
 
+/* Deprecated.  */
 kern_return_t
 S_exec_exec (struct trivfs_protid *protid,
             file_t file,
@@ -1451,13 +1541,53 @@ S_exec_exec (struct trivfs_protid *protid,
             mach_port_t *deallocnames, mach_msg_type_number_t ndeallocnames,
             mach_port_t *destroynames, mach_msg_type_number_t ndestroynames)
 {
+  return S_exec_exec_paths (protid,
+                               file,
+                               oldtask,
+                               flags,
+                               "",
+                               "",
+                               argv, argvlen, argv_copy,
+                               envp, envplen, envp_copy,
+                               dtable, dtablesize,
+                               dtable_copy,
+                               portarray, nports,
+                               portarray_copy,
+                               intarray, nints,
+                               intarray_copy,
+                               deallocnames, ndeallocnames,
+                               destroynames, ndestroynames);
+}
+
+kern_return_t
+S_exec_exec_paths (struct trivfs_protid *protid,
+                      file_t file,
+                      task_t oldtask,
+                      int flags,
+                      char *path,
+                      char *abspath,
+                      char *argv, mach_msg_type_number_t argvlen,
+                      boolean_t argv_copy,
+                      char *envp, mach_msg_type_number_t envplen,
+                      boolean_t envp_copy,
+                      mach_port_t *dtable, mach_msg_type_number_t dtablesize,
+                      boolean_t dtable_copy,
+                      mach_port_t *portarray, mach_msg_type_number_t nports,
+                      boolean_t portarray_copy,
+                      int *intarray, mach_msg_type_number_t nints,
+                      boolean_t intarray_copy,
+                      mach_port_t *deallocnames,
+                      mach_msg_type_number_t ndeallocnames,
+                      mach_port_t *destroynames,
+                      mach_msg_type_number_t ndestroynames)
+{
   if (! protid)
     return EOPNOTSUPP;
 
   /* There were no user-specified exec servers,
      or none of them could be found.  */
 
-  return do_exec (file, oldtask, flags,
+  return do_exec (file, oldtask, flags, path, abspath,
                  argv, argvlen, argv_copy,
                  envp, envplen, envp_copy,
                  dtable, dtablesize, dtable_copy,
diff --git a/exec/hashexec.c b/exec/hashexec.c
index 68b4881..71c0238 100644
--- a/exec/hashexec.c
+++ b/exec/hashexec.c
@@ -1,5 +1,6 @@
 /* GNU Hurd standard exec server, #! script execution support.
-   Copyright (C) 1995,96,97,98,99,2000,02 Free Software Foundation, Inc.
+   Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2002, 2010
+   Free Software Foundation, Inc.
    Written by Roland McGrath.
 
    This file is part of the GNU Hurd.
@@ -35,6 +36,7 @@ check_hashbang (struct execdata *e,
                file_t file,
                task_t oldtask,
                int flags,
+               char *file_name_exec,
                char *argv, u_int argvlen, boolean_t argv_copy,
                char *envp, u_int envplen, boolean_t envp_copy,
                mach_port_t *dtable, u_int dtablesize, boolean_t dtable_copy,
@@ -227,10 +229,13 @@ check_hashbang (struct execdata *e,
            file_name = NULL;
          else if (! (flags & EXEC_SECURE))
            {
-             /* Try to figure out the file's name.  We guess that if ARGV[0]
-                contains a slash, it might be the name of the file; and that
-                if it contains no slash, looking for files named by ARGV[0] in
-                the `PATH' environment variable might find it.  */
+             /* Try to figure out the file's name.  If FILE_NAME_EXEC
+                is not NULL and not the empty string, then it's the
+                file's name.  Otherwise we guess that if ARGV[0]
+                contains a slash, it might be the name of the file;
+                and that if it contains no slash, looking for files
+                named by ARGV[0] in the `PATH' environment variable
+                might find it.  */
 
              error_t error;
              char *name;
@@ -266,49 +271,59 @@ check_hashbang (struct execdata *e,
                  return err;
                }
 
-             error = io_identity (file, &fileid, &filefsid, &fileno);
-             if (error)
-               goto out;
-             mach_port_deallocate (mach_task_self (), filefsid);
-
-             if (memchr (argv, '\0', argvlen) == NULL)
-               {
-                 name = alloca (argvlen + 1);
-                 memcpy (name, argv, argvlen);
-                 name[argvlen] = '\0';
-               }
+             if (file_name_exec && file_name_exec[0] != '\0')
+               name = file_name_exec;
              else
-               name = argv;
-
-             if (strchr (name, '/') != NULL)
-               error = lookup (name, 0, &name_file);
-             else if ((error = hurd_catch_signal
-                       (sigmask (SIGBUS) | sigmask (SIGSEGV),
-                        (vm_address_t) envp, (vm_address_t) envp + envplen,
-                        &search_path, SIG_ERR)))
-               name_file = MACH_PORT_NULL;
-
-             if (!error && name_file != MACH_PORT_NULL)
                {
-                 mach_port_t id, fsid;
-                 ino_t ino;
-                 error = io_identity (name_file, &id, &fsid, &ino);
-                 mach_port_deallocate (mach_task_self (), name_file);
-                 if (!error)
+                 /* Try to locate the file.  */
+                 error = io_identity (file, &fileid, &filefsid, &fileno);
+                 if (error)
+                   goto out;
+                 mach_port_deallocate (mach_task_self (), filefsid);
+
+                 if (memchr (argv, '\0', argvlen) == NULL)
                    {
-                     mach_port_deallocate (mach_task_self (), fsid);
-                     mach_port_deallocate (mach_task_self (), id);
+                     name = alloca (argvlen + 1);
+                     memcpy (name, argv, argvlen);
+                     name[argvlen] = '\0';
                    }
-                 if (!error && id == fileid)
+                 else
+                   name = argv;
+
+                 if (strchr (name, '/') != NULL)
+                   error = lookup (name, 0, &name_file);
+                 else if ((error = hurd_catch_signal
+                           (sigmask (SIGBUS) | sigmask (SIGSEGV),
+                            (vm_address_t) envp, (vm_address_t) envp + envplen,
+                            &search_path, SIG_ERR)))
+                   name_file = MACH_PORT_NULL;
+
+                 /* See whether we found the right file.  */
+                 if (!error && name_file != MACH_PORT_NULL)
                    {
-                     file_name = name;
-                     free_file_name = free_name;
+                     mach_port_t id, fsid;
+                     ino_t ino;
+                     error = io_identity (name_file, &id, &fsid, &ino);
+                     mach_port_deallocate (mach_task_self (), name_file);
+                     if (!error)
+                       {
+                         mach_port_deallocate (mach_task_self (), fsid);
+                         mach_port_deallocate (mach_task_self (), id);
+                         if (id != fileid)
+                           error = 1;
+                       }
                    }
-                 else if (free_name)
-                   free (name);
+
+                 mach_port_deallocate (mach_task_self (), fileid);
                }
 
-             mach_port_deallocate (mach_task_self (), fileid);
+             if (!error)
+               {
+                 file_name = name;
+                 free_file_name = free_name;
+               }
+             else if (free_name)
+               free (name);
            }
 
          if (file_name == NULL)
@@ -417,16 +432,32 @@ check_hashbang (struct execdata *e,
     /* We cannot open the interpreter file to execute it.  Lose!  */
     return;
 
+#ifdef HAVE_FILE_EXEC_PATHS
   /* Execute the interpreter program.  */
-  e->error = file_exec (interp_file,
-                       oldtask, flags,
-                       new_argv, new_argvlen, envp, envplen,
-                       new_dtable ?: dtable, MACH_MSG_TYPE_COPY_SEND,
-                       new_dtable ? new_dtablesize : dtablesize,
-                       portarray, MACH_MSG_TYPE_COPY_SEND, nports,
-                       intarray, nints,
-                       deallocnames, ndeallocnames,
-                       destroynames, ndestroynames);
+  e->error = file_exec_paths (interp_file,
+                             oldtask, flags, interp, interp,
+                             new_argv, new_argvlen, envp, envplen,
+                             new_dtable ?: dtable,
+                             MACH_MSG_TYPE_COPY_SEND,
+                             new_dtable ? new_dtablesize : dtablesize,
+                             portarray, MACH_MSG_TYPE_COPY_SEND, nports,
+                             intarray, nints,
+                             deallocnames, ndeallocnames,
+                             destroynames, ndestroynames);
+  /* For backwards compatibility.  Just drop it when we kill file_exec.  */
+  if (e->error == MIG_BAD_ID)
+#endif
+    e->error = file_exec (interp_file,
+                         oldtask, flags,
+                         new_argv, new_argvlen, envp, envplen,
+                         new_dtable ?: dtable, MACH_MSG_TYPE_COPY_SEND,
+                         new_dtable ? new_dtablesize : dtablesize,
+                         portarray, MACH_MSG_TYPE_COPY_SEND, nports,
+                         intarray, nints,
+                         deallocnames, ndeallocnames,
+                         destroynames, ndestroynames);
+
+
   mach_port_deallocate (mach_task_self (), interp_file);
   munmap (new_argv, new_argvlen);
 
diff --git a/exec/priv.h b/exec/priv.h
index be08580..e84d815 100644
--- a/exec/priv.h
+++ b/exec/priv.h
@@ -1,5 +1,6 @@
 /* GNU Hurd standard exec server, private declarations.
-   Copyright (C) 1992,93,94,95,96,99,2000,02, 04 Free Software Foundation, Inc.
+   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1999, 2000, 2002, 2004,
+   2010 Free Software Foundation, Inc.
    Written by Roland McGrath.
 
 This file is part of the GNU Hurd.
@@ -135,6 +136,7 @@ void check_hashbang (struct execdata *e,
                     file_t file,
                     task_t oldtask,
                     int flags,
+                    char *filename,
                     char *argv, u_int argvlen, boolean_t argv_copy,
                     char *envp, u_int envplen, boolean_t envp_copy,
                     mach_port_t *dtable, u_int dtablesize,
diff --git a/hurd/exec.defs b/hurd/exec.defs
index 2888fb1..d879df1 100644
--- a/hurd/exec.defs
+++ b/hurd/exec.defs
@@ -1,5 +1,6 @@
 /* Interface definitions for the exec servers.
-   Copyright (C) 1991,92,93,94,95,2001 Free Software Foundation, Inc.
+   Copyright (C) 1991, 1992, 1993, 1994, 1995, 2001, 2010
+   Free Software Foundation, Inc.
 
 This file is part of the GNU Hurd.
 
@@ -29,6 +30,7 @@ EXEC_IMPORTS
 
 INTR_INTERFACE
 
+/* Deprecated: use exec_exec_paths instead.  */
 routine exec_exec (
        execserver: file_t;
        file: mach_port_send_t;
@@ -55,3 +57,18 @@ simpleroutine exec_setexecdata (
        execserver: file_t;
        ports: portarray_t SCP;
        ints: intarray_t SCP);
+
+routine exec_exec_paths (
+       execserver: file_t;
+       file: mach_port_send_t;
+       oldtask: task_t;
+       flags: int;
+       path: string_t;
+       abspath: string_t;
+       argv: data_t SCP;
+       envp: data_t SCP;
+       dtable: portarray_t SCP;
+       portarray: portarray_t SCP;
+       intarray: intarray_t SCP;
+       deallocnames: mach_port_name_array_t;
+       destroynames: mach_port_name_array_t);
diff --git a/hurd/fs.defs b/hurd/fs.defs
index 6c0b573..14800d9 100644
--- a/hurd/fs.defs
+++ b/hurd/fs.defs
@@ -1,5 +1,6 @@
 /* Definitions for the filesystem interface.
-   Copyright (C) 1994,95,96,97,98,99,2002 Free Software Foundation, Inc.
+   Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2002, 2010
+   Free Software Foundation, Inc.
 
 This file is part of the GNU Hurd.
 
@@ -35,7 +36,8 @@ INTR_INTERFACE
 /* Overlay a task with a file.  Necessary initialization, including
    authentication changes associated with set[ug]id execution must be
    handled by the filesystem.  Filesystems normally implement this by
-   using exec_newtask or exec_loadtask as appropriate.  */
+   using exec_newtask or exec_loadtask as appropriate.
+   Deprecated: use file_exec_paths instead.  */
 routine file_exec (
        exec_file: file_t;
        RPT
@@ -129,8 +131,8 @@ routine file_lock_stat (
    (regardless of the current open modes for this port).  ALLOWED is a
    bitwise OR of O_READ, O_WRITE, and O_EXEC.  This is not necessarily the
    same as what an open or exec would allow; O_EXEC is set for root even if
-   no executable bits are on (in which case file_exec should fail) and
-   O_WRITE is set a directory can be modified, even though it can't be
+   no executable bits are on (in which case file_exec_paths should fail)
+   and O_WRITE is set a directory can be modified, even though it can't be
    written directly.  */
 routine file_check_access (
        file: file_t;
@@ -355,3 +357,22 @@ routine file_reparent (
 
 skip;  /* Was: file_get_children.  */
 skip;  /* Was: file_get_source.  */
+
+/* Overlay a task with a file.  Necessary initialization, including
+   authentication changes associated with set[ug]id execution must be
+   handled by the filesystem.  Filesystems normally implement this by
+   using exec_newtask or exec_loadtask as appropriate.  */
+routine file_exec_paths (
+       exec_file: file_t;
+       RPT
+       exec_task: task_t;
+       flags: int;
+       path: string_t;
+       abspath: string_t;
+       argv: data_t SCP;
+       envp: data_t SCP;
+       fdarray: portarray_t SCP;
+       portarray: portarray_t SCP;
+       intarray: intarray_t SCP;
+       deallocnames: mach_port_name_array_t SCP;
+       destroynames: mach_port_name_array_t SCP);
diff --git a/hurd/hurd_types.h b/hurd/hurd_types.h
index 2960a29..9fa9ae2 100644
--- a/hurd/hurd_types.h
+++ b/hurd/hurd_types.h
@@ -1,5 +1,6 @@
 /* C declarations for Hurd server interfaces
-   Copyright (C) 1993,94,95,96,98,99,2001,02 Free Software Foundation, Inc.
+   Copyright (C) 1993, 1994, 1995, 1996, 1998, 1999, 2001, 2002,
+   2010 Free Software Foundation, Inc.
 
 This file is part of the GNU Hurd.
 
@@ -81,7 +82,7 @@ typedef struct timespec timespec_t;
 /* Many such parameters and flags are also defined in various libc
    headers. */
 
-/* Bits for flags in fs.defs:file_exec and exec.defs:exec_* calls: */
+/* Bits for flags in fs.defs:file_exec_paths and exec.defs:exec_* calls: */
 #define EXEC_NEWTASK   0x00000001 /* Create new task; kill old one.  */
 #define EXEC_SECURE    0x00000002 /* Use secure values of portarray, etc. */
 #define EXEC_DEFAULTS  0x00000004 /* Use defaults for unspecified ports.  */
@@ -350,7 +351,7 @@ typedef int *procinfo_t;
 #define FSTYPE_MEMFS   0x00000019 /* In-core filesystem */
 #define FSTYPE_ISO9660 0x0000001a /* ISO9660 */
 
-/* Standard port assignments for file_exec and exec_* */
+/* Standard port assignments for file_exec_paths and exec_* */
 enum
   {
     INIT_PORT_CWDIR,
@@ -364,7 +365,7 @@ enum
     INIT_PORT_MAX
   };
 
-/* Standard ints for file_exec and exec_* */
+/* Standard ints for file_exec_paths and exec_* */
 enum
   {
     INIT_UMASK,
diff --git a/hurd/process.defs b/hurd/process.defs
index d724e20..d515a46 100644
--- a/hurd/process.defs
+++ b/hurd/process.defs
@@ -413,3 +413,24 @@ routine proc_get_code (
 routine proc_make_task_namespace (
        process: process_t;
        notify: mach_port_send_t);
+
+/* Set the process binary executable path.  */
+routine proc_set_exe (
+       process: process_t;
+       path: string_t);
+
+/* Get the process binary executable path.  */
+routine proc_get_exe (
+       process: process_t;
+       which: pid_t;
+       out path: string_t);
+
+/* Set the locations of the executable entry.  */
+routine proc_set_entry (
+       process: process_t;
+       entry: vm_address_t);
+
+/* Fetch the locations of the executable entry.  */
+routine proc_get_entry (
+       process: process_t;
+       out entry: vm_address_t);
diff --git a/hurd/process_reply.defs b/hurd/process_reply.defs
index 2fc21c9..2eefcc3 100644
--- a/hurd/process_reply.defs
+++ b/hurd/process_reply.defs
@@ -194,3 +194,9 @@ simpleroutine proc_get_code_reply (
        end_code: vm_address_t);
 
 skip; /* proc_make_task_namespace  */
+
+skip; /* proc_set_exe */
+skip; /* proc_get_exe */
+
+skip; /* proc_set_entry */
+skip; /* proc_get_entry */
diff --git a/hurd/process_request.defs b/hurd/process_request.defs
index 20e0ee1..fc9127a 100644
--- a/hurd/process_request.defs
+++ b/hurd/process_request.defs
@@ -417,3 +417,9 @@ simpleroutine proc_make_task_namespace_request (
        process: process_t;
        ureplyport reply: reply_port_t;
        notify: mach_port_send_t);
+
+skip; /* proc_set_exe */
+skip; /* proc_get_exe */
+
+skip; /* proc_set_entry */
+skip; /* proc_get_entry */
diff --git a/libdiskfs/boot-start.c b/libdiskfs/boot-start.c
index 1b76ebd..0154854 100644
--- a/libdiskfs/boot-start.c
+++ b/libdiskfs/boot-start.c
@@ -202,7 +202,7 @@ diskfs_start_bootstrap ()
       diskfs_exec_ctl = MACH_PORT_NULL;        /* Not used after this.  */
     }
 
-  /* Cache the exec server port for file_exec to use.  */
+  /* Cache the exec server port for file_exec_paths to use.  */
   _hurd_port_set (&_diskfs_exec_portcell, diskfs_exec);
 
   if (_diskfs_boot_command)
diff --git a/libdiskfs/file-exec.c b/libdiskfs/file-exec.c
index e544b14..3e83016 100644
--- a/libdiskfs/file-exec.c
+++ b/libdiskfs/file-exec.c
@@ -1,5 +1,6 @@
-/* File execution (file_exec RPC) for diskfs servers, using exec server.
-   Copyright (C) 1993,94,95,96,97,98,2000,02 Free Software Foundation, Inc.
+/* File execution (file_exec_paths RPC) for diskfs servers, using exec server.
+   Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 2000, 2002,
+   2010 Free Software Foundation, Inc.
 
    This file is part of the GNU Hurd.
 
@@ -47,6 +48,41 @@ diskfs_S_file_exec (struct protid *cred,
                    mach_port_t *destroynames,
                    size_t destroynameslen)
 {
+  return diskfs_S_file_exec_paths (cred,
+                                  task,
+                                  flags,
+                                  "",
+                                  "",
+                                  argv, argvlen,
+                                  envp, envplen,
+                                  fds, fdslen,
+                                  portarray, portarraylen,
+                                  intarray, intarraylen,
+                                  deallocnames, deallocnameslen,
+                                  destroynames, destroynameslen);
+}
+
+kern_return_t
+diskfs_S_file_exec_paths (struct protid *cred,
+                         task_t task,
+                         int flags,
+                         char *path,
+                         char *abspath,
+                         char *argv,
+                         size_t argvlen,
+                         char *envp,
+                         size_t envplen,
+                         mach_port_t *fds,
+                         size_t fdslen,
+                         mach_port_t *portarray,
+                         size_t portarraylen,
+                         int *intarray,
+                         size_t intarraylen,
+                         mach_port_t *deallocnames,
+                         size_t deallocnameslen,
+                         mach_port_t *destroynames,
+                         size_t destroynameslen)
+{
   struct node *np;
   uid_t uid;
   gid_t gid;
@@ -136,9 +172,9 @@ diskfs_S_file_exec (struct protid *cred,
 
   if (! err)
     /* Make a new peropen for the exec server to access the file, since any
-       seeking the exec server might want to do should not affect the
-       original peropen on which file_exec was called.  (The new protid for
-       this peropen clones the caller's iouser to preserve the caller's
+       seeking the exec server might want to do should not affect the original
+       peropen on which file_exec_paths was called.  (The new protid
+       for this peropen clones the caller's iouser to preserve the caller's
        authentication credentials.)  The new peropen's openmodes must have
        O_READ even if the caller had only O_EXEC privilege, so the exec
        server can read the executable file.  We also include O_EXEC so that
@@ -159,14 +195,31 @@ diskfs_S_file_exec (struct protid *cred,
       do
        {
          right = ports_get_send_right (newpi);
-         err = exec_exec (execserver,
-                          right, MACH_MSG_TYPE_COPY_SEND,
-                          task, flags, argv, argvlen, envp, envplen,
-                          fds, MACH_MSG_TYPE_COPY_SEND, fdslen,
-                          portarray, MACH_MSG_TYPE_COPY_SEND, portarraylen,
-                          intarray, intarraylen,
-                          deallocnames, deallocnameslen,
-                          destroynames, destroynameslen);
+#ifdef HAVE_EXEC_EXEC_PATHS
+         err = exec_exec_paths (execserver,
+                                right, MACH_MSG_TYPE_COPY_SEND,
+                                task, flags, path, abspath,
+                                argv, argvlen, envp, envplen,
+                                fds, MACH_MSG_TYPE_COPY_SEND, fdslen,
+                                portarray, MACH_MSG_TYPE_COPY_SEND,
+                                portarraylen,
+                                intarray, intarraylen,
+                                deallocnames, deallocnameslen,
+                                destroynames, destroynameslen);
+         /* For backwards compatibility.  Just drop it when we kill
+            exec_exec.  */
+         if (err == MIG_BAD_ID)
+#endif
+           err = exec_exec (execserver,
+                            right, MACH_MSG_TYPE_COPY_SEND,
+                            task, flags, argv, argvlen, envp, envplen,
+                            fds, MACH_MSG_TYPE_COPY_SEND, fdslen,
+                            portarray, MACH_MSG_TYPE_COPY_SEND, portarraylen,
+                            intarray, intarraylen,
+                            deallocnames, deallocnameslen,
+                            destroynames, destroynameslen);
+
+
          mach_port_deallocate (mach_task_self (), right);
          if (err == MACH_SEND_INVALID_DEST)
            {
diff --git a/libfshelp/get-identity.c b/libfshelp/get-identity.c
index 17244de..ac1b4f0 100644
--- a/libfshelp/get-identity.c
+++ b/libfshelp/get-identity.c
@@ -32,17 +32,52 @@ struct idspec
 {
   struct port_info pi;
   hurd_ihash_locp_t id_hashloc;
+  ino_t cache_id;
 };
 
+/* The size of ino_t is larger than hurd_ihash_key_t on 32 bit
+   platforms.  We therefore have to use libihashs generalized key
+   interface.  */
+
+/* This is the mix function of fasthash, see
+   https://code.google.com/p/fast-hash/ for reference.  */
+#define mix_fasthash(h) ({              \
+        (h) ^= (h) >> 23;               \
+        (h) *= 0x2127599bf4325c37ULL;   \
+        (h) ^= (h) >> 47; })
+
+static hurd_ihash_key_t
+hash (const void *key)
+{
+  ino_t i;
+  i = *(ino_t *) key;
+  mix_fasthash (i);
+  return (hurd_ihash_key_t) i;
+}
+
+static int
+compare (const void *a, const void *b)
+{
+  return *(ino_t *) a == *(ino_t *) b;
+}
+
 static struct hurd_ihash idhash
-  = HURD_IHASH_INITIALIZER (offsetof (struct idspec, id_hashloc));
+  = HURD_IHASH_INITIALIZER_GKI (offsetof (struct idspec, id_hashloc),
+                                NULL, NULL, hash, compare);
 
 static void
 id_clean (void *cookie)
 {
   struct idspec *i = cookie;
   pthread_mutex_lock (&idlock);
-  hurd_ihash_locp_remove (&idhash, i->id_hashloc);
+  if (refcounts_hard_references(&i->pi.refcounts) == 0
+      && i->id_hashloc != NULL)
+    {
+      /* Nobody got a send right in between, we can remove i from the hash.  */
+      hurd_ihash_locp_remove (&idhash, i->id_hashloc);
+      i->id_hashloc = NULL;
+      ports_port_deref_weak (&i->pi);
+    }
   pthread_mutex_unlock (&idlock);
 }
 
@@ -50,7 +85,7 @@ static void
 id_initialize ()
 {
   assert_backtrace (!idclass);
-  idclass = ports_create_class (id_clean, NULL);
+  idclass = ports_create_class (NULL, id_clean);
 }
 
 error_t
@@ -65,16 +100,20 @@ fshelp_get_identity (struct port_bucket *bucket,
   if (!idclass)
     id_initialize ();
 
-  i = hurd_ihash_find (&idhash, (hurd_ihash_key_t) fileno);
+  i = hurd_ihash_find (&idhash, (hurd_ihash_key_t) &fileno);
   if (i == NULL)
     {
       err = ports_create_port (idclass, bucket, sizeof (struct idspec), &i);
       if (err)
         goto lose;
-      err = hurd_ihash_add (&idhash, (hurd_ihash_key_t) fileno, i);
+      i->cache_id = fileno;
+      err = hurd_ihash_add (&idhash, (hurd_ihash_key_t) &i->cache_id, i);
       if (err)
         goto lose_port;
 
+      /* Weak reference for the hash entry.  */
+      ports_port_ref_weak(&i->pi);
+
       *pt = ports_get_right (i);
       ports_port_deref (i);
     }
diff --git a/libfshelp/start-translator-long.c 
b/libfshelp/start-translator-long.c
index e1aea3c..707b59e 100644
--- a/libfshelp/start-translator-long.c
+++ b/libfshelp/start-translator-long.c
@@ -1,5 +1,6 @@
 /*
-   Copyright (C) 1995,96,99,2000,02, 04 Free Software Foundation, Inc.
+   Copyright (C) 1995, 1996, 1999, 2000, 2002, 2004, 2010
+   Free Software Foundation, Inc.
    Written by Miles Bader and Michael I. Bushnell.
 
    This file is part of the GNU Hurd.
@@ -273,12 +274,22 @@ fshelp_start_translator_long (fshelp_open_fn_t 
underlying_open_fn,
   saveport = ports[INIT_PORT_BOOTSTRAP];
   ports[INIT_PORT_BOOTSTRAP] = bootstrap;
 
+#ifdef HAVE_FILE_EXEC_PATHS
   /* Try and exec the translator in TASK...  */
-  err = file_exec (executable, task, EXEC_DEFAULTS,
-                  argz, argz_len, 0, 0,
-                  fds, fds_type, fds_len,
-                  ports, ports_type, ports_len,
-                  ints, ints_len, 0, 0, 0, 0);
+  err = file_exec_paths (executable, task, EXEC_DEFAULTS, name, name,
+                        argz, argz_len, 0, 0,
+                        fds, fds_type, fds_len,
+                        ports, ports_type, ports_len,
+                        ints, ints_len, 0, 0, 0, 0);
+  /* For backwards compatibility.  Just drop it when we kill file_exec.  */
+  if (err == MIG_BAD_ID)
+#endif
+    err = file_exec (executable, task, EXEC_DEFAULTS,
+                    argz, argz_len, 0, 0,
+                    fds, fds_type, fds_len,
+                    ports, ports_type, ports_len,
+                    ints, ints_len, 0, 0, 0, 0);
+
   ports_moved = 1;
 
   if (ports_type == MACH_MSG_TYPE_COPY_SEND)
diff --git a/libihash/Makefile b/libihash/Makefile
index 3377ef4..7ceaf1d 100644
--- a/libihash/Makefile
+++ b/libihash/Makefile
@@ -23,6 +23,7 @@ libname := libihash
 SRCS = ihash.c murmur3.c
 installhdrs = ihash.h
 
+HURDLIBS = shouldbeinlibc
 OBJS = $(SRCS:.c=.o)
 
 include ../Makeconf
diff --git a/libihash/ihash.c b/libihash/ihash.c
index 2fc7093..1b25544 100644
--- a/libihash/ihash.c
+++ b/libihash/ihash.c
@@ -29,6 +29,7 @@
 #include <stdlib.h>
 #include <stdint.h>
 #include <assert.h>
+#include <assert-backtrace.h>
 
 #include "ihash.h"
 
@@ -110,6 +111,7 @@ static inline void
 locp_remove (hurd_ihash_t ht, hurd_ihash_locp_t locp)
 {
   struct _hurd_ihash_item *item = (struct _hurd_ihash_item *) locp;
+  assert_backtrace (hurd_ihash_value_valid (item->value));
   if (ht->cleanup)
     (*ht->cleanup) (item->value, ht->cleanup_data);
   item->value = _HURD_IHASH_DELETED;
diff --git a/libnetfs/file-exec.c b/libnetfs/file-exec.c
index 638f0ae..8153b1f 100644
--- a/libnetfs/file-exec.c
+++ b/libnetfs/file-exec.c
@@ -1,5 +1,6 @@
 /*
-   Copyright (C) 1996,97,2000,01,02 Free Software Foundation, Inc.
+   Copyright (C) 1996, 1997, 2000, 2001, 2002, 2010
+   Free Software Foundation, Inc.
    Written by Michael I. Bushnell, p/BSG.
 
    This file is part of the GNU Hurd.
@@ -49,6 +50,41 @@ netfs_S_file_exec (struct protid *cred,
                   mach_port_t *destroynames,
                   size_t destroynameslen)
 {
+  return netfs_S_file_exec_paths (cred,
+                                 task,
+                                 flags,
+                                 "",
+                                 "",
+                                 argv, argvlen,
+                                 envp, envplen,
+                                 fds, fdslen,
+                                 portarray, portarraylen,
+                                 intarray, intarraylen,
+                                 deallocnames, deallocnameslen,
+                                 destroynames, destroynameslen);
+}
+
+kern_return_t
+netfs_S_file_exec_paths (struct protid *cred,
+                        task_t task,
+                        int flags,
+                        char *path,
+                        char *abspath,
+                        char *argv,
+                        size_t argvlen,
+                        char *envp,
+                        size_t envplen,
+                        mach_port_t *fds,
+                        size_t fdslen,
+                        mach_port_t *portarray,
+                        size_t portarraylen,
+                        int *intarray,
+                        size_t intarraylen,
+                        mach_port_t *deallocnames,
+                        size_t deallocnameslen,
+                        mach_port_t *destroynames,
+                        size_t destroynameslen)
+{
   struct node *np;
   error_t err;
   uid_t uid;
@@ -133,14 +169,31 @@ netfs_S_file_exec (struct protid *cred,
          if (newpi)
            {
              right = ports_get_send_right (newpi);
-             err = exec_exec (_netfs_exec,
-                              right, MACH_MSG_TYPE_COPY_SEND,
-                              task, flags, argv, argvlen, envp, envplen,
-                              fds, MACH_MSG_TYPE_COPY_SEND, fdslen,
-                              portarray, MACH_MSG_TYPE_COPY_SEND, portarraylen,
-                              intarray, intarraylen,
-                              deallocnames, deallocnameslen,
-                              destroynames, destroynameslen);
+#ifdef HAVE_EXEC_EXEC_PATHS
+             err = exec_exec_paths (_netfs_exec,
+                                    right, MACH_MSG_TYPE_COPY_SEND,
+                                    task, flags, path, abspath,
+                                    argv, argvlen, envp, envplen,
+                                    fds, MACH_MSG_TYPE_COPY_SEND, fdslen,
+                                    portarray, MACH_MSG_TYPE_COPY_SEND,
+                                    portarraylen,
+                                    intarray, intarraylen,
+                                    deallocnames, deallocnameslen,
+                                    destroynames, destroynameslen);
+             /* For backwards compatibility.  Just drop it when we kill
+                exec_exec.  */
+             if (err == MIG_BAD_ID)
+#endif
+               err = exec_exec (_netfs_exec,
+                                right, MACH_MSG_TYPE_COPY_SEND,
+                                task, flags, argv, argvlen, envp, envplen,
+                                fds, MACH_MSG_TYPE_COPY_SEND, fdslen,
+                                portarray, MACH_MSG_TYPE_COPY_SEND,
+                                portarraylen,
+                                intarray, intarraylen,
+                                deallocnames, deallocnameslen,
+                                destroynames, destroynameslen);
+
              mach_port_deallocate (mach_task_self (), right);
              ports_port_deref (newpi);
            }
diff --git a/libps/procstat.c b/libps/procstat.c
index f6420ee..d042341 100644
--- a/libps/procstat.c
+++ b/libps/procstat.c
@@ -956,6 +956,23 @@ proc_stat_set_flags (struct proc_stat *ps, ps_flags_t 
flags)
        }
     }
 
+  /* The process's path to binary executable */
+  if (NEED (PSTAT_EXE, PSTAT_PID))
+    {
+      ps->exe = malloc (sizeof(string_t));
+      if (ps->exe)
+       {
+         if (proc_get_exe (server, ps->pid, ps->exe))
+           free (ps->exe);
+         else
+           {
+             ps->exe_len = strlen(ps->exe);
+             have |= PSTAT_EXE;
+             ps->exe_vm_alloced = 0;
+           }
+       }
+    }
+
   /* The ctty id port; note that this is just a magic cookie;
      we use it to fetch a port to the actual terminal -- it's not useful for
      much else.  */
diff --git a/libps/ps.h b/libps/ps.h
index 3ee142d..2725b1f 100644
--- a/libps/ps.h
+++ b/libps/ps.h
@@ -272,6 +272,7 @@ struct proc_stat
   unsigned thread_waits_vm_alloced : 1;
   unsigned args_vm_alloced : 1;
   unsigned env_vm_alloced : 1;
+  unsigned exe_vm_alloced : 1;
 
   /* Various libc ports:  */
 
@@ -305,6 +306,11 @@ struct proc_stat
   size_t env_len;
 
   unsigned num_ports;
+
+  /* The path to process's binary executable.  */
+  char *exe;
+  /* The length of EXE.  */
+  size_t exe_len;
 };
 
 /* Proc_stat flag bits; each bit is set in the FLAGS field if that
@@ -344,12 +350,13 @@ struct proc_stat
 #define PSTAT_HOOK           0x800000 /* Has a non-zero hook */
 #define PSTAT_NUM_PORTS      0x4000000 /* Number of Mach ports in the task */
 #define PSTAT_TIMES          0x8000000 /* Task/thread user and system times */
+#define PSTAT_EXE           0x10000000 /* Path to binary executable */
 
 /* Flag bits that don't correspond precisely to any field.  */
 #define PSTAT_NO_MSGPORT     0x1000000 /* Don't use the msgport at all */
 
 /* Bits from PSTAT_USER_BASE on up are available for user-use.  */
-#define PSTAT_USER_BASE      0x10000000
+#define PSTAT_USER_BASE      0x20000000
 #define PSTAT_USER_MASK      ~(PSTAT_USER_BASE - 1)
 
 /* If the PSTAT_STATE flag is set, then the proc_stats state field holds a
@@ -448,6 +455,8 @@ extern char *proc_stat_state_tags;
 #define proc_stat_tty(ps) ((ps)->tty)
 #define proc_stat_task_events_info(ps) ((ps)->task_events_info)
 #define proc_stat_num_ports(ps) ((ps)->num_ports)
+#define proc_stat_exe(ps) ((ps)->exe)
+#define proc_stat_exe_len(ps) ((ps)->exe_len)
 #define proc_stat_has(ps, needs) (((ps)->flags & needs) == needs)
 
 /* True if PS refers to a thread and not a process.  */
diff --git a/libps/spec.c b/libps/spec.c
index 5e540f8..4760c43 100644
--- a/libps/spec.c
+++ b/libps/spec.c
@@ -358,6 +358,14 @@ ps_get_num_ports (struct proc_stat *ps)
 const struct ps_getter ps_num_ports_getter =
 {"num_ports", PSTAT_NUM_PORTS, (vf) ps_get_num_ports};
 
+static void
+ps_get_exe (struct proc_stat *ps, char **exe_p, int *exe_len_p)
+{
+  *exe_p = proc_stat_exe (ps);
+  *exe_len_p = proc_stat_exe_len (ps);
+}
+const struct ps_getter ps_exe_getter =
+{"exe", PSTAT_EXE, ps_get_exe};
 /* ---------------------------------------------------------------- */
 /* some printing functions */
 
@@ -1166,6 +1174,8 @@ static const struct ps_fmt_spec specs[] =
    &ps_zero_fills_getter,  ps_emit_int,            ps_cmp_ints,   
ps_nominal_zint},
   {"Ports",    0,      -5, -1, 0,
    &ps_num_ports_getter,       ps_emit_int,        ps_cmp_ints,   0},
+  {"Exe",      0,       0, -1, 0,
+   &ps_exe_getter,        ps_emit_string,  ps_cmp_strings,ps_nominal_string},
   {0}
 };
 
diff --git a/libtrivfs/file-exec.c b/libtrivfs/file-exec.c
index b353d8a..a0a2a50 100644
--- a/libtrivfs/file-exec.c
+++ b/libtrivfs/file-exec.c
@@ -1,5 +1,5 @@
 /*
-   Copyright (C) 1994,2002 Free Software Foundation, Inc.
+   Copyright (C) 1994, 2002, 2010 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
@@ -41,3 +41,29 @@ trivfs_S_file_exec (trivfs_protid_t exec_file,
 {
   return EOPNOTSUPP;
 }
+
+kern_return_t
+trivfs_S_file_exec_paths (trivfs_protid_t exec_file,
+                         mach_port_t reply,
+                         mach_msg_type_name_t replyPoly,
+                         mach_port_t exec_task,
+                         int flags,
+                         string_t path,
+                         string_t abspath,
+                         data_t argv,
+                         mach_msg_type_number_t argvCnt,
+                         data_t envp,
+                         mach_msg_type_number_t envpCnt,
+                         portarray_t fdarray,
+                         mach_msg_type_number_t fdarrayCnt,
+                         portarray_t portarray,
+                         mach_msg_type_number_t portarrayCnt,
+                         intarray_t intarray,
+                         mach_msg_type_number_t intarrayCnt,
+                         mach_port_array_t deallocnames,
+                         mach_msg_type_number_t deallocnamesCnt,
+                         mach_port_array_t destroynames,
+                         mach_msg_type_number_t destroynamesCnt)
+{
+  return EOPNOTSUPP;
+}
diff --git a/lwip/Makefile b/lwip/Makefile
new file mode 100644
index 0000000..c5f3c8c
--- /dev/null
+++ b/lwip/Makefile
@@ -0,0 +1,50 @@
+#   Copyright (C) 2017 Free Software Foundation, Inc.
+#
+#   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.  If not, see <http://www.gnu.org/licenses/>.
+
+dir            = lwip
+makemode       = server
+
+PORTDIR = $(srcdir)/port
+
+SRCS           = main.c io-ops.c socket-ops.c pfinet-ops.c iioctl-ops.c 
port-objs.c \
+                 startup-ops.c options.c lwip-util.c startup.c
+IFSRCS         = ifcommon.c hurdethif.c hurdloopif.c hurdtunif.c
+MIGSRCS                = ioServer.c socketServer.c pfinetServer.c 
iioctlServer.c \
+                 startup_notifyServer.c
+OBJS           = $(patsubst %.S,%.o,$(patsubst %.c,%.o,\
+                 $(SRCS) $(IFSRCS) $(MIGSRCS)))
+
+HURDLIBS = trivfs fshelp ports ihash shouldbeinlibc iohelp
+LDLIBS = -lpthread $(liblwip_LIBS)
+
+target = lwip
+
+include ../Makeconf
+
+vpath %.c $(PORTDIR) $(PORTDIR)/netif
+
+CFLAGS += -I$(PORTDIR)/include $(liblwip_CFLAGS)
+
+MIGCOMSFLAGS += -prefix lwip_
+mig-sheader-prefix = lwip_
+io-MIGSFLAGS = -imacros $(srcdir)/mig-mutate.h
+socket-MIGSFLAGS = -imacros $(srcdir)/mig-mutate.h
+iioctl-MIGSFLAGS = -imacros $(srcdir)/mig-mutate.h
+
+# cpp doesn't automatically make dependencies for -imacros dependencies. argh.
+lwip_io_S.h ioServer.c lwip_socket_S.h socketServer.c: mig-mutate.h
+$(OBJS): config.h
diff --git a/lwip/iioctl-ops.c b/lwip/iioctl-ops.c
new file mode 100644
index 0000000..fcb7e87
--- /dev/null
+++ b/lwip/iioctl-ops.c
@@ -0,0 +1,410 @@
+/*
+   Copyright (C) 2000, 2007, 2017 Free Software Foundation, Inc.
+   Written by Marcus Brinkmann.
+
+   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.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Ioctls for network device configuration */
+
+#include <lwip_iioctl_S.h>
+
+#include <lwip/sockets.h>
+#include <device/device.h>
+#include <device/net_status.h>
+
+#include <lwip-hurd.h>
+#include <lwip-util.h>
+#include <netif/ifcommon.h>
+
+/* Get the interface from its name */
+static struct netif *
+get_if (char *name)
+{
+  char ifname[IFNAMSIZ];
+  struct netif *netif;
+
+  memcpy (ifname, name, IFNAMSIZ - 1);
+  ifname[IFNAMSIZ - 1] = 0;
+
+  for (netif = netif_list; netif != 0; netif = netif->next)
+    {
+      if (strcmp (netif_get_state (netif)->devname, ifname) == 0)
+       break;
+    }
+
+  return netif;
+}
+
+enum siocgif_type
+{
+  ADDR,
+  NETMASK,
+  DSTADDR,
+  BRDADDR
+};
+
+#define SIOCGIF(name, type)                                            \
+  kern_return_t                                                                
\
+  lwip_S_iioctl_siocgif##name (struct sock_user *user,                       \
+        ifname_t ifnam,                                \
+        sockaddr_t *addr)                              \
+  {                                                                    \
+    return siocgifXaddr (user, ifnam, addr, type);                     \
+  }
+
+/* Get some sockaddr type of info.  */
+static kern_return_t
+siocgifXaddr (struct sock_user *user,
+             ifname_t ifnam, sockaddr_t * addr, enum siocgif_type type)
+{
+  error_t err = 0;
+  struct sockaddr_in *sin = (struct sockaddr_in *) addr;
+  size_t buflen = sizeof (struct sockaddr);
+  struct netif *netif;
+  uint32_t addrs[4];
+
+  if (!user)
+    return EOPNOTSUPP;
+
+  netif = get_if (ifnam);
+  if (!netif)
+    return ENODEV;
+
+  if (type == DSTADDR)
+    return EOPNOTSUPP;
+
+  /* We're only interested in geting the address family */
+  err = lwip_getsockname (user->sock->sockno, addr, (socklen_t *) & buflen);
+  if (err)
+    return err;
+
+  if (sin->sin_family != AF_INET)
+    err = EINVAL;
+  else
+    {
+      inquire_device (netif, &addrs[0], &addrs[1], &addrs[2], &addrs[3], 0, 0,
+                     0);
+      sin->sin_addr.s_addr = addrs[type];
+    }
+
+  return err;
+}
+
+#define SIOCSIF(name, type)                                            \
+  kern_return_t                                                                
\
+  lwip_S_iioctl_siocsif##name (struct sock_user *user,                       \
+                         ifname_t ifnam,                               \
+                         sockaddr_t addr)                              \
+  {                                                                    \
+    return siocsifXaddr (user, ifnam, &addr, type);                    \
+  }
+
+/* Set some sockaddr type of info.  */
+static kern_return_t
+siocsifXaddr (struct sock_user *user,
+             ifname_t ifnam, sockaddr_t * addr, enum siocgif_type type)
+{
+  error_t err = 0;
+  struct sockaddr_in sin;
+  size_t buflen = sizeof (struct sockaddr_in);
+  struct netif *netif;
+  uint32_t ipv4_addrs[5];
+
+  if (!user)
+    return EOPNOTSUPP;
+
+  if (!user->isroot)
+    return EPERM;
+
+  netif = get_if (ifnam);
+
+  if (!netif)
+    return ENODEV;
+
+  if (type == DSTADDR || type == BRDADDR)
+    return EOPNOTSUPP;
+
+  err = lwip_getsockname (user->sock->sockno,
+                         (sockaddr_t *) & sin, (socklen_t *) & buflen);
+  if (err)
+    return err;
+
+  if (sin.sin_family != AF_INET)
+    err = EINVAL;
+  else
+    {
+      inquire_device (netif, &ipv4_addrs[0], &ipv4_addrs[1],
+                     &ipv4_addrs[2], &ipv4_addrs[3], &ipv4_addrs[4], 0, 0);
+
+      ipv4_addrs[type] = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
+
+      err = configure_device (netif, ipv4_addrs[0], ipv4_addrs[1],
+                             ipv4_addrs[2], ipv4_addrs[3], ipv4_addrs[4], 0,
+                             0);
+    }
+
+  return err;
+}
+
+/* 12 SIOCSIFADDR -- Set address of a network interface.  */
+SIOCSIF (addr, ADDR);
+
+/* 14 SIOCSIFDSTADDR -- Set point-to-point (peer) address of a network 
interface.  */
+SIOCSIF (dstaddr, DSTADDR);
+
+/* 16 SIOCSIFFLAGS -- Set flags of a network interface.  */
+kern_return_t
+lwip_S_iioctl_siocsifflags (struct sock_user * user,
+                           ifname_t ifnam,
+                           short flags)
+{
+  error_t err = 0;
+  struct netif *netif;
+
+  if (!user)
+    return EOPNOTSUPP;
+
+  netif = get_if (ifnam);
+
+  if (!user->isroot)
+    err = EPERM;
+  else if (!netif)
+    err = ENODEV;
+  else
+    err = if_change_flags (netif, flags);
+
+  return err;
+}
+
+/* 17 SIOCGIFFLAGS -- Get flags of a network interface.  */
+kern_return_t
+lwip_S_iioctl_siocgifflags (struct sock_user * user, char *name, short *flags)
+{
+  error_t err = 0;
+  struct netif *netif;
+
+  if (!user)
+    return EOPNOTSUPP;
+
+  netif = get_if (name);
+  if (!netif)
+    err = ENODEV;
+  else
+    {
+      *flags = netif_get_state (netif)->flags;
+    }
+
+  return err;
+}
+
+/* 19 SIOCSIFBRDADDR -- Set broadcast address of a network interface.  */
+SIOCSIF (brdaddr, BRDADDR);
+
+/* 22 SIOCSIFNETMASK -- Set netmask of a network interface.  */
+SIOCSIF (netmask, NETMASK);
+
+/* 23 SIOCGIFMETRIC -- Get metric of a network interface.  */
+kern_return_t
+lwip_S_iioctl_siocgifmetric (struct sock_user * user,
+                            ifname_t ifnam,
+                            int *metric)
+{
+  error_t err = 0;
+  struct netif *netif;
+
+  if (!user)
+    return EOPNOTSUPP;
+
+  netif = get_if (ifnam);
+  if (!netif)
+    err = ENODEV;
+  else
+    {
+      *metric = 0;             /* Not supported.  */
+    }
+
+  return err;
+}
+
+/* 24 SIOCSIFMETRIC -- Set metric of a network interface.  */
+kern_return_t
+lwip_S_iioctl_siocsifmetric (struct sock_user * user,
+                            ifname_t ifnam,
+                            int metric)
+{
+  return EOPNOTSUPP;
+}
+
+/* 25 SIOCDIFADDR -- Delete interface address.  */
+kern_return_t
+lwip_S_iioctl_siocdifaddr (struct sock_user * user,
+                          ifname_t ifnam,
+                          sockaddr_t addr)
+{
+  return EOPNOTSUPP;
+}
+
+/* 33 SIOCGIFADDR -- Get address of a network interface.  */
+SIOCGIF (addr, ADDR);
+
+/* 34 SIOCGIFDSTADDR -- Get point-to-point address of a network interface.  */
+SIOCGIF (dstaddr, DSTADDR);
+
+/* 35 SIOCGIFBRDADDR -- Get broadcast address of a network interface.  */
+SIOCGIF (brdaddr, BRDADDR);
+
+/* 37 SIOCGIFNETMASK -- Get netmask of a network interface.  */
+SIOCGIF (netmask, NETMASK);
+
+/* 39 SIOCGIFHWADDR -- Get the hardware address of a network interface.  */
+error_t
+lwip_S_iioctl_siocgifhwaddr (struct sock_user * user,
+                            ifname_t ifname,
+                            sockaddr_t * addr)
+{
+  error_t err = 0;
+  struct netif *netif;
+
+  if (!user)
+    return EOPNOTSUPP;
+
+  netif = get_if (ifname);
+  if (!netif)
+    err = ENODEV;
+  else
+    {
+      memcpy (addr->sa_data, netif->hwaddr, netif->hwaddr_len);
+      addr->sa_family = netif_get_state (netif)->type;
+    }
+
+  return err;
+}
+
+/* 51 SIOCGIFMTU -- Get mtu of a network interface.  */
+error_t
+lwip_S_iioctl_siocgifmtu (struct sock_user * user, ifname_t ifnam, int *mtu)
+{
+  error_t err = 0;
+  struct netif *netif;
+
+  if (!user)
+    return EOPNOTSUPP;
+
+  netif = get_if (ifnam);
+  if (!netif)
+    err = ENODEV;
+  else
+    {
+      *mtu = netif->mtu;
+    }
+
+  return err;
+}
+
+/* 51 SIOCSIFMTU -- Set mtu of a network interface.  */
+error_t
+lwip_S_iioctl_siocsifmtu (struct sock_user * user, ifname_t ifnam, int mtu)
+{
+  error_t err = 0;
+  struct netif *netif;
+
+  if (!user)
+    return EOPNOTSUPP;
+
+  if (!user->isroot)
+    return EPERM;
+
+  if (mtu <= 0)
+    return EINVAL;
+
+  netif = get_if (ifnam);
+  if (!netif)
+    err = ENODEV;
+  else
+    {
+      err = netif_get_state (netif)->update_mtu (netif, mtu);
+    }
+
+  return err;
+}
+
+/* 100 SIOCGIFINDEX -- Get index number of a network interface.  */
+error_t
+lwip_S_iioctl_siocgifindex (struct sock_user * user,
+                           ifname_t ifnam,
+                           int *index)
+{
+  error_t err = 0;
+  struct netif *netif;
+  int i;
+
+  if (!user)
+    return EOPNOTSUPP;
+
+  i = 1;                       /* The first index must be 1 */
+  for (netif = netif_list; netif != 0; netif = netif->next)
+    {
+      if (strcmp (netif_get_state (netif)->devname, ifnam) == 0)
+       {
+         *index = i;
+         break;
+       }
+
+      i++;
+    }
+
+  if (!netif)
+    err = ENODEV;
+
+  return err;
+}
+
+/* 101 SIOCGIFNAME -- Get name of a network interface from index number.  */
+error_t
+lwip_S_iioctl_siocgifname (struct sock_user * user,
+                          ifname_t ifnam,
+                          int *index)
+{
+  error_t err = 0;
+  struct netif *netif;
+  int i;
+
+  if (!user)
+    return EOPNOTSUPP;
+
+  if (*index < 0)
+    return EINVAL;
+
+  i = 1;                       /* The first index is 1 */
+  for (netif = netif_list; netif != 0; netif = netif->next)
+    {
+      if (i == *index)
+       break;
+
+      i++;
+    }
+
+  if (!netif)
+    err = ENODEV;
+  else
+    {
+      strncpy (ifnam, netif_get_state (netif)->devname, IFNAMSIZ);
+      ifnam[IFNAMSIZ - 1] = '\0';
+    }
+
+  return err;
+}
diff --git a/lwip/io-ops.c b/lwip/io-ops.c
new file mode 100644
index 0000000..636c26f
--- /dev/null
+++ b/lwip/io-ops.c
@@ -0,0 +1,554 @@
+/*
+   Copyright (C) 1995,96,97,98,99,2000,02,17 Free Software Foundation, Inc.
+   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 the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* General input/output operations */
+
+#include <lwip_io_S.h>
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <lwip/sockets.h>
+
+error_t
+lwip_S_io_write (struct sock_user *user,
+                char *data,
+                size_t datalen,
+                off_t offset, mach_msg_type_number_t * amount)
+{
+  int sent;
+  int sockflags;
+
+  if (!user)
+    return EOPNOTSUPP;
+
+  sockflags = lwip_fcntl (user->sock->sockno, F_GETFL, 0);
+  sent = lwip_send (user->sock->sockno, data, datalen,
+                   (sockflags & O_NONBLOCK) ? MSG_DONTWAIT : 0);
+
+  if (sent >= 0)
+    {
+      *amount = sent;
+    }
+
+  return errno;
+}
+
+error_t
+lwip_S_io_read (struct sock_user * user,
+               char **data,
+               size_t * datalen, off_t offset, mach_msg_type_number_t amount)
+{
+  error_t err;
+  int alloced = 0;
+  int flags;
+
+  if (!user)
+    return EOPNOTSUPP;
+
+  /* Instead of this, we should peek and the socket and only
+     allocate as much as necessary. */
+  if (amount > *datalen)
+    {
+      *data = mmap (0, amount, PROT_READ | PROT_WRITE, MAP_ANON, 0, 0);
+      if (*data == MAP_FAILED)
+       /* Should check whether errno is indeed ENOMEM --
+          but this can't be done in a straightforward way,
+          because the glue headers #undef errno. */
+       return ENOMEM;
+      alloced = 1;
+    }
+
+  /* Get flags */
+  flags = lwip_fcntl (user->sock->sockno, F_GETFL, 0);
+
+  err = lwip_recv (user->sock->sockno, *data, amount,
+                  (flags & O_NONBLOCK) ? MSG_DONTWAIT : 0);
+
+  if (err < 0)
+    {
+      if (alloced)
+       munmap (*data, amount);
+    }
+  else
+    {
+      *datalen = err;
+      if (alloced && round_page (*datalen) < round_page (amount))
+       munmap (*data + round_page (*datalen),
+               round_page (amount) - round_page (*datalen));
+      errno = 0;
+    }
+
+  return errno;
+}
+
+error_t
+lwip_S_io_seek (struct sock_user * user,
+               off_t offset, int whence, off_t * newp)
+{
+  return user ? ESPIPE : EOPNOTSUPP;
+}
+
+error_t
+lwip_S_io_readable (struct sock_user * user, mach_msg_type_number_t * amount)
+{
+  error_t err;
+  if (!user)
+    return EOPNOTSUPP;
+
+  err = lwip_ioctl (user->sock->sockno, FIONREAD, amount);
+
+  if (err < 0)
+    *amount = 0;
+
+  return errno;
+}
+
+error_t
+lwip_S_io_set_all_openmodes (struct sock_user * user, int bits)
+{
+  int opt;
+
+  if (!user)
+    return EOPNOTSUPP;
+
+  if (bits & O_NONBLOCK)
+    opt = 1;
+  else
+    opt = 0;
+
+  lwip_ioctl (user->sock->sockno, FIONBIO, &opt);
+
+  return errno;
+}
+
+error_t
+lwip_S_io_get_openmodes (struct sock_user * user, int *bits)
+{
+  if (!user)
+    return EOPNOTSUPP;
+
+  *bits = lwip_fcntl (user->sock->sockno, F_GETFL, 0);
+
+  return errno;
+}
+
+error_t
+lwip_S_io_set_some_openmodes (struct sock_user * user, int bits)
+{
+  if (!user)
+    return EOPNOTSUPP;
+
+  if (bits & O_NONBLOCK)
+    {
+      int opt = 1;
+      lwip_ioctl (user->sock->sockno, FIONBIO, &opt);
+    }
+
+  return errno;
+}
+
+
+error_t
+lwip_S_io_clear_some_openmodes (struct sock_user * user, int bits)
+{
+  if (!user)
+    return EOPNOTSUPP;
+
+  if (bits & O_NONBLOCK)
+    {
+      int opt = 0;
+      lwip_ioctl (user->sock->sockno, FIONBIO, &opt);
+    }
+
+  return errno;
+}
+
+/*
+ * Arrange things to call lwip_poll()
+ */
+static error_t
+lwip_io_select_common (struct sock_user *user,
+                      mach_port_t reply,
+                      mach_msg_type_name_t reply_type,
+                      struct timespec *tv, int *select_type)
+{
+  int ret;
+  int timeout;
+  struct pollfd fdp;
+  nfds_t nfds;
+
+  if (!user)
+    return EOPNOTSUPP;
+
+  /* Make this thread cancellable */
+  ports_interrupt_self_on_notification (user, reply, MACH_NOTIFY_DEAD_NAME);
+
+  memset (&fdp, 0, sizeof (struct pollfd));
+  fdp.fd = user->sock->sockno;
+
+  if (*select_type & SELECT_READ)
+    {
+      fdp.events |= POLLIN;
+    }
+  if (*select_type & SELECT_WRITE)
+    {
+      fdp.events |= POLLOUT;
+    }
+  if (*select_type & SELECT_URG)
+    {
+      fdp.events |= POLLPRI;
+    }
+
+  *select_type = 0;
+
+  nfds = 1;
+  timeout = tv ? tv->tv_sec * 1000 + tv->tv_nsec / 1000000 : -1;
+  ret = lwip_poll (&fdp, nfds, timeout);
+
+  if (ret > 0)
+    {
+      if (fdp.revents & POLLIN)
+       *select_type |= SELECT_READ;
+
+      if (fdp.revents & POLLOUT)
+       *select_type |= SELECT_WRITE;
+
+      if (fdp.revents & POLLPRI)
+       *select_type |= SELECT_URG;
+    }
+
+  return errno;
+}
+
+error_t
+lwip_S_io_select (struct sock_user * user,
+                 mach_port_t reply,
+                 mach_msg_type_name_t reply_type, int *select_type)
+{
+  return lwip_io_select_common (user, reply, reply_type, 0, select_type);
+}
+
+error_t
+lwip_S_io_select_timeout (struct sock_user * user,
+                         mach_port_t reply,
+                         mach_msg_type_name_t reply_type,
+                         struct timespec ts, int *select_type)
+{
+  struct timespec current_ts;
+  clock_gettime (CLOCK_REALTIME, &current_ts);
+
+  ts.tv_sec -= current_ts.tv_sec;
+  ts.tv_nsec -= current_ts.tv_nsec;
+
+  return lwip_io_select_common (user, reply, reply_type, &ts, select_type);
+}
+
+
+error_t
+lwip_S_io_stat (struct sock_user * user, struct stat * st)
+{
+  if (!user)
+    return EOPNOTSUPP;
+
+  memset (st, 0, sizeof (struct stat));
+
+  st->st_fstype = FSTYPE_SOCKET;
+  st->st_fsid = getpid ();
+  st->st_ino = user->sock->sockno;
+
+  st->st_mode = S_IFSOCK | ACCESSPERMS;
+  st->st_blksize = 512;                /* ???? */
+
+  return 0;
+}
+
+error_t
+lwip_S_io_reauthenticate (struct sock_user * user, mach_port_t rend)
+{
+  struct sock_user *newuser;
+  uid_t gubuf[20], ggbuf[20], aubuf[20], agbuf[20];
+  uid_t *gen_uids, *gen_gids, *aux_uids, *aux_gids;
+  size_t genuidlen, gengidlen, auxuidlen, auxgidlen;
+  error_t err;
+  size_t i, j;
+  auth_t auth;
+  mach_port_t newright;
+
+  if (!user)
+    return EOPNOTSUPP;
+
+  genuidlen = gengidlen = auxuidlen = auxgidlen = 20;
+  gen_uids = gubuf;
+  gen_gids = ggbuf;
+  aux_uids = aubuf;
+  aux_gids = agbuf;
+
+  newuser = make_sock_user (user->sock, 0, 1, 0);
+
+  auth = getauth ();
+  newright = ports_get_send_right (newuser);
+  assert_backtrace (newright != MACH_PORT_NULL);
+
+  do
+    err = auth_server_authenticate (auth,
+                                   rend,
+                                   MACH_MSG_TYPE_COPY_SEND,
+                                   newright,
+                                   MACH_MSG_TYPE_COPY_SEND,
+                                   &gen_uids, &genuidlen,
+                                   &aux_uids, &auxuidlen,
+                                   &gen_gids, &gengidlen,
+                                   &aux_gids, &auxgidlen);
+  while (err == EINTR);
+
+  mach_port_deallocate (mach_task_self (), rend);
+  mach_port_deallocate (mach_task_self (), newright);
+  mach_port_deallocate (mach_task_self (), auth);
+
+  if (err)
+    newuser->isroot = 0;
+  else
+    /* Check permission as fshelp_isowner would do.  */
+    for (i = 0; i < genuidlen; i++)
+      {
+       if (gen_uids[i] == 0 || gen_uids[i] == lwip_owner)
+         newuser->isroot = 1;
+       if (gen_uids[i] == lwip_group)
+         for (j = 0; j < gengidlen; j++)
+           if (gen_gids[j] == lwip_group)
+             newuser->isroot = 1;
+      }
+
+  mach_port_move_member (mach_task_self (), newuser->pi.port_right,
+                        lwip_bucket->portset);
+
+  ports_port_deref (newuser);
+
+  if (gubuf != gen_uids)
+    munmap (gen_uids, genuidlen * sizeof (uid_t));
+  if (ggbuf != gen_gids)
+    munmap (gen_gids, gengidlen * sizeof (uid_t));
+  if (aubuf != aux_uids)
+    munmap (aux_uids, auxuidlen * sizeof (uid_t));
+  if (agbuf != aux_gids)
+    munmap (aux_gids, auxgidlen * sizeof (uid_t));
+
+  return 0;
+}
+
+error_t
+lwip_S_io_restrict_auth (struct sock_user * user,
+                        mach_port_t * newobject,
+                        mach_msg_type_name_t * newobject_type,
+                        uid_t * uids, size_t uidslen,
+                        uid_t * gids, size_t gidslen)
+{
+  struct sock_user *newuser;
+  int i, j;
+  int isroot;
+
+  if (!user)
+    return EOPNOTSUPP;
+
+  isroot = 0;
+  if (user->isroot)
+    /* Check permission as fshelp_isowner would do.  */
+    for (i = 0; i < uidslen; i++)
+      {
+       if (uids[i] == 0 || uids[i] == lwip_owner)
+         isroot = 1;
+       if (uids[i] == lwip_group)
+         for (j = 0; j < gidslen; j++)
+           if (gids[j] == lwip_group)
+             isroot = 1;
+      }
+
+  newuser = make_sock_user (user->sock, isroot, 0, 0);
+  *newobject = ports_get_right (newuser);
+  *newobject_type = MACH_MSG_TYPE_MAKE_SEND;
+  ports_port_deref (newuser);
+
+  return 0;
+}
+
+error_t
+lwip_S_io_duplicate (struct sock_user * user,
+                    mach_port_t * newobject,
+                    mach_msg_type_name_t * newobject_type)
+{
+  struct sock_user *newuser;
+  if (!user)
+    return EOPNOTSUPP;
+
+  newuser = make_sock_user (user->sock, user->isroot, 0, 0);
+  *newobject = ports_get_right (newuser);
+  *newobject_type = MACH_MSG_TYPE_MAKE_SEND;
+  ports_port_deref (newuser);
+
+  return 0;
+}
+
+error_t
+lwip_S_io_identity (struct sock_user * user,
+                   mach_port_t * id,
+                   mach_msg_type_name_t * idtype,
+                   mach_port_t * fsys,
+                   mach_msg_type_name_t * fsystype, ino_t * fileno)
+{
+  error_t err;
+
+  if (!user)
+    return EOPNOTSUPP;
+
+  if (user->sock->identity == MACH_PORT_NULL)
+    {
+      err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+                               &user->sock->identity);
+      if (err)
+       {
+         return err;
+       }
+    }
+
+  *id = user->sock->identity;
+  *idtype = MACH_MSG_TYPE_MAKE_SEND;
+  *fsys = fsys_identity;
+  *fsystype = MACH_MSG_TYPE_MAKE_SEND;
+  *fileno = user->sock->sockno;
+
+  return 0;
+}
+
+error_t
+lwip_S_io_revoke (struct sock_user * user)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+lwip_S_io_async (struct sock_user * user,
+                mach_port_t notify,
+                mach_port_t * id, mach_msg_type_name_t * idtype)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+lwip_S_io_mod_owner (struct sock_user * user, pid_t owner)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+lwip_S_io_get_owner (struct sock_user * user, pid_t * owner)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+lwip_S_io_get_icky_async_id (struct sock_user * user,
+                            mach_port_t * id, mach_msg_type_name_t * idtype)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+lwip_S_io_server_version (struct sock_user * user,
+                         char *name, int *major, int *minor, int *edit)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+lwip_S_io_pathconf (struct sock_user * user, int name, int *value)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+lwip_S_io_map (struct sock_user * user,
+              mach_port_t * rdobj,
+              mach_msg_type_name_t * rdobj_type,
+              mach_port_t * wrobj, mach_msg_type_name_t * wrobj_type)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+lwip_S_io_map_cntl (struct sock_user * user,
+                   mach_port_t * obj, mach_msg_type_name_t * obj_type)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+lwip_S_io_get_conch (struct sock_user * user)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+lwip_S_io_release_conch (struct sock_user * user)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+lwip_S_io_eofnotify (struct sock_user * user)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+lwip_S_io_prenotify (struct sock_user * user,
+                    vm_offset_t start, vm_offset_t end)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+lwip_S_io_postnotify (struct sock_user * user,
+                     vm_offset_t start, vm_offset_t end)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+lwip_S_io_readnotify (struct sock_user * user)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+lwip_S_io_readsleep (struct sock_user * user)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+lwip_S_io_sigio (struct sock_user * user)
+{
+  return EOPNOTSUPP;
+}
diff --git a/lwip/lwip-hurd.h b/lwip/lwip-hurd.h
new file mode 100644
index 0000000..9e05550
--- /dev/null
+++ b/lwip/lwip-hurd.h
@@ -0,0 +1,102 @@
+/*
+   Copyright (C) 1995, 1996, 1999, 2000, 2002, 2007, 2017
+     Free Software Foundation, Inc.
+
+   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 the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Translator global declarations */
+
+#ifndef LWIP_HURD_H
+#define LWIP_HURD_H
+
+#include <sys/socket.h>
+#include <hurd/ports.h>
+#include <hurd/trivfs.h>
+#include <refcount.h>
+
+struct port_bucket *lwip_bucket;
+struct port_class *socketport_class;
+struct port_class *addrport_class;
+struct port_class *shutdown_notify_class;
+
+struct port_class *lwip_protid_portclasses[2];
+struct port_class *lwip_cntl_portclasses[2];
+
+/* Which portclass to install on the bootstrap port, default to IPv4. */
+int lwip_bootstrap_portclass;
+
+mach_port_t fsys_identity;
+
+/* Trivfs control structure for lwip.  */
+struct trivfs_control *lwipcntl;
+
+/* Address family port classes. */
+enum
+{
+  PORTCLASS_INET,
+  PORTCLASS_INET6,
+};
+
+struct socket
+{
+  int sockno;
+  mach_port_t identity;
+  refcount_t refcnt;
+};
+
+/* Multiple sock_user's can point to the same socket. */
+struct sock_user
+{
+  struct port_info pi;
+  int isroot;
+  struct socket *sock;
+};
+
+/* Socket address ports. */
+struct sock_addr
+{
+  struct port_info pi;
+  union
+  {
+    struct sockaddr_storage storage;
+    struct sockaddr sa;
+  } address;
+};
+
+/* Owner of the underlying node.  */
+uid_t lwip_owner;
+
+/* Group of the underlying node.  */
+uid_t lwip_group;
+
+struct socket *sock_alloc (void);
+void sock_release (struct socket *);
+
+void clean_addrport (void *);
+void clean_socketport (void *);
+
+struct sock_user *make_sock_user (struct socket *, int, int, int);
+error_t make_sockaddr_port (int, int, mach_port_t *, mach_msg_type_name_t *);
+
+void init_ifs (void *);
+
+/* Install portclass on node NAME. */
+void translator_bind (int portclass, const char *name);
+
+#endif
diff --git a/lwip/lwip-util.c b/lwip/lwip-util.c
new file mode 100644
index 0000000..c65b093
--- /dev/null
+++ b/lwip/lwip-util.c
@@ -0,0 +1,343 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   Written by Joan Lledó.
+
+   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.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Lwip management module */
+
+#include <lwip-util.h>
+
+#include <error.h>
+#include <net/if_arp.h>
+
+#include <lwip/sockets.h>
+#include <lwip/netifapi.h>
+
+#include <lwip-hurd.h>
+#include <options.h>
+#include <netif/hurdethif.h>
+#include <netif/hurdtunif.h>
+#include <netif/hurdloopif.h>
+
+/*
+ * Detect the proper module for the given device name
+ * and returns its init callback
+ */
+static error_t
+create_netif_state (char *name, struct ifcommon *ifc)
+{
+  char *base_name;
+
+  memset (ifc, 0, sizeof (struct ifcommon));
+
+  base_name = strrchr (name, '/');
+  if (base_name)
+    base_name++;
+  else
+    base_name = name;
+
+  if (strncmp (base_name, "tun", 3) == 0)
+    ifc->init = hurdtunif_device_init;
+  else
+    ifc->init = hurdethif_device_init;
+
+  /* Freed in the module terminate callback */
+  ifc->devname = strndup (name, strlen (name));
+
+  return errno;
+}
+
+/* Some checks for IPv4 configurations */
+static int
+ipv4config_is_valid (uint32_t addr, uint32_t netmask,
+                    uint32_t gateway, uint32_t broadcast)
+{
+  /* Check whether the user provided a valid netmask */
+  if (netmask != INADDR_NONE && !ip4_addr_netmask_valid (netmask))
+    {
+      error (0, 0, "Error: Invalid network mask.\n");
+      return 0;
+    }
+
+  /* The given gateway, if any, must be in the same network as the address */
+  if (gateway != INADDR_NONE && (gateway & netmask) != (addr & netmask))
+    {
+      error (0, 0,
+            "Error: the gateway is not in the same network as the address.\n");
+      return 0;
+    }
+
+  /*
+   * LwIP doesn't allow setting the broadcast address.
+   * We must ensure the given broadcast address is the default one for this
+   * network.
+   */
+  if (broadcast != INADDR_NONE
+      && netmask != INADDR_NONE && broadcast != (addr | ~netmask))
+    {
+      error (0, 0,
+            "Error: the broadcast address doesn't match the network mask.\n");
+      return 0;
+    }
+
+  return 1;
+}
+
+/* Configure the loopback interface */
+static void
+init_loopback ()
+{
+  struct ifcommon ifc;
+
+  memset (&ifc, 0, sizeof (struct ifcommon));
+  ifc.init = hurdloopif_device_init;
+  netif_list->state = &ifc;
+
+  if_init (netif_list);
+}
+
+/* Remove the existing interfaces, but the loopback one */
+void
+remove_ifs ()
+{
+  struct netif *netif;
+
+  netif = netif_list;
+  while (netif != 0)
+    {
+      /* Skip the loopback interface */
+      if (netif_get_state (netif)->type == ARPHRD_LOOPBACK)
+       {
+         netif = netif->next;
+         continue;
+       }
+      if_terminate (netif);
+      netifapi_netif_remove (netif);
+      free (netif);
+
+      netif = netif_list;
+    }
+
+  return;
+}
+
+/* Initialize the interfaces given by the user through command line */
+void
+init_ifs (void *arg)
+{
+  error_t err;
+  struct parse_interface *in;
+  struct parse_hook *ifs;
+  struct netif *netif;
+  struct ifcommon ifc;
+  int8_t ipv6_addr_idx;
+  ip6_addr_t *address6;
+  int i;
+
+  if (netif_list != 0)
+    {
+      if (netif_list->next == 0)
+       init_loopback ();
+      else
+       remove_ifs ();
+    }
+
+  /*
+   * Go through the list backwards. For LwIP
+   * to create its list in the proper order.
+   */
+  ifs = (struct parse_hook *) arg;
+  for (in = ifs->interfaces + ifs->num_interfaces - 1;
+       in >= ifs->interfaces; in--)
+    {
+      /* The interface hasn't been completely configured */
+      if (!in->dev_name[0])
+       continue;
+
+      if (!ipv4config_is_valid (in->address.addr, in->netmask.addr,
+                               in->gateway.addr, INADDR_NONE))
+       continue;
+
+      netif = calloc (1, sizeof (struct netif));
+
+      create_netif_state (in->dev_name, &ifc);
+
+      /*
+       * Create a new interface and configre IPv4.
+       *
+       * Fifth parameter (in->name) is a hook.
+       */
+      err = netifapi_netif_add
+       (netif, &in->address, &in->netmask, &in->gateway, &ifc, if_init,
+        tcpip_input);
+      if (err)
+       {
+         /* The interface failed to init */
+         if (netif->state != in->dev_name)
+           /* It failed after setting the control block, must free it */
+           mem_free (netif->state);
+         free (netif);
+         continue;
+       }
+
+      /* Add IPv6 configuration */
+      netif->ip6_autoconfig_enabled = 1;
+      netif_create_ip6_linklocal_address (netif, 1);
+
+      /* Add user given unicast addresses */
+      for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++)
+       {
+         address6 = (ip6_addr_t *) & in->addr6[i];
+
+         if (!ip6_addr_isany (address6) && !ip6_addr_ismulticast (address6))
+           {
+             netif_add_ip6_address (netif, address6, &ipv6_addr_idx);
+
+             if (ipv6_addr_idx >= 0)
+               /* First use DAD to make sure nobody else has it */
+               netif_ip6_addr_set_state (netif, ipv6_addr_idx,
+                                         IP6_ADDR_TENTATIVE);
+             else
+               error (0, 0, "No free slot for IPv6 address: %s\n",
+                      ip6addr_ntoa (address6));
+           }
+       }
+
+      /* Up the inerface */
+      netifapi_netif_set_up (netif);
+
+      /* Set the first interface with valid gateway as default */
+      if (in->gateway.addr != INADDR_NONE)
+       {
+         netifapi_netif_set_default (netif);
+       }
+    }
+
+  /* Free the hook */
+  free (ifs->interfaces);
+  free (ifs);
+
+  return;
+}
+
+/*
+ * Change the IP configuration of an interface
+ */
+static error_t
+update_if (struct netif *netif, uint32_t addr, uint32_t netmask,
+          uint32_t peer, uint32_t broadcast, uint32_t gateway,
+          uint32_t * addr6, uint8_t * addr6_prefix_len)
+{
+  error_t err;
+  int i;
+
+  err = 0;
+
+  netifapi_netif_set_addr (netif, (ip4_addr_t *) & addr,
+                          (ip4_addr_t *) & netmask,
+                          (ip4_addr_t *) & gateway);
+
+  if (addr6)
+    for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++)
+      {
+       ip6_addr_t *laddr6 = ((ip6_addr_t *) addr6 + i);
+       if (!ip6_addr_isany (laddr6))
+         {
+           netif_ip6_addr_set (netif, i, laddr6);
+
+           if (!ip6_addr_islinklocal (laddr6))
+             netif_ip6_addr_set_state (netif, i, IP6_ADDR_TENTATIVE);
+         }
+      }
+
+  if (addr6_prefix_len)
+    for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++)
+      *(addr6_prefix_len + i) = 64;
+
+  return err;
+}
+
+/* Get the IP configuration of an interface */
+void
+inquire_device (struct netif *netif, uint32_t * addr, uint32_t * netmask,
+               uint32_t * peer, uint32_t * broadcast, uint32_t * gateway,
+               uint32_t * addr6, uint8_t * addr6_prefix_len)
+{
+  int i;
+
+  if (netif)
+    {
+      if (addr)
+       *addr = netif_ip4_addr (netif)->addr;
+
+      if (netmask)
+       *netmask = netif_ip4_netmask (netif)->addr;
+
+      if (peer)
+       *peer = INADDR_NONE;
+
+      if (broadcast)
+       *broadcast =
+         netif_ip4_addr (netif)->addr | ~netif_ip4_netmask (netif)->addr;
+
+      if (gateway)
+       *gateway = netif_ip4_gw (netif)->addr;
+
+      if (addr6)
+       for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++)
+         {
+           *(addr6 + i * 4 + 0) = netif_ip6_addr (netif, i)->addr[0];
+           *(addr6 + i * 4 + 1) = netif_ip6_addr (netif, i)->addr[1];
+           *(addr6 + i * 4 + 2) = netif_ip6_addr (netif, i)->addr[2];
+           *(addr6 + i * 4 + 3) = netif_ip6_addr (netif, i)->addr[3];
+         }
+
+      if (addr6_prefix_len)
+       for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++)
+         *(addr6_prefix_len + i) = 64;
+    }
+}
+
+/*
+ * Check and change the IP configuration of an interface.
+ * Called from ioctls.
+ */
+error_t
+configure_device (struct netif *netif, uint32_t addr, uint32_t netmask,
+                 uint32_t peer, uint32_t broadcast, uint32_t gateway,
+                 uint32_t * addr6, uint8_t * addr6_prefix_len)
+{
+  error_t err = 0;
+
+  if (netmask != INADDR_NONE)
+    /*
+     * If broadcasting is enabled and we have a netmask lesser than 31 bits
+     * long, we need to update the broadcast address too.
+     */
+    if ((netif->flags & NETIF_FLAG_BROADCAST)
+       && ip4_addr_netmask_valid (netmask) && netmask <= 0xfffffffc)
+      broadcast = (addr | ~netmask);
+
+  if (!ipv4config_is_valid (addr, netmask, gateway, broadcast))
+    err = EINVAL;
+  else
+    err = update_if (netif, addr, netmask, peer, broadcast,
+                    gateway, addr6, addr6_prefix_len);
+
+  return err;
+}
diff --git a/lwip/lwip-util.h b/lwip/lwip-util.h
new file mode 100644
index 0000000..0302233
--- /dev/null
+++ b/lwip/lwip-util.h
@@ -0,0 +1,41 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   Written by Joan Lledó.
+
+   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.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Lwip management module */
+
+#ifndef LWIP_UTIL_H
+#define LWIP_UTIL_H
+
+#define LOOP_DEV_NAME   "lo"
+
+#include <lwip/netif.h>
+
+void init_ifs (void *arg);
+
+void inquire_device (struct netif *netif, uint32_t * addr, uint32_t * netmask,
+                    uint32_t * peer, uint32_t * broadcast,
+                    uint32_t * gateway, uint32_t * addr6,
+                    uint8_t * addr6_prefix_len);
+error_t configure_device (struct netif *netif, uint32_t addr,
+                         uint32_t netmask, uint32_t peer, uint32_t broadcast,
+                         uint32_t gateway, uint32_t * addr6,
+                         uint8_t * addr6_prefix_len);
+
+#endif /* LWIP_UTIL_H */
diff --git a/lwip/main.c b/lwip/main.c
new file mode 100644
index 0000000..9f7eb9b
--- /dev/null
+++ b/lwip/main.c
@@ -0,0 +1,272 @@
+/*
+   Copyright (C) 1995,96,97,99,2000,02,07,17 Free Software Foundation, Inc.
+   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 the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <lwip-hurd.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <error.h>
+#include <fcntl.h>
+#include <argp.h>
+#include <sys/mman.h>
+#include <hurd/trivfs.h>
+
+#include <lwip_io_S.h>
+#include <lwip_socket_S.h>
+#include <lwip_pfinet_S.h>
+#include <lwip_iioctl_S.h>
+#include <lwip_startup_notify_S.h>
+
+#include <netif/hurdethif.h>
+#include <netif/hurdtunif.h>
+#include <startup.h>
+
+/* Translator initialization */
+
+extern struct argp lwip_argp;
+
+extern struct netif *netif_list;
+
+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;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, io_statbuf_t * st)
+{
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+  if (flags & FSYS_GOAWAY_FORCE)
+    exit (0);
+  else
+    {
+      /* Stop new requests.  */
+      ports_inhibit_class_rpcs (lwip_cntl_portclasses[0]);
+      ports_inhibit_class_rpcs (lwip_protid_portclasses[0]);
+      ports_inhibit_class_rpcs (lwip_cntl_portclasses[1]);
+      ports_inhibit_class_rpcs (lwip_protid_portclasses[1]);
+      ports_inhibit_class_rpcs (socketport_class);
+      ports_inhibit_class_rpcs (addrport_class);
+
+      if (ports_count_class (socketport_class) != 0
+         || ports_count_class (addrport_class) != 0)
+       {
+         /* We won't go away, so start things going again...  */
+         ports_resume_class_rpcs (addrport_class);
+         ports_resume_class_rpcs (socketport_class);
+         ports_resume_class_rpcs (lwip_cntl_portclasses[1]);
+         ports_resume_class_rpcs (lwip_protid_portclasses[1]);
+         ports_resume_class_rpcs (lwip_cntl_portclasses[0]);
+         ports_resume_class_rpcs (lwip_protid_portclasses[0]);
+
+         return EBUSY;
+       }
+
+      /* There are no sockets, so we can die without breaking anybody
+         too badly.  We don't let user ports on the /servers/socket/2
+         file keep us alive because those get cached in every process
+         that ever makes a PF_INET socket, libc copes with getting
+         MACH_SEND_INVALID_DEST and looking up the new translator.  */
+      exit (0);
+    }
+}
+
+int
+lwip_demuxer (mach_msg_header_t * inp, mach_msg_header_t * outp)
+{
+  struct port_info *pi;
+
+  /* Clear errno to prevent raising previous errors again */
+  errno = 0;
+
+  /* We have several classes in one bucket, which need to be demuxed
+     differently.  */
+  if (MACH_MSGH_BITS_LOCAL (inp->msgh_bits) ==
+      MACH_MSG_TYPE_PROTECTED_PAYLOAD)
+    pi = ports_lookup_payload (lwip_bucket,
+                              inp->msgh_protected_payload, socketport_class);
+  else
+    pi = ports_lookup_port (lwip_bucket,
+                           inp->msgh_local_port, socketport_class);
+
+  if (pi)
+    {
+      ports_port_deref (pi);
+
+      mig_routine_t routine;
+      if ((routine = lwip_io_server_routine (inp)) ||
+         (routine = lwip_socket_server_routine (inp)) ||
+         (routine = lwip_pfinet_server_routine (inp)) ||
+         (routine = lwip_iioctl_server_routine (inp)) ||
+         (routine = NULL, trivfs_demuxer (inp, outp)) ||
+         (routine = lwip_startup_notify_server_routine (inp)))
+       {
+         if (routine)
+           (*routine) (inp, outp);
+         return TRUE;
+       }
+      else
+       return FALSE;
+    }
+  else
+    {
+      mig_routine_t routine;
+      if ((routine = lwip_socket_server_routine (inp)) ||
+         (routine = lwip_pfinet_server_routine (inp)) ||
+         (routine = lwip_iioctl_server_routine (inp)) ||
+         (routine = NULL, trivfs_demuxer (inp, outp)) ||
+         (routine = lwip_startup_notify_server_routine (inp)))
+       {
+         if (routine)
+           (*routine) (inp, outp);
+         return TRUE;
+       }
+      else
+       return FALSE;
+    }
+
+  return 0;
+}
+
+void
+translator_bind (int portclass, const char *name)
+{
+  struct trivfs_control *cntl;
+  error_t err = 0;
+  mach_port_t right;
+  file_t file = file_name_lookup (name, O_CREAT | O_NOTRANS, 0666);
+
+  if (file == MACH_PORT_NULL)
+    err = errno;
+
+  if (!err)
+    {
+      if (lwip_protid_portclasses[portclass] != MACH_PORT_NULL)
+       error (1, 0, "Cannot bind one protocol to multiple nodes.\n");
+
+      err =
+       trivfs_add_protid_port_class (&lwip_protid_portclasses[portclass]);
+      if (err)
+       error (1, 0, "error creating control port class");
+
+      err = trivfs_add_control_port_class (&lwip_cntl_portclasses[portclass]);
+      if (err)
+       error (1, 0, "error creating control port class");
+
+      err = trivfs_create_control (file, lwip_cntl_portclasses[portclass],
+                                  lwip_bucket,
+                                  lwip_protid_portclasses[portclass],
+                                  lwip_bucket, &cntl);
+    }
+
+  if (!err)
+    {
+      right = ports_get_send_right (cntl);
+      err = file_set_translator (file, 0, FS_TRANS_EXCL | FS_TRANS_SET,
+                                0, 0, 0, right, MACH_MSG_TYPE_COPY_SEND);
+      mach_port_deallocate (mach_task_self (), right);
+    }
+
+  if (err)
+    error (1, err, "%s", name);
+
+  ports_port_deref (cntl);
+}
+
+int
+main (int argc, char **argv)
+{
+  error_t err;
+  struct stat st;
+  mach_port_t bootstrap;
+
+  lwip_bucket = ports_create_bucket ();
+  addrport_class = ports_create_class (clean_addrport, 0);
+  socketport_class = ports_create_class (clean_socketport, 0);
+  lwip_bootstrap_portclass = PORTCLASS_INET;
+
+  mach_port_allocate (mach_task_self (),
+                     MACH_PORT_RIGHT_RECEIVE, &fsys_identity);
+
+  /* Init the device modules */
+  hurdethif_module_init ();
+  hurdtunif_module_init ();
+
+  /* Parse options.  When successful, this configures the interfaces
+     before returning */
+  argp_parse (&lwip_argp, argc, argv, 0, 0, 0);
+
+  task_get_bootstrap_port (mach_task_self (), &bootstrap);
+  if (bootstrap == MACH_PORT_NULL)
+    error (-1, 0, "Must be started as a translator");
+
+  /* Create portclass to install on the bootstrap port. */
+  if (lwip_protid_portclasses[lwip_bootstrap_portclass] != MACH_PORT_NULL)
+    error (1, 0, "No portclass left to assign to bootstrap port");
+
+  err =
+    trivfs_add_protid_port_class (&lwip_protid_portclasses
+                                 [lwip_bootstrap_portclass]);
+  if (err)
+    error (1, 0, "error creating control port class");
+
+  err =
+    trivfs_add_control_port_class (&lwip_cntl_portclasses
+                                  [lwip_bootstrap_portclass]);
+  if (err)
+    error (1, 0, "error creating control port class");
+
+  /* Reply to our parent */
+  err = trivfs_startup (bootstrap, 0,
+                       lwip_cntl_portclasses[lwip_bootstrap_portclass],
+                       lwip_bucket,
+                       lwip_protid_portclasses[lwip_bootstrap_portclass],
+                       lwip_bucket, &lwipcntl);
+  mach_port_deallocate (mach_task_self (), bootstrap);
+  if (err)
+    {
+      return (-1);
+    }
+
+  /* Initialize status from underlying node.  */
+  lwip_owner = lwip_group = 0;
+  err = io_stat (lwipcntl->underlying, &st);
+  if (!err)
+    {
+      lwip_owner = st.st_uid;
+      lwip_group = st.st_gid;
+    }
+
+  /* Ask init to tell us when the system is going down,
+     so we can try to be friendly to our correspondents on the network.  */
+  arrange_shutdown_notification ();
+
+  ports_manage_port_operations_multithread (lwip_bucket, lwip_demuxer,
+                                           30 * 1000, 2 * 60 * 1000, 0);
+
+  return 0;
+}
diff --git a/lwip/mig-decls.h b/lwip/mig-decls.h
new file mode 100644
index 0000000..907369e
--- /dev/null
+++ b/lwip/mig-decls.h
@@ -0,0 +1,68 @@
+/*
+   Copyright (C) 1995,96,2000,2017 Free Software Foundation, Inc.
+   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 the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __LWIP_MIG_DECLS_H__
+#define __LWIP_MIG_DECLS_H__
+
+#include <lwip-hurd.h>
+
+/* MiG bogosity */
+typedef struct sock_user *sock_user_t;
+typedef struct sock_addr *sock_addr_t;
+
+static inline struct sock_user * __attribute__ ((unused))
+begin_using_socket_port (mach_port_t port)
+{
+  return ports_lookup_port (lwip_bucket, port, socketport_class);
+}
+
+static inline struct sock_user * __attribute__ ((unused))
+begin_using_socket_payload (unsigned long payload)
+{
+  return ports_lookup_payload (lwip_bucket, payload, socketport_class);
+}
+
+static inline void __attribute__ ((unused))
+end_using_socket_port (struct sock_user *user)
+{
+  if (user)
+    ports_port_deref (user);
+}
+
+static inline struct sock_addr * __attribute__ ((unused))
+begin_using_sockaddr_port (mach_port_t port)
+{
+  return ports_lookup_port (lwip_bucket, port, addrport_class);
+}
+
+static inline struct sock_addr * __attribute__ ((unused))
+begin_using_sockaddr_payload (unsigned long payload)
+{
+  return ports_lookup_payload (lwip_bucket, payload, addrport_class);
+}
+
+static inline void __attribute__ ((unused))
+end_using_sockaddr_port (struct sock_addr *addr)
+{
+  if (addr)
+    ports_port_deref (addr);
+}
+
+#endif /* __LWIP_MIG_DECLS_H__ */
diff --git a/lwip/mig-mutate.h b/lwip/mig-mutate.h
new file mode 100644
index 0000000..3ed89c5
--- /dev/null
+++ b/lwip/mig-mutate.h
@@ -0,0 +1,44 @@
+/*
+   Copyright (C) 1995,2017 Free Software Foundation, Inc.
+   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 the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Only CPP macro definitions should go in this file. */
+
+#define IO_SELECT_REPLY_PORT
+
+#define IO_INTRAN sock_user_t begin_using_socket_port (io_t)
+#define IO_INTRAN_PAYLOAD sock_user_t begin_using_socket_payload
+#define IO_DESTRUCTOR end_using_socket_port (sock_user_t)
+#define IO_IMPORTS import "mig-decls.h";
+#define IIOCTL_IMPORTS import "mig-decls.h";
+
+#define SOCKET_INTRAN sock_user_t begin_using_socket_port (socket_t)
+#define SOCKET_INTRAN_PAYLOAD sock_user_t begin_using_socket_payload
+#define SOCKET_DESTRUCTOR end_using_socket_port (sock_user_t)
+#define SOCKET_IMPORTS                         \
+  import "mig-decls.h";                                \
+  import "../libtrivfs/mig-decls.h";           \
+
+#define ADDRPORT_INTRAN sock_addr_t begin_using_sockaddr_port (addr_port_t)
+#define ADDRPORT_INTRAN_PAYLOAD sock_addr_t begin_using_sockaddr_payload
+#define ADDRPORT_DESTRUCTOR end_using_sockaddr_port (sock_addr_t)
+
+#define PF_INTRAN trivfs_protid_t trivfs_begin_using_protid (pf_t)
+#define PF_INTRAN_PAYLOAD trivfs_protid_t trivfs_begin_using_protid_payload
+#define PF_DESTRUCTOR trivfs_end_using_protid (trivfs_protid_t)
diff --git a/lwip/options.c b/lwip/options.c
new file mode 100644
index 0000000..6591ac5
--- /dev/null
+++ b/lwip/options.c
@@ -0,0 +1,342 @@
+/*
+   Copyright (C) 1996, 1997, 2000, 2001, 2006, 2007, 2017
+     Free Software Foundation, Inc.
+
+   Written by Miles Bader <address@hidden>
+
+   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.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Fsysopts and command line option parsing */
+
+#include <options.h>
+
+#include <stdlib.h>
+#include <argp.h>
+#include <argz.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if_arp.h>
+#include <error.h>
+
+#include <lwip/netif.h>
+#include <lwip/tcpip.h>
+
+#include <lwip-hurd.h>
+#include <lwip-util.h>
+#include <netif/ifcommon.h>
+
+/* Fsysopts and command line option parsing */
+
+/* Adds an empty interface slot to H, and sets H's current interface to it, or
+   returns an error. */
+static error_t
+parse_hook_add_interface (struct parse_hook *h)
+{
+  int i;
+
+  struct parse_interface *new = realloc (h->interfaces,
+                                        (h->num_interfaces +
+                                         1) *
+                                        sizeof (struct parse_interface));
+  if (!new)
+    return ENOMEM;
+
+  h->interfaces = new;
+  h->num_interfaces++;
+  h->curint = new + h->num_interfaces - 1;
+  memset (&h->curint->dev_name, 0, DEV_NAME_LEN);
+  h->curint->address.addr = INADDR_NONE;
+  h->curint->netmask.addr = INADDR_NONE;
+  h->curint->peer.addr = INADDR_NONE;
+  h->curint->gateway.addr = INADDR_NONE;
+  for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++)
+    ip6_addr_set_zero ((ip6_addr_t *) & h->curint->addr6[i]);
+
+  return 0;
+}
+
+/* Option parser */
+static error_t
+parse_opt (int opt, char *arg, struct argp_state *state)
+{
+  error_t err = 0;
+  struct parse_hook *h = state->hook;
+  int i;
+
+  /* Return _ERR from this routine */
+#define RETURN(_err)                          \
+  do { return _err; } while (0)
+
+  /* Print a parsing error message and (if exiting is turned off) return the
+     error code ERR.  */
+#define PERR(err, fmt, args...)               \
+  do { argp_error (state, fmt , ##args); RETURN (err); } while (0)
+
+  /* Like PERR but for non-parsing errors.  */
+#define FAIL(rerr, status, perr, fmt, args...)  \
+  do{ argp_failure (state, status, perr, fmt , ##args); RETURN (rerr); } 
while(0)
+
+  /* Parse STR and return the corresponding  internet address.  If STR is not
+     a valid internet address, signal an error mentioned TYPE.  */
+#undef ADDR
+#define ADDR(str, type)                         \
+  ({ unsigned long addr = inet_addr (str);      \
+     if (addr == INADDR_NONE) PERR (EINVAL, "Malformed %s", type);  \
+     addr; })
+
+  if (!arg && state->next < state->argc && (*state->argv[state->next] != '-'))
+    {
+      arg = state->argv[state->next];
+      state->next++;
+    }
+
+  switch (opt)
+    {
+      struct parse_interface *in;
+      uint8_t addr6_prefix_len;
+      ip6_addr_t *address6;
+      char *ptr;
+
+    case 'i':
+      /* An interface.  */
+      err = 0;
+
+      /* First see if a previously specified one is being re-specified.  */
+      for (in = h->interfaces; in < h->interfaces + h->num_interfaces; in++)
+       if (strcmp (in->dev_name, arg) == 0)
+         /* Re-use an old slot.  */
+         {
+           h->curint = in;
+           return 0;
+         }
+
+      if (h->curint->dev_name[0])
+       /* The current interface slot is not available.  */
+       {
+         /* Add a new interface entry.  */
+         err = parse_hook_add_interface (h);
+       }
+      in = h->curint;
+
+      strncpy (in->dev_name, arg, DEV_NAME_LEN);
+      break;
+
+    case 'a':
+      /* An address */
+      if (arg)
+       {
+         /* Check if it's legal */
+         h->curint->address.addr = ADDR (arg, "address");
+         if (!IN_CLASSA (ntohl (h->curint->address.addr))
+             && !IN_CLASSB (ntohl (h->curint->address.addr))
+             && !IN_CLASSC (ntohl (h->curint->address.addr)))
+           {
+             if (IN_MULTICAST (ntohl (h->curint->address.addr)))
+               FAIL (EINVAL, 1, 0,
+                     "%s: Cannot set interface address to multicast address",
+                     arg);
+             else
+               FAIL (EINVAL, 1, 0,
+                     "%s: Illegal or undefined network address", arg);
+           }
+       }
+      else
+       {
+         /* No address given, set default values */
+         h->curint->address.addr = ADDR ("0.0.0.0", "address");
+         h->curint->netmask.addr = ADDR ("255.0.0.0", "netmask");
+         h->curint->gateway.addr = INADDR_NONE;
+       }
+      break;
+
+    case 'm':
+      /* Netmask */
+      if (arg)
+       h->curint->netmask.addr = ADDR (arg, "netmask");
+      else
+       h->curint->netmask.addr = INADDR_NONE;
+      break;
+
+    case 'p':
+      /* Peer address */
+      if (arg)
+       h->curint->peer.addr = ADDR (arg, "peer");
+      else
+       h->curint->peer.addr = INADDR_NONE;
+      break;
+
+    case 'g':
+      /* Gateway for the current interface */
+      if (arg)
+       {
+         h->curint->gateway.addr = ADDR (arg, "gateway");
+       }
+      else
+       h->curint->gateway.addr = INADDR_NONE;
+      break;
+
+    case '4':
+      translator_bind (PORTCLASS_INET, arg);
+
+      /* Install IPv6 port class on bootstrap port. */
+      lwip_bootstrap_portclass = PORTCLASS_INET6;
+      break;
+
+    case '6':
+      translator_bind (PORTCLASS_INET6, arg);
+      break;
+
+    case 'A':
+      /* IPv6 address */
+      if (arg)
+       {
+         /* Check prefix */
+         if ((ptr = strchr (arg, '/')))
+           {
+             addr6_prefix_len = atoi (ptr + 1);
+             if (addr6_prefix_len > 128)
+               FAIL (EINVAL, 1, 0, "%s: The prefix-length is invalid", arg);
+
+             /* Remove the prefix from the address */
+             *ptr = 0;
+
+             if (addr6_prefix_len != 64)
+               {
+                 error (0, 0,
+                        "The only supported value for the prefix-length"
+                        " is /64. Defaulting to %s/64.\n", arg);
+               }
+           }
+         else
+           {
+             error (0, 0, "No prefix-length given, "
+                    "defaulting to %s/64.\n", arg);
+           }
+
+         for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++)
+           {
+             address6 = (ip6_addr_t *) & h->curint->addr6[i];
+
+             /* Is the slot free? */
+             if (!ip6_addr_isany (address6))
+               continue;
+
+             /* Use the slot */
+             if (ip6addr_aton (arg, address6) <= 0)
+               PERR (EINVAL, "Malformed address");
+
+             break;
+           }
+       }
+
+      break;
+
+    case ARGP_KEY_INIT:
+      /* Initialize our parsing state.  */
+      h = malloc (sizeof (struct parse_hook));
+      if (!h)
+       FAIL (ENOMEM, 11, ENOMEM, "option parsing");
+
+      h->interfaces = 0;
+      h->num_interfaces = 0;
+      err = parse_hook_add_interface (h);
+      if (err)
+       FAIL (err, 12, err, "option parsing");
+
+      state->hook = h;
+      break;
+
+    case ARGP_KEY_SUCCESS:
+      /* If the interface list is not empty, a previous configuration exists */
+      if (netif_list == 0)
+       /* Inititalize LwIP */
+       tcpip_init (init_ifs, h);
+      else
+       /* No need to initialize the stack again */
+       init_ifs (h);
+      break;
+
+    case ARGP_KEY_ERROR:
+      /* Parsing error occurred, free everything. */
+      free (h->interfaces);
+      free (h);
+      break;
+
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+
+  return err;
+}
+
+/* Create the output for fsysopts */
+error_t
+trivfs_append_args (struct trivfs_control * fsys, char **argz,
+                   size_t * argz_len)
+{
+  error_t err = 0;
+  struct netif *netif;
+  int i;
+  uint32_t addr, netmask, gateway;
+  uint32_t addr6[LWIP_IPV6_NUM_ADDRESSES][4];
+  uint8_t addr6_prefix_len[LWIP_IPV6_NUM_ADDRESSES];
+
+#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)
+#define ADD_ADDR_OPT(name, addr)        \
+  do { struct in_addr i;                \
+       i.s_addr = (addr);               \
+       ADD_OPT ("--%s=%s", name, inet_ntoa (i)); } while (0)
+
+  for (netif = netif_list; netif != 0; netif = netif->next)
+    {
+      /* Skip the loopback interface */
+      if (netif_get_state (netif)->type == ARPHRD_LOOPBACK)
+       {
+         continue;
+       }
+
+      inquire_device (netif, &addr, &netmask, 0, 0, &gateway,
+                     (uint32_t *) addr6, addr6_prefix_len);
+
+      ADD_OPT ("--interface=%s", netif_get_state (netif)->devname);
+      if (addr != INADDR_NONE)
+       ADD_ADDR_OPT ("address", addr);
+      if (netmask != INADDR_NONE)
+       ADD_ADDR_OPT ("netmask", netmask);
+      if (gateway != INADDR_NONE)
+       ADD_ADDR_OPT ("gateway", gateway);
+      for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++)
+       if (!ip6_addr_isany (((ip6_addr_t *) & addr6[i])))
+         ADD_OPT ("--address6=%s/%d",
+                  ip6addr_ntoa (((ip6_addr_t *) & addr6[i])),
+                  addr6_prefix_len[i]);
+    }
+
+#undef ADD_ADDR_OPT
+
+#undef ADD_OPT
+  return err;
+}
+
+struct argp lwip_argp = { options, parse_opt, 0, doc };
+
+struct argp *trivfs_runtime_argp = &lwip_argp;
diff --git a/lwip/options.h b/lwip/options.h
new file mode 100644
index 0000000..e370fab
--- /dev/null
+++ b/lwip/options.h
@@ -0,0 +1,81 @@
+/*
+   Copyright (C) 1996, 1997, 2000, 2001, 2006, 2007, 2017
+     Free Software Foundation, Inc.
+
+   Written by Miles Bader <address@hidden>
+
+   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.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Fsysopts and command line option parsing */
+
+#ifndef OPTIONS_H
+#define OPTIONS_H
+
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <argp.h>
+
+#include <lwip/ip.h>
+#include <lwip/netif.h>
+
+#define DEV_NAME_LEN    256
+
+/* Used to describe a particular interface during argument parsing.  */
+struct parse_interface
+{
+  /* The network interface in question.  */
+  char dev_name[DEV_NAME_LEN];
+
+  /* New values to apply to it. (IPv4) */
+  ip4_addr_t address, netmask, peer, gateway;
+
+  /* New IPv6 configuration to apply. */
+  uint32_t addr6[LWIP_IPV6_NUM_ADDRESSES][4];
+};
+
+/* Used to hold data during argument parsing.  */
+struct parse_hook
+{
+  /* A list of specified interfaces and their corresponding options.  */
+  struct parse_interface *interfaces;
+  size_t num_interfaces;
+
+  /* Interface to which options apply.  If the device field isn't filled in
+     then it should be by the next --interface option.  */
+  struct parse_interface *curint;
+};
+
+/* Lwip translator options.  Used for both startup and runtime.  */
+static const struct argp_option options[] = {
+  {"interface", 'i', "DEVICE", 0, "Network interface to use", 1},
+  {0, 0, 0, 0, "These apply to a given interface:", 2},
+  {"address", 'a', "ADDRESS", OPTION_ARG_OPTIONAL, "Set the network address"},
+  {"netmask", 'm', "MASK", OPTION_ARG_OPTIONAL, "Set the netmask"},
+  {"gateway", 'g', "ADDRESS", OPTION_ARG_OPTIONAL, "Set the default gateway"},
+  {"ipv4", '4', "NAME", 0, "Put active IPv4 translator on NAME"},
+  {"ipv6", '6', "NAME", 0, "Put active IPv6 translator on NAME"},
+  {"address6", 'A', "ADDR/LEN", OPTION_ARG_OPTIONAL,
+   "Set the global IPv6 address"},
+  {0}
+};
+
+static const char doc[] = "Interface-specific options before the first \
+interface specification apply to the first following interface; otherwise \
+they apply to the previously specified interface.";
+
+#endif // OPTIONS_H
diff --git a/lwip/pfinet-ops.c b/lwip/pfinet-ops.c
new file mode 100644
index 0000000..96d2d12
--- /dev/null
+++ b/lwip/pfinet-ops.c
@@ -0,0 +1,113 @@
+/*
+   Copyright (C) 2000,02,17 Free Software Foundation, Inc.
+   Written by Marcus Brinkmann.
+
+   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.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Operations offered by the stack */
+
+#include <lwip_pfinet_S.h>
+
+#include <string.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <lwip/netif.h>
+#include <sys/mman.h>
+
+#include <lwip-util.h>
+#include <netif/hurdethif.h>
+
+/*
+ * Get all the data requested by SIOCGIFCONF for a particular interface.
+ *
+ * When ifc->ifc_ifreq == NULL, this function is being called for getting
+ * the needed buffer length and not the actual data.
+ */
+static void
+dev_ifconf (struct ifconf *ifc)
+{
+  struct netif *netif;
+  struct ifreq *ifr;
+  struct sockaddr_in *saddr;
+  int len;
+
+  ifr = ifc->ifc_req;
+  len = ifc->ifc_len;
+  saddr = (struct sockaddr_in *) &ifr->ifr_addr;
+  for (netif = netif_list; netif != 0; netif = netif->next)
+    {
+      if (ifc->ifc_req != 0)
+       {
+         /* Get the data */
+         if (len < (int) sizeof (struct ifreq))
+           break;
+
+         memset (ifr, 0, sizeof (struct ifreq));
+
+         strncpy (ifr->ifr_name, netif_get_state (netif)->devname,
+                  strlen (netif_get_state (netif)->devname) + 1);
+         saddr->sin_len = sizeof (struct sockaddr_in);
+         saddr->sin_family = AF_INET;
+         saddr->sin_addr.s_addr = netif_ip4_addr (netif)->addr;
+
+         len -= sizeof (struct ifreq);
+       }
+      /* Update the needed buffer length */
+      ifr++;
+    }
+
+  ifc->ifc_len = (uintptr_t) ifr - (uintptr_t) ifc->ifc_req;
+}
+
+/* Return the list of devices in the format provided by SIOCGIFCONF
+   in IFR, but don't return more then AMOUNT bytes. If AMOUNT is
+   negative, there is no limit.  */
+error_t
+lwip_S_pfinet_siocgifconf (io_t port,
+                          vm_size_t amount,
+                          char **ifr, mach_msg_type_number_t * len)
+{
+  struct ifconf ifc;
+
+  if (amount == (vm_size_t) - 1)
+    {
+      /* Get the needed buffer length */
+      ifc.ifc_buf = 0;
+      ifc.ifc_len = 0;
+      dev_ifconf (&ifc);
+      amount = ifc.ifc_len;
+    }
+  else
+    ifc.ifc_len = amount;
+
+  if (amount > 0)
+    {
+      /* Possibly allocate a new buffer */
+      if (*len < amount)
+       ifc.ifc_buf = (char *) mmap (0, amount, PROT_READ | PROT_WRITE,
+                                    MAP_ANON, 0, 0);
+      else
+       ifc.ifc_buf = *ifr;
+
+      dev_ifconf (&ifc);
+    }
+
+  *len = ifc.ifc_len;
+  *ifr = ifc.ifc_buf;
+
+  return 0;
+}
diff --git a/lwip/port-objs.c b/lwip/port-objs.c
new file mode 100644
index 0000000..07bcab8
--- /dev/null
+++ b/lwip/port-objs.c
@@ -0,0 +1,144 @@
+/*
+   Copyright (C) 1995,2000,02,17 Free Software Foundation, Inc.
+
+   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.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Libports objects management */
+
+#include "lwip-hurd.h"
+
+#include <assert.h>
+#include <refcount.h>
+
+#include <lwip/sockets.h>
+
+/* Create a sockaddr port.  Fill in *ADDR and *ADDRTYPE accordingly.
+   The address should come from SOCK; PEER is 0 if we want this socket's
+   name and 1 if we want the peer's name. */
+error_t
+make_sockaddr_port (int sock,
+                   int peer,
+                   mach_port_t * addr, mach_msg_type_name_t * addrtype)
+{
+  struct sockaddr_storage buf;
+  int buflen = sizeof buf;
+  error_t err;
+  struct sock_addr *addrstruct;
+
+  if (peer)
+    err =
+      lwip_getpeername (sock, (struct sockaddr *) &buf,
+                       (socklen_t *) & buflen);
+  else
+    err =
+      lwip_getsockname (sock, (struct sockaddr *) &buf,
+                       (socklen_t *) & buflen);
+  if (err)
+    return -err;
+
+  err = ports_create_port (addrport_class, lwip_bucket,
+                          (offsetof (struct sock_addr, address)
+                           +buflen), &addrstruct);
+  if (!err)
+    {
+      addrstruct->address.sa.sa_family = buf.ss_family;
+      addrstruct->address.sa.sa_len = buflen;
+      memcpy (addrstruct->address.sa.sa_data,
+             ((struct sockaddr *) &buf)->sa_data,
+             buflen - offsetof (struct sockaddr, sa_data));
+      *addr = ports_get_right (addrstruct);
+      *addrtype = MACH_MSG_TYPE_MAKE_SEND;
+    }
+
+  ports_port_deref (addrstruct);
+
+  return err;
+}
+
+struct socket *
+sock_alloc (void)
+{
+  struct socket *sock;
+
+  sock = calloc (1, sizeof *sock);
+  if (!sock)
+    return 0;
+  sock->sockno = -1;
+  sock->identity = MACH_PORT_NULL;
+  refcount_init (&sock->refcnt, 1);
+
+  return sock;
+}
+
+/* This is called from the port cleanup function below, and on
+   a newly allocated socket when something went wrong in its creation.  */
+void
+sock_release (struct socket *sock)
+{
+  if (refcount_deref (&sock->refcnt) != 0)
+    return;
+
+  if (sock->sockno > -1)
+    lwip_close (sock->sockno);
+
+  if (sock->identity != MACH_PORT_NULL)
+    mach_port_destroy (mach_task_self (), sock->identity);
+
+  free (sock);
+}
+
+/* Create a sock_user structure, initialized from SOCK and ISROOT.
+   If NOINSTALL is set, don't put it in the portset.*/
+struct sock_user *
+make_sock_user (struct socket *sock, int isroot, int noinstall, int consume)
+{
+  error_t err;
+  struct sock_user *user;
+
+  assert_backtrace (sock->refcnt != 0);
+
+  if (noinstall)
+    err = ports_create_port_noinstall (socketport_class, lwip_bucket,
+                                      sizeof (struct sock_user), &user);
+  else
+    err = ports_create_port (socketport_class, lwip_bucket,
+                            sizeof (struct sock_user), &user);
+  if (err)
+    return 0;
+
+  if (!consume)
+    refcount_ref (&sock->refcnt);
+
+  user->isroot = isroot;
+  user->sock = sock;
+  return user;
+}
+
+/*  Release the referenced socket. */
+void
+clean_socketport (void *arg)
+{
+  struct sock_user *const user = arg;
+
+  sock_release (user->sock);
+}
+
+/* Nothing need be done here. */
+void
+clean_addrport (void *arg)
+{
+}
diff --git a/lwip/port/include/netif/hurdethif.h 
b/lwip/port/include/netif/hurdethif.h
new file mode 100644
index 0000000..326b1cf
--- /dev/null
+++ b/lwip/port/include/netif/hurdethif.h
@@ -0,0 +1,39 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   Written by Joan Lledó.
+
+   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.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Ethernet devices module */
+
+#ifndef LWIP_HURDETHIF_H
+#define LWIP_HURDETHIF_H
+
+#include <hurd/ports.h>
+
+#include <lwip/netif.h>
+#include <netif/ifcommon.h>
+
+typedef struct ifcommon hurdethif;
+
+/* Device initialization */
+error_t hurdethif_device_init (struct netif *netif);
+
+/* Module initialization */
+error_t hurdethif_module_init ();
+
+#endif /* LWIP_HURDETHIF_H */
diff --git a/lwip/port/include/netif/hurdloopif.h 
b/lwip/port/include/netif/hurdloopif.h
new file mode 100644
index 0000000..fb5c5b8
--- /dev/null
+++ b/lwip/port/include/netif/hurdloopif.h
@@ -0,0 +1,36 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   Written by Joan Lledó.
+
+   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.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Loopback devices module */
+
+#ifndef LWIP_HURDLOOPIF_H
+#define LWIP_HURDLOOPIF_H
+
+#include <hurd/ports.h>
+
+#include <lwip/netif.h>
+#include <netif/ifcommon.h>
+
+typedef struct ifcommon hurdloopif;
+
+/* Device initialization */
+error_t hurdloopif_device_init (struct netif *netif);
+
+#endif /* LWIP_HURDLOOPIF_H */
diff --git a/lwip/port/include/netif/hurdtunif.h 
b/lwip/port/include/netif/hurdtunif.h
new file mode 100644
index 0000000..938465b
--- /dev/null
+++ b/lwip/port/include/netif/hurdtunif.h
@@ -0,0 +1,65 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   Written by Joan Lledó.
+
+   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.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Tunnel devices module */
+
+#ifndef LWIP_HURDTUNIF_H
+#define LWIP_HURDTUNIF_H
+
+#include <hurd/ports.h>
+
+#include <lwip/netif.h>
+#include <netif/ifcommon.h>
+
+/* Queue of data in the tunnel */
+struct pbufqueue
+{
+  struct pbuf *head;
+  struct pbuf **tail;
+  uint8_t len;
+};
+
+/* Extension of the common device interface to store tunnel metadata */
+struct hurdtunif
+{
+  struct ifcommon comm;
+
+  struct trivfs_control *cntl; /* Identify the tunnel device in use */
+  file_t underlying;           /* Underlying node where the tunnel is bound */
+  struct iouser *user;         /* Restrict the access to one user at a time */
+  struct pbufqueue queue;      /* Output queue */
+
+  /* Concurrent access to the queue */
+  pthread_mutex_t lock;
+  pthread_cond_t read;
+  pthread_cond_t select;
+  uint8_t read_blocked;
+};
+
+struct port_class *tunnel_cntlclass;
+struct port_class *tunnel_class;
+
+/* Device initialization */
+error_t hurdtunif_device_init (struct netif *netif);
+
+/* Module initialization */
+error_t hurdtunif_module_init ();
+
+#endif /* LWIP_HURDTUNIF_H */
diff --git a/lwip/port/include/netif/ifcommon.h 
b/lwip/port/include/netif/ifcommon.h
new file mode 100644
index 0000000..15493dc
--- /dev/null
+++ b/lwip/port/include/netif/ifcommon.h
@@ -0,0 +1,60 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   Written by Joan Lledó.
+
+   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.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Common interface for all kinds of devices */
+
+#ifndef LWIP_IFCOMMON_H
+#define LWIP_IFCOMMON_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <device/device.h>
+
+#include <lwip/netif.h>
+
+/*
+ * Helper struct to hold private data used to operate your interface.
+ */
+struct ifcommon
+{
+  uint16_t type;
+  device_t ether_port;
+  struct port_info *readpt;
+  mach_port_t readptname;
+  char *devname;
+  uint16_t flags;
+
+  /* Callbacks */
+    error_t (*init) (struct netif * netif);
+    error_t (*terminate) (struct netif * netif);
+    error_t (*open) (struct netif * netif);
+    error_t (*close) (struct netif * netif);
+    error_t (*update_mtu) (struct netif * netif, uint32_t mtu);
+    error_t (*change_flags) (struct netif * netif, uint16_t flags);
+};
+
+error_t if_init (struct netif *netif);
+error_t if_terminate (struct netif *netif);
+error_t if_change_flags (struct netif *netif, uint16_t flags);
+
+/* Get the state from a netif */
+#define netif_get_state(netif)  ((struct ifcommon *)netif->state)
+
+#endif /* LWIP_IFCOMMON_H */
diff --git a/lwip/port/netif/hurdethif.c b/lwip/port/netif/hurdethif.c
new file mode 100644
index 0000000..bcf2e4d
--- /dev/null
+++ b/lwip/port/netif/hurdethif.c
@@ -0,0 +1,573 @@
+/*
+   Copyright (C) 1995, 1996, 1998, 1999, 2000, 2002, 2007, 2017
+     Free Software Foundation, Inc.
+
+   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 the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Ethernet devices module */
+
+#include <netif/hurdethif.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <pthread.h>
+#include <error.h>
+#include <device/device.h>
+#include <device/net_status.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+
+#include <lwip/opt.h>
+#include <lwip/def.h>
+#include <lwip/mem.h>
+#include <lwip/pbuf.h>
+#include <lwip/stats.h>
+#include <lwip/snmp.h>
+#include <lwip/ethip6.h>
+#include <lwip/etharp.h>
+
+/* Get the MAC address from an array of int */
+#define GET_HWADDR_BYTE(x,n)  (((char*)x)[n])
+
+static short ether_filter[] = {
+#ifdef NETF_IN
+  /* We have to tell the packet filtering code that we're interested in
+     incoming packets.  */
+  NETF_IN,                     /* Header.  */
+#endif
+  NETF_PUSHLIT | NETF_NOP,
+  1
+};
+
+static int ether_filter_len = sizeof (ether_filter) / sizeof (short);
+
+static struct bpf_insn bpf_ether_filter[] = {
+  {NETF_IN | NETF_BPF, 0, 0, 0},       /* Header. */
+  {BPF_LD | BPF_H | BPF_ABS, 0, 0, 12},        /* Load Ethernet type */
+  {BPF_JMP | BPF_JEQ | BPF_K, 2, 0, 0x0806},   /* Accept ARP */
+  {BPF_JMP | BPF_JEQ | BPF_K, 1, 0, 0x0800},   /* Accept IPv4 */
+  {BPF_JMP | BPF_JEQ | BPF_K, 0, 1, 0x86DD},   /* Accept IPv6 */
+  /*
+   * And return an amount of bytes equal to:
+   * MSS + IP and transport headers length + Ethernet header length
+   */
+  {BPF_RET | BPF_K, 0, 0, TCP_MSS + 0x28 + PBUF_LINK_HLEN},
+  {BPF_RET | BPF_K, 0, 0, 0},  /* Or discard it all */
+};
+
+static int bpf_ether_filter_len = sizeof (bpf_ether_filter) / sizeof (short);
+
+/* Bucket and class for the incoming data */
+struct port_bucket *etherport_bucket;
+struct port_class *etherread_class;
+
+/* Thread for the incoming data */
+static pthread_t input_thread;
+
+/* Get the device flags */
+static error_t
+hurdethif_device_get_flags (struct netif *netif, uint16_t * flags)
+{
+  error_t err = 0;
+  size_t count;
+  struct net_status status;
+  hurdethif *ethif;
+
+  memset (&status, 0, sizeof (struct net_status));
+
+  ethif = netif_get_state (netif);
+  count = NET_STATUS_COUNT;
+  err = device_get_status (ethif->ether_port,
+                          NET_STATUS, (dev_status_t) & status, &count);
+  if (err == D_INVALID_OPERATION)
+    {
+      /*
+       * eth-multiplexer doesn't support setting flags.
+       * We must ignore D_INVALID_OPERATION.
+       */
+      error (0, 0, "%s: hardware doesn't support getting flags.\n",
+            ethif->devname);
+      err = 0;
+    }
+  else if (err)
+    error (0, err, "%s: Cannot get hardware flags", ethif->devname);
+  else
+    *flags = status.flags;
+
+  return err;
+}
+
+/* Set the device flags */
+static error_t
+hurdethif_device_set_flags (struct netif *netif, uint16_t flags)
+{
+  error_t err = 0;
+  hurdethif *ethif;
+  int sflags;
+
+  sflags = flags;
+  ethif = netif_get_state (netif);
+
+  if (ethif->ether_port == MACH_PORT_NULL)
+    /* The device is closed */
+    return 0;
+
+  err = device_set_status (ethif->ether_port, NET_FLAGS, &sflags, 1);
+  if (err == D_INVALID_OPERATION)
+    {
+      /*
+       * eth-multiplexer doesn't support setting flags.
+       * We must ignore D_INVALID_OPERATION.
+       */
+      error (0, 0, "%s: hardware doesn't support setting flags.\n",
+            ethif->devname);
+      err = 0;
+    }
+  else if (err)
+    error (0, err, "%s: Cannot set hardware flags", ethif->devname);
+  else
+    ethif->flags = flags;
+
+  return err;
+}
+
+/* Use the device interface to access the device */
+static error_t
+hurdethif_device_open (struct netif *netif)
+{
+  error_t err = ERR_OK;
+  device_t master_device;
+  hurdethif *ethif = netif_get_state (netif);
+
+  if (ethif->ether_port != MACH_PORT_NULL)
+    {
+      error (0, 0, "Already opened: %s", ethif->devname);
+      return -1;
+    }
+
+  err = ports_create_port (etherread_class, etherport_bucket,
+                          sizeof (struct port_info), &ethif->readpt);
+  if (err)
+    {
+      error (0, err, "ports_create_port on %s", ethif->devname);
+    }
+  else
+    {
+      ethif->readptname = ports_get_right (ethif->readpt);
+      mach_port_insert_right (mach_task_self (), ethif->readptname,
+                             ethif->readptname, MACH_MSG_TYPE_MAKE_SEND);
+
+      mach_port_set_qlimit (mach_task_self (), ethif->readptname,
+                           MACH_PORT_QLIMIT_MAX);
+
+      master_device = file_name_lookup (ethif->devname, O_RDWR, 0);
+      if (master_device != MACH_PORT_NULL)
+       {
+         /* The device name here is the path of a device file.  */
+         err = device_open (master_device, D_WRITE | D_READ,
+                            "eth", &ethif->ether_port);
+         mach_port_deallocate (mach_task_self (), master_device);
+         if (err)
+           error (0, err, "device_open on %s", ethif->devname);
+         else
+           {
+             err = device_set_filter (ethif->ether_port, ethif->readptname,
+                                      MACH_MSG_TYPE_MAKE_SEND, 0,
+                                      (filter_array_t) bpf_ether_filter,
+                                      bpf_ether_filter_len);
+             if (err)
+               error (0, err, "device_set_filter on %s", ethif->devname);
+           }
+       }
+      else
+       {
+         /* No, perhaps a Mach device?  */
+         int file_errno = errno;
+         err = get_privileged_ports (0, &master_device);
+         if (err)
+           {
+             error (0, file_errno, "file_name_lookup %s", ethif->devname);
+             error (0, err, "and cannot get device master port");
+           }
+         else
+           {
+             err = device_open (master_device, D_WRITE | D_READ,
+                                ethif->devname, &ethif->ether_port);
+             mach_port_deallocate (mach_task_self (), master_device);
+             if (err)
+               {
+                 error (0, file_errno, "file_name_lookup %s",
+                        ethif->devname);
+                 error (0, err, "device_open(%s)", ethif->devname);
+               }
+             else
+               {
+                 err =
+                   device_set_filter (ethif->ether_port, ethif->readptname,
+                                      MACH_MSG_TYPE_MAKE_SEND, 0,
+                                      (filter_array_t) ether_filter,
+                                      ether_filter_len);
+                 if (err)
+                   error (0, err, "device_set_filter on %s", ethif->devname);
+               }
+           }
+       }
+    }
+
+  return err;
+}
+
+/* Destroy our link to the device */
+static error_t
+hurdethif_device_close (struct netif *netif)
+{
+  hurdethif *ethif = netif_get_state (netif);
+
+  if (ethif->ether_port == MACH_PORT_NULL)
+    {
+      error (0, 0, "Already closed: %s", ethif->devname);
+      return -1;
+    }
+
+  mach_port_deallocate (mach_task_self (), ethif->readptname);
+  ethif->readptname = MACH_PORT_NULL;
+  ports_destroy_right (ethif->readpt);
+  ethif->readpt = NULL;
+  device_close (ethif->ether_port);
+  mach_port_deallocate (mach_task_self (), ethif->ether_port);
+  ethif->ether_port = MACH_PORT_NULL;
+
+  return ERR_OK;
+}
+
+/*
+ * Called from lwip when outgoing data is ready
+ */
+static error_t
+hurdethif_output (struct netif *netif, struct pbuf *p)
+{
+  error_t err;
+  hurdethif *ethif = netif_get_state (netif);
+  int count;
+  uint8_t tried;
+
+  if (p->tot_len != p->len)
+    /* Drop the packet */
+    return ERR_OK;
+
+  tried = 0;
+  /* Send the data from the pbuf to the interface, one pbuf at a
+     time. The size of the data in each pbuf is kept in the ->len
+     variable. */
+  do
+    {
+      tried++;
+      err = device_write (ethif->ether_port, D_NOWAIT, 0,
+                         p->payload, p->len, &count);
+      if (err)
+       {
+         if (tried == 2)
+           /* Too many tries, abort */
+           break;
+
+         if (err == EMACH_SEND_INVALID_DEST || err == EMIG_SERVER_DIED)
+           {
+             /* Device probably just died, try to reopen it.  */
+             hurdethif_device_close (netif);
+             hurdethif_device_open (netif);
+           }
+       }
+      else if (count != p->len)
+       /* Incomplete package sent, reattempt */
+       err = -1;
+    }
+  while (err);
+
+  return ERR_OK;
+}
+
+/*
+ * Called from the demuxer when incoming data is ready
+ */
+void
+hurdethif_input (struct netif *netif, struct net_rcv_msg *msg)
+{
+  struct pbuf *p, *q;
+  uint16_t len;
+  uint16_t off;
+  uint16_t next_read;
+
+  /* Get the size of the whole packet */
+  len = PBUF_LINK_HLEN
+    + msg->packet_type.msgt_number - sizeof (struct packet_header);
+
+  /* Allocate an empty pbuf chain for the data */
+  p = pbuf_alloc (PBUF_RAW, len, PBUF_POOL);
+
+  if (p)
+    {
+      /*
+       * Iterate to fill the pbuf chain.
+       * 
+       * First read the Ethernet header from msg->header. Then read the
+       * payload from msg->packet
+       */
+      q = p;
+      off = 0;
+      do
+       {
+         if (off < PBUF_LINK_HLEN)
+           {
+             /* We still haven't ended copying the header */
+             next_read = (off + q->len) > PBUF_LINK_HLEN ?
+               (PBUF_LINK_HLEN - off) : q->len;
+             memcpy (q->payload, msg->header + off, next_read);
+
+             if ((off + q->len) > PBUF_LINK_HLEN)
+               memcpy (q->payload + PBUF_LINK_HLEN,
+                       msg->packet + sizeof (struct packet_header),
+                       q->len - next_read);
+           }
+         else
+           /* The header is copyied yet */
+           memcpy (q->payload, msg->packet +
+                   sizeof (struct packet_header) + off - PBUF_LINK_HLEN,
+                   q->len);
+
+         off += q->len;
+
+         /* q->tot_len == q->len means this was the last pbuf in the chain */
+         if (q->tot_len == q->len)
+           break;
+         else
+           q = q->next;
+       }
+      while (1);
+
+      /* Pass the pbuf chain to he input function */
+      if (netif->input (p, netif) != ERR_OK)
+       {
+         LWIP_DEBUGF (NETIF_DEBUG, ("hurdethif_input: IP input error\n"));
+         pbuf_free (p);
+         p = NULL;
+       }
+    }
+}
+
+/* Demux incoming RPCs from the device */
+int
+hurdethif_demuxer (mach_msg_header_t * inp, mach_msg_header_t * outp)
+{
+  struct net_rcv_msg *msg = (struct net_rcv_msg *) inp;
+  struct netif *netif;
+  mach_port_t local_port;
+
+  if (inp->msgh_id != NET_RCV_MSG_ID)
+    return 0;
+
+  if (MACH_MSGH_BITS_LOCAL (inp->msgh_bits) ==
+      MACH_MSG_TYPE_PROTECTED_PAYLOAD)
+    {
+      struct port_info *pi = ports_lookup_payload (NULL,
+                                                  inp->msgh_protected_payload,
+                                                  NULL);
+      if (pi)
+       {
+         local_port = pi->port_right;
+         ports_port_deref (pi);
+       }
+      else
+       local_port = MACH_PORT_NULL;
+    }
+  else
+    local_port = inp->msgh_local_port;
+
+  for (netif = netif_list; netif; netif = netif->next)
+    if (local_port == netif_get_state (netif)->readptname)
+      break;
+
+  if (!netif)
+    {
+      if (inp->msgh_remote_port != MACH_PORT_NULL)
+       mach_port_deallocate (mach_task_self (), inp->msgh_remote_port);
+      return 1;
+    }
+
+  hurdethif_input (netif, msg);
+
+  return 1;
+}
+
+/*
+ * Update the interface's MTU and the BPF filter
+ */
+static error_t
+hurdethif_device_update_mtu (struct netif *netif, uint32_t mtu)
+{
+  error_t err = 0;
+
+  netif->mtu = mtu;
+
+  bpf_ether_filter[5].k = mtu + PBUF_LINK_HLEN;
+
+  return err;
+}
+
+/*
+ * Release all resources of this netif.
+ *
+ * Returns 0 on success.
+ */
+static error_t
+hurdethif_device_terminate (struct netif *netif)
+{
+  /* Free the hook */
+  free (netif_get_state (netif)->devname);
+  free (netif_get_state (netif));
+
+  return 0;
+}
+
+/*
+ * Initializes a single device.
+ * 
+ * The module must be initialized before calling this function.
+ */
+error_t
+hurdethif_device_init (struct netif * netif)
+{
+  error_t err;
+  size_t count = 2;
+  int net_address[2];
+  device_t ether_port;
+  hurdethif *ethif;
+
+  /*
+   * Replace the hook by a new one with the proper size.
+   * The old one is in the stack and will be removed soon.
+   */
+  ethif = calloc (1, sizeof (hurdethif));
+  if (!ethif)
+    {
+      LWIP_DEBUGF (NETIF_DEBUG, ("hurdethif_init: out of memory\n"));
+      return ERR_MEM;
+    }
+  memcpy (ethif, netif_get_state (netif), sizeof (struct ifcommon));
+  netif->state = ethif;
+
+  /* Interface type */
+  ethif->type = ARPHRD_ETHER;
+
+  /* Set callbacks */
+  netif->output = etharp_output;
+  netif->output_ip6 = ethip6_output;
+  netif->linkoutput = hurdethif_output;
+
+  ethif->open = hurdethif_device_open;
+  ethif->close = hurdethif_device_close;
+  ethif->terminate = hurdethif_device_terminate;
+  ethif->update_mtu = hurdethif_device_update_mtu;
+  ethif->change_flags = hurdethif_device_set_flags;
+
+  /* ---- Hardware initialization ---- */
+
+  /* We need the device to be opened to configure it */
+  err = hurdethif_device_open (netif);
+  if (err)
+    return err;
+
+  /* Get the MAC address */
+  ether_port = netif_get_state (netif)->ether_port;
+  err = device_get_status (ether_port, NET_ADDRESS, net_address, &count);
+  if (err)
+    error (0, err, "%s: Cannot get hardware Ethernet address",
+          netif_get_state (netif)->devname);
+  else if (count * sizeof (int) >= ETHARP_HWADDR_LEN)
+    {
+      net_address[0] = ntohl (net_address[0]);
+      net_address[1] = ntohl (net_address[1]);
+
+      /* Set MAC hardware address length */
+      netif->hwaddr_len = ETHARP_HWADDR_LEN;
+
+      /* Set MAC hardware address */
+      netif->hwaddr[0] = GET_HWADDR_BYTE (net_address, 0);
+      netif->hwaddr[1] = GET_HWADDR_BYTE (net_address, 1);
+      netif->hwaddr[2] = GET_HWADDR_BYTE (net_address, 2);
+      netif->hwaddr[3] = GET_HWADDR_BYTE (net_address, 3);
+      netif->hwaddr[4] = GET_HWADDR_BYTE (net_address, 4);
+      netif->hwaddr[5] = GET_HWADDR_BYTE (net_address, 5);
+    }
+  else
+    error (0, 0, "%s: Invalid Ethernet address",
+          netif_get_state (netif)->devname);
+
+  /* Maximum transfer unit: MSS + IP header size + TCP header size */
+  netif->mtu = TCP_MSS + 20 + 20;
+
+  /* Enable Ethernet multicasting */
+  hurdethif_device_get_flags (netif, &netif_get_state (netif)->flags);
+  netif_get_state (netif)->flags |=
+    IFF_UP | IFF_RUNNING | IFF_BROADCAST | IFF_ALLMULTI;
+  hurdethif_device_set_flags (netif, netif_get_state (netif)->flags);
+
+  /*
+   * Up the link, set the interface type to NETIF_FLAG_ETHARP
+   * and enable other features.
+   */
+  netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP
+    | NETIF_FLAG_IGMP | NETIF_FLAG_MLD6;
+
+  return ERR_OK;
+}
+
+static void *
+hurdethif_input_thread (void *arg)
+{
+  ports_manage_port_operations_one_thread (etherport_bucket,
+                                          hurdethif_demuxer, 0);
+
+  return 0;
+}
+
+/*
+ * Init the thread for the incoming data.
+ *
+ * This function should be called once.
+ */
+error_t
+hurdethif_module_init ()
+{
+  error_t err;
+  etherport_bucket = ports_create_bucket ();
+  etherread_class = ports_create_class (0, 0);
+
+  err = pthread_create (&input_thread, 0, hurdethif_input_thread, 0);
+  if (!err)
+    pthread_detach (input_thread);
+  else
+    {
+      errno = err;
+      perror ("pthread_create");
+    }
+
+  return err;
+}
diff --git a/lwip/port/netif/hurdloopif.c b/lwip/port/netif/hurdloopif.c
new file mode 100644
index 0000000..ef64b8b
--- /dev/null
+++ b/lwip/port/netif/hurdloopif.c
@@ -0,0 +1,112 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   Written by Joan Lledó.
+
+   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.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Loopback devices module */
+
+#include <netif/hurdloopif.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <string.h>
+
+#include <lwip-util.h>
+
+/* Set the device flags */
+static error_t
+hurdloopif_device_set_flags (struct netif *netif, uint16_t flags)
+{
+  error_t err = 0;
+  hurdloopif *loopif;
+
+  loopif = netif_get_state (netif);
+  loopif->flags = flags;
+
+  return err;
+}
+
+/*
+ * Update the interface's MTU
+ */
+static error_t
+hurdloopif_device_update_mtu (struct netif *netif, uint32_t mtu)
+{
+  error_t err = 0;
+
+  netif->mtu = mtu;
+
+  return err;
+}
+
+/*
+ * Release all resources of this netif.
+ *
+ * Returns 0 on success.
+ */
+static error_t
+hurdloopif_device_terminate (struct netif *netif)
+{
+  /* Free the hook */
+  free (netif_get_state (netif)->devname);
+  free (netif_get_state (netif));
+
+  return 0;
+}
+
+/*
+ * Set up the LwIP loopback interface
+ */
+error_t
+hurdloopif_device_init (struct netif * netif)
+{
+  error_t err = 0;
+  hurdloopif *loopif;
+
+  /*
+   * Replace the hook by a new one with the proper size.
+   * The old one is in the stack and will be removed soon.
+   */
+  loopif = calloc (1, sizeof (hurdloopif));
+  if (loopif == NULL)
+    {
+      LWIP_DEBUGF (NETIF_DEBUG, ("hurdloopif_init: out of memory\n"));
+      return ERR_MEM;
+    }
+  memcpy (loopif, netif_get_state (netif), sizeof (struct ifcommon));
+  netif->state = loopif;
+
+  /* Device name and type */
+  loopif->devname = LOOP_DEV_NAME;
+  loopif->type = ARPHRD_LOOPBACK;
+
+  /* MTU = MSS + IP header + TCP header */
+  netif->mtu = TCP_MSS + 20 + 20;
+
+  /* Set flags */
+  hurdloopif_device_set_flags (netif, IFF_UP | IFF_RUNNING | IFF_LOOPBACK);
+
+  /* Set callbacks */
+  loopif->open = 0;
+  loopif->close = 0;
+  loopif->terminate = hurdloopif_device_terminate;
+  loopif->update_mtu = hurdloopif_device_update_mtu;
+  loopif->change_flags = hurdloopif_device_set_flags;
+
+  return err;
+}
diff --git a/lwip/port/netif/hurdtunif.c b/lwip/port/netif/hurdtunif.c
new file mode 100644
index 0000000..d7991ba
--- /dev/null
+++ b/lwip/port/netif/hurdtunif.c
@@ -0,0 +1,721 @@
+/*
+   Copyright (C) 1995,96,98,99,2000,02,17 Free Software Foundation, Inc.
+   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 the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Tunnel devices module */
+
+#include <netif/hurdtunif.h>
+
+#include <hurd/trivfs.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <error.h>
+#include <sys/mman.h>
+
+#include <lwip-hurd.h>
+
+/* Add to the end of the queue */
+static void
+enqueue (struct pbufqueue *q, struct pbuf *p)
+{
+  *(q->tail) = p;
+  p->next = 0;
+  q->tail = &p->next;
+
+  q->len++;
+}
+
+/* Get from the head of the queue */
+static struct pbuf *
+dequeue (struct pbufqueue *q)
+{
+  struct pbuf *ret;
+
+  if (!q->head)
+    return 0;
+
+  ret = q->head;
+  q->head = q->head->next;
+  ret->next = 0;
+  q->len--;
+
+  if (!q->head)
+    q->tail = &q->head;
+
+  return ret;
+}
+
+/*
+ * Update the interface's MTU
+ */
+static error_t
+hurdtunif_device_update_mtu (struct netif *netif, uint32_t mtu)
+{
+  error_t err = 0;
+
+  netif->mtu = mtu;
+
+  return err;
+}
+
+/* Set the device flags */
+static error_t
+hurdtunif_device_set_flags (struct netif *netif, uint16_t flags)
+{
+  error_t err = 0;
+  struct ifcommon *tunif;
+
+  tunif = netif_get_state (netif);
+  tunif->flags = flags;
+
+  return err;
+}
+
+/*
+ * Release all resources of this netif.
+ *
+ * Returns 0 on success.
+ */
+static error_t
+hurdtunif_device_terminate (struct netif *netif)
+{
+  struct pbuf *p;
+  struct hurdtunif *tunif = (struct hurdtunif *) netif_get_state (netif);
+
+  /* Clear the queue */
+  while ((p = dequeue (&tunif->queue)) != 0)
+    pbuf_free (p);
+  pthread_cond_destroy (&tunif->read);
+  pthread_cond_destroy (&tunif->select);
+  pthread_mutex_destroy (&tunif->lock);
+
+  /* Free the hook */
+  free (netif_get_state (netif)->devname);
+  free (netif_get_state (netif));
+
+  return 0;
+}
+
+/*
+ * Called from lwip.
+ *
+ * Just enqueue the data.
+ */
+static error_t
+hurdtunif_output (struct netif *netif, struct pbuf *p,
+                 const ip4_addr_t * ipaddr)
+{
+  error_t err = 0;
+  struct hurdtunif *tunif;
+  struct pbuf *pcopy, *oldest;
+
+  tunif = (struct hurdtunif *) netif_get_state (netif);
+
+  /*
+   * The stack is responsible for allocating and freeing the pbuf p.
+   * Sometimes it keeps the pbuf for the case it needs to be retransmitted,
+   * but at other times it frees the pbuf while it's still in our queue,
+   * that's why we need a copy.
+   */
+  pcopy = pbuf_alloc (PBUF_IP, p->tot_len, PBUF_RAM);
+  if (pcopy != NULL)
+    if (pbuf_copy (pcopy, p) != ERR_OK)
+      {
+       pbuf_free (pcopy);
+       pcopy = NULL;
+      }
+
+  pthread_mutex_lock (&tunif->lock);
+
+  /* Avoid unlimited growth. */
+  if (tunif->queue.len > 128)
+    {
+      oldest = dequeue (&tunif->queue);
+      pbuf_free (oldest);
+    }
+
+  enqueue (&tunif->queue, pcopy);
+
+  if (tunif->read_blocked)
+    {
+      tunif->read_blocked = 0;
+      pthread_cond_broadcast (&tunif->read);
+      pthread_cond_broadcast (&tunif->select);
+    }
+
+  pthread_mutex_unlock (&tunif->lock);
+
+  return err;
+}
+
+/*
+ * Set up the tunnel a new tunnel device
+ */
+error_t
+hurdtunif_device_init (struct netif * netif)
+{
+  error_t err = 0;
+  struct hurdtunif *tunif;
+  char *base_name, *name = netif_get_state (netif)->devname;
+
+  /*
+   * Replace the hook by a new one with the proper size.
+   * The old one is in the stack and will be removed soon.
+   */
+  tunif = calloc (1, sizeof (struct hurdtunif));
+  if (tunif == NULL)
+    {
+      LWIP_DEBUGF (NETIF_DEBUG, ("hurdtunif_init: out of memory\n"));
+      return ERR_MEM;
+    }
+  memcpy (tunif, netif_get_state (netif), sizeof (struct ifcommon));
+  netif->state = tunif;
+
+  base_name = strrchr (name, '/');
+  if (base_name)
+    /* The user provided a path */
+    base_name++;
+  else
+    /* The user provided a name for the tunnel. We'll create it at /dev */
+    base_name = name;
+
+  if (base_name != name)
+    tunif->comm.devname = strdup (name);
+  else
+    /* Setting up the translator at /dev/tunX.  */
+    asprintf (&tunif->comm.devname, "/dev/%s", base_name);
+
+  /* Set the device type */
+  tunif->comm.type = ARPHRD_TUNNEL;
+
+  /* MTU = MSS + IP header + TCP header */
+  netif->mtu = TCP_MSS + 20 + 20;
+
+  /* Set flags */
+  hurdtunif_device_set_flags (netif,
+                             IFF_UP | IFF_RUNNING | IFF_POINTOPOINT |
+                             IFF_NOARP);
+
+  netif->flags = NETIF_FLAG_LINK_UP;
+
+  /* Set the callbacks */
+  netif->output = hurdtunif_output;
+  tunif->comm.open = 0;
+  tunif->comm.close = 0;
+  tunif->comm.terminate = hurdtunif_device_terminate;
+  tunif->comm.update_mtu = hurdtunif_device_update_mtu;
+  tunif->comm.change_flags = hurdtunif_device_set_flags;
+
+  /* Bind the translator to tunif->comm.devname */
+  tunif->underlying = file_name_lookup (tunif->comm.devname,
+                                       O_CREAT | O_NOTRANS, 0664);
+
+  if (tunif->underlying == MACH_PORT_NULL)
+    {
+      error (0, 0, "%s", tunif->comm.devname);
+      return -1;
+    }
+
+  err = trivfs_create_control (tunif->underlying, tunnel_cntlclass,
+                              lwip_bucket, tunnel_class, lwip_bucket,
+                              &tunif->cntl);
+
+  if (!err)
+    {
+      mach_port_t right = ports_get_send_right (tunif->cntl);
+      err = file_set_translator (tunif->underlying, 0,
+                                FS_TRANS_SET | FS_TRANS_ORPHAN, 0, 0, 0,
+                                right, MACH_MSG_TYPE_COPY_SEND);
+      mach_port_deallocate (mach_task_self (), right);
+    }
+
+  if (err)
+    error (0, err, "%s", tunif->comm.devname);
+
+  /* We'll need to get the netif from trivfs operations */
+  tunif->cntl->hook = netif;
+
+  /* Output queue initialization */
+  tunif->queue.head = 0;
+  tunif->queue.tail = &tunif->queue.head;
+  tunif->queue.len = 0;
+  pthread_mutex_init (&tunif->lock, NULL);
+  pthread_cond_init (&tunif->read, NULL);
+  pthread_cond_init (&tunif->select, NULL);
+  tunif->read_blocked = 0;
+
+  return err;
+}
+
+/*
+ * Set libports classes
+ *
+ * This function should be called once.
+ */
+error_t
+hurdtunif_module_init ()
+{
+  error_t err = 0;
+
+  trivfs_add_control_port_class (&tunnel_cntlclass);
+  trivfs_add_protid_port_class (&tunnel_class);
+
+  return err;
+}
+
+/* If a new open with read and/or write permissions is requested,
+   restrict to exclusive usage.  */
+static error_t
+check_open_hook (struct trivfs_control *cntl, struct iouser *user, int flags)
+{
+  struct netif *netif;
+  struct hurdtunif *tunif;
+
+  for (netif = netif_list; netif; netif = netif->next)
+    {
+      tunif = (struct hurdtunif *) netif_get_state (netif);
+      if (tunif->cntl == cntl)
+       break;
+    }
+
+  if (netif && flags != O_NORW)
+    {
+      if (tunif->user)
+       return EBUSY;
+      else
+       tunif->user = user;
+    }
+
+  return 0;
+}
+
+/* When a protid is destroyed, check if it is the current user.
+   If yes, release the interface for other users.  */
+static void
+pi_destroy_hook (struct trivfs_protid *cred)
+{
+  struct netif *netif;
+  struct hurdtunif *tunif;
+
+  if (cred->pi.class != tunnel_class)
+    return;
+
+  netif = (struct netif *) cred->po->cntl->hook;
+  tunif = (struct hurdtunif *) netif_get_state (netif);
+
+  if (tunif->user == cred->user)
+    tunif->user = 0;
+}
+
+/* If this variable is set, it is called every time a new peropen
+   structure is created and initialized. */
+error_t (*trivfs_check_open_hook) (struct trivfs_control *,
+                                  struct iouser *, int) = check_open_hook;
+
+/* If this variable is set, it is called every time a protid structure
+   is about to be destroyed. */
+void (*trivfs_protid_destroy_hook) (struct trivfs_protid *) = pi_destroy_hook;
+
+/* Read data from an IO object.  If offset is -1, read from the object
+   maintained file pointer.  If the object is not seekable, offset is
+   ignored.  The amount desired to be read is in AMOUNT.  */
+error_t
+trivfs_S_io_read (struct trivfs_protid *cred,
+                 mach_port_t reply, mach_msg_type_name_t reply_type,
+                 char **data, mach_msg_type_number_t * data_len,
+                 loff_t offs, size_t amount)
+{
+  struct hurdtunif *tunif;
+  struct pbuf *p;
+
+  if (!cred)
+    return EOPNOTSUPP;
+
+  if (cred->pi.class != tunnel_class)
+    return EOPNOTSUPP;
+
+  tunif =
+    (struct hurdtunif *)
+    netif_get_state (((struct netif *) cred->po->cntl->hook));
+
+  pthread_mutex_lock (&tunif->lock);
+
+  while (tunif->queue.len == 0)
+    {
+      if (cred->po->openmodes & O_NONBLOCK)
+       {
+         pthread_mutex_unlock (&tunif->lock);
+         return EWOULDBLOCK;
+       }
+
+      tunif->read_blocked = 1;
+      if (pthread_hurd_cond_wait_np (&tunif->read, &tunif->lock))
+       {
+         pthread_mutex_unlock (&tunif->lock);
+         return EINTR;
+       }
+    }
+
+  p = dequeue (&tunif->queue);
+
+  if (p->tot_len < amount)
+    amount = p->tot_len;
+  if (amount > 0)
+    {
+      /* Possibly allocate a new buffer. */
+      if (*data_len < amount)
+       {
+         *data = mmap (0, amount, PROT_READ | PROT_WRITE, MAP_ANON, 0, 0);
+         if (*data == MAP_FAILED)
+           {
+             pbuf_free (p);
+             pthread_mutex_unlock (&tunif->lock);
+             return ENOMEM;
+           }
+       }
+
+      /* Copy the constant data into the buffer. */
+      memcpy ((char *) *data, p->payload, amount);
+    }
+  *data_len = amount;
+  pbuf_free (p);
+
+  pthread_mutex_unlock (&tunif->lock);
+
+  return 0;
+}
+
+/* Write data to an IO object.  If offset is -1, write at the object
+   maintained file pointer.  If the object is not seekable, offset is
+   ignored.  The amount successfully written is returned in amount.  A
+   given user should not have more than one outstanding io_write on an
+   object at a time; servers implement congestion control by delaying
+   responses to io_write.  Servers may drop data (returning ENOBUFS)
+   if they receive more than one write when not prepared for it.  */
+error_t
+trivfs_S_io_write (struct trivfs_protid * cred,
+                  mach_port_t reply,
+                  mach_msg_type_name_t replytype,
+                  char *data,
+                  mach_msg_type_number_t datalen,
+                  off_t offset, mach_msg_type_number_t * amount)
+{
+  struct netif *netif;
+  struct pbuf *p, *q;
+  uint16_t off;
+
+  /* Deny access if they have bad credentials. */
+  if (!cred)
+    return EOPNOTSUPP;
+
+  else if (!(cred->po->openmodes & O_WRITE))
+    return EBADF;
+
+  if (cred->pi.class != tunnel_class)
+    return EOPNOTSUPP;
+
+  netif = (struct netif *) cred->po->cntl->hook;
+
+  /* Allocate an empty pbuf chain for the data */
+  p = pbuf_alloc (PBUF_RAW, datalen, PBUF_POOL);
+
+  if (p)
+    {
+      /* Iterate to fill the pbuf chain. */
+      q = p;
+      off = 0;
+      do
+       {
+         memcpy (q->payload, data, q->len);
+
+         off += q->len;
+
+         if (q->tot_len == q->len)
+           break;
+         else
+           q = q->next;
+       }
+      while (1);
+
+      /* pass it to the stack */
+      if (netif->input (p, netif) != ERR_OK)
+       {
+         LWIP_DEBUGF (NETIF_DEBUG, ("trivfs_S_io_write: IP input error\n"));
+         pbuf_free (p);
+         p = NULL;
+       }
+
+      *amount = datalen;
+    }
+
+  return 0;
+}
+
+/* Tell how much data can be read from the object without blocking for
+   a "long time" (this should be the same meaning of "long time" used
+   by the nonblocking flag.  */
+kern_return_t
+trivfs_S_io_readable (struct trivfs_protid * cred,
+                     mach_port_t reply, mach_msg_type_name_t replytype,
+                     mach_msg_type_number_t * amount)
+{
+  struct hurdtunif *tunif;
+
+  if (!cred)
+    return EOPNOTSUPP;
+
+  if (cred->pi.class != tunnel_class)
+    return EOPNOTSUPP;
+
+  tunif =
+    (struct hurdtunif *)
+    netif_get_state (((struct netif *) cred->po->cntl->hook));
+
+  pthread_mutex_lock (&tunif->lock);
+
+  if (tunif->queue.head)
+    *amount = tunif->queue.head->tot_len;
+  else
+    *amount = 0;
+
+  pthread_mutex_unlock (&tunif->lock);
+
+  return 0;
+}
+
+/* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and SELECT_URG.
+   Block until one of the indicated types of i/o can be done "quickly", and
+   return the types that are then available.  ID_TAG is returned as passed; it
+   is just for the convenience of the user in matching up reply messages with
+   specific requests sent.  */
+static error_t
+io_select_common (struct trivfs_protid *cred,
+                 mach_port_t reply,
+                 mach_msg_type_name_t reply_type,
+                 struct timespec *tsp, int *type)
+{
+  error_t err;
+  struct hurdtunif *tunif;
+
+  if (!cred)
+    return EOPNOTSUPP;
+
+  if (cred->pi.class != tunnel_class)
+    return EOPNOTSUPP;
+
+  /* Make this thread cancellable */
+  ports_interrupt_self_on_port_death (cred, reply);
+
+  /* We only deal with SELECT_READ and SELECT_WRITE here.  */
+  *type &= SELECT_READ | SELECT_WRITE;
+
+  if (*type == 0)
+    return 0;
+
+  tunif =
+    (struct hurdtunif *)
+    netif_get_state (((struct netif *) cred->po->cntl->hook));
+
+  pthread_mutex_lock (&tunif->lock);
+
+  if (*type & SELECT_WRITE)
+    {
+      /* We are always writable.  */
+      if (tunif->queue.len == 0)
+       *type &= ~SELECT_READ;
+      pthread_mutex_unlock (&tunif->lock);
+      return 0;
+    }
+
+  while (1)
+    {
+      /* There's data on the queue */
+      if (tunif->queue.len != 0)
+       {
+         *type = SELECT_READ;
+         pthread_mutex_unlock (&tunif->lock);
+         return 0;
+       }
+
+      /* The queue is empty, we must wait */
+      tunif->read_blocked = 1;
+      err =
+       pthread_hurd_cond_timedwait_np (&tunif->select, &tunif->lock, tsp);
+      if (err)
+       {
+         *type = 0;
+         pthread_mutex_unlock (&tunif->lock);
+
+         if (err == ETIMEDOUT)
+           err = 0;
+
+         return err;
+       }
+    }
+}
+
+error_t
+trivfs_S_io_select (struct trivfs_protid * cred,
+                   mach_port_t reply,
+                   mach_msg_type_name_t reply_type, int *type)
+{
+  return io_select_common (cred, reply, reply_type, NULL, type);
+}
+
+error_t
+trivfs_S_io_select_timeout (struct trivfs_protid * cred,
+                           mach_port_t reply,
+                           mach_msg_type_name_t reply_type,
+                           struct timespec ts, int *type)
+{
+  return io_select_common (cred, reply, reply_type, &ts, type);
+}
+
+/* Change current read/write offset */
+error_t
+trivfs_S_io_seek (struct trivfs_protid * cred,
+                 mach_port_t reply, mach_msg_type_name_t reply_type,
+                 off_t offs, int whence, off_t * new_offs)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  if (cred->pi.class != tunnel_class)
+    return EOPNOTSUPP;
+
+  return ESPIPE;
+}
+
+/* Change the size of the file.  If the size increases, new blocks are
+   zero-filled.  After successful return, it is safe to reference mapped
+   areas of the file up to NEW_SIZE.  */
+error_t
+trivfs_S_file_set_size (struct trivfs_protid * cred,
+                       mach_port_t reply, mach_msg_type_name_t reply_type,
+                       off_t size)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  if (cred->pi.class != tunnel_class)
+    return EOPNOTSUPP;
+
+  return size == 0 ? 0 : EINVAL;
+}
+
+/* These four routines modify the O_APPEND, O_ASYNC, O_FSYNC, and
+   O_NONBLOCK bits for the IO object. In addition, io_get_openmodes
+   will tell you which of O_READ, O_WRITE, and O_EXEC the object can
+   be used for.  The O_ASYNC bit affects icky async I/O; good async
+   I/O is done through io_async which is orthogonal to these calls. */
+error_t
+trivfs_S_io_set_all_openmodes (struct trivfs_protid * cred,
+                              mach_port_t reply,
+                              mach_msg_type_name_t reply_type, int mode)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  if (cred->pi.class != tunnel_class)
+    return EOPNOTSUPP;
+
+  return 0;
+}
+
+error_t
+trivfs_S_io_set_some_openmodes (struct trivfs_protid * cred,
+                               mach_port_t reply,
+                               mach_msg_type_name_t reply_type, int bits)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  if (cred->pi.class != tunnel_class)
+    return EOPNOTSUPP;
+
+  return 0;
+}
+
+error_t
+trivfs_S_io_clear_some_openmodes (struct trivfs_protid * cred,
+                                 mach_port_t reply,
+                                 mach_msg_type_name_t reply_type, int bits)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  if (cred->pi.class != tunnel_class)
+    return EOPNOTSUPP;
+
+  return 0;
+}
+
+error_t
+trivfs_S_io_get_owner (struct trivfs_protid * cred,
+                      mach_port_t reply,
+                      mach_msg_type_name_t reply_type, pid_t * owner)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  if (cred->pi.class != tunnel_class)
+    return EOPNOTSUPP;
+
+  *owner = 0;
+  return 0;
+}
+
+error_t
+trivfs_S_io_mod_owner (struct trivfs_protid * cred,
+                      mach_port_t reply, mach_msg_type_name_t reply_type,
+                      pid_t owner)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  if (cred->pi.class != tunnel_class)
+    return EOPNOTSUPP;
+
+  return EINVAL;
+}
+
+/* Return objects mapping the data underlying this memory object.  If
+   the object can be read then memobjrd will be provided; if the
+   object can be written then memobjwr will be provided.  For objects
+   where read data and write data are the same, these objects will be
+   equal, otherwise they will be disjoint.  Servers are permitted to
+   implement io_map but not io_map_cntl.  Some objects do not provide
+   mapping; they will set none of the ports and return an error.  Such
+   objects can still be accessed by io_read and io_write.  */
+error_t
+trivfs_S_io_map (struct trivfs_protid * cred,
+                mach_port_t reply,
+                mach_msg_type_name_t replyPoly,
+                memory_object_t * rdobj,
+                mach_msg_type_name_t * rdtype,
+                memory_object_t * wrobj, mach_msg_type_name_t * wrtype)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  if (cred->pi.class != tunnel_class)
+    return EOPNOTSUPP;
+
+  return EINVAL;
+}
diff --git a/lwip/port/netif/ifcommon.c b/lwip/port/netif/ifcommon.c
new file mode 100644
index 0000000..11ede76
--- /dev/null
+++ b/lwip/port/netif/ifcommon.c
@@ -0,0 +1,121 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   Written by Joan Lledó.
+
+   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.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Common interface for all kinds of devices */
+
+#include <netif/ifcommon.h>
+
+#include <net/if.h>
+
+#include <lwip/netifapi.h>
+
+/* Open the device and set the interface up */
+static error_t
+if_open (struct netif *netif)
+{
+  error_t err = 0;
+  struct ifcommon *ifc = netif_get_state (netif);
+
+  if (ifc->open)
+    err = ifc->open (netif);
+  if (!err)
+    {
+      /* Up the inerface */
+      ifc->flags |= IFF_UP | IFF_RUNNING;
+      netifapi_netif_set_up (netif);
+    }
+
+  return err;
+}
+
+/* Close the device and set the interface down */
+static error_t
+if_close (struct netif *netif)
+{
+  error_t err = 0;
+  struct ifcommon *ifc = netif_get_state (netif);
+
+  if (ifc->close)
+    err = ifc->close (netif);
+  if (!err)
+    {
+      /* Down the inerface */
+      ifc->flags &= ~(IFF_UP | IFF_RUNNING);
+      netifapi_netif_set_down (netif);
+    }
+
+  return err;
+}
+
+/*
+ * Common initialization callback for all kinds of devices.
+ *
+ * This function doesn't assume there's a device nor tries to open it.
+ * If a device is present, it must be opened from the ifc->init() callback.
+ */
+error_t
+if_init (struct netif * netif)
+{
+  struct ifcommon *ifc = netif_get_state (netif);
+
+  if (netif == NULL)
+    /* The user provided no interface */
+    return -1;
+
+  return ifc->init (netif);
+}
+
+/* Tries to close the device and frees allocated resources */
+error_t
+if_terminate (struct netif * netif)
+{
+  error_t err;
+  struct ifcommon *ifc = netif_get_state (netif);
+
+  if (netif == NULL)
+    /* The user provided no interface */
+    return -1;
+
+  err = if_close (netif);
+  if (err)
+    return err;
+
+  return ifc->terminate (netif);
+}
+
+/*
+ * Change device flags.
+ *
+ * If IFF_UP changes, it opens/closes the device accordingly.
+ */
+error_t
+if_change_flags (struct netif * netif, uint16_t flags)
+{
+  error_t err;
+  struct ifcommon *ifc = netif_get_state (netif);
+  uint16_t oldflags = ifc->flags;
+
+  err = ifc->change_flags (netif, flags);
+
+  if ((oldflags ^ flags) & IFF_UP)     /* Bit is different  ? */
+    ((oldflags & IFF_UP) ? if_close : if_open) (netif);
+
+  return err;
+}
diff --git a/lwip/socket-ops.c b/lwip/socket-ops.c
new file mode 100644
index 0000000..62b36e0
--- /dev/null
+++ b/lwip/socket-ops.c
@@ -0,0 +1,451 @@
+/*
+   Copyright (C) 1995,96,97,99,2000,02,07,17 Free Software Foundation, Inc.
+   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 the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Socket operations */
+
+#include <lwip_socket_S.h>
+
+#include <sys/mman.h>
+#include <hurd/fshelp.h>
+
+#include <lwip/sockets.h>
+#include <lwip-hurd.h>
+
+error_t
+lwip_S_socket_create (struct trivfs_protid *master,
+                     int sock_type,
+                     int protocol,
+                     mach_port_t * port, mach_msg_type_name_t * porttype)
+{
+  error_t err;
+  struct sock_user *user;
+  struct socket *sock;
+  int isroot;
+  int domain;
+
+  if (!master)
+    return EOPNOTSUPP;
+
+  if (sock_type != SOCK_STREAM
+      && sock_type != SOCK_DGRAM && sock_type != SOCK_RAW)
+    return EPROTOTYPE;
+
+  /* The class tell us which domain must we use */
+  if (master->pi.class == lwip_protid_portclasses[PORTCLASS_INET])
+    domain = PF_INET;
+  else
+    domain = PF_INET6;
+
+  sock = sock_alloc ();
+  if (!sock)
+    return ENOMEM;
+
+  sock->sockno = lwip_socket (domain, sock_type, protocol);
+  if (sock->sockno < 0)
+    {
+      sock_release (sock);
+      return errno;
+    }
+
+  isroot = master->isroot;
+  if (!isroot)
+    {
+      struct stat st;
+
+      st.st_uid = lwip_owner;
+      st.st_gid = lwip_group;
+
+      err = fshelp_isowner (&st, master->user);
+      if (!err)
+       isroot = 1;
+    }
+
+  user = make_sock_user (sock, isroot, 0, 1);
+  *port = ports_get_right (user);
+  *porttype = MACH_MSG_TYPE_MAKE_SEND;
+  ports_port_deref (user);
+
+  return errno;
+}
+
+
+/* Listen on a socket. */
+error_t
+lwip_S_socket_listen (struct sock_user * user, int queue_limit)
+{
+  if (!user)
+    return EOPNOTSUPP;
+
+  lwip_listen (user->sock->sockno, queue_limit);
+
+  return errno;
+}
+
+error_t
+lwip_S_socket_accept (struct sock_user * user,
+                     mach_port_t * new_port,
+                     mach_msg_type_name_t * new_port_type,
+                     mach_port_t * addr_port,
+                     mach_msg_type_name_t * addr_port_type)
+{
+  struct sock_user *newuser;
+  struct sockaddr_storage addr;
+  socklen_t addr_len;
+  error_t err;
+  struct socket *sock, *newsock;
+
+  if (!user)
+    return EOPNOTSUPP;
+
+  sock = user->sock;
+
+  newsock = sock_alloc ();
+  if (!newsock)
+    return ENOMEM;
+
+  addr_len = sizeof (addr);
+  newsock->sockno =
+    lwip_accept (sock->sockno, (struct sockaddr *) &addr, &addr_len);
+
+  if (newsock->sockno == -1)
+    {
+      sock_release (newsock);
+    }
+  else
+    {
+      /* Set the peer's address for the caller */
+      err =
+       lwip_S_socket_create_address (0, addr.ss_family, (void *) &addr,
+                                     addr_len, addr_port, addr_port_type);
+      if (err)
+       return err;
+
+      newuser = make_sock_user (newsock, user->isroot, 0, 1);
+      *new_port = ports_get_right (newuser);
+      *new_port_type = MACH_MSG_TYPE_MAKE_SEND;
+      ports_port_deref (newuser);
+    }
+
+  return errno;
+}
+
+error_t
+lwip_S_socket_connect (struct sock_user * user, struct sock_addr * addr)
+{
+  error_t err;
+
+  if (!user || !addr)
+    return EOPNOTSUPP;
+
+  err = lwip_connect (user->sock->sockno,
+                     &addr->address.sa, addr->address.sa.sa_len);
+
+  /* MiG should do this for us, but it doesn't. */
+  if (!err)
+    mach_port_deallocate (mach_task_self (), addr->pi.port_right);
+
+  /* When a connection fails, e.g. there's nobody there, LwIP returns 
ECONNRESET
+   * but Glibc doesn't expect that, we must return ECONNREFUSED instead. */
+  if (errno == ECONNRESET)
+    errno = ECONNREFUSED;
+
+  return errno;
+}
+
+error_t
+lwip_S_socket_bind (struct sock_user * user, struct sock_addr * addr)
+{
+  error_t err;
+
+  if (!user)
+    return EOPNOTSUPP;
+  if (!addr)
+    return EADDRNOTAVAIL;
+
+  err = lwip_bind (user->sock->sockno,
+                  &addr->address.sa, addr->address.sa.sa_len);
+
+  /* MiG should do this for us, but it doesn't. */
+  if (!err)
+    mach_port_deallocate (mach_task_self (), addr->pi.port_right);
+
+  return errno;
+}
+
+error_t
+lwip_S_socket_name (struct sock_user * user,
+                   mach_port_t * addr_port,
+                   mach_msg_type_name_t * addr_port_name)
+{
+  error_t err;
+
+  if (!user)
+    return EOPNOTSUPP;
+
+  err = make_sockaddr_port (user->sock->sockno, 0, addr_port, addr_port_name);
+
+  return err;
+}
+
+error_t
+lwip_S_socket_peername (struct sock_user * user,
+                       mach_port_t * addr_port,
+                       mach_msg_type_name_t * addr_port_name)
+{
+  error_t err;
+
+  if (!user)
+    return EOPNOTSUPP;
+
+  err = make_sockaddr_port (user->sock->sockno, 1, addr_port, addr_port_name);
+
+  return err;
+}
+
+error_t
+lwip_S_socket_connect2 (struct sock_user * user, struct sock_user * sock2)
+{
+  /* We don't answer AF_UNIX requests */
+  return EOPNOTSUPP;
+}
+
+/*
+ * Receive address data, create a libports object and return its port
+ */
+error_t
+lwip_S_socket_create_address (mach_port_t server,
+                             int sockaddr_type,
+                             char *data,
+                             mach_msg_type_number_t data_len,
+                             mach_port_t * addr_port,
+                             mach_msg_type_name_t * addr_port_type)
+{
+  error_t err;
+  struct sock_addr *addrstruct;
+  const struct sockaddr *const sa = (void *) data;
+
+  if (sockaddr_type != AF_INET && sockaddr_type != AF_INET6
+      && sockaddr_type != AF_UNSPEC)
+    return EAFNOSUPPORT;
+  if (sa->sa_family != sockaddr_type
+      || data_len < offsetof (struct sockaddr, sa_data))
+      return EINVAL;
+
+  err = ports_create_port (addrport_class, lwip_bucket,
+                          (offsetof (struct sock_addr, address)
+                           +data_len), &addrstruct);
+  if (err)
+    return err;
+
+  memcpy (&addrstruct->address.sa, data, data_len);
+
+  /* BSD does not require incoming sa_len to be set, so we don't either.  */
+  addrstruct->address.sa.sa_len = data_len;
+
+  *addr_port = ports_get_right (addrstruct);
+  *addr_port_type = MACH_MSG_TYPE_MAKE_SEND;
+  ports_port_deref (addrstruct);
+  return 0;
+}
+
+error_t
+lwip_S_socket_fabricate_address (mach_port_t server,
+                                int sockaddr_type,
+                                mach_port_t * addr_port,
+                                mach_msg_type_name_t * addr_port_type)
+{
+  return EOPNOTSUPP;
+}
+
+/*
+ * Receive a libports object and return its data
+ */
+error_t
+lwip_S_socket_whatis_address (struct sock_addr * addr,
+                             int *type,
+                             char **data, mach_msg_type_number_t * datalen)
+{
+  if (!addr)
+    return EOPNOTSUPP;
+
+  *type = addr->address.sa.sa_family;
+  if (*datalen < addr->address.sa.sa_len)
+    *data = mmap (0, addr->address.sa.sa_len,
+                 PROT_READ | PROT_WRITE, MAP_ANON, 0, 0);
+  *datalen = addr->address.sa.sa_len;
+  memcpy (*data, &addr->address.sa, addr->address.sa.sa_len);
+
+  return 0;
+}
+
+error_t
+lwip_S_socket_shutdown (struct sock_user * user, int direction)
+{
+  if (!user)
+    return EOPNOTSUPP;
+
+  lwip_shutdown (user->sock->sockno, direction);
+
+  return errno;
+}
+
+error_t
+lwip_S_socket_getopt (struct sock_user * user,
+                     int level, int option, char **data, size_t * datalen)
+{
+  if (!user)
+    return EOPNOTSUPP;
+
+  int len = *datalen;
+  lwip_getsockopt (user->sock->sockno, level, option, *data,
+                  (socklen_t *) & len);
+  *datalen = len;
+
+  return errno;
+}
+
+error_t
+lwip_S_socket_setopt (struct sock_user * user,
+                     int level, int option, char *data, size_t datalen)
+{
+  if (!user)
+    return EOPNOTSUPP;
+
+  lwip_setsockopt (user->sock->sockno, level, option, data, datalen);
+
+  return errno;
+}
+
+error_t
+lwip_S_socket_send (struct sock_user * user,
+                   struct sock_addr * addr,
+                   int flags,
+                   char *data,
+                   size_t datalen,
+                   mach_port_t * ports,
+                   size_t nports,
+                   char *control,
+                   size_t controllen, mach_msg_type_number_t * amount)
+{
+  int sent;
+  int sockflags;
+  struct iovec iov = { data, datalen };
+struct msghdr m = { msg_name:addr ? &addr->address : 0,
+  msg_namelen:addr ? addr->address.sa.sa_len : 0,
+  msg_flags:flags,
+  msg_controllen: 0, msg_iov: &iov, msg_iovlen:1
+  };
+
+  if (!user)
+    return EOPNOTSUPP;
+
+  /* Don't do this yet, it's too bizarre to think about right now. */
+  if (nports != 0 || controllen != 0)
+    return EINVAL;
+
+  sockflags = lwip_fcntl (user->sock->sockno, F_GETFL, 0);
+  if (sockflags & O_NONBLOCK)
+    flags |= MSG_DONTWAIT;
+  sent = lwip_sendmsg (user->sock->sockno, &m, flags);
+
+  /* MiG should do this for us, but it doesn't. */
+  if (addr && sent >= 0)
+    mach_port_deallocate (mach_task_self (), addr->pi.port_right);
+
+  if (sent >= 0)
+    {
+      *amount = sent;
+    }
+
+  return errno;
+}
+
+error_t
+lwip_S_socket_recv (struct sock_user * user,
+                   mach_port_t * addrport,
+                   mach_msg_type_name_t * addrporttype,
+                   int flags,
+                   char **data,
+                   size_t * datalen,
+                   mach_port_t ** ports,
+                   mach_msg_type_name_t * portstype,
+                   size_t * nports,
+                   char **control,
+                   size_t * controllen,
+                   int *outflags, mach_msg_type_number_t amount)
+{
+  error_t err;
+  struct sockaddr_storage addr;
+  socklen_t addrlen = sizeof (addr);
+  int alloced = 0;
+  int sockflags;
+
+  if (!user)
+    return EOPNOTSUPP;
+
+  /* Instead of this, we should peek and the socket and only
+     allocate as much as necessary. */
+  if (amount > *datalen)
+    {
+      *data = mmap (0, amount, PROT_READ | PROT_WRITE, MAP_ANON, 0, 0);
+      if (*data == MAP_FAILED)
+       /* Should check whether errno is indeed ENOMEM --
+          but this can't be done in a straightforward way,
+          because the glue headers #undef errno. */
+       return ENOMEM;
+      alloced = 1;
+    }
+
+  sockflags = lwip_fcntl (user->sock->sockno, F_GETFL, 0);
+  if (sockflags & O_NONBLOCK)
+    flags |= MSG_DONTWAIT;
+
+  /* TODO: use recvmsg instead */
+  err = lwip_recvfrom (user->sock->sockno, *data, amount,
+                      flags, (struct sockaddr *) &addr, &addrlen);
+
+  if (err < 0)
+    {
+      if (alloced)
+       munmap (*data, amount);
+    }
+  else
+    {
+      *datalen = err;
+      if (alloced && round_page (*datalen) < round_page (amount))
+       munmap (*data + round_page (*datalen),
+               round_page (amount) - round_page (*datalen));
+
+      /* Set the peer's address for the caller */
+      err =
+       lwip_S_socket_create_address (0, addr.ss_family, (void *) &addr,
+                                     addrlen, addrport, addrporttype);
+
+      if (err && alloced)
+       munmap (*data, *datalen);
+
+      *outflags = 0;           /* FIXME */
+      *nports = 0;
+      *portstype = MACH_MSG_TYPE_COPY_SEND;
+      *controllen = 0;
+    }
+
+  return errno;
+}
diff --git a/lwip/startup-ops.c b/lwip/startup-ops.c
new file mode 100644
index 0000000..ac1fe54
--- /dev/null
+++ b/lwip/startup-ops.c
@@ -0,0 +1,39 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   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 the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <lwip_startup_notify_S.h>
+
+#include <lwip-hurd.h>
+
+/* The system is going down; destroy all the extant port rights.  That
+   will cause net channels and such to close promptly.  */
+error_t
+lwip_S_startup_dosync (mach_port_t handle)
+{
+  struct port_info *inpi = ports_lookup_port (lwip_bucket, handle,
+                                             shutdown_notify_class);
+
+  if (!inpi)
+    return EOPNOTSUPP;
+
+  ports_class_iterate (socketport_class, ports_destroy_right);
+  ports_class_iterate (addrport_class, ports_destroy_right);
+  return 0;
+}
diff --git a/lwip/startup.c b/lwip/startup.c
new file mode 100644
index 0000000..a21dfe3
--- /dev/null
+++ b/lwip/startup.c
@@ -0,0 +1,69 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   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 the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <startup.h>
+
+#include <unistd.h>
+#include <hurd/paths.h>
+#include <hurd/startup.h>
+
+#include <lwip-hurd.h>
+
+static void
+sigterm_handler (int signo)
+{
+  ports_class_iterate (socketport_class, ports_destroy_right);
+  ports_class_iterate (addrport_class, ports_destroy_right);
+  sleep (10);
+  signal (SIGTERM, SIG_DFL);
+  raise (SIGTERM);
+}
+
+void
+arrange_shutdown_notification ()
+{
+  error_t err;
+  mach_port_t initport, notify;
+  struct port_info *pi;
+
+  shutdown_notify_class = ports_create_class (0, 0);
+
+  signal (SIGTERM, sigterm_handler);
+
+  /* Arrange to get notified when the system goes down,
+     but if we fail for some reason, just silently give up.  No big deal. */
+
+  err = ports_create_port (shutdown_notify_class, lwip_bucket,
+                          sizeof (struct port_info), &pi);
+  if (err)
+    return;
+
+  initport = file_name_lookup (_SERVERS_STARTUP, 0, 0);
+  if (initport == MACH_PORT_NULL)
+    return;
+
+  notify = ports_get_send_right (pi);
+  ports_port_deref (pi);
+  startup_request_notification (initport, notify,
+                               MACH_MSG_TYPE_MAKE_SEND,
+                               program_invocation_short_name);
+  mach_port_deallocate (mach_task_self (), notify);
+  mach_port_deallocate (mach_task_self (), initport);
+}
diff --git a/lwip/startup.h b/lwip/startup.h
new file mode 100644
index 0000000..0c197bf
--- /dev/null
+++ b/lwip/startup.h
@@ -0,0 +1,26 @@
+/*
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   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 the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef LWIP_STARTUP_H
+#define LWIP_STARTUP_H
+
+void arrange_shutdown_notification ();
+
+#endif /* LWIP_STARTUP_H */
diff --git a/proc/info.c b/proc/info.c
index 3c1bf6d..6ab9f3f 100644
--- a/proc/info.c
+++ b/proc/info.c
@@ -24,6 +24,7 @@
 #include <sys/mman.h>
 #include <hurd/hurd_types.h>
 #include <stdlib.h>
+#include <stdio.h>
 #include <errno.h>
 #include <string.h>
 #include <sys/resource.h>
@@ -1017,3 +1018,43 @@ S_proc_getnports (struct proc *callerp,
 
   return err;
 }
+
+/* Implement proc_set_path as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_set_exe (struct proc *p,
+               char *path)
+{
+  char *copy;
+
+  if (!p)
+    return EOPNOTSUPP;
+
+  copy = strdup(path);
+  if (! copy)
+    return ENOMEM;
+
+  free(p->exe);
+  p->exe = copy;
+  return 0;
+}
+
+/* Implement proc_get_path as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_get_exe (struct proc *callerp,
+               pid_t pid,
+               char *path)
+{
+  struct proc *p = pid_find (pid);
+
+  /* No need to check CALLERP here; we don't use it. */
+
+  if (!p)
+    return ESRCH;
+
+  if (p->exe)
+    snprintf (path, 1024 /* XXX */, "%s", p->exe);
+  else
+    path[0] = 0;
+  return 0;
+}
+
diff --git a/proc/mgt.c b/proc/mgt.c
index 750073a..d92bf52 100644
--- a/proc/mgt.c
+++ b/proc/mgt.c
@@ -223,6 +223,8 @@ S_proc_child (struct proc *parentp,
       childp->start_code = parentp->start_code;
       childp->end_code = parentp->end_code;
     }
+  if (! childp->exe && parentp->exe)
+    childp->exe = strdup (parentp->exe);
 
   if (MACH_PORT_VALID (parentp->p_task_namespace))
     {
@@ -348,6 +350,24 @@ S_proc_get_arg_locations (struct proc *p,
   return 0;
 }
 
+/* Implement proc_set_entry as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_set_entry (struct proc *p, vm_address_t entry)
+{
+  if (!p)
+    return EOPNOTSUPP;
+  p->p_entry = entry;
+  return 0;
+}
+
+/* Implement proc_get_entry as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_get_entry (struct proc *p, vm_address_t *entry)
+{
+  *entry = p->p_entry;
+  return 0;
+}
+
 /* Implement proc_dostop as described in <hurd/process.defs>. */
 kern_return_t
 S_proc_dostop (struct proc *p,
@@ -842,6 +862,8 @@ process_has_exited (struct proc *p)
 
   if (!--p->p_login->l_refcnt)
     free (p->p_login);
+  free (p->exe);
+  p->exe = NULL;
 
   ids_rele (p->p_id);
 
diff --git a/proc/proc.h b/proc/proc.h
index 333e884..a974f62 100644
--- a/proc/proc.h
+++ b/proc/proc.h
@@ -68,9 +68,11 @@ struct proc
   pthread_cond_t p_wakeup;
 
   /* Miscellaneous information */
+  char *exe;                   /* path to binary executable */
   vm_address_t p_argv, p_envp;
   vm_address_t start_code;     /* all executable segments are in this range */
   vm_address_t end_code;
+  vm_address_t p_entry;                /* executable entry */
   int p_status;                        /* to return via wait */
   int p_sigcode;
   struct rusage p_rusage;      /* my usage if I'm dead, to return via wait */
diff --git a/procfs/process.c b/procfs/process.c
index ece37b7..59653b2 100644
--- a/procfs/process.c
+++ b/procfs/process.c
@@ -95,6 +95,19 @@ static int args_filename_length (const char *name)
 /* Actual content generators */
 
 static ssize_t
+process_file_gc_exe (struct proc_stat *ps, char **contents)
+{
+  if (proc_stat_exe_len (ps) == 0)
+    {
+      *contents = "-";
+      return 1;
+    }
+
+  *contents = proc_stat_exe(ps);
+  return proc_stat_exe_len(ps);
+}
+
+static ssize_t
 process_file_gc_cmdline (struct proc_stat *ps, char **contents)
 {
   *contents = proc_stat_args(ps);
@@ -410,6 +423,14 @@ process_file_make_node (void *dir_hook, const void 
*entry_hook)
   return np;
 }
 
+static struct node *
+process_file_symlink_make_node (void *dir_hook, const void *entry_hook)
+{
+  struct node *np = process_file_make_node (dir_hook, entry_hook);
+  if (np) procfs_node_chtype (np, S_IFLNK);
+  return np;
+}
+
 /* Stat needs its own constructor in order to set its mode according to
    the --stat-mode command-line option.  */
 static struct node *
@@ -425,6 +446,17 @@ process_stat_make_node (void *dir_hook, const void 
*entry_hook)
 
 static struct procfs_dir_entry entries[] = {
   {
+    .name = "exe",
+    .hook = & (struct process_file_desc) {
+      .get_contents = process_file_gc_exe,
+      .needs = PSTAT_EXE,
+      .no_cleanup = 1,
+    },
+    .ops = {
+      .make_node = process_file_symlink_make_node,
+    },
+  },
+  {
     .name = "cmdline",
     .hook = & (struct process_file_desc) {
       .get_contents = process_file_gc_cmdline,
diff --git a/startup/startup.c b/startup/startup.c
index 7c3fbf0..9a06f7c 100644
--- a/startup/startup.c
+++ b/startup/startup.c
@@ -1,7 +1,7 @@
 /* Start and maintain hurd core servers and system run state
 
    Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-     2005, 2008, 2013 Free Software Foundation, Inc.
+     2005, 2008, 2010, 2013 Free Software Foundation, Inc.
    This file is part of the GNU Hurd.
 
    The GNU Hurd is free software; you can redistribute it and/or modify
@@ -402,13 +402,28 @@ run (const char *server, mach_port_t *ports, task_t *task,
              fprintf (stderr, "Pausing for %s\n", prog);
              getchar ();
            }
-         err = file_exec (file, *task, 0,
-                          argz, argz_len, /* Args.  */
-                          startup_envz, startup_envz_len,
-                          default_dtable, MACH_MSG_TYPE_COPY_SEND, 3,
-                          ports, MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX,
-                          default_ints, INIT_INT_MAX,
-                          NULL, 0, NULL, 0);
+#ifdef HAVE_FILE_EXEC_PATHS
+         err = file_exec_paths (file, *task, 0, (char *)prog, (char *)prog,
+                                argz,
+                                argz_len, /* Args.  */
+                                startup_envz, startup_envz_len,
+                                default_dtable,
+                                MACH_MSG_TYPE_COPY_SEND, 3,
+                                ports, MACH_MSG_TYPE_COPY_SEND,
+                                INIT_PORT_MAX,
+                                default_ints, INIT_INT_MAX,
+                                NULL, 0, NULL, 0);
+         /* For backwards compatibility.  Just drop it when we kill
+            file_exec.  */
+         if (err == MIG_BAD_ID)
+#endif
+           err = file_exec (file, *task, 0,
+                            argz, argz_len, /* Args.  */
+                            startup_envz, startup_envz_len,
+                            default_dtable, MACH_MSG_TYPE_COPY_SEND, 3,
+                            ports, MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX,
+                            default_ints, INIT_INT_MAX,
+                            NULL, 0, NULL, 0);
          if (!err)
            break;
 
@@ -538,14 +553,27 @@ run_for_real (char *filename, char *args, int arglen, 
mach_port_t ctty,
     ++progname;
   else
     progname = filename;
-  err = file_exec (file, task, 0,
-                  args, arglen,
-                  startup_envz, startup_envz_len,
-                  default_dtable, MACH_MSG_TYPE_COPY_SEND, 3,
-                  default_ports, MACH_MSG_TYPE_COPY_SEND,
-                  INIT_PORT_MAX,
-                  default_ints, INIT_INT_MAX,
-                  NULL, 0, NULL, 0);
+#ifdef HAVE_FILE_EXEC_PATHS
+  err = file_exec_paths (file, task, 0, filename, filename,
+                        args, arglen,
+                        startup_envz, startup_envz_len,
+                        default_dtable, MACH_MSG_TYPE_COPY_SEND, 3,
+                        default_ports, MACH_MSG_TYPE_COPY_SEND,
+                        INIT_PORT_MAX,
+                        default_ints, INIT_INT_MAX,
+                        NULL, 0, NULL, 0);
+  /* For backwards compatibility.  Just drop it when we kill file_exec.  */
+  if (err == MIG_BAD_ID)
+#endif
+    err = file_exec (file, task, 0,
+                    args, arglen,
+                    startup_envz, startup_envz_len,
+                    default_dtable, MACH_MSG_TYPE_COPY_SEND, 3,
+                    default_ports, MACH_MSG_TYPE_COPY_SEND,
+                    INIT_PORT_MAX,
+                    default_ints, INIT_INT_MAX,
+                    NULL, 0, NULL, 0);
+
   mach_port_deallocate (mach_task_self (), default_ports[INIT_PORT_PROC]);
   mach_port_deallocate (mach_task_self (), task);
   if (ctty != MACH_PORT_NULL)
@@ -804,6 +832,7 @@ launch_core_servers (void)
   assert_perror_backtrace (err);
   err = proc_mark_exec (procserver);
   assert_perror_backtrace (err);
+  proc_set_exe (procserver, "/hurd/startup");
 
   /* Declare that the filesystem and auth are our children. */
   err = proc_child (procserver, fstask);
@@ -817,6 +846,7 @@ launch_core_servers (void)
   assert_perror_backtrace (err);
   err = proc_mark_exec (authproc);
   assert_perror_backtrace (err);
+  proc_set_exe (authproc, "/hurd/auth");
 
   err = install_as_translator ();
   if (err)
@@ -855,6 +885,7 @@ launch_core_servers (void)
     {
       proc_mark_important (procproc);
       proc_mark_exec (procproc);
+      proc_set_exe (procproc, "/hurd/proc");
       mach_port_deallocate (mach_task_self (), procproc);
     }
 
@@ -870,6 +901,7 @@ launch_core_servers (void)
   assert_perror_backtrace (err);
   err = proc_mark_exec (fsproc);
   assert_perror_backtrace (err);
+  proc_set_exe (fsproc, "fs");
 
   fprintf (stderr, ".\n");
 
@@ -1017,6 +1049,8 @@ frob_kernel_process (void)
   err = record_essential_task ("kernel", task);
   assert_perror_backtrace (err);
 
+  proc_set_exe (proc, "kernel");
+
   err = task_get_bootstrap_port (task, &kbs);
   assert_perror_backtrace (err);
   if (kbs == MACH_PORT_NULL)
@@ -1244,13 +1278,26 @@ start_child (const char *prog, char **progargs)
       getchar ();
     }
 
-  err = file_exec (file, child_task, 0,
-                  args, arglen,
-                  startup_envz, startup_envz_len,
-                  NULL, MACH_MSG_TYPE_COPY_SEND, 0, /* No fds.  */
-                  default_ports, MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX,
-                  default_ints, INIT_INT_MAX,
-                  NULL, 0, NULL, 0);
+#ifdef HAVE_FILE_EXEC_PATHS
+  err = file_exec_paths (file, child_task, 0, args, args,
+                        args, arglen,
+                        startup_envz, startup_envz_len,
+                        NULL, MACH_MSG_TYPE_COPY_SEND, 0, /* No fds.  */
+                        default_ports, MACH_MSG_TYPE_COPY_SEND,
+                        INIT_PORT_MAX,
+                        default_ints, INIT_INT_MAX,
+                        NULL, 0, NULL, 0);
+  /* For backwards compatibility.  Just drop it when we kill file_exec.  */
+  if (err == MIG_BAD_ID)
+#endif
+    err = file_exec (file, child_task, 0,
+                    args, arglen,
+                    startup_envz, startup_envz_len,
+                    NULL, MACH_MSG_TYPE_COPY_SEND, 0, /* No fds.  */
+                    default_ports, MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX,
+                    default_ints, INIT_INT_MAX,
+                    NULL, 0, NULL, 0);
+
   proc_mark_important (default_ports[INIT_PORT_PROC]);
   mach_port_deallocate (mach_task_self (), default_ports[INIT_PORT_PROC]);
   mach_port_deallocate (mach_task_self (), file);
@@ -1414,6 +1461,7 @@ S_startup_essential_task (mach_port_t server,
           mach_port_t execproc;
           proc_task2proc (procserver, task, &execproc);
           proc_mark_important (execproc);
+          proc_set_exe (execproc, "/hurd/exec");
         }
       else if (!strcmp (name, "proc"))
        procinit = 1;
diff --git a/trans/fakeroot.c b/trans/fakeroot.c
index df47b00..711a856 100644
--- a/trans/fakeroot.c
+++ b/trans/fakeroot.c
@@ -882,23 +882,25 @@ netfs_file_get_storage_info (struct iouser *cred,
 }
 
 kern_return_t
-netfs_S_file_exec (struct protid *user,
-                   task_t task,
-                   int flags,
-                   char *argv,
-                   size_t argvlen,
-                   char *envp,
-                   size_t envplen,
-                   mach_port_t *fds,
-                   size_t fdslen,
-                   mach_port_t *portarray,
-                   size_t portarraylen,
-                   int *intarray,
-                   size_t intarraylen,
-                   mach_port_t *deallocnames,
-                   size_t deallocnameslen,
-                   mach_port_t *destroynames,
-                   size_t destroynameslen)
+netfs_S_file_exec_paths (struct protid *user,
+                        task_t task,
+                        int flags,
+                        char *path,
+                        char *abspath,
+                        char *argv,
+                        size_t argvlen,
+                        char *envp,
+                        size_t envplen,
+                        mach_port_t *fds,
+                        size_t fdslen,
+                        mach_port_t *portarray,
+                        size_t portarraylen,
+                        int *intarray,
+                        size_t intarraylen,
+                        mach_port_t *deallocnames,
+                        size_t deallocnameslen,
+                        mach_port_t *destroynames,
+                        size_t destroynameslen)
 {
   error_t err;
   file_t file;
@@ -917,14 +919,30 @@ netfs_S_file_exec (struct protid *user,
 
   if (!err)
     {
+#ifdef HAVE_FILE_EXEC_PATHS
       /* We cannot use MACH_MSG_TYPE_MOVE_SEND because we might need to
         retry an interrupted call that would have consumed the rights.  */
-      err = file_exec (netfs_node_netnode (user->po->np)->file,
-                      task, flags, argv, argvlen,
-                      envp, envplen, fds, MACH_MSG_TYPE_COPY_SEND, fdslen,
-                      portarray, MACH_MSG_TYPE_COPY_SEND, portarraylen,
-                      intarray, intarraylen, deallocnames, deallocnameslen,
-                      destroynames, destroynameslen);
+      err = file_exec_paths (netfs_node_netnode (user->po->np)->file,
+                            task, flags,
+                            path, abspath,
+                            argv, argvlen,
+                            envp, envplen,
+                            fds, MACH_MSG_TYPE_COPY_SEND, fdslen,
+                            portarray, MACH_MSG_TYPE_COPY_SEND,
+                            portarraylen,
+                            intarray, intarraylen,
+                            deallocnames, deallocnameslen,
+                            destroynames, destroynameslen);
+      /* For backwards compatibility.  Just drop it when we kill
+        file_exec.  */
+      if (err == MIG_BAD_ID)
+#endif
+       err = file_exec (user->po->np->nn->file, task, flags, argv, argvlen,
+                        envp, envplen, fds, MACH_MSG_TYPE_COPY_SEND, fdslen,
+                        portarray, MACH_MSG_TYPE_COPY_SEND, portarraylen,
+                        intarray, intarraylen, deallocnames, deallocnameslen,
+                        destroynames, destroynameslen);
+
       mach_port_deallocate (mach_task_self (), file);
     }
 
@@ -940,6 +958,39 @@ netfs_S_file_exec (struct protid *user,
   return err;
 }
 
+kern_return_t
+netfs_S_file_exec (struct protid *user,
+                   task_t task,
+                   int flags,
+                   char *argv,
+                   size_t argvlen,
+                   char *envp,
+                   size_t envplen,
+                   mach_port_t *fds,
+                   size_t fdslen,
+                   mach_port_t *portarray,
+                   size_t portarraylen,
+                   int *intarray,
+                   size_t intarraylen,
+                   mach_port_t *deallocnames,
+                   size_t deallocnameslen,
+                   mach_port_t *destroynames,
+                   size_t destroynameslen)
+{
+  return netfs_S_file_exec_paths (user,
+                                 task,
+                                 flags,
+                                 "",
+                                 "",
+                                 argv, argvlen,
+                                 envp, envplen,
+                                 fds, fdslen,
+                                 portarray, portarraylen,
+                                 intarray, intarraylen,
+                                 deallocnames, deallocnameslen,
+                                 destroynames, destroynameslen);
+}
+
 error_t
 netfs_S_io_map (struct protid *user,
                mach_port_t *rdobj, mach_msg_type_name_t *rdobjtype,
diff --git a/utils/fakeauth.c b/utils/fakeauth.c
index 5a349a8..264be22 100644
--- a/utils/fakeauth.c
+++ b/utils/fakeauth.c
@@ -1,5 +1,5 @@
 /* fakeauth -- proxy auth server to lie to users about what their IDs are
-   Copyright (C) 2002 Free Software Foundation, Inc.
+   Copyright (C) 2002, 2010 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
@@ -397,7 +397,7 @@ believe it has restricted them to different identities or 
no identity at all.\
   /* We cannot use fork because it doesn't do the right thing with our send
      rights that point to our own receive rights, i.e. the new auth port.
      Since posix_spawn might be implemented with fork (prior to glibc 2.3),
-     we cannot use that simple interface either.  We use _hurd_exec
+     we cannot use that simple interface either.  We use _hurd_exec_paths
      directly to effect what posix_spawn does in the simple case.  */
   {
     task_t newtask;
@@ -426,7 +426,12 @@ believe it has restricted them to different identities or 
no identity at all.\
     if (err)
       error (3, err, "proc_child");
 
+#ifdef HAVE__HURD_EXEC_PATHS
+    err = _hurd_exec_paths (newtask, execfile, argv[argi], argv[argi],
+                           &argv[argi], environ);
+#else
     err = _hurd_exec (newtask, execfile, &argv[argi], environ);
+#endif
     mach_port_deallocate (mach_task_self (), newtask);
     mach_port_deallocate (mach_task_self (), execfile);
     if (err)
diff --git a/utils/login.c b/utils/login.c
index 1a12c3c..8d0cf92 100644
--- a/utils/login.c
+++ b/utils/login.c
@@ -1,6 +1,7 @@
 /* Hurdish login
 
-   Copyright (C) 1995,96,97,98,99,2002 Free Software Foundation, Inc.
+   Copyright (C) 1995, 1996, 1997, 1998, 1999, 2002, 2010
+   Free Software Foundation, Inc.
 
    Written by Miles Bader <address@hidden>
 
@@ -882,12 +883,22 @@ main(int argc, char *argv[])
        }
     }
 
-  err = file_exec (exec, mach_task_self (), EXEC_DEFAULTS,
-                  sh_args, sh_args_len, env, env_len,
-                  fds, MACH_MSG_TYPE_COPY_SEND, 3,
-                  ports, MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX,
-                  ints, INIT_INT_MAX,
-                  0, 0, 0, 0);
+#ifdef HAVE_FILE_EXEC_PATHS
+  err = file_exec_paths (exec, mach_task_self (), EXEC_DEFAULTS, shell, shell,
+                        sh_args, sh_args_len, env, env_len,
+                        fds, MACH_MSG_TYPE_COPY_SEND, 3,
+                        ports, MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX,
+                        ints, INIT_INT_MAX,
+                        0, 0, 0, 0);
+  /* Fallback in case the file server hasn't been restarted.  */
+  if (err == MIG_BAD_ID)
+#endif
+    err = file_exec (exec, mach_task_self (), EXEC_DEFAULTS,
+                    sh_args, sh_args_len, env, env_len,
+                    fds, MACH_MSG_TYPE_COPY_SEND, 3,
+                    ports, MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX,
+                    ints, INIT_INT_MAX,
+                    0, 0, 0, 0);
   if (err)
     error(5, err, "%s", shell);
 
diff --git a/utils/rpctrace.c b/utils/rpctrace.c
index 0aecfc4..b4de175 100644
--- a/utils/rpctrace.c
+++ b/utils/rpctrace.c
@@ -1620,8 +1620,9 @@ traced_spawn (char **argv, char **envp)
   task_t traced_task;
   struct sender_info *ti;
   struct receiver_info *receive_ti;
+  char *prefixed_name;
   file_t file = file_name_path_lookup (argv[0], getenv ("PATH"),
-                                      O_EXEC, 0, 0);
+                                      O_EXEC, 0, &prefixed_name);
 
   if (file == MACH_PORT_NULL)
     error (1, errno, "command not found: %s", argv[0]);
@@ -1661,7 +1662,12 @@ traced_spawn (char **argv, char **envp)
   /* Now actually run the command they told us to trace.  We do the exec on
      the actual task, so the RPCs to map in the program itself do not get
      traced.  Could have an option to use TASK_WRAPPER here instead.  */
+#ifdef HAVE__HURD_EXEC_PATHS
+  err = _hurd_exec_paths (traced_task, file, prefixed_name ?: *argv,
+                         prefixed_name ?: *argv, argv, envp);
+#else
   err = _hurd_exec (traced_task, file, argv, envp);
+#endif
   if (err)
     error (2, err, "cannot exec `%s'", argv[0]);
 
@@ -1669,6 +1675,7 @@ traced_spawn (char **argv, char **envp)
      cannot die and hence our TRACED_TASK ref cannot have been released.  */
   mach_port_deallocate (mach_task_self (), task_wrapper);
 
+  free (prefixed_name);
   return pid;
 }
 
diff --git a/utils/shd.c b/utils/shd.c
index 09a4790..e978061 100644
--- a/utils/shd.c
+++ b/utils/shd.c
@@ -1,5 +1,5 @@
 /*
-   Copyright (C) 1994,95,99,2002 Free Software Foundation
+   Copyright (C) 1994, 1995, 1999, 2002, 2010 Free Software Foundation
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
@@ -160,15 +160,18 @@ run (char **argv, int fd0, int fd1)
              movefd (fd1, 1, &save1))
            return -1;
 
+#ifdef HAVE__HURD_EXEC_PATHS
+         err = _hurd_exec_paths (task, file, program, program, argv, environ);
+#else
          err = _hurd_exec (task, file, argv, environ);
-
+#endif
          if (restorefd (fd0, 0, &save0) ||
              restorefd (fd1, 1, &save1))
            return -1;
 
          if (err)
            {
-             error (0, err, "_hurd_exec");
+             error (0, err, "_hurd_exec_paths");
              err = task_terminate (task);
              if (err)
                error (0, err, "task_terminate");

-- 
Alioth's /usr/local/bin/git-commit-notice on 
/srv/git.debian.org/git/pkg-hurd/hurd.git



reply via email to

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