1# This file is part of Scapy. 2# See http://www.secdev.org/projects/scapy for more information. 3# 4# Scapy is free software: you can redistribute it and/or modify 5# it under the terms of the GNU General Public License as published by 6# the Free Software Foundation, either version 2 of the License, or 7# (at your option) any later version. 8# 9# Scapy is distributed in the hope that it will be useful, 10# but WITHOUT ANY WARRANTY; without even the implied warranty of 11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12# GNU General Public License for more details. 13# 14# You should have received a copy of the GNU General Public License 15# along with Scapy. If not, see <http://www.gnu.org/licenses/>. 16# 17# Copyright (C) 2020 Rahul Jadhav <nyrahul@gmail.com> 18 19# RFC 6551 20# scapy.contrib.description = Routing Metrics used for Path Calc in LLNs 21# scapy.contrib.status = loads 22 23""" 24RFC 6551 - Routing Metrics Used for Path Calculation in LLNs 25 26+----------------------------+ 27| Metrics & Constraint Types | 28+----------------------------+ 29| DAGMC Option | 30+----------------------------+ 31| RPL-DIO | 32+----------------------------+ 33""" 34 35import struct 36from scapy.compat import orb 37from scapy.packet import Packet 38from scapy.fields import ByteEnumField, ByteField, ShortField, BitField, \ 39 BitEnumField, FieldLenField, StrLenField, IntField 40from scapy.layers.inet6 import _PhantomAutoPadField, _OptionsField 41from scapy.contrib.rpl import RPLOPTSSTR, RPLOPTS 42 43 44class _DAGMetricContainer(Packet): 45 name = 'Dummy DAG Metric container' 46 47 def post_build(self, pkt, pay): 48 pkt += pay 49 tmp_len = self.len 50 if self.len is None: 51 tmp_len = len(pkt) - 2 52 pkt = pkt[:1] + struct.pack("B", tmp_len) + pkt[2:] 53 return pkt 54 55 56DAGMC_OBJTYPE = {1: "Node State and Attributes", 57 2: "Node Energy", 58 3: "Hop Count", 59 4: "Link Throughput", 60 5: "Link Latency", 61 6: "Link Quality Level", 62 7: "Link ETX", 63 8: "Link Color"} 64 65 66class DAGMCObjUnknown(Packet): 67 """ 68 Dummy unknown metric/constraint 69 """ 70 name = 'Unknown DAGMC Object Option' 71 fields_desc = [ByteEnumField("otype", 3, DAGMC_OBJTYPE), 72 FieldLenField("olen", None, length_of="odata", fmt="B"), 73 StrLenField("odata", "", 74 length_from=lambda pkt: pkt.olen)] 75 76 @classmethod 77 def dispatch_hook(cls, _pkt=None, *_, **kargs): 78 """ 79 Dispatch hook for DAGMC sub-fields 80 """ 81 if _pkt: 82 opt_type = orb(_pkt[0]) # Option type 83 if opt_type in DAGMC_CLS: 84 return DAGMC_CLS[opt_type] 85 return cls 86 87 88AGG_RTMETRIC = {0: "additive", 89 1: "maximum", 90 2: "minimum", 91 3: "multiplicative"} # RFC 6551 92 93 94class DAGMCObj(Packet): 95 """ 96 Set the length field in DAG Metric Constraint Control Option 97 """ 98 name = 'Dummy DAG MC Object' 99 # RFC 6551 - 2.1 100 fields_desc = [ByteEnumField("otype", 0, DAGMC_OBJTYPE), 101 BitField("resflags", 0, 5), 102 BitField("P", 0, 1), 103 BitField("C", 0, 1), 104 BitField("O", 0, 1), 105 BitField("R", 0, 1), 106 BitEnumField("A", 0, 3, AGG_RTMETRIC), 107 BitField("prec", 0, 4), 108 ByteField("len", None)] 109 110 def post_build(self, pkt, pay): 111 pkt += pay 112 tmp_len = self.len 113 if self.len is None: 114 tmp_len = len(pkt) - 4 115 pkt = pkt[:3] + struct.pack("B", tmp_len) + pkt[4:] 116 return pkt 117 118 119class RPLDAGMCNSA(DAGMCObj): 120 """ 121 DAG Metric: Node State and Attributes 122 """ 123 name = "Node State and Attributes" 124 otype = 1 125 # RFC 6551 - 3.1 126 fields_desc = DAGMCObj.fields_desc + [ 127 # NSA Object Body Format 128 ByteField("res", 0), 129 BitField("flags", 0, 6), 130 BitField("Agg", 0, 1), # A 131 BitField("Overload", 0, 1), # O 132 ] 133 134 135class RPLDAGMCNodeEnergy(DAGMCObj): 136 """ 137 DAG Metric: Node Energy 138 """ 139 name = "Node Energy" 140 otype = 2 141 # RFC 6551 - 3.2 142 fields_desc = DAGMCObj.fields_desc + [ 143 # NE Sub-Object Format 144 BitField("flags", 0, 4), 145 BitField("I", 0, 1), 146 BitField("T", 0, 2), 147 BitField("E", 0, 1), 148 ByteField("E_E", 0) 149 ] 150 151 152class RPLDAGMCHopCount(DAGMCObj): 153 """ 154 DAG Metric: Hop Count 155 """ 156 name = "Hop Count" 157 otype = 3 158 # RFC 6551 - 3.3 159 fields_desc = DAGMCObj.fields_desc + [ 160 # Sub-Object Format 161 BitField("res", 0, 4), 162 BitField("flags", 0, 4), 163 ByteField("HopCount", 1) 164 ] 165 166 167class RPLDAGMCLinkThroughput(DAGMCObj): 168 """ 169 DAG Metric: Link Throughput 170 """ 171 name = "Link Throughput" 172 otype = 4 173 # RFC 6551 - 4.1 174 fields_desc = DAGMCObj.fields_desc + [ 175 # Sub-Object Format 176 IntField("Throughput", 1) 177 ] 178 179 180class RPLDAGMCLinkLatency(DAGMCObj): 181 """ 182 DAG Metric: Link Latency 183 """ 184 name = "Link Latency" 185 otype = 5 186 # RFC 6551 - 4.2 187 fields_desc = DAGMCObj.fields_desc + [ 188 # NE Sub-Object Format 189 IntField("Latency", 1) 190 ] 191 192 193class RPLDAGMCLinkQualityLevel(DAGMCObj): 194 """ 195 DAG Metric: Link Quality Level (LQL) 196 """ 197 name = "Link Quality Level" 198 otype = 6 199 # RFC 6551 - 4.3.1 200 fields_desc = DAGMCObj.fields_desc + [ 201 # Sub-Object Format 202 ByteField("res", 0), 203 BitField("val", 0, 3), 204 BitField("counter", 0, 5) 205 ] 206 207 208class RPLDAGMCLinkETX(DAGMCObj): 209 """ 210 DAG Metric: Link ETX 211 """ 212 name = "Link ETX" 213 otype = 7 214 # RFC 6551 - 4.3.2 215 fields_desc = DAGMCObj.fields_desc + [ 216 # Sub-Object Format 217 ShortField("ETX", 1) 218 ] 219 220 221# Note: Wireshark shows warning decoding LinkColor. 222# This seems to be wireshark issue! 223class RPLDAGMCLinkColor(DAGMCObj): 224 """ 225 DAG Metric: Link Color 226 """ 227 name = "Link Color" 228 otype = 8 229 # RFC 6551 - 4.4.1 230 fields_desc = DAGMCObj.fields_desc + [ 231 # Sub-Object Format 232 ByteField("res", 0), 233 BitField("color", 1, 10), 234 BitField("counter", 1, 6) 235 ] 236 237 238DAGMC_CLS = {1: RPLDAGMCNSA, 239 2: RPLDAGMCNodeEnergy, 240 3: RPLDAGMCHopCount, 241 4: RPLDAGMCLinkThroughput, 242 5: RPLDAGMCLinkLatency, 243 6: RPLDAGMCLinkQualityLevel, 244 7: RPLDAGMCLinkETX, 245 8: RPLDAGMCLinkColor} 246 247 248class RPLOptDAGMC(_DAGMetricContainer): 249 """ 250 Control Option: DAG Metric Container 251 """ 252 name = "DAG Metric Container" 253 fields_desc = [ByteEnumField("otype", 2, RPLOPTSSTR), 254 ByteField("len", None), 255 _PhantomAutoPadField("autopad", 0), 256 _OptionsField("options", [], DAGMCObjUnknown, 8, 257 length_from=lambda pkt: 8 * pkt.len)] 258 259 260# https://www.iana.org/assignments/rpl/rpl.xhtml#control-message-options 261RPLOPTS.update({2: RPLOptDAGMC}) 262