@cached_property<EOL><INDENT>def protocol(self):<DEDENT>
if self.address_family == socket.AF_INET:<EOL><INDENT>proto = i(self.raw[<NUM_LIT:9>])<EOL>start = (i(self.raw[<NUM_LIT:0>]) & <NUM_LIT>) * <NUM_LIT:4><EOL><DEDENT>elif self.address_family == socket.AF_INET6:<EOL><INDENT>proto = i(self.raw[<NUM_LIT:6>])<EOL>start = <NUM_LIT><EOL>while proto in IPV6_EXT_HEADERS:<EOL><INDENT>if start >= len(self.raw):<EOL><INDENT>start = None<EOL>proto = None<EOL>break<EOL><DEDENT>if proto == Protocol.FRAGMENT:<EOL><INDENT>hdrlen = <NUM_LIT:8><EOL><DEDENT>elif proto == Protocol.AH:<EOL><INDENT>hdrlen = (i(self.raw[start + <NUM_LIT:1>]) + <NUM_LIT:2>) * <NUM_LIT:4><EOL><DEDENT>else:<EOL><INDENT>hdrlen = (i(self.raw[start + <NUM_LIT:1>]) + <NUM_LIT:1>) * <NUM_LIT:8><EOL><DEDENT>proto = i(self.raw[start])<EOL>start += hdrlen<EOL><DEDENT><DEDENT>else:<EOL><INDENT>start = None<EOL>proto = None<EOL><DEDENT>out_of_bounds = (<EOL>(proto == Protocol.TCP and start + <NUM_LIT:20> > len(self.raw)) or<EOL>(proto == Protocol.UDP and start + <NUM_LIT:8> > len(self.raw)) or<EOL>(proto in {Protocol.ICMP, Protocol.ICMPV6} and start + <NUM_LIT:4> > len(self.raw))<EOL>)<EOL>if out_of_bounds:<EOL><INDENT>start = None<EOL>proto = None<EOL><DEDENT>return proto, start<EOL>
- | A (ipproto, proto_start) tuple.
  | ``ipproto`` is the IP protocol in use, e.g. Protocol.TCP or Protocol.UDP.
  | ``proto_start`` denotes the beginning of the protocol data.
  | If the packet does not match our expectations, both ipproto and proto_start are None.