]> git.ipfire.org Git - thirdparty/mtr.git/commitdiff
mtr-packet: drop capabilities + using BSD's linked lists for probes
authorMatt Kimball <matt.kimball@gmail.com>
Sun, 25 Dec 2016 17:26:34 +0000 (09:26 -0800)
committerMatt Kimball <matt.kimball@gmail.com>
Sun, 25 Dec 2016 17:26:34 +0000 (09:26 -0800)
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.

18 files changed:
BSDCOPYING [new file with mode: 0644]
Makefile.am
SECURITY
configure.ac
packet/command.c
packet/command_cygwin.c
packet/command_unix.c
packet/deconstruct_unix.c
packet/packet.c
packet/probe.c
packet/probe.h
packet/probe_cygwin.c
packet/probe_cygwin.h
packet/probe_unix.c
packet/probe_unix.h
packet/wait_cygwin.c
packet/wait_unix.c
portability/queue.h [new file with mode: 0644]

diff --git a/BSDCOPYING b/BSDCOPYING
new file mode 100644 (file)
index 0000000..c8560bc
--- /dev/null
@@ -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.
index 257baf4b867374f4425ae4ecd25ec1aecd8efe0c..a24c643e573de4869ed126678ba75413fc707051 100644 (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
index f9127ac65f1335cb8b0218b74a5b99eb3bbdc196..1d39c2ebaaf66018f94bc3fce9c56743fb2d4a62 100644 (file)
--- a/SECURITY
+++ b/SECURITY
@@ -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.
index 30bab76e4a8ab85460251ad7454ff9ed7c7ef7fd..25d9179b293e368d4c1a4ce51a0cc2a1fffd269a 100644 (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])],
index 2b9a054684614e80b7657b47afd4ad00def757fd..39dedff50229366f27bbb813ec46cf396bbac925 100644 (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++) {
index d952178e616ac6d9e599a0d8449f1086f715b837..4f477eb972e8c17cfee660ee37a7455e9df0785e 100644 (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);
         }
     }
 
index 0b2f112fc270e9c3f8708ec47796507ae5c4d235..917857725b67c34f6bf7b879578999679f4c0a79 100644 (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);
         }
     }
 
index 8065dd7128d4607562045b0c919bf62a6181718e..5fa169e0588c7e839192df8c97648d9ad56bc752 100644 (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);
     }
 }
 
index 76edc7ca6d32052c07563443db45461dd9198cf2..b09f36f883a80237311c188edd717cdf53f11a59 100644 (file)
     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;
             }
         }
index 3e7cffdc5e7bb4872b7bbdf2f6789c92e2e576e9..aedbdf02488a9de09de4ce67be4831ca515374ae 100644 (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 (net_state->outstanding_probe_count >= MAX_PROBES) {
+        return NULL;
+    }
 
-        if (!probe->used) {
-            memset(probe, 0, sizeof(struct probe_t));
+    probe = malloc(sizeof(struct probe_t));
+    if (probe == NULL) {
+        return NULL;
+    }
 
-            probe->used = true;
-            probe->token = token;
+    memset(probe, 0, sizeof(struct probe_t));
+    probe->token = token;
 
-            platform_alloc_probe(net_state, probe);
+    platform_alloc_probe(net_state, probe);
 
-            return probe;
-        }
-    }
+    net_state->outstanding_probe_count++;
+    LIST_INSERT_HEAD(&net_state->outstanding_probes, probe, probe_list_entry);
 
-    return NULL;
+    return probe;
 }
 
 /*  Mark a probe tracking structure as unused  */
 void free_probe(
+    struct net_state_t *net_state,
     struct probe_t *probe)
 {
-    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;
+    LIST_REMOVE(probe, probe_list_entry);
+    net_state->outstanding_probe_count--;
 
-    count = 0;
-    for (i = 0; i < MAX_PROBES; i++) {
-        probe = &net_state->probes[i];
-
-        if (probe->used) {
-            count++;
-        }
-    }
+    platform_free_probe(probe);
 
-    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);
 }
 
 /*
index 25a225d3197e631a9afc8d71d21e952afd844993..d83e2460de1c5addfbc1ad156f1a382039d84cea 100644 (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,19 +174,17 @@ struct probe_t *alloc_probe(
     struct net_state_t *net_state,
     int token);
 
-void platform_alloc_probe(
+void free_probe(
     struct net_state_t *net_state,
     struct probe_t *probe);
 
-void platform_free_probe(
+void platform_alloc_probe(
+    struct net_state_t *net_state,
     struct probe_t *probe);
 
-void free_probe(
+void platform_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,
index 2857db9a2944c99e0f670ce3342d65407f2a1778..98159f7b97b4640d4689a64a62ee2b6f1ea47aae 100644 (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(
index 7fab3279e6736f471ddbeda09f4a4d6066f7caa0..5012c2122f8d747c7afb98291a67c70d9992ddb8 100644 (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;
 
index 424901ec53ac82da640588158d348c2fd081e010..44fd41684d17781b789942d2711f36970cfe1a70 100644 (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 =
index 8a36e295eb3dd1b972bbda0f05f8669de1453f49..fcb5c88a77bd1ce0126e8cea070a69133747157a 100644 (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,
index 517f71f7d652b446d24bc6ff56da1794d6906a7f..1b9f81a26c9e3b0f8b42019878638df66b595ea0 100644 (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);
     }
 }
index 781546b222fa17f6089a78b18f146ae6670fefc6..408df82be06e41662b9c1b4429661a63305d601b 100644 (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);
         }
     }
 }
diff --git a/portability/queue.h b/portability/queue.h
new file mode 100644 (file)
index 0000000..00156dd
--- /dev/null
@@ -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_ */