That perfectly matches with what I've already scripted. I also prefer the openvpn config file much more than stuffing openvpn option into uci, so up to now I've only implemented 3 options: 'config', 'dev' and 'dev_type' (whereas the latter two are optional).
So my network config looks like:
config interface foo
option proto 'openvpn'
option config 'myserverconfig' (mandatory, if the value contains a '/' it is used as config file with full path, if not then it is searched for '/etc/openvpn/${value}.conf')
option dev 'vpn0' (optional: can also be specified in the config file)
option dev_type 'tun' (optional: can also be specified in the config file)
After a bit of cleanup and testing (probably today), I'm going to post my files here for further discussion.
I don't see yet why it is important to know the vpn ifname in advance, everything I've done so far doesn't require that. The only issue I've stumbled upon was the LuCI integration, where a little patch on /usr/lib/lua/luci/model/network.lua helped:
diff -Naur old/network.lua new/network.lua
--- old/network.lua 2012-09-12 07:45:25.495021315 +0200
+++ new/network.lua 2012-09-10 16:02:31.048423055 +0200
@@ -862,8 +862,11 @@
function protocol.get_interface(self)
if self:is_virtual() then
- _tunnel[self:proto() .. "-" .. self.sid] = true
- return interface(self:proto() .. "-" .. self.sid, self)
+ _tunnel[self:ifname()] = true
+ return interface(self:ifname(), self)
elseif self:is_bridge() then
_bridge["br-" .. self.sid] = true
return interface("br-" .. self.sid, self)
Every (virtual) network proto instance I've seen so far returns self:proto() .. "-" .. self.sid when self:ifname() is called, so that patch shouldn't change any existing behaviour. But it allows the openvpn proto instance to respond with the real name of the interface when the tunnel is up and return a default name when it's down.
Here's my /usr/lib/lua/luci/model/network/proto_openvpn.lua file so far:
local netmod = luci.model.network
local proto = netmod:register_protocol("openvpn")
function proto.get_i18n(self)
return luci.i18n.translate("OpenVPN")
end
function proto.ifname(self)
ifname = self:_ubus("l3_device")
if ifname then
return ifname
else
return "vpn-" .. self.sid
end
end
function proto.opkg_package(self)
return "openvpn"
end
function proto.is_installed(self)
return nixio.fs.access("/usr/sbin/openvpn")
end
function proto.is_floating(self)
return true
end
function proto.is_virtual(self)
return true
end
function proto.get_interfaces(self)
return nil
end
function proto.contains_interface(self, ifc)
return (netmod:ifnameof(ifc) == self:ifname())
end
netmod:register_pattern_virtual("^vpn%d")
netmod:register_pattern_virtual("^vpn-%w")
Your point with suppressing harmful options is very good, I didn't think of that. My current implementation calls openvpn with the --route-noexec parameter because telling the routes to netifd in the up script additionally sets the routes which resulted in each route existing twice. So I'm telling openvpn to not create any routes and totally leave that to the scripts. This might even simplify detecting and filtering hostile pushed settings. I'm not even sure what the effect of a pushed 'redirect-gateway' would be when openvpn is called with --route-noexec, will test that.