From e3a6cbf1b64c1c74d98e19624bf5ea23fe384847 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 11 Jun 2020 17:09:46 -0400 Subject: [PATCH] Prefer `ip` command from iproute2 over `ifconfig` On Linux systems, `ifconfig` is long deprecated in favor of the `ip` command from iproute2. Some systems don't have iproute2 (BSDs in particular), so ddclient will still attempt `ifconfig` if `ip` is missing. Also: Don't hide STDERR because error messages are important for troubleshooting problems. To avoid STDERR noise on systems without the `ip` command, the command's existence is checked before it is run. Notes: * The fetched addresses could be limited to IPv4 or IPv6 depending on `opt('ipv6')`, and non-global addresses could be filtered out, but any filtering risks breaking a nontrivial number of existing configurations. * This change runs the risk of breaking existing configs that set `if-skip`. Due to the deprecation of `ifconfig`, and the belief that only a negligible number of users set `if-skip`, the benefits of this change are believed to outweigh the config migration burden imposed on users. Fixes #93. --- ChangeLog.md | 3 +++ ddclient | 17 ++++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 6f494ab..ac709a2 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -12,6 +12,9 @@ repository history](https://github.com/ddclient/ddclient/commits/master). ### Compatibility changes * Perl v5.10.1 or later is now required. + * When `use=if`, iproute2's `ip` command is now attempted before falling back + to `ifconfig` (it used to be the other way around). If you set `if-skip`, + please check that your configuration still works as expected. * Removed the `concont` protocol. If you still use this protocol, please [file a bug report](https://github.com/ddclient/ddclient/issues) and we will restore it. diff --git a/ddclient b/ddclient index b6b6aa9..414618e 100755 --- a/ddclient +++ b/ddclient @@ -754,7 +754,7 @@ my @opt = ( [ "ip", "=s", "-ip address : set the IP address to 'address'" ], "", [ "if", "=s", "-if interface : obtain IP address from 'interface'" ], - [ "if-skip", "=s", "-if-skip pattern : skip any IP addresses before 'pattern' in the output of ifconfig {if}" ], + [ "if-skip", "=s", "-if-skip pattern : skip any IP addresses before 'pattern' in the output of 'ip address show dev {if}' (or 'ifconfig {if}')" ], "", [ "web", "=s", "-web provider|url : obtain IP address from provider's IP checking page" ], [ "web-skip", "=s", "-web-skip pattern : skip any IP addresses before 'pattern' on the web provider|url" ], @@ -1477,8 +1477,15 @@ sub test_possible_ip { { local $opt{'use'} = 'if'; - foreach my $if (grep {/^[a-zA-Z]/} `ifconfig -a`) { - $if =~ s/:?\s.*//is; + # Note: The `ip` command adds a `@eth0` suffix to the names of VLAN + # interfaces. That `@eth0` suffix is NOT part of the interface name. + my @ifs = map({ /^[^\s:]*:\s*([^\s:@]+)/ ? $1 : () } + `command -v ip >/dev/null && ip -o link show`); + @ifs = map({ /^([a-zA-Z].*?)(?::?\s.*)?$/ ? $1 : () } + `command -v ifconfig >/dev/null && ifconfig -a`) if $? || !@ifs; + @ifs = () if $?; + warning("failed to get list of interfaces") if !@ifs; + foreach my $if (@ifs) { local $opt{'if'} = $if; printf "use=if, if=%s address is %s\n", opt('if'), define(get_ip('if'), 'NOT FOUND'); } @@ -2201,8 +2208,8 @@ sub get_ip { } elsif ($use eq 'if') { $skip = opt('if-skip', $h) || ''; - $reply = `ifconfig $arg 2> /dev/null`; - $reply = `ip addr list dev $arg 2> /dev/null` if $?; + $reply = `command -v ip >/dev/null && ip address show dev $arg`; + $reply = `command -v ifconfig >/dev/null && ifconfig $arg` if $?; $reply = '' if $?; } elsif ($use eq 'cmd') {