diff options
Diffstat (limited to 'pfinet/main.c')
-rw-r--r-- | pfinet/main.c | 495 |
1 files changed, 495 insertions, 0 deletions
diff --git a/pfinet/main.c b/pfinet/main.c new file mode 100644 index 00000000..5b0262fe --- /dev/null +++ b/pfinet/main.c @@ -0,0 +1,495 @@ +/* + 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. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include "pfinet.h" +#include <unistd.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <error.h> +#include <argp.h> +#include <hurd/startup.h> +#include <string.h> +#include <fcntl.h> +#include <version.h> +#include <pids.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 = 1; +int trivfs_support_write = 1; +int trivfs_support_exec = 0; +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; + +#include "io_S.h" +#include "socket_S.h" +#include "pfinet_S.h" +#include "iioctl_S.h" +#include "startup_notify_S.h" + +int +pfinet_demuxer (mach_msg_header_t *inp, + mach_msg_header_t *outp) +{ + struct port_info *pi; + + /* 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); + + mig_routine_t routine; + if ((routine = io_server_routine (inp)) || + (routine = socket_server_routine (inp)) || + (routine = pfinet_server_routine (inp)) || + (routine = iioctl_server_routine (inp)) || + (routine = NULL, trivfs_demuxer (inp, outp)) || + (routine = startup_notify_server_routine (inp))) + { + if (routine) + (*routine) (inp, outp); + return TRUE; + } + else + return FALSE; + } + else + { + mig_routine_t routine; + if ((routine = socket_server_routine (inp)) || + (routine = pfinet_server_routine (inp)) || + (routine = iioctl_server_routine (inp)) || + (routine = NULL, trivfs_demuxer (inp, outp)) || + (routine = startup_notify_server_routine (inp))) + { + if (routine) + (*routine) (inp, outp); + return TRUE; + } + else + return FALSE; + } +} + +/* The system is going down; destroy all the extant port rights. That + will cause net channels and such to close promptly. */ +error_t +S_startup_dosync (mach_port_t handle) +{ + struct port_info *inpi = ports_lookup_port (pfinet_bucket, handle, + shutdown_notify_class); + + if (!inpi) + return EOPNOTSUPP; + + ports_class_iterate (socketport_class, ports_destroy_right); + return 0; +} + +void +sigterm_handler (int signo) +{ + ports_class_iterate (socketport_class, ports_destroy_right); + sleep (10); + signal (SIGTERM, SIG_DFL); + raise (SIGTERM); +} + +void +arrange_shutdown_notification () +{ + error_t err; + mach_port_t initport, notify; + process_t procserver; + 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, pfinet_bucket, + sizeof (struct port_info), &pi); + if (err) + return; + + procserver = getproc (); + if (!procserver) + return; + + err = proc_getmsgport (procserver, HURD_PID_STARTUP, &initport); + mach_port_deallocate (mach_task_self (), procserver); + if (err) + 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); +} + + +/* 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) +{ + struct device *dev = dev_base; + char *base_name; + + /* 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 = dev; + return 0; + } + + base_name = strrchr(name, '/'); + if (base_name) + base_name++; + else + base_name = name; + + if (strncmp(base_name, "tun", 3) == 0) + setup_tunnel_device (name, device); + else if (strncmp(base_name, "dummy", 5) == 0) + setup_dummy_device (name, device); + else + setup_ethernet_device (name, device); + + /* Turn on device. */ + 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. */ +error_t +enumerate_devices (error_t (*fun) (struct device *dev)) +{ + 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; + pthread_t thread; + + pfinet_bucket = ports_create_bucket (); + addrport_class = ports_create_class (clean_addrport, 0); + socketport_class = ports_create_class (clean_socketport, 0); + trivfs_fsid = getpid (); + mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, + &fsys_identity); + + /* Generic initialization */ + + init_time (); + ethernet_initialize (); + err = pthread_create (&thread, NULL, net_bh_worker, NULL); + if (!err) + pthread_detach (thread); + else + { + errno = err; + perror ("pthread_create"); + } + + pthread_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); + + /* 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)); + + pthread_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); + + task_get_bootstrap_port (mach_task_self (), &bootstrap); + + 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, + 30 * 1000, 2 * 60 * 1000, 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) +{ +} + +error_t +trivfs_goaway (struct trivfs_control *cntl, int flags) +{ + 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); + } +} |