1""" 2Internet Protocol version 4. 3 4RFC 791 5""" 6import logging 7 8from pypacker import pypacker, triggerlist, checksum 9from pypacker.layer3.ip_shared import IP_PROTO_IP6, IP_PROTO_ICMP, IP_PROTO_IGMP, IP_PROTO_TCP,\ 10 IP_PROTO_UDP, IP_PROTO_ESP, IP_PROTO_PIM, IP_PROTO_IPXIP, IP_PROTO_SCTP, IP_PROTO_OSPF 11from pypacker.pypacker import FIELD_FLAG_AUTOUPDATE, FIELD_FLAG_IS_TYPEFIELD 12# handler 13from pypacker.layer3 import esp, icmp, igmp, ip6, ipx, ospf, pim 14from pypacker.layer4 import tcp, udp, sctp 15 16 17logger = logging.getLogger("pypacker") 18 19# avoid references for performance reasons 20in_cksum = checksum.in_cksum 21 22# IP options 23# http://www.iana.org/assignments/ip-parameters/ip-parameters.xml 24IP_OPT_EOOL = 0 25IP_OPT_NOP = 1 26IP_OPT_SEC = 2 27IP_OPT_LSR = 3 28IP_OPT_TS = 4 29IP_OPT_ESEC = 5 30IP_OPT_CIPSO = 6 31IP_OPT_RR = 7 32IP_OPT_SID = 8 33IP_OPT_SSR = 9 34IP_OPT_ZSU = 10 35IP_OPT_MTUP = 11 36IP_OPT_MTUR = 12 37IP_OPT_FINN = 13 38IP_OPT_VISA = 14 39IP_OPT_ENCODE = 15 40IP_OPT_IMITD = 16 41IP_OPT_EIP = 17 42IP_OPT_TR = 18 43IP_OPT_ADDEXT = 19 44IP_OPT_RTRALT = 20 45IP_OPT_SDB = 21 46IP_OPT_UNASSGNIED = 22 47IP_OPT_DPS = 23 48IP_OPT_UMP = 24 49IP_OPT_QS = 25 50IP_OPT_EXP = 30 51 52 53class IPOptSingle(pypacker.Packet): 54 __hdr__ = ( 55 ("type", "B", 0), 56 ) 57 58 59class IPOptMulti(pypacker.Packet): 60 """ 61 len = total length (header + data) 62 """ 63 __hdr__ = ( 64 ("type", "B", 0), 65 ("len", "B", 2), 66 ) 67 68 def _update_fields(self): 69 self.len = len(self) 70 71 72class IP(pypacker.Packet): 73 __hdr__ = ( 74 ("v_hl", "B", 69, FIELD_FLAG_AUTOUPDATE), # = 0x45 75 ("tos", "B", 0), 76 ("len", "H", 20, FIELD_FLAG_AUTOUPDATE), 77 ("id", "H", 0), 78 ("frag_off", "H", 0), 79 ("ttl", "B", 64), 80 ("p", "B", IP_PROTO_TCP, FIELD_FLAG_IS_TYPEFIELD), 81 ("sum", "H", 0, FIELD_FLAG_AUTOUPDATE), 82 ("src", "4s", b"\x00" * 4), 83 ("dst", "4s", b"\x00" * 4), 84 ("opts", None, triggerlist.TriggerList) 85 ) 86 87 __handler__ = { 88 IP_PROTO_ICMP: icmp.ICMP, 89 IP_PROTO_IGMP: igmp.IGMP, 90 IP_PROTO_TCP: tcp.TCP, 91 IP_PROTO_UDP: udp.UDP, 92 IP_PROTO_IP6: ip6.IP6, 93 IP_PROTO_ESP: esp.ESP, 94 IP_PROTO_PIM: pim.PIM, 95 IP_PROTO_IPXIP: ipx.IPX, 96 IP_PROTO_SCTP: sctp.SCTP, 97 IP_PROTO_OSPF: ospf.OSPF 98 } 99 100 UPDATE_DEPENDANTS = {tcp.TCP, udp.UDP} 101 102 def __get_v(self): 103 return self.v_hl >> 4 104 105 def __set_v(self, value): 106 self.v_hl = (value << 4) | (self.v_hl & 0xF) 107 # version 108 v = property(__get_v, __set_v) 109 110 def __get_hl(self): 111 return self.v_hl & 0x0F 112 113 def __set_hl(self, value): 114 self.v_hl = (self.v_hl & 0xF0) | value 115 # header length 116 hl = property(__get_hl, __set_hl) 117 118 def __get_flags(self): 119 return (self.frag_off & 0xE000) >> 13 120 121 def __set_flags(self, value): 122 self.frag_off = (self.frag_off & ~0xE000) | (value << 13) 123 flags = property(__get_flags, __set_flags) 124 125 def __get_offset(self): 126 return self.frag_off & ~0xE000 127 128 def __set_offset(self, value): 129 self.frag_off = (self.frag_off & 0xE000) | value 130 offset = property(__get_offset, __set_offset) 131 132 def create_fragments(self, fragment_len=1480): 133 """ 134 Create fragment packets from this IP packet with max fragment_len bytes each. 135 This will set the flags and offset values accordingly (see header field off). 136 137 fragment_len -- max length of a fragment (IP header + payload) 138 return -- fragment IP packets created from this packet 139 """ 140 if fragment_len % 8 != 0: 141 raise Exception("fragment_len not multipe of 8 bytes: %r" % fragment_len) 142 143 fragments = [] 144 length_ip_total = len(self.bin()) 145 payload = self.body_bytes 146 length_ip_header = length_ip_total - len(payload) 147 length_payload = length_ip_total - length_ip_header 148 149 off = 0 150 151 while off < length_payload: 152 payload_sub = payload[off: off + fragment_len] 153 154 ip_frag = IP(id=self.id, p=self.p, src=self.src, dst=self.dst) 155 156 if length_payload - off > fragment_len: 157 # more fragments follow 158 ip_frag.flags = 0x1 159 else: 160 # last fragment 161 ip_frag.flags = 0x0 162 163 ip_frag.offset = int(off / 8) 164 ip_frag.body_bytes = payload_sub 165 fragments.append(ip_frag) 166 off += fragment_len 167 168 return fragments 169 170 # Convenient access for: src[_s], dst[_s] 171 src_s = pypacker.get_property_ip4("src") 172 dst_s = pypacker.get_property_ip4("dst") 173 p_t = pypacker.get_property_translator("p", "IP_PROTO_") 174 175 def _dissect(self, buf): 176 total_header_length = ((buf[0] & 0xF) << 2) 177 options_length = total_header_length - 20 # total IHL - standard IP-len = options length 178 179 if options_length > 0: 180 # logger.debug("got some IP options: %s" % tl_opts) 181 self._init_triggerlist("opts", buf[20: 20 + options_length], self._parse_opts) 182 elif options_length < 0: 183 # invalid header length: assume no options at all 184 raise Exception("Invalid options length: %d" % options_length) 185 # TODO: extract real data length: 186 # There are some cases where padding can not be identified on ethernet -> do it here (eg VSS shit trailer) 187 self._init_handler(buf[9], buf[total_header_length:]) 188 return total_header_length 189 190 __IP_OPT_SINGLE = {IP_OPT_EOOL, IP_OPT_NOP} 191 192 @staticmethod 193 def _parse_opts(buf): 194 """Parse IP options and return them as list.""" 195 optlist = [] 196 i = 0 197 p = None 198 199 while i < len(buf): 200 # logger.debug("got IP-option type %s" % buf[i]) 201 if buf[i] in IP.__IP_OPT_SINGLE: 202 p = IPOptSingle(type=buf[i]) 203 i += 1 204 else: 205 olen = buf[i + 1] 206 # logger.debug("IPOptMulti") 207 p = IPOptMulti(type=buf[i], len=olen, body_bytes=buf[i + 2: i + olen]) 208 # logger.debug("body bytes: %s" % buf[i + 2: i + olen]) 209 i += olen # typefield + lenfield + data-len 210 # logger.debug("IPOptMulti 2") 211 optlist.append(p) 212 return optlist 213 214 def _update_fields(self): 215 self._update_higherlayer_id() 216 217 if self.len_au_active: 218 self.len = len(self) 219 if self.v_hl_au_active: 220 # Update header length. NOTE: needs to be a multiple of 4 Bytes. 221 # logger.debug("updating: %r" % self._packet) 222 # options length need to be multiple of 4 Bytes 223 self.hl = int(self.header_len / 4) & 0xF 224 if self.sum_au_active: 225 # length changed so we have to recalculate checksum 226 # logger.debug(">>> IP: calculating sum, current: %0X" % self.sum) 227 # reset checksum for recalculation, mark as changed / clear cache 228 self.sum = 0 229 # logger.debug(">>> IP: bytes for sum: %s" % self.header_bytes) 230 self.sum = in_cksum(self._pack_header()) 231 # logger.debug("IP: new hl: %d / %d" % (self._packet.hdr_len, hdr_len_off)) 232 # logger.debug("new sum: %0X" % self.sum) 233 234 def direction(self, other): 235 # logger.debug("checking direction: %s<->%s" % (self, next)) 236 direction = 0 237 if self.src == other.src and self.dst == other.dst: 238 direction |= pypacker.Packet.DIR_SAME 239 if self.src == other.dst and self.dst == other.src: 240 direction |= pypacker.Packet.DIR_REV 241 if direction == 0: 242 direction = pypacker.Packet.DIR_UNKNOWN 243 return direction 244 245 def reverse_address(self): 246 self.src, self.dst = self.dst, self.src 247 248# Type of service (ip_tos), RFC 1349 ("obsoleted by RFC 2474") 249IP_TOS_DEFAULT = 0x00 # default 250IP_TOS_LOWDELAY = 0x10 # low delay 251IP_TOS_THROUGHPUT = 0x08 # high throughput 252IP_TOS_RELIABILITY = 0x04 # high reliability 253IP_TOS_LOWCOST = 0x02 # low monetary cost - XXX 254IP_TOS_ECT = 0x02 # ECN-capable transport 255IP_TOS_CE = 0x01 # congestion experienced 256 257# IP precedence (high 3 bits of ip_tos), hopefully unused 258IP_TOS_PREC_ROUTINE = 0x00 259IP_TOS_PREC_PRIORITY = 0x20 260IP_TOS_PREC_IMMEDIATE = 0x40 261IP_TOS_PREC_FLASH = 0x60 262IP_TOS_PREC_FLASHOVERRIDE = 0x80 263IP_TOS_PREC_CRITIC_ECP = 0xA0 264IP_TOS_PREC_INTERNETCONTROL = 0xC0 265IP_TOS_PREC_NETCONTROL = 0xE0 266 267# Fragmentation flags (ip_off) 268IP_RF = 0x4 # reserved 269IP_DF = 0x2 # don't fragment 270IP_MF = 0x1 # more fragments (not last frag) 271 272# Time-to-live (ip_ttl), seconds 273IP_TTL_DEFAULT = 64 # default ttl, RFC 1122, RFC 1340 274IP_TTL_MAX = 255 # maximum ttl 275