/** Service a control-plane event. * * @param[in] kq the kq to service * @param[in] kev the kevent to service * @param[in] ctx the fr_worker_t */ static void fr_network_evfilt_user(UNUSED int kq, UNUSED struct kevent const *kev, void *ctx) { fr_time_t now; fr_network_t *nr = talloc_get_type_abort(ctx, fr_network_t); uint8_t data[256]; now = fr_time(); /* * Service all available control-plane events */ fr_control_service(nr->control, data, sizeof(data), now); }
static ssize_t test_read(void *ctx, UNUSED void **packet_ctx, fr_time_t **recv_time, uint8_t *buffer, size_t buffer_len, size_t *leftover, uint32_t *priority, bool *is_dup) { ssize_t data_size; fr_listen_test_t const *io_ctx = talloc_get_type_abort(ctx, fr_listen_test_t); tpc.salen = sizeof(tpc.src); *leftover = 0; *is_dup = false; data_size = recvfrom(io_ctx->sockfd, buffer, buffer_len, 0, (struct sockaddr *) &tpc.src, &tpc.salen); if (data_size <= 0) return data_size; /* * @todo - check if it's RADIUS. */ tpc.id = buffer[1]; memcpy(tpc.vector, buffer + 4, sizeof(tpc.vector)); start_time = fr_time(); *recv_time = &start_time; *priority = 0; return data_size; }
static ssize_t mod_read(fr_listen_t *li, void **packet_ctx, fr_time_t **recv_time, uint8_t *buffer, size_t buffer_len, size_t *leftover, UNUSED uint32_t *priority, UNUSED bool *is_dup) { proto_dhcpv4_udp_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_dhcpv4_udp_thread_t); fr_io_address_t *address, **address_p; int flags; ssize_t data_size; size_t packet_len; struct timeval timestamp; uint8_t message_type; uint32_t xid, ipaddr; dhcp_packet_t *packet; fr_time_t *recv_time_p; *leftover = 0; /* always for UDP */ /* * Where the addresses should go. This is a special case * for proto_dhcpv4. */ address_p = (fr_io_address_t **) packet_ctx; address = *address_p; recv_time_p = *recv_time; /* * Tell udp_recv if we're connected or not. */ flags = UDP_FLAGS_CONNECTED * (thread->connection != NULL); data_size = udp_recv(thread->sockfd, buffer, buffer_len, flags, &address->src_ipaddr, &address->src_port, &address->dst_ipaddr, &address->dst_port, &address->if_index, ×tamp); if (data_size < 0) { DEBUG2("proto_dhvpv4_udp got read error %zd: %s", data_size, fr_strerror()); return data_size; } if (!data_size) { DEBUG2("proto_dhcpv4_udp got no data: ignoring"); return 0; } /* * @todo - make this take "&packet_len", as the DHCPv4 * packet may be smaller than the parent UDP packet. */ if (!fr_dhcpv4_ok(buffer, data_size, &message_type, &xid)) { DEBUG2("proto_dhcpv4_udp got invalid packet, ignoring it - %s", fr_strerror()); return 0; } packet_len = data_size; /* * We've seen a server reply to this port, but the giaddr * is *not* our address. Drop it. */ packet = (dhcp_packet_t *) buffer; memcpy(&ipaddr, &packet->giaddr, 4); if ((packet->opcode == 2) && (ipaddr != address->dst_ipaddr.addr.v4.s_addr)) { DEBUG2("Ignoring server reply which was not meant for us (was for 0x%x).", ntohl(address->dst_ipaddr.addr.v4.s_addr)); return 0; } // @todo - maybe convert timestamp? *recv_time_p = fr_time(); /* * proto_dhcpv4 sets the priority */ /* * Print out what we received. */ DEBUG2("proto_dhcpv4_udp - Received %s XID %08x length %d %s", dhcp_message_types[message_type], xid, (int) packet_len, thread->name); return packet_len; }
/** Read a packet from the network. * * @param[in] el the event list. * @param[in] sockfd the socket which is ready to read. * @param[in] flags from kevent. * @param[in] ctx the network socket context. */ static void fr_network_read(UNUSED fr_event_list_t *el, int sockfd, UNUSED int flags, void *ctx) { int num_messages = 0; fr_network_socket_t *s = ctx; fr_network_t *nr = s->nr; ssize_t data_size; fr_channel_data_t *cd, *next; fr_time_t *recv_time; if (!fr_cond_assert(s->fd == sockfd)) return; DEBUG3("network read"); if (!s->cd) { cd = (fr_channel_data_t *) fr_message_reserve(s->ms, s->listen->default_message_size); if (!cd) { fr_log(nr->log, L_ERR, "Failed allocating message size %zd! - Closing socket", s->listen->default_message_size); fr_network_socket_dead(nr, s); return; } } else { cd = s->cd; } rad_assert(cd->m.data != NULL); rad_assert(cd->m.rb_size >= 256); next_message: /* * Poll this socket, but not too often. We have to go * service other sockets, too. */ if (num_messages > 16) { s->cd = cd; return; } cd->request.is_dup = false; cd->priority = PRIORITY_NORMAL; /* * Read data from the network. * * Return of 0 means "no data", which is fine for UDP. * For TCP, if an underlying read() on the TCP socket * returns 0, (which signals that the FD is no longer * usable) this function should return -1, so that the * network side knows that it needs to close the * connection. */ data_size = s->listen->app_io->read(s->listen->app_io_instance, &cd->packet_ctx, &recv_time, cd->m.data, cd->m.rb_size, &s->leftover, &cd->priority, &cd->request.is_dup); if (data_size == 0) { /* * Cache the message for later. This is * important for stream sockets, which can do * partial reads into the current buffer. We * need to be able to give the same buffer back * to the stream socket for subsequent reads. * * Since we have a message set for each * fr_io_socket_t, no "head of line" * blocking issues can happen for stream sockets. */ s->cd = cd; return; } /* * Error: close the connection, and remove the fr_listen_t */ if (data_size < 0) { // fr_log(nr->log, L_DBG_ERR, "error from transport read on socket %d", sockfd); fr_network_socket_dead(nr, s); return; } s->cd = NULL; DEBUG("Network received packet size %zd", data_size); nr->stats.in++; s->stats.in++; /* * Initialize the rest of the fields of the channel data. * * We always use "now" as the time of the message, as the * packet MAY be a duplicate packet magically resurrected * from the past. */ cd->m.when = fr_time(); cd->listen = s->listen; cd->request.recv_time = recv_time; /* * Nothing in the buffer yet. Allocate room for one * packet. */ if ((cd->m.data_size == 0) && (!s->leftover)) { (void) fr_message_alloc(s->ms, &cd->m, data_size); next = NULL; } else { /* * There are leftover bytes in the buffer, feed * them to the next round of reading. */ next = (fr_channel_data_t *) fr_message_alloc_reserve(s->ms, &cd->m, data_size, s->leftover, s->listen->default_message_size); if (!next) { fr_log(nr->log, L_ERR, "Failed reserving partial packet."); // @todo - probably close the socket... rad_assert(0 == 1); } } if (!fr_network_send_request(nr, cd)) { fr_log(nr->log, L_ERR, "Failed sending packet to worker"); fr_message_done(&cd->m); nr->stats.dropped++; s->stats.dropped++; } else { /* * One more packet sent to a worker. */ s->outstanding++; } /* * If there is a next message, go read it from the buffer. * * @todo - note that this calls read(), even if the * app_io has paused the reader. We likely want to be * able to check that, too. We might just remove this * "goto"... */ if (next) { cd = next; num_messages++; goto next_message; } }
static ssize_t mod_read(fr_listen_t *li, void **packet_ctx, fr_time_t **recv_time, uint8_t *buffer, size_t buffer_len, size_t *leftover, UNUSED uint32_t *priority, UNUSED bool *is_dup) { proto_radius_udp_t const *inst = talloc_get_type_abort_const(li->app_io_instance, proto_radius_udp_t); proto_radius_udp_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_radius_udp_thread_t); fr_io_address_t *address, **address_p; int flags; ssize_t data_size; size_t packet_len; struct timeval timestamp; decode_fail_t reason; fr_time_t *recv_time_p; *leftover = 0; /* always for UDP */ /* * Where the addresses should go. This is a special case * for proto_radius. */ address_p = (fr_io_address_t **) packet_ctx; address = *address_p; recv_time_p = *recv_time; /* * Tell udp_recv if we're connected or not. */ flags = UDP_FLAGS_CONNECTED * (thread->connection != NULL); data_size = udp_recv(thread->sockfd, buffer, buffer_len, flags, &address->src_ipaddr, &address->src_port, &address->dst_ipaddr, &address->dst_port, &address->if_index, ×tamp); if (data_size < 0) { DEBUG2("proto_radius_udp got read error: %s", fr_strerror()); return data_size; } if (!data_size) { DEBUG2("proto_radius_udp got no data: ignoring"); return 0; } packet_len = data_size; if (data_size < 20) { DEBUG2("proto_radius_udp got 'too short' packet size %zd", data_size); thread->stats.total_malformed_requests++; return 0; } if (packet_len > inst->max_packet_size) { DEBUG2("proto_radius_udp got 'too long' packet size %zd > %u", data_size, inst->max_packet_size); thread->stats.total_malformed_requests++; return 0; } if ((buffer[0] == 0) || (buffer[0] > FR_MAX_PACKET_CODE)) { DEBUG("proto_radius_udp got invalid packet code %d", buffer[0]); thread->stats.total_unknown_types++; return 0; } /* * If it's not a RADIUS packet, ignore it. */ if (!fr_radius_ok(buffer, &packet_len, inst->max_attributes, false, &reason)) { /* * @todo - check for F5 load balancer packets. <sigh> */ DEBUG2("proto_radius_udp got a packet which isn't RADIUS"); thread->stats.total_malformed_requests++; return 0; } // @todo - maybe convert timestamp? *recv_time_p = fr_time(); /* * proto_radius sets the priority */ /* * Print out what we received. */ DEBUG2("proto_radius_udp - Received %s ID %d length %d %s", fr_packet_codes[buffer[0]], buffer[1], (int) packet_len, thread->name); return packet_len; }