pypcapkit 1.3.3.post1__cp313-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (466) hide show
  1. pcapkit/__init__.py +126 -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 +294 -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 +66 -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 +32702 -0
  120. pcapkit/const/reg/ethertype.py +714 -0
  121. pcapkit/const/reg/linktype.py +902 -0
  122. pcapkit/const/reg/transtype.py +523 -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 +240 -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 +361 -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 +710 -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 +198 -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 +192 -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.3.post1.dist-info/LICENSE +29 -0
  462. pypcapkit-1.3.3.post1.dist-info/METADATA +236 -0
  463. pypcapkit-1.3.3.post1.dist-info/RECORD +466 -0
  464. pypcapkit-1.3.3.post1.dist-info/WHEEL +5 -0
  465. pypcapkit-1.3.3.post1.dist-info/entry_points.txt +3 -0
  466. pypcapkit-1.3.3.post1.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}')