This is a small patch for 'ddclient' that allows you to set a parameter to updates the interface address to the public address you're using.

This probably doesn't have a lot of general use-cases but I have an Eircom Netopia box that allows me to configure an 'IP passthrough' option. Of course, it has an option to bind it a known host (presumably via the system's MAC address) but I want to use it with my DragonFly vkernels and the bridge addresses are dynamically assigned. That's not a problem either and in truth I'll get the TSIG updates on 'dyndns.org' going soon but in the meantime this get's the job done.

the code

I would have to be honest and say that this is prett nasty (hence the reason it's under the 'hacks' section) but it does match the 'ddclient' script which itself, appears to be a pretty nasty hack, in that it has no structure and does lot's of clever things without any explanation (i.e. your typical perl script). Either way we've tried to maintain the coding style and semantics of the original as per our principles for mods.

After adding our if-set definition to the table of options and updating the description of if-skip for our new masking option the only thing left to look at is the get_ip function which is called to return the public IP address from whatever source is suitable.

Although there's no direct assurance that we should use the address found in get_ip (particularly as it appears to be called repeatedly by the mutually exlusive use options) we simply check to ensure that we're not setting the address incorrectly.

All this is all, quite simply, tagged on at the end of the of the get_ip function as below:

    ######################################################################
    ## get_ip
    ######################################################################
    sub get_ip {
    [ ... ]
    @@ -1691,8 +1693,29 @@
          if (($use ne 'ip') && (define($ip,'') eq '0.0.0.0')) {
        $ip = undef;
          }
          debug("get_ip: using %s, %s reports %s", $use, $arg, define($ip, "<undefined>"));
    +
    +    # check whether to set the interface address
    +    # and, if so, do so
    +    # NB: even though this is logically exclusive
    +    # to 'use=if' we don't bother testing we just
    +    # proceed; i don't think this'll cause bother
    +    $arg = opt('if-set');
    +    if ($arg) {
    +        $skip = opt('if-skip')  || '';
    +        $reply = `ifconfig $arg 2>/dev/null`;
    +        # search through 'inet' addresses ...
    +        while ($reply =~ /inet\D+((\d{1,3}\.){3}\d{1,3})\b/sg) {
    +            $use = undef if ($1 eq $ip);
    +                        # high-jack 'use' as flag for 'no change' on local ip
    +            # remove current addresses
    +            `ifconfig $arg $1 -alias` unless $1 =~ /(^$ip)|(^$skip)/;
    +        }
    +   # check flag (i.e. 'use'll be undef'd if local's unchanged)
    +        if (defined($use)) {
    +            `ifconfig $arg inet $ip netmask 255.255.255.255 alias`;
    +        }
    +    }
          return $ip;
    [ ... ]
     }

The only interesting bit to consider is whether I've messed up the address matching component (which was insanely/lazily expressed)

+        while ($reply =~ /inet\D+((\d{1,3}\.){3}\d{1,3})\b/sg) {

and whether the actual address command is generic enough to be useful (it's not but it will work on many *NIX like systems).

Yes, this $ip check could have been merged with the 'undef' condition but, really, what would it win us?

+            `ifconfig $arg $1 -alias` unless $1 =~ /(^$ip)|(^$skip)/;

Which, as you'll see, simply removes the alias unless it's either the current, public IP or a local address (i.e. defined in 'if-skip').

The final ifconfig sets up the address.

+            `ifconfig $arg inet $ip netmask 255.255.255.255 alias`;

and should probably have been this;

+            `ifconfig $arg inet alias $ip netmask 255.255.255.255`;

But again, I'm going to leave that discussion to you.

In hindsight it would probably have been better to define a new local protocol and process it during update_nics (a rather confusingly named function that has nothing to do with what I would consider a NIC to be) rather than the current method but since both the code and the method are nasty we'll leave that as an exercise for Alanna.
;-) (Just kidding, Missus)

how-to

Use at your own risk!

Do NOT use with 'web' updates as I would dread to consider the implications of an attack on that.

Use the TSIG updates instead http://www.dyn.com/support/clients/dynamic-dns-updates-via-tsig/.

Assuming you choose to ignore the above warnings I'd recommend downloading the complete file (rather than trying to patch). After that, simply add the 'if-set' option to your configuration file and set 'if-skip' to match the local network address space (e.g. 192.168. or 192.168.1).

the patch

--- ddclient.pl 2011-11-07 21:35:16.904937000 +0000
+++ ddclient,LOCAL0.pl  2011-11-07 22:06:52.974070000 +0000
@@ -1,6 +1,6 @@
 #!/usr/pkg/bin/perl -w
 ######################################################################
-# $Header: /cvsroot/ddclient/ddclient/ddclient,v 1.11 2005/03/20 10:58:34 wimpunk Exp $
+# $Header: /cvsroot/ddclient/ddclient/ddclient,v 1.11, LOCAL0 2005/03/20 10:58:34 wimpunk Exp $
 #
 # DDCLIENT - a Perl client for updating DynDNS information
 #
@@ -226,6 +226,7 @@
    'ip'                  => setv(T_IP,    0, 0, 1, undef,                undef),
    'if'                  => setv(T_IF,    0, 0, 1, 'ppp0',               undef),
    'if-skip'             => setv(T_STRING,1, 0, 1, '',                   undef),
+   'if-set'              => setv(T_STRING,1, 0, 1, '',                   undef),
    'web'                 => setv(T_STRING,0, 0, 1, 'dyndns',             undef),
    'web-skip'            => setv(T_STRING,1, 0, 1, '',                   undef),
    'fw'                  => setv(T_ANY,   0, 0, 1, '',               undef),
@@ -388,7 +389,8 @@
      [ "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 ifconfig {if}; also used as a mask to avoid unsetting public addresses when using 'if-set' option" ],
+    [ "if-set",      "=s", "-if-set interface     : set the interface address alias to the external address (used for my netopia 'passthrough' configuration')" ],
      "",
      [ "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" ],
@@ -1691,8 +1693,29 @@
      if (($use ne 'ip') && (define($ip,'') eq '0.0.0.0')) {
    $ip = undef;
      }
-
      debug("get_ip: using %s, %s reports %s", $use, $arg, define($ip, "<undefined>"));
+
+    # check whether to set the interface address
+    # and, if so, do so
+    # NB: even though this is logically exclusive
+    # to 'use=if' we don't bother testing we just
+    # proceed; i don't think this'll cause bother
+    $arg = opt('if-set');
+    if ($arg) {
+        $skip = opt('if-skip')  || '';
+        $reply = `ifconfig $arg 2>/dev/null`;
+        # search through 'inet' addresses ...
+        while ($reply =~ /inet\D+((\d{1,3}\.){3}\d{1,3})\b/sg) {
+            $use = undef if ($1 eq $ip);
+                        # high-jack 'use' as flag for 'no change' on local ip
+            # remove current addresses
+            `ifconfig $arg $1 -alias` unless $1 =~ /(^$ip)|(^$skip)/;
+        }
+   # check flag (i.e. 'use'll be undef'd if local's unchanged)
+        if (defined($use)) {
+            `ifconfig $arg inet $ip netmask 255.255.255.255 alias`;
+        }
+    }
      return $ip;
 }

complete code listing

ddclient.pl