
FFI::Struct handles nested structs and nested arrays much better, and avoids duplicating logic about structure alignment (which it probably does more correctly that CStruct) However it's awkward to use in other ways. e.g. no accessor methods; no proper #inspect; no ntohl for in_addr; no zero-sized arrays at end of struct; no hooks to convert int32 <-> IPAddr as far as I can see.
82 lines
2.2 KiB
Ruby
82 lines
2.2 KiB
Ruby
require 'linux/iptables'
|
|
require 'ipaddr'
|
|
|
|
module Linux
|
|
#-
|
|
# Definitions mainly from linux/netfilter_ipv4/ip_tables.h
|
|
#+
|
|
|
|
# struct ipt_getinfo
|
|
class IPTGetInfo < FFI::Struct
|
|
layout :name, [:char, IPT_TABLE_MAXNAMELEN],
|
|
:valid_hooks, :uint,
|
|
:hook_entry, [:uint, NF_INET_NUMHOOKS],
|
|
:underflow, [:uint, NF_INET_NUMHOOKS],
|
|
:num_entries, :uint,
|
|
:size, :uint
|
|
end
|
|
|
|
class IPTIP < FFI::Struct
|
|
layout :src, :int32, # FIXME: needs ntohl
|
|
:dst, :int32,
|
|
:smsk, :int32,
|
|
:dmsk, :int32,
|
|
:iniface, [:char, IFNAMSIZ],
|
|
:outiface, [:char, IFNAMSIZ],
|
|
:iniface_mask, [:uchar, IFNAMSIZ],
|
|
:outiface_mask, [:uchar, IFNAMSIZ],
|
|
:proto, :uint16,
|
|
:flags, :uint8,
|
|
:invflags, :uint8
|
|
end
|
|
|
|
# struct xt_counters (netfilter/x_tables.h)
|
|
class XTCounters < FFI::Struct
|
|
layout :pcnt, :uint64,
|
|
:bcnt, :uint64
|
|
end
|
|
|
|
# struct ipt_entry
|
|
class IPTEntry < FFI::Struct
|
|
layout :ip, IPTIP,
|
|
:nfcache, :uint,
|
|
:target_offset, :uint16, # size of ipt_entry + matches
|
|
:next_offset, :uint16, # size of ipt_entry + matches + target
|
|
:comefrom, :uint,
|
|
:counters, XTCounters,
|
|
:elems, [:uchar, 1] # should be [:uchar, 0]
|
|
end
|
|
|
|
# struct ipt_get_entries
|
|
class IPTGetEntries < FFI::Struct
|
|
layout :name, [:uchar, IPT_TABLE_MAXNAMELEN],
|
|
:size, :uint,
|
|
:entrytable, [IPTEntry, 1] # should be [IPTEntry, 0]
|
|
end
|
|
|
|
# Class for handling iptables. Note that this doesn't actually use
|
|
# Netlink at all :-(
|
|
class Iptables4 < Iptables
|
|
PROC_TABLES = "/proc/net/ip_tables_names"
|
|
PROC_TARGETS = "/proc/net/ip_tables_targets"
|
|
PROC_MATCHES = "/proc/net/ip_tables_matches"
|
|
|
|
TABLE_MAXNAMELEN = IPT_TABLE_MAXNAMELEN
|
|
TC_AF = Socket::AF_INET
|
|
TC_IPPROTO = Socket::IPPROTO_IP
|
|
SO_GET_INFO = IPT_SO_GET_INFO
|
|
SO_GET_ENTRIES = IPT_SO_GET_ENTRIES
|
|
STRUCT_ENTRY = IPTEntry
|
|
STRUCT_GETINFO = IPTGetInfo
|
|
STRUCT_GET_ENTRIES = IPTGetEntries
|
|
# This is a frig because of [1] instead of [0] above
|
|
STRUCT_GET_ENTRIES_SIZE = IPTGetEntries.offset_of(:entrytable)
|
|
end
|
|
end
|
|
|
|
if __FILE__ == $0
|
|
require 'pp'
|
|
pp Linux::Iptables4.tables
|
|
pp Linux::Iptables4.table("filter").rules
|
|
end
|