int n_dhcp4_s_connection_dispatch_io(NDhcp4SConnection *connection, NDhcp4Incoming **messagep) { _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = NULL; struct sockaddr_in dest = {}; int r; r = n_dhcp4_s_socket_udp_recv(connection->fd_udp, connection->buf, sizeof(connection->buf), &message, &dest); if (r) return r; r = n_dhcp4_s_connection_verify_incoming(connection, message, dest.sin_addr.s_addr == INADDR_BROADCAST); if (r) { if (r == N_DHCP4_E_MALFORMED || r == N_DHCP4_E_UNEXPECTED) { *messagep = NULL; return 0; } return -ENOTRECOVERABLE; } *messagep = message; message = NULL; return 0; }
/** * b1_handle_transfer() - transfer a handle from one peer to another * @src_handle: source handle * @dst: destination peer * @dst_handlep: pointer to destination handle * * In order for peers to communicate, they must be reachable from one another. * This transfers a handle from one peer to another. * * Return: 0 on success, or a negative error code on failure. */ _c_public_ int b1_handle_transfer(B1Handle *src_handle, B1Peer *dst, B1Handle **dst_handlep) { _c_cleanup_(b1_handle_unrefp) B1Handle *dst_handle = NULL; uint64_t src_handle_id, dst_handle_id = BUS1_HANDLE_INVALID; int r; if (src_handle->id == BUS1_HANDLE_INVALID) src_handle_id = BUS1_NODE_FLAG_MANAGED | BUS1_NODE_FLAG_ALLOCATE; else src_handle_id = src_handle->id; r = bus1_peer_handle_transfer(src_handle->holder->peer, dst->peer, &src_handle_id, &dst_handle_id); if (r < 0) return r; if (src_handle->id == BUS1_HANDLE_INVALID) { r = b1_handle_link(src_handle, src_handle_id); if (r < 0) return r; if (src_handle->node) { r = b1_node_link(src_handle->node, src_handle_id); if (r < 0) return r; } } r = b1_handle_acquire(dst, &dst_handle, dst_handle_id); if (r < 0) return r; *dst_handlep = dst_handle; dst_handle = NULL; return 0; }
static void test_handle(void) { _c_cleanup_(b1_peer_unrefp) B1Peer *peer = NULL; _c_cleanup_(b1_node_freep) B1Node *node = NULL; _c_cleanup_(b1_handle_unrefp) B1Handle *handle = NULL; int r; r = b1_peer_new(&peer); assert(r >= 0); r = b1_node_new(peer, &node); assert(r >= 0); r = b1_handle_transfer(b1_node_get_handle(node), peer, &handle); assert(r >= 0); assert(handle); assert(handle == b1_node_get_handle(node)); }
static void test_message(void) { _c_cleanup_(b1_peer_unrefp) B1Peer *peer = NULL; _c_cleanup_(b1_node_freep) B1Node *node = NULL; _c_cleanup_(b1_message_unrefp) B1Message *message = NULL; B1Handle *handle; int r, fd; r = b1_peer_new(&peer); assert(r >= 0); r = b1_node_new(peer, &node); assert(r >= 0); r = b1_message_new(peer, &message); assert(r >= 0); assert(message); assert(b1_message_get_type(message) == BUS1_MSG_DATA); assert(!b1_message_get_destination_node(message)); assert(!b1_message_get_destination_handle(message)); assert(b1_message_get_uid(message) == (uid_t)-1); assert(b1_message_get_gid(message) == (gid_t)-1); assert(b1_message_get_pid(message) == 0); assert(b1_message_get_tid(message) == 0); handle = b1_node_get_handle(node); r = b1_message_set_handles(message, &handle, 1); assert(r >= 0); r = b1_message_get_handle(message, 0, &handle); assert(r >= 0); assert(handle); assert(handle == b1_node_get_handle(node)); fd = b1_peer_get_fd(peer); r = b1_message_set_fds(message, &fd, 1); assert(r >= 0); r = b1_message_get_fd(message, 0, &fd); assert(r >= 0); assert(fd >= 0); assert(fd != b1_peer_get_fd(peer)); }
static void test_node(void) { _c_cleanup_(b1_peer_unrefp) B1Peer *peer = NULL; _c_cleanup_(b1_node_freep) B1Node *node = NULL; B1Handle *handle; int r; r = b1_peer_new(&peer); assert(r >= 0); r = b1_node_new(peer, &node); assert(r >= 0); assert(node); assert(b1_node_get_peer(node) == peer); handle = b1_node_get_handle(node); assert(handle); handle = b1_handle_ref(handle); assert(handle); b1_handle_unref(handle); }
/** * n_dhcp4_server_lease_new() - XXX */ int n_dhcp4_server_lease_new(NDhcp4ServerLease **leasep, NDhcp4Incoming *message) { _c_cleanup_(n_dhcp4_server_lease_unrefp) NDhcp4ServerLease *lease = NULL; c_assert(leasep); lease = malloc(sizeof(*lease)); if (!lease) return -ENOMEM; *lease = (NDhcp4ServerLease)N_DHCP4_SERVER_LEASE_NULL(*lease); lease->request = message; *leasep = lease; lease = NULL; return 0; }
/** * b1_node_new() - create a new node for a peer * @peer: the owning peer, or null * @nodep: pointer to the new node object * @userdata: userdata to associate with the node * * A node is the recipient of messages, and. Nodes are allocated lazily in the * kernel, so it is not guaranteed to be a kernel equivalent to the userspace * object at all times. A node is associated with at most one peer, and for a * lazily created node it may not be associated with any peer until it is * actually created, at which point it is associated with the creating peer. * * Return: 0 on success, and a negative error code on failure. */ _c_public_ int b1_node_new(B1Peer *peer, B1Node **nodep, void *userdata) { _c_cleanup_(b1_node_freep) B1Node *node = NULL; int r; r = b1_node_new_internal(peer, &node, userdata, BUS1_HANDLE_INVALID, NULL); if (r < 0) return r; r = b1_handle_new(peer, BUS1_HANDLE_INVALID, &node->handle); if (r < 0) return r; node->handle->node = node; *nodep = node; node = NULL; return 0; }
int b1_handle_new(B1Peer *peer, uint64_t id, B1Handle **handlep) { _c_cleanup_(b1_handle_unrefp) B1Handle *handle = NULL; assert(peer); assert(handlep); handle = calloc(1, sizeof(*handle)); if (!handle) return -ENOMEM; handle->n_ref = 1; handle->holder = b1_peer_ref(peer); handle->id = id; handle->marked = false; c_rbnode_init(&handle->rb); *handlep = handle; handle = NULL; return 0; }
static void test_peer(void) { _c_cleanup_(b1_peer_unrefp) B1Peer *peer1 = NULL, *peer2 = NULL, *peer3 = NULL; int fd, r; /* create three peers: peer1 and peer2 are two instances of the same */ r = b1_peer_new(&peer1); assert(r >= 0); assert(peer1); fd = b1_peer_get_fd(peer1); assert(fd >= 0); r = b1_peer_new_from_fd(&peer2, fd); assert(r >= 0); assert(peer2); r = b1_peer_new(&peer3); assert(r >= 0); assert(peer3); }
static int b1_handle_new(B1Peer *peer, B1Handle **handlep) { _c_cleanup_(b1_handle_unrefp) B1Handle *handle = NULL; assert(peer); assert(handlep); handle = calloc(1, sizeof(*handle)); if (!handle) return -ENOMEM; handle->ref = (CRef)C_REF_INIT; handle->holder = b1_peer_ref(peer); handle->id = BUS1_HANDLE_INVALID; handle->marked = false; handle->live = false; c_rbnode_init(&handle->rb); *handlep = handle; handle = NULL; return 0; }
/** * n_dhcp4_client_lease_new() - allocate new client lease object * @leasep: output argumnet for new client lease object * @message: incoming message representing the lease * * This creates a new client lease object. Client lease objects are simple * wrappers around an incoming message representing a lease. * * Return: 0 on success, negative error code on failure. */ int n_dhcp4_client_lease_new(NDhcp4ClientLease **leasep, NDhcp4Incoming *message) { _c_cleanup_(n_dhcp4_client_lease_unrefp) NDhcp4ClientLease *lease = NULL; int r; c_assert(leasep); lease = malloc(sizeof(*lease)); if (!lease) return -ENOMEM; *lease = (NDhcp4ClientLease)N_DHCP4_CLIENT_LEASE_NULL(*lease); r = n_dhcp4_incoming_get_timeouts(message, &lease->t1, &lease->t2, &lease->lifetime); if (r) return r; lease->message = message; *leasep = lease; lease = NULL; return 0; }
/** * b1_handle_subscribe() - subscribe to handle events * @handle: handle to operate on * @slotp: output argument for newly created slot * @fn: slot callback function * @userdata: userdata to be passed to the callback function * * When a node is destroyed, all handle holders to that node receive a node * destruction notification. This function registers a new handler for such * notifications. * * The handler will stay linked to the handle as long as the returned slot is * valid. Once the slot is destroyed, the handler is unlinked as well. * * Return: 0 on success, negative error code on failure. */ _c_public_ int b1_handle_subscribe(B1Handle *handle, B1Subscription **subscriptionp, B1SubscriptionFn fn, void *userdata) { _c_cleanup_(b1_subscription_freep) B1Subscription *subscription = NULL; assert(subscriptionp); assert(fn); subscription = calloc(1, sizeof(*subscription)); if (!subscription) return -ENOMEM; subscription->fn = fn; subscription->userdata = userdata; subscription->handle = handle; subscription->next = handle->subscriptions; handle->subscriptions = subscription; *subscriptionp = subscription; subscription = NULL; return 0; }
/** * b1_node_new() - create a new node for a peer * @peer: the owning peer * @nodep: pointer to the new node object * * Return: 0 on success, and a negative error code on failure. */ _c_public_ int b1_node_new(B1Peer *peer, B1Node **nodep) { _c_cleanup_(b1_node_freep) B1Node *node = NULL; int r; node = calloc(1, sizeof(*node)); if (!node) return -ENOMEM; node->id = BUS1_HANDLE_INVALID; node->owner = b1_peer_ref(peer); c_rbnode_init(&node->rb_nodes); r = b1_handle_new(peer, &node->handle); if (r < 0) return r; node->handle->node = node; *nodep = node; node = NULL; return 0; }
/** * b1_peer_new_from_fd() - create new peer object from existing fd * @peerp: the new peer object * @fd: a file descriptor representing an existing peer * * This takes a pre-initialized bus1 filedescriptor and creates a b1_peer object * around it. * * Return: 0 on success, a negative error code on failure. */ _c_public_ int b1_peer_new_from_fd(B1Peer **peerp, int fd) { _c_cleanup_(b1_peer_unrefp) B1Peer *peer = NULL; int r; peer = calloc(1, sizeof(*peer)); if (!peer) return -ENOMEM; peer->ref = (CRef)C_REF_INIT; r = bus1_peer_new_from_fd(&peer->peer, fd); if (r < 0) return r; r = bus1_peer_mmap(peer->peer); if (r < 0) return r; *peerp = peer; peer = NULL; return 0; }
int b1_node_new_internal(B1Peer *peer, B1Node **nodep, void *userdata, uint64_t id, const char *name) { _c_cleanup_(b1_node_freep) B1Node *node = NULL; size_t n_name; assert(peer); assert(nodep); n_name = name ? strlen(name) + 1 : 0; node = calloc(1, sizeof(*node) + n_name); if (!node) return -ENOMEM; if (name) node->name = memcpy((void*)(node + 1), name, n_name); node->id = id; node->owner = b1_peer_ref(peer); node->userdata = userdata; node->live = false; *nodep = node; node = NULL; return 0; }
static void test_transaction(void) { _c_cleanup_(b1_peer_unrefp) B1Peer *src = NULL, *dst = NULL; _c_cleanup_(b1_node_freep) B1Node *node = NULL; _c_cleanup_(b1_handle_unrefp) B1Handle *handle = NULL; _c_cleanup_(b1_message_unrefp) B1Message *message = NULL; const char *payload = "WOOF"; struct iovec vec = { .iov_base = (void*)payload, .iov_len = strlen(payload) + 1, }; struct iovec *vec_out; size_t n_vec; int r, fd; r = b1_peer_new(&src); assert(r >= 0); r = b1_peer_new(&dst); assert(r >= 0); r = b1_node_new(dst, &node); assert(r >= 0); r = b1_handle_transfer(b1_node_get_handle(node), src, &handle); assert(r >= 0); r = b1_message_new(src, &message); assert(r >= 0); r = b1_message_set_payload(message, &vec, 1); assert(r >= 0); r = b1_message_set_handles(message, &handle, 1); assert(r >= 0); fd = eventfd(0, 0); assert(fd >= 0); r = b1_message_set_fds(message, &fd, 1); assert(r >= 0); assert(close(fd) >= 0); r = b1_message_send(message, &handle, 1); assert(r >= 0); message = b1_message_unref(message); assert(!message); handle = b1_handle_unref(handle); assert(!handle); r = b1_peer_recv(dst, &message); assert(r >= 0); assert(message); assert(b1_message_get_type(message) == BUS1_MSG_DATA); assert(b1_message_get_destination_node(message) == node); assert(b1_message_get_uid(message) == getuid()); assert(b1_message_get_gid(message) == getgid()); assert(b1_message_get_pid(message) == getpid()); assert(b1_message_get_tid(message) == c_syscall_gettid()); r = b1_message_get_payload(message, &vec_out, &n_vec); assert(r >= 0); assert(vec_out); assert(n_vec == 1); assert(vec_out->iov_len == strlen("WOOF") + 1); assert(strcmp(vec_out->iov_base, "WOOF") == 0); r = b1_message_get_handle(message, 0, &handle); assert(r >= 0); assert(handle == b1_node_get_handle(node)); handle = NULL; r = b1_message_get_fd(message, 0, &fd); assert(r >= 0); assert(fd >= 0); message = b1_message_unref(message); r = b1_peer_recv(dst, &message); assert(r >= 0); assert(message); assert(b1_message_get_type(message) == BUS1_MSG_NODE_RELEASE); assert(b1_message_get_destination_node(message) == node); assert(b1_message_get_uid(message) == (uid_t)-1); assert(b1_message_get_gid(message) == (gid_t)-1); assert(b1_message_get_pid(message) == 0); assert(b1_message_get_tid(message) == 0); message = b1_message_unref(message); r = b1_node_destroy(node); assert(r >= 0); r = b1_peer_recv(dst, &message); assert(r >= 0); assert(message); assert(b1_message_get_type(message) == BUS1_MSG_NODE_DESTROY); assert(b1_message_get_destination_node(message)); assert(b1_message_get_destination_node(message) == node); assert(b1_message_get_uid(message) == (uid_t)-1); assert(b1_message_get_gid(message) == (gid_t)-1); assert(b1_message_get_pid(message) == 0); assert(b1_message_get_tid(message) == 0); message = b1_message_unref(message); r = b1_peer_recv(dst, &message); assert(r == -EAGAIN); r = b1_peer_recv(src, &message); assert(r == -EAGAIN); } static void test_multicast(void) { _c_cleanup_(b1_peer_unrefp) B1Peer *src = NULL, *dst1 = NULL, *dst2 = NULL; _c_cleanup_(b1_node_freep) B1Node *node1 = NULL, *node2 = NULL; _c_cleanup_(b1_message_unrefp) B1Message *message = NULL; const char *payload = "WOOF"; struct iovec vec = { .iov_base = (void*)payload, .iov_len = strlen(payload) + 1, }; struct iovec *vec_out; size_t n_vec; B1Handle *handles[2], *handle; int r, fd; r = b1_peer_new(&src); assert(r >= 0); r = b1_peer_new(&dst1); assert(r >= 0); r = b1_peer_new(&dst2); assert(r >= 0); r = b1_node_new(dst1, &node1); assert(r >= 0); r = b1_node_new(dst2, &node2); assert(r >= 0); r = b1_handle_transfer(b1_node_get_handle(node1), src, &handles[0]); assert(r >= 0); r = b1_handle_transfer(b1_node_get_handle(node2), src, &handles[1]); assert(r >= 0); r = b1_message_new(src, &message); assert(r >= 0); r = b1_message_set_payload(message, &vec, 1); assert(r >= 0); r = b1_message_set_handles(message, handles, 2); assert(r >= 0); fd = eventfd(0, 0); assert(fd >= 0); r = b1_message_set_fds(message, &fd, 1); assert(r >= 0); assert(close(fd) >= 0); r = b1_message_send(message, handles, 2); assert(r >= 0); message = b1_message_unref(message); assert(!message); handles[0] = b1_handle_unref(handles[0]); assert(!handles[0]); handles[1] = b1_handle_unref(handles[1]); assert(!handles[1]); r = b1_peer_recv(dst1, &message); assert(r >= 0); assert(message); assert(b1_message_get_type(message) == BUS1_MSG_DATA); assert(b1_message_get_destination_node(message) == node1); assert(b1_message_get_uid(message) == getuid()); assert(b1_message_get_gid(message) == getgid()); assert(b1_message_get_pid(message) == getpid()); assert(b1_message_get_tid(message) == c_syscall_gettid()); r = b1_message_get_payload(message, &vec_out, &n_vec); assert(r >= 0); assert(vec_out); assert(n_vec == 1); assert(vec_out->iov_len == strlen("WOOF") + 1); assert(strcmp(vec_out->iov_base, "WOOF") == 0); r = b1_message_get_handle(message, 0, &handle); assert(r >= 0); assert(handle == b1_node_get_handle(node1)); r = b1_message_get_handle(message, 1, &handle); assert(r >= 0); assert(handle); assert(handle != b1_node_get_handle(node1)); r = b1_message_get_fd(message, 0, &fd); assert(r >= 0); assert(fd >= 0); message = b1_message_unref(message); r = b1_peer_recv(dst2, &message); assert(r >= 0); assert(message); assert(b1_message_get_type(message) == BUS1_MSG_DATA); assert(b1_message_get_destination_node(message) == node2); assert(b1_message_get_uid(message) == getuid()); assert(b1_message_get_gid(message) == getgid()); assert(b1_message_get_pid(message) == getpid()); assert(b1_message_get_tid(message) == c_syscall_gettid()); r = b1_message_get_payload(message, &vec_out, &n_vec); assert(r >= 0); assert(vec_out); assert(n_vec == 1); assert(vec_out->iov_len == strlen("WOOF") + 1); assert(strcmp(vec_out->iov_base, "WOOF") == 0); r = b1_message_get_handle(message, 0, &handle); assert(r >= 0); assert(handle); assert(handle != b1_node_get_handle(node2)); r = b1_message_get_handle(message, 1, &handle); assert(r >= 0); assert(handle); assert(handle == b1_node_get_handle(node2)); r = b1_message_get_fd(message, 0, &fd); assert(r >= 0); assert(fd >= 0); message = b1_message_unref(message); r = b1_peer_recv(dst1, &message); assert(r >= 0); assert(message); assert(b1_message_get_type(message) == BUS1_MSG_NODE_RELEASE); assert(b1_message_get_destination_node(message) == node1); assert(b1_message_get_uid(message) == (uid_t)-1); assert(b1_message_get_gid(message) == (gid_t)-1); assert(b1_message_get_pid(message) == 0); assert(b1_message_get_tid(message) == 0); message = b1_message_unref(message); r = b1_peer_recv(dst2, &message); assert(r >= 0); assert(message); assert(b1_message_get_type(message) == BUS1_MSG_NODE_RELEASE); assert(b1_message_get_destination_node(message) == node2); assert(b1_message_get_uid(message) == (uid_t)-1); assert(b1_message_get_gid(message) == (gid_t)-1); assert(b1_message_get_pid(message) == 0); assert(b1_message_get_tid(message) == 0); message = b1_message_unref(message); r = b1_node_destroy(node1); assert(r >= 0); r = b1_peer_recv(dst1, &message); assert(r >= 0); assert(message); assert(b1_message_get_type(message) == BUS1_MSG_NODE_DESTROY); assert(b1_message_get_destination_node(message) == node1); assert(b1_message_get_uid(message) == (uid_t)-1); assert(b1_message_get_gid(message) == (gid_t)-1); assert(b1_message_get_pid(message) == 0); assert(b1_message_get_tid(message) == 0); message = b1_message_unref(message); r = b1_node_destroy(node2); assert(r >= 0); r = b1_peer_recv(dst2, &message); assert(r >= 0); assert(message); assert(b1_message_get_type(message) == BUS1_MSG_NODE_DESTROY); assert(b1_message_get_destination_node(message) == node2); assert(b1_message_get_uid(message) == (uid_t)-1); assert(b1_message_get_gid(message) == (gid_t)-1); assert(b1_message_get_pid(message) == 0); assert(b1_message_get_tid(message) == 0); message = b1_message_unref(message); r = b1_peer_recv(dst1, &message); assert(r == -EAGAIN); r = b1_peer_recv(dst2, &message); assert(r == -EAGAIN); r = b1_peer_recv(src, &message); assert(r == -EAGAIN); } int main(int argc, char **argv) { if (access("/dev/bus1", F_OK) < 0 && errno == ENOENT) return 77; test_peer(); test_node(); test_handle(); test_message(); test_transaction(); test_multicast(); return 0; }