diff options
author | Marcus Brinkmann <marcus@gnu.org> | 2000-10-06 15:14:12 +0000 |
---|---|---|
committer | Marcus Brinkmann <marcus@gnu.org> | 2000-10-06 15:14:12 +0000 |
commit | f4814f877d82f6242c95c10bb661fadecc5ab24b (patch) | |
tree | 37bdddbc686b7cd112620461312327eadb8f6c80 /pfinet | |
parent | 898424326fc1188384f67963ab6789f18dd57fd3 (diff) |
2000-10-06 Marcus Brinkmann <marcus@gnu.org>
* Makefile (SRCS): Add tunnel.c
* tunnel.c: New file.
* linux-src/net/ipv4/devinet.c (configure_device): New argument PEER.
Implement support for setting the destination address for
point-to-point interfaces.
(inquire_device): New argument PEER.
Implement support to get the destination address.
* main.c: Include fcntl.h.
Update prototype of configure_device.
Set trivfs_support_read and trivfs_support_write to 1, as we support
those in some cases now. For the same reason, set trivfs_allow_open
to O_READ | O_WRITE.
(pfinet_demuxer): If the port is not in the socketport_class,
don't try io_server (so requests for trivfs are not catched too early).
(find_device): Use setup_tunnel_device for tun* interfaces.
(main): Add peer argument to configure_device call for the lo interface.
* options.c: Update prototypes for configure_device, inquire_device.
(options): Add --peer option.
(stuct parse_interface): Add peer member.
(parse_hook_add_interface): Initialize peer.
(parse_opt): Add a case for --peer.
Add peer argument to configure_device call.
(add_dev_opts): Add peer variable, add it to inquire_device call arguments.
Check for peer argument and add it to command line if it is there.
* pfinet.h: Add prototype for setup_tunnel_device.
Diffstat (limited to 'pfinet')
-rw-r--r-- | pfinet/ChangeLog | 28 | ||||
-rw-r--r-- | pfinet/Makefile | 2 | ||||
-rw-r--r-- | pfinet/linux-src/net/ipv4/devinet.c | 18 | ||||
-rw-r--r-- | pfinet/main.c | 29 | ||||
-rw-r--r-- | pfinet/options.c | 23 | ||||
-rw-r--r-- | pfinet/pfinet.h | 1 | ||||
-rw-r--r-- | pfinet/tunnel.c | 627 |
7 files changed, 704 insertions, 24 deletions
diff --git a/pfinet/ChangeLog b/pfinet/ChangeLog index 5a39b20e..6eddecf8 100644 --- a/pfinet/ChangeLog +++ b/pfinet/ChangeLog @@ -1,3 +1,31 @@ +2000-10-06 Marcus Brinkmann <marcus@gnu.org> + + * Makefile (SRCS): Add tunnel.c + * tunnel.c: New file. + * linux-src/net/ipv4/devinet.c (configure_device): New argument PEER. + Implement support for setting the destination address for + point-to-point interfaces. + (inquire_device): New argument PEER. + Implement support to get the destination address. + * main.c: Include fcntl.h. + Update prototype of configure_device. + Set trivfs_support_read and trivfs_support_write to 1, as we support + those in some cases now. For the same reason, set trivfs_allow_open + to O_READ | O_WRITE. + (pfinet_demuxer): If the port is not in the socketport_class, + don't try io_server (so requests for trivfs are not catched too early). + (find_device): Use setup_tunnel_device for tun* interfaces. + (main): Add peer argument to configure_device call for the lo interface. + * options.c: Update prototypes for configure_device, inquire_device. + (options): Add --peer option. + (stuct parse_interface): Add peer member. + (parse_hook_add_interface): Initialize peer. + (parse_opt): Add a case for --peer. + Add peer argument to configure_device call. + (add_dev_opts): Add peer variable, add it to inquire_device call arguments. + Check for peer argument and add it to command line if it is there. + * pfinet.h: Add prototype for setup_tunnel_device. + 2000-10-04 Marcus Brinkmann <marcus@gnu.org> * Makefile (SRCS): Add dummy.c diff --git a/pfinet/Makefile b/pfinet/Makefile index c15321be..5f086559 100644 --- a/pfinet/Makefile +++ b/pfinet/Makefile @@ -61,7 +61,7 @@ ipv4-srcs := af_inet.c \ LINUXSRCS = $(core-srcs) $(ethernet-srcs) $(ipv4-srcs) $(arch-lib-srcs) SRCS = sched.c timer-emul.c socket.c main.c ethernet.c \ io-ops.c socket-ops.c misc.c time.c options.c loopback.c \ - kmem_cache.c stubs.c dummy.c + kmem_cache.c stubs.c dummy.c tunnel.c MIGSRCS = ioServer.c socketServer.c startup_notifyServer.c OBJS := $(patsubst %.c,%.o,$(LINUXSRCS) $(SRCS) $(MIGSRCS)) LCLHDRS = config.h mapped-time.h mutations.h pfinet.h diff --git a/pfinet/linux-src/net/ipv4/devinet.c b/pfinet/linux-src/net/ipv4/devinet.c index 64525bb3..71beaf18 100644 --- a/pfinet/linux-src/net/ipv4/devinet.c +++ b/pfinet/linux-src/net/ipv4/devinet.c @@ -401,7 +401,7 @@ static __inline__ int inet_abc_len(u32 addr) error_t configure_device (struct device *dev, - uint32_t addr, uint32_t netmask) + uint32_t addr, uint32_t netmask, uint32_t peer) { struct in_device *in_dev = dev->ip_ptr; struct in_ifaddr *ifa = in_dev ? in_dev->ifa_list : 0; @@ -422,7 +422,7 @@ configure_device (struct device *dev, if (addr != INADDR_NONE) ifa->ifa_address = ifa->ifa_local = addr; - if (netmask != INADDR_NONE) + if (netmask != INADDR_NONE && !(dev->flags & IFF_POINTOPOINT)) { ifa->ifa_mask = netmask; ifa->ifa_prefixlen = inet_mask_len (ifa->ifa_mask); @@ -431,6 +431,15 @@ configure_device (struct device *dev, else ifa->ifa_broadcast = 0; } + if (peer != INADDR_NONE && (dev->flags & IFF_POINTOPOINT)) + { + ifa->ifa_prefixlen = 32; + if (netmask != INADDR_NONE) + ifa->ifa_mask = netmask; + else + ifa->ifa_mask = inet_make_mask(32); + ifa->ifa_address = peer; + } return - (inet_set_ifa (dev, ifa) ?: dev_change_flags (dev, dev->flags | IFF_UP)); @@ -438,7 +447,7 @@ configure_device (struct device *dev, void inquire_device (struct device *dev, - uint32_t *addr, uint32_t *netmask) + uint32_t *addr, uint32_t *netmask, uint32_t *peer) { struct in_device *in_dev = dev->ip_ptr; struct in_ifaddr *ifa = in_dev ? in_dev->ifa_list : 0; @@ -447,9 +456,10 @@ inquire_device (struct device *dev, { *addr = ifa->ifa_local; *netmask = ifa->ifa_mask; + *peer = ifa->ifa_address; } else - *addr = *netmask = INADDR_NONE; + *addr = *netmask = *peer = INADDR_NONE; } #else diff --git a/pfinet/main.c b/pfinet/main.c index 591192ac..f364e550 100644 --- a/pfinet/main.c +++ b/pfinet/main.c @@ -26,20 +26,21 @@ #include <argp.h> #include <hurd/startup.h> #include <string.h> +#include <fcntl.h> #include <linux/netdevice.h> #include <linux/inet.h> /* devinet.c */ extern error_t configure_device (struct device *dev, - uint32_t addr, uint32_t netmask); + uint32_t addr, uint32_t netmask, uint32_t peer); 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; +int trivfs_allow_open = O_READ | O_WRITE; struct port_class *trivfs_protid_portclasses[1]; int trivfs_protid_nportclasses = 1; struct port_class *trivfs_cntl_portclasses[1]; @@ -58,10 +59,16 @@ pfinet_demuxer (mach_msg_header_t *inp, 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 *); - return (io_server (inp, outp) - || socket_server (inp, outp) - || trivfs_demuxer (inp, outp) - || startup_notify_server (inp, outp)); + + if (ports_lookup_port(pfinet_bucket, inp->msgh_local_port, socketport_class) != 0) + return (io_server (inp, outp) + || socket_server (inp, outp) + || trivfs_demuxer (inp, outp) + || startup_notify_server (inp, outp)); + else + return (socket_server (inp, outp) + || trivfs_demuxer (inp, outp) + || startup_notify_server (inp, outp)); } /* The system is going down; destroy all the extant port rights. That @@ -161,7 +168,9 @@ find_device (char *name, struct device **device) return 0; } - if (strncmp(name, "dummy", 5) == 0) + 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); @@ -238,7 +247,7 @@ main (int argc, /* ifconfig lo up 127.0.0.1 netmask 0xff000000 */ configure_device (&loopback_dev, - htonl (INADDR_LOOPBACK), htonl (IN_CLASSA_NET)); + htonl (INADDR_LOOPBACK), htonl (IN_CLASSA_NET), htonl (INADDR_NONE)); __mutex_unlock (&global_lock); diff --git a/pfinet/options.c b/pfinet/options.c index 0a00ea9d..12a7a4c1 100644 --- a/pfinet/options.c +++ b/pfinet/options.c @@ -44,10 +44,10 @@ extern error_t find_device (char *name, struct device **device); extern error_t enumerate_devices (error_t (*fun) (struct device *dev)); /* devinet.c */ -extern error_t configure_device (struct device *dev, - uint32_t addr, uint32_t netmask); -extern void inquire_device (struct device *dev, - uint32_t *addr, uint32_t *netmask); +extern error_t configure_device (struct device *dev, uint32_t addr, + uint32_t netmask, uint32_t peer); +extern void inquire_device (struct device *dev, uint32_t *addr, + uint32_t *netmask, uint32_t *peer); /* Pfinet options. Used for both startup and runtime. */ static const struct argp_option options[] = @@ -56,6 +56,7 @@ static const struct argp_option options[] = {0,0,0,0,"These apply to a given interface:", 2}, {"address", 'a', "ADDRESS", 0, "Set the network address"}, {"netmask", 'm', "MASK", 0, "Set the netmask"}, + {"peer", 'p', "ADDRESS", 0, "Set the peer address"}, {"gateway", 'g', "ADDRESS", 0, "Set the default gateway"}, {"shutdown", 's', 0, 0, "Shut it down"}, {0} @@ -72,7 +73,7 @@ struct parse_interface struct device *device; /* New values to apply to it. */ - uint32_t address, netmask, gateway; + uint32_t address, netmask, peer, gateway; }; /* Used to hold data during argument parsing. */ @@ -103,6 +104,7 @@ parse_hook_add_interface (struct parse_hook *h) h->curint->device = 0; h->curint->address = INADDR_NONE; h->curint->netmask = INADDR_NONE; + h->curint->peer = INADDR_NONE; h->curint->gateway = INADDR_NONE; return 0; } @@ -187,6 +189,8 @@ parse_opt (int opt, char *arg, struct argp_state *state) break; case 'm': h->curint->netmask = ADDR (arg, "netmask"); break; + case 'p': + h->curint->peer = ADDR (arg, "peer"); break; case 'g': h->curint->gateway = ADDR (arg, "gateway"); break; @@ -244,7 +248,7 @@ parse_opt (int opt, char *arg, struct argp_state *state) for (in = h->interfaces; in < h->interfaces + h->num_interfaces; in++) if (in->address != INADDR_NONE || in->netmask != INADDR_NONE) { - err = configure_device (in->device, in->address, in->netmask); + err = configure_device (in->device, in->address, in->netmask, in->peer); if (err) FAIL (err, 16, 0, "cannot configure interface"); } @@ -328,9 +332,9 @@ trivfs_append_args (struct trivfs_control *fsys, char **argz, size_t *argz_len) error_t add_dev_opts (struct device *dev) { error_t err = 0; - uint32_t addr, mask; + uint32_t addr, mask, peer; - inquire_device (dev, &addr, &mask); + inquire_device (dev, &addr, &mask, &peer); #define ADD_OPT(fmt, args...) \ do { char buf[100]; \ @@ -347,7 +351,8 @@ trivfs_append_args (struct trivfs_control *fsys, char **argz, size_t *argz_len) ADD_ADDR_OPT ("address", addr); if (mask != INADDR_NONE) ADD_ADDR_OPT ("netmask", mask); - + if (peer != addr) + ADD_ADDR_OPT ("peer", peer); /* XXX how do we figure out the default gateway? */ #undef ADD_ADDR_OPT diff --git a/pfinet/pfinet.h b/pfinet/pfinet.h index 2352be70..57e678c0 100644 --- a/pfinet/pfinet.h +++ b/pfinet/pfinet.h @@ -58,6 +58,7 @@ void ethernet_initialize (void); int ethernet_demuxer (mach_msg_header_t *, mach_msg_header_t *); void setup_ethernet_device (char *, struct device **); void setup_dummy_device (char *, struct device **); +void setup_tunnel_device (char *, struct device **); struct sock_user *make_sock_user (struct socket *, int, int, int); error_t make_sockaddr_port (struct socket *, int, mach_port_t *, mach_msg_type_name_t *); diff --git a/pfinet/tunnel.c b/pfinet/tunnel.c new file mode 100644 index 00000000..6c206fec --- /dev/null +++ b/pfinet/tunnel.c @@ -0,0 +1,627 @@ +/* + Copyright (C) 1995,96,98,99,2000 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 <hurd.h> +#include <cthreads.h> +#include <fcntl.h> +#include <device/device.h> +#include <device/net_status.h> +#include <netinet/in.h> +#include <string.h> +#include <error.h> +#include <errno.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/if_arp.h> +#include <linux/ppp_defs.h> +#include <linux/if_ppp.h> + +struct port_class *tunnel_cntlclass; +struct port_class *tunnel_class; + +struct tunnel_device +{ + struct tunnel_device *next; + struct trivfs_control *cntl; + char *devname; + file_t underlying; + struct iouser *user; + struct sk_buff_head xq; /* Transmit queue. */ + struct condition wait; /* For read and select. */ + struct condition select_alert; /* For read and select. */ + struct mutex lock; /* For read and select. */ + int read_blocked; /* For read and select. */ + struct device dev; + struct net_device_stats stats; +}; + + +/* Linked list of all tunnel devices. */ +struct tunnel_device *tunnel_dev; + + +struct net_device_stats * +tunnel_get_stats (struct device *dev) +{ + struct tunnel_device *tdev = (struct tunnel_device *) dev->priv; + + assert (tdev); + + return &tdev->stats; +} + +int +tunnel_stop (struct device *dev) +{ + struct tunnel_device *tdev = (struct tunnel_device *) dev->priv; + struct sk_buff *skb; + + assert (tdev); + + while ((skb = skb_dequeue (&tdev->xq)) != 0) + dev_kfree_skb(skb); + + /* Call those only if removing the device completely. */ + /* free (tdev->devname); */ + /* XXX??? mach_port_deallocate (mach_task_self, tdev->underlying) */ + return 0; +} + +void +tunnel_set_multi (struct device *dev) +{ +} + +void +tunnel_initialize (void) +{ +} + +int +tunnel_open (struct device *dev) +{ + struct tunnel_device *tdev = (struct tunnel_device *) dev->priv; + + assert (tdev); + + skb_queue_head_init(&tdev->xq); + + return 0; +} + +/* Transmit an ethernet frame */ +int +tunnel_xmit (struct sk_buff *skb, struct device *dev) +{ + struct tunnel_device *tdev = (struct tunnel_device *) dev->priv; + + assert (tdev); + + __mutex_lock (&tdev->lock); + + /* Avoid unlimited growth. */ + if (skb_queue_len(&tdev->xq) > 128) + { + struct sk_buff *skb; + + skb = skb_dequeue(&tdev->xq); + dev_kfree_skb(skb); + } + + /* Queue it for processing. */ + skb_queue_tail(&tdev->xq, skb); + + if (tdev->read_blocked) + { + tdev->read_blocked = 0; + condition_broadcast (&tdev->wait); + } + + __mutex_unlock (&tdev->lock); + + return 0; +} + +void +setup_tunnel_device (char *name, struct device **device) +{ + error_t err; + struct tunnel_device *tdev; + struct device *dev; + + /* Do global initialization before setting up first tunnel device. */ + if (!tunnel_dev) + { + trivfs_add_control_port_class (&tunnel_cntlclass); + trivfs_add_protid_port_class (&tunnel_class); + } + + tdev = calloc (1, sizeof (struct tunnel_device)); + if (!tdev) + error (2, ENOMEM, "%s", name); + tdev->next = tunnel_dev; + tunnel_dev = tdev; + + *device = dev = &tdev->dev; + + dev->name = strdup (name); + + dev->priv = tdev; + dev->get_stats = tunnel_get_stats; + + /* Functions. These ones are the true "hardware layer" in Linux. */ + dev->open = tunnel_open; + dev->stop = tunnel_stop; + dev->hard_start_xmit = tunnel_xmit; + dev->set_multicast_list = tunnel_set_multi; + + /* These are the ones set by drivers/net/ppp_generic.c::ppp_net_init. */ + dev->hard_header = 0; + dev->hard_header_len = 0; + dev->mtu = PPP_MTU; + dev->addr_len = 0; + dev->tx_queue_len = 3; + dev->type = ARPHRD_PPP; + dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; + + dev_init_buffers (dev); + + /* Setting up the translator at /dev/tunX. */ + asprintf (&tdev->devname, "/dev/%s", tdev->dev.name); + tdev->underlying = file_name_lookup (tdev->devname, O_CREAT|O_NOTRANS, 0664); + + if (tdev->underlying == MACH_PORT_NULL) + error (2, /* XXX */ 1, "%s", tdev->dev.name); + + err = trivfs_create_control (tdev->underlying, tunnel_cntlclass, + pfinet_bucket, tunnel_class, pfinet_bucket, + &tdev->cntl); + tdev->cntl->hook = tdev; + + if (! err) + { + mach_port_t right = ports_get_send_right (tdev->cntl); + err = file_set_translator (tdev->underlying, 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 (2, err, "%s", tdev->dev.name); + + __mutex_init (&tdev->lock); + condition_init (&tdev->wait); + condition_init (&tdev->select_alert); + condition_implies (&tdev->wait, &tdev->select_alert); + + /* This call adds the device to the `dev_base' chain, + initializes its `ifindex' member (which matters!), + and tells the protocol stacks about the device. */ + err = - register_netdevice (dev); + assert_perror (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 tunnel_device *tdev; + + for (tdev = tunnel_dev; tdev; tdev = tdev->next) + if (tdev->cntl == cntl) + break; + + if (tdev && flags != O_NORW) + { + if (tdev->user) + return EBUSY; + else + tdev->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 tunnel_device *tdev; + + if (cred->pi.class != tunnel_class) + return; + + tdev = (struct tunnel_device *) cred->po->cntl->hook; + + if (tdev->user == cred->user) + tdev->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, + vm_address_t *data, mach_msg_type_number_t *data_len, + off_t offs, mach_msg_type_number_t amount) +{ + struct tunnel_device *tdev; + struct sk_buff *skb; + + /* Deny access if they have bad credentials. */ + if (! cred) + return EOPNOTSUPP; + else if (! (cred->po->openmodes & O_READ)) + return EBADF; + + if (cred->pi.class != tunnel_class) + return EOPNOTSUPP; + + tdev = (struct tunnel_device *) cred->po->cntl->hook; + + __mutex_lock (&tdev->lock); + + while (skb_queue_len(&tdev->xq) == 0) + { + if (cred->po->openmodes & O_NONBLOCK) + { + __mutex_unlock (&tdev->lock); + return EWOULDBLOCK; + } + + tdev->read_blocked = 1; + if (hurd_condition_wait (&tdev->wait, &tdev->lock)) + { + __mutex_unlock (&tdev->lock); + return EINTR; + } + /* See term/users.c for possible race? */ + } + + skb = skb_dequeue (&tdev->xq); + assert(skb); + + if (skb->len < amount) + amount = skb->len; + if (amount > 0) + { + /* Possibly allocate a new buffer. */ + if (*data_len < amount) + *data = (vm_address_t) mmap (0, amount, PROT_READ|PROT_WRITE, + MAP_ANON, 0, 0); + + /* Copy the constant data into the buffer. */ + memcpy ((char *) *data, skb->data, amount); + } + *data_len = amount; + dev_kfree_skb (skb); + + /* Set atime, see term/users.c */ + + __mutex_unlock (&tdev->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 tunnel_device *tdev; + struct sk_buff *skb; + + /* 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; + + tdev = (struct tunnel_device *) cred->po->cntl->hook; + + __mutex_lock (&tdev->lock); + + __mutex_lock (&net_bh_lock); + skb = alloc_skb (datalen, GFP_ATOMIC); + skb->len = datalen; + skb->dev = &tdev->dev; + + bcopy (data, skb->data, datalen); + + /* Drop it on the queue. */ + skb->mac.raw = skb->data; + skb->protocol = htons (ETH_P_IP); + netif_rx (skb); + __mutex_unlock (&net_bh_lock); + + *amount = datalen; + + __mutex_unlock (&tdev->lock); + 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 tunnel_device *tdev; + struct sk_buff *skb; + + /* Deny access if they have bad credentials. */ + if (! cred) + return EOPNOTSUPP; + else if (! (cred->po->openmodes & O_READ)) + return EBADF; + + if (cred->pi.class != tunnel_class) + return EOPNOTSUPP; + + tdev = (struct tunnel_device *) cred->po->cntl->hook; + + __mutex_lock (&tdev->lock); + + /* XXX: Now return the length of the next entry in the queue. + From the BSD manual: + The tunnel device, normally /dev/tunN, is exclusive-open (it cannot be + opened if it is already open) and is restricted to the super-user. A + read() call will return an error (EHOSTDOWN) if the interface is not + ``ready'' address has been set (which means that the control device is + open and the interface's). Once the interface is ready, read() will re- + turn a packet if one is available; if not, it will either block until one + is or return EWOULDBLOCK, depending on whether non-blocking I/O has been + enabled. If the packet is longer than is allowed for in the buffer + passed to read(), the extra data will be silently dropped. + */ + + skb = skb_dequeue(&tdev->xq); + if (skb) + { + *amount = skb->len; + skb_queue_head(&tdev->xq, skb); + } + else + *amount = 0; + + __mutex_unlock (&tdev->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. */ +error_t +trivfs_S_io_select (struct trivfs_protid *cred, + mach_port_t reply, + mach_msg_type_name_t reply_type, + int *type, + int *idtag) +{ + struct tunnel_device *tdev; + + if (!cred) + return EOPNOTSUPP; + + if (cred->pi.class != tunnel_class) + return EOPNOTSUPP; + + tdev = (struct tunnel_device *) cred->po->cntl->hook; + + /* We only deal with SELECT_READ here. */ + if (*type & ~SELECT_READ) + return EINVAL; + + if (*type == 0) + return 0; + + __mutex_lock (&tdev->lock); + + while (1) + { + if (skb_queue_len (&tdev->xq) != 0) + { + *type = SELECT_READ; + __mutex_unlock (&tdev->lock); + return 0; + } + + ports_interrupt_self_on_port_death (cred, reply); + tdev->read_blocked = 1; + if (hurd_condition_wait (&tdev->select_alert, &tdev->lock)) + { + *type = 0; + __mutex_unlock (&tdev->lock); + return EINTR; + } + } +} + +/* 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; +} + +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, + 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; +} |