dnet-ffi 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/lib/dnet/intf.rb ADDED
@@ -0,0 +1,194 @@
1
+
2
+
3
+ module Dnet
4
+
5
+ # Bindings and interface to dnet(3)'s api for network interfaces
6
+ module Intf
7
+
8
+ # An interface table entry
9
+ #
10
+ # field :len, :uint, :desc => 'length of entry'
11
+ # array :if_name, [:uint8, INTF_NAME_LEN], :desc => 'interface name'
12
+ # field :itype, :ushort, :desc => 'interface type'
13
+ # field :flags, :ushort, :desc => 'interface flags'
14
+ # field :mtu, :uint, :desc => 'interface MTU'
15
+ # struct :if_addr, ::Dnet::Addr, :desc => 'interface address'
16
+ # struct :dst_addr, ::Dnet::Addr, :desc => 'point-to-point dst'
17
+ # struct :link_addr, ::Dnet::Addr, :desc => 'link-layer address'
18
+ # field :alias_num, :uint, :desc => 'number of aliases'
19
+ # # :if_aliases, ::Dnet::Addr ) ## variable-length array of aliases
20
+ #
21
+ class Entry < ::FFI::Struct
22
+ include ::FFI::DRY::StructHelper
23
+
24
+ dsl_layout do
25
+ field :len, :uint, :desc => 'length of entry'
26
+ array :if_name, [:uint8, INTF_NAME_LEN], :desc => 'interface name'
27
+ field :itype, :ushort, :desc => 'interface type'
28
+ field :flags, :ushort, :desc => 'interface flags'
29
+ field :mtu, :uint, :desc => 'interface MTU'
30
+ struct :if_addr, ::Dnet::Addr, :desc => 'interface address'
31
+ struct :dst_addr, ::Dnet::Addr, :desc => 'point-to-point dst'
32
+ struct :link_addr, ::Dnet::Addr, :desc => 'link-layer address'
33
+ field :alias_num, :uint, :desc => 'number of aliases'
34
+ # :if_aliases, ::Dnet::Addr ) ## variable-length array of aliases
35
+ end
36
+
37
+ # Constants map for interface flags. This is a map of flags, so
38
+ # lookups for integers using [nnn] return a list of flags present in nnn.
39
+ # Lookups for names using ["XX"], ["xx"] or [:xx] still return the value
40
+ # of the constant named XX.
41
+ module Flags
42
+ include ::FFI::DRY::ConstFlagsMap
43
+ slurp_constants(::Dnet, "INTF_FLAG_")
44
+ def self.list; @@list ||= super(); end
45
+ end
46
+
47
+ def lookup_flags; Flags[self[:flags]]; end
48
+
49
+ # Constants map for interface types
50
+ module Itype
51
+ include ::FFI::DRY::ConstMap
52
+ slurp_constants(::Dnet, "INTF_TYPE_")
53
+ def self.list; @@list ||= super(); end
54
+ end
55
+
56
+ def lookup_itype; Itype[self[:itype]]; end
57
+
58
+ # Returns a newly instantiated and allocated copy of this interface entry
59
+ def copy
60
+ xtra = (::Dnet::Addr.size * self.alias_num)
61
+ if self.len == (Entry.size + xtra)
62
+ super(xtra)
63
+ else
64
+ super()
65
+ end
66
+ end
67
+
68
+ # returns an array containing all the aliases for this interface
69
+ def aliases
70
+ ary = []
71
+ asz = ::Dnet::Addr.size
72
+ p = (self.to_ptr() + Entry.size) # start at end of struct
73
+ self[:alias_num].times do
74
+ ary << ::Dnet::Addr.new(p)
75
+ p += asz
76
+ end
77
+ return ary
78
+ end
79
+
80
+ # returns the interface name string of this object
81
+ def if_name; self[:if_name].to_ptr.read_string; end
82
+
83
+ # sets the interface name string on this object.
84
+ def if_name=(val)
85
+ name = ::Dnet.truncate_cstr(val, INTF_NAME_LEN)
86
+ self[:if_name].to_ptr.write_string_length(name.to_s, len)
87
+ end
88
+ alias set_name if_name=
89
+
90
+ end # class Entry
91
+
92
+ # A handle to the network interface configuration.
93
+ # This class manages a dnet(3) intf_t handle through intf_open() and
94
+ # intf_close() and wraps several libdnet functions as ruby methods.
95
+ class Handle < ::Dnet::LoopableHandle
96
+
97
+ # Obtains a handle to access the network interface configuration.
98
+ def initialize
99
+ if (@handle = ::Dnet.intf_open).address == 0
100
+ raise H_ERR.new("unable to open interface handle")
101
+ end
102
+ _handle_opened!
103
+ end
104
+
105
+ # Closes the handle. Uses dnet(3)'s arp_close() function under the hood
106
+ def close
107
+ _do_if_open { _handle_closed!; ::Dnet.intf_close(@handle) }
108
+ end
109
+
110
+ # Iterates over all network interfaces, yielding each entry (cast as an
111
+ # Intf::Entry) to a block. Uses dnet(3)'s intf_loop() function under the
112
+ # hood.
113
+ def loop &block
114
+ _loop ::Dnet, :intf_loop, Entry, &block
115
+ end
116
+
117
+ # intf_get() retrieves an interface configuration entry, keyed on
118
+ # intf_name. For all intf_get() functions, intf_len should be set to the
119
+ # size of the buffer pointed to by entry (usually sizeof(struct
120
+ # intf_entry), but should be larger to accomodate any interface alias
121
+ # addresses.
122
+ #
123
+ # int intf_get(intf_t *i, struct intf_entry *entry);
124
+ #
125
+ def get(name, ie=nil)
126
+ _check_open!
127
+ ie ||= Entry.new
128
+ ie.if_name = name.to_s
129
+ return ie if ::Dnet.intf_get(@handle, ie) == 0
130
+ end
131
+
132
+ # retrieves the configuration for the interface whose primary address
133
+ # matches the specified src.
134
+ #
135
+ # int intf_get_src(intf_t *i, struct intf_entry *entry, struct addr *src);
136
+ def get_src(addr, ie=nil)
137
+ _check_open!
138
+ src = Addr.new(addr.to_s)
139
+ ie ||= Entry.new
140
+ return ie if ::Dnet.intf_get_src(@handle, ie, src) == 0
141
+ end
142
+
143
+ # Retrieves the configuration for the best interface with which to reach
144
+ # the specified destination.
145
+ #
146
+ # int intf_get_dst(intf_t *i, struct intf_entry *entry, struct addr *dst);
147
+ def get_dst(addr, ie=nil)
148
+ _check_open!
149
+ dst = Addr.new(addr.to_s)
150
+ ie ||= Entry.new
151
+ return ie if ::Dnet.intf_get_dst(@handle, ie, dst) == 0
152
+ end
153
+
154
+ # Sets the interface configuration entry. Usually requires 'root'
155
+ # privileges.
156
+ #
157
+ # int intf_set(intf_t *i, const struct intf_entry *entry);
158
+ def set(entry)
159
+ _check_open!
160
+ return true if ::Dnet.intf_set(@handle, entry) == 0
161
+ end
162
+
163
+ end # Handle
164
+
165
+ def self.open(*args)
166
+ Handle.open(*args) {|*y| yield(*y) if block_given? }
167
+ end
168
+
169
+ def self.each_entry(*args)
170
+ Intf::Handle.each_entry(*args){|*y| yield(*y) }
171
+ end
172
+
173
+ def self.entries
174
+ Intf::Handle.entries
175
+ end
176
+
177
+ end # Intf
178
+
179
+ # Alias for Intf::Handle
180
+ IntfHandle = Intf::Handle
181
+
182
+
183
+ callback :intf_handler, [Intf::Entry, :ulong] , :int
184
+
185
+ attach_function :intf_open, [], :intf_t
186
+ attach_function :intf_get, [:intf_t, Intf::Entry], :int
187
+ attach_function :intf_get_src, [:intf_t, Intf::Entry, Addr], :int
188
+ attach_function :intf_get_dst, [:intf_t, Intf::Entry, Addr], :int
189
+ attach_function :intf_set, [:intf_t, Intf::Entry], :int
190
+ attach_function :intf_loop, [:intf_t, :intf_handler, :ulong], :int
191
+ attach_function :intf_close, [:intf_t], :intf_t
192
+
193
+ end
194
+
data/lib/dnet/ip.rb ADDED
@@ -0,0 +1,315 @@
1
+
2
+ ### libdnet's IP interface
3
+
4
+ module Dnet
5
+
6
+ module Ip
7
+ # Protocols (proto) - http://www.iana.org/assignments/protocol-numbers
8
+ #
9
+ # Contains mappings for all the IP_PROTO_[A-Z].* constants
10
+ # (defined in constants.rb)
11
+ module Proto
12
+ include ::FFI::DRY::ConstMap
13
+ slurp_constants(::Dnet, "IP_PROTO_")
14
+ def self.list; @@list ||= super(); end
15
+ end
16
+
17
+ # IP header, without options
18
+ #
19
+ # field :v_hl, :uint8, :desc => 'v=vers(. & 0xf0) / '+
20
+ # 'hl=hdr len(. & 0x0f)'
21
+ # field :tos, :uint8, :desc => 'type of service'
22
+ # field :len, :uint16, :desc => 'total length (incl header)'
23
+ # field :id, :uint16, :desc => 'identification'
24
+ # field :off, :uint16, :desc => 'fragment offset and flags'
25
+ # field :ttl, :uint8, :desc => 'time to live'
26
+ # field :proto, :uint8, :desc => 'protocol'
27
+ # field :sum, :uint16, :desc => 'checksum'
28
+ # field :src, :uint32, :desc => 'source address'
29
+ # field :dst, :uint32, :desc => 'destination address'
30
+ #
31
+ class Hdr < ::FFI::Struct
32
+ include ::FFI::DRY::StructHelper
33
+ include ::Dnet::NetEndianHelper
34
+
35
+ dsl_layout do
36
+ field :v_hl, :uint8, :desc => 'v=vers(. & 0xf0) / '+
37
+ 'hl=hdr len(. & 0x0f)'
38
+ field :tos, :uint8, :desc => 'type of service'
39
+ field :len, :uint16, :desc => 'total length (incl header)'
40
+ field :id, :uint16, :desc => 'identification'
41
+ field :off, :uint16, :desc => 'fragment offset and flags'
42
+ field :ttl, :uint8, :desc => 'time to live'
43
+ field :proto, :uint8, :desc => 'protocol'
44
+ field :sum, :uint16, :desc => 'checksum'
45
+ field :src, :uint32, :desc => 'source address'
46
+ field :dst, :uint32, :desc => 'destination address'
47
+ end
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
+ #
54
+ def set_fields(params=nil)
55
+ params ||= {}
56
+ super({:v_hl => 0x45}.merge(params))
57
+ end
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
+
79
+ # Type of service (ip_tos), RFC 1349 ("obsoleted by RFC 2474")
80
+ #
81
+ # Contains mappings for all the IP_TOS_[A-Z].* flags constants
82
+ module Tos
83
+ include ::FFI::DRY::ConstFlagsMap
84
+ slurp_constants(::Dnet, "IP_TOS_")
85
+ def self.list; @@list ||= super(); end
86
+ end
87
+
88
+ def lookup_tos; Tos[ self.tos ]; end
89
+
90
+ # Alias to ::Dnet::Ip::Proto
91
+ Proto = ::Dnet::Ip::Proto
92
+
93
+ def lookup_proto; Proto[ self.proto ]; end
94
+
95
+ # Sets source IP address in the header from an IPv4 address string or
96
+ # 32-bit number.
97
+ def src=(val)
98
+ val = ::Dnet::Util.ipv4_atol(val) if val.kind_of? String
99
+ self[:src] = ::Dnet.htonl(val)
100
+ end
101
+
102
+ # Returns the source IP address as an IPv4 address string as an IPv4
103
+ # address string.
104
+ def src
105
+ ::Dnet::Util.ipv4_ltoa( ::Dnet.ntohl( self[:src] ))
106
+ end
107
+
108
+ # Sets destination IP address in the header from an IPv4 address string
109
+ # or 32-bit number.
110
+ def dst=(val)
111
+ val = ::Dnet::Util.ipv4_atol(val) if val.kind_of? String
112
+ self[:dst] = ::Dnet.htonl(val)
113
+ end
114
+
115
+ # Returns the destination IP address from the header as an IPv4 address
116
+ # string.
117
+ def dst
118
+ ::Dnet::Util.ipv4_ltoa( ::Dnet.ntohl( self[:dst] ))
119
+ end
120
+
121
+ end # class Hdr
122
+
123
+
124
+ # IP option (following IP header)
125
+ #
126
+ # array :otype, :uint8, :desc => 'option type'
127
+ # array :len, :uint8, :desc => 'option length >= IP_OPE_LEN'
128
+ # array :data, [:uint8, DATA_LEN], :desc => 'option message data '
129
+ #
130
+ class Opt < ::FFI::Struct
131
+ include ::FFI::DRY::StructHelper
132
+ include ::Dnet::NetEndianHelper
133
+
134
+ DATA_LEN = IP_OPT_LEN_MAX - IP_OPT_LEN
135
+
136
+ dsl_layout do
137
+ field :otype, :uint8, :desc => 'option type'
138
+ field :len, :uint8, :desc => 'option length >= IP_OPE_LEN'
139
+ array :data, [:uint8, DATA_LEN], :desc => 'option message data '
140
+ end
141
+
142
+ # Option types (otype) - http://www.iana.org/assignments/ip-parameters
143
+ #
144
+ # Contains mappings for all the IP_OTYPE_[A-Z].* constants
145
+ module Otype
146
+ include ::FFI::DRY::ConstMap
147
+ slurp_constants(::Dnet, "IP_OTYPE_")
148
+ def self.list; @@list ||= super(); end
149
+ end
150
+
151
+ # Security option data - RFC 791, 3.1
152
+ #
153
+ # field :sec, :uint16, :desc => 'security'
154
+ # field :cpt, :uint16, :desc => 'compartments'
155
+ # field :hr, :uint16, :desc => 'handling restrictions'
156
+ # array :tcc, [:uint8, 3], :desc => 'transmission control code'
157
+ #
158
+ class DataSEC < ::FFI::Struct
159
+ include ::FFI::DRY::StructHelper
160
+ include ::Dnet::NetEndianHelper
161
+
162
+ dsl_layout do
163
+ field :sec, :uint16, :desc => 'security'
164
+ field :cpt, :uint16, :desc => 'compartments'
165
+ field :hr, :uint16, :desc => 'handling restrictions'
166
+ array :tcc, [:uint8, 3], :desc => 'transmission control code'
167
+ end
168
+
169
+ end
170
+
171
+ # Timestamp option data - RFC 791, 3.1
172
+ #
173
+ # field :ptr, :uint8, :desc => 'from start of option'
174
+ # field :oflw_flg, :uint8, :desc => 'oflw = number of IPs skipped /'+
175
+ # 'flg = address/timestamp flag'
176
+ # field :iptspairs, :uint32, :desc => 'IP addr/ts pairs, var-length'
177
+ #
178
+ class DataTS < ::FFI::Struct
179
+ include ::FFI::DRY::StructHelper
180
+ include ::Dnet::NetEndianHelper
181
+ dsl_layout do
182
+ field :ptr, :uint8, :desc => 'from start of option'
183
+ field :oflw_flg, :uint8, :desc => 'oflw = number of IPs skipped /'+
184
+ 'flg = address/timestamp flag'
185
+ field :iptspairs, :uint32, :desc => 'IP addr/ts pairs, var-length'
186
+ end
187
+
188
+ end
189
+
190
+ # (Loose Source/Record/Strict Source) Route option data - RFC 791, 3.1
191
+ #
192
+ # field :ptr, :uint8, :desc => 'from start of option'
193
+ # field :iplist, :uint32, :desc => 'var-length list of IPs'
194
+ #
195
+ class DataRR < ::FFI::Struct
196
+ include ::FFI::DRY::StructHelper
197
+ include ::Dnet::NetEndianHelper
198
+
199
+ dsl_layout do
200
+ field :ptr, :uint8, :desc => 'from start of option'
201
+ field :iplist, :uint32, :desc => 'var-length list of IPs'
202
+ end
203
+
204
+ end
205
+
206
+ # Traceroute option data - RFC 1393, 2.2
207
+ #
208
+ # struct ip_opt_data_tr {
209
+ # uint16_t id; /* ID number */
210
+ # uint16_t ohc; /* outbound hop count */
211
+ # uint16_t rhc; /* return hop count */
212
+ # uint32_t origip; /* originator IP address */
213
+ # } __attribute__((__packed__));
214
+ #
215
+ class DataTR < ::FFI::Struct
216
+ include ::FFI::DRY::StructHelper
217
+ include ::Dnet::NetEndianHelper
218
+
219
+ dsl_layout do
220
+ field :id, :uint16, :desc => 'ID number'
221
+ field :ohc, :uint16, :desc => 'outbound hop count'
222
+ field :rhc, :uint16, :desc => 'return hop count'
223
+ field :origip, :uint32, :desc => 'originator IP address'
224
+ end
225
+ end
226
+
227
+ end # class Opt
228
+
229
+
230
+ # Abstraction around dnet(3)'s ip_t handle for transmitting raw IP packets
231
+ # routed by the kernel.
232
+ class Handle < ::Dnet::Handle
233
+
234
+ # Obtains a handle to transmit raw IP packets, routed by the kernel.
235
+ # Uses dnet(3)'s ip_open() function under the hood.
236
+ def initialize
237
+ if (@handle=::Dnet.ip_open).address == 0
238
+ raise H_ERR.new("unable to open IP raw packet handle")
239
+ end
240
+ _handle_opened!
241
+ end
242
+
243
+ # Transmits len bytes of the IP packet 'buf'. Len can be left
244
+ # blank for String objects, which will use the size of the string.
245
+ def ip_send(buf, len=nil)
246
+ pbuf, sz = ::Dnet::Util.derive_pointer(buf, len)
247
+ ::Dnet.ip_send(@handle, pbuf, sz)
248
+ end
249
+
250
+ # closes the IP raw packet handle
251
+ def close
252
+ _do_if_open { _handle_closed!; ::Dnet.ip_close(@handle) }
253
+ end
254
+
255
+ # Transmits len bytes of the IP packet pointed to by buf through a
256
+ # temporary Ip::Handle which is closed immediately after sending.
257
+ #
258
+ # See also Ip::Handle#ip_send
259
+ def self.ip_send(buf, len=nil)
260
+ open {|h| h.ip_send(buf, len)}
261
+ end
262
+
263
+ # Instance alias to ::Dnet.ip_add_option()
264
+ def add_option(*args); ::Dnet::Ip.ip_add_option(*args); end
265
+
266
+ # Instance alias to ::Dnet.ip_checksum()
267
+ def checksum(*args); ::Dnet::Ip.ip_checksum(*args); end
268
+
269
+ end # class Handle
270
+
271
+
272
+ # Adds the header option for the protocol proto specified
273
+ # by 'optbuf' of length 'osz' and appends it to the appropriate header of
274
+ # the IP packet contained in 'buf' of size 'bsz', shifting any existing
275
+ # payload and adding NOPs to pad the option to a word boundary if necessary.
276
+ #
277
+ # The buf and/or optbuf can be String or FFI::Pointer objects. Pointers
278
+ # must also include the associated size in bsz or osz respectively.
279
+ #
280
+ # sizes are required for buffer
281
+ #
282
+ # ssize_t ip_add_option(void *buf, size_t len, int proto,
283
+ # const void *optbuf, size_t optlen);
284
+ def self.add_option(buf, proto, optbuf, bsz = nil, osz = nil)
285
+ bufp, blen = ::Dnet::Util.derive_pointer(buf, bsz)
286
+ optbufp, olen = ::Dnet::Util.derive_pointer(optbuf, osz)
287
+ ::Dnet.ip_add_option(bufp, proto, blen, optbufp, olen)
288
+ end
289
+
290
+ # Sets the IP checksum and any appropriate transport protocol
291
+ # checksum for the IP packet pointed to by buf of length len
292
+ #
293
+ # returns [buf-pointer, buf-length]
294
+ #
295
+ # void ip_checksum(void *buf, size_t len);
296
+ def self.checksum(buf, len=nil)
297
+ bufp, plen = ::Dnet::Util.derive_pointer(buf, len)
298
+ ::Dnet.ip_checksum bufp, plen
299
+ return [bufp, plen]
300
+ end
301
+
302
+
303
+ end # module Ip
304
+
305
+ # Alias for Ip::Handle
306
+ IpHandle = Ip::Handle
307
+
308
+ attach_function :ip_open, [], :ip_t
309
+ attach_function :ip_add_option, [:pointer, :size_t, :int, :pointer, :size_t], :ssize_t
310
+ attach_function :ip_checksum, [:pointer, :size_t], :void
311
+ attach_function :ip_send, [:ip_t, :pointer, :size_t], :ssize_t
312
+ attach_function :ip_close, [:ip_t], :ip_t
313
+
314
+ end
315
+