Ravn::Silvus::

Configurator class

Calculate network parameters and settings for an arbitrary batch of Silvus Radios.

Attributes

configs R

Calculated networks for each radio.

encryption_key R

The encryption key used for this group of radios.

network_name R

The mesh network name for this group of radios.

Public Class Methods

new()

Instantiate a new Configurator.

# File lib/ravn/silvus/configurator.rb, line 94
def initialize
        @encryption_key = self.class.encryption_key || self.make_encryption_key
        @network_name   = self.class.network_name   || self.make_network_name
        @configs = {}

        self.generate_networks
end

Public Instance Methods

deferred_update( radio )

Use a single radio in the configuration to queue a change across the existing mesh network. This uses a deferred API, useful to alter crypto keys for radios that are present, but not connected via ethernet.

NOTE: Only mesh/crypto settings are updated – you’ll likely need to still perform network configurations per-radio.

# File lib/ravn/silvus/configurator.rb, line 190
def deferred_update( radio )
        radio.defer do
                self.update_mesh_settings( radio )
        end
end
generate_networks()

For each radio in the set, calculate valid network settings.

# File lib/ravn/silvus/configurator.rb, line 114
def generate_networks
        self.class.radios.sort_by!{|r| self.nodeid(r.ip) }.each do |radio|
                gateway, dhcp_start, dhcp_end = self.calculate_next_network
                self.configs[ radio ] = {
                        gateway:    gateway,
                        dhcp_start: dhcp_start,
                        dhcp_end:   dhcp_end
                }
        end

ensure
        @last_network = nil
end
max_radios()

Calculate the maximum number of radios that will fit within the configured subnet range.

# File lib/ravn/silvus/configurator.rb, line 200
def max_radios
        return @max_radios if @max_radios

        radio_clients = self.class.virtual_network.to_range.count
        virtual_network = IPAddr.new "%s/%s" % [
                self.class.virtual_network.to_s,
                self.class.mesh_netmask
        ]
        mesh_clients = virtual_network.to_range.count

        @max_radios = mesh_clients / radio_clients

        return @max_radios
end
update( *specified_radios )

Write the changes to each radio. Returns a hash:

{ success: [ (radios) ], failure: { radio1 => error, radio2 => error } }

# File lib/ravn/silvus/configurator.rb, line 139
def update( *specified_radios )
        result  = { success: [], failure: {} }

        specified_radios.flatten!
        radios = self.configs.filter do |radio|
                specified_radios.empty? || specified_radios.include?( radio.label )
        end

        radios.each do |radio, network|
                self.log.info "Writing changes to %p" % [ radio.label ]
                begin
                        radio.batch do
                                radio.virtual_network( true )
                                radio.virtual_ip_address( network[:gateway] )
                                radio.virtual_ip_gateway( network[:gateway] )
                                radio.virtual_ip_netmask( self.class.mesh_netmask )

                                radio.dhcp_settings({
                                        dhcp:        "on",
                                        dhcp_start:  network[ :dhcp_start ],
                                        dhcp_end:    network[ :dhcp_end ],
                                        dhcp_router: network[ :gateway ],
                                        dhcp_subnet: self.class.mesh_netmask,
                                        lease_time:  1800
                                })
                                radio.dhcp_on_mesh( false )
                        end

                        radio.batch do
                                self.update_mesh_settings( radio )
                        end

                        result[ :success ] << radio

                rescue Errno::EHOSTUNREACH, Net::OpenTimeout, Net::ReadTimeout => err
                        self.log.error "Unable to talk to radio %p, skipping" % [ radio.label ]
                        result[ :failure ][ radio ] = err
                end
        end

        return result
end

Protected Instance Methods

calculate_next_network()

Get all the network specifics necessary for configuration.

# File lib/ravn/silvus/configurator.rb, line 266
def calculate_next_network
        virtual_network = @next_network || self.class.virtual_network

        netrange    = virtual_network.to_range.lazy
        netrangerev = netrange.reverse_each

        _, gateway  = netrange.next, netrange.next
        broadcast   = netrangerev.next

        dhcp_start = netrange.next
        dhcp_end   = netrangerev.next

        @next_network = IPAddr.new( "%s/%s" % [
                virtual_network.to_range.end.succ, virtual_network.netmask
        ] )

        return [ gateway, dhcp_start, dhcp_end ].map( &:to_s )
end
make_encryption_key()

Generate a reproducable mesh encryption key for the current radio set. Truncate at 32 characters – the web interface claims there’s a limit.

# File lib/ravn/silvus/configurator.rb, line 251
def make_encryption_key
        return Digest::SHA256.hexdigest( 'ravn-' + self.radio_hash )[0..31]
end
make_network_name()

Generate a reproducable mesh network name for the current radio set.

# File lib/ravn/silvus/configurator.rb, line 259
def make_network_name
        return 'ravn-' + self.radio_hash[ 0..5 ]
end
nodeid( ip )

Generate a Silvus nodeid for the given ip address.

# File lib/ravn/silvus/configurator.rb, line 222
def nodeid( ip )
        oct3, oct4 = ip.to_s.split( '.' )[2, 3]
        id = ( oct3.to_i * 256 ) + oct4.to_i
        return id <= 65535 ? id + 4 * 65536 : id
end
nodeids_sum()

Return the sum of all nodes identifiers in this radio set. Note, this does not use the radio API.

# File lib/ravn/silvus/configurator.rb, line 232
def nodeids_sum
        ips = self.class.radios.map( &:ip )
        return ips.map{|ip| self.nodeid(ip) }.sum
end
radio_hash()

Return a sha256 for the current radio set.

# File lib/ravn/silvus/configurator.rb, line 240
def radio_hash
        return @radio_hash if @radio_hash
        @radio_hash = Digest::SHA256.hexdigest( self.nodeids_sum.to_s )
        return @radio_hash
end
update_mesh_settings( radio )

Update all settings specific to the mesh network.

# File lib/ravn/silvus/configurator.rb, line 288
def update_mesh_settings( radio )
        radio.freq_bandwidth( self.class.frequency, self.class.bandwidth )
        radio.distance( self.class.distance )
        radio.routing_protocol( self.class.routing_protocol )
        radio.mesh_network( self.network_name )

        key  = self.encryption_key
        hmac = Digest::SHA256.hexdigest( key )
        wrap = Digest::SHA256.hexdigest( hmac )

        case self.class.encryption_algorithm

        when :des
                radio.encryption_algorithm( :des_ecb, 64 )
                radio.encryption_hmac_key( hmac )
                radio.encryption_key( self.encryption_key )
                radio.encryption_wrap_key( wrap )

        when :aes
                uni   = Digest::SHA256.hexdigest( wrap )
                bcast = Digest::SHA256.hexdigest( uni )

                radio.encryption_algorithm( :aes_gcm )
                radio.encryption_key( key )
                radio.encryption_hmac_key( hmac )
                radio.encryption_wrap_key( wrap )
                radio.encryption_rfauth_key( uni )
                radio.encryption_rfbcast_key( bcast )
        else
                raise "Unknown encryption type: %p" % [ self.class.encryption_algorithm ]
        end

        radio.encryption( true )
end