[systemd-devel] Enable port forwarding via upnp

Kai Krakow hurikhan77 at gmail.com
Wed Feb 25 12:14:57 PST 2015


Lennart Poettering <lennart at poettering.net> schrieb:

> On Wed, 25.02.15 08:16, Kai Krakow (hurikhan77 at gmail.com) wrote:
> 
>> Hello!
>> 
>> Is it possible to somehow create a service which enables port forwardings
>> on my router using upnp? Currently, I guess it is not possible (except
>> maybe using ExecPost or ExecPre and the upnpc program). But when my
>> client IP changes via DHCP, it should be reapplied. Also, it needs to be
>> maintained as the programmed port forwardings would timeout and be
>> cleared from the router after a while. So it needs to be hooked up to
>> systemd-networkd somehow (at least that is what I am using).
> 
> Not really. Such a upnp port setup tool could just subscribe to rtnl
> and update the router's configuration dynamically each time the local
> configuration is changed, without having to know anything about
> networkd, NM or anything else.

Okay, didn't know about rtnl. I will look into it. It makes sense to keep 
things separated to allow people to choose between networks, NM or anything 
else.

> That said I think it wouldn't be too far off I figure to add logic for
> this to networkd. I mean, it speaks a variety of client side protocols
> already, and this could just be one more. The major difference though
> is that upnp is a frickin insane pseudo-HTTP XML craziness, while
> DHCP, IPv4LL and so on are much more low-level. That said, I figure
> we'll have some kind of HTTP logic in networkd eventually anyway to
> support wispr and things like that, so maybe doing upnp wouldn't be
> completely off either...

To make sure we speak the same language: I propose to have a upnp client 
only, not a server - my system is a desktop, not a router. I just want to 
offer some services on my public router IP to get access from outside 
without having to resort to static IP assignment and without having to edit 
port numbers in two or more places. As far as I know, there's already a upnp 
client library one could link to and use that to talk to the upnp server on 
the router.

My idea is to add a config option to socket activated services which just 
reuses the Listen directives to publish the ports to upnp on the router. 
Maybe something like

[Socket]
ListenStream=22
PublishStream=yes

which would instruct yet the to be planned upnp client to create a port 
forwarding for 22/tcp on the router. The same could work for ListenDatagram. 
Of course this only makes sense, if sockets are not bound to localhost only.

It may make sense to move "PublishStream" to the service section of a unit 
(as "PublishStreams=yes") so it could publish all sockets from each socket 
unit associated with it - though I'm not sure if this is wanted as it moves 
socket logic into another section. And it probably doesn't make sense either 
because the service is started lazily when triggered by the sockets - which 
suggests the upnp mapping should have run earlier (when the socket unit was 
started).

But it would make sense for non-socket-activated services where the 
PublishStreams directive could just be reused in the following form:

[Service]
PublishStreams=22 # SSH server

or

[Service]
PublishStreams=80,443 # WWW server

[Service]
PublishDatagrams=51900-51999 # example service publishing 51900-99/udp

[Service]
PublishDatagrams=51900-51999:5190
  # publish 51900-99/udp as 5190-99/udp on eth*

[Service]
PublishStreams=8080:80 # publish 8080/tcp on the router as 80 on eth*


So instead of defining port mappings on the router manually, this would be 
done automatically, depending on which services/sockets are started.

> I figure if we can do this with existing deps, the internal XML parser
> and so on, this could work. I.e. a minimal upnp port forwarding
> library รก la sd-dhcp would be acceptable...

It should not take much more than a wrapper around libminiupnpc.so, it's 
designed around having the smallest possible footprint:

http://miniupnp.free.fr/

It comes with a small test client program "upnpc" which provides all the 
stuff needed to implement the above ideas.

>> So I guess this logic should be built into systemd-networkd, maybe
>> offering some dbus interface also. And service and/or socket files could
>> have a flag to enable upnp forwards. This way, systemd-networkd knows
>> about the registered forwards and maintains them, and systemd will
>> trigger registration/unregistration on it whenever services start or stop
>> which have such flags enabled. By using dbus, this could be an interface
>> implemented by other network management daemons, too.
> 
> This is already the second step... It kinda opens another can of worms
> though: we discussed on and off how integration between mDNS/DNS-SD
> and .socket units could look like. This port forwarding thing and the
> mDNS hookup are somewhat related: in both cases we have a concept of
> actively registering with some external code as long as as a socket is
> up.
> 
> Originally, when we discussed that in the mDNS context I thought of
> simply using ExecStartPre= and ExecStopPre= for this, and forking off
> a tool that pushes the mDNS registration into avahi/resolved as long
> as the socket is up. But I ended not liking this idea, since in most
> cases this would mean having one addition process around, that does
> some bits when it starts up, then only hangs around, and then does a
> bit more when it shuts down. Hanging around pointlessly is bad
> though. So the next idea was maybe then PID 1 could simply call
> directly into avahi/resolved via async bus calls, so that we have no
> extra pointless processes hanging around.
> 
> However, that idea I don't like either, because it makes PID 1 client
> of another daemon, and I really don't like that, I'd really prefer it
> the other way round: the higher level daemons should call into the
> lower level daemons, and subscribe to them, but the lower level
> daemons should never call into the higher level daemons...
> 
> In networkd we followed this correct stacking design quite
> nicely. networkd never pushes NTP servers into timesyncd or DNS
> servers into resolved. Instead, timesyncd and resolved subscribe to
> what networkd announces and pull the data out that. Most likely that's
> the logic we should follow here too:
> 
> - For the port forwarding logic we should introduce a new .socket
>   property, that PID 1 doesn't really do much with, except exporting
>   it on the bus
> 
> - networkd susbcribes to .socket units starting and stopping in
>   PID 1, and pulls out that one property of them, and acting on it.
> 
> Similar, we would handle the mDNS case: the .socket units would gain a
> new DNSSDService property, that resolved then keeps track of an
> operates on.
> 
> I hope that makes some sense?

Well, then I suppose it would be better to have a standalone daemon, let's 
call it systemd-upnpclientd.

This service would subscribe to rtnl you mentioned above. This will make it 
independent of systemd-networkd and could support other implementations like 
NM etc.

Next step, I'm not sure however: It also needs to gain knowledge about every 
socket monitored by systemd and every socket mentioned in service files, as 
outlined in my examples. But I'm sure PID1 could simply export such 
information. Or alternatively: You mentioned that one could subscribe to 
units starting and stopping, so we could simply pull out the information 
needed.

I currently see problems with both approaches:

When getting exported info from systemd about sockets to be published on the 
router: How do we get notified about changes introduced by starting or 
stopping units?

When subscribing to starting and stopping of units, how do we pull the info 
from the unit files if systemd-upnpclientd gets started after some units 
wanting to publish sockets are already up?

Putting all together, and after reading through your thoughts, I'd favor a 
solution of a standalone daemon instead of putting possibly unrelated stuff 
into PID1 or networkd - with the exception of maybe exporting additional 
information.

-- 
Replies to list only preferred.



More information about the systemd-devel mailing list