1# Copyright (C) 2018 antoine.torre <torreantoine1@gmail.com>
2##
3# This program is published under a GPLv2 license
4
5
6# scapy.contrib.description = ATA Over Internet
7# scapy.contrib.status = loads
8
9from scapy.packet import Packet, bind_layers
10from scapy.fields import FlagsField, XByteField, ByteField, XShortField, \
11    ShortField, StrLenField, BitField, BitEnumField, ByteEnumField, \
12    FieldLenField, PacketListField, FieldListField, MACField, PacketField, \
13    ConditionalField, XIntField
14from scapy.layers.l2 import Ether
15from scapy.data import ETHER_ANY
16
17
18class IssueATACommand(Packet):
19    name = "Issue ATA Command"
20    fields_desc = [FlagsField("flags", 0, 8, "zezdzzaw"),
21                   XByteField("err_feature", 0),
22                   ByteField("sector_count", 1),
23                   XByteField("cmd_status", 0xec),
24                   XByteField("lba0", 0),
25                   XByteField("lba1", 0),
26                   XByteField("lba2", 0),
27                   XByteField("lba3", 0),
28                   XByteField("lba4", 0),
29                   XByteField("lba5", 0),
30                   XShortField("reserved", 0),
31                   StrLenField("data", "",
32                               length_from=lambda x: x.sector_count * 512)]
33
34    def extract_padding(self, s):
35        return "", s
36
37
38class QueryConfigInformation(Packet):
39    name = "Query Config Information"
40    fields_desc = [ShortField("buffer_count", 0),
41                   ShortField("firmware", 0),
42                   ByteField("sector_count", 0),
43                   BitField("aoe", 0, 4),
44                   BitEnumField("ccmd", 0, 4, {0: "Read config string",
45                                               1: "Test config string",
46                                               2: "Test config string prefix",
47                                               3: "Set config string",
48                                               4: "Force set config string"}),
49                   FieldLenField("config_length", None, length_of="config"),
50                   StrLenField("config", None,
51                               length_from=lambda x: x.config_length)]
52
53    def extract_padding(self, s):
54        return "", s
55
56
57class Directive(Packet):
58    name = "Directive"
59    fields_desc = [ByteField("reserved", 0),
60                   ByteEnumField("dcmd", 0,
61                                 {0: "No directive",
62                                  1: "Add mac address to mask list",
63                                  2: "Delete mac address from mask list"}),
64                   MACField("mac_addr", ETHER_ANY)]
65
66
67class MacMaskList(Packet):
68    name = "Mac Mask List"
69    fields_desc = [ByteField("reserved", 0),
70                   ByteEnumField("mcmd", 0, {0: "Read Mac Mask List",
71                                             1: "Edit Mac Mask List"}),
72                   ByteEnumField("merror", 0, {0: "",
73                                               1: "Unspecified error",
74                                               2: "Bad dcmd directive",
75                                               3: "Mask List Full"}),
76                   FieldLenField("dir_count", None, count_of="directives"),
77                   PacketListField("directives", None, Directive,
78                                   count_from=lambda pkt: pkt.dir_count)]
79
80    def extract_padding(self, s):
81        return "", s
82
83
84class ReserveRelease(Packet):
85    name = "Reserve / Release"
86    fields_desc = [ByteEnumField("rcmd", 0, {0: "Read Reserve List",
87                                             1: "Set Reserve List",
88                                             2: "Force Set Reserve List"}),
89                   FieldLenField("nb_mac", None, count_of="mac_addrs"),
90                   FieldListField("mac_addrs", None, MACField("", ETHER_ANY),
91                                  count_from=lambda pkt: pkt.nb_mac)]
92
93    def extract_padding(self, s):
94        return "", s
95
96
97class AOE(Packet):
98    name = "ATA over Ethernet"
99    fields_desc = [BitField("version", 1, 4),
100                   FlagsField("flags", 0, 4, ["Response", "Error",
101                                              "r1", "r2"]),
102                   ByteEnumField("error", 0, {1: "Unrecognized command code",
103                                              2: "Bad argument parameter",
104                                              3: "Device unavailable",
105                                              4: "Config string present",
106                                              5: "Unsupported exception",
107                                              6: "Target is reserved"}),
108                   XShortField("major", 0xFFFF),
109                   XByteField("minor", 0xFF),
110                   ByteEnumField("cmd", 1, {0: "Issue ATA Command",
111                                            1: "Query Config Information",
112                                            2: "Mac Mask List",
113                                            3: "Reserve / Release"}),
114                   XIntField("tag", 0),
115                   ConditionalField(PacketField("i_ata_cmd", IssueATACommand(),
116                                                IssueATACommand),
117                                    lambda x: x.cmd == 0),
118                   ConditionalField(PacketField("q_conf_info",
119                                                QueryConfigInformation(),
120                                                QueryConfigInformation),
121                                    lambda x: x.cmd == 1),
122                   ConditionalField(PacketField("mac_m_list", MacMaskList(),
123                                                MacMaskList),
124                                    lambda x: x.cmd == 2),
125                   ConditionalField(PacketField("res_rel", ReserveRelease(),
126                                                ReserveRelease),
127                                    lambda x: x.cmd == 3)]
128
129    def extract_padding(self, s):
130        return "", s
131
132
133bind_layers(Ether, AOE, type=0x88A2)
134bind_layers(AOE, IssueATACommand, cmd=0)
135bind_layers(AOE, QueryConfigInformation, cmd=1)
136bind_layers(AOE, MacMaskList, cmd=2)
137bind_layers(AOE, ReserveRelease, cmd=3)
138