1## This file is part of Scapy 2## See http://www.secdev.org/projects/scapy for more informations 3## Copyright (C) Philippe Biondi <phil@secdev.org> 4## This program is published under a GPLv2 license 5## Netflow V5 appended by spaceB0x and Guillaume Valadon 6## Netflow V9 appended ny Gabriel Potter 7 8""" 9Cisco NetFlow protocol v1, v5 and v9 10 11 12 13- NetflowV9 build example: 14 15pkt = NetflowHeader()/\ 16 NetflowHeaderV9()/\ 17 NetflowFlowsetV9(templates=[ 18 NetflowTemplateV9(templateID=258, template_fields=[ 19 NetflowTemplateFieldV9(fieldType=1), 20 NetflowTemplateFieldV9(fieldType=62), 21 ]), 22 NetflowTemplateV9(templateID=257, template_fields=[ 23 NetflowTemplateFieldV9(fieldType=1), 24 NetflowTemplateFieldV9(fieldType=62), 25 ]), 26 ])/NetflowDataflowsetV9(templateID=258, records=[ 27 NetflowRecordV9(fieldValue=b"\x01\x02\x03\x05"), 28 NetflowRecordV9(fieldValue=b"\x05\x03\x02\x01\x04\x03\x02\x01\x04\x03\x02\x01\x04\x03\x02\x01"), 29 ])/NetflowDataflowsetV9(templateID=257, records=[ 30 NetflowRecordV9(fieldValue=b"\x01\x02\x03\x04"), 31 NetflowRecordV9(fieldValue=b"\x04\x03\x02\x01\x04\x03\x02\x01\x04\x03\x02\x01\x04\x03\x02\x01"), 32 ])/NetflowOptionsFlowsetV9(templateID=256, scopes=[NetflowOptionsFlowsetScopeV9(scopeFieldType=1, scopeFieldlength=4), 33 NetflowOptionsFlowsetScopeV9(scopeFieldType=1, scopeFieldlength=3)], 34 options=[NetflowOptionsFlowsetOptionV9(optionFieldType=1, optionFieldlength=2), 35 NetflowOptionsFlowsetOptionV9(optionFieldType=1, optionFieldlength=1)])/\ 36 NetflowOptionsDataRecordV9(templateID=256, records=[NetflowOptionsRecordScopeV9(fieldValue=b"\x01\x02\x03\x04"), 37 NetflowOptionsRecordScopeV9(fieldValue=b"\x01\x02\x03"), 38 NetflowOptionsRecordOptionV9(fieldValue=b"\x01\x02"), 39 NetflowOptionsRecordOptionV9(fieldValue=b"\x01")]) 40""" 41 42 43from scapy.fields import * 44from scapy.packet import * 45from scapy.data import IP_PROTOS 46from scapy.layers.inet import UDP 47 48 49class NetflowHeader(Packet): 50 name = "Netflow Header" 51 fields_desc = [ ShortField("version", 1) ] 52 53 54########################################### 55### Netflow Version 1 56########################################### 57 58 59class NetflowHeaderV1(Packet): 60 name = "Netflow Header v1" 61 fields_desc = [ ShortField("count", 0), 62 IntField("sysUptime", 0), 63 UTCTimeField("unixSecs", 0), 64 UTCTimeField("unixNanoSeconds", 0, use_nano=True) ] 65 66 67class NetflowRecordV1(Packet): 68 name = "Netflow Record v1" 69 fields_desc = [ IPField("ipsrc", "0.0.0.0"), 70 IPField("ipdst", "0.0.0.0"), 71 IPField("nexthop", "0.0.0.0"), 72 ShortField("inputIfIndex", 0), 73 ShortField("outpuIfIndex", 0), 74 IntField("dpkts", 0), 75 IntField("dbytes", 0), 76 IntField("starttime", 0), 77 IntField("endtime", 0), 78 ShortField("srcport", 0), 79 ShortField("dstport", 0), 80 ShortField("padding", 0), 81 ByteField("proto", 0), 82 ByteField("tos", 0), 83 IntField("padding1", 0), 84 IntField("padding2", 0) ] 85 86 87bind_layers( NetflowHeader, NetflowHeaderV1, version=1) 88bind_layers( NetflowHeaderV1, NetflowRecordV1 ) 89bind_layers( NetflowRecordV1, NetflowRecordV1 ) 90 91 92######################################### 93### Netflow Version 5 94######################################### 95 96 97class NetflowHeaderV5(Packet): 98 name = "Netflow Header v5" 99 fields_desc = [ ShortField("count", 0), 100 IntField("sysUptime", 0), 101 UTCTimeField("unixSecs", 0), 102 UTCTimeField("unixNanoSeconds", 0, use_nano=True), 103 IntField("flowSequence",0), 104 ByteField("engineType", 0), 105 ByteField("engineID", 0), 106 ShortField("samplingInterval", 0) ] 107 108 109class NetflowRecordV5(Packet): 110 name = "Netflow Record v5" 111 fields_desc = [ IPField("src", "127.0.0.1"), 112 IPField("dst", "127.0.0.1"), 113 IPField("nexthop", "0.0.0.0"), 114 ShortField("input", 0), 115 ShortField("output", 0), 116 IntField("dpkts", 1), 117 IntField("dOctets", 60), 118 IntField("first", 0), 119 IntField("last", 0), 120 ShortField("srcport", 0), 121 ShortField("dstport", 0), 122 ByteField("pad1", 0), 123 FlagsField("tcpFlags", 0x2, 8, "FSRPAUEC"), 124 ByteEnumField("prot", IP_PROTOS["tcp"], IP_PROTOS), 125 ByteField("tos",0), 126 ShortField("src_as", 0), 127 ShortField("dst_as", 0), 128 ByteField("src_mask", 0), 129 ByteField("dst_mask", 0), 130 ShortField("pad2", 0)] 131 132 133bind_layers( NetflowHeader, NetflowHeaderV5, version=5) 134bind_layers( NetflowHeaderV5, NetflowRecordV5 ) 135bind_layers( NetflowRecordV5, NetflowRecordV5 ) 136 137######################################### 138### Netflow Version 9 139######################################### 140 141# https://www.ietf.org/rfc/rfc3954.txt 142 143NetflowV9TemplateFieldTypes = { 144 1: "IN_BYTES", 145 2: "IN_PKTS", 146 3: "FLOWS", 147 4: "PROTOCOL", 148 5: "TOS", 149 6: "TCP_FLAGS", 150 7: "L4_SRC_PORT", 151 8: "IPV4_SRC_ADDR", 152 9: "SRC_MASK", 153 10: "INPUT_SNMP", 154 11: "L4_DST_PORT", 155 12: "IPV4_DST_ADDR", 156 13: "DST_MASK", 157 14: "OUTPUT_SNMP", 158 15: "IPV4_NEXT_HOP", 159 16: "SRC_AS", 160 17: "DST_AS", 161 18: "BGP_IPV4_NEXT_HOP", 162 19: "MUL_DST_PKTS", 163 20: "MUL_DST_BYTES", 164 21: "LAST_SWITCHED", 165 22: "FIRST_SWITCHED", 166 23: "OUT_BYTES", 167 24: "OUT_PKTS", 168 27: "IPV6_SRC_ADDR", 169 28: "IPV6_DST_ADDR", 170 29: "IPV6_SRC_MASK", 171 30: "IPV6_DST_MASK", 172 31: "IPV6_FLOW_LABEL", 173 32: "ICMP_TYPE", 174 33: "MUL_IGMP_TYPE", 175 34: "SAMPLING_INTERVAL", 176 35: "SAMPLING_ALGORITHM", 177 36: "FLOW_ACTIVE_TIMEOUT", 178 37: "FLOW_INACTIVE_TIMEOUT", 179 38: "ENGINE_TYPE", 180 39: "ENGINE_ID", 181 40: "TOTAL_BYTES_EXP", 182 41: "TOTAL_PKTS_EXP", 183 42: "TOTAL_FLOWS_EXP", 184 46: "MPLS_TOP_LABEL_TYPE", 185 47: "MPLS_TOP_LABEL_IP_ADDR", 186 48: "FLOW_SAMPLER_ID", 187 49: "FLOW_SAMPLER_MODE", 188 50: "FLOW_SAMPLER_RANDOM_INTERVAL", 189 55: "DST_TOS", 190 56: "SRC_MAC", 191 57: "DST_MAC", 192 58: "SRC_VLAN", 193 59: "DST_VLAN", 194 60: "IP_PROTOCOL_VERSION", 195 61: "DIRECTION", 196 62: "IPV6_NEXT_HOP", 197 63: "BGP_IPV6_NEXT_HOP", 198 64: "IPV6_OPTION_HEADERS", 199 70: "MPLS_LABEL_1", 200 71: "MPLS_LABEL_2", 201 72: "MPLS_LABEL_3", 202 73: "MPLS_LABEL_4", 203 74: "MPLS_LABEL_5", 204 75: "MPLS_LABEL_6", 205 76: "MPLS_LABEL_7", 206 77: "MPLS_LABEL_8", 207 78: "MPLS_LABEL_9", 208 79: "MPLS_LABEL_10", 209 } 210 211ScopeFieldTypes = { 212 1: "System", 213 2: "Interface", 214 3: "Line card", 215 4: "Cache", 216 5: "Template", 217 } 218 219NetflowV9TemplateFieldDefaultLengths = { 220 1: 4, 221 2: 4, 222 3: 4, 223 4: 1, 224 5: 1, 225 6: 1, 226 7: 2, 227 8: 4, 228 9: 1, 229 10: 2, 230 11: 2, 231 12: 4, 232 13: 1, 233 14: 2, 234 15: 4, 235 16: 2, 236 17: 2, 237 18: 4, 238 19: 4, 239 20: 4, 240 21: 4, 241 22: 4, 242 23: 4, 243 24: 4, 244 27: 16, 245 28: 16, 246 29: 1, 247 30: 1, 248 31: 3, 249 32: 2, 250 33: 1, 251 34: 4, 252 35: 1, 253 36: 2, 254 37: 2, 255 38: 1, 256 39: 1, 257 40: 4, 258 41: 4, 259 42: 4, 260 46: 1, 261 47: 4, 262 48: 1, 263 49: 1, 264 50: 4, 265 55: 1, 266 56: 6, 267 57: 6, 268 58: 2, 269 59: 2, 270 60: 1, 271 61: 1, 272 62: 16, 273 63: 16, 274 64: 4, 275 70: 3, 276 71: 3, 277 72: 3, 278 73: 3, 279 74: 3, 280 75: 3, 281 76: 3, 282 77: 3, 283 78: 3, 284 79: 3, 285 } 286 287class NetflowHeaderV9(Packet): 288 name = "Netflow Header V9" 289 fields_desc = [ ShortField("count", 0), 290 IntField("sysUptime", 0), 291 UTCTimeField("unixSecs", 0), 292 IntField("packageSequence",0), 293 IntField("SourceID", 0) ] 294 295class NetflowTemplateFieldV9(Packet): 296 name = "Netflow Flowset Template Field V9" 297 fields_desc = [ ShortEnumField("fieldType", None, NetflowV9TemplateFieldTypes), 298 ShortField("fieldLength", 0) ] 299 def __init__(self, *args, **kwargs): 300 Packet.__init__(self, *args, **kwargs) 301 if self.fieldType != None: 302 self.fieldLength = NetflowV9TemplateFieldDefaultLengths[self.fieldType] 303 304 def default_payload_class(self, p): 305 return conf.padding_layer 306 307class NetflowTemplateV9(Packet): 308 name = "Netflow Flowset Template V9" 309 fields_desc = [ ShortField("templateID", 255), 310 FieldLenField("fieldCount", None, count_of="template_fields"), 311 PacketListField("template_fields", [], NetflowTemplateFieldV9, 312 count_from = lambda pkt: pkt.fieldCount) ] 313 314 def default_payload_class(self, p): 315 return conf.padding_layer 316 317class NetflowFlowsetV9(Packet): 318 name = "Netflow FlowSet V9" 319 fields_desc = [ ShortField("flowSetID", 0), 320 FieldLenField("length", None, length_of="templates", adjust=lambda pkt,x:x+4), 321 PacketListField("templates", [], NetflowTemplateV9, 322 length_from = lambda pkt: pkt.length-4) ] 323 324class NetflowRecordV9(Packet): 325 name = "Netflow DataFlowset Record V9" 326 fields_desc = [ StrField("fieldValue", "") ] 327 328 def default_payload_class(self, p): 329 return conf.padding_layer 330 331class NetflowDataflowsetV9(Packet): 332 name = "Netflow DataFlowSet V9" 333 fields_desc = [ ShortField("templateID", 255), 334 FieldLenField("length", None, length_of="records", adjust = lambda pkt,x:x+4), 335 PadField(PacketListField("records", [], NetflowRecordV9, 336 length_from = lambda pkt: pkt.length-4), 337 4, padwith=b"\x00") ] 338 339 @classmethod 340 def dispatch_hook(cls, _pkt=None, *args, **kargs): 341 if _pkt: 342 if _pkt[:2] == b"\x00\x01": 343 return NetflowOptionsFlowsetV9 344 return cls 345 346 def post_dissection(self, pkt): 347 # We need the whole packet to be dissected to access field def in NetflowFlowsetV9 348 root = pkt.firstlayer() 349 current = root 350 # Get all linked NetflowFlowsetV9 351 while current.payload.haslayer(NetflowFlowsetV9): 352 current = current.payload[NetflowFlowsetV9] 353 for ntv9 in current.templates: 354 current_ftl = root.getlayer(NetflowDataflowsetV9, templateID=ntv9.templateID) 355 if current_ftl: 356 # Matched 357 if len(current_ftl.records) > 1: 358 # post_dissection is not necessary 359 return 360 # All data is stored in one record, awaiting to be splitted 361 data = current_ftl.records.pop(0).fieldValue 362 res = [] 363 # Now, according to the NetflowFlowsetV9 data, re-dissect NetflowDataflowsetV9 364 for template in ntv9.template_fields: 365 _l = template.fieldLength 366 if _l: 367 res.append(NetflowRecordV9(data[:_l])) 368 data = data[_l:] 369 if data: 370 res.append(Raw(data)) 371 # Inject dissected data 372 current_ftl.records = res 373 else: 374 warning("[NetflowFlowsetV9 templateID=%s]: No matching NetflowDataflowsetV9 !" % ntv9.templateID) 375 376class NetflowOptionsFlowsetScopeV9(Packet): 377 name = "Netflow Options Template FlowSet V9 - Scope" 378 fields_desc = [ ShortEnumField("scopeFieldType", None, ScopeFieldTypes), 379 ShortField("scopeFieldlength", 0) ] 380 381 def default_payload_class(self, p): 382 return conf.padding_layer 383 384class NetflowOptionsRecordScopeV9(NetflowRecordV9): 385 name = "Netflow Options Template Record V9 - Scope" 386 387class NetflowOptionsRecordOptionV9(NetflowRecordV9): 388 name = "Netflow Options Template Record V9 - Option" 389 390class NetflowOptionsFlowsetOptionV9(Packet): 391 name = "Netflow Options Template FlowSet V9 - Option" 392 fields_desc = [ ShortEnumField("optionFieldType", None, NetflowV9TemplateFieldTypes), 393 ShortField("optionFieldlength", 0) ] 394 395 def default_payload_class(self, p): 396 return conf.padding_layer 397 398class NetflowOptionsFlowsetV9(Packet): 399 name = "Netflow Options Template FlowSet V9" 400 fields_desc = [ ShortField("flowSetID", 1), 401 LenField("length", None), 402 ShortField("templateID", 255), 403 FieldLenField("option_scope_length", None, length_of="scopes"), 404 FieldLenField("option_field_length", None, length_of="options"), 405 PacketListField("scopes", [], NetflowOptionsFlowsetScopeV9, 406 length_from = lambda pkt: pkt.option_scope_length), 407 PadField(PacketListField("options", [], NetflowOptionsFlowsetOptionV9, 408 length_from = lambda pkt: pkt.option_field_length), 409 4, padwith=b"\x00") ] 410 411class NetflowOptionsDataRecordV9(NetflowDataflowsetV9): 412 name = "Netflow Options Data Record V9" 413 fields_desc = [ ShortField("templateID", 255), 414 FieldLenField("length", None, length_of="records", adjust = lambda pkt,x:x+4), 415 PadField(PacketListField("records", [], NetflowRecordV9, 416 length_from = lambda pkt: pkt.length-4), 417 4, padwith=b"\x00") ] 418 419 def post_dissection(self, pkt): 420 options_data_record = pkt[NetflowOptionsDataRecordV9] 421 if pkt.haslayer(NetflowOptionsFlowsetV9): 422 options_flowset = pkt[NetflowOptionsFlowsetV9] 423 data = options_data_record.records.pop(0).fieldValue 424 res = [] 425 # Now, according to the NetflowOptionsFlowsetV9 data, re-dissect NetflowOptionsDataRecordV9 426 for scope in options_flowset.scopes: 427 _l = scope.scopeFieldlength 428 if _l: 429 res.append(NetflowOptionsRecordScopeV9(data[:_l])) 430 data = data[_l:] 431 432 # Now, according to the NetflowOptionsFlowsetV9 data, re-dissect NetflowOptionsDataRecordV9 433 for option in options_flowset.options: 434 _l = option.optionFieldlength 435 if _l: 436 res.append(NetflowOptionsRecordOptionV9(data[:_l])) 437 data = data[_l:] 438 if data: 439 res.append(Raw(data)) 440 # Inject dissected data 441 options_data_record.records = res 442 443bind_layers( NetflowHeader, NetflowHeaderV9, version=9 ) 444bind_layers( NetflowHeaderV9, NetflowFlowsetV9 ) 445bind_layers( NetflowFlowsetV9, NetflowDataflowsetV9 ) 446bind_layers( NetflowDataflowsetV9, NetflowDataflowsetV9 ) 447 448bind_layers( NetflowOptionsFlowsetV9, NetflowOptionsDataRecordV9 ) 449