mtr-packet: drop capabilities + using BSD's linked lists for probes

At startup, we now use cap_set_proc to drop all privileged
capabilities for the mtr-packet process.  This means that
capabilities granted through the commandline setcap to the
mtr-packet executable will only be in effect while the necessary
raw sockets are opened, and will be dropped before any command
requests are read.

Now we use BSD's queue.h linked list support for storing outstanding
probes.  This makes iterating through in-flight probes more efficient,
as we don't need to loop through many unused probe entires when only
a few probes are outstanding.

Changed mtr-packet's default probe size to 64 bytes, to match
mainline mtr's default.

The code consistently uses 'exit(EXIT_FAILURE)' instead of 'exit(1)'.
The effect is the same, but the intent is clearer.
This commit is contained in:
Matt Kimball 2016-12-25 09:26:34 -08:00
parent 5f76affc0e
commit 2ff8de3102
18 changed files with 950 additions and 137 deletions

30
BSDCOPYING Normal file
View File

@ -0,0 +1,30 @@
Portions of this software have the following copyright.
--
Copyright (c) 1991, 1993
The Regents of the University of California. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
4. Neither the name of the University nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

View File

@ -1,4 +1,5 @@
EXTRA_DIST = \
BSDCOPYING \
SECURITY \
mtr.bat \
img/mtr_icon.xpm
@ -97,6 +98,8 @@ mtr_packet_SOURCES = \
packet/timeval.c packet/timeval.h \
packet/wait.h
mtr_packet_LDADD = $(CAP_LIBS)
if CYGWIN
@ -104,11 +107,12 @@ mtr_packet_SOURCES += \
packet/command_cygwin.c packet/command_cygwin.h \
packet/probe_cygwin.c packet/probe_cygwin.h \
packet/wait_cygwin.c
mtr_packet_LDADD = -lcygwin -liphlpapi -lws2_32
mtr_packet_LDADD += -lcygwin -liphlpapi -lws2_32
dist_windows_aux = \
$(srcdir)/mtr.bat \
$(srcdir)/AUTHORS \
$(srcdir)/BSDCOPYING \
$(srcdir)/COPYING \
$(srcdir)/README \
$(srcdir)/NEWS

View File

@ -44,6 +44,8 @@ justified. mtr-packet does the following two things after it is launched:
ICMP packets.
* mtr-packet drops root privileges by setting the effective uid to
match uid or the user calling mtr.
* If capabilities support is availabe, mtr-packet drops all privileged
capabilities.
See main() in packet.c and init_net_state_privileged() in probe_unix.c
for the details of this process.

View File

@ -108,6 +108,10 @@ AS_IF([test "x$with_ncurses" = "xyes"],
])
AM_CONDITIONAL([WITH_NCURSES], [test "x$with_ncurses" = xyes])
AC_CHECK_LIB([cap], [cap_set_proc], [],
AS_IF([test "$host_os" = linux-gnu],
AC_MSG_WARN([Capabilities support is strongly recommended for increased security. See SECURITY for more information.])))
# Enable ipinfo
AC_ARG_WITH([ipinfo],
[AS_HELP_STRING([--without-ipinfo], [Do not try to use ipinfo lookup at all])],

View File

@ -278,7 +278,7 @@ void send_probe_command(
param.command_token = command->token;
param.protocol = IPPROTO_ICMP;
param.ttl = 255;
param.packet_size = 128;
param.packet_size = 64;
param.timeout = 10;
for (i = 0; i < command->argument_count; i++) {

View File

@ -48,7 +48,7 @@ void CALLBACK finish_read_command(
}
fprintf(stderr, "ReadFileEx completion failure %d\n", status);
exit(1);
exit(EXIT_FAILURE);
}
/* Copy from the overlapped I/O buffer to the incoming command buffer */
@ -77,7 +77,7 @@ void queue_empty_apc(void)
if (QueueUserAPC((PAPCFUNC)empty_apc, GetCurrentThread(), 0) == 0) {
fprintf(
stderr, "Unexpected QueueUserAPC failure %d\n", GetLastError());
exit(1);
exit(EXIT_FAILURE);
}
}
@ -115,7 +115,7 @@ void start_read_command(
} else if (err != WAIT_IO_COMPLETION) {
fprintf(
stderr, "Unexpected ReadFileEx failure %d\n", GetLastError());
exit(1);
exit(EXIT_FAILURE);
}
}

View File

@ -42,13 +42,13 @@ void init_command_buffer(
flags = fcntl(command_stream, F_GETFL, 0);
if (flags == -1) {
perror("Unexpected command stream error");
exit(1);
exit(EXIT_FAILURE);
}
/* Set the O_NONBLOCK bit */
if (fcntl(command_stream, F_SETFL, flags | O_NONBLOCK)) {
perror("Unexpected command stream error");
exit(1);
exit(EXIT_FAILURE);
}
}
@ -82,7 +82,7 @@ int read_commands(
/* EINTR indicates we received a signal during read */
if (errno != EINTR && errno != EAGAIN) {
perror("Unexpected command buffer read error");
exit(1);
exit(EXIT_FAILURE);
}
}

View File

@ -50,7 +50,8 @@ void find_and_receive_probe(
}
receive_probe(
probe, icmp_type, remote_addr, timestamp, mpls_count, mpls);
net_state, probe, icmp_type,
remote_addr, timestamp, mpls_count, mpls);
}
/*
@ -82,7 +83,8 @@ void handle_inner_udp_packet(
if (probe != NULL) {
receive_probe(
probe, icmp_result, remote_addr, timestamp, mpls_count, mpls);
net_state, probe, icmp_result,
remote_addr, timestamp, mpls_count, mpls);
}
}

View File

@ -16,23 +16,55 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "config.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifdef HAVE_LIBCAP
# include <sys/capability.h>
#endif
#include "wait.h"
/* Drop SUID privileges. To be used after accquiring raw sockets. */
static
void drop_suid_permissions(void)
int drop_elevated_permissions(void)
{
#ifdef HAVE_LIBCAP
cap_t cap;
#endif
/* Drop any suid permissions granted */
if (setgid(getgid()) || setuid(getuid())) {
perror("Unable to drop suid permissions");
return -1;
}
if (geteuid() != getuid() || getegid() != getgid()) {
perror("Unable to drop suid permissions");
return -1;
}
/*
Drop all process capabilities.
This will revoke anything granted by a commandline 'setcap'
*/
#ifdef HAVE_LIBCAP
cap = cap_get_proc();
if (cap == NULL) {
return -1;
}
if (cap_clear(cap)) {
return -1;
}
if (cap_set_proc(cap)) {
return -1;
}
#endif
return 0;
}
int main(
@ -49,7 +81,10 @@ int main(
raw sockets.
*/
init_net_state_privileged(&net_state);
drop_suid_permissions();
if (drop_elevated_permissions()) {
perror("Unable to drop elevated permissions");
exit(EXIT_FAILURE);
}
init_net_state(&net_state);
init_command_buffer(&command_buffer, fileno(stdin));
@ -93,7 +128,7 @@ int main(
in-flight probes have reported their status.
*/
if (!command_pipe_open) {
if (count_in_flight_probes(&net_state) == 0) {
if (net_state.outstanding_probe_count == 0) {
break;
}
}

View File

@ -115,57 +115,39 @@ struct probe_t *alloc_probe(
struct net_state_t *net_state,
int token)
{
int i;
struct probe_t *probe;
for (i = 0; i < MAX_PROBES; i++) {
probe = &net_state->probes[i];
if (!probe->used) {
memset(probe, 0, sizeof(struct probe_t));
probe->used = true;
probe->token = token;
platform_alloc_probe(net_state, probe);
return probe;
}
if (net_state->outstanding_probe_count >= MAX_PROBES) {
return NULL;
}
return NULL;
probe = malloc(sizeof(struct probe_t));
if (probe == NULL) {
return NULL;
}
memset(probe, 0, sizeof(struct probe_t));
probe->token = token;
platform_alloc_probe(net_state, probe);
net_state->outstanding_probe_count++;
LIST_INSERT_HEAD(&net_state->outstanding_probes, probe, probe_list_entry);
return probe;
}
/* Mark a probe tracking structure as unused */
void free_probe(
struct net_state_t *net_state,
struct probe_t *probe)
{
LIST_REMOVE(probe, probe_list_entry);
net_state->outstanding_probe_count--;
platform_free_probe(probe);
probe->used = false;
}
/*
Return the number of probes which haven't yet received a reply
and haven't yet timed out.
*/
int count_in_flight_probes(
struct net_state_t *net_state)
{
int i;
int count;
struct probe_t *probe;
count = 0;
for (i = 0; i < MAX_PROBES; i++) {
probe = &net_state->probes[i];
if (probe->used) {
count++;
}
}
return count;
free(probe);
}
/*
@ -178,7 +160,6 @@ struct probe_t *find_probe(
int id,
int sequence)
{
int i;
struct probe_t *probe;
/*
@ -195,14 +176,9 @@ struct probe_t *find_probe(
}
}
for (i = 0; i < MAX_PROBES; i++) {
probe = &net_state->probes[i];
if (probe->used) {
if (htons(probe->sequence) == sequence) {
return probe;
}
LIST_FOREACH(probe, &net_state->outstanding_probes, probe_list_entry) {
if (htons(probe->sequence) == sequence) {
return probe;
}
}
@ -251,6 +227,7 @@ void format_mpls_string(
sent the probe.
*/
void respond_to_probe(
struct net_state_t *net_state,
struct probe_t *probe,
int icmp_type,
const struct sockaddr_storage *remote_addr,
@ -289,7 +266,7 @@ void respond_to_probe(
remote_addr->ss_family, addr, ip_text, IP_TEXT_LENGTH) == NULL) {
perror("inet_ntop failure");
exit(1);
exit(EXIT_FAILURE);
}
snprintf(
@ -308,7 +285,7 @@ void respond_to_probe(
}
puts(response);
free_probe(probe);
free_probe(net_state, probe);
}
/*

View File

@ -25,6 +25,8 @@
#include <stdbool.h>
#include <sys/time.h>
#include "portability/queue.h"
#ifdef PLATFORM_CYGWIN
#include "probe_cygwin.h"
#else
@ -82,8 +84,8 @@ struct probe_param_t
/* Tracking information for an outstanding probe */
struct probe_t
{
/* true if this entry is in use */
bool used;
/* Our entry in the probe list */
LIST_ENTRY(probe_t) probe_list_entry;
/*
Also the ICMP sequence ID used to identify the probe.
@ -106,13 +108,17 @@ struct probe_t
/* Global state for interacting with the network */
struct net_state_t
{
/* The number of entries in the outstanding_probes list */
int outstanding_probe_count;
/* Tracking information for in-flight probes */
struct probe_t probes[MAX_PROBES];
LIST_HEAD(probe_list_head_t, probe_t) outstanding_probes;
/* Platform specific tracking information */
struct net_state_platform_t platform;
};
/* Multiprotocol Label Switching information */
struct mpls_label_t
{
uint32_t label;
@ -146,6 +152,7 @@ void check_probe_timeouts(
struct net_state_t *net_state);
void respond_to_probe(
struct net_state_t *net_state,
struct probe_t *probe,
int icmp_type,
const struct sockaddr_storage *remote_addr,
@ -167,6 +174,10 @@ struct probe_t *alloc_probe(
struct net_state_t *net_state,
int token);
void free_probe(
struct net_state_t *net_state,
struct probe_t *probe);
void platform_alloc_probe(
struct net_state_t *net_state,
struct probe_t *probe);
@ -174,12 +185,6 @@ void platform_alloc_probe(
void platform_free_probe(
struct probe_t *probe);
void free_probe(
struct probe_t *probe);
int count_in_flight_probes(
struct net_state_t *net_state);
struct probe_t *find_probe(
struct net_state_t *net_state,
int protocol,

View File

@ -38,13 +38,13 @@ void init_net_state(
net_state->platform.icmp4 = IcmpCreateFile();
if (net_state->platform.icmp4 == INVALID_HANDLE_VALUE) {
fprintf(stderr, "Failure opening ICMPv4 %d\n", GetLastError());
exit(1);
exit(EXIT_FAILURE);
}
net_state->platform.icmp6 = Icmp6CreateFile();
if (net_state->platform.icmp6 == INVALID_HANDLE_VALUE) {
fprintf(stderr, "Failure opening ICMPv6 %d\n", GetLastError());
exit(1);
exit(EXIT_FAILURE);
}
}
@ -60,11 +60,12 @@ bool is_protocol_supported(
return false;
}
/* No special action is required for Cygwin on probe allocation */
/* Set the back pointer to the net_state when a probe is allocated */
void platform_alloc_probe(
struct net_state_t *net_state,
struct probe_t *probe)
{
probe->platform.net_state = net_state;
}
/* Free the reply buffer when the probe is freed */
@ -112,6 +113,7 @@ void WINAPI on_icmp_reply(
ULONG reserved)
{
struct probe_t *probe = (struct probe_t *)context;
struct net_state_t *net_state = probe->platform.net_state;
int icmp_type;
int round_trip_us = 0;
int reply_count;
@ -163,7 +165,7 @@ void WINAPI on_icmp_reply(
err = GetLastError();
report_win_error(probe->token, err);
free_probe(probe);
free_probe(net_state, probe);
return;
}
@ -178,7 +180,8 @@ void WINAPI on_icmp_reply(
if (icmp_type != -1) {
/* Record probe result */
respond_to_probe(
probe, icmp_type, &remote_addr, round_trip_us, 0, NULL);
net_state, probe, icmp_type,
&remote_addr, round_trip_us, 0, NULL);
} else {
fprintf(stderr, "Unexpected ICMP result %d\n", icmp_type);
}
@ -227,7 +230,7 @@ void icmp_send_probe(
probe->platform.reply4 = malloc(reply_size);
if (probe->platform.reply4 == NULL) {
perror("failure to allocate reply buffer");
exit(1);
exit(EXIT_FAILURE);
}
if (param->ip_version == 6) {
@ -258,7 +261,7 @@ void icmp_send_probe(
*/
if (err != ERROR_IO_PENDING) {
report_win_error(probe->token, err);
free_probe(probe);
free_probe(net_state, probe);
}
}
}
@ -324,7 +327,7 @@ void send_probe(
payload_size = fill_payload(param, payload, PACKET_BUFFER_SIZE);
if (payload_size < 0) {
perror("Error construction packet");
exit(1);
exit(EXIT_FAILURE);
}
icmp_send_probe(

View File

@ -51,6 +51,12 @@ typedef struct icmpv6_echo_reply_lh
*/
struct probe_platform_t
{
/*
We need a backpointer to the net_state because of the way
IcmpSendEcho2 passes our context.
*/
struct net_state_t *net_state;
/* IP version (4 or 6) used for the probe */
int ip_version;

View File

@ -99,7 +99,7 @@ void check_length_order(
if (resolve_probe_addresses(&param, &dest_sockaddr, &src_sockaddr)) {
fprintf(stderr, "Error decoding localhost address\n");
exit(1);
exit(EXIT_FAILURE);
}
/* First attempt to ping the localhost with network byte order */
@ -111,7 +111,7 @@ void check_length_order(
&dest_sockaddr, &src_sockaddr, &param);
if (packet_size < 0) {
perror("Unable to send to localhost");
exit(1);
exit(EXIT_FAILURE);
}
bytes_sent = send_packet(
@ -129,14 +129,14 @@ void check_length_order(
&dest_sockaddr, &src_sockaddr, &param);
if (packet_size < 0) {
perror("Unable to send to localhost");
exit(1);
exit(EXIT_FAILURE);
}
bytes_sent = send_packet(
net_state, &param, packet, packet_size, &dest_sockaddr);
if (bytes_sent < 0) {
perror("Unable to send with swapped length");
exit(1);
exit(EXIT_FAILURE);
}
}
@ -171,12 +171,12 @@ void set_socket_nonblocking(
flags = fcntl(socket, F_GETFL, 0);
if (flags == -1) {
perror("Unexpected socket F_GETFL error");
exit(1);
exit(EXIT_FAILURE);
}
if (fcntl(socket, F_SETFL, flags | O_NONBLOCK)) {
perror("Unexpected socket F_SETFL O_NONBLOCK error");
exit(1);
exit(EXIT_FAILURE);
}
}
@ -192,7 +192,7 @@ void open_ip4_sockets(
send_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if (send_socket == -1) {
perror("Failure opening IPv4 send socket");
exit(1);
exit(EXIT_FAILURE);
}
/*
@ -203,7 +203,7 @@ void open_ip4_sockets(
send_socket, IPPROTO_IP, IP_HDRINCL, &trueopt, sizeof(int))) {
perror("Failure to set IP_HDRINCL");
exit(1);
exit(EXIT_FAILURE);
}
/*
@ -213,7 +213,7 @@ void open_ip4_sockets(
recv_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (recv_socket == -1) {
perror("Failure opening IPv4 receive socket");
exit(1);
exit(EXIT_FAILURE);
}
net_state->platform.ip4_send_socket = send_socket;
@ -232,19 +232,19 @@ void open_ip6_sockets(
send_socket_icmp = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
if (send_socket_icmp == -1) {
perror("Failure opening ICMPv6 send socket");
exit(1);
exit(EXIT_FAILURE);
}
send_socket_udp = socket(AF_INET6, SOCK_RAW, IPPROTO_UDP);
if (send_socket_udp == -1) {
perror("Failure opening UDPv6 send socket");
exit(1);
exit(EXIT_FAILURE);
}
recv_socket = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
if (recv_socket == -1) {
perror("Failure opening IPv6 receive socket");
exit(1);
exit(EXIT_FAILURE);
}
set_socket_nonblocking(recv_socket);
@ -352,13 +352,13 @@ void send_probe(
if (resolve_probe_addresses(param, &probe->remote_addr, &src_sockaddr)) {
printf("%d invalid-argument\n", param->command_token);
free_probe(probe);
free_probe(net_state, probe);
return;
}
if (gettimeofday(&probe->platform.departure_time, NULL)) {
perror("gettimeofday failure");
exit(1);
exit(EXIT_FAILURE);
}
packet_size = construct_packet(
@ -375,10 +375,11 @@ void send_probe(
*/
if (errno == ECONNREFUSED) {
receive_probe(
probe, ICMP_ECHOREPLY, &probe->remote_addr, NULL, 0, NULL);
net_state, probe, ICMP_ECHOREPLY,
&probe->remote_addr, NULL, 0, NULL);
} else {
report_packet_error(param->command_token);
free_probe(probe);
free_probe(net_state, probe);
}
return;
@ -390,7 +391,7 @@ void send_probe(
packet, packet_size, &probe->remote_addr) == -1) {
report_packet_error(param->command_token);
free_probe(probe);
free_probe(net_state, probe);
return;
}
}
@ -429,6 +430,7 @@ void platform_free_probe(
to the platform agnostic response handling.
*/
void receive_probe(
struct net_state_t *net_state,
struct probe_t *probe,
int icmp_type,
const struct sockaddr_storage *remote_addr,
@ -443,7 +445,7 @@ void receive_probe(
if (timestamp == NULL) {
if (gettimeofday(&now, NULL)) {
perror("gettimeofday failure");
exit(1);
exit(EXIT_FAILURE);
}
timestamp = &now;
@ -454,7 +456,8 @@ void receive_probe(
timestamp->tv_usec - departure_time->tv_usec;
respond_to_probe(
probe, icmp_type, remote_addr, round_trip_us, mpls_count, mpls);
net_state, probe, icmp_type,
remote_addr, round_trip_us, mpls_count, mpls);
}
/*
@ -486,7 +489,7 @@ void receive_replies_from_icmp_socket(
*/
if (gettimeofday(&timestamp, NULL)) {
perror("gettimeofday failure");
exit(1);
exit(EXIT_FAILURE);
}
if (packet_length == -1) {
@ -507,7 +510,7 @@ void receive_replies_from_icmp_socket(
}
perror("Failure receiving replies");
exit(1);
exit(EXIT_FAILURE);
}
handle_received_packet(
@ -547,7 +550,7 @@ void receive_replies_from_probe_socket(
return;
} else {
perror("probe socket select error");
exit(1);
exit(EXIT_FAILURE);
}
}
@ -560,7 +563,7 @@ void receive_replies_from_probe_socket(
if (getsockopt(probe_socket, SOL_SOCKET, SO_ERROR, &err, &err_length)) {
perror("probe socket SO_ERROR");
exit(1);
exit(EXIT_FAILURE);
}
/*
@ -569,11 +572,12 @@ void receive_replies_from_probe_socket(
*/
if (!err || err == ECONNREFUSED) {
receive_probe(
probe, ICMP_ECHOREPLY, &probe->remote_addr, NULL, 0, NULL);
net_state, probe, ICMP_ECHOREPLY,
&probe->remote_addr, NULL, 0, NULL);
} else {
errno = err;
report_packet_error(probe->token);
free_probe(probe);
free_probe(net_state, probe);
}
}
@ -581,8 +585,8 @@ void receive_replies_from_probe_socket(
void receive_replies(
struct net_state_t *net_state)
{
int i;
struct probe_t *probe;
struct probe_t *probe_safe_iter;
receive_replies_from_icmp_socket(
net_state, net_state->platform.ip4_recv_socket,
@ -592,12 +596,11 @@ void receive_replies(
net_state, net_state->platform.ip6_recv_socket,
handle_received_ip6_packet);
for (i = 0; i < MAX_PROBES; i++) {
probe = &net_state->probes[i];
LIST_FOREACH_SAFE(
probe, &net_state->outstanding_probes,
probe_list_entry, probe_safe_iter) {
if (probe->used) {
receive_replies_from_probe_socket(net_state, probe);
}
receive_replies_from_probe_socket(net_state, probe);
}
}
@ -609,18 +612,16 @@ int gather_probe_sockets(
const struct net_state_t *net_state,
fd_set *write_set)
{
int i;
int probe_socket;
int nfds;
const struct probe_t *probe;
nfds = 0;
for (i = 0; i < MAX_PROBES; i++) {
probe = &net_state->probes[i];
LIST_FOREACH(probe, &net_state->outstanding_probes, probe_list_entry) {
probe_socket = probe->platform.socket;
if (probe->used && probe_socket) {
if (probe_socket) {
FD_SET(probe_socket, write_set);
if (probe_socket >= nfds) {
nfds = probe_socket + 1;
@ -641,26 +642,22 @@ void check_probe_timeouts(
{
struct timeval now;
struct probe_t *probe;
int i;
struct probe_t *probe_safe_iter;
if (gettimeofday(&now, NULL)) {
perror("gettimeofday failure");
exit(1);
exit(EXIT_FAILURE);
}
for (i = 0; i < MAX_PROBES; i++) {
probe = &net_state->probes[i];
/* Don't check probes which aren't currently outstanding */
if (!probe->used) {
continue;
}
LIST_FOREACH_SAFE(
probe, &net_state->outstanding_probes,
probe_list_entry, probe_safe_iter) {
if (compare_timeval(probe->platform.timeout_time, now) < 0) {
/* Report timeout to the command stream */
printf("%d no-reply\n", probe->token);
free_probe(probe);
free_probe(net_state, probe);
}
}
}
@ -677,7 +674,6 @@ bool get_next_probe_timeout(
const struct net_state_t *net_state,
struct timeval *timeout)
{
int i;
bool have_timeout;
const struct probe_t *probe;
struct timeval now;
@ -685,16 +681,11 @@ bool get_next_probe_timeout(
if (gettimeofday(&now, NULL)) {
perror("gettimeofday failure");
exit(1);
exit(EXIT_FAILURE);
}
have_timeout = false;
for (i = 0; i < MAX_PROBES; i++) {
probe = &net_state->probes[i];
if (!probe->used) {
continue;
}
LIST_FOREACH(probe, &net_state->outstanding_probes, probe_list_entry) {
probe_timeout.tv_sec =
probe->platform.timeout_time.tv_sec - now.tv_sec;
probe_timeout.tv_usec =

View File

@ -75,6 +75,7 @@ void set_socket_nonblocking(
int socket);
void receive_probe(
struct net_state_t *net_state,
struct probe_t *probe,
int icmp_type,
const struct sockaddr_storage *remote_addr,

View File

@ -50,6 +50,6 @@ void wait_for_activity(
if (wait_result == WAIT_FAILED) {
fprintf(stderr, "SleepEx failure %d\n", GetLastError());
exit(1);
exit(EXIT_FAILURE);
}
}

View File

@ -115,7 +115,7 @@ void wait_for_activity(
if (errno != EINTR && errno != EAGAIN) {
/* We don't expect other errors, so report them */
perror("unexpected select error");
exit(1);
exit(EXIT_FAILURE);
}
}
}

753
portability/queue.h Normal file
View File

@ -0,0 +1,753 @@
/*-
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)queue.h 8.5 (Berkeley) 8/20/94
* $FreeBSD: releng/11.0/sys/sys/queue.h 284915 2015-06-28 21:06:45Z hselasky $
*/
#ifndef _SYS_QUEUE_H_
#define _SYS_QUEUE_H_
#include <sys/cdefs.h>
/*
* This file defines four types of data structures: singly-linked lists,
* singly-linked tail queues, lists and tail queues.
*
* A singly-linked list is headed by a single forward pointer. The elements
* are singly linked for minimum space and pointer manipulation overhead at
* the expense of O(n) removal for arbitrary elements. New elements can be
* added to the list after an existing element or at the head of the list.
* Elements being removed from the head of the list should use the explicit
* macro for this purpose for optimum efficiency. A singly-linked list may
* only be traversed in the forward direction. Singly-linked lists are ideal
* for applications with large datasets and few or no removals or for
* implementing a LIFO queue.
*
* A singly-linked tail queue is headed by a pair of pointers, one to the
* head of the list and the other to the tail of the list. The elements are
* singly linked for minimum space and pointer manipulation overhead at the
* expense of O(n) removal for arbitrary elements. New elements can be added
* to the list after an existing element, at the head of the list, or at the
* end of the list. Elements being removed from the head of the tail queue
* should use the explicit macro for this purpose for optimum efficiency.
* A singly-linked tail queue may only be traversed in the forward direction.
* Singly-linked tail queues are ideal for applications with large datasets
* and few or no removals or for implementing a FIFO queue.
*
* A list is headed by a single forward pointer (or an array of forward
* pointers for a hash table header). The elements are doubly linked
* so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before
* or after an existing element or at the head of the list. A list
* may be traversed in either direction.
*
* A tail queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or
* after an existing element, at the head of the list, or at the end of
* the list. A tail queue may be traversed in either direction.
*
* For details on the use of these macros, see the queue(3) manual page.
*
*
* SLIST LIST STAILQ TAILQ
* _HEAD + + + +
* _CLASS_HEAD + + + +
* _HEAD_INITIALIZER + + + +
* _ENTRY + + + +
* _CLASS_ENTRY + + + +
* _INIT + + + +
* _EMPTY + + + +
* _FIRST + + + +
* _NEXT + + + +
* _PREV - + - +
* _LAST - - + +
* _FOREACH + + + +
* _FOREACH_FROM + + + +
* _FOREACH_SAFE + + + +
* _FOREACH_FROM_SAFE + + + +
* _FOREACH_REVERSE - - - +
* _FOREACH_REVERSE_FROM - - - +
* _FOREACH_REVERSE_SAFE - - - +
* _FOREACH_REVERSE_FROM_SAFE - - - +
* _INSERT_HEAD + + + +
* _INSERT_BEFORE - + - +
* _INSERT_AFTER + + + +
* _INSERT_TAIL - - + +
* _CONCAT - - + +
* _REMOVE_AFTER + - + -
* _REMOVE_HEAD + - + -
* _REMOVE + + + +
* _SWAP + + + +
*
*/
#ifdef QUEUE_MACRO_DEBUG
/* Store the last 2 places the queue element or head was altered */
struct qm_trace {
unsigned long lastline;
unsigned long prevline;
const char *lastfile;
const char *prevfile;
};
#define TRACEBUF struct qm_trace trace;
#define TRACEBUF_INITIALIZER { __LINE__, 0, __FILE__, NULL } ,
#define TRASHIT(x) do {(x) = (void *)-1;} while (0)
#define QMD_SAVELINK(name, link) void **name = (void *)&(link)
#define QMD_TRACE_HEAD(head) do { \
(head)->trace.prevline = (head)->trace.lastline; \
(head)->trace.prevfile = (head)->trace.lastfile; \
(head)->trace.lastline = __LINE__; \
(head)->trace.lastfile = __FILE__; \
} while (0)
#define QMD_TRACE_ELEM(elem) do { \
(elem)->trace.prevline = (elem)->trace.lastline; \
(elem)->trace.prevfile = (elem)->trace.lastfile; \
(elem)->trace.lastline = __LINE__; \
(elem)->trace.lastfile = __FILE__; \
} while (0)
#else
#define QMD_TRACE_ELEM(elem)
#define QMD_TRACE_HEAD(head)
#define QMD_SAVELINK(name, link)
#define TRACEBUF
#define TRACEBUF_INITIALIZER
#define TRASHIT(x)
#endif /* QUEUE_MACRO_DEBUG */
#ifdef __cplusplus
/*
* In C++ there can be structure lists and class lists:
*/
#define QUEUE_TYPEOF(type) type
#else
#define QUEUE_TYPEOF(type) struct type
#endif
/*
* Singly-linked List declarations.
*/
#define SLIST_HEAD(name, type) \
struct name { \
struct type *slh_first; /* first element */ \
}
#define SLIST_CLASS_HEAD(name, type) \
struct name { \
class type *slh_first; /* first element */ \
}
#define SLIST_HEAD_INITIALIZER(head) \
{ NULL }
#define SLIST_ENTRY(type) \
struct { \
struct type *sle_next; /* next element */ \
}
#define SLIST_CLASS_ENTRY(type) \
struct { \
class type *sle_next; /* next element */ \
}
/*
* Singly-linked List functions.
*/
#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
#define SLIST_FIRST(head) ((head)->slh_first)
#define SLIST_FOREACH(var, head, field) \
for ((var) = SLIST_FIRST((head)); \
(var); \
(var) = SLIST_NEXT((var), field))
#define SLIST_FOREACH_FROM(var, head, field) \
for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
(var); \
(var) = SLIST_NEXT((var), field))
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = SLIST_FIRST((head)); \
(var) && ((tvar) = SLIST_NEXT((var), field), 1); \
(var) = (tvar))
#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
(var) && ((tvar) = SLIST_NEXT((var), field), 1); \
(var) = (tvar))
#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
for ((varp) = &SLIST_FIRST((head)); \
((var) = *(varp)) != NULL; \
(varp) = &SLIST_NEXT((var), field))
#define SLIST_INIT(head) do { \
SLIST_FIRST((head)) = NULL; \
} while (0)
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
SLIST_NEXT((slistelm), field) = (elm); \
} while (0)
#define SLIST_INSERT_HEAD(head, elm, field) do { \
SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
SLIST_FIRST((head)) = (elm); \
} while (0)
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
#define SLIST_REMOVE(head, elm, type, field) do { \
QMD_SAVELINK(oldnext, (elm)->field.sle_next); \
if (SLIST_FIRST((head)) == (elm)) { \
SLIST_REMOVE_HEAD((head), field); \
} \
else { \
QUEUE_TYPEOF(type) *curelm = SLIST_FIRST(head); \
while (SLIST_NEXT(curelm, field) != (elm)) \
curelm = SLIST_NEXT(curelm, field); \
SLIST_REMOVE_AFTER(curelm, field); \
} \
TRASHIT(*oldnext); \
} while (0)
#define SLIST_REMOVE_AFTER(elm, field) do { \
SLIST_NEXT(elm, field) = \
SLIST_NEXT(SLIST_NEXT(elm, field), field); \
} while (0)
#define SLIST_REMOVE_HEAD(head, field) do { \
SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
} while (0)
#define SLIST_SWAP(head1, head2, type) do { \
QUEUE_TYPEOF(type) *swap_first = SLIST_FIRST(head1); \
SLIST_FIRST(head1) = SLIST_FIRST(head2); \
SLIST_FIRST(head2) = swap_first; \
} while (0)
/*
* Singly-linked Tail queue declarations.
*/
#define STAILQ_HEAD(name, type) \
struct name { \
struct type *stqh_first;/* first element */ \
struct type **stqh_last;/* addr of last next element */ \
}
#define STAILQ_CLASS_HEAD(name, type) \
struct name { \
class type *stqh_first; /* first element */ \
class type **stqh_last; /* addr of last next element */ \
}
#define STAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).stqh_first }
#define STAILQ_ENTRY(type) \
struct { \
struct type *stqe_next; /* next element */ \
}
#define STAILQ_CLASS_ENTRY(type) \
struct { \
class type *stqe_next; /* next element */ \
}
/*
* Singly-linked Tail queue functions.
*/
#define STAILQ_CONCAT(head1, head2) do { \
if (!STAILQ_EMPTY((head2))) { \
*(head1)->stqh_last = (head2)->stqh_first; \
(head1)->stqh_last = (head2)->stqh_last; \
STAILQ_INIT((head2)); \
} \
} while (0)
#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
#define STAILQ_FIRST(head) ((head)->stqh_first)
#define STAILQ_FOREACH(var, head, field) \
for((var) = STAILQ_FIRST((head)); \
(var); \
(var) = STAILQ_NEXT((var), field))
#define STAILQ_FOREACH_FROM(var, head, field) \
for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
(var); \
(var) = STAILQ_NEXT((var), field))
#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = STAILQ_FIRST((head)); \
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
(var) = (tvar))
#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
(var) = (tvar))
#define STAILQ_INIT(head) do { \
STAILQ_FIRST((head)) = NULL; \
(head)->stqh_last = &STAILQ_FIRST((head)); \
} while (0)
#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
STAILQ_NEXT((tqelm), field) = (elm); \
} while (0)
#define STAILQ_INSERT_HEAD(head, elm, field) do { \
if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
STAILQ_FIRST((head)) = (elm); \
} while (0)
#define STAILQ_INSERT_TAIL(head, elm, field) do { \
STAILQ_NEXT((elm), field) = NULL; \
*(head)->stqh_last = (elm); \
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
} while (0)
#define STAILQ_LAST(head, type, field) \
(STAILQ_EMPTY((head)) ? NULL : \
__containerof((head)->stqh_last, \
QUEUE_TYPEOF(type), field.stqe_next))
#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
#define STAILQ_REMOVE(head, elm, type, field) do { \
QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \
if (STAILQ_FIRST((head)) == (elm)) { \
STAILQ_REMOVE_HEAD((head), field); \
} \
else { \
QUEUE_TYPEOF(type) *curelm = STAILQ_FIRST(head); \
while (STAILQ_NEXT(curelm, field) != (elm)) \
curelm = STAILQ_NEXT(curelm, field); \
STAILQ_REMOVE_AFTER(head, curelm, field); \
} \
TRASHIT(*oldnext); \
} while (0)
#define STAILQ_REMOVE_AFTER(head, elm, field) do { \
if ((STAILQ_NEXT(elm, field) = \
STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
} while (0)
#define STAILQ_REMOVE_HEAD(head, field) do { \
if ((STAILQ_FIRST((head)) = \
STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
(head)->stqh_last = &STAILQ_FIRST((head)); \
} while (0)
#define STAILQ_SWAP(head1, head2, type) do { \
QUEUE_TYPEOF(type) *swap_first = STAILQ_FIRST(head1); \
QUEUE_TYPEOF(type) **swap_last = (head1)->stqh_last; \
STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \
(head1)->stqh_last = (head2)->stqh_last; \
STAILQ_FIRST(head2) = swap_first; \
(head2)->stqh_last = swap_last; \
if (STAILQ_EMPTY(head1)) \
(head1)->stqh_last = &STAILQ_FIRST(head1); \
if (STAILQ_EMPTY(head2)) \
(head2)->stqh_last = &STAILQ_FIRST(head2); \
} while (0)
/*
* List declarations.
*/
#define LIST_HEAD(name, type) \
struct name { \
struct type *lh_first; /* first element */ \
}
#define LIST_CLASS_HEAD(name, type) \
struct name { \
class type *lh_first; /* first element */ \
}
#define LIST_HEAD_INITIALIZER(head) \
{ NULL }
#define LIST_ENTRY(type) \
struct { \
struct type *le_next; /* next element */ \
struct type **le_prev; /* address of previous next element */ \
}
#define LIST_CLASS_ENTRY(type) \
struct { \
class type *le_next; /* next element */ \
class type **le_prev; /* address of previous next element */ \
}
/*
* List functions.
*/
#if (defined(_KERNEL) && defined(INVARIANTS))
#define QMD_LIST_CHECK_HEAD(head, field) do { \
if (LIST_FIRST((head)) != NULL && \
LIST_FIRST((head))->field.le_prev != \
&LIST_FIRST((head))) \
panic("Bad list head %p first->prev != head", (head)); \
} while (0)
#define QMD_LIST_CHECK_NEXT(elm, field) do { \
if (LIST_NEXT((elm), field) != NULL && \
LIST_NEXT((elm), field)->field.le_prev != \
&((elm)->field.le_next)) \
panic("Bad link elm %p next->prev != elm", (elm)); \
} while (0)
#define QMD_LIST_CHECK_PREV(elm, field) do { \
if (*(elm)->field.le_prev != (elm)) \
panic("Bad link elm %p prev->next != elm", (elm)); \
} while (0)
#else
#define QMD_LIST_CHECK_HEAD(head, field)
#define QMD_LIST_CHECK_NEXT(elm, field)
#define QMD_LIST_CHECK_PREV(elm, field)
#endif /* (_KERNEL && INVARIANTS) */
#define LIST_EMPTY(head) ((head)->lh_first == NULL)
#define LIST_FIRST(head) ((head)->lh_first)
#define LIST_FOREACH(var, head, field) \
for ((var) = LIST_FIRST((head)); \
(var); \
(var) = LIST_NEXT((var), field))
#define LIST_FOREACH_FROM(var, head, field) \
for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
(var); \
(var) = LIST_NEXT((var), field))
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = LIST_FIRST((head)); \
(var) && ((tvar) = LIST_NEXT((var), field), 1); \
(var) = (tvar))
#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
(var) && ((tvar) = LIST_NEXT((var), field), 1); \
(var) = (tvar))
#define LIST_INIT(head) do { \
LIST_FIRST((head)) = NULL; \
} while (0)
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
QMD_LIST_CHECK_NEXT(listelm, field); \
if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
LIST_NEXT((listelm), field)->field.le_prev = \
&LIST_NEXT((elm), field); \
LIST_NEXT((listelm), field) = (elm); \
(elm)->field.le_prev = &LIST_NEXT((listelm), field); \
} while (0)
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
QMD_LIST_CHECK_PREV(listelm, field); \
(elm)->field.le_prev = (listelm)->field.le_prev; \
LIST_NEXT((elm), field) = (listelm); \
*(listelm)->field.le_prev = (elm); \
(listelm)->field.le_prev = &LIST_NEXT((elm), field); \
} while (0)
#define LIST_INSERT_HEAD(head, elm, field) do { \
QMD_LIST_CHECK_HEAD((head), field); \
if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
LIST_FIRST((head)) = (elm); \
(elm)->field.le_prev = &LIST_FIRST((head)); \
} while (0)
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
#define LIST_PREV(elm, head, type, field) \
((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \
__containerof((elm)->field.le_prev, \
QUEUE_TYPEOF(type), field.le_next))
#define LIST_REMOVE(elm, field) do { \
QMD_SAVELINK(oldnext, (elm)->field.le_next); \
QMD_SAVELINK(oldprev, (elm)->field.le_prev); \
QMD_LIST_CHECK_NEXT(elm, field); \
QMD_LIST_CHECK_PREV(elm, field); \
if (LIST_NEXT((elm), field) != NULL) \
LIST_NEXT((elm), field)->field.le_prev = \
(elm)->field.le_prev; \
*(elm)->field.le_prev = LIST_NEXT((elm), field); \
TRASHIT(*oldnext); \
TRASHIT(*oldprev); \
} while (0)
#define LIST_SWAP(head1, head2, type, field) do { \
QUEUE_TYPEOF(type) *swap_tmp = LIST_FIRST(head1); \
LIST_FIRST((head1)) = LIST_FIRST((head2)); \
LIST_FIRST((head2)) = swap_tmp; \
if ((swap_tmp = LIST_FIRST((head1))) != NULL) \
swap_tmp->field.le_prev = &LIST_FIRST((head1)); \
if ((swap_tmp = LIST_FIRST((head2))) != NULL) \
swap_tmp->field.le_prev = &LIST_FIRST((head2)); \
} while (0)
/*
* Tail queue declarations.
*/
#define TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; /* first element */ \
struct type **tqh_last; /* addr of last next element */ \
TRACEBUF \
}
#define TAILQ_CLASS_HEAD(name, type) \
struct name { \
class type *tqh_first; /* first element */ \
class type **tqh_last; /* addr of last next element */ \
TRACEBUF \
}
#define TAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).tqh_first, TRACEBUF_INITIALIZER }
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
TRACEBUF \
}
#define TAILQ_CLASS_ENTRY(type) \
struct { \
class type *tqe_next; /* next element */ \
class type **tqe_prev; /* address of previous next element */ \
TRACEBUF \
}
/*
* Tail queue functions.
*/
#if (defined(_KERNEL) && defined(INVARIANTS))
#define QMD_TAILQ_CHECK_HEAD(head, field) do { \
if (!TAILQ_EMPTY(head) && \
TAILQ_FIRST((head))->field.tqe_prev != \
&TAILQ_FIRST((head))) \
panic("Bad tailq head %p first->prev != head", (head)); \
} while (0)
#define QMD_TAILQ_CHECK_TAIL(head, field) do { \
if (*(head)->tqh_last != NULL) \
panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \
} while (0)
#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \
if (TAILQ_NEXT((elm), field) != NULL && \
TAILQ_NEXT((elm), field)->field.tqe_prev != \
&((elm)->field.tqe_next)) \
panic("Bad link elm %p next->prev != elm", (elm)); \
} while (0)
#define QMD_TAILQ_CHECK_PREV(elm, field) do { \
if (*(elm)->field.tqe_prev != (elm)) \
panic("Bad link elm %p prev->next != elm", (elm)); \
} while (0)
#else
#define QMD_TAILQ_CHECK_HEAD(head, field)
#define QMD_TAILQ_CHECK_TAIL(head, headname)
#define QMD_TAILQ_CHECK_NEXT(elm, field)
#define QMD_TAILQ_CHECK_PREV(elm, field)
#endif /* (_KERNEL && INVARIANTS) */
#define TAILQ_CONCAT(head1, head2, field) do { \
if (!TAILQ_EMPTY(head2)) { \
*(head1)->tqh_last = (head2)->tqh_first; \
(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
(head1)->tqh_last = (head2)->tqh_last; \
TAILQ_INIT((head2)); \
QMD_TRACE_HEAD(head1); \
QMD_TRACE_HEAD(head2); \
} \
} while (0)
#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
#define TAILQ_FIRST(head) ((head)->tqh_first)
#define TAILQ_FOREACH(var, head, field) \
for ((var) = TAILQ_FIRST((head)); \
(var); \
(var) = TAILQ_NEXT((var), field))
#define TAILQ_FOREACH_FROM(var, head, field) \
for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
(var); \
(var) = TAILQ_NEXT((var), field))
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = TAILQ_FIRST((head)); \
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
(var) = (tvar))
#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
(var) = (tvar))
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
for ((var) = TAILQ_LAST((head), headname); \
(var); \
(var) = TAILQ_PREV((var), headname, field))
#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \
for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
(var); \
(var) = TAILQ_PREV((var), headname, field))
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
for ((var) = TAILQ_LAST((head), headname); \
(var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
(var) = (tvar))
#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \
for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
(var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
(var) = (tvar))
#define TAILQ_INIT(head) do { \
TAILQ_FIRST((head)) = NULL; \
(head)->tqh_last = &TAILQ_FIRST((head)); \
QMD_TRACE_HEAD(head); \
} while (0)
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
QMD_TAILQ_CHECK_NEXT(listelm, field); \
if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
TAILQ_NEXT((elm), field)->field.tqe_prev = \
&TAILQ_NEXT((elm), field); \
else { \
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
QMD_TRACE_HEAD(head); \
} \
TAILQ_NEXT((listelm), field) = (elm); \
(elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
QMD_TRACE_ELEM(&(elm)->field); \
QMD_TRACE_ELEM(&(listelm)->field); \
} while (0)
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
QMD_TAILQ_CHECK_PREV(listelm, field); \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
TAILQ_NEXT((elm), field) = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
QMD_TRACE_ELEM(&(elm)->field); \
QMD_TRACE_ELEM(&(listelm)->field); \
} while (0)
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
QMD_TAILQ_CHECK_HEAD(head, field); \
if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
TAILQ_FIRST((head))->field.tqe_prev = \
&TAILQ_NEXT((elm), field); \
else \
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
TAILQ_FIRST((head)) = (elm); \
(elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
QMD_TRACE_HEAD(head); \
QMD_TRACE_ELEM(&(elm)->field); \
} while (0)
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
QMD_TAILQ_CHECK_TAIL(head, field); \
TAILQ_NEXT((elm), field) = NULL; \
(elm)->field.tqe_prev = (head)->tqh_last; \
*(head)->tqh_last = (elm); \
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
QMD_TRACE_HEAD(head); \
QMD_TRACE_ELEM(&(elm)->field); \
} while (0)
#define TAILQ_LAST(head, headname) \
(*(((struct headname *)((head)->tqh_last))->tqh_last))
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define TAILQ_PREV(elm, headname, field) \
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
#define TAILQ_REMOVE(head, elm, field) do { \
QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \
QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \
QMD_TAILQ_CHECK_NEXT(elm, field); \
QMD_TAILQ_CHECK_PREV(elm, field); \
if ((TAILQ_NEXT((elm), field)) != NULL) \
TAILQ_NEXT((elm), field)->field.tqe_prev = \
(elm)->field.tqe_prev; \
else { \
(head)->tqh_last = (elm)->field.tqe_prev; \
QMD_TRACE_HEAD(head); \
} \
*(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
TRASHIT(*oldnext); \
TRASHIT(*oldprev); \
QMD_TRACE_ELEM(&(elm)->field); \
} while (0)
#define TAILQ_SWAP(head1, head2, type, field) do { \
QUEUE_TYPEOF(type) *swap_first = (head1)->tqh_first; \
QUEUE_TYPEOF(type) **swap_last = (head1)->tqh_last; \
(head1)->tqh_first = (head2)->tqh_first; \
(head1)->tqh_last = (head2)->tqh_last; \
(head2)->tqh_first = swap_first; \
(head2)->tqh_last = swap_last; \
if ((swap_first = (head1)->tqh_first) != NULL) \
swap_first->field.tqe_prev = &(head1)->tqh_first; \
else \
(head1)->tqh_last = &(head1)->tqh_first; \
if ((swap_first = (head2)->tqh_first) != NULL) \
swap_first->field.tqe_prev = &(head2)->tqh_first; \
else \
(head2)->tqh_last = &(head2)->tqh_first; \
} while (0)
#endif /* !_SYS_QUEUE_H_ */