/* 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 #include #include #include #include #include #include #include #include /* 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 #include #include 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; 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 *); /* 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 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, 1, &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, 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) { } 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); } }