Initial commit, work in progress
This commit is contained in:
151
lib/netlink/message.rb
Normal file
151
lib/netlink/message.rb
Normal file
@@ -0,0 +1,151 @@
|
||||
require 'netlink/constants'
|
||||
|
||||
module Netlink
|
||||
# Base class for Netlink messages
|
||||
class Message
|
||||
# Map of numeric message type code => message class
|
||||
CODE_TO_MESSAGE = {}
|
||||
|
||||
# You can initialize a message from a Hash or from another
|
||||
# instance of itself.
|
||||
#
|
||||
# class Foo < Message
|
||||
# field :foo, "C", 0xff
|
||||
# field :bar, "L", 0
|
||||
# end
|
||||
# msg = Foo.new(:bar => 123) # or ("bar" => 123)
|
||||
# msg2 = Foo.new(msg)
|
||||
# msg3 = Foo.new(:qux => 999) # error, no method qux=
|
||||
def initialize(h={})
|
||||
if h.instance_of?(self.class)
|
||||
@attrs = h.to_hash.dup
|
||||
else
|
||||
@attrs = self.class::DEFAULTS.dup
|
||||
h.each { |k,v| self[k] = v }
|
||||
end
|
||||
end
|
||||
|
||||
def to_hash
|
||||
@attrs
|
||||
end
|
||||
|
||||
def each(&blk)
|
||||
@attrs.each(&blk)
|
||||
end
|
||||
|
||||
# Set a field by name. Can use either symbol or string as key.
|
||||
def []=(k,v)
|
||||
send "#{k}=", v
|
||||
end
|
||||
|
||||
# Retrieve a field by name. Must use symbol as key.
|
||||
def [](k)
|
||||
@attrs[k]
|
||||
end
|
||||
|
||||
def self.inherited(subclass) #:nodoc:
|
||||
subclass.const_set(:FIELDS, [])
|
||||
subclass.const_set(:FORMAT, "")
|
||||
subclass.const_set(:DEFAULTS, {})
|
||||
end
|
||||
|
||||
# Define which message type code(s) use this structure
|
||||
def self.code(*codes)
|
||||
codes.each { |code| CODE_TO_MESSAGE[code] = self }
|
||||
end
|
||||
|
||||
# Define a field for this message, which creates accessor methods. The
|
||||
# "pattern" is the Array#pack or String#unpack code to extract this field.
|
||||
def self.field(name, pattern, default=nil, opt={})
|
||||
self::FIELDS << name
|
||||
self::FORMAT << pattern
|
||||
self::DEFAULTS[name] = default
|
||||
define_method name do
|
||||
@attrs.fetch name
|
||||
end
|
||||
define_method "#{name}=" do |val|
|
||||
@attrs.store name, val
|
||||
end
|
||||
end
|
||||
|
||||
def self.uchar(name, *args); field name, "C", 0, *args; end
|
||||
def self.uint16(name, *args); field name, "S", 0, *args; end
|
||||
def self.uint32(name, *args); field name, "L", 0, *args; end
|
||||
def self.char(name, *args); field name, "c", 0, *args; end
|
||||
def self.int16(name, *args); field name, "s", 0, *args; end
|
||||
def self.int32(name, *args); field name, "l", 0, *args; end
|
||||
def self.ushort(name, *args); field name, "S_", 0, *args; end
|
||||
def self.uint(name, *args); field name, "I", 0, *args; end
|
||||
def self.ulong(name, *args); field name, "L_", 0, *args; end
|
||||
def self.short(name, *args); field name, "s_", 0, *args; end
|
||||
def self.int(name, *args); field name, "i", 0, *args; end
|
||||
def self.long(name, *args); field name, "l_", 0, *args; end
|
||||
|
||||
# Returns the packed binary representation of this message (without
|
||||
# header, and not padded to NLMSG_ALIGNTO bytes)
|
||||
def to_s
|
||||
self.class::FIELDS.map { |key| self[key] }.pack(self.class::FORMAT)
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<#{self.class} #{@attrs.inspect}>"
|
||||
end
|
||||
|
||||
# Convert a binary representation of this message into an object instance
|
||||
def self.parse(str)
|
||||
res = new
|
||||
str.unpack(self::FORMAT).zip(self::FIELDS).each do |val, key|
|
||||
res[key] = val
|
||||
end
|
||||
res
|
||||
end
|
||||
|
||||
NLMSG_ALIGNTO_1 = NLMSG_ALIGNTO-1 #:nodoc:
|
||||
NLMSG_ALIGNTO_1_MASK = ~NLMSG_ALIGNTO_1 #:nodoc:
|
||||
|
||||
# Round up a length to a multiple of NLMSG_ALIGNTO bytes
|
||||
def self.align(n)
|
||||
(n + NLMSG_ALIGNTO_1) & NLMSG_ALIGNTO_1_MASK
|
||||
end
|
||||
|
||||
PADDING = ("\000" * NLMSG_ALIGNTO).freeze #:nodoc:
|
||||
|
||||
# Pad a string up to a multiple of NLMSG_ALIGNTO bytes. Returns str.
|
||||
def self.pad(str)
|
||||
str << PADDING[0, align(str.bytesize) - str.bytesize]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Link < Message
|
||||
code RTM_NEWLINK, RTM_DELLINK, RTM_GETLINK
|
||||
uchar :family
|
||||
uchar :pad
|
||||
ushort :type
|
||||
int :index
|
||||
uint :flags
|
||||
uint :change
|
||||
end
|
||||
|
||||
class Addr < Message
|
||||
code RTM_NEWADDR, RTM_DELADDR, RTM_GETADDR
|
||||
uchar :family
|
||||
uchar :prefixlen
|
||||
uchar :flags
|
||||
uchar :scope
|
||||
int :index
|
||||
end
|
||||
|
||||
class Route < Message
|
||||
code RTM_NEWROUTE, RTM_DELROUTE, RTM_GETROUTE
|
||||
uchar :family
|
||||
uchar :dst_len
|
||||
uchar :src_len
|
||||
uchar :tos
|
||||
uchar :table
|
||||
uchar :protocol
|
||||
uchar :scope
|
||||
uchar :type
|
||||
uint :flags
|
||||
end
|
||||
end
|
Reference in New Issue
Block a user