dnet-ffi 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
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
+