packetfu 1.1.8 → 1.1.9

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.
Files changed (84) hide show
  1. data/README.rdoc +11 -12
  2. data/bench/octets.rb +9 -9
  3. data/examples/100kpackets.rb +13 -12
  4. data/examples/ackscan.rb +17 -16
  5. data/examples/arp.rb +36 -35
  6. data/examples/arphood.rb +37 -36
  7. data/examples/dissect_thinger.rb +7 -6
  8. data/examples/ethernet.rb +1 -0
  9. data/examples/examples.rb +1 -0
  10. data/examples/ifconfig.rb +1 -0
  11. data/examples/new-simple-stats.rb +24 -23
  12. data/examples/packetfu-shell.rb +26 -25
  13. data/examples/simple-sniffer.rb +10 -9
  14. data/examples/simple-stats.rb +24 -23
  15. data/examples/slammer.rb +4 -3
  16. data/lib/packetfu.rb +128 -127
  17. data/lib/packetfu/capture.rb +170 -169
  18. data/lib/packetfu/config.rb +53 -52
  19. data/lib/packetfu/inject.rb +57 -56
  20. data/lib/packetfu/packet.rb +529 -528
  21. data/lib/packetfu/pcap.rb +580 -579
  22. data/lib/packetfu/protos/arp.rb +91 -90
  23. data/lib/packetfu/protos/arp/header.rb +159 -158
  24. data/lib/packetfu/protos/arp/mixin.rb +37 -36
  25. data/lib/packetfu/protos/eth.rb +45 -44
  26. data/lib/packetfu/protos/eth/header.rb +244 -243
  27. data/lib/packetfu/protos/eth/mixin.rb +4 -3
  28. data/lib/packetfu/protos/hsrp.rb +70 -69
  29. data/lib/packetfu/protos/hsrp/header.rb +108 -107
  30. data/lib/packetfu/protos/hsrp/mixin.rb +30 -29
  31. data/lib/packetfu/protos/icmp.rb +72 -71
  32. data/lib/packetfu/protos/icmp/header.rb +83 -82
  33. data/lib/packetfu/protos/icmp/mixin.rb +15 -14
  34. data/lib/packetfu/protos/invalid.rb +50 -49
  35. data/lib/packetfu/protos/ip.rb +70 -69
  36. data/lib/packetfu/protos/ip/header.rb +292 -291
  37. data/lib/packetfu/protos/ip/mixin.rb +41 -40
  38. data/lib/packetfu/protos/ipv6.rb +51 -50
  39. data/lib/packetfu/protos/ipv6/header.rb +189 -188
  40. data/lib/packetfu/protos/ipv6/mixin.rb +30 -29
  41. data/lib/packetfu/protos/lldp.rb +3 -1
  42. data/lib/packetfu/protos/lldp/header.rb +1 -0
  43. data/lib/packetfu/protos/lldp/mixin.rb +1 -0
  44. data/lib/packetfu/protos/tcp.rb +177 -176
  45. data/lib/packetfu/protos/tcp/ecn.rb +36 -35
  46. data/lib/packetfu/protos/tcp/flags.rb +75 -74
  47. data/lib/packetfu/protos/tcp/header.rb +269 -268
  48. data/lib/packetfu/protos/tcp/hlen.rb +33 -32
  49. data/lib/packetfu/protos/tcp/mixin.rb +47 -46
  50. data/lib/packetfu/protos/tcp/option.rb +322 -321
  51. data/lib/packetfu/protos/tcp/options.rb +96 -95
  52. data/lib/packetfu/protos/tcp/reserved.rb +36 -35
  53. data/lib/packetfu/protos/udp.rb +117 -116
  54. data/lib/packetfu/protos/udp/header.rb +92 -91
  55. data/lib/packetfu/protos/udp/mixin.rb +4 -3
  56. data/lib/packetfu/structfu.rb +281 -280
  57. data/lib/packetfu/utils.rb +211 -208
  58. data/lib/packetfu/version.rb +42 -41
  59. data/packetfu.gemspec +1 -1
  60. data/spec/ethpacket_spec.rb +48 -48
  61. data/spec/packet_spec.rb +57 -57
  62. data/spec/packet_subclasses_spec.rb +8 -8
  63. data/spec/packetfu_spec.rb +59 -59
  64. data/spec/structfu_spec.rb +268 -268
  65. data/spec/tcp_spec.rb +75 -75
  66. data/test/all_tests.rb +13 -13
  67. data/test/func_lldp.rb +3 -3
  68. data/test/ptest.rb +2 -2
  69. data/test/test_arp.rb +116 -116
  70. data/test/test_capture.rb +45 -45
  71. data/test/test_eth.rb +68 -68
  72. data/test/test_hsrp.rb +9 -9
  73. data/test/test_icmp.rb +52 -52
  74. data/test/test_inject.rb +18 -18
  75. data/test/test_invalid.rb +16 -16
  76. data/test/test_ip.rb +36 -36
  77. data/test/test_ip6.rb +48 -48
  78. data/test/test_octets.rb +21 -21
  79. data/test/test_packet.rb +154 -154
  80. data/test/test_pcap.rb +170 -170
  81. data/test/test_structfu.rb +97 -97
  82. data/test/test_tcp.rb +320 -320
  83. data/test/test_udp.rb +76 -76
  84. metadata +2 -2
@@ -1,38 +1,39 @@
1
+ # -*- coding: binary -*-
1
2
  module PacketFu
2
- # This Mixin simplifies access to the ARPHeaders. Mix this in with your
3
- # packet interface, and it will add methods that essentially delegate to
4
- # the 'arp_header' method (assuming that it is a ARPHeader object)
5
- module ARPHeaderMixin
6
- def arp_hw=(v); self.arp_header.arp_hw= v; end
7
- def arp_hw; self.arp_header.arp_hw; end
8
- def arp_proto=(v); self.arp_header.arp_proto= v; end
9
- def arp_proto; self.arp_header.arp_proto; end
10
- def arp_hw_len=(v); self.arp_header.arp_hw_len= v; end
11
- def arp_hw_len; self.arp_header.arp_hw_len; end
12
- def arp_proto_len=(v); self.arp_header.arp_proto_len= v; end
13
- def arp_proto_len; self.arp_header.arp_proto_len; end
14
- def arp_opcode=(v); self.arp_header.arp_opcode= v; end
15
- def arp_opcode; self.arp_header.arp_opcode; end
16
- def arp_src_mac=(v); self.arp_header.arp_src_mac= v; end
17
- def arp_src_mac; self.arp_header.arp_src_mac; end
18
- def arp_src_ip=(v); self.arp_header.arp_src_ip= v; end
19
- def arp_src_ip; self.arp_header.arp_src_ip; end
20
- def arp_dst_mac=(v); self.arp_header.arp_dst_mac= v; end
21
- def arp_dst_mac; self.arp_header.arp_dst_mac; end
22
- def arp_dst_ip=(v); self.arp_header.arp_dst_ip= v; end
23
- def arp_dst_ip; self.arp_header.arp_dst_ip; end
24
- def arp_saddr_mac=(v); self.arp_header.arp_saddr_mac= v; end
25
- def arp_saddr_mac; self.arp_header.arp_saddr_mac; end
26
- def arp_daddr_mac=(v); self.arp_header.arp_daddr_mac= v; end
27
- def arp_daddr_mac; self.arp_header.arp_daddr_mac; end
28
- def arp_saddr_ip=(v); self.arp_header.arp_saddr_ip= v; end
29
- def arp_saddr_ip; self.arp_header.arp_saddr_ip; end
30
- def arp_daddr_ip=(v); self.arp_header.arp_daddr_ip= v; end
31
- def arp_daddr_ip; self.arp_header.arp_daddr_ip; end
32
- def arp_src_mac_readable; self.arp_header.arp_src_mac_readable; end
33
- def arp_dst_mac_readable; self.arp_header.arp_dst_mac_readable; end
34
- def arp_src_ip_readable; self.arp_header.arp_src_ip_readable; end
35
- def arp_dst_ip_readable; self.arp_header.arp_dst_ip_readable; end
36
- def arp_proto_readable; self.arp_header.arp_proto_readable; end
37
- end
3
+ # This Mixin simplifies access to the ARPHeaders. Mix this in with your
4
+ # packet interface, and it will add methods that essentially delegate to
5
+ # the 'arp_header' method (assuming that it is a ARPHeader object)
6
+ module ARPHeaderMixin
7
+ def arp_hw=(v); self.arp_header.arp_hw= v; end
8
+ def arp_hw; self.arp_header.arp_hw; end
9
+ def arp_proto=(v); self.arp_header.arp_proto= v; end
10
+ def arp_proto; self.arp_header.arp_proto; end
11
+ def arp_hw_len=(v); self.arp_header.arp_hw_len= v; end
12
+ def arp_hw_len; self.arp_header.arp_hw_len; end
13
+ def arp_proto_len=(v); self.arp_header.arp_proto_len= v; end
14
+ def arp_proto_len; self.arp_header.arp_proto_len; end
15
+ def arp_opcode=(v); self.arp_header.arp_opcode= v; end
16
+ def arp_opcode; self.arp_header.arp_opcode; end
17
+ def arp_src_mac=(v); self.arp_header.arp_src_mac= v; end
18
+ def arp_src_mac; self.arp_header.arp_src_mac; end
19
+ def arp_src_ip=(v); self.arp_header.arp_src_ip= v; end
20
+ def arp_src_ip; self.arp_header.arp_src_ip; end
21
+ def arp_dst_mac=(v); self.arp_header.arp_dst_mac= v; end
22
+ def arp_dst_mac; self.arp_header.arp_dst_mac; end
23
+ def arp_dst_ip=(v); self.arp_header.arp_dst_ip= v; end
24
+ def arp_dst_ip; self.arp_header.arp_dst_ip; end
25
+ def arp_saddr_mac=(v); self.arp_header.arp_saddr_mac= v; end
26
+ def arp_saddr_mac; self.arp_header.arp_saddr_mac; end
27
+ def arp_daddr_mac=(v); self.arp_header.arp_daddr_mac= v; end
28
+ def arp_daddr_mac; self.arp_header.arp_daddr_mac; end
29
+ def arp_saddr_ip=(v); self.arp_header.arp_saddr_ip= v; end
30
+ def arp_saddr_ip; self.arp_header.arp_saddr_ip; end
31
+ def arp_daddr_ip=(v); self.arp_header.arp_daddr_ip= v; end
32
+ def arp_daddr_ip; self.arp_header.arp_daddr_ip; end
33
+ def arp_src_mac_readable; self.arp_header.arp_src_mac_readable; end
34
+ def arp_dst_mac_readable; self.arp_header.arp_dst_mac_readable; end
35
+ def arp_src_ip_readable; self.arp_header.arp_src_ip_readable; end
36
+ def arp_dst_ip_readable; self.arp_header.arp_dst_ip_readable; end
37
+ def arp_proto_readable; self.arp_header.arp_proto_readable; end
38
+ end
38
39
  end
@@ -1,53 +1,54 @@
1
+ # -*- coding: binary -*-
1
2
  require 'packetfu/protos/eth/header'
2
3
  require 'packetfu/protos/eth/mixin'
3
4
 
4
5
  module PacketFu
5
- # EthPacket is used to construct Ethernet packets. They contain an
6
- # Ethernet header, and that's about it.
7
- #
8
- # == Example
9
- #
10
- # require 'packetfu'
11
- # eth_pkt = PacketFu::EthPacket.new
12
- # eth_pkt.eth_saddr="00:1c:23:44:55:66"
13
- # eth_pkt.eth_daddr="00:1c:24:aa:bb:cc"
14
- #
15
- # eth_pkt.to_w('eth0') # Inject on the wire. (require root)
16
- #
17
- class EthPacket < Packet
6
+ # EthPacket is used to construct Ethernet packets. They contain an
7
+ # Ethernet header, and that's about it.
8
+ #
9
+ # == Example
10
+ #
11
+ # require 'packetfu'
12
+ # eth_pkt = PacketFu::EthPacket.new
13
+ # eth_pkt.eth_saddr="00:1c:23:44:55:66"
14
+ # eth_pkt.eth_daddr="00:1c:24:aa:bb:cc"
15
+ #
16
+ # eth_pkt.to_w('eth0') # Inject on the wire. (require root)
17
+ #
18
+ class EthPacket < Packet
18
19
  include ::PacketFu::EthHeaderMixin
19
20
 
20
- attr_accessor :eth_header
21
-
22
- def self.can_parse?(str)
23
- # XXX Temporary fix. Need to extend the EthHeader class to handle more.
24
- valid_eth_types = [0x0800, 0x0806, 0x86dd, 0x88cc]
25
- return false unless str.size >= 14
26
- type = str[12,2].unpack("n").first rescue nil
27
- return false unless valid_eth_types.include? type
28
- true
29
- end
30
-
31
- def read(str=nil,args={})
32
- raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
33
- @eth_header.read(str)
34
- super(args)
35
- return self
36
- end
37
-
38
- # Does nothing, really, since there's no length or
39
- # checksum to calculate for a straight Ethernet packet.
40
- def recalc(args={})
41
- @headers[0].inspect
42
- end
43
-
44
- def initialize(args={})
45
- @eth_header = EthHeader.new(args).read(args[:eth])
46
- @headers = [@eth_header]
47
- super
48
- end
49
-
50
- end
21
+ attr_accessor :eth_header
22
+
23
+ def self.can_parse?(str)
24
+ # XXX Temporary fix. Need to extend the EthHeader class to handle more.
25
+ valid_eth_types = [0x0800, 0x0806, 0x86dd, 0x88cc]
26
+ return false unless str.size >= 14
27
+ type = str[12,2].unpack("n").first rescue nil
28
+ return false unless valid_eth_types.include? type
29
+ true
30
+ end
31
+
32
+ def read(str=nil,args={})
33
+ raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
34
+ @eth_header.read(str)
35
+ super(args)
36
+ return self
37
+ end
38
+
39
+ # Does nothing, really, since there's no length or
40
+ # checksum to calculate for a straight Ethernet packet.
41
+ def recalc(args={})
42
+ @headers[0].inspect
43
+ end
44
+
45
+ def initialize(args={})
46
+ @eth_header = EthHeader.new(args).read(args[:eth])
47
+ @headers = [@eth_header]
48
+ super
49
+ end
50
+
51
+ end
51
52
 
52
53
  end
53
54
 
@@ -1,247 +1,248 @@
1
+ # -*- coding: binary -*-
1
2
  module PacketFu
2
- # EthOui is the Organizationally Unique Identifier portion of a MAC address, used in EthHeader.
3
- #
4
- # See the OUI list at http://standards.ieee.org/regauth/oui/oui.txt
5
- #
6
- # ==== Header Definition
7
- #
8
- # Fixnum :b0
9
- # Fixnum :b1
10
- # Fixnum :b2
11
- # Fixnum :b3
12
- # Fixnum :b4
13
- # Fixnum :b5
14
- # Fixnum :local
15
- # Fixnum :multicast
16
- # Int16 :oui, Default: 0x1ac5 :)
17
- class EthOui < Struct.new(:b5, :b4, :b3, :b2, :b1, :b0, :local, :multicast, :oui)
18
-
19
- # EthOui is unusual in that the bit values do not enjoy StructFu typing.
20
- def initialize(args={})
21
- args[:local] ||= 0
22
- args[:oui] ||= 0x1ac # :)
23
- args.each_pair {|k,v| args[k] = 0 unless v}
24
- super(args[:b5], args[:b4], args[:b3], args[:b2],
25
- args[:b1], args[:b0], args[:local], args[:multicast],
26
- args[:oui])
27
- end
28
-
29
- # Returns the object in string form.
30
- def to_s
31
- byte = 0
32
- byte += 0b10000000 if b5.to_i == 1
33
- byte += 0b01000000 if b4.to_i == 1
34
- byte += 0b00100000 if b3.to_i == 1
35
- byte += 0b00010000 if b2.to_i == 1
36
- byte += 0b00001000 if b1.to_i == 1
37
- byte += 0b00000100 if b0.to_i == 1
38
- byte += 0b00000010 if local.to_i == 1
39
- byte += 0b00000001 if multicast.to_i == 1
40
- [byte,oui].pack("Cn")
41
- end
42
-
43
- # Reads a string to populate the object.
44
- def read(str)
45
- force_binary(str)
46
- return self if str.nil?
47
- if 1.respond_to? :ord
48
- byte = str[0].ord
49
- else
50
- byte = str[0]
51
- end
52
- self[:b5] = byte & 0b10000000 == 0b10000000 ? 1 : 0
53
- self[:b4] = byte & 0b01000000 == 0b01000000 ? 1 : 0
54
- self[:b3] = byte & 0b00100000 == 0b00100000 ? 1 : 0
55
- self[:b2] = byte & 0b00010000 == 0b00010000 ? 1 : 0
56
- self[:b1] = byte & 0b00001000 == 0b00001000 ? 1 : 0
57
- self[:b0] = byte & 0b00000100 == 0b00000100 ? 1 : 0
58
- self[:local] = byte & 0b00000010 == 0b00000010 ? 1 : 0
59
- self[:multicast] = byte & 0b00000001 == 0b00000001 ? 1 : 0
60
- self[:oui] = str[1,2].unpack("n").first
61
- self
62
- end
63
-
64
- end
3
+ # EthOui is the Organizationally Unique Identifier portion of a MAC address, used in EthHeader.
4
+ #
5
+ # See the OUI list at http://standards.ieee.org/regauth/oui/oui.txt
6
+ #
7
+ # ==== Header Definition
8
+ #
9
+ # Fixnum :b0
10
+ # Fixnum :b1
11
+ # Fixnum :b2
12
+ # Fixnum :b3
13
+ # Fixnum :b4
14
+ # Fixnum :b5
15
+ # Fixnum :local
16
+ # Fixnum :multicast
17
+ # Int16 :oui, Default: 0x1ac5 :)
18
+ class EthOui < Struct.new(:b5, :b4, :b3, :b2, :b1, :b0, :local, :multicast, :oui)
19
+
20
+ # EthOui is unusual in that the bit values do not enjoy StructFu typing.
21
+ def initialize(args={})
22
+ args[:local] ||= 0
23
+ args[:oui] ||= 0x1ac # :)
24
+ args.each_pair {|k,v| args[k] = 0 unless v}
25
+ super(args[:b5], args[:b4], args[:b3], args[:b2],
26
+ args[:b1], args[:b0], args[:local], args[:multicast],
27
+ args[:oui])
28
+ end
29
+
30
+ # Returns the object in string form.
31
+ def to_s
32
+ byte = 0
33
+ byte += 0b10000000 if b5.to_i == 1
34
+ byte += 0b01000000 if b4.to_i == 1
35
+ byte += 0b00100000 if b3.to_i == 1
36
+ byte += 0b00010000 if b2.to_i == 1
37
+ byte += 0b00001000 if b1.to_i == 1
38
+ byte += 0b00000100 if b0.to_i == 1
39
+ byte += 0b00000010 if local.to_i == 1
40
+ byte += 0b00000001 if multicast.to_i == 1
41
+ [byte,oui].pack("Cn")
42
+ end
43
+
44
+ # Reads a string to populate the object.
45
+ def read(str)
46
+ force_binary(str)
47
+ return self if str.nil?
48
+ if 1.respond_to? :ord
49
+ byte = str[0].ord
50
+ else
51
+ byte = str[0]
52
+ end
53
+ self[:b5] = byte & 0b10000000 == 0b10000000 ? 1 : 0
54
+ self[:b4] = byte & 0b01000000 == 0b01000000 ? 1 : 0
55
+ self[:b3] = byte & 0b00100000 == 0b00100000 ? 1 : 0
56
+ self[:b2] = byte & 0b00010000 == 0b00010000 ? 1 : 0
57
+ self[:b1] = byte & 0b00001000 == 0b00001000 ? 1 : 0
58
+ self[:b0] = byte & 0b00000100 == 0b00000100 ? 1 : 0
59
+ self[:local] = byte & 0b00000010 == 0b00000010 ? 1 : 0
60
+ self[:multicast] = byte & 0b00000001 == 0b00000001 ? 1 : 0
61
+ self[:oui] = str[1,2].unpack("n").first
62
+ self
63
+ end
64
+
65
+ end
65
66
 
66
67
  # EthNic is the Network Interface Controler portion of a MAC address, used in EthHeader.
67
- #
68
- # ==== Header Definition
69
- #
70
- # Fixnum :n1
71
- # Fixnum :n2
72
- # Fixnum :n3
73
- #
74
- class EthNic < Struct.new(:n0, :n1, :n2)
75
-
76
- # EthNic does not enjoy StructFu typing.
77
- def initialize(args={})
78
- args.each_pair {|k,v| args[k] = 0 unless v}
79
- super(args[:n0], args[:n1], args[:n2])
80
- end
81
-
82
- # Returns the object in string form.
83
- def to_s
84
- [n0,n1,n2].map {|x| x.to_i}.pack("C3")
85
- end
86
-
87
- # Reads a string to populate the object.
88
- def read(str)
89
- force_binary(str)
90
- return self if str.nil?
91
- self[:n0], self[:n1], self[:n2] = str[0,3].unpack("C3")
92
- self
93
- end
94
-
95
- end
96
-
97
- # EthMac is the combination of an EthOui and EthNic, used in EthHeader.
98
- #
99
- # ==== Header Definition
100
- #
101
- # EthOui :oui # See EthOui
102
- # EthNic :nic # See EthNic
103
- class EthMac < Struct.new(:oui, :nic)
104
-
105
- def initialize(args={})
106
- super(
107
- EthOui.new.read(args[:oui]),
108
- EthNic.new.read(args[:nic]))
109
- end
110
-
111
- # Returns the object in string form.
112
- def to_s
113
- "#{self[:oui]}#{self[:nic]}"
114
- end
115
-
116
- # Reads a string to populate the object.
117
- def read(str)
118
- force_binary(str)
119
- return self if str.nil?
120
- self.oui.read str[0,3]
121
- self.nic.read str[3,3]
122
- self
123
- end
124
-
125
- end
126
-
127
- # EthHeader is a complete Ethernet struct, used in EthPacket.
128
- # It's the base header for all other protocols, such as IPHeader,
129
- # TCPHeader, etc.
130
- #
131
- # For more on the construction on MAC addresses, see
132
- # http://en.wikipedia.org/wiki/MAC_address
133
- #
134
- # TODO: Need to come up with a good way of dealing with vlan
135
- # tagging. Having a usually empty struct member seems weird,
136
- # but there may not be another way to do it if I want to preserve
137
- # the Eth-ness of vlan-tagged 802.1Q packets. Also, may as well
138
- # deal with 0x88a8 as well (http://en.wikipedia.org/wiki/802.1ad)
139
- #
140
- # ==== Header Definition
141
- #
142
- # EthMac :eth_dst # See EthMac
143
- # EthMac :eth_src # See EthMac
144
- # Int16 :eth_proto, Default: 0x8000 # IP 0x0800, Arp 0x0806
145
- # String :body
146
- class EthHeader < Struct.new(:eth_dst, :eth_src, :eth_proto, :body)
147
- include StructFu
148
-
149
- def initialize(args={})
150
- super(
151
- EthMac.new.read(args[:eth_dst]),
152
- EthMac.new.read(args[:eth_src]),
153
- Int16.new(args[:eth_proto] || 0x0800),
154
- StructFu::String.new.read(args[:body])
155
- )
156
- end
157
-
158
- # Setter for the Ethernet destination address.
159
- def eth_dst=(i); typecast(i); end
160
- # Getter for the Ethernet destination address.
161
- def eth_dst; self[:eth_dst].to_s; end
162
- # Setter for the Ethernet source address.
163
- def eth_src=(i); typecast(i); end
164
- # Getter for the Ethernet source address.
165
- def eth_src; self[:eth_src].to_s; end
166
- # Setter for the Ethernet protocol number.
167
- def eth_proto=(i); typecast(i); end
168
- # Getter for the Ethernet protocol number.
169
- def eth_proto; self[:eth_proto].to_i; end
170
-
171
- # Returns the object in string form.
172
- def to_s
173
- self.to_a.map {|x| x.to_s}.join
174
- end
175
-
176
- # Reads a string to populate the object.
177
- def read(str)
178
- force_binary(str)
179
- return self if str.nil?
180
- self[:eth_dst].read str[0,6]
181
- self[:eth_src].read str[6,6]
182
- self[:eth_proto].read str[12,2]
183
- self[:body].read str[14,str.size]
184
- self
185
- end
186
-
187
- # Converts a readable MAC (11:22:33:44:55:66) to a binary string.
188
- # Readable MAC's may be split on colons, dots, spaces, or underscores.
189
- #
190
- # irb> PacketFu::EthHeader.mac2str("11:22:33:44:55:66")
191
- #
192
- # #=> "\021\"3DUf"
193
- def self.mac2str(mac)
194
- if mac.split(/[:\x2d\x2e\x5f]+/).size == 6
195
- ret = mac.split(/[:\x2d\x2e\x20\x5f]+/).collect {|x| x.to_i(16)}.pack("C6")
196
- else
197
- raise ArgumentError, "Unkown format for mac address."
198
- end
199
- return ret
200
- end
201
-
202
- # Converts a binary string to a readable MAC (11:22:33:44:55:66).
203
- #
204
- # irb> PacketFu::EthHeader.str2mac("\x11\x22\x33\x44\x55\x66")
205
- #
206
- # #=> "11:22:33:44:55:66"
207
- def self.str2mac(mac='')
208
- if mac.to_s.size == 6 && mac.kind_of?(::String)
209
- ret = mac.unpack("C6").map {|x| sprintf("%02x",x)}.join(":")
210
- end
211
- end
212
-
213
- # Sets the source MAC address in a more readable way.
214
- def eth_saddr=(mac)
215
- mac = EthHeader.mac2str(mac)
216
- self[:eth_src].read mac
217
- self[:eth_src]
218
- end
219
-
220
- # Gets the source MAC address in a more readable way.
221
- def eth_saddr
222
- EthHeader.str2mac(self[:eth_src].to_s)
223
- end
224
-
225
- # Set the destination MAC address in a more readable way.
226
- def eth_daddr=(mac)
227
- mac = EthHeader.mac2str(mac)
228
- self[:eth_dst].read mac
229
- self[:eth_dst]
230
- end
231
-
232
- # Gets the destination MAC address in a more readable way.
233
- def eth_daddr
234
- EthHeader.str2mac(self[:eth_dst].to_s)
235
- end
236
-
237
- # Readability aliases
238
-
239
- alias :eth_dst_readable :eth_daddr
240
- alias :eth_src_readable :eth_saddr
241
-
242
- def eth_proto_readable
243
- "0x%04x" % eth_proto
244
- end
245
-
246
- end
68
+ #
69
+ # ==== Header Definition
70
+ #
71
+ # Fixnum :n1
72
+ # Fixnum :n2
73
+ # Fixnum :n3
74
+ #
75
+ class EthNic < Struct.new(:n0, :n1, :n2)
76
+
77
+ # EthNic does not enjoy StructFu typing.
78
+ def initialize(args={})
79
+ args.each_pair {|k,v| args[k] = 0 unless v}
80
+ super(args[:n0], args[:n1], args[:n2])
81
+ end
82
+
83
+ # Returns the object in string form.
84
+ def to_s
85
+ [n0,n1,n2].map {|x| x.to_i}.pack("C3")
86
+ end
87
+
88
+ # Reads a string to populate the object.
89
+ def read(str)
90
+ force_binary(str)
91
+ return self if str.nil?
92
+ self[:n0], self[:n1], self[:n2] = str[0,3].unpack("C3")
93
+ self
94
+ end
95
+
96
+ end
97
+
98
+ # EthMac is the combination of an EthOui and EthNic, used in EthHeader.
99
+ #
100
+ # ==== Header Definition
101
+ #
102
+ # EthOui :oui # See EthOui
103
+ # EthNic :nic # See EthNic
104
+ class EthMac < Struct.new(:oui, :nic)
105
+
106
+ def initialize(args={})
107
+ super(
108
+ EthOui.new.read(args[:oui]),
109
+ EthNic.new.read(args[:nic]))
110
+ end
111
+
112
+ # Returns the object in string form.
113
+ def to_s
114
+ "#{self[:oui]}#{self[:nic]}"
115
+ end
116
+
117
+ # Reads a string to populate the object.
118
+ def read(str)
119
+ force_binary(str)
120
+ return self if str.nil?
121
+ self.oui.read str[0,3]
122
+ self.nic.read str[3,3]
123
+ self
124
+ end
125
+
126
+ end
127
+
128
+ # EthHeader is a complete Ethernet struct, used in EthPacket.
129
+ # It's the base header for all other protocols, such as IPHeader,
130
+ # TCPHeader, etc.
131
+ #
132
+ # For more on the construction on MAC addresses, see
133
+ # http://en.wikipedia.org/wiki/MAC_address
134
+ #
135
+ # TODO: Need to come up with a good way of dealing with vlan
136
+ # tagging. Having a usually empty struct member seems weird,
137
+ # but there may not be another way to do it if I want to preserve
138
+ # the Eth-ness of vlan-tagged 802.1Q packets. Also, may as well
139
+ # deal with 0x88a8 as well (http://en.wikipedia.org/wiki/802.1ad)
140
+ #
141
+ # ==== Header Definition
142
+ #
143
+ # EthMac :eth_dst # See EthMac
144
+ # EthMac :eth_src # See EthMac
145
+ # Int16 :eth_proto, Default: 0x8000 # IP 0x0800, Arp 0x0806
146
+ # String :body
147
+ class EthHeader < Struct.new(:eth_dst, :eth_src, :eth_proto, :body)
148
+ include StructFu
149
+
150
+ def initialize(args={})
151
+ super(
152
+ EthMac.new.read(args[:eth_dst]),
153
+ EthMac.new.read(args[:eth_src]),
154
+ Int16.new(args[:eth_proto] || 0x0800),
155
+ StructFu::String.new.read(args[:body])
156
+ )
157
+ end
158
+
159
+ # Setter for the Ethernet destination address.
160
+ def eth_dst=(i); typecast(i); end
161
+ # Getter for the Ethernet destination address.
162
+ def eth_dst; self[:eth_dst].to_s; end
163
+ # Setter for the Ethernet source address.
164
+ def eth_src=(i); typecast(i); end
165
+ # Getter for the Ethernet source address.
166
+ def eth_src; self[:eth_src].to_s; end
167
+ # Setter for the Ethernet protocol number.
168
+ def eth_proto=(i); typecast(i); end
169
+ # Getter for the Ethernet protocol number.
170
+ def eth_proto; self[:eth_proto].to_i; end
171
+
172
+ # Returns the object in string form.
173
+ def to_s
174
+ self.to_a.map {|x| x.to_s}.join
175
+ end
176
+
177
+ # Reads a string to populate the object.
178
+ def read(str)
179
+ force_binary(str)
180
+ return self if str.nil?
181
+ self[:eth_dst].read str[0,6]
182
+ self[:eth_src].read str[6,6]
183
+ self[:eth_proto].read str[12,2]
184
+ self[:body].read str[14,str.size]
185
+ self
186
+ end
187
+
188
+ # Converts a readable MAC (11:22:33:44:55:66) to a binary string.
189
+ # Readable MAC's may be split on colons, dots, spaces, or underscores.
190
+ #
191
+ # irb> PacketFu::EthHeader.mac2str("11:22:33:44:55:66")
192
+ #
193
+ # #=> "\021\"3DUf"
194
+ def self.mac2str(mac)
195
+ if mac.split(/[:\x2d\x2e\x5f]+/).size == 6
196
+ ret = mac.split(/[:\x2d\x2e\x20\x5f]+/).collect {|x| x.to_i(16)}.pack("C6")
197
+ else
198
+ raise ArgumentError, "Unkown format for mac address."
199
+ end
200
+ return ret
201
+ end
202
+
203
+ # Converts a binary string to a readable MAC (11:22:33:44:55:66).
204
+ #
205
+ # irb> PacketFu::EthHeader.str2mac("\x11\x22\x33\x44\x55\x66")
206
+ #
207
+ # #=> "11:22:33:44:55:66"
208
+ def self.str2mac(mac='')
209
+ if mac.to_s.size == 6 && mac.kind_of?(::String)
210
+ ret = mac.unpack("C6").map {|x| sprintf("%02x",x)}.join(":")
211
+ end
212
+ end
213
+
214
+ # Sets the source MAC address in a more readable way.
215
+ def eth_saddr=(mac)
216
+ mac = EthHeader.mac2str(mac)
217
+ self[:eth_src].read mac
218
+ self[:eth_src]
219
+ end
220
+
221
+ # Gets the source MAC address in a more readable way.
222
+ def eth_saddr
223
+ EthHeader.str2mac(self[:eth_src].to_s)
224
+ end
225
+
226
+ # Set the destination MAC address in a more readable way.
227
+ def eth_daddr=(mac)
228
+ mac = EthHeader.mac2str(mac)
229
+ self[:eth_dst].read mac
230
+ self[:eth_dst]
231
+ end
232
+
233
+ # Gets the destination MAC address in a more readable way.
234
+ def eth_daddr
235
+ EthHeader.str2mac(self[:eth_dst].to_s)
236
+ end
237
+
238
+ # Readability aliases
239
+
240
+ alias :eth_dst_readable :eth_daddr
241
+ alias :eth_src_readable :eth_saddr
242
+
243
+ def eth_proto_readable
244
+ "0x%04x" % eth_proto
245
+ end
246
+
247
+ end
247
248
  end