diff options
Diffstat (limited to 'pfinet/main.c')
-rw-r--r-- | pfinet/main.c | 382 |
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 = ðer_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 (ðer_dev); - - *device = ðer_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) (ðer_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); + } } |