emonti-dnet-ffi 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/VERSION +1 -1
- data/dnet-ffi.gemspec +4 -2
- data/lib/dnet/addr.rb +5 -2
- data/lib/dnet/eth.rb +29 -6
- data/lib/dnet/intf.rb +1 -1
- data/lib/dnet/ip.rb +25 -0
- data/lib/dnet/util.rb +2 -1
- data/samples/eth_send_raw.rb +29 -0
- data/samples/ifconfig-alike.rb +44 -0
- data/samples/udp_send_raw.rb +8 -12
- metadata +6 -3
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.3
|
data/dnet-ffi.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{dnet-ffi}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.3"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Eric Monti"]
|
12
|
-
s.date = %q{2009-09-
|
12
|
+
s.date = %q{2009-09-17}
|
13
13
|
s.description = %q{Ruby FFI bindings for the libdnet raw network library}
|
14
14
|
s.email = %q{emonti@matasano.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -44,6 +44,8 @@ Gem::Specification.new do |s|
|
|
44
44
|
"lib/dnet/typedefs.rb",
|
45
45
|
"lib/dnet/udp.rb",
|
46
46
|
"lib/dnet/util.rb",
|
47
|
+
"samples/eth_send_raw.rb",
|
48
|
+
"samples/ifconfig-alike.rb",
|
47
49
|
"samples/udp_send_raw.rb",
|
48
50
|
"spec/addr_spec.rb",
|
49
51
|
"spec/arp_spec.rb",
|
data/lib/dnet/addr.rb
CHANGED
@@ -23,7 +23,8 @@ module Dnet
|
|
23
23
|
class Addr < ::FFI::Struct
|
24
24
|
include ::FFI::DRY::StructHelper
|
25
25
|
|
26
|
-
|
26
|
+
# A mapping of dnet address types
|
27
|
+
ADDR_TYPES = [ nil, :link, :inet, :inet6 ]
|
27
28
|
|
28
29
|
# struct addr { ... };
|
29
30
|
dsl_layout do
|
@@ -53,7 +54,8 @@ module Dnet
|
|
53
54
|
# leaves setting :bits up to chance depending on which hash key gets
|
54
55
|
# plucked first in set_fields() because of how ip_aton() works under
|
55
56
|
# the hood.
|
56
|
-
def set_fields(params)
|
57
|
+
def set_fields(params=nil)
|
58
|
+
params ||= {}
|
57
59
|
if params[:bits] and params[:addr]
|
58
60
|
raise( ::ArgumentError,
|
59
61
|
"Don't use :addr and :bits fields together. "+
|
@@ -66,6 +68,7 @@ module Dnet
|
|
66
68
|
# Looks up this object's 'atype' member against ADDR_TYPES
|
67
69
|
# Returns a symbol for the type or nil a type is not found.
|
68
70
|
def addr_type; ADDR_TYPES[ self[:atype] ] ; end
|
71
|
+
alias lookup_atype addr_type
|
69
72
|
|
70
73
|
# Returns a human-readable network address from self. Uses dnet(3)'s
|
71
74
|
# addr_ntoa function under the hood.
|
data/lib/dnet/eth.rb
CHANGED
@@ -9,21 +9,26 @@ module Dnet
|
|
9
9
|
# struct eth_addr { ... } eth_addr_t;
|
10
10
|
dsl_layout{ array :data, [:uchar, ETH_ADDR_LEN] }
|
11
11
|
|
12
|
-
MAC_RX = /^[a-f0-9]{1,2}([:-]?)(?:[a-f0-9]{1,2}\1){4}[a-f0-9]{1,2}$/i
|
13
|
-
|
14
12
|
# Adds the ability to initialize a new EthAddr with a mac address
|
15
13
|
# string such as 'de:ad:be:ef:ba:be'. This argument is only parsed
|
16
14
|
# if it is passed as the only String argument.
|
17
15
|
def initialize(*args)
|
18
16
|
if args.size == 1 and (s=args[0]).is_a? String
|
19
|
-
|
20
|
-
|
21
|
-
super(::FFI::MemoryPointer.from_string(raw))
|
17
|
+
super()
|
18
|
+
self.addr = s
|
22
19
|
else
|
23
20
|
super(*args)
|
24
21
|
end
|
25
22
|
end
|
26
23
|
|
24
|
+
def addr=(val)
|
25
|
+
unless val.to_s =~ /^#{Dnet::Util::RX_MAC_ADDR}$/
|
26
|
+
raise(ArgumentError, "invalid mac address #{val.inspect}")
|
27
|
+
end
|
28
|
+
raw = ::Dnet::Util.unhexify(val, /[:-]/)
|
29
|
+
self[:data].to_ptr.write_string(raw, ETH_ADDR_LEN)
|
30
|
+
end
|
31
|
+
|
27
32
|
# Returns the MAC address as an array of unsigned char values.
|
28
33
|
def chars; self[:data].to_a ; end
|
29
34
|
|
@@ -42,13 +47,31 @@ module Dnet
|
|
42
47
|
include ::FFI::DRY::StructHelper
|
43
48
|
include ::Dnet::NetEndianHelper
|
44
49
|
|
50
|
+
module Etype
|
51
|
+
include ::FFI::DRY::ConstMap
|
52
|
+
slurp_constants ::Dnet, "ETH_TYPE_"
|
53
|
+
def list; @@list ||= super(); end
|
54
|
+
end
|
55
|
+
|
45
56
|
dsl_layout do
|
46
57
|
struct :dst, EthAddr, :desc => 'destination address'
|
47
58
|
struct :src, EthAddr, :desc => 'source address'
|
48
59
|
field :etype, :ushort, :desc => 'ethernet payload type'
|
49
60
|
end
|
50
|
-
end
|
51
61
|
|
62
|
+
def lookup_etype
|
63
|
+
Etype[ self.etype ]
|
64
|
+
end
|
65
|
+
|
66
|
+
alias _divert_set_eth etype=
|
67
|
+
|
68
|
+
def etype=(val)
|
69
|
+
if val.kind_of? String or val.kind_of? Symbol
|
70
|
+
val = Etype[ val ] or raise(ArgumentError, "invalid eth type #{val}")
|
71
|
+
end
|
72
|
+
_divert_set_eth(val)
|
73
|
+
end
|
74
|
+
end
|
52
75
|
|
53
76
|
# Obtains a new handle to transmit raw Ethernet frames via the specified
|
54
77
|
# network device.
|
data/lib/dnet/intf.rb
CHANGED
data/lib/dnet/ip.rb
CHANGED
@@ -46,11 +46,36 @@ module Dnet
|
|
46
46
|
field :dst, :uint32, :desc => 'destination address'
|
47
47
|
end
|
48
48
|
|
49
|
+
# Overrides set_fields to supply a default value for the 'v_hl' field.
|
50
|
+
#
|
51
|
+
# v = 4
|
52
|
+
# hl = 5 # number of 32-bit words - 20 bytes (size of hdr without opts)
|
53
|
+
#
|
49
54
|
def set_fields(params=nil)
|
50
55
|
params ||= {}
|
51
56
|
super({:v_hl => 0x45}.merge(params))
|
52
57
|
end
|
53
58
|
|
59
|
+
# Sets the value of the hl field. This field is a 4-bit value occupying
|
60
|
+
# the lower 4 bits of the 'v_hl' field.
|
61
|
+
#
|
62
|
+
# This value is the size of the IP header (including opts if present)
|
63
|
+
# in 32-bit words. The byte size maximum is 15*4 - 60 bytes.
|
64
|
+
def hl=(val)
|
65
|
+
raise(ArgumentError, "value for header length too high") if val > 0xf
|
66
|
+
self[:v_hl] &= 0xf0
|
67
|
+
self[:v_hl] += val
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns the value of the hl field. This field is a 4-bit value occupying
|
71
|
+
# the lower 4 bits of the 'v_hl' field.
|
72
|
+
#
|
73
|
+
# This value is the size of the IP header (including opts if present)
|
74
|
+
# in 32-bit words. The byte size maximum is 15*4 - 60 bytes.
|
75
|
+
def hl
|
76
|
+
self[:v_hl] & 0x0f
|
77
|
+
end
|
78
|
+
|
54
79
|
# Type of service (ip_tos), RFC 1349 ("obsoleted by RFC 2474")
|
55
80
|
#
|
56
81
|
# Contains mappings for all the IP_TOS_[A-Z].* flags constants
|
data/lib/dnet/util.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
module Dnet
|
3
3
|
module Util
|
4
4
|
RX_IP4_ADDR = /(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/
|
5
|
+
RX_MAC_ADDR = /(?:(?:[a-f0-9]{1,2}[:-])?{5}[a-f0-9]{1,2})/i
|
5
6
|
|
6
7
|
# A number of helper methods which can be used to extend class, instance,
|
7
8
|
# or module
|
@@ -39,7 +40,7 @@ module Dnet
|
|
39
40
|
|
40
41
|
# takes a IPv4 number and returns it as a 32-bit number
|
41
42
|
def ipv4_atol(str)
|
42
|
-
unless str =~ /^#{RX_IP4_ADDR}$/
|
43
|
+
unless str =~ /^#{::Dnet::Util::RX_IP4_ADDR}$/
|
43
44
|
raise(::ArgumentError, "invalid IP address #{str.inspect}")
|
44
45
|
else
|
45
46
|
u32=0
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
begin ; require 'rubygems'; rescue LoadError ; end
|
4
|
+
require 'dnet'
|
5
|
+
include Dnet
|
6
|
+
|
7
|
+
unless dev=ARGV.shift
|
8
|
+
STDERR.puts "Usage: #{File.basename $0} interface"
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
|
12
|
+
STDERR.puts "Input data via stdin:"
|
13
|
+
data = STDIN.read
|
14
|
+
|
15
|
+
begin
|
16
|
+
sent = Eth::Handle.open(dev) {|h| h.eth_send(data) }
|
17
|
+
rescue Exception => e
|
18
|
+
STDERR.puts "Error: <#{e.class}> - #{e}"
|
19
|
+
STDERR.puts " ** try running as root?" if e.is_a? Dnet::HandleError
|
20
|
+
exit 1
|
21
|
+
end
|
22
|
+
|
23
|
+
if sent == data.size
|
24
|
+
puts "Sent: #{sent} bytes"
|
25
|
+
exit 0
|
26
|
+
else
|
27
|
+
STDERR.puts "Error: expected #{data.size} sent bytes - got #{sent}"
|
28
|
+
exit 1
|
29
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
begin; require 'rubygems' ; rescue LoadError; end
|
4
|
+
require 'dnet'
|
5
|
+
|
6
|
+
@arg_if = ARGV.shift
|
7
|
+
|
8
|
+
module Dumper
|
9
|
+
def self.dump_if_inet(a)
|
10
|
+
return "#{a.lookup_atype} address: #{a.string} net: #{a.net.string} "+
|
11
|
+
"bcast: #{a.bcast.string}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.dump_if_inet6(a)
|
15
|
+
return "#{a.lookup_atype} address: #{a.string} net: #{a.net.string} "
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.dump_if_link(a)
|
19
|
+
return "#{a.lookup_atype} address: #{a.string}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
Dnet::IntfHandle.each_entry do |entry|
|
24
|
+
next if @arg_if and entry.if_name != @arg_if
|
25
|
+
|
26
|
+
puts("#{entry.if_name}: "+
|
27
|
+
"type=#{entry.lookup_itype.downcase} "+
|
28
|
+
"flags=#{entry.flags}<#{entry.lookup_flags.join(',')}> "+
|
29
|
+
"mtu #{entry.mtu}\n" )
|
30
|
+
|
31
|
+
[:if_addr, :link_addr, :dst_addr].each do |addr_field|
|
32
|
+
addr = entry.send(addr_field)
|
33
|
+
if kind=addr.lookup_atype
|
34
|
+
puts " " + Dumper.__send__(:"dump_if_#{kind}", addr)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
entry.aliases.each do |alias_addr|
|
39
|
+
if kind = alias_addr.lookup_atype
|
40
|
+
puts " alias: " + Dumper.__send__(:"dump_if_#{kind}", alias_addr)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
data/samples/udp_send_raw.rb
CHANGED
@@ -54,21 +54,17 @@ blob.write(udp_hdr.to_ptr, Udp::Hdr.size)
|
|
54
54
|
blob.write(data)
|
55
55
|
|
56
56
|
begin
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
end
|
57
|
+
blob.rewind
|
58
|
+
sent = Ip::Handle.open { |h| h.ip_send(blob.read) }
|
59
|
+
|
60
|
+
if sent != tot_sz
|
61
|
+
raise "expected #{tot_sz} bytes sent, but got #{sent}"
|
62
|
+
else
|
63
|
+
STDERR.puts "Sent: #{sent} bytes"
|
65
64
|
end
|
66
|
-
rescue ::Dnet::HandleError => e
|
67
|
-
STDERR.puts "Error: <#{e.class}> - #{e}"
|
68
|
-
STDERR.puts " ** try running as root?"
|
69
|
-
exit 1
|
70
65
|
rescue Exception => e
|
71
66
|
STDERR.puts "Error: <#{e.class}> - #{e}"
|
67
|
+
STDERR.puts " ** try running as root?" if e.is_a? Dnet::HandleError
|
72
68
|
exit 1
|
73
69
|
ensure
|
74
70
|
blob.release
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: emonti-dnet-ffi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Monti
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-09-
|
12
|
+
date: 2009-09-17 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -59,6 +59,8 @@ files:
|
|
59
59
|
- lib/dnet/typedefs.rb
|
60
60
|
- lib/dnet/udp.rb
|
61
61
|
- lib/dnet/util.rb
|
62
|
+
- samples/eth_send_raw.rb
|
63
|
+
- samples/ifconfig-alike.rb
|
62
64
|
- samples/udp_send_raw.rb
|
63
65
|
- spec/addr_spec.rb
|
64
66
|
- spec/arp_spec.rb
|
@@ -76,6 +78,7 @@ files:
|
|
76
78
|
- spec/tun_spec.rb
|
77
79
|
has_rdoc: false
|
78
80
|
homepage: http://github.com/emonti/dnet-ffi
|
81
|
+
licenses:
|
79
82
|
post_install_message:
|
80
83
|
rdoc_options:
|
81
84
|
- --charset=UTF-8
|
@@ -96,7 +99,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
96
99
|
requirements: []
|
97
100
|
|
98
101
|
rubyforge_project:
|
99
|
-
rubygems_version: 1.
|
102
|
+
rubygems_version: 1.3.5
|
100
103
|
signing_key:
|
101
104
|
specification_version: 3
|
102
105
|
summary: Ruby FFI bindings for libdnet
|