aboutsummaryrefslogtreecommitdiff
path: root/pfinet/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'pfinet/main.c')
-rw-r--r--pfinet/main.c382
1 files changed, 299 insertions, 83 deletions
diff --git a/pfinet/main.c b/pfinet/main.c
index 0df713e4..1357b037 100644
--- a/pfinet/main.c
+++ b/pfinet/main.c
@@ -1,5 +1,5 @@
/*
- Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Copyright (C) 1995,96,97,99,2000,02,07 Free Software Foundation, Inc.
Written by Michael I. Bushnell, p/BSG.
This file is part of the GNU Hurd.
@@ -26,20 +26,48 @@
#include <argp.h>
#include <hurd/startup.h>
#include <string.h>
+#include <fcntl.h>
+#include <version.h>
+
+/* Include Hurd's errno.h file, but don't include glue-include/hurd/errno.h,
+ since it #undef's the errno macro. */
+#define _HACK_ERRNO_H
+#include <errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/inet.h>
+
+static void pfinet_activate_ipv6 (void);
+
+/* devinet.c */
+extern error_t configure_device (struct device *dev,
+ uint32_t addr, uint32_t netmask,
+ uint32_t peer, uint32_t broadcast);
+
+/* addrconf.c */
+extern int addrconf_notify(struct notifier_block *this, unsigned long event,
+ void * data);
int trivfs_fstype = FSTYPE_MISC;
int trivfs_fsid;
-int trivfs_support_read = 0;
-int trivfs_support_write = 0;
+int trivfs_support_read = 1;
+int trivfs_support_write = 1;
int trivfs_support_exec = 0;
-int trivfs_allow_open = 0;
-struct port_class *trivfs_protid_portclasses[1];
-int trivfs_protid_nportclasses = 1;
-struct port_class *trivfs_cntl_portclasses[1];
-int trivfs_cntl_nportclasses = 1;
+int trivfs_allow_open = O_READ | O_WRITE;
+
+struct port_class *trivfs_protid_portclasses[2];
+int trivfs_protid_nportclasses = 2;
+
+struct port_class *trivfs_cntl_portclasses[2];
+int trivfs_cntl_nportclasses = 2;
+
+/* Which portclass to install on the bootstrap port, default to IPv4. */
+int pfinet_bootstrap_portclass = PORTCLASS_INET;
struct port_class *shutdown_notify_class;
+const char *argp_program_version = STANDARD_HURD_VERSION (pfinet);
+
/* Option parser. */
extern struct argp pfinet_argp;
@@ -47,14 +75,34 @@ int
pfinet_demuxer (mach_msg_header_t *inp,
mach_msg_header_t *outp)
{
+ struct port_info *pi;
extern int io_server (mach_msg_header_t *, mach_msg_header_t *);
extern int socket_server (mach_msg_header_t *, mach_msg_header_t *);
extern int startup_notify_server (mach_msg_header_t *, mach_msg_header_t *);
+ extern int pfinet_server (mach_msg_header_t *, mach_msg_header_t *);
+ extern int iioctl_server (mach_msg_header_t *, mach_msg_header_t *);
- return (io_server (inp, outp)
- || socket_server (inp, outp)
- || trivfs_demuxer (inp, outp)
- || startup_notify_server (inp, outp));
+ /* We have several classes in one bucket, which need to be demuxed
+ differently. */
+ pi = ports_lookup_port(pfinet_bucket, inp->msgh_local_port, socketport_class);
+
+ if (pi)
+ {
+ ports_port_deref (pi);
+
+ return (io_server (inp, outp)
+ || socket_server (inp, outp)
+ || pfinet_server (inp, outp)
+ || iioctl_server (inp, outp)
+ || trivfs_demuxer (inp, outp)
+ || startup_notify_server (inp, outp));
+ }
+ else
+ return (socket_server (inp, outp)
+ || pfinet_server (inp, outp)
+ || iioctl_server (inp, outp)
+ || trivfs_demuxer (inp, outp)
+ || startup_notify_server (inp, outp));
}
/* The system is going down; destroy all the extant port rights. That
@@ -64,35 +112,18 @@ S_startup_dosync (mach_port_t handle)
{
struct port_info *inpi = ports_lookup_port (pfinet_bucket, handle,
shutdown_notify_class);
- error_t
- do1 (void *port)
- {
- struct port_info *pi = port;
-
- if (pi->class == socketport_class)
- ports_destroy_right (pi);
- return 0;
- }
if (!inpi)
return EOPNOTSUPP;
- ports_bucket_iterate (pfinet_bucket, do1);
+ ports_class_iterate (socketport_class, ports_destroy_right);
return 0;
}
void
sigterm_handler (int signo)
{
- error_t
- do1 (void *port)
- {
- struct port_info *pi = port;
- if (pi->class == socketport_class)
- ports_destroy_right (pi);
- return 0;
- }
- ports_bucket_iterate (pfinet_bucket, do1);
+ ports_class_iterate (socketport_class, ports_destroy_right);
sleep (10);
signal (SIGTERM, SIG_DFL);
raise (SIGTERM);
@@ -105,7 +136,7 @@ arrange_shutdown_notification ()
mach_port_t initport, notify;
process_t procserver;
struct port_info *pi;
-
+
shutdown_notify_class = ports_create_class (0, 0);
signal (SIGTERM, sigterm_handler);
@@ -113,88 +144,113 @@ arrange_shutdown_notification ()
/* 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, pfinet_bucket,
+ err = ports_create_port (shutdown_notify_class, pfinet_bucket,
sizeof (struct port_info), &pi);
if (err)
return;
-
+
procserver = getproc ();
if (!procserver)
return;
-
+
err = proc_getmsgport (procserver, 1, &initport);
mach_port_deallocate (mach_task_self (), procserver);
if (err)
return;
-
- notify = ports_get_right (pi);
+
+ notify = ports_get_send_right (pi);
ports_port_deref (pi);
- startup_request_notification (initport, notify,
- MACH_MSG_TYPE_MAKE_SEND,
+ 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);
}
-static char *already_open = 0;
-/* Return an open device called NAME. If NMAE is 0, and there is a single
- active device, it is returned, otherwise an error.
- XXX hacky single-interface version. */
+/* Return an open device called NAME. If NAME is 0, and there is a single
+ active device, it is returned, otherwise an error. */
error_t
find_device (char *name, struct device **device)
{
- if (already_open)
- if (!name || strcmp (already_open, (*device)->name) == 0)
+ struct device *dev = dev_base;
+
+ /* Skip loopback interface. */
+ assert (dev);
+ dev = dev->next;
+
+ if (!name)
+ {
+ if (dev)
+ {
+ if (dev->next)
+ return EBUSY; /* XXXACK */
+ else
+ {
+ *device = dev;
+ return 0;
+ }
+ }
+ else
+ return ENXIO; /* XXX */
+ }
+
+ for (; dev; dev = dev->next)
+ if (strcmp (dev->name, name) == 0)
{
- *device = &ether_dev;
+ *device = dev;
return 0;
}
- else
- return EBUSY; /* XXXACK */
- else if (! name)
- return ENXIO; /* XXX */
-
- name = already_open = strdup (name);
- setup_ethernet_device (name);
-
- /* Default mask is 255.255.255.0. XXX should be class dependent. */
- {
- char addr[4] = {255, 255, 255, 0};
- ether_dev.pa_mask = *(u_long *)addr;
- }
+ if (strncmp(name, "tun", 3) == 0)
+ setup_tunnel_device (name, device);
+ else if (strncmp(name, "dummy", 5) == 0)
+ setup_dummy_device (name, device);
+ else
+ setup_ethernet_device (name, device);
/* Turn on device. */
- dev_open (&ether_dev);
-
- *device = &ether_dev;
+ dev_open (*device);
return 0;
}
/* Call FUN with each active device. If a call to FUN returns a
non-zero value, this function will return immediately. Otherwise 0 is
- returned.
- XXX hacky single-interface version. */
+ returned. */
error_t
enumerate_devices (error_t (*fun) (struct device *dev))
{
- if (already_open)
- return (*fun) (&ether_dev);
- else
- return 0;
+ error_t err;
+ struct device *dev = dev_base;
+
+ /* Skip loopback device. */
+ assert (dev);
+ dev = dev->next;
+
+ for (; dev; dev = dev->next)
+ {
+ err = (*fun) (dev);
+ if (err)
+ return err;
+ }
+
+ return 0;
}
+extern void sk_init (void), skb_init (void);
+extern int net_dev_init (void);
+extern void inet6_proto_init (struct net_proto *pro);
+
int
main (int argc,
char **argv)
{
error_t err;
mach_port_t bootstrap;
+ struct stat st;
pfinet_bucket = ports_create_bucket ();
- trivfs_protid_portclasses[0] = ports_create_class (trivfs_clean_protid, 0);
- trivfs_cntl_portclasses[0] = ports_create_class (trivfs_clean_cntl, 0);
addrport_class = ports_create_class (clean_addrport, 0);
socketport_class = ports_create_class (clean_socketport, 0);
trivfs_fsid = getpid ();
@@ -203,34 +259,169 @@ main (int argc,
/* Generic initialization */
- init_devices ();
init_time ();
+ ethernet_initialize ();
+ cthread_detach (cthread_fork (net_bh_worker, 0));
+
+ __mutex_lock (&global_lock);
+
+ prepare_current (1); /* Set up to call into Linux initialization. */
+ sk_init ();
+#ifdef SLAB_SKB
+ skb_init ();
+#endif
inet_proto_init (0);
- arrange_shutdown_notification ();
+ /* This initializes the Linux network device layer, including
+ initializing each device on the `dev_base' list. For us,
+ that means just loopback_dev, which will get fully initialized now.
+ After this, we can use `register_netdevice' for new interfaces. */
+ net_dev_init ();
+
+ /* ifconfig lo up 127.0.0.1 netmask 0xff000000 */
+ configure_device (&loopback_dev,
+ htonl (INADDR_LOOPBACK), htonl (IN_CLASSA_NET),
+ htonl (INADDR_NONE), htonl (INADDR_NONE));
- /* Parse options. */
+ __mutex_unlock (&global_lock);
+
+ /* Parse options. When successful, this configures the interfaces
+ before returning; to do so, it will acquire the global_lock.
+ (And when not successful, it never returns.) */
argp_parse (&pfinet_argp, argc, argv, 0,0,0);
- /* Talk to parent and link us in. */
task_get_bootstrap_port (mach_task_self (), &bootstrap);
- if (bootstrap == MACH_PORT_NULL)
- error (1, 0, "Must be started as a translator");
- err = trivfs_startup (bootstrap, 0,
- trivfs_cntl_portclasses[0], pfinet_bucket,
- trivfs_protid_portclasses[0], pfinet_bucket, 0);
- if (err)
- error (1, errno, "contacting parent");
+ pfinet_owner = pfinet_group = 0;
+
+ if (bootstrap != MACH_PORT_NULL) {
+ /* Create portclass to install on the bootstrap port. */
+ if(trivfs_protid_portclasses[pfinet_bootstrap_portclass]
+ != MACH_PORT_NULL)
+ error(1, 0, "No portclass left to assign to bootstrap port");
+
+#ifdef CONFIG_IPV6
+ if (pfinet_bootstrap_portclass == PORTCLASS_INET6)
+ pfinet_activate_ipv6 ();
+#endif
+
+ trivfs_protid_portclasses[pfinet_bootstrap_portclass] =
+ ports_create_class (trivfs_clean_protid, 0);
+ trivfs_cntl_portclasses[pfinet_bootstrap_portclass] =
+ ports_create_class (trivfs_clean_cntl, 0);
+
+ /* Talk to parent and link us in. */
+ err = trivfs_startup (bootstrap, 0,
+ trivfs_cntl_portclasses[pfinet_bootstrap_portclass],
+ pfinet_bucket, trivfs_protid_portclasses
+ [pfinet_bootstrap_portclass], pfinet_bucket,
+ &pfinetctl);
+
+ if (err)
+ error (1, err, "contacting parent");
+
+ /* Initialize status from underlying node. */
+ err = io_stat (pfinetctl->underlying, &st);
+ if (! err)
+ {
+ pfinet_owner = st.st_uid;
+ pfinet_group = st.st_gid;
+ }
+ }
+ else { /* no bootstrap port. */
+ int i;
+ /* Check that at least one portclass has been bound,
+ error out otherwise. */
+ for (i = 0; i < trivfs_protid_nportclasses; i ++)
+ if (trivfs_protid_portclasses[i] != MACH_PORT_NULL)
+ break;
+
+ if (i == trivfs_protid_nportclasses)
+ error (1, 0, "should be started as a translator.\n");
+ }
+
+ /* 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 ();
/* Launch */
ports_manage_port_operations_multithread (pfinet_bucket,
pfinet_demuxer,
- 0, 0, 1, 0);
+ 0, 0, 0);
return 0;
}
+#ifdef CONFIG_IPV6
+static void
+pfinet_activate_ipv6 (void)
+{
+ inet6_proto_init (0);
+
+ /* Since we're registering the protocol after the devices have been
+ initialized, we need to care for the linking by ourselves. */
+ struct device *dev = dev_base;
+
+ if (dev)
+ do
+ {
+ if (!(dev->flags & IFF_UP))
+ continue;
+
+ addrconf_notify (NULL, NETDEV_REGISTER, dev);
+ addrconf_notify (NULL, NETDEV_UP, dev);
+ }
+ while ((dev = dev->next));
+}
+#endif /* CONFIG_IPV6 */
+
+void
+pfinet_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 (trivfs_protid_portclasses[portclass] != MACH_PORT_NULL)
+ error (1, 0, "Cannot bind one protocol to multiple nodes.\n");
+
+#ifdef CONFIG_IPV6
+ if (portclass == PORTCLASS_INET6)
+ pfinet_activate_ipv6 ();
+#endif
+
+ trivfs_protid_portclasses[portclass] =
+ ports_create_class (trivfs_clean_protid, 0);
+ trivfs_cntl_portclasses[portclass] =
+ ports_create_class (trivfs_clean_cntl, 0);
+
+ err = trivfs_create_control (file, trivfs_cntl_portclasses[portclass],
+ pfinet_bucket,
+ trivfs_protid_portclasses[portclass],
+ pfinet_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);
+
+}
+
+
void
trivfs_modify_stat (struct trivfs_protid *cred,
struct stat *st)
@@ -240,5 +431,30 @@ trivfs_modify_stat (struct trivfs_protid *cred,
error_t
trivfs_goaway (struct trivfs_control *cntl, int flags)
{
- return EBUSY;
+ if (flags & FSYS_GOAWAY_FORCE)
+ exit (0);
+ else
+ {
+ /* Stop new requests. */
+ ports_inhibit_class_rpcs (trivfs_cntl_portclasses[0]);
+ ports_inhibit_class_rpcs (trivfs_protid_portclasses[0]);
+ ports_inhibit_class_rpcs (socketport_class);
+
+ if (ports_count_class (socketport_class) != 0)
+ {
+ /* We won't go away, so start things going again... */
+ ports_enable_class (socketport_class);
+ ports_resume_class_rpcs (trivfs_cntl_portclasses[0]);
+ ports_resume_class_rpcs (trivfs_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);
+ }
}