From 8c8d199a09c01cbb0204d5c88ead76b2beb889b0 Mon Sep 17 00:00:00 2001 From: Brian Candler Date: Fri, 29 Apr 2011 17:04:02 +0100 Subject: [PATCH] Unpack metrics --- lib/netlink/constants.rb | 17 ++++++++++++ lib/netlink/message.rb | 59 +++++++++++++++++++++++++--------------- 2 files changed, 54 insertions(+), 22 deletions(-) diff --git a/lib/netlink/constants.rb b/lib/netlink/constants.rb index 0391c58..f2ac379 100644 --- a/lib/netlink/constants.rb +++ b/lib/netlink/constants.rb @@ -183,6 +183,23 @@ module Netlink RTA_CACHEINFO = 12 RTA_TABLE = 15 + # Keys for Route#metrics + RTAX_UNSPEC = 0 + RTAX_LOCK = 1 + RTAX_MTU = 2 + RTAX_WINDOW = 3 + RTAX_RTT = 4 + RTAX_RTTVAR = 5 + RTAX_SSTHRESH = 6 + RTAX_CWND = 7 + RTAX_ADVMSS = 8 + RTAX_REORDERING = 9 + RTAX_HOPLIMIT = 10 + RTAX_INITCWND = 11 + RTAX_FEATURES = 12 + RTAX_RTO_MIN = 13 + RTAX_INITRWND = 14 + # from linux/if_link.h IFLA_UNSPEC = 0 IFLA_ADDRESS = 1 diff --git a/lib/netlink/message.rb b/lib/netlink/message.rb index 2f86c0a..40dcea1 100644 --- a/lib/netlink/message.rb +++ b/lib/netlink/message.rb @@ -23,6 +23,9 @@ module Netlink # Map of numeric message type code => message class CODE_TO_MESSAGE = {} + METRIC_PACK = "SSL".freeze #:nodoc: + METRIC_SIZE = [0,0,0].pack(METRIC_PACK).bytesize #:nodoc: + # Defines each of the possible field types TYPE_INFO = { :uchar => { :pattern => "C" }, @@ -55,6 +58,16 @@ module Netlink :pack => lambda { |val| val.to_a.pack("L*") }, :unpack => lambda { |str| IFACacheInfo.new(*(str.unpack("L*"))) }, }, + :metrics => { + :pack => lambda { |pairs| + pairs.map { |code,val| [METRIC_SIZE,code,val].pack(METRIC_PACK) }.join + }, + :unpack => lambda { |str| + res = {} # in kernel the dst.metrics structure is array of u32 + RtattrMessage.unpack_rtattr(str) { |code,val| res[code] = val.unpack("L").first } + res + }, + }, :l3addr => { :pack => lambda { |val| val.hton }, :unpack => lambda { |val| IPAddr.new_ntoh(val) }, @@ -204,34 +217,36 @@ module Netlink def self.parse(data) res = super - ptr = attr_offset + attrs = res.to_hash + unpack_rtattr(data, attr_offset) do |code, val| + name, info = self::RTATTRS[code] + if name + if !info + # skip + elsif unpack = info[:unpack] + val = unpack[val] + elsif pattern = info[:pattern] + val = val.unpack(pattern).first + end + warn "Duplicate attribute #{name} (#{code}): #{attrs[name].inspect} -> #{val.inspect}" if attrs[name] + attrs[name] = val + else + warn "Unknown attribute #{code}, value #{val.inspect}" + attrs[code] = val + end + end + res + end + + def self.unpack_rtattr(data, ptr=0) #:nodoc: while ptr < data.bytesize raise "Truncated rtattr header!" if ptr + RTATTR_SIZE > data.bytesize len, code = data[ptr, RTATTR_SIZE].unpack(RTATTR_PACK) raise "Truncated rtattr body!" if ptr + len > data.bytesize raise "Invalid rtattr len!" if len < RTATTR_SIZE - res._add_attr(code, data[ptr+RTATTR_SIZE, len-RTATTR_SIZE]) + yield code, data[ptr+RTATTR_SIZE, len-RTATTR_SIZE] ptr = Message.align(ptr + len) end - res - end - - def _add_attr(code, val) # :nodoc: - name, info = self.class::RTATTRS[code] - if name - if !info - # skip - elsif unpack = info[:unpack] - val = unpack[val] - elsif pattern = info[:pattern] - val = val.unpack(pattern).first - end - warn "Duplicate attribute #{name} (#{code}): #{@attrs[name].inspect} -> #{val.inspect}" if @attrs[name] - @attrs[name] = val - else - warn "Unknown attribute #{code}, value #{val.inspect}" - @attrs[code] = val - end end end @@ -303,7 +318,7 @@ module Netlink rtattr :gateway, RTA_GATEWAY, :l3addr rtattr :priority, RTA_PRIORITY, :uint32 rtattr :prefsrc, RTA_PREFSRC, :l3addr - rtattr :metrics, RTA_METRICS + rtattr :metrics, RTA_METRICS, :metrics rtattr :multipath, RTA_MULTIPATH rtattr :flow, RTA_FLOW rtattr :cacheinfo, RTA_CACHEINFO, :rta_cacheinfo