1 # This file is part of Scapy 2 # Scapy is free software: you can redistribute it and/or modify 3 # it under the terms of the GNU General Public License as published by 4 # the Free Software Foundation, either version 2 of the License, or 5 # any later version. 6 # 7 # Scapy is distributed in the hope that it will be useful, 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 # GNU General Public License for more details. 11 # 12 # You should have received a copy of the GNU General Public License 13 # along with Scapy. If not, see <http://www.gnu.org/licenses/>. 14 15 # Copyright (C) 2016 Gauthier Sebaux 16 17 # scapy.contrib.description = ProfinetIO base layer 18 # scapy.contrib.status = loads 19 20 """ 21 A simple and non exhaustive Profinet IO layer for scapy 22 """ 23 24 # Scapy imports 25 from __future__ import absolute_import 26 from scapy.all import Packet, bind_layers, Ether, UDP 27 from scapy.fields import XShortEnumField 28 from scapy.modules.six.moves import range 29 30 # Some constants 31 PNIO_FRAME_IDS = { 32 0x0020:"PTCP-RTSyncPDU-followup", 33 0x0080:"PTCP-RTSyncPDU", 34 0xFC01:"Alarm High", 35 0xFE01:"Alarm Low", 36 0xFEFC:"DCP-Hello-Req", 37 0xFEFD:"DCP-Get-Set", 38 0xFEFE:"DCP-Identify-ReqPDU", 39 0xFEFF:"DCP-Identify-ResPDU", 40 0xFF00:"PTCP-AnnouncePDU", 41 0xFF20:"PTCP-FollowUpPDU", 42 0xFF40:"PTCP-DelayReqPDU", 43 0xFF41:"PTCP-DelayResPDU-followup", 44 0xFF42:"PTCP-DelayFuResPDU", 45 0xFF43:"PTCP-DelayResPDU", 46 } 47 for i in range(0x0100, 0x1000): 48 PNIO_FRAME_IDS[i] = "RT_CLASS_3" 49 for i in range(0x8000, 0xC000): 50 PNIO_FRAME_IDS[i] = "RT_CLASS_1" 51 for i in range(0xC000, 0xFC00): 52 PNIO_FRAME_IDS[i] = "RT_CLASS_UDP" 53 for i in range(0xFF80, 0xFF90): 54 PNIO_FRAME_IDS[i] = "FragmentationFrameID" 55 56 ################# 57 ## PROFINET IO ## 58 ################# 59 60 class ProfinetIO(Packet): 61 """Basic PROFINET IO dispatcher""" 62 fields_desc = [XShortEnumField("frameID", 0, PNIO_FRAME_IDS)] 63 overload_fields = { 64 Ether: {"type": 0x8892}, 65 UDP: {"dport": 0x8892}, 66 } 67 68 def guess_payload_class(self, payload): 69 # For frameID in the RT_CLASS_* range, use the RTC packet as payload 70 if (self.frameID >= 0x0100 and self.frameID < 0x1000) or \ 71 (self.frameID >= 0x8000 and self.frameID < 0xFC00): 72 from scapy.contrib.pnio_rtc import PNIORealTime 73 return PNIORealTime 74 else: 75 return Packet.guess_payload_class(self, payload) 76 77 bind_layers(Ether, ProfinetIO, type=0x8892) 78 bind_layers(UDP, ProfinetIO, dport=0x8892) 79 80