###
### This file is part of Trivial IP Encryption (TrIPE).
###
-### TrIPE is free software; you can redistribute it and/or modify
-### it under the terms of the GNU General Public License as published by
-### the Free Software Foundation; either version 2 of the License, or
-### (at your option) any later version.
+### TrIPE is free software: you can redistribute it and/or modify it under
+### the terms of the GNU General Public License as published by the Free
+### Software Foundation; either version 3 of the License, or (at your
+### option) any later version.
###
-### TrIPE is distributed in the hope that it will be useful,
-### but WITHOUT ANY WARRANTY; without even the implied warranty of
-### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-### GNU General Public License for more details.
+### TrIPE is distributed in the hope that it will be useful, but WITHOUT
+### ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+### FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+### for more details.
###
### You should have received a copy of the GNU General Public License
-### along with TrIPE; if not, write to the Free Software Foundation,
-### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+### along with TrIPE. If not, see <https://www.gnu.org/licenses/>.
VERSION = '@VERSION@'
import dbus as D
for i in ['mainloop', 'mainloop.glib']:
__import__('dbus.%s' % i)
-import gobject as G
+try: from gi.repository import GLib as G
+except ImportError: import gobject as G
from struct import pack, unpack
SM = T.svcmgr
## so turn them all off.
cp = RawConfigParser()
cp.read(me._file)
+ if T._debug: print '# reread config'
## Save the test address. Make sure it's vaguely sensible. The default
## is probably good for most cases, in fact, since that address isn't
### This will be a configuration file.
CF = None
+def straddr(a): return a is None and '#<none>' or S.inet_ntoa(pack('>L', a))
+def strmask(m):
+ for i in xrange(33):
+ if m == 0xffffffff ^ ((1 << (32 - i)) - 1): return i
+ return straddr(m)
+
+def cmd_showconfig():
+ T.svcinfo('test-addr=%s' % CF.testaddr)
+def cmd_showgroups():
+ for sec, pats in CF.groups:
+ T.svcinfo(sec)
+def cmd_showgroup(g):
+ for s, p in CF.groups:
+ if s == g:
+ pats = p
+ break
+ else:
+ raise T.TripeJobError, 'unknown-group', g
+ for t, p, a, m in pats:
+ T.svcinfo('peer', t,
+ 'target', p or '(default)',
+ 'net', '%s/%s' % (straddr(a), strmask(m)))
+
###--------------------------------------------------------------------------
### Responding to a network up/down event.
_kick = T.Queue()
def kickpeers():
- lastip = {}
while True:
upness, reason = _kick.get()
+ if T._debug: print '# kickpeers %s: %s' % (upness, reason)
+ select = []
## Make sure the configuration file is up-to-date. Don't worry if we
## can't do anything useful.
upness = False
else:
addr = None
+ if not T._debug: pass
+ elif addr: print '# local address = %s' % straddr(addr)
+ else: print '# offline'
## Now decide what to do.
changes = []
for g, pp in CF.groups:
+ if T._debug: print '# check group %s' % g
## Find out which peer in the group ought to be active.
ip = None
ipq = addr
else:
ipq = localaddr(p)
+ if T._debug:
+ info = 'peer=%s; target=%s; net=%s/%s; local=%s' % (
+ t, p or '(default)', straddr(a), strmask(m), straddr(ipq))
if upness and ip is None and \
ipq is not None and (ipq & m) == a:
+ if T._debug: print '# %s: SELECTED' % info
map[t] = 'up'
+ select.append('%s=%s' % (g, t))
if t == 'down' or t.startswith('down/'):
want = None
else:
ip = ipq
else:
map[t] = 'down'
+ if T._debug: print '# %s: skipped' % info
## Shut down the wrong ones.
found = False
+ if T._debug: print '# peer-map = %r' % map
for p in peers:
what = map.get(p, 'leave')
if what == 'up':
found = True
+ if T._debug: print '# peer %s: already up' % p
elif what == 'down':
def _(p = p):
try:
pass
else:
raise
+ if T._debug: print '# peer %s: bring down' % p
changes.append(_)
## Start the right one if necessary.
- if want is not None and (not found or ip != lastip.get(g, None)):
+ if want is not None and not found:
def _(want = want):
try:
SM.svcsubmit('connect', 'active', want)
except T.TripeError, exc:
SM.warn('conntrack', 'connect-failed', want, *exc.args)
+ if T._debug: print '# peer %s: bring up' % want
changes.append(_)
- lastip[g] = ip
## Commit the changes.
if changes:
- SM.notify('conntrack', upness and 'up' or 'down', *reason)
+ SM.notify('conntrack', upness and 'up' or 'down', *select + reason)
for c in changes: c()
def netupdown(upness, reason):
NM_IFACE = NM_NAME
NMCA_IFACE = NM_NAME + '.Connection.Active'
-NM_STATE_CONNECTED = 3
+NM_STATE_CONNECTED = 3 #obsolete
+NM_STATE_CONNECTED_LOCAL = 50
+NM_STATE_CONNECTED_SITE = 60
+NM_STATE_CONNECTED_GLOBAL = 70
+NM_CONNSTATES = set([NM_STATE_CONNECTED,
+ NM_STATE_CONNECTED_LOCAL,
+ NM_STATE_CONNECTED_SITE,
+ NM_STATE_CONNECTED_GLOBAL])
class NetworkManagerMonitor (object):
"""
try:
nm = bus.get_object(NM_NAME, NM_PATH)
state = nm.Get(NM_IFACE, 'State')
- if state == NM_STATE_CONNECTED:
+ if state in NM_CONNSTATES:
netupdown(True, ['nm', 'initially-connected'])
else:
netupdown(False, ['nm', 'initially-disconnected'])
except D.DBusException:
pass
- bus.add_signal_receiver(me._nm_state, 'StateChanged', NM_IFACE,
- NM_NAME, NM_PATH)
- bus.add_signal_receiver(me._nm_connchange,
- 'PropertiesChanged', NMCA_IFACE,
- NM_NAME, None)
+ bus.add_signal_receiver(me._nm_state, 'StateChanged',
+ NM_IFACE, NM_NAME, NM_PATH)
+ bus.add_signal_receiver(me._nm_connchange, 'PropertiesChanged',
+ NMCA_IFACE, NM_NAME, None)
def _nm_state(me, state):
- if state == NM_STATE_CONNECTED:
+ if state in NM_CONNSTATES:
netupdown(True, ['nm', 'connected'])
else:
netupdown(False, ['nm', 'disconnected'])
if props.get('Default', False):
netupdown(True, ['nm', 'default-connection-change'])
+##--------------------------------------------------------------------------
+### Connman monitor.
+
+CM_NAME = 'net.connman'
+CM_PATH = '/'
+CM_IFACE = 'net.connman.Manager'
+
+class ConnManMonitor (object):
+ """
+ Watch ConnMan signls for changes in network state.
+ """
+
+ ## Strategy. Everything seems to be usefully encoded in the `State'
+ ## property. If it's `offline', `idle' or `ready' then we don't expect a
+ ## network connection. During handover from one network to another, the
+ ## property passes through `ready' to `online'.
+
+ def attach(me, bus):
+ try:
+ cm = bus.get_object(CM_NAME, CM_PATH)
+ props = cm.GetProperties(dbus_interface = CM_IFACE)
+ state = props['State']
+ netupdown(state == 'online', ['connman', 'initially-%s' % state])
+ except D.DBusException:
+ pass
+ bus.add_signal_receiver(me._cm_state, 'PropertyChanged',
+ CM_IFACE, CM_NAME, CM_PATH)
+
+ def _cm_state(me, prop, value):
+ if prop != 'State': return
+ netupdown(value == 'online', ['connman', value])
+
###--------------------------------------------------------------------------
### Maemo monitor.
T.Coroutine(kickpeers, name = 'kickpeers').switch()
DBM = DBusMonitor()
DBM.addmon(NetworkManagerMonitor())
+ DBM.addmon(ConnManMonitor())
DBM.addmon(MaemoICdMonitor())
G.timeout_add_seconds(30, lambda: (netupdown(True, ['interval-timer'])
or True))
return lambda *args: T.defer(netupdown, upness, ['manual'] + list(args))
service_info = [('conntrack', VERSION, {
'up': (0, None, '', cmd_updown(True)),
- 'down': (0, None, '', cmd_updown(False))
+ 'down': (0, None, '', cmd_updown(False)),
+ 'show-config': (0, 0, '', cmd_showconfig),
+ 'show-groups': (0, 0, '', cmd_showgroups),
+ 'show-group': (1, 1, 'GROUP', cmd_showgroup)
})]
if __name__ == '__main__':