pypcapkit 1.3.5.post6__pp310-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (466) hide show
  1. pcapkit/__init__.py +124 -0
  2. pcapkit/__main__.py +138 -0
  3. pcapkit/all.py +136 -0
  4. pcapkit/const/__init__.py +81 -0
  5. pcapkit/const/arp/__init__.py +25 -0
  6. pcapkit/const/arp/hardware.py +181 -0
  7. pcapkit/const/arp/operation.py +131 -0
  8. pcapkit/const/ftp/__init__.py +25 -0
  9. pcapkit/const/ftp/command.py +309 -0
  10. pcapkit/const/ftp/return_code.py +304 -0
  11. pcapkit/const/hip/__init__.py +94 -0
  12. pcapkit/const/hip/certificate.py +77 -0
  13. pcapkit/const/hip/cipher.py +65 -0
  14. pcapkit/const/hip/di.py +59 -0
  15. pcapkit/const/hip/ecdsa_curve.py +59 -0
  16. pcapkit/const/hip/ecdsa_low_curve.py +56 -0
  17. pcapkit/const/hip/eddsa_curve.py +65 -0
  18. pcapkit/const/hip/esp_transform_suite.py +98 -0
  19. pcapkit/const/hip/group.py +86 -0
  20. pcapkit/const/hip/hi_algorithm.py +86 -0
  21. pcapkit/const/hip/hit_suite.py +68 -0
  22. pcapkit/const/hip/nat_traversal.py +62 -0
  23. pcapkit/const/hip/notify_message.py +200 -0
  24. pcapkit/const/hip/packet.py +89 -0
  25. pcapkit/const/hip/parameter.py +377 -0
  26. pcapkit/const/hip/registration.py +68 -0
  27. pcapkit/const/hip/registration_failure.py +84 -0
  28. pcapkit/const/hip/suite.py +71 -0
  29. pcapkit/const/hip/transport.py +59 -0
  30. pcapkit/const/http/__init__.py +39 -0
  31. pcapkit/const/http/error_code.py +95 -0
  32. pcapkit/const/http/frame.py +95 -0
  33. pcapkit/const/http/method.py +184 -0
  34. pcapkit/const/http/setting.py +96 -0
  35. pcapkit/const/http/status_code.py +298 -0
  36. pcapkit/const/ipv4/__init__.py +57 -0
  37. pcapkit/const/ipv4/classification_level.py +64 -0
  38. pcapkit/const/ipv4/option_class.py +55 -0
  39. pcapkit/const/ipv4/option_number.py +137 -0
  40. pcapkit/const/ipv4/protection_authority.py +63 -0
  41. pcapkit/const/ipv4/qs_function.py +51 -0
  42. pcapkit/const/ipv4/router_alert.py +251 -0
  43. pcapkit/const/ipv4/tos_del.py +51 -0
  44. pcapkit/const/ipv4/tos_ecn.py +55 -0
  45. pcapkit/const/ipv4/tos_pre.py +63 -0
  46. pcapkit/const/ipv4/tos_rel.py +51 -0
  47. pcapkit/const/ipv4/tos_thr.py +51 -0
  48. pcapkit/const/ipv4/ts_flag.py +53 -0
  49. pcapkit/const/ipv6/__init__.py +53 -0
  50. pcapkit/const/ipv6/extension_header.py +69 -0
  51. pcapkit/const/ipv6/option.py +137 -0
  52. pcapkit/const/ipv6/option_action.py +55 -0
  53. pcapkit/const/ipv6/qs_function.py +51 -0
  54. pcapkit/const/ipv6/router_alert.py +266 -0
  55. pcapkit/const/ipv6/routing.py +80 -0
  56. pcapkit/const/ipv6/seed_id.py +55 -0
  57. pcapkit/const/ipv6/smf_dpd_mode.py +51 -0
  58. pcapkit/const/ipv6/tagger_id.py +62 -0
  59. pcapkit/const/ipx/__init__.py +27 -0
  60. pcapkit/const/ipx/packet.py +72 -0
  61. pcapkit/const/ipx/socket.py +104 -0
  62. pcapkit/const/l2tp/__init__.py +21 -0
  63. pcapkit/const/l2tp/type.py +51 -0
  64. pcapkit/const/mh/__init__.py +204 -0
  65. pcapkit/const/mh/access_type.py +92 -0
  66. pcapkit/const/mh/ack_status_code.py +71 -0
  67. pcapkit/const/mh/ani_suboption.py +74 -0
  68. pcapkit/const/mh/auth_subtype.py +53 -0
  69. pcapkit/const/mh/binding_ack_flag.py +66 -0
  70. pcapkit/const/mh/binding_error.py +51 -0
  71. pcapkit/const/mh/binding_revocation.py +59 -0
  72. pcapkit/const/mh/binding_update_flag.py +81 -0
  73. pcapkit/const/mh/cga_extension.py +66 -0
  74. pcapkit/const/mh/cga_sec.py +57 -0
  75. pcapkit/const/mh/cga_type.py +68 -0
  76. pcapkit/const/mh/dhcp_support_mode.py +53 -0
  77. pcapkit/const/mh/dns_status_code.py +65 -0
  78. pcapkit/const/mh/dsmip6_tls_packet.py +62 -0
  79. pcapkit/const/mh/dsmipv6_home_address.py +74 -0
  80. pcapkit/const/mh/enumerating_algorithm.py +56 -0
  81. pcapkit/const/mh/fb_ack_status.py +62 -0
  82. pcapkit/const/mh/fb_action.py +71 -0
  83. pcapkit/const/mh/fb_indication_trigger.py +65 -0
  84. pcapkit/const/mh/fb_type.py +59 -0
  85. pcapkit/const/mh/flow_id_status.py +77 -0
  86. pcapkit/const/mh/flow_id_suboption.py +71 -0
  87. pcapkit/const/mh/handoff_type.py +71 -0
  88. pcapkit/const/mh/handover_ack_flag.py +54 -0
  89. pcapkit/const/mh/handover_ack_status.py +92 -0
  90. pcapkit/const/mh/handover_initiate_flag.py +57 -0
  91. pcapkit/const/mh/handover_initiate_status.py +62 -0
  92. pcapkit/const/mh/home_address_reply.py +71 -0
  93. pcapkit/const/mh/lla_code.py +63 -0
  94. pcapkit/const/mh/lma_mag_suboption.py +59 -0
  95. pcapkit/const/mh/mn_group_id.py +59 -0
  96. pcapkit/const/mh/mn_id_subtype.py +77 -0
  97. pcapkit/const/mh/operator_id.py +63 -0
  98. pcapkit/const/mh/option.py +260 -0
  99. pcapkit/const/mh/packet.py +119 -0
  100. pcapkit/const/mh/qos_attribute.py +89 -0
  101. pcapkit/const/mh/revocation_status_code.py +83 -0
  102. pcapkit/const/mh/revocation_trigger.py +86 -0
  103. pcapkit/const/mh/status_code.py +232 -0
  104. pcapkit/const/mh/traffic_selector.py +62 -0
  105. pcapkit/const/mh/upa_status.py +71 -0
  106. pcapkit/const/mh/upn_reason.py +80 -0
  107. pcapkit/const/ospf/__init__.py +27 -0
  108. pcapkit/const/ospf/authentication.py +65 -0
  109. pcapkit/const/ospf/packet.py +71 -0
  110. pcapkit/const/pcapng/__init__.py +51 -0
  111. pcapkit/const/pcapng/block_type.py +152 -0
  112. pcapkit/const/pcapng/filter_type.py +48 -0
  113. pcapkit/const/pcapng/hash_algorithm.py +59 -0
  114. pcapkit/const/pcapng/option_type.py +233 -0
  115. pcapkit/const/pcapng/record_type.py +57 -0
  116. pcapkit/const/pcapng/secrets_type.py +56 -0
  117. pcapkit/const/pcapng/verdict_type.py +53 -0
  118. pcapkit/const/reg/__init__.py +34 -0
  119. pcapkit/const/reg/apptype.py +32728 -0
  120. pcapkit/const/reg/ethertype.py +714 -0
  121. pcapkit/const/reg/linktype.py +890 -0
  122. pcapkit/const/reg/transtype.py +526 -0
  123. pcapkit/const/tcp/__init__.py +35 -0
  124. pcapkit/const/tcp/checksum.py +55 -0
  125. pcapkit/const/tcp/flags.py +73 -0
  126. pcapkit/const/tcp/mp_tcp_option.py +80 -0
  127. pcapkit/const/tcp/option.py +198 -0
  128. pcapkit/const/vlan/__init__.py +23 -0
  129. pcapkit/const/vlan/priority_level.py +71 -0
  130. pcapkit/corekit/__init__.py +59 -0
  131. pcapkit/corekit/fields/__init__.py +45 -0
  132. pcapkit/corekit/fields/collections.py +282 -0
  133. pcapkit/corekit/fields/field.py +269 -0
  134. pcapkit/corekit/fields/ipaddress.py +274 -0
  135. pcapkit/corekit/fields/misc.py +722 -0
  136. pcapkit/corekit/fields/numbers.py +375 -0
  137. pcapkit/corekit/fields/strings.py +245 -0
  138. pcapkit/corekit/infoclass.py +394 -0
  139. pcapkit/corekit/io.py +506 -0
  140. pcapkit/corekit/module.py +39 -0
  141. pcapkit/corekit/multidict.py +626 -0
  142. pcapkit/corekit/protochain.py +263 -0
  143. pcapkit/corekit/version.py +33 -0
  144. pcapkit/dumpkit/__init__.py +15 -0
  145. pcapkit/dumpkit/common.py +199 -0
  146. pcapkit/dumpkit/null.py +77 -0
  147. pcapkit/dumpkit/pcap.py +144 -0
  148. pcapkit/foundation/__init__.py +45 -0
  149. pcapkit/foundation/engines/__init__.py +36 -0
  150. pcapkit/foundation/engines/dpkt.py +230 -0
  151. pcapkit/foundation/engines/engine.py +194 -0
  152. pcapkit/foundation/engines/pcap.py +188 -0
  153. pcapkit/foundation/engines/pcapng.py +310 -0
  154. pcapkit/foundation/engines/pyshark.py +166 -0
  155. pcapkit/foundation/engines/scapy.py +161 -0
  156. pcapkit/foundation/extraction.py +915 -0
  157. pcapkit/foundation/reassembly/__init__.py +49 -0
  158. pcapkit/foundation/reassembly/data/__init__.py +48 -0
  159. pcapkit/foundation/reassembly/data/ip.py +117 -0
  160. pcapkit/foundation/reassembly/data/tcp.py +145 -0
  161. pcapkit/foundation/reassembly/ip.py +192 -0
  162. pcapkit/foundation/reassembly/ipv4.py +50 -0
  163. pcapkit/foundation/reassembly/ipv6.py +50 -0
  164. pcapkit/foundation/reassembly/reassembly.py +389 -0
  165. pcapkit/foundation/reassembly/tcp.py +249 -0
  166. pcapkit/foundation/registry/__init__.py +41 -0
  167. pcapkit/foundation/registry/foundation.py +327 -0
  168. pcapkit/foundation/registry/protocols.py +885 -0
  169. pcapkit/foundation/traceflow/__init__.py +44 -0
  170. pcapkit/foundation/traceflow/data/__init__.py +30 -0
  171. pcapkit/foundation/traceflow/data/tcp.py +105 -0
  172. pcapkit/foundation/traceflow/tcp.py +159 -0
  173. pcapkit/foundation/traceflow/traceflow.py +390 -0
  174. pcapkit/interface/__init__.py +22 -0
  175. pcapkit/interface/core.py +185 -0
  176. pcapkit/interface/misc.py +120 -0
  177. pcapkit/protocols/__init__.py +85 -0
  178. pcapkit/protocols/application/NotImplemented/bgp.py +0 -0
  179. pcapkit/protocols/application/NotImplemented/dhcp.py +0 -0
  180. pcapkit/protocols/application/NotImplemented/dhcpv6.py +0 -0
  181. pcapkit/protocols/application/NotImplemented/dns.py +0 -0
  182. pcapkit/protocols/application/NotImplemented/imap.py +0 -0
  183. pcapkit/protocols/application/NotImplemented/ldap.py +0 -0
  184. pcapkit/protocols/application/NotImplemented/mqtt.py +0 -0
  185. pcapkit/protocols/application/NotImplemented/nntp.py +0 -0
  186. pcapkit/protocols/application/NotImplemented/ntp.py +0 -0
  187. pcapkit/protocols/application/NotImplemented/onc_rpc.py +0 -0
  188. pcapkit/protocols/application/NotImplemented/pop.py +0 -0
  189. pcapkit/protocols/application/NotImplemented/rip.py +0 -0
  190. pcapkit/protocols/application/NotImplemented/rtp.py +0 -0
  191. pcapkit/protocols/application/NotImplemented/sip.py +0 -0
  192. pcapkit/protocols/application/NotImplemented/smtp.py +0 -0
  193. pcapkit/protocols/application/NotImplemented/snmp.py +0 -0
  194. pcapkit/protocols/application/NotImplemented/ssh.py +0 -0
  195. pcapkit/protocols/application/NotImplemented/telnet.py +0 -0
  196. pcapkit/protocols/application/NotImplemented/tls.py +0 -0
  197. pcapkit/protocols/application/NotImplemented/xmpp.py +0 -0
  198. pcapkit/protocols/application/__init__.py +34 -0
  199. pcapkit/protocols/application/application.py +114 -0
  200. pcapkit/protocols/application/ftp.py +206 -0
  201. pcapkit/protocols/application/http.py +176 -0
  202. pcapkit/protocols/application/httpv1.py +320 -0
  203. pcapkit/protocols/application/httpv2.py +1255 -0
  204. pcapkit/protocols/data/__init__.py +192 -0
  205. pcapkit/protocols/data/application/__init__.py +57 -0
  206. pcapkit/protocols/data/application/ftp.py +59 -0
  207. pcapkit/protocols/data/application/httpv1.py +79 -0
  208. pcapkit/protocols/data/application/httpv2.py +293 -0
  209. pcapkit/protocols/data/data.py +25 -0
  210. pcapkit/protocols/data/internet/__init__.py +298 -0
  211. pcapkit/protocols/data/internet/ah.py +31 -0
  212. pcapkit/protocols/data/internet/hip.py +804 -0
  213. pcapkit/protocols/data/internet/hopopt.py +351 -0
  214. pcapkit/protocols/data/internet/ipv4.py +369 -0
  215. pcapkit/protocols/data/internet/ipv6.py +67 -0
  216. pcapkit/protocols/data/internet/ipv6_frag.py +29 -0
  217. pcapkit/protocols/data/internet/ipv6_opts.py +368 -0
  218. pcapkit/protocols/data/internet/ipv6_route.py +86 -0
  219. pcapkit/protocols/data/internet/ipx.py +56 -0
  220. pcapkit/protocols/data/internet/mh.py +509 -0
  221. pcapkit/protocols/data/link/__init__.py +33 -0
  222. pcapkit/protocols/data/link/arp.py +74 -0
  223. pcapkit/protocols/data/link/ethernet.py +28 -0
  224. pcapkit/protocols/data/link/l2tp.py +63 -0
  225. pcapkit/protocols/data/link/ospf.py +58 -0
  226. pcapkit/protocols/data/link/vlan.py +42 -0
  227. pcapkit/protocols/data/misc/__init__.py +109 -0
  228. pcapkit/protocols/data/misc/null.py +18 -0
  229. pcapkit/protocols/data/misc/pcap/__init__.py +18 -0
  230. pcapkit/protocols/data/misc/pcap/frame.py +56 -0
  231. pcapkit/protocols/data/misc/pcap/header.py +53 -0
  232. pcapkit/protocols/data/misc/pcapng.py +925 -0
  233. pcapkit/protocols/data/misc/raw.py +25 -0
  234. pcapkit/protocols/data/protocol.py +32 -0
  235. pcapkit/protocols/data/transport/__init__.py +71 -0
  236. pcapkit/protocols/data/transport/tcp.py +555 -0
  237. pcapkit/protocols/data/transport/udp.py +29 -0
  238. pcapkit/protocols/internet/NotImplemented/ecn.py +0 -0
  239. pcapkit/protocols/internet/NotImplemented/esp.py +97 -0
  240. pcapkit/protocols/internet/NotImplemented/icmp.py +0 -0
  241. pcapkit/protocols/internet/NotImplemented/icmpv6.py +0 -0
  242. pcapkit/protocols/internet/NotImplemented/igmp.py +0 -0
  243. pcapkit/protocols/internet/NotImplemented/shim6.py +0 -0
  244. pcapkit/protocols/internet/__init__.py +43 -0
  245. pcapkit/protocols/internet/ah.py +275 -0
  246. pcapkit/protocols/internet/hip.py +4727 -0
  247. pcapkit/protocols/internet/hopopt.py +1879 -0
  248. pcapkit/protocols/internet/internet.py +249 -0
  249. pcapkit/protocols/internet/ip.py +51 -0
  250. pcapkit/protocols/internet/ipsec.py +50 -0
  251. pcapkit/protocols/internet/ipv4.py +1782 -0
  252. pcapkit/protocols/internet/ipv6.py +412 -0
  253. pcapkit/protocols/internet/ipv6_frag.py +258 -0
  254. pcapkit/protocols/internet/ipv6_opts.py +1890 -0
  255. pcapkit/protocols/internet/ipv6_route.py +708 -0
  256. pcapkit/protocols/internet/ipx.py +230 -0
  257. pcapkit/protocols/internet/mh.py +2764 -0
  258. pcapkit/protocols/link/NotImplemented/dsl.py +0 -0
  259. pcapkit/protocols/link/NotImplemented/eapol.py +1 -0
  260. pcapkit/protocols/link/NotImplemented/fddi.py +0 -0
  261. pcapkit/protocols/link/NotImplemented/isdn.py +0 -0
  262. pcapkit/protocols/link/NotImplemented/ndp.py +0 -0
  263. pcapkit/protocols/link/NotImplemented/ppp.py +0 -0
  264. pcapkit/protocols/link/__init__.py +35 -0
  265. pcapkit/protocols/link/arp.py +421 -0
  266. pcapkit/protocols/link/ethernet.py +248 -0
  267. pcapkit/protocols/link/l2tp.py +267 -0
  268. pcapkit/protocols/link/link.py +140 -0
  269. pcapkit/protocols/link/ospf.py +342 -0
  270. pcapkit/protocols/link/rarp.py +82 -0
  271. pcapkit/protocols/link/vlan.py +225 -0
  272. pcapkit/protocols/misc/__init__.py +37 -0
  273. pcapkit/protocols/misc/null.py +129 -0
  274. pcapkit/protocols/misc/pcap/__init__.py +17 -0
  275. pcapkit/protocols/misc/pcap/frame.py +478 -0
  276. pcapkit/protocols/misc/pcap/header.py +358 -0
  277. pcapkit/protocols/misc/pcapng.py +5520 -0
  278. pcapkit/protocols/misc/raw.py +180 -0
  279. pcapkit/protocols/protocol.py +1216 -0
  280. pcapkit/protocols/schema/__init__.py +140 -0
  281. pcapkit/protocols/schema/application/__init__.py +40 -0
  282. pcapkit/protocols/schema/application/ftp.py +21 -0
  283. pcapkit/protocols/schema/application/httpv1.py +21 -0
  284. pcapkit/protocols/schema/application/httpv2.py +384 -0
  285. pcapkit/protocols/schema/internet/__init__.py +294 -0
  286. pcapkit/protocols/schema/internet/ah.py +40 -0
  287. pcapkit/protocols/schema/internet/hip.py +1184 -0
  288. pcapkit/protocols/schema/internet/hopopt.py +679 -0
  289. pcapkit/protocols/schema/internet/ipv4.py +576 -0
  290. pcapkit/protocols/schema/internet/ipv6.py +63 -0
  291. pcapkit/protocols/schema/internet/ipv6_frag.py +48 -0
  292. pcapkit/protocols/schema/internet/ipv6_opts.py +680 -0
  293. pcapkit/protocols/schema/internet/ipv6_route.py +197 -0
  294. pcapkit/protocols/schema/internet/ipx.py +40 -0
  295. pcapkit/protocols/schema/internet/mh.py +718 -0
  296. pcapkit/protocols/schema/link/__init__.py +19 -0
  297. pcapkit/protocols/schema/link/arp.py +39 -0
  298. pcapkit/protocols/schema/link/ethernet.py +51 -0
  299. pcapkit/protocols/schema/link/l2tp.py +88 -0
  300. pcapkit/protocols/schema/link/ospf.py +90 -0
  301. pcapkit/protocols/schema/link/vlan.py +69 -0
  302. pcapkit/protocols/schema/misc/__init__.py +108 -0
  303. pcapkit/protocols/schema/misc/null.py +18 -0
  304. pcapkit/protocols/schema/misc/pcap/__init__.py +10 -0
  305. pcapkit/protocols/schema/misc/pcap/frame.py +51 -0
  306. pcapkit/protocols/schema/misc/pcap/header.py +63 -0
  307. pcapkit/protocols/schema/misc/pcapng.py +1689 -0
  308. pcapkit/protocols/schema/misc/raw.py +24 -0
  309. pcapkit/protocols/schema/schema.py +809 -0
  310. pcapkit/protocols/schema/transport/__init__.py +69 -0
  311. pcapkit/protocols/schema/transport/tcp.py +928 -0
  312. pcapkit/protocols/schema/transport/udp.py +90 -0
  313. pcapkit/protocols/transport/NotImplemented/dccp.py +0 -0
  314. pcapkit/protocols/transport/NotImplemented/rsvp.py +0 -0
  315. pcapkit/protocols/transport/NotImplemented/sctp.py +0 -0
  316. pcapkit/protocols/transport/__init__.py +27 -0
  317. pcapkit/protocols/transport/tcp.py +3025 -0
  318. pcapkit/protocols/transport/transport.py +158 -0
  319. pcapkit/protocols/transport/udp.py +214 -0
  320. pcapkit/py.typed +0 -0
  321. pcapkit/toolkit/__init__.py +57 -0
  322. pcapkit/toolkit/dpkt.py +306 -0
  323. pcapkit/toolkit/pcap.py +212 -0
  324. pcapkit/toolkit/pcapng.py +251 -0
  325. pcapkit/toolkit/pyshark.py +99 -0
  326. pcapkit/toolkit/scapy.py +297 -0
  327. pcapkit/utilities/__init__.py +20 -0
  328. pcapkit/utilities/compat.py +196 -0
  329. pcapkit/utilities/decorators.py +197 -0
  330. pcapkit/utilities/exceptions.py +365 -0
  331. pcapkit/utilities/logging.py +55 -0
  332. pcapkit/utilities/warnings.py +185 -0
  333. pcapkit/vendor/__init__.py +105 -0
  334. pcapkit/vendor/__main__.py +92 -0
  335. pcapkit/vendor/arp/__init__.py +27 -0
  336. pcapkit/vendor/arp/hardware.py +29 -0
  337. pcapkit/vendor/arp/operation.py +29 -0
  338. pcapkit/vendor/default.py +474 -0
  339. pcapkit/vendor/ftp/__init__.py +27 -0
  340. pcapkit/vendor/ftp/command.py +244 -0
  341. pcapkit/vendor/ftp/return_code.py +256 -0
  342. pcapkit/vendor/hip/__init__.py +94 -0
  343. pcapkit/vendor/hip/certificate.py +29 -0
  344. pcapkit/vendor/hip/cipher.py +29 -0
  345. pcapkit/vendor/hip/di.py +29 -0
  346. pcapkit/vendor/hip/ecdsa_curve.py +29 -0
  347. pcapkit/vendor/hip/ecdsa_low_curve.py +29 -0
  348. pcapkit/vendor/hip/eddsa_curve.py +85 -0
  349. pcapkit/vendor/hip/esp_transform_suite.py +29 -0
  350. pcapkit/vendor/hip/group.py +87 -0
  351. pcapkit/vendor/hip/hi_algorithm.py +29 -0
  352. pcapkit/vendor/hip/hit_suite.py +29 -0
  353. pcapkit/vendor/hip/nat_traversal.py +29 -0
  354. pcapkit/vendor/hip/notify_message.py +29 -0
  355. pcapkit/vendor/hip/packet.py +88 -0
  356. pcapkit/vendor/hip/parameter.py +88 -0
  357. pcapkit/vendor/hip/registration.py +29 -0
  358. pcapkit/vendor/hip/registration_failure.py +29 -0
  359. pcapkit/vendor/hip/suite.py +29 -0
  360. pcapkit/vendor/hip/transport.py +29 -0
  361. pcapkit/vendor/http/__init__.py +39 -0
  362. pcapkit/vendor/http/error_code.py +95 -0
  363. pcapkit/vendor/http/frame.py +91 -0
  364. pcapkit/vendor/http/method.py +167 -0
  365. pcapkit/vendor/http/setting.py +93 -0
  366. pcapkit/vendor/http/status_code.py +185 -0
  367. pcapkit/vendor/ipv4/__init__.py +57 -0
  368. pcapkit/vendor/ipv4/classification_level.py +91 -0
  369. pcapkit/vendor/ipv4/option_class.py +80 -0
  370. pcapkit/vendor/ipv4/option_number.py +105 -0
  371. pcapkit/vendor/ipv4/protection_authority.py +84 -0
  372. pcapkit/vendor/ipv4/qs_function.py +78 -0
  373. pcapkit/vendor/ipv4/router_alert.py +93 -0
  374. pcapkit/vendor/ipv4/tos_del.py +78 -0
  375. pcapkit/vendor/ipv4/tos_ecn.py +95 -0
  376. pcapkit/vendor/ipv4/tos_pre.py +84 -0
  377. pcapkit/vendor/ipv4/tos_rel.py +78 -0
  378. pcapkit/vendor/ipv4/tos_thr.py +77 -0
  379. pcapkit/vendor/ipv4/ts_flag.py +79 -0
  380. pcapkit/vendor/ipv6/__init__.py +53 -0
  381. pcapkit/vendor/ipv6/extension_header.py +171 -0
  382. pcapkit/vendor/ipv6/option.py +104 -0
  383. pcapkit/vendor/ipv6/option_action.py +90 -0
  384. pcapkit/vendor/ipv6/qs_function.py +78 -0
  385. pcapkit/vendor/ipv6/router_alert.py +93 -0
  386. pcapkit/vendor/ipv6/routing.py +87 -0
  387. pcapkit/vendor/ipv6/seed_id.py +81 -0
  388. pcapkit/vendor/ipv6/smf_dpd_mode.py +78 -0
  389. pcapkit/vendor/ipv6/tagger_id.py +81 -0
  390. pcapkit/vendor/ipx/__init__.py +37 -0
  391. pcapkit/vendor/ipx/packet.py +123 -0
  392. pcapkit/vendor/ipx/socket.py +125 -0
  393. pcapkit/vendor/l2tp/__init__.py +21 -0
  394. pcapkit/vendor/l2tp/type.py +78 -0
  395. pcapkit/vendor/mh/__init__.py +204 -0
  396. pcapkit/vendor/mh/access_type.py +87 -0
  397. pcapkit/vendor/mh/ack_status_code.py +88 -0
  398. pcapkit/vendor/mh/ani_suboption.py +88 -0
  399. pcapkit/vendor/mh/auth_subtype.py +83 -0
  400. pcapkit/vendor/mh/binding_ack_flag.py +148 -0
  401. pcapkit/vendor/mh/binding_error.py +78 -0
  402. pcapkit/vendor/mh/binding_revocation.py +87 -0
  403. pcapkit/vendor/mh/binding_update_flag.py +147 -0
  404. pcapkit/vendor/mh/cga_extension.py +91 -0
  405. pcapkit/vendor/mh/cga_sec.py +91 -0
  406. pcapkit/vendor/mh/cga_type.py +74 -0
  407. pcapkit/vendor/mh/dhcp_support_mode.py +77 -0
  408. pcapkit/vendor/mh/dns_status_code.py +87 -0
  409. pcapkit/vendor/mh/dsmip6_tls_packet.py +87 -0
  410. pcapkit/vendor/mh/dsmipv6_home_address.py +87 -0
  411. pcapkit/vendor/mh/enumerating_algorithm.py +82 -0
  412. pcapkit/vendor/mh/fb_ack_status.py +87 -0
  413. pcapkit/vendor/mh/fb_action.py +88 -0
  414. pcapkit/vendor/mh/fb_indication_trigger.py +87 -0
  415. pcapkit/vendor/mh/fb_type.py +88 -0
  416. pcapkit/vendor/mh/flow_id_status.py +87 -0
  417. pcapkit/vendor/mh/flow_id_suboption.py +87 -0
  418. pcapkit/vendor/mh/handoff_type.py +87 -0
  419. pcapkit/vendor/mh/handover_ack_flag.py +143 -0
  420. pcapkit/vendor/mh/handover_ack_status.py +87 -0
  421. pcapkit/vendor/mh/handover_initiate_flag.py +143 -0
  422. pcapkit/vendor/mh/handover_initiate_status.py +87 -0
  423. pcapkit/vendor/mh/home_address_reply.py +87 -0
  424. pcapkit/vendor/mh/lla_code.py +97 -0
  425. pcapkit/vendor/mh/lma_mag_suboption.py +88 -0
  426. pcapkit/vendor/mh/mn_group_id.py +87 -0
  427. pcapkit/vendor/mh/mn_id_subtype.py +87 -0
  428. pcapkit/vendor/mh/operator_id.py +87 -0
  429. pcapkit/vendor/mh/option.py +83 -0
  430. pcapkit/vendor/mh/packet.py +82 -0
  431. pcapkit/vendor/mh/qos_attribute.py +87 -0
  432. pcapkit/vendor/mh/revocation_status_code.py +87 -0
  433. pcapkit/vendor/mh/revocation_trigger.py +87 -0
  434. pcapkit/vendor/mh/status_code.py +91 -0
  435. pcapkit/vendor/mh/traffic_selector.py +87 -0
  436. pcapkit/vendor/mh/upa_status.py +87 -0
  437. pcapkit/vendor/mh/upn_reason.py +87 -0
  438. pcapkit/vendor/ospf/__init__.py +27 -0
  439. pcapkit/vendor/ospf/authentication.py +29 -0
  440. pcapkit/vendor/ospf/packet.py +29 -0
  441. pcapkit/vendor/pcapng/__init__.py +51 -0
  442. pcapkit/vendor/pcapng/block_type.py +94 -0
  443. pcapkit/vendor/pcapng/filter_type.py +77 -0
  444. pcapkit/vendor/pcapng/hash_algorithm.py +82 -0
  445. pcapkit/vendor/pcapng/option_type.py +287 -0
  446. pcapkit/vendor/pcapng/record_type.py +81 -0
  447. pcapkit/vendor/pcapng/secrets_type.py +81 -0
  448. pcapkit/vendor/pcapng/verdict_type.py +79 -0
  449. pcapkit/vendor/reg/__init__.py +34 -0
  450. pcapkit/vendor/reg/apptype.py +338 -0
  451. pcapkit/vendor/reg/ethertype.py +121 -0
  452. pcapkit/vendor/reg/linktype.py +110 -0
  453. pcapkit/vendor/reg/transtype.py +111 -0
  454. pcapkit/vendor/tcp/__init__.py +35 -0
  455. pcapkit/vendor/tcp/checksum.py +80 -0
  456. pcapkit/vendor/tcp/flags.py +149 -0
  457. pcapkit/vendor/tcp/mp_tcp_option.py +90 -0
  458. pcapkit/vendor/tcp/option.py +103 -0
  459. pcapkit/vendor/vlan/__init__.py +23 -0
  460. pcapkit/vendor/vlan/priority_level.py +97 -0
  461. pypcapkit-1.3.5.post6.dist-info/LICENSE +29 -0
  462. pypcapkit-1.3.5.post6.dist-info/METADATA +238 -0
  463. pypcapkit-1.3.5.post6.dist-info/RECORD +466 -0
  464. pypcapkit-1.3.5.post6.dist-info/WHEEL +5 -0
  465. pypcapkit-1.3.5.post6.dist-info/entry_points.txt +3 -0
  466. pypcapkit-1.3.5.post6.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1782 @@
1
+ # -*- coding: utf-8 -*-
2
+ """IPv4 - Internet Protocol version 4
3
+ ========================================
4
+
5
+ .. module:: pcapkit.protocols.internet.ipv4
6
+
7
+ :mod:`pcapkit.protocols.internet.ipv4` contains
8
+ :class:`~pcapkit.protocols.internet.ipv4.IPv4` only,
9
+ which implements extractor for Internet Protocol
10
+ version 4 (IPv4) [*]_, whose structure is described
11
+ as below:
12
+
13
+ ======= ========= ====================== =============================================
14
+ Octets Bits Name Description
15
+ ======= ========= ====================== =============================================
16
+ 0 0 ``ip.version`` Version (``4``)
17
+ 0 4 ``ip.hdr_len`` Internal Header Length (IHL)
18
+ 1 8 ``ip.dsfield.dscp`` Differentiated Services Code Point (DSCP)
19
+ 1 14 ``ip.dsfield.ecn`` Explicit Congestion Notification (ECN)
20
+ 2 16 ``ip.len`` Total Length
21
+ 4 32 ``ip.id`` Identification
22
+ 6 48 Reserved Bit (must be ``\\x00``)
23
+ 6 49 ``ip.flags.df`` Don't Fragment (DF)
24
+ 6 50 ``ip.flags.mf`` More Fragments (MF)
25
+ 6 51 ``ip.frag_offset`` Fragment Offset
26
+ 8 64 ``ip.ttl`` Time To Live (TTL)
27
+ 9 72 ``ip.proto`` Protocol (Transport Layer)
28
+ 10 80 ``ip.checksum`` Header Checksum
29
+ 12 96 ``ip.src`` Source IP Address
30
+ 16 128 ``ip.dst`` Destination IP Address
31
+ 20 160 ``ip.options`` IP Options (if IHL > ``5``)
32
+ ======= ========= ====================== =============================================
33
+
34
+ .. [*] https://en.wikipedia.org/wiki/IPv4
35
+
36
+ """
37
+ import datetime
38
+ import ipaddress
39
+ import math
40
+ from typing import TYPE_CHECKING, cast
41
+
42
+ from pcapkit.const.ipv4.classification_level import ClassificationLevel as Enum_ClassificationLevel
43
+ from pcapkit.const.ipv4.option_class import OptionClass as Enum_OptionClass
44
+ from pcapkit.const.ipv4.option_number import OptionNumber as Enum_OptionNumber
45
+ from pcapkit.const.ipv4.protection_authority import ProtectionAuthority as Enum_ProtectionAuthority
46
+ from pcapkit.const.ipv4.qs_function import QSFunction as Enum_QSFunction
47
+ from pcapkit.const.ipv4.router_alert import RouterAlert as Enum_RouterAlert
48
+ from pcapkit.const.ipv4.tos_del import ToSDelay as Enum_ToSDelay
49
+ from pcapkit.const.ipv4.tos_ecn import ToSECN as Enum_ToSECN
50
+ from pcapkit.const.ipv4.tos_pre import ToSPrecedence as Enum_ToSPrecedence
51
+ from pcapkit.const.ipv4.tos_rel import ToSReliability as Enum_ToSReliability
52
+ from pcapkit.const.ipv4.tos_thr import ToSThroughput as Enum_ToSThroughput
53
+ from pcapkit.const.ipv4.ts_flag import TSFlag as Enum_TSFlag
54
+ from pcapkit.const.reg.transtype import TransType as Enum_TransType
55
+ from pcapkit.corekit.multidict import OrderedMultiDict
56
+ from pcapkit.protocols.data.internet.ipv4 import EOOLOption as Data_EOOLOption
57
+ from pcapkit.protocols.data.internet.ipv4 import ESECOption as Data_ESECOption
58
+ from pcapkit.protocols.data.internet.ipv4 import Flags as Data_Flags
59
+ from pcapkit.protocols.data.internet.ipv4 import IPv4 as Data_IPv4
60
+ from pcapkit.protocols.data.internet.ipv4 import LSROption as Data_LSROption
61
+ from pcapkit.protocols.data.internet.ipv4 import MTUPOption as Data_MTUPOption
62
+ from pcapkit.protocols.data.internet.ipv4 import MTUROption as Data_MTUROption
63
+ from pcapkit.protocols.data.internet.ipv4 import NOPOption as Data_NOPOption
64
+ from pcapkit.protocols.data.internet.ipv4 import OptionType as Data_OptionType
65
+ from pcapkit.protocols.data.internet.ipv4 import QSOption as Data_QSOption
66
+ from pcapkit.protocols.data.internet.ipv4 import \
67
+ QuickStartReportOption as Data_QuickStartReportOption
68
+ from pcapkit.protocols.data.internet.ipv4 import \
69
+ QuickStartRequestOption as Data_QuickStartRequestOption
70
+ from pcapkit.protocols.data.internet.ipv4 import RROption as Data_RROption
71
+ from pcapkit.protocols.data.internet.ipv4 import RTRALTOption as Data_RTRALTOption
72
+ from pcapkit.protocols.data.internet.ipv4 import SECOption as Data_SECOption
73
+ from pcapkit.protocols.data.internet.ipv4 import SIDOption as Data_SIDOption
74
+ from pcapkit.protocols.data.internet.ipv4 import SSROption as Data_SSROption
75
+ from pcapkit.protocols.data.internet.ipv4 import ToSField as Data_ToSField
76
+ from pcapkit.protocols.data.internet.ipv4 import TROption as Data_TROption
77
+ from pcapkit.protocols.data.internet.ipv4 import TSOption as Data_TSOption
78
+ from pcapkit.protocols.data.internet.ipv4 import UnassignedOption as Data_UnassignedOption
79
+ from pcapkit.protocols.internet.ip import IP
80
+ from pcapkit.protocols.schema.internet.ipv4 import EOOLOption as Schema_EOOLOption
81
+ from pcapkit.protocols.schema.internet.ipv4 import ESECOption as Schema_ESECOption
82
+ from pcapkit.protocols.schema.internet.ipv4 import IPv4 as Schema_IPv4
83
+ from pcapkit.protocols.schema.internet.ipv4 import LSROption as Schema_LSROption
84
+ from pcapkit.protocols.schema.internet.ipv4 import MTUPOption as Schema_MTUPOption
85
+ from pcapkit.protocols.schema.internet.ipv4 import MTUROption as Schema_MTUROption
86
+ from pcapkit.protocols.schema.internet.ipv4 import NOPOption as Schema_NOPOption
87
+ from pcapkit.protocols.schema.internet.ipv4 import QSOption as Schema_QSOption
88
+ from pcapkit.protocols.schema.internet.ipv4 import \
89
+ QuickStartReportOption as Schema_QuickStartReportOption
90
+ from pcapkit.protocols.schema.internet.ipv4 import \
91
+ QuickStartRequestOption as Schema_QuickStartRequestOption
92
+ from pcapkit.protocols.schema.internet.ipv4 import RROption as Schema_RROption
93
+ from pcapkit.protocols.schema.internet.ipv4 import RTRALTOption as Schema_RTRALTOption
94
+ from pcapkit.protocols.schema.internet.ipv4 import SECOption as Schema_SECOption
95
+ from pcapkit.protocols.schema.internet.ipv4 import SIDOption as Schema_SIDOption
96
+ from pcapkit.protocols.schema.internet.ipv4 import SSROption as Schema_SSROption
97
+ from pcapkit.protocols.schema.internet.ipv4 import TROption as Schema_TROption
98
+ from pcapkit.protocols.schema.internet.ipv4 import TSOption as Schema_TSOption
99
+ from pcapkit.protocols.schema.internet.ipv4 import UnassignedOption as Schema_UnassignedOption
100
+ from pcapkit.protocols.schema.schema import Schema
101
+ from pcapkit.utilities.exceptions import ProtocolError
102
+ from pcapkit.utilities.warnings import ProtocolWarning, RegistryWarning, warn
103
+
104
+ if TYPE_CHECKING:
105
+ from datetime import timedelta
106
+ from enum import IntEnum as StdlibEnum
107
+ from ipaddress import IPv4Address
108
+ from typing import Any, Callable, Optional, Type
109
+
110
+ from aenum import IntEnum as AenumEnum
111
+ from mypy_extensions import DefaultArg, KwArg, NamedArg
112
+ from typing_extensions import Literal
113
+
114
+ from pcapkit.protocols.data.internet.ipv4 import Option as Data_Option
115
+ from pcapkit.protocols.protocol import ProtocolBase as Protocol
116
+ from pcapkit.protocols.schema.internet.ipv4 import Option as Schema_Option
117
+
118
+ Option = OrderedMultiDict[Enum_OptionNumber, Data_Option]
119
+ OptionParser = Callable[[Schema_Option, NamedArg(Option, 'options')], Data_Option]
120
+ OptionConstructor = Callable[[Enum_OptionNumber, DefaultArg(Optional[Data_Option]),
121
+ KwArg(Any)], Schema_Option]
122
+
123
+ __all__ = ['IPv4']
124
+
125
+
126
+ class IPv4(IP[Data_IPv4, Schema_IPv4],
127
+ schema=Schema_IPv4, data=Data_IPv4):
128
+ """This class implements Internet Protocol version 4.
129
+
130
+ This class currently supports parsing of the following IPv4 options,
131
+ which are directly mapped to the :class:`pcapkit.const.ipv4.option_number.OptionNumber`
132
+ enumeration:
133
+
134
+ .. list-table::
135
+ :header-rows: 1
136
+
137
+ * - Option Code
138
+ - Option Parser
139
+ - Option Constructor
140
+ * - :attr:`~pcapkit.const.ipv4.option_number.OptionNumber.EOOL`
141
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._read_opt_eool`
142
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._make_opt_eool`
143
+ * - :attr:`~pcapkit.const.ipv4.option_number.OptionNumber.NOP`
144
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._read_opt_nop`
145
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._make_opt_nop`
146
+ * - :attr:`~pcapkit.const.ipv4.option_number.OptionNumber.SEC`
147
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._read_opt_sec`
148
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._make_opt_sec`
149
+ * - :attr:`~pcapkit.const.ipv4.option_number.OptionNumber.LSR`
150
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._read_opt_lsr`
151
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._make_opt_lsr`
152
+ * - :attr:`~pcapkit.const.ipv4.option_number.OptionNumber.TS`
153
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._read_opt_ts`
154
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._make_opt_ts`
155
+ * - :attr:`~pcapkit.const.ipv4.option_number.OptionNumber.E_SEC`
156
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._read_opt_e_sec`
157
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._make_opt_e_sec`
158
+ * - :attr:`~pcapkit.const.ipv4.option_number.OptionNumber.RR`
159
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._read_opt_rr`
160
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._make_opt_rr`
161
+ * - :attr:`~pcapkit.const.ipv4.option_number.OptionNumber.SID`
162
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._read_opt_sid`
163
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._make_opt_sid`
164
+ * - :attr:`~pcapkit.const.ipv4.option_number.OptionNumber.SSR`
165
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._read_opt_ssr`
166
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._make_opt_ssr`
167
+ * - :attr:`~pcapkit.const.ipv4.option_number.OptionNumber.MTUP`
168
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._read_opt_mtup`
169
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._make_opt_mtup`
170
+ * - :attr:`~pcapkit.const.ipv4.option_number.OptionNumber.MTUR`
171
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._read_opt_mtur`
172
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._make_opt_mtur`
173
+ * - :attr:`~pcapkit.const.ipv4.option_number.OptionNumber.TR`
174
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._read_opt_tr`
175
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._make_opt_tr`
176
+ * - :attr:`~pcapkit.const.ipv4.option_number.OptionNumber.RTRALT`
177
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._read_opt_rtralt`
178
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._make_opt_rtralt`
179
+ * - :attr:`~pcapkit.const.ipv4.option_number.OptionNumber.QS`
180
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._read_opt_qs`
181
+ - :meth:`~pcapkit.protocols.internet.ipv4.IPv4._make_opt_qs`
182
+
183
+ """
184
+
185
+ ##########################################################################
186
+ # Properties.
187
+ ##########################################################################
188
+
189
+ @property
190
+ def name(self) -> 'Literal["Internet Protocol version 4"]':
191
+ """Name of corresponding protocol."""
192
+ return 'Internet Protocol version 4'
193
+
194
+ @property
195
+ def length(self) -> 'int':
196
+ """Header length of corresponding protocol."""
197
+ return self._info.hdr_len
198
+
199
+ @property
200
+ def protocol(self) -> 'Enum_TransType':
201
+ """Name of next layer protocol."""
202
+ return self._info.protocol
203
+
204
+ # source IP address
205
+ @property
206
+ def src(self) -> 'IPv4Address':
207
+ """Source IP address."""
208
+ return self._info.src
209
+
210
+ # destination IP address
211
+ @property
212
+ def dst(self) -> 'IPv4Address':
213
+ """Destination IP address."""
214
+ return self._info.dst
215
+
216
+ ##########################################################################
217
+ # Methods.
218
+ ##########################################################################
219
+
220
+ def read(self, length: 'Optional[int]' = None, **kwargs: 'Any') -> 'Data_IPv4': # pylint: disable=unused-argument
221
+ """Read Internet Protocol version 4 (IPv4).
222
+
223
+ Structure of IPv4 header [:rfc:`791`]:
224
+
225
+ .. code-block:: text
226
+
227
+ 0 1 2 3
228
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
229
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
230
+ |Version| IHL |Type of Service| Total Length |
231
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
232
+ | Identification |Flags| Fragment Offset |
233
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
234
+ | Time to Live | Protocol | Header Checksum |
235
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
236
+ | Source Address |
237
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
238
+ | Destination Address |
239
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
240
+ | Options | Padding |
241
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
242
+
243
+ Args:
244
+ length: Length of packet data.
245
+ **kwargs: Arbitrary keyword arguments.
246
+
247
+ Returns:
248
+ Parsed packet data.
249
+
250
+ """
251
+ if length is None:
252
+ length = len(self)
253
+ schema = self.__header__
254
+
255
+ if schema.vihl['version'] != 4:
256
+ raise ProtocolError(f"[IPv4] invalid version: {schema.vihl['version']}")
257
+
258
+ ipv4 = Data_IPv4(
259
+ version=schema.vihl['version'], # type: ignore[arg-type]
260
+ hdr_len=schema.vihl['ihl'] * 4,
261
+ tos=Data_ToSField.from_dict({
262
+ 'pre': Enum_ToSPrecedence.get(schema.tos['pre']),
263
+ 'del': Enum_ToSDelay.get(schema.tos['del']),
264
+ 'thr': Enum_ToSThroughput.get(schema.tos['thr']),
265
+ 'rel': Enum_ToSReliability.get(schema.tos['rel']),
266
+ 'ecn': Enum_ToSECN.get(schema.tos['ecn']),
267
+ }),
268
+ len=schema.length,
269
+ id=schema.id,
270
+ flags=Data_Flags(
271
+ df=bool(schema.flags['df']),
272
+ mf=bool(schema.flags['mf']),
273
+ ),
274
+ offset=int(schema.flags['offset']) * 8,
275
+ ttl=datetime.timedelta(seconds=schema.ttl),
276
+ protocol=schema.proto,
277
+ checksum=schema.chksum,
278
+ src=schema.src,
279
+ dst=schema.dst,
280
+ )
281
+
282
+ _optl = ipv4.hdr_len - 20
283
+ if _optl:
284
+ ipv4.__update__([
285
+ ('options', self._read_ipv4_options(_optl)),
286
+ ])
287
+
288
+ return self._decode_next_layer(ipv4, ipv4.protocol, ipv4.len - ipv4.hdr_len)
289
+
290
+ def make(self,
291
+ tos_pre: 'Enum_ToSPrecedence | StdlibEnum | AenumEnum | int | str' = Enum_ToSPrecedence.Routine,
292
+ tos_pre_default: 'Optional[int]' = None,
293
+ tos_pre_namespace: 'Optional[dict[str, int] | dict[int, str] | Type[StdlibEnum] | Type[AenumEnum]]' = None, # pylint: disable=line-too-long
294
+ tos_pre_reversed: 'bool' = False,
295
+ tos_del: 'Enum_ToSDelay | StdlibEnum | AenumEnum | int | str' = Enum_ToSDelay.NORMAL,
296
+ tos_del_default: 'Optional[int]' = None,
297
+ tos_del_namespace: 'Optional[dict[str, int] | dict[int, str] | Type[StdlibEnum] | Type[AenumEnum]]' = None, # pylint: disable=line-too-long
298
+ tos_del_reversed: 'bool' = False,
299
+ tos_thr: 'Enum_ToSThroughput | StdlibEnum | AenumEnum | int | str' = Enum_ToSThroughput.NORMAL,
300
+ tos_thr_default: 'Optional[int]' = None,
301
+ tos_thr_namespace: 'Optional[dict[str, int] | dict[int, str] | Type[StdlibEnum] | Type[AenumEnum]]' = None, # pylint: disable=line-too-long
302
+ tos_thr_reversed: 'bool' = False,
303
+ tos_rel: 'Enum_ToSReliability | StdlibEnum | AenumEnum | int | str' = Enum_ToSReliability.NORMAL,
304
+ tos_rel_default: 'Optional[int]' = None,
305
+ tos_rel_namespace: 'Optional[dict[str, int] | dict[int, str] | Type[StdlibEnum] | Type[AenumEnum]]' = None, # pylint: disable=line-too-long
306
+ tos_rel_reversed: 'bool' = False,
307
+ tos_ecn: 'Enum_ToSECN | StdlibEnum | AenumEnum | int | str' = Enum_ToSECN.Not_ECT,
308
+ tos_ecn_default: 'Optional[int]' = None,
309
+ tos_ecn_namespace: 'Optional[dict[str, int] | dict[int, str] | Type[StdlibEnum] | Type[AenumEnum]]' = None, # pylint: disable=line-too-long
310
+ tos_ecn_reversed: 'bool' = False,
311
+ id: 'int' = 0,
312
+ df: 'bool' = False,
313
+ mf: 'bool' = False,
314
+ offset: 'int' = 0,
315
+ ttl: 'timedelta | int' = 0,
316
+ protocol: 'Enum_TransType | StdlibEnum | AenumEnum | int | str' = Enum_TransType.UDP,
317
+ protocol_default: 'Optional[int]' = None,
318
+ protocol_namespace: 'Optional[dict[str, int] | dict[int, str] | Type[StdlibEnum] | Type[AenumEnum]]' = None, # pylint: disable=line-too-long
319
+ protocol_reversed: 'bool' = False,
320
+ checksum: 'bytes' = b'\x00\x00',
321
+ src: 'IPv4Address | str | int | bytes' = '127.0.0.1',
322
+ dst: 'IPv4Address | str | int | bytes' = '0.0.0.0', # nosec: B104
323
+ options: 'Optional[list[Schema_Option | tuple[Enum_OptionNumber, dict[str, Any]] | bytes] | Option]' = None, # pylint: disable=line-too-long
324
+ payload: 'bytes | Protocol | Schema' = b'',
325
+ **kwargs: 'Any') -> 'Schema_IPv4':
326
+ """Make (construct) packet data.
327
+
328
+ Args:
329
+ tos_pre: Precedence of the packet.
330
+ tos_pre_default: Default value of ``tos_pre``.
331
+ tos_pre_namespace: Namespace of ``tos_pre``.
332
+ tos_pre_reversed: If the namespace of ``tos_pre`` is reversed.
333
+ tos_del: Delay of the packet.
334
+ tos_del_default: Default value of ``tos_del``.
335
+ tos_del_namespace: Namespace of ``tos_del``.
336
+ tos_del_reversed: If the namespace of ``tos_del`` is reversed.
337
+ tos_thr: Throughput of the packet.
338
+ tos_thr_default: Default value of ``tos_thr``.
339
+ tos_thr_namespace: Namespace of ``tos_thr``.
340
+ tos_thr_reversed: If the namespace of ``tos_thr`` is reversed.
341
+ tos_rel: Reliability of the packet.
342
+ tos_rel_default: Default value of ``tos_rel``.
343
+ tos_rel_namespace: Namespace of ``tos_rel``.
344
+ tos_rel_reversed: If the namespace of ``tos_rel`` is reversed.
345
+ tos_ecn: ECN of the packet.
346
+ tos_ecn_default: Default value of ``tos_ecn``.
347
+ tos_ecn_namespace: Namespace of ``tos_ecn``.
348
+ tos_ecn_reversed: If the namespace of ``tos_ecn`` is reversed.
349
+ id: Identification of the packet.
350
+ df: Don't fragment flag.
351
+ mf: More fragments flag.
352
+ offset: Fragment offset.
353
+ ttl: Time to live of the packet.
354
+ protocol: Payload protocol of the packet.
355
+ protocol_default: Default value of ``protocol``.
356
+ protocol_namespace: Namespace of ``protocol``.
357
+ protocol_reversed: If the namespace of ``protocol`` is reversed.
358
+ checksum: Checksum of the packet.
359
+ src: Source address of the packet.
360
+ dst: Destination address of the packet.
361
+ options: Options of the packet.
362
+ payload: Payload of the packet.
363
+ **kwargs: Arbitrary keyword arguments.
364
+
365
+ Returns:
366
+ Constructed packet data.
367
+
368
+ """
369
+ tos_pre_val = self._make_index(tos_pre, tos_pre_default, namespace=tos_pre_namespace,
370
+ reversed=tos_pre_reversed, pack=False)
371
+ tos_del_val = self._make_index(tos_del, tos_del_default, namespace=tos_del_namespace,
372
+ reversed=tos_del_reversed, pack=False)
373
+ tos_thr_val = self._make_index(tos_thr, tos_thr_default, namespace=tos_thr_namespace,
374
+ reversed=tos_thr_reversed, pack=False)
375
+ tos_rel_val = self._make_index(tos_rel, tos_rel_default, namespace=tos_rel_namespace,
376
+ reversed=tos_rel_reversed, pack=False)
377
+ tos_ecn_val = self._make_index(tos_ecn, tos_ecn_default, namespace=tos_ecn_namespace,
378
+ reversed=tos_ecn_reversed, pack=False)
379
+
380
+ proto = self._make_index(protocol, protocol_default, namespace=protocol_namespace,
381
+ reversed=protocol_reversed, pack=False)
382
+ ttl_val = ttl if isinstance(ttl, int) else math.ceil(ttl.total_seconds())
383
+
384
+ if options is not None:
385
+ options_value, total_length = self._make_ipv4_options(options)
386
+ else:
387
+ options_value, total_length = [], 0
388
+
389
+ ihl = 5 + math.ceil(total_length / 4)
390
+ len = ihl * 4 + len(payload)
391
+
392
+ return Schema_IPv4(
393
+ vihl={
394
+ 'version': 4,
395
+ 'ihl': ihl,
396
+ },
397
+ tos={
398
+ 'pre': tos_pre_val,
399
+ 'del': tos_del_val,
400
+ 'thr': tos_thr_val,
401
+ 'rel': tos_rel_val,
402
+ 'ecn': tos_ecn_val,
403
+ },
404
+ length=len,
405
+ id=id,
406
+ flags={
407
+ 'df': df,
408
+ 'mf': mf,
409
+ 'offset': offset,
410
+ },
411
+ ttl=ttl_val,
412
+ proto=proto, # type: ignore[arg-type]
413
+ chksum=checksum,
414
+ src=src,
415
+ dst=dst,
416
+ options=options_value,
417
+ payload=payload,
418
+ )
419
+
420
+ @classmethod
421
+ def id(cls) -> 'tuple[Literal["IPv4"]]': # type: ignore[override]
422
+ """Index ID of the protocol.
423
+
424
+ Returns:
425
+ Index ID of the protocol.
426
+
427
+ """
428
+ return ('IPv4',)
429
+
430
+ @classmethod
431
+ def register_option(cls, code: 'Enum_OptionNumber', meth: 'str | tuple[OptionParser, OptionConstructor]') -> 'None':
432
+ """Register an option parser.
433
+
434
+ Args:
435
+ code: IPv4 option code.
436
+ meth: Method name or callable to parse and/or construct the option.
437
+
438
+ """
439
+ name = code.name.lower()
440
+ if hasattr(cls, f'_read_opt_{name}'):
441
+ warn(f'option {code} already registered, overwriting', RegistryWarning)
442
+
443
+ if isinstance(meth, str):
444
+ meth = (getattr(cls, f'_read_opt_{meth}', cls._read_opt_unassigned), # type: ignore[arg-type]
445
+ getattr(cls, f'_make_opt_{meth}', cls._make_opt_unassigned)) # type: ignore[arg-type]
446
+
447
+ setattr(cls, f'_read_opt_{name}', meth[0])
448
+ setattr(cls, f'_make_opt_{name}', meth[1])
449
+
450
+ ##########################################################################
451
+ # Data models.
452
+ ##########################################################################
453
+
454
+ def __length_hint__(self) -> 'Literal[20]':
455
+ """Return an estimated length for the object."""
456
+ return 20
457
+
458
+ @classmethod
459
+ def __index__(cls) -> 'Enum_TransType': # pylint: disable=invalid-index-returned
460
+ """Numeral registry index of the protocol.
461
+
462
+ Returns:
463
+ Numeral registry index of the protocol in `IANA`_.
464
+
465
+ .. _IANA: https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
466
+
467
+ """
468
+ return Enum_TransType.IPv4 # type: ignore[return-value]
469
+
470
+ ##########################################################################
471
+ # Utilities.
472
+ ##########################################################################
473
+
474
+ @classmethod
475
+ def _make_data(cls, data: 'Data_IPv4') -> 'dict[str, Any]': # type: ignore[override]
476
+ """Create key-value pairs from ``data`` for protocol construction.
477
+
478
+ Args:
479
+ data: protocol data
480
+
481
+ Returns:
482
+ Key-value pairs for protocol construction.
483
+
484
+ """
485
+ return {
486
+ 'tos_pre': data.tos.pre,
487
+ 'tos_del': data.tos['del'],
488
+ 'tos_thr': data.tos.thr,
489
+ 'tos_rel': data.tos.rel,
490
+ 'tos_ecn': data.tos.ecn,
491
+ 'id': data.id,
492
+ 'df': data.flags.df,
493
+ 'mf': data.flags.mf,
494
+ 'offset': data.offset,
495
+ 'ttl': data.ttl,
496
+ 'protocol': data.protocol,
497
+ 'checksum': data.checksum,
498
+ 'src': data.src,
499
+ 'dst': data.dst,
500
+ 'options': data.options,
501
+ 'payload': cls._make_payload(data),
502
+ }
503
+
504
+ def _read_ipv4_addr(self) -> 'IPv4Address':
505
+ """Read IP address.
506
+
507
+ Returns:
508
+ Parsed IP address.
509
+
510
+ """
511
+ _byte = self._read_fileng(4)
512
+ # _addr = '.'.join([str(_) for _ in _byte])
513
+ # return _addr
514
+ return ipaddress.ip_address(_byte) # type: ignore[return-value]
515
+
516
+ def _read_ipv4_opt_type(self, code: 'int') -> 'Data_OptionType':
517
+ """Read option type field.
518
+
519
+ Arguments:
520
+ code: option kind value
521
+
522
+ Returns:
523
+ Extracted IPv4 option type, as an object of the option flag (copied
524
+ flag), option class, and option number.
525
+
526
+ """
527
+ oflg = bool(code >> 7)
528
+ ocls = Enum_OptionClass.get((code >> 5) & 0b11)
529
+ onum = code & 0b11111
530
+
531
+ return Data_OptionType.from_dict({
532
+ 'change': oflg,
533
+ 'class': ocls,
534
+ 'number': onum,
535
+ })
536
+
537
+ def _read_ipv4_options(self, length: 'int') -> 'Option':
538
+ """Read IPv4 option list.
539
+
540
+ Arguments:
541
+ length: length of options
542
+
543
+ Returns:
544
+ Extracted IPv4 options.
545
+
546
+ Raises:
547
+ ProtocolError: If the threshold is **NOT** matching.
548
+
549
+ """
550
+ counter = 0 # length of read option list
551
+ options = OrderedMultiDict() # type: Option
552
+
553
+ for schema in self.__header__.options:
554
+ kind = schema.type
555
+ name = kind.name.lower()
556
+
557
+ meth_name = f'_read_opt_{name}'
558
+ meth = cast('OptionParser',
559
+ getattr(self, meth_name, self._read_opt_unassigned))
560
+ data = meth(schema, options=options)
561
+
562
+ # record option data
563
+ counter += data.length
564
+ options.add(kind, data)
565
+
566
+ # break when End of Option List (EOOL) triggered
567
+ if kind == Enum_OptionNumber.EOOL:
568
+ break
569
+
570
+ # check threshold
571
+ if counter > length:
572
+ raise ProtocolError(f'IPv4: invalid format')
573
+ return options
574
+
575
+ def _read_opt_unassigned(self, schema: 'Schema_UnassignedOption', *, options: 'Option') -> 'Data_UnassignedOption': # pylint: disable=unused-argument
576
+ """Read IPv4 unassigned options.
577
+
578
+ Structure of IPv4 unassigned options [:rfc:`791`]:
579
+
580
+ .. code-block:: text
581
+
582
+ 0 1 2 3
583
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
584
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
585
+ | type | length | option data ...
586
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
587
+
588
+ Arguments:
589
+ schema: parsed option schema
590
+ options: extracted IPv4 options
591
+
592
+ Returns:
593
+ Parsed option data.
594
+
595
+ Raises:
596
+ ProtocolError: If ``length`` is **LESS THAN** ``3``.
597
+
598
+ """
599
+ if schema.length < 3:
600
+ raise ProtocolError(f'{self.alias}: [OptNo {schema.type}] invalid format')
601
+
602
+ opt = Data_UnassignedOption(
603
+ code=schema.type,
604
+ type=self._read_ipv4_opt_type(schema.type),
605
+ length=schema.length,
606
+ data=schema.data,
607
+ )
608
+ return opt
609
+
610
+ def _read_opt_eool(self, schema: 'Schema_EOOLOption', *, options: 'Option') -> 'Data_EOOLOption': # pylint: disable=unused-argument
611
+ """Read IPv4 End of Option List (``EOOL``) option.
612
+
613
+ Structure of IPv4 End of Option List (``EOOL``) option [:rfc:`719`]:
614
+
615
+ .. code-block:: text
616
+
617
+ +--------+
618
+ |00000000|
619
+ +--------+
620
+ Type=0
621
+
622
+ Arguments:
623
+ schema: parsed option schema
624
+ options: extracted IPv4 options
625
+
626
+ Returns:
627
+ Parsed option data.
628
+
629
+ """
630
+ opt = Data_EOOLOption(
631
+ code=schema.type,
632
+ type=self._read_ipv4_opt_type(schema.type),
633
+ length=1,
634
+ )
635
+ return opt
636
+
637
+ def _read_opt_nop(self, schema: 'Schema_NOPOption', *, options: 'Option') -> 'Data_NOPOption': # pylint: disable=unused-argument
638
+ """Read IPv4 No Operation (``NOP``) option.
639
+
640
+ Structure of IPv4 No Operation (``NOP``) option [:rfc:`719`]:
641
+
642
+ .. code-block:: text
643
+
644
+ +--------+
645
+ |00000001|
646
+ +--------+
647
+ Type=1
648
+
649
+ Arguments:
650
+ schema: parsed option schema
651
+ options: extracted IPv4 options
652
+
653
+ Returns:
654
+ Parsed option data.
655
+
656
+ """
657
+ opt = Data_NOPOption(
658
+ code=schema.type,
659
+ type=self._read_ipv4_opt_type(schema.type),
660
+ length=1,
661
+ )
662
+ return opt
663
+
664
+ def _read_opt_sec(self, schema: 'Schema_SECOption', *, options: 'Option') -> 'Data_SECOption': # pylint: disable=unused-argument
665
+ """Read IPv4 Security (``SEC``) option.
666
+
667
+ Structure of IPv4 Security (``SEC``) option [:rfc:`1108`]:
668
+
669
+ .. code-block:: text
670
+
671
+ +------------+------------+------------+-------------//----------+
672
+ | 10000010 | XXXXXXXX | SSSSSSSS | AAAAAAA[1] AAAAAAA0 |
673
+ | | | | [0] |
674
+ +------------+------------+------------+-------------//----------+
675
+ TYPE = 130 LENGTH CLASSIFICATION PROTECTION
676
+ LEVEL AUTHORITY
677
+ FLAGS
678
+
679
+ Arguments:
680
+ schema: parsed option schema
681
+ options: extracted IPv4 options
682
+
683
+ Returns:
684
+ Parsed option data.
685
+
686
+ Raises:
687
+ ProtocolError: If ``length`` is **LESS THAN** ``3``.
688
+
689
+ """
690
+ if schema.length < 3:
691
+ raise ProtocolError(f'{self.alias}: [OptNo {schema.type}] invalid format')
692
+
693
+ if schema.length > 3:
694
+ flags = [] # type: list[Enum_ProtectionAuthority]
695
+ for base, byte in enumerate(schema.data):
696
+ for bit in range(7):
697
+ authority = Enum_ProtectionAuthority.get(base * 8 + bit)
698
+ if byte & (0x80 >> bit):
699
+ if 'Unassigned' in authority.name:
700
+ warn(f'{self.alias}: [OptNo {schema.type}] invalid format: unknown protection authority: {authority}', ProtocolWarning)
701
+ flags.append(authority)
702
+
703
+ if byte & 0x01 == 1 and base < schema.length - 4:
704
+ #raise ProtocolError(f'{self.alias}: [OptNo {kind}] invalid format: remaining data')
705
+ warn(f'{self.alias}: [OptNo {schema.type}] invalid format: remaining data', ProtocolWarning)
706
+
707
+ if schema.data[-1] & 0x01 == 0:
708
+ warn(f'{self.alias}: [OptNo {schema.type}] invalid format: field termination indicator not set', ProtocolWarning)
709
+ else:
710
+ flags = []
711
+
712
+ opt = Data_SECOption(
713
+ code=schema.type,
714
+ type=self._read_ipv4_opt_type(schema.type),
715
+ length=schema.length,
716
+ level=schema.level,
717
+ flags=tuple(flags),
718
+ )
719
+
720
+ return opt
721
+
722
+ def _read_opt_lsr(self, schema: 'Schema_LSROption', *, options: 'Option') -> 'Data_LSROption': # pylint: disable=unused-argument
723
+ """Read IPv4 Loose Source Route (``LSR``) option.
724
+
725
+ Structure of IPv4 Loose Source Route (``LSR``) option [:rfc:`791`]:
726
+
727
+ .. code-block:: text
728
+
729
+ +--------+--------+--------+---------//--------+
730
+ |10000011| length | pointer| route data |
731
+ +--------+--------+--------+---------//--------+
732
+
733
+ Arguments:
734
+ schema: parsed option schema
735
+ options: extracted IPv4 options
736
+
737
+ Returns:
738
+ Parsed option data.
739
+
740
+ Raises:
741
+ ProtocolError: If option is malformed.
742
+
743
+ """
744
+ if schema.length < 3 or (schema.length - 3) % 4 != 0:
745
+ raise ProtocolError(f'{self.alias}: [OptNo {schema.type}] invalid format')
746
+ if schema.pointer < 4:
747
+ raise ProtocolError(f'{self.alias}: [OptNo {schema.type}] invalid format: pointer too small: {schema.pointer}')
748
+
749
+ opt = Data_LSROption(
750
+ code=schema.type,
751
+ type=self._read_ipv4_opt_type(schema.type),
752
+ length=schema.length,
753
+ pointer=schema.pointer,
754
+ route=tuple(schema.route),
755
+ )
756
+ return opt
757
+
758
+ def _read_opt_ts(self, schema: 'Schema_TSOption', *, options: 'Option') -> 'Data_TSOption': # pylint: disable=unused-argument
759
+ """Read IPv4 Time Stamp (``TS``) option.
760
+
761
+ Structure of IPv4 Time Stamp (``TS``) option [:rfc:`791`]:
762
+
763
+ .. code-block:: text
764
+
765
+ +--------+--------+--------+--------+
766
+ |01000100| length | pointer|oflw|flg|
767
+ +--------+--------+--------+--------+
768
+ | internet address |
769
+ +--------+--------+--------+--------+
770
+ | timestamp |
771
+ +--------+--------+--------+--------+
772
+ | . |
773
+ .
774
+ .
775
+
776
+ Arguments:
777
+ schema: parsed option schema
778
+ options: extracted IPv4 options
779
+
780
+ Returns:
781
+ Parsed option data.
782
+
783
+ Raises:
784
+ ProtocolError: If the option is malformed.
785
+
786
+ """
787
+ if schema.length > 40 or schema.length < 4:
788
+ raise ProtocolError(f'{self.alias}: [OptNo {schema.type}] invalid format')
789
+ if schema.pointer < 5:
790
+ raise ProtocolError(f'{self.alias}: [OptNo {schema.type}] invalid format: pointer too small: {schema.pointer}')
791
+
792
+ opt = Data_TSOption(
793
+ code=schema.type,
794
+ type=self._read_ipv4_opt_type(schema.type),
795
+ length=schema.length,
796
+ pointer=schema.pointer,
797
+ overflow=schema.flags['oflw'],
798
+ flag=schema.ts_flag,
799
+ timestamp=schema.timestamp,
800
+ )
801
+ return opt
802
+
803
+ def _read_opt_e_sec(self, schema: 'Schema_ESECOption', *, options: 'Option') -> 'Data_ESECOption': # pylint: disable=unused-argument
804
+ """Read IPv4 Extended Security (``E-SEC``) option.
805
+
806
+ Structure of IPv4 Extended Security (``E-SEC``) option [:rfc:`1108`]:
807
+
808
+ .. code-block:: text
809
+
810
+ +------------+------------+------------+-------//-------+
811
+ | 10000101 | 000LLLLL | AAAAAAAA | add sec info |
812
+ +------------+------------+------------+-------//-------+
813
+ TYPE = 133 LENGTH ADDITIONAL ADDITIONAL
814
+ SECURITY INFO SECURITY
815
+ FORMAT CODE INFO
816
+
817
+ Arguments:
818
+ schema: parsed option schema
819
+ options: extracted IPv4 options
820
+
821
+ Returns:
822
+ Parsed option data.
823
+
824
+ Raises:
825
+ ProtocolError: If ``length`` is **LESS THAN** ``3``.
826
+
827
+ """
828
+ if schema.length < 3:
829
+ raise ProtocolError(f'{self.alias}: [OptNo {schema.type}] invalid format')
830
+
831
+ opt = Data_ESECOption(
832
+ code=schema.type,
833
+ type=self._read_ipv4_opt_type(schema.type),
834
+ length=schema.length,
835
+ format=schema.format,
836
+ info=schema.info,
837
+ )
838
+ return opt
839
+
840
+ def _read_opt_rr(self, schema: 'Schema_RROption', *, options: 'Option') -> 'Data_RROption': # pylint: disable=unused-argument
841
+ """Read IPv4 Record Route (``RR``) option.
842
+
843
+ Structure of IPv4 Record Route (``RR``) option [:rfc:`791`]:
844
+
845
+ .. code-block:: text
846
+
847
+ +--------+--------+--------+---------//--------+
848
+ |00000111| length | pointer| route data |
849
+ +--------+--------+--------+---------//--------+
850
+ Type=7
851
+
852
+ Arguments:
853
+ schema: parsed option schema
854
+ options: extracted IPv4 options
855
+
856
+ Returns:
857
+ Parsed option data.
858
+
859
+ Raises:
860
+ ProtocolError: If option is malformed.
861
+
862
+ """
863
+ if schema.length < 3 or (schema.length - 3) % 4 != 0:
864
+ raise ProtocolError(f'{self.alias}: [OptNo {schema.type}] invalid format')
865
+ if schema.pointer < 4:
866
+ raise ProtocolError(f'{self.alias}: [OptNo {schema.type}] invalid format: pointer too small: {schema.pointer}')
867
+
868
+ opt = Data_RROption(
869
+ code=schema.type,
870
+ type=self._read_ipv4_opt_type(schema.type),
871
+ length=schema.length,
872
+ pointer=schema.pointer,
873
+ route=tuple(schema.route),
874
+ )
875
+ return opt
876
+
877
+ def _read_opt_sid(self, schema: 'Schema_SIDOption', *, options: 'Option') -> 'Data_SIDOption': # pylint: disable=unused-argument
878
+ """Read IPv4 Stream ID (``SID``) option.
879
+
880
+ Structure of IPv4 Stream ID (``SID``) option [:rfc:`791`][:rfc:`6814`]:
881
+
882
+ .. code-block:: text
883
+
884
+ +--------+--------+--------+--------+
885
+ |10001000|00000010| Stream ID |
886
+ +--------+--------+--------+--------+
887
+ Type=136 Length=4
888
+
889
+ Arguments:
890
+ schema: parsed option schema
891
+ options: extracted IPv4 options
892
+
893
+ Returns:
894
+ Parsed option data.
895
+
896
+ Raises:
897
+ ProtocolError: If ``length`` is **NOT** ``4``.
898
+
899
+ """
900
+ if schema.length != 4:
901
+ raise ProtocolError(f'{self.alias}: [OptNo {schema.type}] invalid format')
902
+
903
+ opt = Data_SIDOption(
904
+ code=schema.type,
905
+ type=self._read_ipv4_opt_type(schema.type),
906
+ length=schema.length,
907
+ sid=schema.sid,
908
+ )
909
+ return opt
910
+
911
+ def _read_opt_ssr(self, schema: 'Schema_SSROption', *, options: 'Option') -> 'Data_SSROption': # pylint: disable=unused-argument
912
+ """Read IPv4 Strict Source Route (``SSR``) option.
913
+
914
+ Structure of IPv4 Strict Source Route (``SSR``) option [:rfc:`791`]:
915
+
916
+ .. code-block:: text
917
+
918
+ +--------+--------+--------+---------//--------+
919
+ |10001001| length | pointer| route data |
920
+ +--------+--------+--------+---------//--------+
921
+ Type=137
922
+
923
+ Arguments:
924
+ schema: parsed option schema
925
+ options: extracted IPv4 options
926
+
927
+ Returns:
928
+ Parsed option data.
929
+
930
+ Raises:
931
+ ProtocolError: If option is malformed.
932
+
933
+ """
934
+ if schema.length < 3 or (schema.length - 3) % 4 != 0:
935
+ raise ProtocolError(f'{self.alias}: [OptNo {schema.type}] invalid format')
936
+ if schema.pointer < 4:
937
+ raise ProtocolError(f'{self.alias}: [OptNo {schema.type}] invalid format: pointer too small: {schema.pointer}')
938
+
939
+ opt = Data_SSROption(
940
+ code=schema.type,
941
+ type=self._read_ipv4_opt_type(schema.type),
942
+ length=schema.length,
943
+ pointer=schema.pointer,
944
+ route=tuple(schema.route),
945
+ )
946
+ return opt
947
+
948
+ def _read_opt_mtup(self, schema: 'Schema_MTUPOption', *, options: 'Option') -> 'Data_MTUPOption': # pylint: disable=unused-argument
949
+ """Read IPv4 MTU Probe (``MTUP``) option.
950
+
951
+ Structure of IPv4 MTU Probe (``MTUP``) option [:rfc:`1063`][:rfc:`1191`]:
952
+
953
+ .. code-block:: text
954
+
955
+ +--------+--------+--------+--------+
956
+ |00001011|00000100| 2 octet value |
957
+ +--------+--------+--------+--------+
958
+
959
+ Arguments:
960
+ schema: parsed option schema
961
+ options: extracted IPv4 options
962
+
963
+ Returns:
964
+ Parsed option data.
965
+
966
+ Raises:
967
+ ProtocolError: If ``length`` is **NOT** ``4``.
968
+
969
+ """
970
+ if schema.length != 4:
971
+ raise ProtocolError(f'{self.alias}: [OptNo {schema.type}] invalid format')
972
+
973
+ opt = Data_MTUPOption(
974
+ code=schema.type,
975
+ type=self._read_ipv4_opt_type(schema.type),
976
+ length=schema.length,
977
+ mtu=schema.mtu,
978
+ )
979
+ return opt
980
+
981
+ def _read_opt_mtur(self, schema: 'Schema_MTUROption', *, options: 'Option') -> 'Data_MTUROption': # pylint: disable=unused-argument
982
+ """Read IPv4 MTU Reply (``MTUR``) option.
983
+
984
+ Structure of IPv4 MTU Reply (``MTUR``) option [:rfc:`1063`][:rfc:`1191`]:
985
+
986
+ .. code-block:: text
987
+
988
+ +--------+--------+--------+--------+
989
+ |00001100|00000100| 2 octet value |
990
+ +--------+--------+--------+--------+
991
+
992
+ Arguments:
993
+ schema: parsed option schema
994
+ options: extracted IPv4 options
995
+
996
+ Returns:
997
+ Parsed option data.
998
+
999
+ Raises:
1000
+ ProtocolError: If ``length`` is **NOT** ``4``.
1001
+
1002
+ """
1003
+ if schema.length != 4:
1004
+ raise ProtocolError(f'{self.alias}: [OptNo {schema.type}] invalid format')
1005
+
1006
+ opt = Data_MTUROption(
1007
+ code=schema.type,
1008
+ type=self._read_ipv4_opt_type(schema.type),
1009
+ length=schema.length,
1010
+ mtu=schema.mtu,
1011
+ )
1012
+ return opt
1013
+
1014
+ def _read_opt_tr(self, schema: 'Schema_TROption', *, options: 'Option') -> 'Data_TROption': # pylint: disable=unused-argument
1015
+ """Read IPv4 Traceroute (``TR``) option.
1016
+
1017
+ Structure of IPv4 Traceroute (``TR``) option [:rfc:`1393`][:rfc:`6814`]:
1018
+
1019
+ .. code-block:: text
1020
+
1021
+ 0 8 16 24
1022
+ +-+-+-+-+-+-+-+-+---------------+---------------+---------------+
1023
+ |F| C | Number | Length | ID Number |
1024
+ +-+-+-+-+-+-+-+-+---------------+---------------+---------------+
1025
+ | Outbound Hop Count | Return Hop Count |
1026
+ +---------------+---------------+---------------+---------------+
1027
+ | Originator IP Address |
1028
+ +---------------+---------------+---------------+---------------+
1029
+
1030
+ Arguments:
1031
+ schema: parsed option schema
1032
+ options: extracted IPv4 options
1033
+
1034
+ Returns:
1035
+ Parsed option data.
1036
+
1037
+ Raises:
1038
+ ProtocolError: If ``length`` is **NOT** ``12``.
1039
+
1040
+ """
1041
+ if schema.length != 12:
1042
+ raise ProtocolError(f'{self.alias}: [OptNo {schema.type}] invalid format')
1043
+
1044
+ opt = Data_TROption.from_dict({
1045
+ 'code': schema.type,
1046
+ 'type': self._read_ipv4_opt_type(schema.type),
1047
+ 'length': schema.length,
1048
+ 'id': schema.id,
1049
+ 'outbound': schema.out,
1050
+ 'return': schema.ret,
1051
+ 'originator': schema.origin,
1052
+ })
1053
+ return opt
1054
+
1055
+ def _read_opt_rtralt(self, schema: 'Schema_RTRALTOption', *, options: 'Option') -> 'Data_RTRALTOption': # pylint: disable=unused-argument
1056
+ """Read IPv4 Router Alert (``RTRALT``) option.
1057
+
1058
+ Structure of IPv4 Router Alert (``RTRALT``) option [:rfc:`2113`]:
1059
+
1060
+ .. code:: text
1061
+
1062
+ +--------+--------+--------+--------+
1063
+ |10010100|00000100| 2 octet value |
1064
+ +--------+--------+--------+--------+
1065
+
1066
+ Arguments:
1067
+ schema: parsed option schema
1068
+ options: extracted IPv4 options
1069
+
1070
+ Returns:
1071
+ Parsed option data.
1072
+
1073
+ Raises:
1074
+ ProtocolError: If ``length`` is **NOT** ``4``.
1075
+
1076
+ """
1077
+ if schema.length != 4:
1078
+ raise ProtocolError(f'{self.alias}: [OptNo {schema.type}] invalid format')
1079
+
1080
+ opt = Data_RTRALTOption(
1081
+ code=schema.type,
1082
+ type=self._read_ipv4_opt_type(schema.type),
1083
+ length=schema.length,
1084
+ alert=schema.alert,
1085
+ )
1086
+ return opt
1087
+
1088
+ def _read_opt_qs(self, schema: 'Schema_QSOption', *, options: 'Option') -> 'Data_QSOption': # pylint: disable=unused-argument
1089
+ """Read IPv4 Quick Start (``QS``) option.
1090
+
1091
+ Structure of IPv4 Quick Start (``QS``) option [:rfc:`4782`]:
1092
+
1093
+ * A Quick-Start Request
1094
+
1095
+ .. code-block:: text
1096
+
1097
+ 0 1 2 3
1098
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1099
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1100
+ | Option | Length=8 | Func. | Rate | QS TTL |
1101
+ | | | 0000 |Request| |
1102
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1103
+ | QS Nonce | R |
1104
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1105
+
1106
+ * Report of Approved Rate
1107
+
1108
+ .. code-block:: text
1109
+
1110
+ 0 1 2 3
1111
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1112
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1113
+ | Option | Length=8 | Func. | Rate | Not Used |
1114
+ | | | 1000 | Report| |
1115
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1116
+ | QS Nonce | R |
1117
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1118
+
1119
+ Arguments:
1120
+ schema: parsed option schema
1121
+ options: extracted IPv4 options
1122
+
1123
+ Returns:
1124
+ Parsed option data.
1125
+
1126
+ Raises:
1127
+ ProtocolError: If the option is malformed.
1128
+
1129
+ """
1130
+ if schema.length != 8:
1131
+ raise ProtocolError(f'{self.alias}: [OptNo {schema.type}] invalid format')
1132
+
1133
+ func = schema.func
1134
+ if func == Enum_QSFunction.Quick_Start_Request:
1135
+ schema_req = cast('Schema_QuickStartRequestOption', schema)
1136
+
1137
+ rate = schema_req.flags['rate']
1138
+ opt = Data_QuickStartRequestOption(
1139
+ code=schema.type,
1140
+ type=self._read_ipv4_opt_type(schema.type),
1141
+ length=schema_req.length,
1142
+ func=func,
1143
+ rate=40000 * (2 ** rate) / 1000 if rate > 0 else 0,
1144
+ ttl=datetime.timedelta(seconds=schema_req.ttl),
1145
+ nonce=schema_req.nonce['nonce'],
1146
+ ) # type: Data_QSOption
1147
+ elif func == Enum_QSFunction.Report_of_Approved_Rate:
1148
+ schema_rep = cast('Schema_QuickStartReportOption', schema)
1149
+
1150
+ rate = schema_rep.flags['rate']
1151
+ opt = Data_QuickStartReportOption(
1152
+ code=schema.type,
1153
+ type=self._read_ipv4_opt_type(schema.type),
1154
+ length=schema_rep.length,
1155
+ func=func,
1156
+ rate=40000 * (2 ** rate) / 1000 if rate > 0 else 0,
1157
+ nonce=schema_rep.nonce['nonce'],
1158
+ )
1159
+ else:
1160
+ raise ProtocolError(f'{self.alias}: [OptNo {schema.type}] unknown QS function: {func}')
1161
+ return opt
1162
+
1163
+ def _make_ipv4_options(self, options: 'list[Schema_Option | tuple[Enum_OptionNumber, dict[str, Any]] | bytes] | Option') -> 'tuple[list[Schema_Option | bytes], int]':
1164
+ """Make options for IPv4.
1165
+
1166
+ Args:
1167
+ option: IPv4 options
1168
+
1169
+ Returns:
1170
+ Tuple of options and total length of options.
1171
+
1172
+ """
1173
+ total_length = 0
1174
+ if isinstance(options, list):
1175
+ options_list = [] # type: list[Schema_Option | bytes]
1176
+ for schema in options:
1177
+ if isinstance(schema, bytes):
1178
+ code = Enum_OptionNumber.get(schema[0])
1179
+ if code in (Enum_OptionNumber.NOP, Enum_OptionNumber.EOOL): # ignore padding options by default
1180
+ continue
1181
+
1182
+ data = schema # type: Schema_Option | bytes
1183
+ data_len = len(data)
1184
+ elif isinstance(schema, Schema):
1185
+ code = schema.type
1186
+ if code in (Enum_OptionNumber.NOP, Enum_OptionNumber.EOOL): # ignore padding options by default
1187
+ continue
1188
+
1189
+ data = schema
1190
+ data_len = len(schema.pack())
1191
+ else:
1192
+ code, args = cast('tuple[Enum_OptionNumber, dict[str, Any]]', schema)
1193
+ if code in (Enum_OptionNumber.NOP, Enum_OptionNumber.EOOL): # ignore padding options by default
1194
+ continue
1195
+
1196
+ name = f'_make_opt_{code.name.lower()}'
1197
+ meth = cast('OptionConstructor',
1198
+ getattr(self, name, self._make_opt_unassigned))
1199
+
1200
+ data = meth(code, **args)
1201
+ data_len = len(data.pack())
1202
+
1203
+ options_list.append(data)
1204
+ total_length += data_len
1205
+
1206
+ # force alignment to 32-bit boundary
1207
+ if data_len % 4:
1208
+ pad_len = 4 - (data_len % 4)
1209
+ pad_opt = self._make_opt_nop(Enum_OptionNumber.NOP) # type: ignore[arg-type]
1210
+ total_length += pad_len
1211
+
1212
+ for _ in range(pad_len - 1):
1213
+ options_list.append(pad_opt)
1214
+ options_list.append(Enum_OptionNumber.EOOL) # type: ignore[arg-type]
1215
+ return options_list, total_length
1216
+
1217
+ options_list = []
1218
+ for code, option in options.items(multi=True):
1219
+ # ignore padding options by default
1220
+ if code in (Enum_OptionNumber.NOP, Enum_OptionNumber.EOOL):
1221
+ continue
1222
+
1223
+ name = f'_make_opt_{code.name.lower()}'
1224
+ meth = cast('OptionConstructor',
1225
+ getattr(self, name, self._make_opt_unassigned))
1226
+
1227
+ data = meth(code, option)
1228
+ data_len = len(data.pack())
1229
+
1230
+ options_list.append(data)
1231
+ total_length += data_len
1232
+
1233
+ # force alignment to 32-bit boundary
1234
+ if data_len % 4:
1235
+ pad_len = 4 - (data_len % 4)
1236
+ pad_opt = self._make_opt_nop(Enum_OptionNumber.NOP) # type: ignore[arg-type]
1237
+ total_length += pad_len
1238
+
1239
+ for _ in range(pad_len - 1):
1240
+ options_list.append(pad_opt)
1241
+ options_list.append(Enum_OptionNumber.EOOL) # type: ignore[arg-type]
1242
+ return options_list, total_length
1243
+
1244
+ def _make_opt_unassigned(self, kind: 'Enum_OptionNumber', option: 'Optional[Data_UnassignedOption]' = None, *,
1245
+ data: 'bytes',
1246
+ **kwargs: 'Any') -> 'Schema_Option':
1247
+ """Make IPv4 unassigned options.
1248
+
1249
+ Args:
1250
+ kind: option type code
1251
+ option: option data
1252
+ data: option payload
1253
+ **kwargs: arbitrary keyword arguments
1254
+
1255
+ Returns:
1256
+ Constructured option schema.
1257
+
1258
+ """
1259
+ if option is not None:
1260
+ data = option.data
1261
+
1262
+ return Schema_UnassignedOption(
1263
+ type=kind,
1264
+ length=len(data),
1265
+ data=data,
1266
+ )
1267
+
1268
+ def _make_opt_eool(self, kind: 'Enum_OptionNumber', option: 'Optional[Data_EOOLOption]' = None,
1269
+ **kwargs: 'Any') -> 'Schema_EOOLOption':
1270
+ """Make IPv4 End of Option List (``EOOL``) option.
1271
+
1272
+ Args:
1273
+ kind: option type code
1274
+ option: option data
1275
+ **kwargs: arbitrary keyword arguments
1276
+
1277
+ Returns:
1278
+ Constructured option schema.
1279
+
1280
+ """
1281
+ return Schema_EOOLOption(
1282
+ type=kind,
1283
+ length=1,
1284
+ )
1285
+
1286
+ def _make_opt_nop(self, kind: 'Enum_OptionNumber', option: 'Optional[Data_NOPOption]' = None,
1287
+ **kwargs: 'Any') -> 'Schema_NOPOption':
1288
+ """Make IPv4 No Operation (``NOP``) option.
1289
+
1290
+ Args:
1291
+ kind: option type code
1292
+ option: option data
1293
+ **kwargs: arbitrary keyword arguments
1294
+
1295
+ Returns:
1296
+ Constructured option schema.
1297
+
1298
+ """
1299
+ return Schema_NOPOption(
1300
+ type=kind,
1301
+ length=1,
1302
+ )
1303
+
1304
+ def _make_opt_sec(self, kind: 'Enum_OptionNumber', option: 'Optional[Data_SECOption]' = None, *,
1305
+ level: 'Enum_ClassificationLevel | StdlibEnum | AenumEnum | int | str' = Enum_ClassificationLevel.Unclassified,
1306
+ level_default: 'Optional[int]' = None,
1307
+ level_namespace: 'Optional[dict[str, int] | dict[int, str] | Type[StdlibEnum] | Type[AenumEnum]]' = None, # pylint: disable=line-too-long
1308
+ level_reversed: 'bool' = False,
1309
+ authorities: 'Optional[list[Enum_ProtectionAuthority]]' = None,
1310
+ **kwargs: 'Any') -> 'Schema_SECOption':
1311
+ """Make IPv4 Security (``SEC``) option.
1312
+
1313
+ Args:
1314
+ kind: option type code
1315
+ option: option data
1316
+ sec: security option
1317
+ **kwargs: arbitrary keyword arguments
1318
+
1319
+ Returns:
1320
+ Constructured option schema.
1321
+
1322
+ """
1323
+ if option is not None:
1324
+ level_val = option.level
1325
+ authorities = cast('list[Enum_ProtectionAuthority]', option.flags)
1326
+ else:
1327
+ level_val = self._make_index(level, level_default, namespace=level_namespace, # type: ignore[assignment]
1328
+ reversed=level_reversed, pack=False)
1329
+ authorities = [] if authorities is None else authorities
1330
+
1331
+ if authorities:
1332
+ max_auth = max(authorities)
1333
+ int_len = math.ceil(max_auth / 8)
1334
+
1335
+ data_list = [b'0' for _ in range(int_len * 8)]
1336
+ for auth in authorities:
1337
+ data_list[auth] = b'1'
1338
+ data = int(b''.join(data_list), base=2).to_bytes(int_len, 'big', signed=False)
1339
+ else:
1340
+ data = b''
1341
+
1342
+ return Schema_SECOption(
1343
+ type=kind,
1344
+ length=3 + len(data),
1345
+ level=level_val,
1346
+ data=data,
1347
+ )
1348
+
1349
+ def _make_opt_lsr(self, kind: 'Enum_OptionNumber', option: 'Optional[Data_LSROption]' = None, *,
1350
+ counts: 'int' = 10, # reasonable default
1351
+ route: 'Optional[list[IPv4Address | str | bytes | int]]' = None,
1352
+ **kwargs: 'Any') -> 'Schema_LSROption':
1353
+ """Make IPv4 Loose Source and Record Route (``LSR``) option.
1354
+
1355
+ Args:
1356
+ kind: option type code
1357
+ option: option data
1358
+ counts: maximum number of addresses to record
1359
+ route: list of IPv4 addresses as recorded routes
1360
+ **kwargs: arbitrary keyword arguments
1361
+
1362
+ Returns:
1363
+ Constructured option schema.
1364
+
1365
+ """
1366
+ if option is not None:
1367
+ route = cast('list[IPv4Address | str | bytes | int]', option.route)
1368
+ pointer = option.pointer
1369
+ length = option.length
1370
+ else:
1371
+ route = [] if route is None else route
1372
+ length = 3 + counts * 4
1373
+ pointer = 4 + min(len(route), counts) * 4
1374
+
1375
+ return Schema_LSROption(
1376
+ type=kind,
1377
+ length=length,
1378
+ pointer=pointer,
1379
+ route=route,
1380
+ )
1381
+
1382
+ def _make_opt_ts(self, kind: 'Enum_OptionNumber', option: 'Optional[Data_TSOption]' = None, *,
1383
+ counts: 'int' = 5,
1384
+ overflow: 'int' = 0,
1385
+ timestamp: 'Optional[list[int | timedelta] | dict[IPv4Address, int | timedelta]]' = None,
1386
+ **kwargs: 'Any') -> 'Schema_TSOption':
1387
+ """Make IPv4 Timestamp (``TS``) option.
1388
+
1389
+ Args:
1390
+ kind: option type code
1391
+ option: option data
1392
+ counts: maximum number of timestamps to record
1393
+ timestamp: list of timestamps
1394
+ **kwargs: arbitrary keyword arguments
1395
+
1396
+ Returns:
1397
+ Constructured option schema.
1398
+
1399
+ """
1400
+ if option is not None:
1401
+ ts_list = [] # type: list[int]
1402
+ if isinstance(option.timestamp, tuple):
1403
+ for ts in option.timestamp:
1404
+ if not isinstance(ts, int):
1405
+ ts = math.floor(ts.total_seconds() * 1000)
1406
+
1407
+ if ts.bit_length() > 31:
1408
+ warn(f'{self.alias}: [OptNo {kind}] timestamp value is too large: {ts}', ProtocolWarning)
1409
+ ts = ts | 0x80000000
1410
+ ts_list.append(ts)
1411
+ else:
1412
+ for ip, ts in option.timestamp.items(True):
1413
+ ts_list.append(int(ip))
1414
+ if not isinstance(ts, int):
1415
+ ts = math.floor(ts.total_seconds() * 1000)
1416
+
1417
+ if ts.bit_length() > 31:
1418
+ warn(f'{self.alias}: [OptNo {kind}] timestamp value is too large: {ts}', ProtocolWarning)
1419
+ ts = ts | 0x80000000
1420
+ ts_list.append(ts)
1421
+
1422
+ length = option.length
1423
+ pointer = option.pointer
1424
+ overflow = option.overflow
1425
+ flag = option.flag
1426
+ else:
1427
+ ts_list = []
1428
+ if isinstance(timestamp, list):
1429
+ flag = Enum_TSFlag.Timestamp_Only # type: ignore[assignment]
1430
+ counts = min(9, counts) # 9 is the maximum number of timestamps
1431
+ length = 4 + counts * 4
1432
+
1433
+ for index, ts in enumerate(timestamp):
1434
+ if index >= counts:
1435
+ warn(f'{self.alias}: [OptNo {kind}] too many timestamps: {len(timestamp)}', ProtocolWarning)
1436
+ break
1437
+
1438
+ if not isinstance(ts, int):
1439
+ ts = math.floor(ts.total_seconds() * 1000)
1440
+
1441
+ if ts.bit_length() > 31:
1442
+ warn(f'{self.alias}: [OptNo {kind}] timestamp value is too large: {ts}', ProtocolWarning)
1443
+ ts = ts | 0x80000000
1444
+ ts_list.append(ts)
1445
+ elif isinstance(timestamp, dict):
1446
+ flag = Enum_TSFlag.IP_with_Timestamp # type: ignore[assignment]
1447
+ counts = min(4, counts) # 4 is the maximum number of timestamps
1448
+ length = 4 + counts * 8
1449
+
1450
+ for index, (ip, ts) in enumerate(timestamp.items()):
1451
+ if index >= counts:
1452
+ warn(f'{self.alias}: [OptNo {kind}] too many timestamps: {len(timestamp)}', ProtocolWarning)
1453
+ break
1454
+
1455
+ ts_list.append(int(ip))
1456
+ if not isinstance(ts, int):
1457
+ ts = math.floor(ts.total_seconds() * 1000)
1458
+ if ts == 0:
1459
+ flag = Enum_TSFlag.Prespecified_IP_with_Timestamp # type: ignore[assignment]
1460
+
1461
+ if ts.bit_length() > 31:
1462
+ warn(f'{self.alias}: [OptNo {kind}] timestamp value is too large: {ts}', ProtocolWarning)
1463
+ ts = ts | 0x80000000
1464
+ ts_list.append(ts)
1465
+ else:
1466
+ raise ProtocolError(f'{self.alias}: [OptNo {kind}] invalid timestamp value: {timestamp}')
1467
+ pointer = 5 + len(ts_list) * 4
1468
+
1469
+ return Schema_TSOption(
1470
+ type=kind,
1471
+ length=length,
1472
+ pointer=pointer,
1473
+ flags={
1474
+ 'oflw': overflow,
1475
+ 'flag': flag,
1476
+ },
1477
+ data=ts_list,
1478
+ )
1479
+
1480
+ def _make_opt_e_sec(self, kind: 'Enum_OptionNumber', option: 'Optional[Data_ESECOption]' = None, *,
1481
+ format: 'int' = 0,
1482
+ info: 'Optional[bytes]' = None,
1483
+ **kwargs: 'Any') -> 'Schema_ESECOption':
1484
+ """Make IPv4 Extended Security (``E-SEC``) option.
1485
+
1486
+ Args:
1487
+ kind: option type code
1488
+ option: option data
1489
+ format: additional security information format code
1490
+ info: additional security information
1491
+ **kwargs: arbitrary keyword arguments
1492
+
1493
+ Returns:
1494
+ Constructured option schema.
1495
+
1496
+ """
1497
+ if option is not None:
1498
+ length = option.length
1499
+ format = option.format
1500
+ info = option.info
1501
+ else:
1502
+ length = (3 + len(info)) if info is not None else 3
1503
+
1504
+ return Schema_ESECOption(
1505
+ type=kind,
1506
+ length=length,
1507
+ format=format,
1508
+ info=info,
1509
+ )
1510
+
1511
+ def _make_opt_rr(self, kind: 'Enum_OptionNumber', option: 'Optional[Data_RROption]' = None, *,
1512
+ counts: 'int' = 10, # reasonable default
1513
+ route: 'Optional[list[IPv4Address | str | bytes | int]]' = None,
1514
+ **kwargs: 'Any') -> 'Schema_RROption':
1515
+ """Make IPv4 Record Route (``RR``) option.
1516
+
1517
+ Args:
1518
+ kind: option type code
1519
+ option: option data
1520
+ counts: maximum number of addresses to record
1521
+ route: list of IPv4 addresses as recorded routes
1522
+ **kwargs: arbitrary keyword arguments
1523
+
1524
+ Returns:
1525
+ Constructured option schema.
1526
+
1527
+ """
1528
+ if option is not None:
1529
+ route = cast('list[IPv4Address | str | bytes | int]', option.route)
1530
+ pointer = option.pointer
1531
+ length = option.length
1532
+ else:
1533
+ route = [] if route is None else route
1534
+ length = 3 + counts * 4
1535
+ pointer = 4 + min(len(route), counts) * 4
1536
+
1537
+ return Schema_RROption(
1538
+ type=kind,
1539
+ length=length,
1540
+ pointer=pointer,
1541
+ route=route,
1542
+ )
1543
+
1544
+ def _make_opt_sid(self, kind: 'Enum_OptionNumber', option: 'Optional[Data_SIDOption]' = None, *,
1545
+ sid: 'int' = 0,
1546
+ **kwargs: 'Any') -> 'Schema_SIDOption':
1547
+ """Make IPv4 Stream ID (``SID``) option.
1548
+
1549
+ Args:
1550
+ kind: option type code
1551
+ option: option data
1552
+ sid: stream ID
1553
+ **kwargs: arbitrary keyword arguments
1554
+
1555
+ Returns:
1556
+ Constructured option schema.
1557
+
1558
+ """
1559
+ if option is not None:
1560
+ sid = option.sid
1561
+
1562
+ return Schema_SIDOption(
1563
+ type=kind,
1564
+ length=4,
1565
+ sid=sid,
1566
+ )
1567
+
1568
+ def _make_opt_ssr(self, kind: 'Enum_OptionNumber', option: 'Optional[Data_SSROption]' = None, *,
1569
+ counts: 'int' = 10, # reasonable default
1570
+ route: 'Optional[list[IPv4Address | str | bytes | int]]' = None,
1571
+ **kwargs: 'Any') -> 'Schema_SSROption':
1572
+ """Make IPv4 Strict Source Route (``SSR``) option.
1573
+
1574
+ Args:
1575
+ kind: option type code
1576
+ option: option data
1577
+ counts: maximum number of addresses to record
1578
+ route: list of IPv4 addresses as recorded routes
1579
+ **kwargs: arbitrary keyword arguments
1580
+
1581
+ Returns:
1582
+ Constructured option schema.
1583
+
1584
+ """
1585
+ if option is not None:
1586
+ route = cast('list[IPv4Address | str | bytes | int]', option.route)
1587
+ pointer = option.pointer
1588
+ length = option.length
1589
+ else:
1590
+ route = [] if route is None else route
1591
+ length = 3 + counts * 4
1592
+ pointer = 4 + min(len(route), counts) * 4
1593
+
1594
+ return Schema_SSROption(
1595
+ type=kind,
1596
+ length=length,
1597
+ pointer=pointer,
1598
+ route=route,
1599
+ )
1600
+
1601
+ def _make_opt_mtup(self, kind: 'Enum_OptionNumber', option: 'Optional[Data_MTUPOption]' = None, *,
1602
+ mtu: 'int' = 0,
1603
+ **kwargs: 'Any') -> 'Schema_MTUPOption':
1604
+ """Make IPv4 MTU Probe (``MTUP``) option.
1605
+
1606
+ Args:
1607
+ kind: option type code
1608
+ option: option data
1609
+ mtu: MTU value
1610
+ **kwargs: arbitrary keyword arguments
1611
+
1612
+ Returns:
1613
+ Constructured option schema.
1614
+
1615
+ """
1616
+ if option is not None:
1617
+ mtu = option.mtu
1618
+
1619
+ return Schema_MTUPOption(
1620
+ type=kind,
1621
+ length=4,
1622
+ mtu=mtu,
1623
+ )
1624
+
1625
+ def _make_opt_mtur(self, kind: 'Enum_OptionNumber', option: 'Optional[Data_MTUROption]' = None, *,
1626
+ mtu: 'int' = 0,
1627
+ **kwargs: 'Any') -> 'Schema_MTUROption':
1628
+ """Make IPv4 MTU Reply (``MTUR``) option.
1629
+
1630
+ Args:
1631
+ kind: option type code
1632
+ option: option data
1633
+ mtu: MTU value
1634
+ **kwargs: arbitrary keyword arguments
1635
+
1636
+ Returns:
1637
+ Constructured option schema.
1638
+
1639
+ """
1640
+ if option is not None:
1641
+ mtu = option.mtu
1642
+
1643
+ return Schema_MTUROption(
1644
+ type=kind,
1645
+ length=4,
1646
+ mtu=mtu,
1647
+ )
1648
+
1649
+ def _make_opt_tr(self, kind: 'Enum_OptionNumber', option: 'Optional[Data_TROption]' = None, *,
1650
+ id: 'int' = 0,
1651
+ out: 'int' = 0,
1652
+ ret: 'int' = 0,
1653
+ origin: 'IPv4Address | str | bytes | int' = '127.0.0.1',
1654
+ **kwargs: 'Any') -> 'Schema_TROption':
1655
+ """Make IPv4 Traceroute (``TR``) option.
1656
+
1657
+ Args:
1658
+ kind: option type code
1659
+ option: option data
1660
+ id: ID number
1661
+ out: outbound hop count
1662
+ ret: return hop count
1663
+ origin: originator IP address
1664
+ **kwargs: arbitrary keyword arguments
1665
+
1666
+ Returns:
1667
+ Constructured option schema.
1668
+
1669
+ """
1670
+ if option is not None:
1671
+ id = option.id
1672
+ out = option.outbound
1673
+ ret = option['return']
1674
+ origin = option.originator
1675
+
1676
+ return Schema_TROption(
1677
+ type=kind,
1678
+ length=12,
1679
+ id=id,
1680
+ out=out,
1681
+ ret=ret,
1682
+ origin=origin,
1683
+ )
1684
+
1685
+ def _make_opt_rtralt(self, kind: 'Enum_OptionNumber', option: 'Optional[Data_RTRALTOption]' = None, *,
1686
+ alert: 'Enum_RouterAlert | StdlibEnum | AenumEnum | int | str' = Enum_RouterAlert.Aggregated_Reservation_Nesting_Level_0,
1687
+ alert_default: 'Optional[int]' = None,
1688
+ alert_namespace: 'Optional[dict[str, int] | dict[int, str] | Type[StdlibEnum] | Type[AenumEnum]]' = None, # pylint: disable=line-too-long
1689
+ alert_reversed: 'bool' = False,
1690
+ **kwargs: 'Any') -> 'Schema_RTRALTOption':
1691
+ """Make IPv4 Router Alert (``RTRALT``) option.
1692
+
1693
+ Args:
1694
+ kind: option type code
1695
+ option: option data
1696
+ alert: router alert type
1697
+ alert_default: default value for router alert type
1698
+ alert_namespace: namespace for router alert type
1699
+ alert_reversed: whether router alert type is reversed
1700
+ **kwargs: arbitrary keyword arguments
1701
+
1702
+ Returns:
1703
+ Constructured option schema.
1704
+
1705
+ """
1706
+ if option is not None:
1707
+ alert_val = option.alert
1708
+ else:
1709
+ alert_val = self._make_index(alert, alert_default, namespace=alert_namespace, # type: ignore[assignment]
1710
+ reversed=alert_reversed, pack=False)
1711
+
1712
+ return Schema_RTRALTOption(
1713
+ type=kind,
1714
+ length=4,
1715
+ alert=alert_val,
1716
+ )
1717
+
1718
+ def _make_opt_qs(self, kind: 'Enum_OptionNumber', option: 'Optional[Data_QuickStartRequestOption | Data_QuickStartReportOption]' = None, *,
1719
+ func: 'Enum_QSFunction | StdlibEnum | AenumEnum | str | int' = Enum_QSFunction.Quick_Start_Request,
1720
+ func_default: 'Optional[int]' = None,
1721
+ func_namespace: 'Optional[dict[str, int] | dict[int, str] | Type[StdlibEnum] | Type[AenumEnum]]' = None, # pylint: disable=line-too-long
1722
+ func_reversed: 'bool' = False,
1723
+ rate: 'int' = 0,
1724
+ ttl: 'timedelta | int' = 0,
1725
+ nonce: 'int' = 0,
1726
+ **kwargs: 'Any') -> 'Schema_QSOption':
1727
+ """Make IPv4 Quick-Start (``QS``) option.
1728
+
1729
+ Args:
1730
+ code: option type value
1731
+ opt: option data
1732
+ func: QS function type
1733
+ func_default: default value for QS function type
1734
+ func_namespace: namespace for QS function type
1735
+ func_reversed: reversed flag for QS function type
1736
+ rate: rate (in kbps)
1737
+ ttl: time to live (in seconds)
1738
+ nonce: nonce value
1739
+ **kwargs: arbitrary keyword arguments
1740
+
1741
+ Returns:
1742
+ Constructured option schema.
1743
+
1744
+ """
1745
+ if option is not None:
1746
+ func_enum = option.func
1747
+ rate = option.rate
1748
+ ttl = getattr(option, 'ttl', 0)
1749
+ nonce = option.nonce
1750
+ else:
1751
+ func_enum = self._make_index(func, func_default, namespace=func_namespace, # type: ignore[assignment]
1752
+ reversed=func_reversed, pack=False)
1753
+ rate_val = math.floor(math.log2(rate * 1000 / 40000)) if rate > 0 else 0
1754
+
1755
+ if func_enum == Enum_QSFunction.Quick_Start_Request:
1756
+ ttl_value = ttl if isinstance(ttl, int) else math.floor(ttl.total_seconds())
1757
+
1758
+ return Schema_QuickStartRequestOption(
1759
+ type=kind,
1760
+ length=8,
1761
+ flags={
1762
+ 'func': func_enum,
1763
+ 'rate': rate_val,
1764
+ },
1765
+ ttl=ttl_value,
1766
+ nonce={
1767
+ 'nonce': nonce,
1768
+ },
1769
+ )
1770
+ if func_enum == Enum_QSFunction.Report_of_Approved_Rate:
1771
+ return Schema_QuickStartReportOption(
1772
+ type=kind,
1773
+ length=8,
1774
+ flags={
1775
+ 'func': func_enum,
1776
+ 'rate': rate_val,
1777
+ },
1778
+ nonce={
1779
+ 'nonce': nonce,
1780
+ },
1781
+ )
1782
+ raise ProtocolError(f'{self.alias}: [OptNo {kind}] invalid QS function: {func_enum}')