1 #! /usr/bin/env python 2 3 # This file is part of Scapy 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 # 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 # scapy.contrib.description = ICMP Extensions 18 # scapy.contrib.status = loads 19 20 from __future__ import absolute_import 21 import scapy 22 from scapy.packet import Packet, bind_layers 23 from scapy.fields import * 24 from scapy.layers.inet import IP, ICMP 25 from scapy.layers.inet6 import IP6Field 26 from scapy.error import warning 27 from scapy.contrib.mpls import MPLS 28 import scapy.modules.six as six 29 30 31 class ICMPExtensionObject(Packet): 32 name = 'ICMP Extension Object' 33 fields_desc = [ ShortField('len', None), 34 ByteField('classnum', 0), 35 ByteField('classtype', 0) ] 36 37 def post_build(self, p, pay): 38 if self.len is None: 39 l = len(p)+len(pay) 40 p = struct.pack('!H', l)+p[2:] 41 return p+pay 42 43 44 class ICMPExtensionHeader(Packet): 45 name = 'ICMP Extension Header (RFC4884)' 46 fields_desc = [ BitField('version', 2, 4), 47 BitField('reserved', 0, 12), 48 BitField('chksum', None, 16) ] 49 50 _min_ieo_len = len(ICMPExtensionObject()) 51 52 def post_build(self, p, pay): 53 if self.chksum is None: 54 ck = checksum(p) 55 p = p[:2]+chr(ck>>8)+chr(ck&0xff)+p[4:] 56 return p+pay 57 58 def guess_payload_class(self, payload): 59 if len(payload) < self._min_ieo_len: 60 return Packet.guess_payload_class(self, payload) 61 62 # Look at fields of the generic ICMPExtensionObject to determine which 63 # bound extension type to use. 64 ieo = ICMPExtensionObject(payload) 65 if ieo.len < self._min_ieo_len: 66 return Packet.guess_payload_class(self, payload) 67 68 for fval, cls in self.payload_guess: 69 ok = 1 70 for k, v in six.iteritems(fval): 71 if not hasattr(ieo, k) or v != ieo.getfieldval(k): 72 ok = 0 73 break 74 if ok: 75 return cls 76 return ICMPExtensionObject 77 78 79 def ICMPExtension_post_dissection(self, pkt): 80 # RFC4884 section 5.2 says if the ICMP packet length 81 # is >144 then ICMP extensions start at byte 137. 82 83 lastlayer = pkt.lastlayer() 84 if not isinstance(lastlayer, conf.padding_layer): 85 return 86 87 if IP in pkt: 88 if ( ICMP in pkt and 89 pkt[ICMP].type in [3,11,12] and 90 pkt.len > 144 ): 91 bytes = pkt[ICMP].build()[136:] 92 else: 93 return 94 elif scapy.layers.inet6.IPv6 in pkt: 95 if ( (scapy.layers.inet6.ICMPv6TimeExceeded in pkt or 96 scapy.layers.inet6.ICMPv6DestUnreach in pkt) and 97 pkt.plen > 144 ): 98 bytes = pkt[scapy.layers.inet6.ICMPv6TimeExceeded].build()[136:] 99 else: 100 return 101 else: 102 return 103 104 # validate checksum 105 ieh = ICMPExtensionHeader(bytes) 106 if checksum(ieh.build()): 107 return # failed 108 109 lastlayer.load = lastlayer.load[:-len(ieh)] 110 lastlayer.add_payload(ieh) 111 112 113 class ICMPExtensionMPLS(ICMPExtensionObject): 114 name = 'ICMP Extension Object - MPLS (RFC4950)' 115 116 fields_desc = [ ShortField('len', None), 117 ByteField('classnum', 1), 118 ByteField('classtype', 1), 119 PacketListField('stack', [], MPLS, 120 length_from=lambda pkt: pkt.len - 4) ] 121 122 123 class ICMPExtensionInterfaceInformation(ICMPExtensionObject): 124 name = 'ICMP Extension Object - Interface Information Object (RFC5837)' 125 126 fields_desc = [ ShortField('len', None), 127 ByteField('classnum', 2), 128 BitField('interface_role', 0, 2), 129 BitField('reserved', 0, 2), 130 BitField('has_ifindex', 0, 1), 131 BitField('has_ipaddr', 0, 1), 132 BitField('has_ifname', 0, 1), 133 BitField('has_mtu', 0, 1), 134 135 ConditionalField( 136 IntField('ifindex', None), 137 lambda pkt: pkt.has_ifindex == 1), 138 139 ConditionalField( 140 ShortField('afi', None), 141 lambda pkt: pkt.has_ipaddr == 1), 142 ConditionalField( 143 ShortField('reserved2', 0), 144 lambda pkt: pkt.has_ipaddr == 1), 145 ConditionalField( 146 IPField('ip4', None), 147 lambda pkt: pkt.afi == 1), 148 ConditionalField( 149 IP6Field('ip6', None), 150 lambda pkt: pkt.afi == 2), 151 152 ConditionalField( 153 FieldLenField('ifname_len', None, fmt='B', 154 length_of='ifname'), 155 lambda pkt: pkt.has_ifname == 1), 156 ConditionalField( 157 StrLenField('ifname', None, 158 length_from=lambda pkt: pkt.ifname_len), 159 lambda pkt: pkt.has_ifname == 1), 160 161 ConditionalField( 162 IntField('mtu', None), 163 lambda pkt: pkt.has_mtu == 1) ] 164 165 def self_build(self, field_pos_list=None): 166 if self.afi is None: 167 if self.ip4 is not None: 168 self.afi = 1 169 elif self.ip6 is not None: 170 self.afi = 2 171 172 if self.has_ifindex and self.ifindex is None: 173 warning('has_ifindex set but ifindex is not set.') 174 if self.has_ipaddr and self.afi is None: 175 warning('has_ipaddr set but afi is not set.') 176 if self.has_ipaddr and self.ip4 is None and self.ip6 is None: 177 warning('has_ipaddr set but ip4 or ip6 is not set.') 178 if self.has_ifname and self.ifname is None: 179 warning('has_ifname set but ifname is not set.') 180 if self.has_mtu and self.mtu is None: 181 warning('has_mtu set but mtu is not set.') 182 183 return ICMPExtensionObject.self_build(self, field_pos_list=field_pos_list) 184 185 186 # Add the post_dissection() method to the existing ICMPv4 and 187 # ICMPv6 error messages 188 scapy.layers.inet.ICMPerror.post_dissection = ICMPExtension_post_dissection 189 scapy.layers.inet.TCPerror.post_dissection = ICMPExtension_post_dissection 190 scapy.layers.inet.UDPerror.post_dissection = ICMPExtension_post_dissection 191 192 scapy.layers.inet6.ICMPv6DestUnreach.post_dissection = ICMPExtension_post_dissection 193 scapy.layers.inet6.ICMPv6TimeExceeded.post_dissection = ICMPExtension_post_dissection 194 195 196 # ICMPExtensionHeader looks at fields from the upper layer object when 197 # determining which upper layer to use. 198 bind_layers(ICMPExtensionHeader, ICMPExtensionMPLS, classnum=1, classtype=1) 199 bind_layers(ICMPExtensionHeader, ICMPExtensionInterfaceInformation, classnum=2) 200