]> git.ipfire.org Git - thirdparty/mtr.git/commitdiff
mtr-packet: allow local address binding
authorMatt Kimball <matt.kimball@gmail.com>
Fri, 23 Dec 2016 17:37:47 +0000 (09:37 -0800)
committerMatt Kimball <matt.kimball@gmail.com>
Fri, 23 Dec 2016 17:37:47 +0000 (09:37 -0800)
A probe can be bound to a local address with the 'local-ip-4' or
'local-ip-6' arguments to 'send-probe'.  The '-a' commandline
argument to mtr will cause sent probes to be bound to the specified
address.

Changed argument names for consistency:

    'localport' is now 'local-port'
    'bitpattern' is now 'bit-pattern'

mtr now opens and connects a UDP socket at startup to determine the
local address for display in the UI.

Windows error codes resulting from send-probe are now using the
same error strings which are used to report errors in the
Unix implementation.

We now use the mtr commandline timeout value for sent probes.
Edited the help and man page to reflect that the timeout value
no longer applies to only TCP probes, but now all probes.

With this change, I believe the mtr-packet implementation is at
feature parity with mainline mtr.

16 files changed:
cmdpipe.c
cmdpipe.h
mtr-packet.8.in
mtr.8.in
mtr.c
mtr.h
net.c
packet/command.c
packet/construct_unix.c
packet/construct_unix.h
packet/probe.c
packet/probe.h
packet/probe_cygwin.c
packet/probe_unix.c
test/param.py
test/probe.py

index 3a38ec7364e10d4873bd86a6d218ecd265f54607..4783e30caa8a70ee11279400a36e14ba02eaf7cc 100644 (file)
--- a/cmdpipe.c
+++ b/cmdpipe.c
@@ -325,24 +325,38 @@ void construct_base_command(
     char *command,
     int buffer_size,
     int command_token,
-    ip_t *address)
+    ip_t *address,
+    ip_t *localaddress)
 {
     char ip_string[INET6_ADDRSTRLEN];
+    char local_ip_string[INET6_ADDRSTRLEN];
     const char *ip_type;
+    const char *local_ip_type;
     const char *protocol = NULL;
 
     /*  Conver the remote IP address to a string  */
     if (inet_ntop(
-            ctl->af, address, ip_string, INET6_ADDRSTRLEN) == NULL) {
+            ctl->af, address,
+            ip_string, INET6_ADDRSTRLEN) == NULL) {
 
         display_close(ctl);
-        error(EXIT_FAILURE, errno, "failure stringifying remote IP address");
+        error(EXIT_FAILURE, errno, "invalid remote IP address");
+    }
+
+    if (inet_ntop(
+            ctl->af, localaddress,
+            local_ip_string, INET6_ADDRSTRLEN) == NULL) {
+
+        display_close(ctl);
+        error(EXIT_FAILURE, errno, "invalid local IP address");
     }
 
     if (ctl->af == AF_INET6) {
         ip_type = "ip-6";
+        local_ip_type = "local-ip-6";
     } else {
         ip_type = "ip-4";
+        local_ip_type = "local-ip-4";
     }
 
     if (ctl->mtrtype == IPPROTO_ICMP) {
@@ -362,8 +376,10 @@ void construct_base_command(
 
     snprintf(
         command, buffer_size,
-        "%d send-probe %s %s protocol %s",
-        command_token, ip_type, ip_string, protocol);
+        "%d send-probe %s %s %s %s protocol %s",
+        command_token,
+        ip_type, ip_string, local_ip_type, local_ip_string,
+        protocol);
 }
 
 
@@ -390,21 +406,23 @@ void send_probe_command(
     struct mtr_ctl *ctl,
     struct packet_command_pipe_t *cmdpipe,
     ip_t *address,
+    ip_t *localaddress,
     int packet_size,
     int sequence,
     int time_to_live)
 {
     char command[COMMAND_BUFFER_SIZE];
     int remaining_size;
+    int timeout;
 
     construct_base_command(
-        ctl, command, COMMAND_BUFFER_SIZE, sequence, address);
+        ctl, command, COMMAND_BUFFER_SIZE, sequence, address, localaddress);
 
     append_command_argument(
         command, COMMAND_BUFFER_SIZE, "size", packet_size);
 
     append_command_argument(
-        command, COMMAND_BUFFER_SIZE, "bitpattern", ctl->bitpattern);
+        command, COMMAND_BUFFER_SIZE, "bit-pattern", ctl->bitpattern);
 
     append_command_argument(
         command, COMMAND_BUFFER_SIZE, "tos", ctl->tos);
@@ -412,6 +430,10 @@ void send_probe_command(
     append_command_argument(
         command, COMMAND_BUFFER_SIZE, "ttl", time_to_live);
 
+    timeout = ctl->probe_timeout / 1000000;
+    append_command_argument(
+        command, COMMAND_BUFFER_SIZE, "timeout", timeout);
+
     if (ctl->remoteport) {
         append_command_argument(
             command, COMMAND_BUFFER_SIZE, "port", ctl->remoteport);
@@ -419,7 +441,7 @@ void send_probe_command(
 
     if (ctl->localport) {
         append_command_argument(
-            command, COMMAND_BUFFER_SIZE, "localport", ctl->localport);
+            command, COMMAND_BUFFER_SIZE, "local-port", ctl->localport);
     }
 
 #ifdef SO_MARK
@@ -609,6 +631,16 @@ void handle_reply_errors(
         error(EXIT_FAILURE, 0, "Permission denied");
     }
 
+    if (!strcmp(reply_name, "address-in-use")) {
+        display_close(ctl);
+        error(EXIT_FAILURE, 0, "Address in use");
+    }
+
+    if (!strcmp(reply_name, "address-not-available")) {
+        display_close(ctl);
+        error(EXIT_FAILURE, 0, "Address not available");
+    }
+
     if (!strcmp(reply_name, "unexpected-error")) {
         display_close(ctl);
         error(EXIT_FAILURE, 0, "Unexpected mtr-packet error");
index 9c0dd869cb0723516d25c68075ec8f567d95495f..8f2cb7069d5499dcef25492dc901b21486b79ae2 100644 (file)
--- a/cmdpipe.h
+++ b/cmdpipe.h
@@ -61,6 +61,7 @@ void send_probe_command(
     struct mtr_ctl *ctl,
     struct packet_command_pipe_t *cmdpipe,
     ip_t *address,
+    ip_t *localaddress,
     int packet_size,
     int sequence,
     int time_to_live);
index 97b46aff8d3e41bf7345e59de1c9f6b7bd864827..9691f74147408c91062397cef565be2533effc0d 100644 (file)
@@ -117,7 +117,21 @@ or
 probes.
 .HP 7
 .IP
-.B localport
+.B local-ip-4
+.I IP-ADDRESS
+.HP 14
+.IP
+The local Internet Procol version 4 address to use when sending probes.
+.HP 7
+.IP
+.B local-ip-6
+.I IP-ADDRESS
+.HP 14
+.IP
+The local Internet Protocol version 6 address to use when sending probes.
+.HP 7
+.IP
+.B local-port
 .I PORT-NUMBER
 .HP 14
 .IP
@@ -154,7 +168,7 @@ The size of the packet used to send the probe, in bytes, including the
 Internet Protocol header and transport protocol header.
 .HP 7
 .IP
-.B bitpattern
+.B bit-pattern
 .I PATTERN-VALUE
 .HP 14
 .IP
index eb9fbe8a7dd95c7856db99ab21607fa54208e6eb..79ffc097531f402afa32a67162dfe884ab0ce96f 100644 (file)
--- a/mtr.8.in
+++ b/mtr.8.in
@@ -407,10 +407,9 @@ The target port number for TCP/SCTP/UDP traces.
 The source port number for UDP traces.
 .TP
 .B \-Z \fISECONDS\fR, \fB\-\-timeout \fISECONDS
-The number of seconds to keep the TCP socket open before giving up on
-the connection.  This will only affect the final hop.  Using large values
-for this, especially combined with a short interval, will use up a lot
-of file descriptors.
+The number of seconds to keep probe sockets open before giving up on
+the connection.  Using large values for this, especially combined with
+a short interval, will use up a lot of file descriptors.
 .TP
 .B \-M \fIMARK\fR, \fB\-\-mark \fIMARK
 Set the mark for each packet sent through this socket similar to the
diff --git a/mtr.c b/mtr.c
index 48307ff20dc5f9b77d420eb72c39340aef3c8424..0f0650ec158677139286d4f0298fadfa2af748e4 100644 (file)
--- a/mtr.c
+++ b/mtr.c
@@ -102,21 +102,21 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
   fputs(" -F, --filename FILE        read hostname(s) from a file\n", out);
   fputs(" -4                         use IPv4 only\n", out);
   fputs(" -6                         use IPv6 only\n", out);
-  fputs(" -u, --udp                  use udp instead of icmp echo\n", out);
-  fputs(" -T, --tcp                  use tcp instead of icmp echo\n", out);
+  fputs(" -u, --udp                  use UDP instead of ICMP echo\n", out);
+  fputs(" -T, --tcp                  use TCP instead of ICMP echo\n", out);
   fputs(" -a, --address ADDRESS      bind the outgoing socket to ADDRESS\n", out);
   fputs(" -f, --first-ttl NUMBER     set what TTL to start\n", out);
   fputs(" -m, --max-ttl NUMBER       maximum number of hops\n", out);
   fputs(" -U, --max-unknown NUMBER   maximum unknown host\n", out);
-  fputs(" -P, --port PORT            target port number for tcp, sctp, or udp\n", out);
-  fputs(" -L, --localport LOCALPORT  source port number for udp\n", out);
+  fputs(" -P, --port PORT            target port number for TCP, SCTP, or UDP\n", out);
+  fputs(" -L, --localport LOCALPORT  source port number for UDP\n", out);
   fputs(" -s, --psize PACKETSIZE     set the packet size used for probing\n", out);
   fputs(" -B, --bitpattern NUMBER    set bit pattern to use in payload\n", out);
-  fputs(" -i, --interval SECONDS     icmp echo request interval\n", out);
+  fputs(" -i, --interval SECONDS     ICMP echo request interval\n", out);
   fputs(" -G, --gracetime SECONDS    number of seconds to wait for responses\n", out);
   fputs(" -Q, --tos NUMBER           type of service field in IP header\n", out);
   fputs(" -e, --mpls                 display information from ICMP extensions\n", out);
-  fputs(" -Z, --timeout SECONDS      seconds to keep the TCP socket open\n", out);
+  fputs(" -Z, --timeout SECONDS      seconds to keep probe sockets open\n", out);
   fputs(" -M, --mark MARK            mark each sent packet\n", out);
   fputs(" -r, --report               output using report mode\n", out);
   fputs(" -w, --report-wide          output wide report\n", out);
@@ -137,7 +137,7 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
   fputs(" -b, --show-ips             show IP numbers and host names\n", out);
   fputs(" -o, --order FIELDS         select output fields\n", out);
 #ifdef HAVE_IPINFO
-  fputs(" -y, --ipinfo NUMBER        select ip information in output\n", out);
+  fputs(" -y, --ipinfo NUMBER        select IP information in output\n", out);
   fputs(" -z, --aslookup             display AS number\n", out);
 #endif
   fputs(" -h, --help                 display this help and exit\n", out);
@@ -323,7 +323,7 @@ static void parse_arg (struct mtr_ctl *ctl, names_t **names, int argc, char **ar
     { "sctp",           0, NULL, 'S' }, /* SCTP (default is ICMP) */
     { "port",           1, NULL, 'P' }, /* target port number for TCP/SCTP/UDP */
     { "localport",      1, NULL, 'L' }, /* source port number for UDP */
-    { "timeout",        1, NULL, 'Z' }, /* timeout for TCP sockets */
+    { "timeout",        1, NULL, 'Z' }, /* timeout for probe sockets */
     { "gracetime",      1, NULL, 'G' }, /* gracetime for replies after last probe */
 #ifdef SO_MARK
     { "mark",           1, NULL, 'M' }, /* use SO_MARK */
@@ -531,8 +531,8 @@ static void parse_arg (struct mtr_ctl *ctl, names_t **names, int argc, char **ar
       }
       break;
     case 'Z':
-      ctl->tcp_timeout = strtonum_or_err(optarg, "invalid argument", STRTO_INT);
-      ctl->tcp_timeout *= 1000000;
+      ctl->probe_timeout = strtonum_or_err(optarg, "invalid argument", STRTO_INT);
+      ctl->probe_timeout *= 1000000;
       break;
     case '4':
       ctl->af = AF_INET;
@@ -648,7 +648,7 @@ extern int main(int argc, char **argv)
   ctl.fstTTL = 1;
   ctl.maxTTL = 30;
   ctl.maxUnknown = 12;
-  ctl.tcp_timeout = 10 * 1000000;
+  ctl.probe_timeout = 10 * 1000000;
   ctl.ipinfo_no = -1;
   ctl.ipinfo_max = -1;
   xstrncpy(ctl.fld_active, "LS NABWV", 2 * MAXFLD);
diff --git a/mtr.h b/mtr.h
index 1058a5a355fbf0e2a697dc3666bf944f842030b8..364ed2ab10330d12d652f69a3fc847e2c6be68fe 100644 (file)
--- a/mtr.h
+++ b/mtr.h
@@ -101,7 +101,7 @@ struct mtr_ctl {
   int maxUnknown;              /* stop ping threshold */
   int remoteport;              /* target port for TCP tracing */
   int localport;               /* source port for UDP tracing */
-  int tcp_timeout;             /* timeout for TCP connections */
+  int probe_timeout;           /* timeout for probe sockets */
   unsigned char fld_active[2 * MAXFLD];        /* SO_MARK to set for ping packet*/
   int display_mode;            /* display mode selector */
   int fld_index[FLD_INDEX_SZ]; /* default display field (defined by key in net.h) and order */
diff --git a/net.c b/net.c
index 3703e9a49c4afc9e2d2ca7d689a618ab4d526aee..e49de8e7a809d445ed6e3f219808993ebe7b5711 100644 (file)
--- a/net.c
+++ b/net.c
 
 #include "config.h"
 
+#include <errno.h>
 #include <math.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/select.h>
+#include <unistd.h>
 
 #ifdef HAVE_ERROR_H
 # include <error.h>
@@ -41,6 +43,8 @@
 
 static int packetsize;         /* packet size used by ping */
 
+static void sockaddrtop( struct sockaddr * saddr, char * strptr, size_t len );
+
 struct nethost {
   ip_t addr;
   ip_t addrs[MAXPATH]; /* for multi paths byMin */
@@ -88,6 +92,7 @@ static struct sockaddr_in sourcesockaddr_struct;
 static struct sockaddr_in remotesockaddr_struct;
 #endif
 
+static struct sockaddr * sourcesockaddr = (struct sockaddr *) &sourcesockaddr_struct;
 static struct sockaddr * remotesockaddr = (struct sockaddr *) &remotesockaddr_struct;
 static struct sockaddr_in * ssa4 = (struct sockaddr_in *) &sourcesockaddr_struct;
 static struct sockaddr_in * rsa4 = (struct sockaddr_in *) &remotesockaddr_struct;
@@ -154,7 +159,8 @@ static void net_send_query(struct mtr_ctl *ctl, int index, int packet_size)
   int time_to_live = index + 1;
 
   send_probe_command(
-    ctl, &packet_command_pipe, remoteaddress, packetsize, seq, time_to_live);
+    ctl, &packet_command_pipe, remoteaddress, sourceaddress,
+    packetsize, seq, time_to_live);
 }
 
 
@@ -481,6 +487,75 @@ extern int net_send_batch(struct mtr_ctl *ctl)
 }
 
 
+/*  Ensure the interface address a valid address for our use  */
+static void net_validate_interface_address(
+  int address_family, char *interface_address)
+{
+  if (inet_pton(
+      address_family, interface_address, sourceaddress) != 1) {
+
+    error(EXIT_FAILURE, errno, "invalid local address");
+  }
+
+  if (inet_ntop(
+      address_family, sourceaddress, localaddr, sizeof(localaddr)) == NULL) {
+
+    error(EXIT_FAILURE, errno, "invalid local address");
+  }
+}
+
+
+/*
+  Find the local address we will use to sent to the remote
+  host by connecting a UDP socket and checking the address
+  the socket is bound to.
+*/
+static void net_find_local_address(void)
+{
+  int udp_socket;
+  int addr_length;
+  struct sockaddr_storage remote_sockaddr;
+  struct sockaddr_in *remote4;
+  struct sockaddr_in6 *remote6;
+
+  udp_socket = socket(remotesockaddr->sa_family, SOCK_DGRAM, IPPROTO_UDP);
+  if (udp_socket == -1) {
+    error(EXIT_FAILURE, errno, "udp socket creation failed");
+  }
+
+  /*
+    We need to set the port to a non-zero value for the connect
+    to succeed.
+  */
+  if (remotesockaddr->sa_family == AF_INET6) {
+    addr_length = sizeof(struct sockaddr_in6);
+
+    memcpy(&remote_sockaddr, rsa6, addr_length);
+    remote6 = (struct sockaddr_in6 *)&remote_sockaddr;
+    remote6->sin6_port = htons(1);
+  } else {
+    addr_length = sizeof(struct sockaddr_in);
+
+    memcpy(&remote_sockaddr, rsa4, addr_length);
+    remote4 = (struct sockaddr_in *)&remote_sockaddr;
+    remote4->sin_port = htons(1);
+  }
+
+  if (connect(udp_socket, (struct sockaddr *)&remote_sockaddr, addr_length)) {
+    error(EXIT_FAILURE, errno, "udp socket connect failed");
+  }
+
+  if (getsockname(udp_socket, sourcesockaddr, &addr_length)) {
+
+    error(EXIT_FAILURE, errno, "local address determination failed");
+  }
+
+  sockaddrtop(sourcesockaddr, localaddr, sizeof(localaddr));
+
+  close(udp_socket);
+}
+
+
 extern int net_open(struct mtr_ctl *ctl, struct hostent * hostent)
 {
   int err;
@@ -512,6 +587,12 @@ extern int net_open(struct mtr_ctl *ctl, struct hostent * hostent)
     error(EXIT_FAILURE, 0, "net_open bad address type");
   }
 
+  if (ctl->InterfaceAddress) {
+    net_validate_interface_address(ctl->af, ctl->InterfaceAddress);
+  } else {
+    net_find_local_address();
+  }
+
   return 0;
 }
 
@@ -618,6 +699,33 @@ extern void net_save_return(int at, int seq, int ms)
   host[at].saved[idx] = ms;
 }
 
+/* Similar to inet_ntop but uses a sockaddr as it's argument. */
+static void sockaddrtop( struct sockaddr * saddr, char * strptr, size_t len ) {
+  struct sockaddr_in *  sa4;
+#ifdef ENABLE_IPV6
+  struct sockaddr_in6 * sa6;
+#endif
+
+  switch ( saddr->sa_family ) {
+  case AF_INET:
+    sa4 = (struct sockaddr_in *) saddr;
+    xstrncpy( strptr, inet_ntoa( sa4->sin_addr ), len - 1 );
+    strptr[ len - 1 ] = '\0';
+    return;
+#ifdef ENABLE_IPV6
+  case AF_INET6:
+    sa6 = (struct sockaddr_in6 *) saddr;
+    inet_ntop( sa6->sin6_family, &(sa6->sin6_addr), strptr, len );
+    return;
+#endif
+  default:
+    error(0, 0, "sockaddrtop unknown address type");
+    strptr[0] = '\0';
+    return;
+  }
+}
+
+
 /* Address comparison. */
 extern int addrcmp( char * a, char * b, int family ) {
   int rc = -1;
index faee5a852ff6b960f54ef8c0c80da084ad5f59b6..2b9a054684614e80b7657b47afd4ad00def757fd 100644 (file)
@@ -150,13 +150,23 @@ bool decode_probe_argument(
     /*  Pass IPv4 addresses as string values  */
     if (!strcmp(name, "ip-4")) {
         param->ip_version = 4;
-        param->address = value;
+        param->remote_address = value;
     }
 
     /*  IPv6 address  */
     if (!strcmp(name, "ip-6")) {
         param->ip_version = 6;
-        param->address = value;
+        param->remote_address = value;
+    }
+
+    /*  IPv4 address to send from  */
+    if (!strcmp(name, "local-ip-4")) {
+        param->local_address = value;
+    }
+
+    /*  IPv6 address to send from  */
+    if (!strcmp(name, "local-ip-6")) {
+        param->local_address = value;
     }
 
     /*  Protocol for the probe  */
@@ -185,7 +195,7 @@ bool decode_probe_argument(
     }
 
     /*  The local port to send UDP probes from  */
-    if (!strcmp(name, "localport")) {
+    if (!strcmp(name, "local-port")) {
         param->local_port = strtol(value, &endstr, 10);
         if (*endstr != 0) {
             return false;
@@ -226,7 +236,7 @@ bool decode_probe_argument(
     }
 
     /*  The packet's bytes will be filled with this value  */
-    if (!strcmp(name, "bitpattern")) {
+    if (!strcmp(name, "bit-pattern")) {
         param->bit_pattern = strtol(value, &endstr, 10);
         if (*endstr != 0) {
             return false;
index 58004265fbbe1b8d9446638a040c738b80203319..6fe368486690327b0ead3e5804aecd600d9e7a67 100644 (file)
@@ -612,6 +612,11 @@ int construct_ip6_packet(
         return 0;
     }
 
+    if (bind(send_socket,
+            (struct sockaddr *)src_sockaddr, sizeof(struct sockaddr_in6))) {
+        return -1;
+    }
+
     /*  The traffic class in IPv6 is analagous to ToS in IPv4  */
     if (setsockopt(
             send_socket, IPPROTO_IPV6,
@@ -647,10 +652,10 @@ int construct_packet(
     char *packet_buffer,
     int packet_buffer_size,
     const struct sockaddr_storage *dest_sockaddr,
+    const struct sockaddr_storage *src_sockaddr,
     const struct probe_param_t *param)
 {
     int packet_size;
-    struct sockaddr_storage src_sockaddr;
 
     packet_size = compute_packet_size(net_state, param);
     if (packet_size < 0) {
@@ -662,24 +667,20 @@ int construct_packet(
         return -1;
     }
 
-    if (find_source_addr(&src_sockaddr, dest_sockaddr)) {
-        return -1;
-    }
-
     memset(packet_buffer, param->bit_pattern, packet_size);
 
     if (param->ip_version == 6) {
         if (construct_ip6_packet(
                 net_state, packet_socket, sequence,
                 packet_buffer, packet_size,
-                &src_sockaddr, dest_sockaddr, param)) {
+                src_sockaddr, dest_sockaddr, param)) {
             return -1;
         }
     } else if (param->ip_version == 4) {
         if (construct_ip4_packet(
                 net_state, packet_socket, sequence,
                 packet_buffer, packet_size,
-                &src_sockaddr, dest_sockaddr, param)) {
+                src_sockaddr, dest_sockaddr, param)) {
             return -1;
         }
     } else {
index 1f0ae48ef2717f77f57d499be8c2d24e20dce2e9..7ac7110f13a66b26ef63b85e0c9625f0dd2eb85f 100644 (file)
@@ -28,6 +28,7 @@ int construct_packet(
     char *packet_buffer,
     int packet_buffer_size,
     const struct sockaddr_storage *dest_sockaddr,
+    const struct sockaddr_storage *src_sockaddr,
     const struct probe_param_t *param);
 
 #endif
index 7817866dcae56c6d11b35eb7ce1e8f2b75f7af98..3e7cffdc5e7bb4872b7bbdf2f6789c92e2e576e9 100644 (file)
 #define IP_TEXT_LENGTH 64
 
 /*  Convert the destination address from text to sockaddr  */
-int decode_dest_addr(
-    const struct probe_param_t *param,
-    struct sockaddr_storage *dest_sockaddr)
+int decode_address_string(
+    int ip_version,
+    const char *address_string,
+    struct sockaddr_storage *address)
 {
-    struct in_addr dest_addr4;
-    struct in6_addr dest_addr6;
+    struct in_addr addr4;
+    struct in6_addr addr6;
     struct sockaddr_in *sockaddr4;
     struct sockaddr_in6 *sockaddr6;
 
-    if (param->address == NULL) {
+    if (address == NULL) {
         errno = EINVAL;
         return -1;
     }
 
-    if (param->ip_version == 6) {
-        sockaddr6 = (struct sockaddr_in6 *)dest_sockaddr;
+    if (ip_version == 6) {
+        sockaddr6 = (struct sockaddr_in6 *)address;
 
-        if (inet_pton(AF_INET6, param->address, &dest_addr6) != 1) {
+        if (inet_pton(AF_INET6, address_string, &addr6) != 1) {
             errno = EINVAL;
             return -1;
         }
@@ -60,19 +61,19 @@ int decode_dest_addr(
         sockaddr6->sin6_family = AF_INET6;
         sockaddr6->sin6_port = 0;
         sockaddr6->sin6_flowinfo = 0;
-        sockaddr6->sin6_addr = dest_addr6;
+        sockaddr6->sin6_addr = addr6;
         sockaddr6->sin6_scope_id = 0;
-    } else if (param->ip_version == 4) {
-        sockaddr4 = (struct sockaddr_in *)dest_sockaddr;
+    } else if (ip_version == 4) {
+        sockaddr4 = (struct sockaddr_in *)address;
 
-        if (inet_pton(AF_INET, param->address, &dest_addr4) != 1) {
+        if (inet_pton(AF_INET, address_string, &addr4) != 1) {
             errno = EINVAL;
             return -1;
         }
 
         sockaddr4->sin_family = AF_INET;
         sockaddr4->sin_port = 0;
-        sockaddr4->sin_addr = dest_addr4;
+        sockaddr4->sin_addr = addr4;
     } else {
         errno = EINVAL;
         return -1;
@@ -81,6 +82,34 @@ int decode_dest_addr(
     return 0;
 }
 
+/*
+    Resolve the probe parameters into a remote and local address
+    for the probe.
+*/
+int resolve_probe_addresses(
+    const struct probe_param_t *param,
+    struct sockaddr_storage *dest_sockaddr,
+    struct sockaddr_storage *src_sockaddr)
+{
+    if (decode_address_string(
+            param->ip_version, param->remote_address, dest_sockaddr)) {
+        return -1;
+    }
+
+    if (param->local_address) {
+        if (decode_address_string(
+                param->ip_version, param->local_address, src_sockaddr)) {
+            return -1;
+        }
+    } else {
+        if (find_source_addr(src_sockaddr, dest_sockaddr)) {
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
 /*  Allocate a structure for tracking a new probe  */
 struct probe_t *alloc_probe(
     struct net_state_t *net_state,
index 653838810ede17ef8f73de785bffebecbeb25526..25a225d3197e631a9afc8d71d21e952afd844993 100644 (file)
@@ -46,7 +46,10 @@ struct probe_param_t
     int command_token;
 
     /*  The IP address to probe  */
-    const char *address;
+    const char *remote_address;
+
+    /*  The local address from which to send probes  */
+    const char *local_address;
 
     /*  Protocol for the probe, using the IPPROTO_* defines  */
     int protocol;
@@ -150,9 +153,15 @@ void respond_to_probe(
     int mpls_count,
     const struct mpls_label_t *mpls);
 
-int decode_dest_addr(
+int decode_address_string(
+    int ip_version,
+    const char *address_string,
+    struct sockaddr_storage *address);
+
+int resolve_probe_addresses(
     const struct probe_param_t *param,
-    struct sockaddr_storage *dest_sockaddr);
+    struct sockaddr_storage *dest_sockaddr,
+    struct sockaddr_storage *src_sockaddr);
 
 struct probe_t *alloc_probe(
     struct net_state_t *net_state,
index 0818f52ae748f232029a0b797ff142952332e8e7..2857db9a2944c99e0f670ce3342d65407f2a1778 100644 (file)
@@ -77,6 +77,29 @@ void platform_free_probe(
     }
 }
 
+/*  Report a windows error code using a platform-independent error string  */
+static
+void report_win_error(
+    int command_token,
+    int err)
+{
+    /*  It could be that we got no reply because of timeout  */
+    if (err == IP_REQ_TIMED_OUT) {
+        printf("%d no-reply\n", command_token);
+    } else if (err == IP_DEST_HOST_UNREACHABLE
+            || err == IP_DEST_NET_UNREACHABLE
+            || err == IP_DEST_UNREACHABLE
+            || err == IP_DEST_NO_ROUTE) {
+        printf("%d no-route\n", command_token);
+    } else if (err == ERROR_INVALID_NETNAME) {
+        printf("%d address-not-available\n", command_token);
+    } else if (err == ERROR_INVALID_PARAMETER) {
+        printf("%d invalid-argument\n", command_token);
+    } else {
+        printf("%d unexpected-error winerror %d\n", command_token, err);
+    }
+}
+
 /*
     The overlapped I/O style completion routine to be called by
     Windows during an altertable wait when an ICMP probe has
@@ -139,16 +162,9 @@ void WINAPI on_icmp_reply(
     if (reply_count == 0) {
         err = GetLastError();
 
-        /*  It could be that we got no reply because of timeout  */
-        if (err == IP_REQ_TIMED_OUT) {
-            printf("%d no-reply\n", probe->token);
-
-            free_probe(probe);
-            return;
-        }
-
-        fprintf(stderr, "IcmpParseReplies failure %d\n", err);
-        exit(1);
+        report_win_error(probe->token, err);
+        free_probe(probe);
+        return;
     }
 
 
@@ -183,6 +199,7 @@ void icmp_send_probe(
     DWORD timeout;
     DWORD send_result;
     int reply_size;
+    int err;
     struct sockaddr_in *dest_sockaddr4;
     struct sockaddr_in6 *src_sockaddr6;
     struct sockaddr_in6 *dest_sockaddr6;
@@ -233,13 +250,15 @@ void icmp_send_probe(
     }
 
     if (send_result == 0) {
+        err = GetLastError();
+
         /*
             ERROR_IO_PENDING is expected for asynchronous probes,
             but any other error is unexpected.
         */
-        if (GetLastError() != ERROR_IO_PENDING) {
-            fprintf(stderr, "IcmpSendEcho2 failure %d\n", GetLastError());
-            exit(1);
+        if (err != ERROR_IO_PENDING) {
+            report_win_error(probe->token, err);
+            free_probe(probe);
         }
     }
 }
@@ -289,7 +308,7 @@ void send_probe(
     char payload[PACKET_BUFFER_SIZE];
     int payload_size;
 
-    if (decode_dest_addr(param, &dest_sockaddr)) {
+    if (resolve_probe_addresses(param, &dest_sockaddr, &src_sockaddr)) {
         printf("%d invalid-argument\n", param->command_token);
         return;
     }
@@ -300,11 +319,6 @@ void send_probe(
         return;
     }
 
-    if (find_source_addr(&src_sockaddr, &dest_sockaddr)) {
-        fprintf(stderr, "error finding source address\n");
-        exit(1);
-    }
-
     probe->platform.ip_version = param->ip_version;
 
     payload_size = fill_payload(param, payload, PACKET_BUFFER_SIZE);
index cdfd6992a58931f1adec4b1e7b23e2653baa1c18..424901ec53ac82da640588158d348c2fd081e010 100644 (file)
@@ -87,6 +87,7 @@ void check_length_order(
     char packet[PACKET_BUFFER_SIZE];
     struct probe_param_t param;
     struct sockaddr_storage dest_sockaddr;
+    struct sockaddr_storage src_sockaddr;
     ssize_t bytes_sent;
     int packet_size;
 
@@ -94,9 +95,9 @@ void check_length_order(
     param.ip_version = 4;
     param.protocol = IPPROTO_ICMP;
     param.ttl = 255;
-    param.address = "127.0.0.1";
+    param.remote_address = "127.0.0.1";
 
-    if (decode_dest_addr(&param, &dest_sockaddr)) {
+    if (resolve_probe_addresses(&param, &dest_sockaddr, &src_sockaddr)) {
         fprintf(stderr, "Error decoding localhost address\n");
         exit(1);
     }
@@ -106,7 +107,8 @@ void check_length_order(
 
     packet_size = construct_packet(
         net_state, NULL, MIN_PORT,
-        packet, PACKET_BUFFER_SIZE, &dest_sockaddr, &param);
+        packet, PACKET_BUFFER_SIZE,
+        &dest_sockaddr, &src_sockaddr, &param);
     if (packet_size < 0) {
         perror("Unable to send to localhost");
         exit(1);
@@ -123,7 +125,8 @@ void check_length_order(
 
     packet_size = construct_packet(
         net_state, NULL, MIN_PORT,
-        packet, PACKET_BUFFER_SIZE, &dest_sockaddr, &param);
+        packet, PACKET_BUFFER_SIZE,
+        &dest_sockaddr, &src_sockaddr, &param);
     if (packet_size < 0) {
         perror("Unable to send to localhost");
         exit(1);
@@ -318,10 +321,14 @@ void report_packet_error(
         printf("%d network-down\n", command_token);
     } else if (errno == ENETUNREACH) {
         printf("%d no-route\n", command_token);
+    } else if (errno == EHOSTUNREACH) {
+        printf("%d no-route\n", command_token);
     } else if (errno == EPERM) {
         printf("%d permission-denied\n", command_token);
     } else if (errno == EADDRINUSE) {
         printf("%d address-in-use\n", command_token);
+    } else if (errno == EADDRNOTAVAIL) {
+        printf("%d address-not-available\n", command_token);
     } else {
         printf("%d unexpected-error errno %d\n", command_token, errno);
     }
@@ -335,6 +342,7 @@ void send_probe(
     char packet[PACKET_BUFFER_SIZE];
     struct probe_t *probe;
     int packet_size;
+    struct sockaddr_storage src_sockaddr;
 
     probe = alloc_probe(net_state, param->command_token);
     if (probe == NULL) {
@@ -342,7 +350,7 @@ void send_probe(
         return;
     }
 
-    if (decode_dest_addr(param, &probe->remote_addr)) {
+    if (resolve_probe_addresses(param, &probe->remote_addr, &src_sockaddr)) {
         printf("%d invalid-argument\n", param->command_token);
         free_probe(probe);
         return;
@@ -355,7 +363,8 @@ void send_probe(
 
     packet_size = construct_packet(
         net_state, &probe->platform.socket, probe->sequence,
-        packet, PACKET_BUFFER_SIZE, &probe->remote_addr, param);
+        packet, PACKET_BUFFER_SIZE,
+        &probe->remote_addr, &src_sockaddr, param);
 
     if (packet_size < 0) {
         /*
index 7db108ce66480b2f7c0a6faa19c88ca8dfb02d97..d5110626d31e4ec9696ac3692aff8235a3f1d02f 100755 (executable)
@@ -43,7 +43,7 @@ class TestParameters(mtrpacket.MtrPacketTest):
         'Test probes are filled with the requested bit pattern'
 
         with mtrpacket.PacketListen('-4') as listen:
-            cmd = '20 send-probe ip-4 127.0.0.1 bitpattern 44'
+            cmd = '20 send-probe ip-4 127.0.0.1 bit-pattern 44'
 
             self.write_command(cmd)
 
@@ -69,7 +69,7 @@ class TestIPv6Parameters(mtrpacket.MtrPacketTest):
         'Test a variety of packet parameters'
 
         with mtrpacket.PacketListen('-6') as listen:
-            param = 'size 256 bitpattern 51 tos 77'
+            param = 'size 256 bit-pattern 51 tos 77'
             cmd = '20 send-probe ip-6 ::1 ' + param
 
             self.write_command(cmd)
index 20ca8926d88706723a5025113d35cc59f75c0d12..871db417b34e8414264f29e1295f45b9e9551dda 100755 (executable)
@@ -326,7 +326,7 @@ class TestProbeUDP(mtrpacket.MtrPacketTest):
         reply = self.parse_reply()
         self.assertEqual('reply', reply.command_name)
 
-        cmd = '82 send-probe protocol udp localport 1991 ' + address
+        cmd = '82 send-probe protocol udp local-port 1991 ' + address
         self.write_command(cmd)
         reply = self.parse_reply()
         self.assertEqual('reply', reply.command_name)