]> git.ipfire.org Git - thirdparty/mtr.git/commitdiff
mtr-packet: Fall back to IPv4 only support if IPv6 sockets fail to open 174/head
authorMatt Kimball <matt.kimball@gmail.com>
Wed, 28 Dec 2016 01:09:49 +0000 (17:09 -0800)
committerMatt Kimball <matt.kimball@gmail.com>
Wed, 28 Dec 2016 01:36:48 +0000 (17:36 -0800)
If we fail to open any IPv6 sockets, rather than aborting with an
unrecoverable error, fall back to IPv4 only support.  Socket
creation might fail, for example, when Linux is booted with
the kernel command-line "ipv6.disable=1".

In the case where opening IPv6 sockets fail,
'check-support feature ip-6' will indicate there is no support
for sending IPv6 probes.

Stricter error reporting revealed that test for protocols other
than ICMP were running on Cygwin.  Modified the tests such that
they won't run if the protocol isn't supported.

packet/command.c
packet/probe.h
packet/probe_cygwin.c
packet/probe_unix.c
packet/probe_unix.h
test/probe.py
ui/cmdpipe.c

index 39dedff50229366f27bbb813ec46cf396bbac925..54c3a4835ac2c38fb8929d54ba169df6b6c7277f 100644 (file)
@@ -67,6 +67,19 @@ const char *check_protocol_support(
     }
 }
 
+/*  Return a feature support string for an IP protocol version  */
+static
+const char *check_ip_version_support(
+    struct net_state_t *net_state,
+    int ip_version)
+{
+    if (is_ip_version_supported(net_state, ip_version)) {
+        return "ok";
+    } else {
+        return "no";
+    }
+}
+
 /*  Given a feature name, return a string for the check-support reply  */
 static
 const char *check_support(
@@ -78,11 +91,11 @@ const char *check_support(
     }
 
     if (!strcmp(feature, "ip-4")) {
-        return "ok";
+        return check_ip_version_support(net_state, 4);
     }
 
     if (!strcmp(feature, "ip-6")) {
-        return "ok";
+        return check_ip_version_support(net_state, 6);
     }
 
     if (!strcmp(feature, "send-probe")) {
@@ -262,6 +275,36 @@ bool decode_probe_argument(
     return true;
 }
 
+/*
+    Check the probe parameters against currently supported features
+    and report and error if there is a problem.
+
+    Return true if everything is okay, false otherwise.
+*/
+static
+bool validate_probe_parameters(
+    struct net_state_t *net_state,
+    struct probe_param_t *param)
+{
+    if (!is_ip_version_supported(net_state, param->ip_version)) {
+        printf(
+            "%d invalid-argument reason ip-version-not-supported\n",
+            param->command_token);
+
+        return false;
+    }
+
+    if (!is_protocol_supported(net_state, param->protocol)) {
+        printf(
+            "%d invalid-argument reason protocol-not-supported\n",
+            param->command_token);
+
+        return false;
+    }
+
+    return true;
+}
+
 /*  Handle "send-probe" commands  */
 static
 void send_probe_command(
@@ -291,6 +334,10 @@ void send_probe_command(
         }
     }
 
+    if (!validate_probe_parameters(net_state, &param)) {
+        return;
+    }
+
     /*  Send the probe using a platform specific mechanism  */
     send_probe(net_state, &param);
 }
index d83e2460de1c5addfbc1ad156f1a382039d84cea..4a343fe3edc1570e5ea257068833e2d70e3133bd 100644 (file)
@@ -133,6 +133,10 @@ void init_net_state_privileged(
 void init_net_state(
     struct net_state_t *net_state);
 
+bool is_ip_version_supported(
+    struct net_state_t *net_state,
+    int ip_version);
+
 bool is_protocol_supported(
     struct net_state_t *net_state,
     int protocol);
index 98159f7b97b4640d4689a64a62ee2b6f1ea47aae..32127fd020d31fc3de766abef25805e1540e4019 100644 (file)
@@ -36,16 +36,31 @@ void init_net_state(
     memset(net_state, 0, sizeof(struct net_state_t));
 
     net_state->platform.icmp4 = IcmpCreateFile();
-    if (net_state->platform.icmp4 == INVALID_HANDLE_VALUE) {
-        fprintf(stderr, "Failure opening ICMPv4 %d\n", GetLastError());
+    net_state->platform.icmp6 = Icmp6CreateFile();
+
+    if (net_state->platform.icmp4 == INVALID_HANDLE_VALUE
+            && net_state->platform.icmp6 == INVALID_HANDLE_VALUE)
+    {
+        fprintf(stderr, "Failure opening ICMP %d\n", GetLastError());
         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(EXIT_FAILURE);
+/*
+    If we succeeded at opening the ICMP file handle, we can
+    assume that IP protocol version is supported.
+*/
+bool is_ip_version_supported(
+    struct net_state_t *net_state,
+    int ip_version)
+{
+    if (ip_version == 4) {
+        return (net_state->platform.icmp4 != INVALID_HANDLE_VALUE);
+    } else if (ip_version == 6) {
+        return (net_state->platform.icmp6 != INVALID_HANDLE_VALUE);
     }
+
+    return false;
 }
 
 /*  On Windows, we only support ICMP probes  */
index 44fd41684d17781b789942d2711f36970cfe1a70..3f52575bdee8a541a9d39b731eceb53aaa0bcfb5 100644 (file)
@@ -182,7 +182,7 @@ void set_socket_nonblocking(
 
 /*  Open the raw sockets for sending/receiving IPv4 packets  */
 static
-void open_ip4_sockets(
+int open_ip4_sockets(
     struct net_state_t *net_state)
 {
     int send_socket;
@@ -191,8 +191,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(EXIT_FAILURE);
+        return -1;
     }
 
     /*
@@ -202,8 +201,8 @@ void open_ip4_sockets(
     if (setsockopt(
         send_socket, IPPROTO_IP, IP_HDRINCL, &trueopt, sizeof(int))) {
 
-        perror("Failure to set IP_HDRINCL");
-        exit(EXIT_FAILURE);
+        close(send_socket);
+        return -1;
     }
 
     /*
@@ -212,17 +211,20 @@ void open_ip4_sockets(
     */
     recv_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
     if (recv_socket == -1) {
-        perror("Failure opening IPv4 receive socket");
-        exit(EXIT_FAILURE);
+        close(send_socket);
+        return -1;
     }
 
+    net_state->platform.ip4_present = true;
     net_state->platform.ip4_send_socket = send_socket;
     net_state->platform.ip4_recv_socket = recv_socket;
+
+    return 0;
 }
 
 /*  Open the raw sockets for sending/receiving IPv6 packets  */
 static
-void open_ip6_sockets(
+int open_ip6_sockets(
     struct net_state_t *net_state)
 {
     int send_socket_icmp;
@@ -231,27 +233,30 @@ 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(EXIT_FAILURE);
+        return -1;
     }
 
     send_socket_udp = socket(AF_INET6, SOCK_RAW, IPPROTO_UDP);
     if (send_socket_udp == -1) {
-        perror("Failure opening UDPv6 send socket");
-        exit(EXIT_FAILURE);
+        close(send_socket_icmp);
+
+        return -1;
     }
 
     recv_socket = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
     if (recv_socket == -1) {
-        perror("Failure opening IPv6 receive socket");
-        exit(EXIT_FAILURE);
-    }
+        close(send_socket_icmp);
+        close(send_socket_udp);
 
-    set_socket_nonblocking(recv_socket);
+        return -1;
+    }
 
+    net_state->platform.ip6_present = true;
     net_state->platform.icmp6_send_socket = send_socket_icmp;
     net_state->platform.udp6_send_socket = send_socket_udp;
     net_state->platform.ip6_recv_socket = recv_socket;
+
+    return 0;
 }
 
 /*
@@ -262,12 +267,35 @@ void open_ip6_sockets(
 void init_net_state_privileged(
     struct net_state_t *net_state)
 {
+    int ip4_err = 0;
+    int ip6_err = 0;
+
     memset(net_state, 0, sizeof(struct net_state_t));
 
     net_state->platform.next_sequence = MIN_PORT;
 
-    open_ip4_sockets(net_state);
-    open_ip6_sockets(net_state);
+    if (open_ip4_sockets(net_state)) {
+        ip4_err = errno;
+    }
+    if (open_ip6_sockets(net_state)) {
+        ip6_err = errno;
+    }
+
+    /*
+        If we couldn't open either IPv4 or IPv6 sockets, we can't do
+        much, so print errors and exit.
+    */
+    if (!net_state->platform.ip4_present
+            && !net_state->platform.ip6_present) {
+
+        errno = ip4_err;
+        perror("Failure to open IPv4 sockets");
+
+        errno = ip6_err;
+        perror("Failure to open IPv6 sockets");
+
+        exit(EXIT_FAILURE);
+    }
 }
 
 /*
@@ -280,10 +308,30 @@ void init_net_state(
     set_socket_nonblocking(net_state->platform.ip4_recv_socket);
     set_socket_nonblocking(net_state->platform.ip6_recv_socket);
 
-    check_length_order(net_state);
+    if (net_state->platform.ip4_present) {
+        check_length_order(net_state);
+    }
+
     check_sctp_support(net_state);
 }
 
+/*
+    Returns true if we were able to open sockets for a particular
+    IP protocol version.
+*/
+bool is_ip_version_supported(
+    struct net_state_t *net_state,
+    int ip_version)
+{
+    if (ip_version == 4) {
+        return net_state->platform.ip4_present;
+    } else if (ip_version == 6) {
+        return net_state->platform.ip6_present;
+    } else {
+        return false;
+    }
+}
+
 /*  Returns true if we can transmit probes using the specified protocol  */
 bool is_protocol_supported(
     struct net_state_t *net_state,
@@ -588,13 +636,17 @@ void receive_replies(
     struct probe_t *probe;
     struct probe_t *probe_safe_iter;
 
-    receive_replies_from_icmp_socket(
-        net_state, net_state->platform.ip4_recv_socket,
-        handle_received_ip4_packet);
+    if (net_state->platform.ip4_present) {
+        receive_replies_from_icmp_socket(
+            net_state, net_state->platform.ip4_recv_socket,
+            handle_received_ip4_packet);
+    }
 
-    receive_replies_from_icmp_socket(
-        net_state, net_state->platform.ip6_recv_socket,
-        handle_received_ip6_packet);
+    if (net_state->platform.ip6_present) {
+        receive_replies_from_icmp_socket(
+            net_state, net_state->platform.ip6_recv_socket,
+            handle_received_ip6_packet);
+    }
 
     LIST_FOREACH_SAFE(
             probe, &net_state->outstanding_probes,
index fcb5c88a77bd1ce0126e8cea070a69133747157a..f895a63a0684cc03e5439cd96a253d97199a8a75 100644 (file)
@@ -39,6 +39,12 @@ struct probe_platform_t
 /*  We'll use rack sockets to send and recieve probes on Unix systems  */
 struct net_state_platform_t
 {
+    /*  true if we were successful at opening IPv4 sockets  */
+    bool ip4_present;
+
+    /*  true if we were successful at opening IPv6 sockets  */
+    bool ip6_present;
+
     /*  Socket used to send raw IPv4 packets  */
     int ip4_send_socket;
 
index 871db417b34e8414264f29e1295f45b9e9551dda..2b500c61a8950189de766065f44333294a8dc65b 100755 (executable)
@@ -316,6 +316,9 @@ class TestProbeUDP(mtrpacket.MtrPacketTest):
     def udp_port_test(self, address):  # type: (unicode) -> None
         'Test UDP probes with variations on source port and dest port'
 
+        if not check_feature(self, 'udp'):
+            return
+
         cmd = '80 send-probe protocol udp ' + address
         self.write_command(cmd)
         reply = self.parse_reply()
@@ -356,6 +359,9 @@ class TestProbeTCP(mtrpacket.MtrPacketTest):
 
         test_basic_probe(self, 4, 'tcp')
 
+        if not check_feature(self, 'tcp'):
+            return
+
         #  Probe a local port assumed to be open  (ssh)
         cmd = '80 send-probe ip-4 127.0.0.1 protocol tcp port 22'
         self.write_command(cmd)
@@ -369,6 +375,9 @@ class TestProbeTCP(mtrpacket.MtrPacketTest):
 
         test_basic_probe(self, 6, 'tcp')
 
+        if not check_feature(self, 'tcp'):
+            return
+
         #  Probe a local port assumed to be open  (ssh)
         cmd = '80 send-probe ip-6 ::1 protocol tcp port 22'
         self.write_command(cmd)
index 4783e30caa8a70ee11279400a36e14ba02eaf7cc..9ce4ce10e4023d34daae6370eb54ef1f8804403b 100644 (file)
@@ -152,6 +152,21 @@ int check_packet_features(
     struct mtr_ctl *ctl,
     struct packet_command_pipe_t *cmdpipe)
 {
+    /*  Check the IP protocol version  */
+    if (ctl->af == AF_INET6) {
+        if (check_feature(ctl, cmdpipe, "ip-6")) {
+            return -1;
+        }
+    } else if (ctl->af == AF_INET) {
+        if (check_feature(ctl, cmdpipe, "ip-4")) {
+            return -1;
+        }
+    } else {
+        errno = EINVAL;
+        return -1;
+    }
+
+    /*  Check the transport protocol  */
     if (ctl->mtrtype == IPPROTO_ICMP) {
         if (check_feature(ctl, cmdpipe, "icmp")) {
             return -1;