#include #include #include #include #include #include #include #include #include #include #include #include static scache_t *nodecache; static uintmax_t currentinode; #define INTERNAL_LOCK(v) MUTEX_ACQUIRE(&(v)->lock) #define INTERNAL_UNLOCK(v) MUTEX_RELEASE(&(v)->lock) int sockfs_open(vnode_t **node, int flags, cred_t *cred) { return ENOSYS; // not needed } int sockfs_close(vnode_t *node, int flags, cred_t *cred) { return ENOSYS; // not needed } int sockfs_read(vnode_t *node, iovec_iterator_t *iovec_iterator, size_t size, uintmax_t offset, int flags, size_t *bytes_read, cred_t *cred) { socket_t *socket = SOCKFS_SOCKET_FROM_NODE(node); sockdesc_t desc = { .addr = NULL, .iovec_iterator = iovec_iterator, .count = size, .flags = flags, .donecount = 0, .ctrl = NULL, .ctrllen = 0 }; int e = socket->ops->recv(socket, &desc); *bytes_read = desc.donecount; return e; } int sockfs_write(vnode_t *node, iovec_iterator_t *iovec_iterator, size_t size, uintmax_t offset, int flags, size_t *bytes_written, cred_t *cred) { socket_t *socket = SOCKFS_SOCKET_FROM_NODE(node); sockdesc_t desc = { .addr = NULL, .iovec_iterator = iovec_iterator, .count = size, .flags = flags, .donecount = 0, .ctrl = NULL, .ctrllen = 0 }; int e = socket->ops->send(socket, &desc); *bytes_written = desc.donecount; return e; } int sockfs_poll(vnode_t *node, polldata_t *data, int events) { socket_t *socket = SOCKFS_SOCKET_FROM_NODE(node); return socket->ops->poll(socket, data, events); } int sockfs_getattr(vnode_t *node, vattr_t *attr, cred_t *cred) { socketnode_t *socketnode = (socketnode_t *)node; INTERNAL_LOCK(node); *attr = socketnode->attr; attr->type = node->type; INTERNAL_UNLOCK(node); return 0; } int sockfs_setattr(vnode_t *node, vattr_t *attr, int which, cred_t *cred) { socketnode_t *socketnode = (socketnode_t *)node; INTERNAL_LOCK(node); if (which & V_ATTR_GID) socketnode->attr.gid = attr->gid; if (which & V_ATTR_UID) socketnode->attr.uid = attr->uid; if (which & V_ATTR_MODE) socketnode->attr.mode = attr->mode; if (which & V_ATTR_ATIME) socketnode->attr.atime = attr->atime; if (which & V_ATTR_MTIME) socketnode->attr.mtime = attr->mtime; if (which & V_ATTR_CTIME) socketnode->attr.ctime = attr->ctime; INTERNAL_UNLOCK(node); return 0; } static int handle_ifreq(unsigned long request, ifreq_t *uifreq, cred_t *cred) { ifreq_t ifreq; int e = USERCOPY_POSSIBLY_FROM_USER(&ifreq, uifreq, sizeof(ifreq_t)); if (e) return e; netdev_t *netdev = netdev_getdev(ifreq.name); if (!netdev) return ENODEV; switch (request) { case SIOCSIFADDR: e = auth_network_check(cred, AUTH_ACTIONS_NETWORK_CONFIGURE, NULL, netdev); if (e) return e; sockaddr_t sockaddr; e = sock_convertaddress(&sockaddr, &ifreq.addr); if (e) return e; netdev->ip = sockaddr.ipv4addr.addr; break; case SIOCGIFHWADDR: { short hwaddr_type = (netdev->flags & NETDEV_FLAGS_LOOPBACK) ? ARPHRD_LOOPBACK : ARPHRD_ETHER; e = USERCOPY_POSSIBLY_TO_USER(&uifreq->addr.type, &hwaddr_type, sizeof(hwaddr_type)); if (e) return e; return USERCOPY_POSSIBLY_TO_USER(uifreq->addr.addr, netdev->mac.address, sizeof(mac_t)); } case SIOCGIFMTU: { int mtu = netdev->mtu; return USERCOPY_POSSIBLY_TO_USER(&uifreq->mtu, &mtu, sizeof(mtu)); } case SIOCGIFFLAGS: { short flags = netdev->flags; return USERCOPY_POSSIBLY_TO_USER(&uifreq->flags, &flags, sizeof(flags)); } } return 0; } // arg can be in userspace int sockfs_ioctl(vnode_t *node, unsigned long request, void *arg, int *result, cred_t *cred) { switch (request) { case SIOCSIFADDR: case SIOCGIFHWADDR: case SIOCGIFMTU: case SIOCGIFFLAGS: return handle_ifreq(request, arg, cred); case SIOCADDRT: { abirtentry_t abirtentry; int e = USERCOPY_POSSIBLY_FROM_USER(&abirtentry, arg, sizeof(abirtentry_t)); if (e) return e; sockaddr_t addr; sockaddr_t gateway; sockaddr_t mask; e = sock_convertaddress(&addr, &abirtentry.rt_dst); if (e) return e; e = sock_convertaddress(&gateway, &abirtentry.rt_gateway); if (e) return e; e = sock_convertaddress(&mask, &abirtentry.rt_genmask); if (e) return e; size_t devlen; e = USERCOPY_POSSIBLY_STRLEN_FROM_USER(abirtentry.rt_dev, &devlen); if (e) return e; char *dev = alloc(devlen + 1); if (dev == NULL) return ENOMEM; e = USERCOPY_POSSIBLY_FROM_USER(dev, abirtentry.rt_dev, devlen); if (e) { free(dev); return e; } netdev_t *netdev = netdev_getdev(dev); if (netdev == NULL) { free(dev); return ENOMEM; } e = auth_network_check(cred, AUTH_ACTIONS_NETWORK_CONFIGURE, NULL, netdev); if (e == 0) e = ipv4_addroute(netdev, addr.ipv4addr.addr, gateway.ipv4addr.addr, mask.ipv4addr.addr, abirtentry.rt_metric); free(dev); return e; } case FIONREAD: { socket_t *socket = SOCKFS_SOCKET_FROM_NODE(node); if (socket->ops->datacount == NULL) return ENOTTY; int count = socket->ops->datacount(socket); return USERCOPY_POSSIBLY_TO_USER(arg, &count, sizeof(int)); } case FIONBIO: { int nonblocking; int e = USERCOPY_POSSIBLY_FROM_USER(&nonblocking, arg, sizeof(nonblocking)); if (e) return e; socket_t *socket = SOCKFS_SOCKET_FROM_NODE(node); MUTEX_ACQUIRE(&socket->mutex); socket->nonblocking = nonblocking != 0; MUTEX_RELEASE(&socket->mutex); return 0; } default: printf("got unknown socket ioctl %lu\n", request); return ENOTTY; } return 0; } static vops_t vnops; int sockfs_inactive(vnode_t *node) { socket_t *socket = SOCKFS_SOCKET_FROM_NODE(node); socketnode_t *socketnode = (socketnode_t *)node; INTERNAL_LOCK(node); socket->ops->destroy(socket); memset(node, 0, sizeof(socketnode_t)); VOP_INIT(&socketnode->vnode, &vnops, 0, V_TYPE_SOCKET, NULL); //socketnode->attr.inode = ++currentinode; slab_free(nodecache, node); return 0; } // the mutex vnode is handled internally // (as we can sleep when waiting for data/space, and the handling happens in the socket layer) // this does mean that operations are not atomic between a INTERNAL_LOCK and INTERNAL_UNLOCK static int sockfs_lock(vnode_t *) { return 0; } static int sockfs_unlock(vnode_t *) { return 0; } static int sockfs_advlock(vnode_t *node, int op, advlock_t *advlock) { return vfs_advlock(node, op, advlock); } static int sockfs_enodev() { return ENODEV; } static vops_t vnops = { .create = sockfs_enodev, .open = sockfs_open, .close = sockfs_close, .getattr = sockfs_getattr, .setattr = sockfs_setattr, .lookup = sockfs_enodev, .poll = sockfs_poll, .read = sockfs_read, .write = sockfs_write, .access = sockfs_enodev, .unlink = sockfs_enodev, .link = sockfs_enodev, .symlink = sockfs_enodev, .readlink = sockfs_enodev, .inactive = sockfs_inactive, .mmap = sockfs_enodev, .munmap = sockfs_enodev, .getdents = sockfs_enodev, .resize = sockfs_enodev, .rename = sockfs_enodev, .ioctl = sockfs_ioctl, .putpage = sockfs_enodev, .getpage = sockfs_enodev, .sync = sockfs_enodev, .advlock = sockfs_advlock, .lock = sockfs_lock, .unlock = sockfs_unlock }; static bool ctor(scache_t *cache, void *obj) { socketnode_t *node = obj; memset(node, 0, sizeof(socketnode_t)); VOP_INIT(&node->vnode, &vnops, 0, V_TYPE_SOCKET, NULL); node->attr.inode = ++currentinode; return true; } void sockfs_init() { nodecache = slab_newcache(sizeof(socketnode_t), 0, ctor, NULL); __assert(nodecache); } INIT_ROUTINE_DEFINE(sockfs, INIT_ROUTINE_FLAGS_NONE, sockfs_init, bsp_early); int sockfs_newsocket(vnode_t **nodep, socket_t *socket) { vnode_t *vnode = slab_allocate(nodecache); socketnode_t *node = (socketnode_t *)vnode; if (node == NULL) return ENOMEM; node->socket = socket; *nodep = vnode; return 0; }