query whether IP address is bound?

Simon McVittie smcv at collabora.com
Fri Nov 23 16:54:31 UTC 2018


On Fri, 23 Nov 2018 at 08:59:35 -0500, Felipe Gasper wrote:
> I’m looking for a way to query whether the (Linux) kernel has a
> specific IP address bound.

Sorry, this is not really an IPC question. D-Bus is a message passing/IPC
mechanism, not a framework for system information.

Services that *use* D-Bus might include frameworks for system information,
but that isn't in D-Bus' scope, in the same way that specific web servers
aren't in the Linux kernel's scope.

This also sounds suspiciously like something that is unlikely to be the
real problem you are trying to solve. If you are asking this question
because it affects whether you can do something else, then you have a
classic time-of-check/time-of-use race condition:

    you: do we have IP address 192.168.0.1?
    kernel: yes
    (your process is pre-empted by some other task)
    (the kernel loses 192.168.0.1)
    (your process resumes)
    you: please do something with 192.168.0.1
    kernel: er, I'm not 192.168.0.1

Using D-Bus actually often makes these ToC/ToU race conditions worse/more
likely, because it's slower than a direct function call.

To avoid these race conditions, a popular pattern is "leap before you
look" or "easier to ask forgiveness than permission": having APIs that
atomically check a precondition and take an action if the precondition
was successful, then return success if they took the action, or a
documented error indicating why they didn't.

For example, rmdir("foo") can be described as: atomically check whether
foo is a directory that we can remove, and if it is, remove it.
If we removed it, return 0. If we didn't, return ENOENT if that was
because it didn't exist, or ENOTDIR if that was because it wasn't a
directory, or ... and so on.

You are meant to use rmdir() with a pattern like this pseudocode:

    # good
    try:
        rmdir("foo")
    except:
        if ENOENT (it wasn't there):
            maybe that's OK for your use case, ignore
        else:
            what's going on? report an error

in preference to using it like this:

    # bad
    if "foo" exists:
        rmdir("foo")

On D-Bus, you see this pattern in things like the standard RequestName()
method call, which you could describe as: atomically check whether
the caller can become the owner of the name, and if it can, do so;
return success if it did, or an error indicating why it couldn't.
Again, instead of calling NameHasOwner() and then RequestName(), which
has the time-of-check/time-of-use problem, you are meant to just call
RequestName(), and if the name already had an owner, recognise and cope
with the (distinctive, documented) error code.

> Does the usual suite of D-Bus interfaces on a Linux machine have any
> function that checks this?

Imagine it did. How would that work? You'd need to send a method call
message to some D-Bus service, and wait (possibly asynchronously) for
a reply.

That service would need to either query the kernel on-demand, or have a
data structure in memory that cached the IP addresses that it thought were
bound, hopefully with a way to update/invalidate that cache at the right
times. But neither of those actually needs IPC - if the service can do
that, then you could equally well implement either of those strategies in
your own application (either by writing it yourself or using a library),
and not need D-Bus or a remote service.

In particular, if the only available kernel interface is to iterate over
a (possibly long) list of IP addresses, *someone* is going to be doing
that - either your application, or the service your application
communicates with. Moving that work into a service and using D-Bus to
communicate with it is pure overhead.

freedesktop.org historians will recognise this as the difference between
HAL (the Hardware Abstraction Layer, one of the first widely-used D-Bus
services) and its more modern replacements (libudev, upower, udisks and
so on).

In HAL, everything happened at the other end of a D-Bus connection,
whether it actually needed to or not. For simple tasks like device
enumeration, this was woefully inefficient.

In the frameworks that have replaced HAL, simple unprivileged actions like
device enumeration happen in a shared library used by the application
(mostly libudev), and D-Bus is only used in the cases where it provides
a real benefit, such as:

- context-dependent privilege escalation where the kernel doesn't have
  enough policy information to determine whether it's OK for unprivileged
  processes to take an action, like udisks letting unprivileged users
  ask it to mount removable disks for them;

- mediating possibly conflicting requests for changes to a single resource,
  like systemd-logind's mechanism to let multiple applications prevent or
  delay system suspend

For completeness, I admit that this reasoning might break down if you
have millions of IP addresses. If you have that many IP addresses,
adding a kernel call for "do you have this IP address?" would make a
lot of sense - the kernel has to have the IP addresses in some data
structure *anyway*, so you might as well ask the kernel rather than
going via D-Bus.

For more general large data collections that don't already have to live
in the kernel, there'd be an advantage to maintaining the data structure
in one service's memory space, rather than having a copy each in several
processes' memory. However, if you have that much data to deal with, you
might well be better off maintaining a file-based data structure on disk
(or in a tmpfs), perhaps with direct reads bypassing D-Bus, and writes
(changes to a shared resource) mediated by a D-Bus service - that's
approximately how dconf, Tracker and evolution-data-server work.

    smcv


More information about the dbus mailing list