class Uplink
Attributes
default_route[RW]
description[R]
fwmark[R]
gateway[R]
id[R]
interface[R]
ip[R]
previous_gateway[R]
previous_ip[R]
previously_default_route[RW]
previously_up[R]
priority_group[R]
rule_priority_1[R]
rule_priority_2[RW]
table[R]
type[R]
up[R]
weight[R]
Public Class Methods
new(config, id)
click to toggle source
# File lib/fault_tolerant_router/uplink.rb, line 5 def initialize(config, id) @id = id @rule_priority_1 = BASE_PRIORITY + @id @table = BASE_TABLE + @id @fwmark = BASE_FWMARK + @id @interface = config['interface'] unless @interface puts 'Error: uplink interface not specified' exit 1 end @type = case config['type'] when 'static' :static when 'ppp' :ppp else puts "Error: '#{config['type']}' is not a valid uplink type" exit 1 end @description = config['description'] unless @description puts 'Error: uplink description not specified' exit 1 end @weight = config['weight'] @priority_group = config['priority_group'] @default_route = false if @type == :static @ip = config['ip'] unless @ip puts 'Error: uplink IP not specified' exit 1 end @gateway = config['gateway'] unless @gateway puts 'Error: uplink gateway not specified' exit 1 end else detect_ppp_ips! end @previous_ip = @ip @previous_gateway = @gateway #a new uplink is supposed to be up @up = true @previously_up = true end
Public Instance Methods
detect_ppp_ips!()
click to toggle source
# File lib/fault_tolerant_router/uplink.rb, line 55 def detect_ppp_ips! @previous_ip = @ip @previous_gateway = @gateway if DEMO @ip = ['3.0.0.101', '3.0.0.102', nil].sample @gateway = ['3.0.0.1', '3.0.0.2', nil].sample else ifaddr = Socket.getifaddrs.find { |i| i.name == @interface && i.addr && i.addr.ipv4? } if ifaddr @ip = ifaddr.addr.ip_address @gateway = ifaddr.dstaddr.ip_address else @ip = nil @gateway = nil end end puts "Uplink #{@description}: detected ip #{@ip || 'none'}, gateway #{@gateway || 'none'}" if DEBUG end
ping(ip_address)
click to toggle source
# File lib/fault_tolerant_router/uplink.rb, line 74 def ping(ip_address) if DEMO sleep 0.1 rand(3) > 0 else `ping -n -c 1 -W 2 -I #{@ip} #{ip_address}` $?.to_i == 0 end end
route_add_commands()
click to toggle source
# File lib/fault_tolerant_router/uplink.rb, line 156 def route_add_commands #- locally generated packets having as source ip the ethX ip #- returning packets of inbound connections coming from ethX #- non-first packets of outbound connections for which the first packet has been sent to ethX via multipath routing [ "ip route replace table #{@table} default via #{@gateway} src #{@ip}", "ip rule add priority #{@rule_priority_1} from #{@ip} lookup #{@table}", "ip rule add priority #{@rule_priority_2} fwmark #{@fwmark} lookup #{@table}" ] end
test!()
click to toggle source
# File lib/fault_tolerant_router/uplink.rb, line 84 def test! #save current state @previously_up = @up successful_tests = 0 unsuccessful_tests = 0 commands = [] if @type == :ppp detect_ppp_ips! if (@previous_ip != @ip) || (@previous_gateway != @gateway) #only apply routing commands if there are an ip and gateway, else they will be applied on next checks, whenever new ip and gateway will be available if @ip && @gateway commands << "ip rule del priority #{@rule_priority_1}" commands << "ip rule del priority #{@rule_priority_2}" commands += route_add_commands end end end #do not ping if there is no ip or gateway (for example in case of a PPP interface down) if @ip && @gateway #for each test (in random order)... TEST_IPS.shuffle.each_with_index do |test, i| successful_test = false #retry for several times... PING_RETRIES.times do if DEBUG print "Uplink #{@description}: ping #{test}... " STDOUT.flush end if ping(test) successful_test = true puts 'ok' if DEBUG #avoid more pings to the same ip after a successful one break else puts 'error' if DEBUG end end if successful_test successful_tests += 1 else unsuccessful_tests += 1 end #if not currently doing the last test... if i + 1 < TEST_IPS.size if successful_tests >= REQUIRED_SUCCESSFUL_TESTS puts "Uplink #{@description}: avoiding more tests because there are enough positive ones" if DEBUG break elsif TEST_IPS.size - unsuccessful_tests < REQUIRED_SUCCESSFUL_TESTS puts "Uplink #{@description}: avoiding more tests because too many have been failed" if DEBUG break end end end end @up = successful_tests >= REQUIRED_SUCCESSFUL_TESTS if DEBUG state = @previously_up ? 'up' : 'down' state += " --> #{@up ? 'up' : 'down'}" if @up != @previously_up puts "Uplink #{@description}: #{successful_tests} successful tests, #{unsuccessful_tests} unsuccessful tests, state #{state}" end commands end