├── .clang-format ├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile ├── Makefile.bpf ├── README.md ├── ape.py ├── common ├── Makefile ├── README.org ├── common.mk ├── common_defines.h ├── common_libbpf.c ├── common_libbpf.h ├── common_params.c ├── common_params.h ├── common_user_bpf_xdp.c ├── common_user_bpf_xdp.h ├── parsing_helpers.h └── rewrite_helpers.h ├── headers ├── bpf_endian.h ├── bpf_helpers.h ├── bpf_util.h ├── jhash.h ├── linux │ ├── bpf.h │ ├── err.h │ ├── if_link.h │ └── if_xdp.h └── perf-sys.h ├── xdp_kern_drop.c ├── xdp_kern_reflect.c ├── xdp_kern_scramble.c ├── xdp_user_drop.c ├── xdp_user_reflect.c └── xdp_user_scramble.c /.clang-format: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # 3 | # clang-format configuration file. Intended for clang-format >= 4. 4 | # 5 | # For more information, see: 6 | # 7 | # Documentation/process/clang-format.rst 8 | # https://clang.llvm.org/docs/ClangFormat.html 9 | # https://clang.llvm.org/docs/ClangFormatStyleOptions.html 10 | # 11 | --- 12 | AccessModifierOffset: -4 13 | AlignAfterOpenBracket: Align 14 | AlignConsecutiveAssignments: false 15 | AlignConsecutiveDeclarations: false 16 | #AlignEscapedNewlines: Left # Unknown to clang-format-4.0 17 | AlignOperands: true 18 | AlignTrailingComments: false 19 | AllowAllParametersOfDeclarationOnNextLine: false 20 | AllowShortBlocksOnASingleLine: false 21 | AllowShortCaseLabelsOnASingleLine: false 22 | AllowShortFunctionsOnASingleLine: None 23 | AllowShortIfStatementsOnASingleLine: false 24 | AllowShortLoopsOnASingleLine: false 25 | AlwaysBreakAfterDefinitionReturnType: None 26 | AlwaysBreakAfterReturnType: None 27 | AlwaysBreakBeforeMultilineStrings: false 28 | AlwaysBreakTemplateDeclarations: false 29 | BinPackArguments: true 30 | BinPackParameters: true 31 | BraceWrapping: 32 | AfterClass: false 33 | AfterControlStatement: false 34 | AfterEnum: false 35 | AfterFunction: true 36 | AfterNamespace: true 37 | AfterObjCDeclaration: false 38 | AfterStruct: false 39 | AfterUnion: false 40 | #AfterExternBlock: false # Unknown to clang-format-5.0 41 | BeforeCatch: false 42 | BeforeElse: false 43 | IndentBraces: false 44 | #SplitEmptyFunction: true # Unknown to clang-format-4.0 45 | #SplitEmptyRecord: true # Unknown to clang-format-4.0 46 | #SplitEmptyNamespace: true # Unknown to clang-format-4.0 47 | BreakBeforeBinaryOperators: None 48 | BreakBeforeBraces: Custom 49 | #BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0 50 | BreakBeforeTernaryOperators: false 51 | BreakConstructorInitializersBeforeComma: false 52 | #BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0 53 | BreakAfterJavaFieldAnnotations: false 54 | BreakStringLiterals: false 55 | ColumnLimit: 80 56 | CommentPragmas: '^ IWYU pragma:' 57 | #CompactNamespaces: false # Unknown to clang-format-4.0 58 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 59 | ConstructorInitializerIndentWidth: 8 60 | ContinuationIndentWidth: 8 61 | Cpp11BracedListStyle: false 62 | DerivePointerAlignment: false 63 | DisableFormat: false 64 | ExperimentalAutoDetectBinPacking: false 65 | #FixNamespaceComments: false # Unknown to clang-format-4.0 66 | 67 | # Taken from: 68 | # git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ \ 69 | # | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \ 70 | # | sort | uniq 71 | ForEachMacros: 72 | - 'apei_estatus_for_each_section' 73 | - 'ata_for_each_dev' 74 | - 'ata_for_each_link' 75 | - '__ata_qc_for_each' 76 | - 'ata_qc_for_each' 77 | - 'ata_qc_for_each_raw' 78 | - 'ata_qc_for_each_with_internal' 79 | - 'ax25_for_each' 80 | - 'ax25_uid_for_each' 81 | - '__bio_for_each_bvec' 82 | - 'bio_for_each_bvec' 83 | - 'bio_for_each_integrity_vec' 84 | - '__bio_for_each_segment' 85 | - 'bio_for_each_segment' 86 | - 'bio_for_each_segment_all' 87 | - 'bio_list_for_each' 88 | - 'bip_for_each_vec' 89 | - 'blkg_for_each_descendant_post' 90 | - 'blkg_for_each_descendant_pre' 91 | - 'blk_queue_for_each_rl' 92 | - 'bond_for_each_slave' 93 | - 'bond_for_each_slave_rcu' 94 | - 'bpf_for_each_spilled_reg' 95 | - 'btree_for_each_safe128' 96 | - 'btree_for_each_safe32' 97 | - 'btree_for_each_safe64' 98 | - 'btree_for_each_safel' 99 | - 'card_for_each_dev' 100 | - 'cgroup_taskset_for_each' 101 | - 'cgroup_taskset_for_each_leader' 102 | - 'cpufreq_for_each_entry' 103 | - 'cpufreq_for_each_entry_idx' 104 | - 'cpufreq_for_each_valid_entry' 105 | - 'cpufreq_for_each_valid_entry_idx' 106 | - 'css_for_each_child' 107 | - 'css_for_each_descendant_post' 108 | - 'css_for_each_descendant_pre' 109 | - 'device_for_each_child_node' 110 | - 'drm_atomic_crtc_for_each_plane' 111 | - 'drm_atomic_crtc_state_for_each_plane' 112 | - 'drm_atomic_crtc_state_for_each_plane_state' 113 | - 'drm_atomic_for_each_plane_damage' 114 | - 'drm_connector_for_each_possible_encoder' 115 | - 'drm_for_each_connector_iter' 116 | - 'drm_for_each_crtc' 117 | - 'drm_for_each_encoder' 118 | - 'drm_for_each_encoder_mask' 119 | - 'drm_for_each_fb' 120 | - 'drm_for_each_legacy_plane' 121 | - 'drm_for_each_plane' 122 | - 'drm_for_each_plane_mask' 123 | - 'drm_for_each_privobj' 124 | - 'drm_mm_for_each_hole' 125 | - 'drm_mm_for_each_node' 126 | - 'drm_mm_for_each_node_in_range' 127 | - 'drm_mm_for_each_node_safe' 128 | - 'flow_action_for_each' 129 | - 'for_each_active_drhd_unit' 130 | - 'for_each_active_iommu' 131 | - 'for_each_available_child_of_node' 132 | - 'for_each_bio' 133 | - 'for_each_board_func_rsrc' 134 | - 'for_each_bvec' 135 | - 'for_each_card_components' 136 | - 'for_each_card_links' 137 | - 'for_each_card_links_safe' 138 | - 'for_each_card_prelinks' 139 | - 'for_each_card_rtds' 140 | - 'for_each_card_rtds_safe' 141 | - 'for_each_cgroup_storage_type' 142 | - 'for_each_child_of_node' 143 | - 'for_each_clear_bit' 144 | - 'for_each_clear_bit_from' 145 | - 'for_each_cmsghdr' 146 | - 'for_each_compatible_node' 147 | - 'for_each_component_dais' 148 | - 'for_each_component_dais_safe' 149 | - 'for_each_comp_order' 150 | - 'for_each_console' 151 | - 'for_each_cpu' 152 | - 'for_each_cpu_and' 153 | - 'for_each_cpu_not' 154 | - 'for_each_cpu_wrap' 155 | - 'for_each_dev_addr' 156 | - 'for_each_dma_cap_mask' 157 | - 'for_each_dpcm_be' 158 | - 'for_each_dpcm_be_rollback' 159 | - 'for_each_dpcm_be_safe' 160 | - 'for_each_dpcm_fe' 161 | - 'for_each_drhd_unit' 162 | - 'for_each_dss_dev' 163 | - 'for_each_efi_memory_desc' 164 | - 'for_each_efi_memory_desc_in_map' 165 | - 'for_each_element' 166 | - 'for_each_element_extid' 167 | - 'for_each_element_id' 168 | - 'for_each_endpoint_of_node' 169 | - 'for_each_evictable_lru' 170 | - 'for_each_fib6_node_rt_rcu' 171 | - 'for_each_fib6_walker_rt' 172 | - 'for_each_free_mem_range' 173 | - 'for_each_free_mem_range_reverse' 174 | - 'for_each_func_rsrc' 175 | - 'for_each_hstate' 176 | - 'for_each_if' 177 | - 'for_each_iommu' 178 | - 'for_each_ip_tunnel_rcu' 179 | - 'for_each_irq_nr' 180 | - 'for_each_link_codecs' 181 | - 'for_each_lru' 182 | - 'for_each_matching_node' 183 | - 'for_each_matching_node_and_match' 184 | - 'for_each_memblock' 185 | - 'for_each_memblock_type' 186 | - 'for_each_memcg_cache_index' 187 | - 'for_each_mem_pfn_range' 188 | - 'for_each_mem_range' 189 | - 'for_each_mem_range_rev' 190 | - 'for_each_migratetype_order' 191 | - 'for_each_msi_entry' 192 | - 'for_each_msi_entry_safe' 193 | - 'for_each_net' 194 | - 'for_each_netdev' 195 | - 'for_each_netdev_continue' 196 | - 'for_each_netdev_continue_rcu' 197 | - 'for_each_netdev_feature' 198 | - 'for_each_netdev_in_bond_rcu' 199 | - 'for_each_netdev_rcu' 200 | - 'for_each_netdev_reverse' 201 | - 'for_each_netdev_safe' 202 | - 'for_each_net_rcu' 203 | - 'for_each_new_connector_in_state' 204 | - 'for_each_new_crtc_in_state' 205 | - 'for_each_new_mst_mgr_in_state' 206 | - 'for_each_new_plane_in_state' 207 | - 'for_each_new_private_obj_in_state' 208 | - 'for_each_node' 209 | - 'for_each_node_by_name' 210 | - 'for_each_node_by_type' 211 | - 'for_each_node_mask' 212 | - 'for_each_node_state' 213 | - 'for_each_node_with_cpus' 214 | - 'for_each_node_with_property' 215 | - 'for_each_of_allnodes' 216 | - 'for_each_of_allnodes_from' 217 | - 'for_each_of_cpu_node' 218 | - 'for_each_of_pci_range' 219 | - 'for_each_old_connector_in_state' 220 | - 'for_each_old_crtc_in_state' 221 | - 'for_each_old_mst_mgr_in_state' 222 | - 'for_each_oldnew_connector_in_state' 223 | - 'for_each_oldnew_crtc_in_state' 224 | - 'for_each_oldnew_mst_mgr_in_state' 225 | - 'for_each_oldnew_plane_in_state' 226 | - 'for_each_oldnew_plane_in_state_reverse' 227 | - 'for_each_oldnew_private_obj_in_state' 228 | - 'for_each_old_plane_in_state' 229 | - 'for_each_old_private_obj_in_state' 230 | - 'for_each_online_cpu' 231 | - 'for_each_online_node' 232 | - 'for_each_online_pgdat' 233 | - 'for_each_pci_bridge' 234 | - 'for_each_pci_dev' 235 | - 'for_each_pci_msi_entry' 236 | - 'for_each_populated_zone' 237 | - 'for_each_possible_cpu' 238 | - 'for_each_present_cpu' 239 | - 'for_each_prime_number' 240 | - 'for_each_prime_number_from' 241 | - 'for_each_process' 242 | - 'for_each_process_thread' 243 | - 'for_each_property_of_node' 244 | - 'for_each_registered_fb' 245 | - 'for_each_reserved_mem_region' 246 | - 'for_each_rtd_codec_dai' 247 | - 'for_each_rtd_codec_dai_rollback' 248 | - 'for_each_rtdcom' 249 | - 'for_each_rtdcom_safe' 250 | - 'for_each_set_bit' 251 | - 'for_each_set_bit_from' 252 | - 'for_each_sg' 253 | - 'for_each_sg_dma_page' 254 | - 'for_each_sg_page' 255 | - 'for_each_sibling_event' 256 | - 'for_each_subelement' 257 | - 'for_each_subelement_extid' 258 | - 'for_each_subelement_id' 259 | - '__for_each_thread' 260 | - 'for_each_thread' 261 | - 'for_each_zone' 262 | - 'for_each_zone_zonelist' 263 | - 'for_each_zone_zonelist_nodemask' 264 | - 'fwnode_for_each_available_child_node' 265 | - 'fwnode_for_each_child_node' 266 | - 'fwnode_graph_for_each_endpoint' 267 | - 'gadget_for_each_ep' 268 | - 'genradix_for_each' 269 | - 'genradix_for_each_from' 270 | - 'hash_for_each' 271 | - 'hash_for_each_possible' 272 | - 'hash_for_each_possible_rcu' 273 | - 'hash_for_each_possible_rcu_notrace' 274 | - 'hash_for_each_possible_safe' 275 | - 'hash_for_each_rcu' 276 | - 'hash_for_each_safe' 277 | - 'hctx_for_each_ctx' 278 | - 'hlist_bl_for_each_entry' 279 | - 'hlist_bl_for_each_entry_rcu' 280 | - 'hlist_bl_for_each_entry_safe' 281 | - 'hlist_for_each' 282 | - 'hlist_for_each_entry' 283 | - 'hlist_for_each_entry_continue' 284 | - 'hlist_for_each_entry_continue_rcu' 285 | - 'hlist_for_each_entry_continue_rcu_bh' 286 | - 'hlist_for_each_entry_from' 287 | - 'hlist_for_each_entry_from_rcu' 288 | - 'hlist_for_each_entry_rcu' 289 | - 'hlist_for_each_entry_rcu_bh' 290 | - 'hlist_for_each_entry_rcu_notrace' 291 | - 'hlist_for_each_entry_safe' 292 | - '__hlist_for_each_rcu' 293 | - 'hlist_for_each_safe' 294 | - 'hlist_nulls_for_each_entry' 295 | - 'hlist_nulls_for_each_entry_from' 296 | - 'hlist_nulls_for_each_entry_rcu' 297 | - 'hlist_nulls_for_each_entry_safe' 298 | - 'i3c_bus_for_each_i2cdev' 299 | - 'i3c_bus_for_each_i3cdev' 300 | - 'ide_host_for_each_port' 301 | - 'ide_port_for_each_dev' 302 | - 'ide_port_for_each_present_dev' 303 | - 'idr_for_each_entry' 304 | - 'idr_for_each_entry_continue' 305 | - 'idr_for_each_entry_ul' 306 | - 'inet_bind_bucket_for_each' 307 | - 'inet_lhash2_for_each_icsk_rcu' 308 | - 'key_for_each' 309 | - 'key_for_each_safe' 310 | - 'klp_for_each_func' 311 | - 'klp_for_each_func_safe' 312 | - 'klp_for_each_func_static' 313 | - 'klp_for_each_object' 314 | - 'klp_for_each_object_safe' 315 | - 'klp_for_each_object_static' 316 | - 'kvm_for_each_memslot' 317 | - 'kvm_for_each_vcpu' 318 | - 'list_for_each' 319 | - 'list_for_each_codec' 320 | - 'list_for_each_codec_safe' 321 | - 'list_for_each_entry' 322 | - 'list_for_each_entry_continue' 323 | - 'list_for_each_entry_continue_rcu' 324 | - 'list_for_each_entry_continue_reverse' 325 | - 'list_for_each_entry_from' 326 | - 'list_for_each_entry_from_rcu' 327 | - 'list_for_each_entry_from_reverse' 328 | - 'list_for_each_entry_lockless' 329 | - 'list_for_each_entry_rcu' 330 | - 'list_for_each_entry_reverse' 331 | - 'list_for_each_entry_safe' 332 | - 'list_for_each_entry_safe_continue' 333 | - 'list_for_each_entry_safe_from' 334 | - 'list_for_each_entry_safe_reverse' 335 | - 'list_for_each_prev' 336 | - 'list_for_each_prev_safe' 337 | - 'list_for_each_safe' 338 | - 'llist_for_each' 339 | - 'llist_for_each_entry' 340 | - 'llist_for_each_entry_safe' 341 | - 'llist_for_each_safe' 342 | - 'media_device_for_each_entity' 343 | - 'media_device_for_each_intf' 344 | - 'media_device_for_each_link' 345 | - 'media_device_for_each_pad' 346 | - 'mp_bvec_for_each_page' 347 | - 'mp_bvec_for_each_segment' 348 | - 'nanddev_io_for_each_page' 349 | - 'netdev_for_each_lower_dev' 350 | - 'netdev_for_each_lower_private' 351 | - 'netdev_for_each_lower_private_rcu' 352 | - 'netdev_for_each_mc_addr' 353 | - 'netdev_for_each_uc_addr' 354 | - 'netdev_for_each_upper_dev_rcu' 355 | - 'netdev_hw_addr_list_for_each' 356 | - 'nft_rule_for_each_expr' 357 | - 'nla_for_each_attr' 358 | - 'nla_for_each_nested' 359 | - 'nlmsg_for_each_attr' 360 | - 'nlmsg_for_each_msg' 361 | - 'nr_neigh_for_each' 362 | - 'nr_neigh_for_each_safe' 363 | - 'nr_node_for_each' 364 | - 'nr_node_for_each_safe' 365 | - 'of_for_each_phandle' 366 | - 'of_property_for_each_string' 367 | - 'of_property_for_each_u32' 368 | - 'pci_bus_for_each_resource' 369 | - 'ping_portaddr_for_each_entry' 370 | - 'plist_for_each' 371 | - 'plist_for_each_continue' 372 | - 'plist_for_each_entry' 373 | - 'plist_for_each_entry_continue' 374 | - 'plist_for_each_entry_safe' 375 | - 'plist_for_each_safe' 376 | - 'pnp_for_each_card' 377 | - 'pnp_for_each_dev' 378 | - 'protocol_for_each_card' 379 | - 'protocol_for_each_dev' 380 | - 'queue_for_each_hw_ctx' 381 | - 'radix_tree_for_each_slot' 382 | - 'radix_tree_for_each_tagged' 383 | - 'rbtree_postorder_for_each_entry_safe' 384 | - 'rdma_for_each_port' 385 | - 'resource_list_for_each_entry' 386 | - 'resource_list_for_each_entry_safe' 387 | - 'rhl_for_each_entry_rcu' 388 | - 'rhl_for_each_rcu' 389 | - 'rht_for_each' 390 | - 'rht_for_each_from' 391 | - 'rht_for_each_entry' 392 | - 'rht_for_each_entry_from' 393 | - 'rht_for_each_entry_rcu' 394 | - 'rht_for_each_entry_rcu_from' 395 | - 'rht_for_each_entry_safe' 396 | - 'rht_for_each_rcu' 397 | - 'rht_for_each_rcu_from' 398 | - '__rq_for_each_bio' 399 | - 'rq_for_each_bvec' 400 | - 'rq_for_each_segment' 401 | - 'scsi_for_each_prot_sg' 402 | - 'scsi_for_each_sg' 403 | - 'sctp_for_each_hentry' 404 | - 'sctp_skb_for_each' 405 | - 'shdma_for_each_chan' 406 | - '__shost_for_each_device' 407 | - 'shost_for_each_device' 408 | - 'sk_for_each' 409 | - 'sk_for_each_bound' 410 | - 'sk_for_each_entry_offset_rcu' 411 | - 'sk_for_each_from' 412 | - 'sk_for_each_rcu' 413 | - 'sk_for_each_safe' 414 | - 'sk_nulls_for_each' 415 | - 'sk_nulls_for_each_from' 416 | - 'sk_nulls_for_each_rcu' 417 | - 'snd_array_for_each' 418 | - 'snd_pcm_group_for_each_entry' 419 | - 'snd_soc_dapm_widget_for_each_path' 420 | - 'snd_soc_dapm_widget_for_each_path_safe' 421 | - 'snd_soc_dapm_widget_for_each_sink_path' 422 | - 'snd_soc_dapm_widget_for_each_source_path' 423 | - 'tb_property_for_each' 424 | - 'tcf_exts_for_each_action' 425 | - 'udp_portaddr_for_each_entry' 426 | - 'udp_portaddr_for_each_entry_rcu' 427 | - 'usb_hub_for_each_child' 428 | - 'v4l2_device_for_each_subdev' 429 | - 'v4l2_m2m_for_each_dst_buf' 430 | - 'v4l2_m2m_for_each_dst_buf_safe' 431 | - 'v4l2_m2m_for_each_src_buf' 432 | - 'v4l2_m2m_for_each_src_buf_safe' 433 | - 'virtio_device_for_each_vq' 434 | - 'xa_for_each' 435 | - 'xa_for_each_marked' 436 | - 'xa_for_each_start' 437 | - 'xas_for_each' 438 | - 'xas_for_each_conflict' 439 | - 'xas_for_each_marked' 440 | - 'zorro_for_each_dev' 441 | 442 | #IncludeBlocks: Preserve # Unknown to clang-format-5.0 443 | IncludeCategories: 444 | - Regex: '.*' 445 | Priority: 1 446 | IncludeIsMainRegex: '(Test)?$' 447 | IndentCaseLabels: false 448 | #IndentPPDirectives: None # Unknown to clang-format-5.0 449 | IndentWidth: 8 450 | IndentWrappedFunctionNames: false 451 | JavaScriptQuotes: Leave 452 | JavaScriptWrapImports: true 453 | KeepEmptyLinesAtTheStartOfBlocks: false 454 | MacroBlockBegin: '' 455 | MacroBlockEnd: '' 456 | MaxEmptyLinesToKeep: 1 457 | NamespaceIndentation: Inner 458 | #ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0 459 | ObjCBlockIndentWidth: 8 460 | ObjCSpaceAfterProperty: true 461 | ObjCSpaceBeforeProtocolList: true 462 | 463 | # Taken from git's rules 464 | #PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0 465 | PenaltyBreakBeforeFirstCallParameter: 30 466 | PenaltyBreakComment: 10 467 | PenaltyBreakFirstLessLess: 0 468 | PenaltyBreakString: 10 469 | PenaltyExcessCharacter: 100 470 | PenaltyReturnTypeOnItsOwnLine: 60 471 | 472 | PointerAlignment: Right 473 | ReflowComments: false 474 | SortIncludes: false 475 | #SortUsingDeclarations: false # Unknown to clang-format-4.0 476 | SpaceAfterCStyleCast: false 477 | SpaceAfterTemplateKeyword: true 478 | SpaceBeforeAssignmentOperators: true 479 | #SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0 480 | #SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0 481 | SpaceBeforeParens: ControlStatements 482 | #SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0 483 | SpaceInEmptyParentheses: false 484 | SpacesBeforeTrailingComments: 1 485 | SpacesInAngles: false 486 | SpacesInContainerLiterals: false 487 | SpacesInCStyleCastParentheses: false 488 | SpacesInParentheses: false 489 | SpacesInSquareBrackets: false 490 | Standard: Cpp03 491 | TabWidth: 8 492 | UseTab: Always 493 | ... 494 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.ll 3 | xdp_user_drop 4 | xdp_user_scramble 5 | xdp_user_reflect 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libbpf"] 2 | path = libbpf 3 | url = https://github.com/libbpf/libbpf/ 4 | ignore = untracked 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BPF_MAKEFILE:="Makefile.bpf" 2 | 3 | all: 4 | $(MAKE) user_drop 5 | $(MAKE) user_scramble 6 | $(MAKE) user_reflect 7 | 8 | kern_drop_clean: 9 | -rm xdp_kern_drop.ll xdp_kern_drop.o 10 | 11 | kern_drop: 12 | $(MAKE) -f $(BPF_MAKEFILE) xdp_kern_drop.o 13 | 14 | kern_reflect_clean: 15 | -rm xdp_kern_reflect.ll xdp_kern_reflect.o 16 | 17 | kern_reflect: 18 | $(MAKE) -f $(BPF_MAKEFILE) xdp_kern_reflect.o 19 | 20 | kern_scramble_clean: 21 | -rm xdp_kern_scramble.ll xdp_kern_scramble.o 22 | 23 | kern_scramble: 24 | $(MAKE) -f $(BPF_MAKEFILE) xdp_kern_scramble.o 25 | 26 | user_drop_clean: 27 | -rm xdp_user_drop 28 | 29 | user_drop: 30 | $(MAKE) -f $(BPF_MAKEFILE) xdp_user_drop 31 | 32 | user_scramble_clean: 33 | -rm xdp_user_scramble 34 | 35 | user_scramble: 36 | $(MAKE) -f $(BPF_MAKEFILE) xdp_user_scramble 37 | 38 | user_reflect_clean: 39 | -rm xdp_user_reflect 40 | 41 | user_reflect: 42 | $(MAKE) -f $(BPF_MAKEFILE) xdp_user_reflect 43 | 44 | clean: 45 | $(MAKE) user_drop_clean 46 | $(MAKE) user_scramble_clean 47 | $(MAKE) user_reflect_clean 48 | 49 | fmt: 50 | -clang-format -i *.h 51 | -clang-format -i *.c 52 | black ape.py 53 | 54 | .PHONY: user_drop_clean kern_drop_clean user_drop kern_drop kern_scramble_clean kern_scramble kern_reflect_clean kern_reflect user_reflect_clean user_reflect 55 | -------------------------------------------------------------------------------- /Makefile.bpf: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) 2 | 3 | # Departing from the implicit _user.c scheme 4 | XDP_TARGETS := xdp_kern_drop xdp_kern_scramble xdp_kern_reflect 5 | USER_TARGETS := xdp_user_drop xdp_user_scramble xdp_user_reflect 6 | 7 | LIBBPF_DIR = libbpf/src/ 8 | COMMON_DIR = common/ 9 | 10 | include $(COMMON_DIR)/common.mk 11 | LIBS += -lpthread -lrt 12 | 13 | CFLAGS += -Wall -Wextra -Wshadow 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ape - an XDP packet manipulation tool 2 | 3 | Ape is a tool to manipulate UDP (both ipv4 and ipv6) packets. 4 | 5 | It can: 6 | 7 | * **drop** a % of UDP packets (pseudorandomly) on an interface, by port or on all ports 8 | * **scramble** a % of UDP packets on an interface, by redirecting them to userspace with AF_XDP and randomly sleeping before forwarding it to the original destination 9 | * **reflect** all the packets from one UDP port to another 10 | 11 | Desired future functionality: 12 | 13 | * **mirror** packets from one port to another. But how? It triggers an XDP feedback loop. XDP redirects port XXX packets to userspace, userspace sends port XXX packets to XXX and YYY, XDP redirects port XXX packets, etc. XDP metadata solution? 14 | 15 | The learning resource I used to create this project is https://github.com/xdp-project/xdp-tutorial - I highly recommended it if you want to get started with XDP. `headers/` and `common/` are copied from it. You most likely need the [bpf-next](https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git) kernel to use AF_XDP. I was able to follow the [Fedora instructions](https://fedoraproject.org/wiki/Building_a_custom_kernel#Building_Vanilla_upstream_kernel) for installing a custom kernel with no problems. 16 | 17 | ### architecture 18 | 19 | Ape looks stitched together, because it is. It consists of the following pieces: 20 | 21 | 1. Userspace XDP loaders, named `xdp_user_*.c` - these must be built **before** using ape, with `make clean all` 22 | 2. Kernel XDP programs, named `xdp_kern_*.c` - these are compiled at runtime by `ape.py` based on command-line arguments which are converted to `-D` compiler switches 23 | 3. `ape.py`, the main Python script and entrypoint of this project, which parses command-line arguments, builds and attaches the XDP kernel objects, and exposes XDP metrics to Prometheus using `bpftool` 24 | 25 | `ape.py` must be run with sudo to execute privileged actions e.g. attaching XDP programs to NICs, but it drops privileges to `SUDO_UID` when compiling the kernel modules to not create root-owned files in the repository. 26 | 27 | The maps intended for packet counts and stats are pinned using `bpffs`, and read using `bpftool`. 28 | 29 | ### drop 30 | 31 | Drop UDP4/6 packets from an interface, with an optional port. 32 | 33 | ape, dropping 50% of UDP packets on port 1337 on lo: 34 | ``` 35 | sevagh:ape $ sudo ./ape.py --udp-drop 50 --udp-port 1337 lo 36 | b'Success: Loaded BPF-object(xdp_kern_drop.o) and used section(xdp_ape_drop)\n - XDP prog attached on device:lo(ifindex:1)\n - Unpinning (remove) prev maps in /sys/fs/bpf/lo/\n - Pinning maps in /sys/fs/bpf/lo/\n' 37 | Starting bpftool listener for map /sys/fs/bpf/lo/drop_count in thread 38 | Started prometheus metrics server at http://localhost:8000 39 | ``` 40 | 41 | socat sender: 42 | ``` 43 | sevagh:~ $ for x in 1 2 3 4 5 6 7 8 9 10; do echo "hello world ${x}" | socat - UDP6-SENDTO:[::1]:1337; done 44 | ``` 45 | 46 | socat receiver: 47 | ``` 48 | sevagh:~ $ socat - UDP6-LISTEN:1337,bind=[::1],fork 49 | hello world 1 50 | hello world 2 51 | hello world 4 52 | hello world 5 53 | hello world 7 54 | hello world 9 55 | hello world 10 56 | ``` 57 | 58 | We can see packets `3, 6, 8` missing. The Prometheus metrics show it, at `http://127.0.0.1:8000`: 59 | 60 | ``` 61 | ape_total_udp_packets{action="drop",device="lo",port="1337"} 10.0 62 | # HELP ape_manipulated_udp_packets UDP packets manipulated by ape 63 | # TYPE ape_manipulated_udp_packets gauge 64 | ape_manipulated_udp_packets{action="drop",device="lo",port="1337"} 3.0 65 | ``` 66 | 67 | ### scramble 68 | 69 | Scramble UDP4/6 packets from an interface, with an optional port. 70 | 71 | The implementation of scramble is much more complex than drop. There's no way to copy a packet, or "sleep", in XDP. 72 | 73 | I need to use [`AF_XDP`](https://www.kernel.org/doc/html/latest/networking/af_xdp.html), which is a way to redirect packets from the XDP kernel program to userspace. Inside `xdp_user_scramble.c`, I set up UDP4 and UDP6 sender sockets. When the XDP kernel scramble program redirects a packet to the XSK map and I receive it in the user scramble program, I sleep randomly between 0-MAX_SLEEP_MS ms before re-sending it on the appropriate UDP4 or UDP6 socket. This results in packets seemingly being received out of order. 74 | 75 | ape, scrambling 50% of UDP packets on port 1337 on lo: 76 | ``` 77 | sevagh:ape $ sudo ./ape.py --udp-scramble 50 --udp-port 1337 lo 78 | sleeping 2s to let module load 79 | Starting bpftool listener for map /sys/fs/bpf/lo/scramble_count in thread 80 | Started prometheus metrics server at http://localhost:8000 81 | ``` 82 | 83 | socat sender: 84 | ``` 85 | sevagh:~ $ for x in 1 2 3 4 5 6 7 8 9 10; do echo "hello world ${x}" | socat - UDP6-SENDTO:[::1]:1337; done 86 | ``` 87 | 88 | socat receiver: 89 | ``` 90 | sevagh:~ $ socat - UDP6-LISTEN:1337,bind=[::1],fork 91 | hello world 2 92 | hello world 5 93 | hello world 3 94 | hello world 6 95 | hello world 1 96 | hello world 8 97 | hello world 9 98 | hello world 10 99 | hello world 7 100 | hello world 4 101 | ``` 102 | 103 | We can see the packets arrive out of order. The Prometheus metrics show it, at `http://127.0.0.1:8000`: 104 | 105 | ``` 106 | ape_total_udp_packets{action="scramble",device="lo",port="1337"} 18.0 107 | # HELP ape_manipulated_udp_packets UDP packets manipulated by ape 108 | # TYPE ape_manipulated_udp_packets gauge 109 | ape_manipulated_udp_packets{action="scramble",device="lo",port="1337"} 8.0 110 | ``` 111 | 112 | There are more than 10 total packets, because `scramble` will feed back to itself (the XDP kernel program will probably re-scramble packets sent by the user scramble program). 113 | 114 | ### reflect 115 | 116 | Reflect, similar to scramble, relies on AF_XDP. 117 | 118 | ape, reflecting UDP packets from port 1337 to 1234 on lo: 119 | ``` 120 | sevagh:ape $ sudo ./ape.py --udp-reflect 1234 --udp-port 1337 lo 121 | sleeping 2s to let module load 122 | Starting bpftool listener for map /sys/fs/bpf/lo/reflect_count in thread 123 | Started prometheus metrics server at http://localhost:8000 124 | ``` 125 | 126 | socat sender: 127 | ``` 128 | sevagh:~ $ for x in 1 2 3 4 5 6 7 8 9 10; do echo "hello world ${x}" | socat - UDP6-SENDTO:[::1]:1337; done 129 | ``` 130 | 131 | socat receiver, port 1234: 132 | ``` 133 | sevagh:~ $ socat - UDP6-LISTEN:1234,bind=[::1],fork 134 | hello world 1 135 | hello world 2 136 | hello world 3 137 | hello world 4 138 | hello world 5 139 | hello world 6 140 | hello world 7 141 | hello world 8 142 | hello world 9 143 | hello world 10 144 | ``` 145 | 146 | socat receiver, port 1337: 147 | ``` 148 | sevagh:~ $ socat - UDP6-LISTEN:1337,bind=[::1],fork 149 | ``` 150 | 151 | Ape metrics: 152 | 153 | ``` 154 | # HELP ape_loaded_xdp_progs XDP kernel programs loaded and attached by ape 155 | # TYPE ape_loaded_xdp_progs gauge 156 | ape_loaded_xdp_progs{interface="lo",name="xdp_ape_reflect"} 1.0 157 | # HELP ape_total_udp_packets UDP packets intercepted by ape 158 | # TYPE ape_total_udp_packets gauge 159 | ape_total_udp_packets{action="reflect",device="lo",port="1234"} 10.0 160 | # HELP ape_manipulated_udp_packets UDP packets manipulated by ape 161 | # TYPE ape_manipulated_udp_packets gauge 162 | ape_manipulated_udp_packets{action="reflect",device="lo",port="1234"} 10.0 163 | ``` 164 | -------------------------------------------------------------------------------- /ape.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3.7 2 | 3 | import signal 4 | import sys 5 | import time 6 | import threading 7 | import subprocess 8 | import argparse 9 | import os 10 | import sys 11 | import logging 12 | from struct import unpack 13 | from binascii import unhexlify 14 | from prometheus_client import start_http_server, Counter, Gauge 15 | import datetime, threading 16 | 17 | 18 | STOP_STATS = False 19 | 20 | 21 | def output_reader(proc): 22 | for line in iter(proc.stdout.readline, b""): 23 | logging.info(line.decode("utf-8")) 24 | 25 | 26 | def stats_thread( 27 | device, 28 | action, 29 | port, 30 | pinned_map_name, 31 | stats_interval_s, 32 | total_metric, 33 | manipulated_metric, 34 | ): 35 | total_pkts = subprocess.check_output( 36 | [ 37 | "bpftool", 38 | "--bpffs", 39 | "map", 40 | "lookup", 41 | "pinned", 42 | pinned_map_name, 43 | "key", 44 | "0", 45 | "0", 46 | "0", 47 | "0", 48 | ] 49 | ) 50 | manip_pkts = subprocess.check_output( 51 | [ 52 | "bpftool", 53 | "--bpffs", 54 | "map", 55 | "lookup", 56 | "pinned", 57 | pinned_map_name, 58 | "key", 59 | "1", 60 | "0", 61 | "0", 62 | "0", 63 | ] 64 | ) 65 | total_packets = unpack( 66 | "<2i", 67 | unhexlify( 68 | total_pkts.split(b"value:")[-1][1:-1] 69 | .decode() 70 | .replace(" ", "") 71 | .encode("utf-8") 72 | ), 73 | )[0] 74 | total_metric.labels(device, action, port).set(total_packets) 75 | manip_packets = unpack( 76 | "<2i", 77 | unhexlify( 78 | manip_pkts.split(b"value:")[-1][1:-1] 79 | .decode() 80 | .replace(" ", "") 81 | .encode("utf-8") 82 | ), 83 | )[0] 84 | manipulated_metric.labels(device, action, port).set(manip_packets) 85 | global STOP_STATS 86 | if not STOP_STATS: 87 | threading.Timer( 88 | stats_interval_s, 89 | stats_thread, 90 | [ 91 | device, 92 | action, 93 | port, 94 | pinned_map_name, 95 | stats_interval_s, 96 | total_metric, 97 | manipulated_metric, 98 | ], 99 | ).start() 100 | else: 101 | print("Stopping stats thread for {0} {1} {2}".format(device, action, port)) 102 | 103 | 104 | def compile_xdp_kern_prog(makerule, cflags): 105 | env = None 106 | if cflags: 107 | env = os.environ.copy() 108 | env["CFLAGS"] = "{0}".format(" ".join(cflags)) 109 | 110 | logging.info( 111 | subprocess.check_output("make {0}".format(makerule), shell=True, env=env) 112 | ) 113 | 114 | 115 | def main(): 116 | if os.geteuid() != 0: 117 | print( 118 | "Start {0} as root, it will drop privileges when compiling .o files".format( 119 | sys.argv[0] 120 | ), 121 | file=sys.stderr, 122 | ) 123 | sys.exit(1) 124 | 125 | sudo_uid = int(os.getenv("SUDO_GID")) 126 | sudo_gid = int(os.getenv("SUDO_UID")) 127 | 128 | parser = argparse.ArgumentParser(description="ape - network chaos tool") 129 | 130 | parser.add_argument("device", type=str, help="iface to attach XDP to") 131 | parser.add_argument( 132 | "--udp-drop", dest="udp_drop", type=int, help="%% of UDP packets to drop" 133 | ) 134 | parser.add_argument( 135 | "--udp-scramble", 136 | dest="udp_scramble", 137 | type=int, 138 | help="%% of UDP packets to scramble", 139 | ) 140 | parser.add_argument( 141 | "--max-sleep-ms", 142 | dest="max_sleep_ms", 143 | type=int, 144 | help="Max sleep (ms) to delay packets in scramble mode", 145 | default=1000, 146 | ) 147 | parser.add_argument( 148 | "--udp-reflect", 149 | dest="udp_reflect", 150 | type=int, 151 | help="Dest port to reflect UDP packets to", 152 | ) 153 | parser.add_argument( 154 | "--udp-port", dest="udp_port", type=int, help="UDP port to drop packets on" 155 | ) 156 | parser.add_argument( 157 | "--listen-port", 158 | dest="prom_port", 159 | type=int, 160 | help="HTTP port for Prometheus metrics endpoint", 161 | default=8000, 162 | ) 163 | parser.add_argument( 164 | "--stats-interval-s", 165 | dest="stats_interval_s", 166 | type=int, 167 | help="Time in s to poll XDP maps and generate Prometheus metrics", 168 | default=1, 169 | ) 170 | parser.add_argument( 171 | "-d", 172 | "--debug", 173 | help="debug out", 174 | action="store_const", 175 | dest="loglevel", 176 | const=logging.DEBUG, 177 | default=logging.WARNING, 178 | ) 179 | parser.add_argument( 180 | "-v", 181 | "--verbose", 182 | help="verbose out", 183 | action="store_const", 184 | dest="loglevel", 185 | const=logging.INFO, 186 | ) 187 | 188 | ape_modules_metric = Gauge( 189 | "ape_loaded_xdp_progs", 190 | "XDP kernel programs loaded and attached by ape", 191 | ["interface", "name"], 192 | ) 193 | ape_total_metric = Gauge( 194 | "ape_total_udp_packets", 195 | "UDP packets intercepted by ape", 196 | ["device", "action", "port"], 197 | ) 198 | ape_manipulated_metric = Gauge( 199 | "ape_manipulated_udp_packets", 200 | "UDP packets manipulated by ape", 201 | ["device", "action", "port"], 202 | ) 203 | 204 | args = parser.parse_args() 205 | logging.basicConfig(level=args.loglevel) 206 | 207 | run_drop = False 208 | run_scramble = False 209 | run_reflect = False 210 | unload_cmds = [] 211 | procs = [] 212 | threads = [] 213 | remove_maps = [] 214 | 215 | if args.udp_drop: 216 | cflags = ["-DUDP_DROP_PROB={0}".format(args.udp_drop)] 217 | run_drop = True 218 | if args.udp_port: 219 | cflags.append("-DUDP_PORT={0}".format(args.udp_port)) 220 | else: 221 | args.udp_port = -1 222 | 223 | # drop privileges 224 | os.setresgid(sudo_gid, sudo_gid, -1) 225 | os.setresuid(sudo_uid, sudo_uid, -1) 226 | 227 | compile_xdp_kern_prog("kern_drop_clean kern_drop", cflags) 228 | 229 | # get them back 230 | os.setresgid(0, 0, -1) 231 | os.setresuid(0, 0, -1) 232 | 233 | if args.udp_scramble: 234 | cflags = ["-DUDP_SCRAMBLE_PROB={0}".format(args.udp_scramble)] 235 | run_scramble = True 236 | if args.udp_port: 237 | cflags.append("-DUDP_PORT={0}".format(args.udp_port)) 238 | else: 239 | args.udp_port = -1 240 | 241 | # drop privileges 242 | os.setresgid(sudo_gid, sudo_gid, -1) 243 | os.setresuid(sudo_uid, sudo_uid, -1) 244 | 245 | compile_xdp_kern_prog("kern_scramble_clean kern_scramble", cflags) 246 | 247 | # get them back 248 | os.setresgid(0, 0, -1) 249 | os.setresuid(0, 0, -1) 250 | 251 | if args.udp_reflect: 252 | if not args.udp_port: 253 | print("--udp-reflect must be used with --udp-port", file=sys.stderr) 254 | sys.exit(1) 255 | run_reflect = True 256 | cflags = ["-DUDP_PORT={0}".format(args.udp_port)] 257 | 258 | # drop privileges 259 | os.setresgid(sudo_gid, sudo_gid, -1) 260 | os.setresuid(sudo_uid, sudo_uid, -1) 261 | 262 | compile_xdp_kern_prog("kern_reflect_clean kern_reflect", cflags) 263 | 264 | # get them back 265 | os.setresgid(0, 0, -1) 266 | os.setresuid(0, 0, -1) 267 | 268 | if run_drop: 269 | print( 270 | subprocess.check_output( 271 | [ 272 | "./xdp_user_drop", 273 | "--auto-mode", 274 | "--dev", 275 | args.device, 276 | "--progsec", 277 | "xdp_ape_drop", 278 | "--filename", 279 | "xdp_kern_drop.o", 280 | ] 281 | ) 282 | ) 283 | ape_modules_metric.labels(args.device, "xdp_ape_drop").set(1.0) 284 | unload_cmds.append( 285 | ["./xdp_user_drop", "--auto-mode", "--unload", "--dev", args.device] 286 | ) 287 | 288 | map_name = "/sys/fs/bpf/{0}/drop_count".format(args.device) 289 | print("Starting bpftool listener for map {0} in thread".format(map_name)) 290 | stats_thread( 291 | args.device, 292 | "drop", 293 | args.udp_port, 294 | map_name, 295 | args.stats_interval_s, 296 | ape_total_metric, 297 | ape_manipulated_metric, 298 | ) 299 | 300 | if run_scramble: 301 | proc = subprocess.Popen( 302 | [ 303 | "./xdp_user_scramble", 304 | "--max-sleep-ms", 305 | "{0}".format(args.max_sleep_ms), 306 | "--auto-mode", 307 | "--poll-mode", 308 | "--dev", 309 | args.device, 310 | "--progsec", 311 | "xdp_ape_scramble", 312 | "--filename", 313 | "xdp_kern_scramble.o", 314 | ], 315 | stdout=subprocess.PIPE, 316 | stderr=subprocess.STDOUT, 317 | ) 318 | print("sleeping 2s to let module load") 319 | time.sleep(2) 320 | scramble_thread = threading.Thread(target=output_reader, args=(proc,)) 321 | scramble_thread.start() 322 | 323 | threads.append(scramble_thread) 324 | procs.append(proc) 325 | 326 | ape_modules_metric.labels(args.device, "xdp_ape_scramble").set(1.0) 327 | unload_cmds.append( 328 | ["./xdp_user_scramble", "--auto-mode", "--unload", "--dev", args.device] 329 | ) 330 | 331 | map_name = "/sys/fs/bpf/{0}/scramble_count".format(args.device) 332 | print("Starting bpftool listener for map {0} in thread".format(map_name)) 333 | stats_thread( 334 | args.device, 335 | "scramble", 336 | args.udp_port, 337 | map_name, 338 | args.stats_interval_s, 339 | ape_total_metric, 340 | ape_manipulated_metric, 341 | ) 342 | xsks_map_name = "/sys/fs/bpf/{0}/xsks_map".format(args.device) 343 | remove_maps.append(map_name) 344 | remove_maps.append(xsks_map_name) 345 | 346 | if run_reflect: 347 | proc = subprocess.Popen( 348 | [ 349 | "./xdp_user_reflect", 350 | "--reflect-port", 351 | "{0}".format(args.udp_reflect), 352 | "--auto-mode", 353 | "--poll-mode", 354 | "--dev", 355 | args.device, 356 | "--progsec", 357 | "xdp_ape_reflect", 358 | "--filename", 359 | "xdp_kern_reflect.o", 360 | ], 361 | stdout=subprocess.PIPE, 362 | stderr=subprocess.STDOUT, 363 | ) 364 | print("sleeping 2s to let module load") 365 | time.sleep(2) 366 | reflect_thread = threading.Thread(target=output_reader, args=(proc,)) 367 | reflect_thread.start() 368 | 369 | threads.append(reflect_thread) 370 | procs.append(proc) 371 | 372 | ape_modules_metric.labels(args.device, "xdp_ape_reflect").set(1.0) 373 | unload_cmds.append( 374 | ["./xdp_user_reflect", "--auto-mode", "--unload", "--dev", args.device] 375 | ) 376 | 377 | map_name = "/sys/fs/bpf/{0}/reflect_count".format(args.device) 378 | print("Starting bpftool listener for map {0} in thread".format(map_name)) 379 | stats_thread( 380 | args.device, 381 | "reflect", 382 | args.udp_port, 383 | map_name, 384 | args.stats_interval_s, 385 | ape_total_metric, 386 | ape_manipulated_metric, 387 | ) 388 | xsks_map_name = "/sys/fs/bpf/{0}/xsks_map".format(args.device) 389 | remove_maps.append(map_name) 390 | remove_maps.append(xsks_map_name) 391 | 392 | def signal_handler(signal, frame): 393 | global STOP_STATS 394 | STOP_STATS = True 395 | for u in unload_cmds: 396 | print(subprocess.check_output(u)) 397 | for p in procs: 398 | p.terminate() 399 | try: 400 | print("waiting 5s for process to terminate...") 401 | p.wait(timeout=5) 402 | except subprocess.TimeoutExpired: 403 | print("subprocess did not terminate in time") 404 | for t in threads: 405 | t.join() 406 | 407 | print("sleeping 2*{0} for stats thread to die".format(args.stats_interval_s)) 408 | time.sleep(2 * args.stats_interval_s) 409 | for m in remove_maps: 410 | print("Removing pinned map '{0}'".format(m)) 411 | os.remove(m) 412 | sys.exit(0) 413 | 414 | signal.signal(signal.SIGINT, signal_handler) 415 | 416 | print("Started prometheus metrics server at http://localhost:8000") 417 | start_http_server(args.prom_port) 418 | 419 | event = threading.Event() 420 | event.wait() 421 | 422 | return 0 423 | 424 | 425 | if __name__ == "__main__": 426 | sys.exit(main()) 427 | -------------------------------------------------------------------------------- /common/Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: (GPL-2.0) 2 | CC := gcc 3 | 4 | all: common_params.o common_user_bpf_xdp.o common_libbpf.o 5 | 6 | CFLAGS := -g -Wall 7 | 8 | LIBBPF_DIR ?= ../libbpf/src/ 9 | CFLAGS += -I$(LIBBPF_DIR)/build/usr/include/ -I../headers 10 | # TODO: Do we need to make libbpf from this make file too? 11 | 12 | common_params.o: common_params.c common_params.h 13 | $(CC) $(CFLAGS) -c -o $@ $< 14 | 15 | common_user_bpf_xdp.o: common_user_bpf_xdp.c common_user_bpf_xdp.h 16 | $(CC) $(CFLAGS) -c -o $@ $< 17 | 18 | common_libbpf.o: common_libbpf.c common_libbpf.h 19 | $(CC) $(CFLAGS) -c -o $@ $< 20 | 21 | .PHONY: clean 22 | 23 | clean: 24 | rm -f *.o 25 | -------------------------------------------------------------------------------- /common/README.org: -------------------------------------------------------------------------------- 1 | # -*- fill-column: 76; -*- 2 | #+TITLE: Common files 3 | #+OPTIONS: ^:nil 4 | 5 | This directory contains code that is common between the different 6 | assignments. This reduce code duplication in each tutorial assignment, and 7 | allow us to hideaway code that is irrelevant or have been seen/introduced in 8 | earlier assignments. 9 | -------------------------------------------------------------------------------- /common/common.mk: -------------------------------------------------------------------------------- 1 | # Common Makefile parts for BPF-building with libbpf 2 | # -------------------------------------------------- 3 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) 4 | # 5 | # This file should be included from your Makefile like: 6 | # COMMON_DIR = ../common/ 7 | # include $(COMMON_DIR)/common.mk 8 | # 9 | # It is expected that you define the variables: 10 | # XDP_TARGETS and USER_TARGETS 11 | # as a space-separated list 12 | # 13 | LLC ?= llc 14 | CLANG ?= clang 15 | CC ?= gcc 16 | 17 | XDP_C = ${XDP_TARGETS:=.c} 18 | XDP_OBJ = ${XDP_C:.c=.o} 19 | USER_C := ${USER_TARGETS:=.c} 20 | USER_OBJ := ${USER_C:.c=.o} 21 | 22 | # Expect this is defined by including Makefile, but define if not 23 | COMMON_DIR ?= common/ 24 | LIBBPF_DIR ?= libbpf/src/ 25 | 26 | OBJECT_LIBBPF = $(LIBBPF_DIR)/libbpf.a 27 | 28 | # Extend if including Makefile already added some 29 | COMMON_OBJS += $(COMMON_DIR)/common_params.o $(COMMON_DIR)/common_user_bpf_xdp.o 30 | 31 | # Create expansions for dependencies 32 | COMMON_H := ${COMMON_OBJS:.o=.h} 33 | 34 | EXTRA_DEPS += 35 | 36 | # BPF-prog kern and userspace shares struct via header file: 37 | KERN_USER_H ?= $(wildcard common_kern_user.h) 38 | 39 | CFLAGS ?= -I$(LIBBPF_DIR)/build/usr/include/ -g 40 | # Extra include for Ubuntu issue #44 41 | CFLAGS += -I/usr/include/x86_64-linux-gnu 42 | CFLAGS += -Iheaders/ 43 | LDFLAGS ?= -L$(LIBBPF_DIR) 44 | 45 | LIBS = -l:libbpf.a -lelf $(USER_LIBS) 46 | 47 | all: llvm-check $(USER_TARGETS) $(XDP_OBJ) $(COPY_LOADER) $(COPY_STATS) 48 | 49 | .PHONY: clean $(CLANG) $(LLC) 50 | 51 | clean: 52 | rm -rf $(LIBBPF_DIR)/build 53 | $(MAKE) -C $(LIBBPF_DIR) clean 54 | $(MAKE) -C $(COMMON_DIR) clean 55 | rm -f $(USER_TARGETS) $(XDP_OBJ) $(USER_OBJ) $(COPY_LOADER) $(COPY_STATS) 56 | rm -f *.ll 57 | rm -f *~ 58 | 59 | # For build dependency on this file, if it gets updated 60 | COMMON_MK = $(COMMON_DIR)/common.mk 61 | 62 | llvm-check: $(CLANG) $(LLC) 63 | @for TOOL in $^ ; do \ 64 | if [ ! $$(command -v $${TOOL} 2>/dev/null) ]; then \ 65 | echo "*** ERROR: Cannot find tool $${TOOL}" ;\ 66 | exit 1; \ 67 | else true; fi; \ 68 | done 69 | 70 | $(OBJECT_LIBBPF): 71 | @if [ ! -d $(LIBBPF_DIR) ]; then \ 72 | echo "Error: Need libbpf submodule"; \ 73 | echo "May need to run git submodule update --init"; \ 74 | exit 1; \ 75 | else \ 76 | cd $(LIBBPF_DIR) && $(MAKE) all; \ 77 | mkdir -p build; DESTDIR=build $(MAKE) install_headers; \ 78 | fi 79 | 80 | # Create dependency: detect if C-file change and touch H-file, to trigger 81 | # target $(COMMON_OBJS) 82 | $(COMMON_H): %.h: %.c 83 | touch $@ 84 | 85 | # Detect if any of common obj changed and create dependency on .h-files 86 | $(COMMON_OBJS): %.o: %.h 87 | make -C $(COMMON_DIR) 88 | 89 | $(USER_TARGETS): %: %.c $(OBJECT_LIBBPF) Makefile $(COMMON_MK) $(COMMON_OBJS) $(KERN_USER_H) $(EXTRA_DEPS) 90 | $(CC) -Wall $(CFLAGS) $(LDFLAGS) -o $@ $(COMMON_OBJS) \ 91 | $< $(LIBS) 92 | 93 | $(XDP_OBJ): %.o: %.c Makefile $(COMMON_MK) $(KERN_USER_H) $(EXTRA_DEPS) 94 | $(CLANG) -S \ 95 | -target bpf \ 96 | -D __BPF_TRACING__ \ 97 | $(CFLAGS) \ 98 | -Wall \ 99 | -Wno-unused-value \ 100 | -Wno-pointer-sign \ 101 | -Wno-compare-distinct-pointer-types \ 102 | -Werror \ 103 | -O2 -emit-llvm -c -g -o ${@:.o=.ll} $< 104 | $(LLC) -march=bpf -filetype=obj -o $@ ${@:.o=.ll} 105 | -------------------------------------------------------------------------------- /common/common_defines.h: -------------------------------------------------------------------------------- 1 | #ifndef __COMMON_DEFINES_H 2 | #define __COMMON_DEFINES_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | struct config { 9 | __u32 xdp_flags; 10 | int ifindex; 11 | char *ifname; 12 | char ifname_buf[IF_NAMESIZE]; 13 | int redirect_ifindex; 14 | char *redirect_ifname; 15 | char redirect_ifname_buf[IF_NAMESIZE]; 16 | bool do_unload; 17 | bool reuse_maps; 18 | char pin_dir[512]; 19 | char filename[512]; 20 | char progsec[32]; 21 | char src_mac[18]; 22 | char dest_mac[18]; 23 | __u16 xsk_bind_flags; 24 | int xsk_if_queue; 25 | bool xsk_poll_mode; 26 | int reflect_port; 27 | int max_sleep_ms; 28 | }; 29 | 30 | /* Defined in common_params.o */ 31 | extern int verbose; 32 | 33 | /* Exit return codes */ 34 | #define EXIT_OK 0 /* == EXIT_SUCCESS (stdlib.h) man exit(3) */ 35 | #define EXIT_FAIL 1 /* == EXIT_FAILURE (stdlib.h) man exit(3) */ 36 | #define EXIT_FAIL_OPTION 2 37 | #define EXIT_FAIL_XDP 30 38 | #define EXIT_FAIL_BPF 40 39 | 40 | #endif /* __COMMON_DEFINES_H */ 41 | -------------------------------------------------------------------------------- /common/common_libbpf.c: -------------------------------------------------------------------------------- 1 | /* Common function that with time should be moved to libbpf */ 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "common_libbpf.h" 10 | 11 | /* From: include/linux/err.h */ 12 | #define MAX_ERRNO 4095 13 | #define IS_ERR_VALUE(x) ((x) >= (unsigned long)-MAX_ERRNO) 14 | static inline bool IS_ERR_OR_NULL(const void *ptr) 15 | { 16 | return (!ptr) || IS_ERR_VALUE((unsigned long)ptr); 17 | } 18 | 19 | #define pr_warning printf 20 | 21 | /* As close as possible to libbpf bpf_prog_load_xattr(), with the 22 | * difference of handling pinned maps. 23 | */ 24 | int bpf_prog_load_xattr_maps(const struct bpf_prog_load_attr_maps *attr, 25 | struct bpf_object **pobj, int *prog_fd) 26 | { 27 | struct bpf_object_open_attr open_attr = { 28 | .file = attr->file, 29 | .prog_type = attr->prog_type, 30 | }; 31 | struct bpf_program *prog, *first_prog = NULL; 32 | enum bpf_attach_type expected_attach_type; 33 | enum bpf_prog_type prog_type; 34 | struct bpf_object *obj; 35 | struct bpf_map *map; 36 | int err; 37 | int i; 38 | 39 | if (!attr) 40 | return -EINVAL; 41 | if (!attr->file) 42 | return -EINVAL; 43 | 44 | 45 | obj = bpf_object__open_xattr(&open_attr); 46 | if (IS_ERR_OR_NULL(obj)) 47 | return -ENOENT; 48 | 49 | bpf_object__for_each_program(prog, obj) { 50 | /* 51 | * If type is not specified, try to guess it based on 52 | * section name. 53 | */ 54 | prog_type = attr->prog_type; 55 | // Was: prog->prog_ifindex = attr->ifindex; 56 | bpf_program__set_ifindex(prog, attr->ifindex); 57 | 58 | expected_attach_type = attr->expected_attach_type; 59 | #if 0 /* Use internal libbpf variables */ 60 | if (prog_type == BPF_PROG_TYPE_UNSPEC) { 61 | err = bpf_program__identify_section(prog, &prog_type, 62 | &expected_attach_type); 63 | if (err < 0) { 64 | bpf_object__close(obj); 65 | return -EINVAL; 66 | } 67 | } 68 | #endif 69 | 70 | bpf_program__set_type(prog, prog_type); 71 | bpf_program__set_expected_attach_type(prog, 72 | expected_attach_type); 73 | 74 | if (!first_prog) 75 | first_prog = prog; 76 | } 77 | 78 | /* Reset attr->pinned_maps.map_fd to identify successful file load */ 79 | for (i = 0; i < attr->nr_pinned_maps; i++) 80 | attr->pinned_maps[i].map_fd = -1; 81 | 82 | bpf_map__for_each(map, obj) { 83 | const char* mapname = bpf_map__name(map); 84 | 85 | if (!bpf_map__is_offload_neutral(map)) 86 | bpf_map__set_ifindex(map, attr->ifindex); 87 | /* Was: map->map_ifindex = attr->ifindex; */ 88 | 89 | for (i = 0; i < attr->nr_pinned_maps; i++) { 90 | struct bpf_pinned_map *pin_map = &attr->pinned_maps[i]; 91 | int fd; 92 | 93 | if (strcmp(mapname, pin_map->name) != 0) 94 | continue; 95 | 96 | /* Matched, try opening pinned file */ 97 | fd = bpf_obj_get(pin_map->filename); 98 | if (fd > 0) { 99 | /* Use FD from pinned map as replacement */ 100 | bpf_map__reuse_fd(map, fd); 101 | /* TODO: Might want to set internal map "name" 102 | * if opened pinned map didn't, to allow 103 | * bpf_object__find_map_fd_by_name() to work. 104 | */ 105 | pin_map->map_fd = fd; 106 | continue; 107 | } 108 | /* Could not open pinned filename map, then this prog 109 | * should then pin the map, BUT this can only happen 110 | * after bpf_object__load(). 111 | */ 112 | } 113 | } 114 | 115 | if (!first_prog) { 116 | pr_warning("object file doesn't contain bpf program\n"); 117 | bpf_object__close(obj); 118 | return -ENOENT; 119 | } 120 | 121 | err = bpf_object__load(obj); 122 | if (err) { 123 | bpf_object__close(obj); 124 | return -EINVAL; 125 | } 126 | 127 | /* Pin the maps that were not loaded via pinned filename */ 128 | bpf_map__for_each(map, obj) { 129 | const char* mapname = bpf_map__name(map); 130 | 131 | for (i = 0; i < attr->nr_pinned_maps; i++) { 132 | struct bpf_pinned_map *pin_map = &attr->pinned_maps[i]; 133 | int err; 134 | 135 | if (strcmp(mapname, pin_map->name) != 0) 136 | continue; 137 | 138 | /* Matched, check if map is already loaded */ 139 | if (pin_map->map_fd != -1) 140 | continue; 141 | 142 | /* Needs to be pinned */ 143 | err = bpf_map__pin(map, pin_map->filename); 144 | if (err) 145 | continue; 146 | pin_map->map_fd = bpf_map__fd(map); 147 | } 148 | } 149 | 150 | /* Help user if requested map name that doesn't exist */ 151 | for (i = 0; i < attr->nr_pinned_maps; i++) { 152 | struct bpf_pinned_map *pin_map = &attr->pinned_maps[i]; 153 | 154 | if (pin_map->map_fd < 0) 155 | pr_warning("%s() requested mapname:%s not seen\n", 156 | __func__, pin_map->name); 157 | } 158 | 159 | *pobj = obj; 160 | *prog_fd = bpf_program__fd(first_prog); 161 | return 0; 162 | } 163 | -------------------------------------------------------------------------------- /common/common_libbpf.h: -------------------------------------------------------------------------------- 1 | /* Common function that with time should be moved to libbpf */ 2 | #ifndef __COMMON_LIBBPF_H 3 | #define __COMMON_LIBBPF_H 4 | 5 | struct bpf_pinned_map { 6 | const char *name; 7 | const char *filename; 8 | int map_fd; 9 | }; 10 | 11 | /* bpf_prog_load_attr extended */ 12 | struct bpf_prog_load_attr_maps { 13 | const char *file; 14 | enum bpf_prog_type prog_type; 15 | enum bpf_attach_type expected_attach_type; 16 | int ifindex; 17 | int nr_pinned_maps; 18 | struct bpf_pinned_map *pinned_maps; 19 | }; 20 | 21 | int bpf_prog_load_xattr_maps(const struct bpf_prog_load_attr_maps *attr, 22 | struct bpf_object **pobj, int *prog_fd); 23 | 24 | #endif /* __COMMON_LIBBPF_H */ 25 | -------------------------------------------------------------------------------- /common/common_params.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include /* XDP_FLAGS_* depend on kernel-headers installed */ 11 | #include 12 | 13 | #include "common_params.h" 14 | 15 | int verbose = 1; 16 | 17 | #define BUFSIZE 30 18 | 19 | void _print_options(const struct option_wrapper *long_options, bool required) 20 | { 21 | int i, pos; 22 | char buf[BUFSIZE]; 23 | 24 | for (i = 0; long_options[i].option.name != 0; i++) { 25 | if (long_options[i].required != required) 26 | continue; 27 | 28 | if (long_options[i].option.val > 64) /* ord('A') = 65 */ 29 | printf(" -%c,", long_options[i].option.val); 30 | else 31 | printf(" "); 32 | pos = snprintf(buf, BUFSIZE, " --%s", long_options[i].option.name); 33 | if (long_options[i].metavar) 34 | snprintf(&buf[pos], BUFSIZE-pos, " %s", long_options[i].metavar); 35 | printf("%-22s", buf); 36 | printf(" %s", long_options[i].help); 37 | printf("\n"); 38 | } 39 | } 40 | 41 | void usage(const char *prog_name, const char *doc, 42 | const struct option_wrapper *long_options, bool full) 43 | { 44 | printf("Usage: %s [options]\n", prog_name); 45 | 46 | if (!full) { 47 | printf("Use --help (or -h) to see full option list.\n"); 48 | return; 49 | } 50 | 51 | printf("\nDOCUMENTATION:\n %s\n", doc); 52 | printf("Required options:\n"); 53 | _print_options(long_options, true); 54 | printf("\n"); 55 | printf("Other options:\n"); 56 | _print_options(long_options, false); 57 | printf("\n"); 58 | } 59 | 60 | int option_wrappers_to_options(const struct option_wrapper *wrapper, 61 | struct option **options) 62 | { 63 | int i, num; 64 | struct option *new_options; 65 | for (i = 0; wrapper[i].option.name != 0; i++) {} 66 | num = i; 67 | 68 | new_options = malloc(sizeof(struct option) * num); 69 | if (!new_options) 70 | return -1; 71 | for (i = 0; i < num; i++) { 72 | memcpy(&new_options[i], &wrapper[i], sizeof(struct option)); 73 | } 74 | 75 | *options = new_options; 76 | return 0; 77 | } 78 | 79 | void parse_cmdline_args(int argc, char **argv, 80 | const struct option_wrapper *options_wrapper, 81 | struct config *cfg, const char *doc) 82 | { 83 | struct option *long_options; 84 | bool full_help = false; 85 | int longindex = 0; 86 | char *dest; 87 | int opt; 88 | 89 | if (option_wrappers_to_options(options_wrapper, &long_options)) { 90 | fprintf(stderr, "Unable to malloc()\n"); 91 | exit(EXIT_FAIL_OPTION); 92 | } 93 | 94 | /* Parse commands line args */ 95 | while ((opt = getopt_long(argc, argv, "hm:d:r:L:R:ASNFUMQ:czpq", 96 | long_options, &longindex)) != -1) { 97 | switch (opt) { 98 | case 'm': 99 | cfg->max_sleep_ms = atoi(optarg); 100 | break; 101 | case 'P': 102 | cfg->reflect_port = atoi(optarg); 103 | break; 104 | case 'd': 105 | if (strlen(optarg) >= IF_NAMESIZE) { 106 | fprintf(stderr, "ERR: --dev name too long\n"); 107 | goto error; 108 | } 109 | cfg->ifname = (char *)&cfg->ifname_buf; 110 | strncpy(cfg->ifname, optarg, IF_NAMESIZE); 111 | cfg->ifindex = if_nametoindex(cfg->ifname); 112 | if (cfg->ifindex == 0) { 113 | fprintf(stderr, 114 | "ERR: --dev name unknown err(%d):%s\n", 115 | errno, strerror(errno)); 116 | goto error; 117 | } 118 | break; 119 | case 'r': 120 | if (strlen(optarg) >= IF_NAMESIZE) { 121 | fprintf(stderr, "ERR: --redirect-dev name too long\n"); 122 | goto error; 123 | } 124 | cfg->redirect_ifname = (char *)&cfg->redirect_ifname_buf; 125 | strncpy(cfg->redirect_ifname, optarg, IF_NAMESIZE); 126 | cfg->redirect_ifindex = if_nametoindex(cfg->redirect_ifname); 127 | if (cfg->redirect_ifindex == 0) { 128 | fprintf(stderr, 129 | "ERR: --redirect-dev name unknown err(%d):%s\n", 130 | errno, strerror(errno)); 131 | goto error; 132 | } 133 | break; 134 | case 'A': 135 | cfg->xdp_flags &= ~XDP_FLAGS_MODES; /* Clear flags */ 136 | break; 137 | case 'S': 138 | cfg->xdp_flags &= ~XDP_FLAGS_MODES; /* Clear flags */ 139 | cfg->xdp_flags |= XDP_FLAGS_SKB_MODE; /* Set flag */ 140 | cfg->xsk_bind_flags &= XDP_ZEROCOPY; 141 | cfg->xsk_bind_flags |= XDP_COPY; 142 | break; 143 | case 'N': 144 | cfg->xdp_flags &= ~XDP_FLAGS_MODES; /* Clear flags */ 145 | cfg->xdp_flags |= XDP_FLAGS_DRV_MODE; /* Set flag */ 146 | break; 147 | case 3: /* --offload-mode */ 148 | cfg->xdp_flags &= ~XDP_FLAGS_MODES; /* Clear flags */ 149 | cfg->xdp_flags |= XDP_FLAGS_HW_MODE; /* Set flag */ 150 | break; 151 | case 'F': 152 | cfg->xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST; 153 | break; 154 | case 'M': 155 | cfg->reuse_maps = true; 156 | break; 157 | case 'U': 158 | cfg->do_unload = true; 159 | break; 160 | case 'p': 161 | cfg->xsk_poll_mode = true; 162 | break; 163 | case 'q': 164 | verbose = false; 165 | break; 166 | case 'Q': 167 | cfg->xsk_if_queue = atoi(optarg); 168 | break; 169 | case 1: /* --filename */ 170 | dest = (char *)&cfg->filename; 171 | strncpy(dest, optarg, sizeof(cfg->filename)); 172 | break; 173 | case 2: /* --progsec */ 174 | dest = (char *)&cfg->progsec; 175 | strncpy(dest, optarg, sizeof(cfg->progsec)); 176 | break; 177 | case 'L': /* --src-mac */ 178 | dest = (char *)&cfg->src_mac; 179 | strncpy(dest, optarg, sizeof(cfg->src_mac)); 180 | break; 181 | case 'R': /* --dest-mac */ 182 | dest = (char *)&cfg->dest_mac; 183 | strncpy(dest, optarg, sizeof(cfg->dest_mac)); 184 | case 'c': 185 | cfg->xsk_bind_flags &= XDP_ZEROCOPY; 186 | cfg->xsk_bind_flags |= XDP_COPY; 187 | break; 188 | case 'z': 189 | cfg->xsk_bind_flags &= XDP_COPY; 190 | cfg->xsk_bind_flags |= XDP_ZEROCOPY; 191 | break; 192 | case 'h': 193 | full_help = true; 194 | /* fall-through */ 195 | error: 196 | default: 197 | usage(argv[0], doc, options_wrapper, full_help); 198 | free(long_options); 199 | exit(EXIT_FAIL_OPTION); 200 | } 201 | } 202 | free(long_options); 203 | } 204 | -------------------------------------------------------------------------------- /common/common_params.h: -------------------------------------------------------------------------------- 1 | /* This common_user.h is used by userspace programs */ 2 | #ifndef __COMMON_PARAMS_H 3 | #define __COMMON_PARAMS_H 4 | 5 | #include 6 | #include "common_defines.h" 7 | 8 | struct option_wrapper { 9 | struct option option; 10 | char *help; 11 | char *metavar; 12 | bool required; 13 | }; 14 | 15 | void usage(const char *prog_name, const char *doc, 16 | const struct option_wrapper *long_options, bool full); 17 | 18 | void parse_cmdline_args(int argc, char **argv, 19 | const struct option_wrapper *long_options, 20 | struct config *cfg, const char *doc); 21 | 22 | #endif /* __COMMON_PARAMS_H */ 23 | -------------------------------------------------------------------------------- /common/common_user_bpf_xdp.c: -------------------------------------------------------------------------------- 1 | #include /* bpf_get_link_xdp_id + bpf_set_link_xdp_id */ 2 | #include /* strerror */ 3 | #include /* IF_NAMESIZE */ 4 | #include /* exit(3) */ 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include /* Need XDP flags */ 11 | #include 12 | 13 | #include "common_defines.h" 14 | 15 | #ifndef PATH_MAX 16 | #define PATH_MAX 4096 17 | #endif 18 | 19 | int xdp_link_attach(int ifindex, __u32 xdp_flags, int prog_fd) 20 | { 21 | int err; 22 | 23 | /* libbpf provide the XDP net_device link-level hook attach helper */ 24 | err = bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags); 25 | if (err == -EEXIST && !(xdp_flags & XDP_FLAGS_UPDATE_IF_NOEXIST)) { 26 | /* Force mode didn't work, probably because a program of the 27 | * opposite type is loaded. Let's unload that and try loading 28 | * again. 29 | */ 30 | 31 | __u32 old_flags = xdp_flags; 32 | 33 | xdp_flags &= ~XDP_FLAGS_MODES; 34 | xdp_flags |= (old_flags & XDP_FLAGS_SKB_MODE) ? XDP_FLAGS_DRV_MODE : XDP_FLAGS_SKB_MODE; 35 | err = bpf_set_link_xdp_fd(ifindex, -1, xdp_flags); 36 | if (!err) 37 | err = bpf_set_link_xdp_fd(ifindex, prog_fd, old_flags); 38 | } 39 | if (err < 0) { 40 | fprintf(stderr, "ERR: " 41 | "ifindex(%d) link set xdp fd failed (%d): %s\n", 42 | ifindex, -err, strerror(-err)); 43 | 44 | switch (-err) { 45 | case EBUSY: 46 | case EEXIST: 47 | fprintf(stderr, "Hint: XDP already loaded on device" 48 | " use --force to swap/replace\n"); 49 | break; 50 | case EOPNOTSUPP: 51 | fprintf(stderr, "Hint: Native-XDP not supported" 52 | " use --skb-mode or --auto-mode\n"); 53 | break; 54 | default: 55 | break; 56 | } 57 | return EXIT_FAIL_XDP; 58 | } 59 | 60 | return EXIT_OK; 61 | } 62 | 63 | int xdp_link_detach(int ifindex, __u32 xdp_flags, __u32 expected_prog_id) 64 | { 65 | __u32 curr_prog_id; 66 | int err; 67 | 68 | err = bpf_get_link_xdp_id(ifindex, &curr_prog_id, xdp_flags); 69 | if (err) { 70 | fprintf(stderr, "ERR: get link xdp id failed (err=%d): %s\n", 71 | -err, strerror(-err)); 72 | return EXIT_FAIL_XDP; 73 | } 74 | 75 | if (!curr_prog_id) { 76 | if (verbose) 77 | printf("INFO: %s() no curr XDP prog on ifindex:%d\n", 78 | __func__, ifindex); 79 | return EXIT_OK; 80 | } 81 | 82 | if (expected_prog_id && curr_prog_id != expected_prog_id) { 83 | fprintf(stderr, "ERR: %s() " 84 | "expected prog ID(%d) no match(%d), not removing\n", 85 | __func__, expected_prog_id, curr_prog_id); 86 | return EXIT_FAIL; 87 | } 88 | 89 | if ((err = bpf_set_link_xdp_fd(ifindex, -1, xdp_flags)) < 0) { 90 | fprintf(stderr, "ERR: %s() link set xdp failed (err=%d): %s\n", 91 | __func__, err, strerror(-err)); 92 | return EXIT_FAIL_XDP; 93 | } 94 | 95 | if (verbose) 96 | printf("INFO: %s() removed XDP prog ID:%d on ifindex:%d\n", 97 | __func__, curr_prog_id, ifindex); 98 | 99 | return EXIT_OK; 100 | } 101 | 102 | struct bpf_object *load_bpf_object_file(const char *filename, int ifindex) 103 | { 104 | int first_prog_fd = -1; 105 | struct bpf_object *obj; 106 | int err; 107 | 108 | /* This struct allow us to set ifindex, this features is used for 109 | * hardware offloading XDP programs (note this sets libbpf 110 | * bpf_program->prog_ifindex and foreach bpf_map->map_ifindex). 111 | */ 112 | struct bpf_prog_load_attr prog_load_attr = { 113 | .prog_type = BPF_PROG_TYPE_XDP, 114 | .ifindex = ifindex, 115 | }; 116 | prog_load_attr.file = filename; 117 | 118 | /* Use libbpf for extracting BPF byte-code from BPF-ELF object, and 119 | * loading this into the kernel via bpf-syscall 120 | */ 121 | err = bpf_prog_load_xattr(&prog_load_attr, &obj, &first_prog_fd); 122 | if (err) { 123 | fprintf(stderr, "ERR: loading BPF-OBJ file(%s) (%d): %s\n", 124 | filename, err, strerror(-err)); 125 | return NULL; 126 | } 127 | 128 | /* Notice how a pointer to a libbpf bpf_object is returned */ 129 | return obj; 130 | } 131 | 132 | static struct bpf_object *open_bpf_object(const char *file, int ifindex) 133 | { 134 | int err; 135 | struct bpf_object *obj; 136 | struct bpf_map *map; 137 | struct bpf_program *prog, *first_prog = NULL; 138 | 139 | struct bpf_object_open_attr open_attr = { 140 | .file = file, 141 | .prog_type = BPF_PROG_TYPE_XDP, 142 | }; 143 | 144 | obj = bpf_object__open_xattr(&open_attr); 145 | if (IS_ERR_OR_NULL(obj)) { 146 | err = -PTR_ERR(obj); 147 | fprintf(stderr, "ERR: opening BPF-OBJ file(%s) (%d): %s\n", 148 | file, err, strerror(-err)); 149 | return NULL; 150 | } 151 | 152 | bpf_object__for_each_program(prog, obj) { 153 | bpf_program__set_type(prog, BPF_PROG_TYPE_XDP); 154 | bpf_program__set_ifindex(prog, ifindex); 155 | if (!first_prog) 156 | first_prog = prog; 157 | } 158 | 159 | bpf_object__for_each_map(map, obj) { 160 | if (!bpf_map__is_offload_neutral(map)) 161 | bpf_map__set_ifindex(map, ifindex); 162 | } 163 | 164 | if (!first_prog) { 165 | fprintf(stderr, "ERR: file %s contains no programs\n", file); 166 | return NULL; 167 | } 168 | 169 | return obj; 170 | } 171 | 172 | static int reuse_maps(struct bpf_object *obj, const char *path) 173 | { 174 | struct bpf_map *map; 175 | 176 | if (!obj) 177 | return -ENOENT; 178 | 179 | if (!path) 180 | return -EINVAL; 181 | 182 | bpf_object__for_each_map(map, obj) { 183 | int len, err; 184 | int pinned_map_fd; 185 | char buf[PATH_MAX]; 186 | 187 | len = snprintf(buf, PATH_MAX, "%s/%s", path, bpf_map__name(map)); 188 | if (len < 0) { 189 | return -EINVAL; 190 | } else if (len >= PATH_MAX) { 191 | return -ENAMETOOLONG; 192 | } 193 | 194 | pinned_map_fd = bpf_obj_get(buf); 195 | if (pinned_map_fd < 0) 196 | return pinned_map_fd; 197 | 198 | err = bpf_map__reuse_fd(map, pinned_map_fd); 199 | if (err) 200 | return err; 201 | } 202 | 203 | return 0; 204 | } 205 | 206 | struct bpf_object *load_bpf_object_file_reuse_maps(const char *file, 207 | int ifindex, 208 | const char *pin_dir) 209 | { 210 | int err; 211 | struct bpf_object *obj; 212 | 213 | obj = open_bpf_object(file, ifindex); 214 | if (!obj) { 215 | fprintf(stderr, "ERR: failed to open object %s\n", file); 216 | return NULL; 217 | } 218 | 219 | err = reuse_maps(obj, pin_dir); 220 | if (err) { 221 | fprintf(stderr, "ERR: failed to reuse maps for object %s, pin_dir=%s\n", 222 | file, pin_dir); 223 | return NULL; 224 | } 225 | 226 | err = bpf_object__load(obj); 227 | if (err) { 228 | fprintf(stderr, "ERR: loading BPF-OBJ file(%s) (%d): %s\n", 229 | file, err, strerror(-err)); 230 | return NULL; 231 | } 232 | 233 | return obj; 234 | } 235 | 236 | struct bpf_object *load_bpf_and_xdp_attach(struct config *cfg) 237 | { 238 | struct bpf_program *bpf_prog; 239 | struct bpf_object *bpf_obj; 240 | int offload_ifindex = 0; 241 | int prog_fd = -1; 242 | int err; 243 | 244 | /* If flags indicate hardware offload, supply ifindex */ 245 | if (cfg->xdp_flags & XDP_FLAGS_HW_MODE) 246 | offload_ifindex = cfg->ifindex; 247 | 248 | /* Load the BPF-ELF object file and get back libbpf bpf_object */ 249 | if (cfg->reuse_maps) 250 | bpf_obj = load_bpf_object_file_reuse_maps(cfg->filename, 251 | offload_ifindex, 252 | cfg->pin_dir); 253 | else 254 | bpf_obj = load_bpf_object_file(cfg->filename, offload_ifindex); 255 | if (!bpf_obj) { 256 | fprintf(stderr, "ERR: loading file: %s\n", cfg->filename); 257 | exit(EXIT_FAIL_BPF); 258 | } 259 | /* At this point: All XDP/BPF programs from the cfg->filename have been 260 | * loaded into the kernel, and evaluated by the verifier. Only one of 261 | * these gets attached to XDP hook, the others will get freed once this 262 | * process exit. 263 | */ 264 | 265 | if (cfg->progsec[0]) 266 | /* Find a matching BPF prog section name */ 267 | bpf_prog = bpf_object__find_program_by_title(bpf_obj, cfg->progsec); 268 | else 269 | /* Find the first program */ 270 | bpf_prog = bpf_program__next(NULL, bpf_obj); 271 | 272 | if (!bpf_prog) { 273 | fprintf(stderr, "ERR: couldn't find a program in ELF section '%s'\n", cfg->progsec); 274 | exit(EXIT_FAIL_BPF); 275 | } 276 | 277 | strncpy(cfg->progsec, bpf_program__title(bpf_prog, false), sizeof(cfg->progsec)); 278 | 279 | prog_fd = bpf_program__fd(bpf_prog); 280 | if (prog_fd <= 0) { 281 | fprintf(stderr, "ERR: bpf_program__fd failed\n"); 282 | exit(EXIT_FAIL_BPF); 283 | } 284 | 285 | /* At this point: BPF-progs are (only) loaded by the kernel, and prog_fd 286 | * is our select file-descriptor handle. Next step is attaching this FD 287 | * to a kernel hook point, in this case XDP net_device link-level hook. 288 | */ 289 | err = xdp_link_attach(cfg->ifindex, cfg->xdp_flags, prog_fd); 290 | if (err) 291 | exit(err); 292 | 293 | return bpf_obj; 294 | } 295 | 296 | #define XDP_UNKNOWN XDP_REDIRECT + 1 297 | #ifndef XDP_ACTION_MAX 298 | #define XDP_ACTION_MAX (XDP_UNKNOWN + 1) 299 | #endif 300 | 301 | static const char *xdp_action_names[XDP_ACTION_MAX] = { 302 | [XDP_ABORTED] = "XDP_ABORTED", 303 | [XDP_DROP] = "XDP_DROP", 304 | [XDP_PASS] = "XDP_PASS", 305 | [XDP_TX] = "XDP_TX", 306 | [XDP_REDIRECT] = "XDP_REDIRECT", 307 | [XDP_UNKNOWN] = "XDP_UNKNOWN", 308 | }; 309 | 310 | const char *action2str(__u32 action) 311 | { 312 | if (action < XDP_ACTION_MAX) 313 | return xdp_action_names[action]; 314 | return NULL; 315 | } 316 | 317 | int check_map_fd_info(const struct bpf_map_info *info, 318 | const struct bpf_map_info *exp) 319 | { 320 | if (exp->key_size && exp->key_size != info->key_size) { 321 | fprintf(stderr, "ERR: %s() " 322 | "Map key size(%d) mismatch expected size(%d)\n", 323 | __func__, info->key_size, exp->key_size); 324 | return EXIT_FAIL; 325 | } 326 | if (exp->value_size && exp->value_size != info->value_size) { 327 | fprintf(stderr, "ERR: %s() " 328 | "Map value size(%d) mismatch expected size(%d)\n", 329 | __func__, info->value_size, exp->value_size); 330 | return EXIT_FAIL; 331 | } 332 | if (exp->max_entries && exp->max_entries != info->max_entries) { 333 | fprintf(stderr, "ERR: %s() " 334 | "Map max_entries(%d) mismatch expected size(%d)\n", 335 | __func__, info->max_entries, exp->max_entries); 336 | return EXIT_FAIL; 337 | } 338 | if (exp->type && exp->type != info->type) { 339 | fprintf(stderr, "ERR: %s() " 340 | "Map type(%d) mismatch expected type(%d)\n", 341 | __func__, info->type, exp->type); 342 | return EXIT_FAIL; 343 | } 344 | 345 | return 0; 346 | } 347 | 348 | int open_bpf_map_file(const char *pin_dir, 349 | const char *mapname, 350 | struct bpf_map_info *info) 351 | { 352 | char filename[PATH_MAX]; 353 | int err, len, fd; 354 | __u32 info_len = sizeof(*info); 355 | 356 | len = snprintf(filename, PATH_MAX, "%s/%s", pin_dir, mapname); 357 | if (len < 0) { 358 | fprintf(stderr, "ERR: constructing full mapname path\n"); 359 | return -1; 360 | } 361 | 362 | fd = bpf_obj_get(filename); 363 | if (fd < 0) { 364 | fprintf(stderr, 365 | "WARN: Failed to open bpf map file:%s err(%d):%s\n", 366 | filename, errno, strerror(errno)); 367 | return fd; 368 | } 369 | 370 | if (info) { 371 | err = bpf_obj_get_info_by_fd(fd, info, &info_len); 372 | if (err) { 373 | fprintf(stderr, "ERR: %s() can't get info - %s\n", 374 | __func__, strerror(errno)); 375 | return EXIT_FAIL_BPF; 376 | } 377 | } 378 | 379 | return fd; 380 | } 381 | -------------------------------------------------------------------------------- /common/common_user_bpf_xdp.h: -------------------------------------------------------------------------------- 1 | /* Common BPF/XDP functions used by userspace side programs */ 2 | #ifndef __COMMON_USER_BPF_XDP_H 3 | #define __COMMON_USER_BPF_XDP_H 4 | 5 | int xdp_link_attach(int ifindex, __u32 xdp_flags, int prog_fd); 6 | int xdp_link_detach(int ifindex, __u32 xdp_flags, __u32 expected_prog_id); 7 | 8 | struct bpf_object *load_bpf_object_file(const char *filename, int ifindex); 9 | struct bpf_object *load_bpf_and_xdp_attach(struct config *cfg); 10 | 11 | const char *action2str(__u32 action); 12 | 13 | int check_map_fd_info(const struct bpf_map_info *info, 14 | const struct bpf_map_info *exp); 15 | 16 | int open_bpf_map_file(const char *pin_dir, 17 | const char *mapname, 18 | struct bpf_map_info *info); 19 | 20 | #endif /* __COMMON_USER_BPF_XDP_H */ 21 | -------------------------------------------------------------------------------- /common/parsing_helpers.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* 3 | * This file contains parsing functions that are used in the packetXX XDP 4 | * programs. The functions are marked as __always_inline, and fully defined in 5 | * this header file to be included in the BPF program. 6 | * 7 | * Each helper parses a packet header, including doing bounds checking, and 8 | * returns the type of its contents if successful, and -1 otherwise. 9 | * 10 | * For Ethernet and IP headers, the content type is the type of the payload 11 | * (h_proto for Ethernet, nexthdr for IPv6), for ICMP it is the ICMP type field. 12 | * All return values are in host byte order. 13 | * 14 | * The versions of the functions included here are slightly expanded versions of 15 | * the functions in the packet01 lesson. For instance, the Ethernet header 16 | * parsing has support for parsing VLAN tags. 17 | */ 18 | 19 | #ifndef __PARSING_HELPERS_H 20 | #define __PARSING_HELPERS_H 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | /* Header cursor to keep track of current parsing position */ 33 | struct hdr_cursor { 34 | void *pos; 35 | }; 36 | 37 | /* 38 | * struct vlan_hdr - vlan header 39 | * @h_vlan_TCI: priority and VLAN ID 40 | * @h_vlan_encapsulated_proto: packet type ID or len 41 | */ 42 | struct vlan_hdr { 43 | __be16 h_vlan_TCI; 44 | __be16 h_vlan_encapsulated_proto; 45 | }; 46 | 47 | /* 48 | * Struct icmphdr_common represents the common part of the icmphdr and icmp6hdr 49 | * structures. 50 | */ 51 | struct icmphdr_common { 52 | __u8 type; 53 | __u8 code; 54 | __sum16 cksum; 55 | }; 56 | 57 | /* Allow users of header file to redefine VLAN max depth */ 58 | #ifndef VLAN_MAX_DEPTH 59 | #define VLAN_MAX_DEPTH 4 60 | #endif 61 | 62 | static __always_inline int proto_is_vlan(__u16 h_proto) 63 | { 64 | return !!(h_proto == bpf_htons(ETH_P_8021Q) || 65 | h_proto == bpf_htons(ETH_P_8021AD)); 66 | } 67 | 68 | /* Notice, parse_ethhdr() will skip VLAN tags, by advancing nh->pos and returns 69 | * next header EtherType, BUT the ethhdr pointer supplied still points to the 70 | * Ethernet header. Thus, caller can look at eth->h_proto to see if this was a 71 | * VLAN tagged packet. 72 | */ 73 | static __always_inline int parse_ethhdr(struct hdr_cursor *nh, void *data_end, 74 | struct ethhdr **ethhdr) 75 | { 76 | struct ethhdr *eth = nh->pos; 77 | int hdrsize = sizeof(*eth); 78 | struct vlan_hdr *vlh; 79 | __u16 h_proto; 80 | int i; 81 | 82 | /* Byte-count bounds check; check if current pointer + size of header 83 | * is after data_end. 84 | */ 85 | if (nh->pos + hdrsize > data_end) 86 | return -1; 87 | 88 | nh->pos += hdrsize; 89 | *ethhdr = eth; 90 | vlh = nh->pos; 91 | h_proto = eth->h_proto; 92 | 93 | /* Use loop unrolling to avoid the verifier restriction on loops; 94 | * support up to VLAN_MAX_DEPTH layers of VLAN encapsulation. 95 | */ 96 | #pragma unroll 97 | for (i = 0; i < VLAN_MAX_DEPTH; i++) { 98 | if (!proto_is_vlan(h_proto)) 99 | break; 100 | 101 | if (vlh + 1 > data_end) 102 | break; 103 | 104 | h_proto = vlh->h_vlan_encapsulated_proto; 105 | vlh++; 106 | } 107 | 108 | nh->pos = vlh; 109 | return h_proto; /* network-byte-order */ 110 | } 111 | 112 | static __always_inline int parse_ip6hdr(struct hdr_cursor *nh, 113 | void *data_end, 114 | struct ipv6hdr **ip6hdr) 115 | { 116 | struct ipv6hdr *ip6h = nh->pos; 117 | 118 | /* Pointer-arithmetic bounds check; pointer +1 points to after end of 119 | * thing being pointed to. We will be using this style in the remainder 120 | * of the tutorial. 121 | */ 122 | if (ip6h + 1 > data_end) 123 | return -1; 124 | 125 | nh->pos = ip6h + 1; 126 | *ip6hdr = ip6h; 127 | 128 | return ip6h->nexthdr; 129 | } 130 | 131 | static __always_inline int parse_iphdr(struct hdr_cursor *nh, 132 | void *data_end, 133 | struct iphdr **iphdr) 134 | { 135 | struct iphdr *iph = nh->pos; 136 | int hdrsize; 137 | 138 | if (iph + 1 > data_end) 139 | return -1; 140 | 141 | hdrsize = iph->ihl * 4; 142 | 143 | /* Variable-length IPv4 header, need to use byte-based arithmetic */ 144 | if (nh->pos + hdrsize > data_end) 145 | return -1; 146 | 147 | nh->pos += hdrsize; 148 | *iphdr = iph; 149 | 150 | return iph->protocol; 151 | } 152 | 153 | static __always_inline int parse_icmp6hdr(struct hdr_cursor *nh, 154 | void *data_end, 155 | struct icmp6hdr **icmp6hdr) 156 | { 157 | struct icmp6hdr *icmp6h = nh->pos; 158 | 159 | if (icmp6h + 1 > data_end) 160 | return -1; 161 | 162 | nh->pos = icmp6h + 1; 163 | *icmp6hdr = icmp6h; 164 | 165 | return icmp6h->icmp6_type; 166 | } 167 | 168 | static __always_inline int parse_icmphdr(struct hdr_cursor *nh, 169 | void *data_end, 170 | struct icmphdr **icmphdr) 171 | { 172 | struct icmphdr *icmph = nh->pos; 173 | 174 | if (icmph + 1 > data_end) 175 | return -1; 176 | 177 | nh->pos = icmph + 1; 178 | *icmphdr = icmph; 179 | 180 | return icmph->type; 181 | } 182 | 183 | static __always_inline int parse_icmphdr_common(struct hdr_cursor *nh, 184 | void *data_end, 185 | struct icmphdr_common **icmphdr) 186 | { 187 | struct icmphdr_common *h = nh->pos; 188 | 189 | if (h + 1 > data_end) 190 | return -1; 191 | 192 | nh->pos = h + 1; 193 | *icmphdr = h; 194 | 195 | return h->type; 196 | } 197 | 198 | /* 199 | * parse_tcphdr: parse the udp header and return the length of the udp payload 200 | */ 201 | static __always_inline int parse_udphdr(struct hdr_cursor *nh, 202 | void *data_end, 203 | struct udphdr **udphdr) 204 | { 205 | int len; 206 | struct udphdr *h = nh->pos; 207 | 208 | if (h + 1 > data_end) 209 | return -1; 210 | 211 | nh->pos = h + 1; 212 | *udphdr = h; 213 | 214 | len = bpf_ntohs(h->len) - sizeof(struct udphdr); 215 | if (len < 0) 216 | return -1; 217 | 218 | return len; 219 | } 220 | 221 | /* 222 | * parse_tcphdr: parse and return the length of the tcp header 223 | */ 224 | static __always_inline int parse_tcphdr(struct hdr_cursor *nh, 225 | void *data_end, 226 | struct tcphdr **tcphdr) 227 | { 228 | int len; 229 | struct tcphdr *h = nh->pos; 230 | 231 | if (h + 1 > data_end) 232 | return -1; 233 | 234 | len = h->doff * 4; 235 | if ((void *) h + len > data_end) 236 | return -1; 237 | 238 | nh->pos = h + 1; 239 | *tcphdr = h; 240 | 241 | return len; 242 | } 243 | 244 | #endif /* __PARSING_HELPERS_H */ 245 | -------------------------------------------------------------------------------- /common/rewrite_helpers.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* 3 | * This file contains functions that are used in the packetXX XDP programs to 4 | * manipulate on packets data. The functions are marked as __always_inline, and 5 | * fully defined in this header file to be included in the BPF program. 6 | */ 7 | 8 | #ifndef __REWRITE_HELPERS_H 9 | #define __REWRITE_HELPERS_H 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "bpf_helpers.h" 17 | #include "bpf_endian.h" 18 | 19 | /* Pops the outermost VLAN tag off the packet. Returns the popped VLAN ID on 20 | * success or negative errno on failure. 21 | */ 22 | static __always_inline int vlan_tag_pop(struct xdp_md *ctx, struct ethhdr *eth) 23 | { 24 | void *data_end = (void *)(long)ctx->data_end; 25 | struct ethhdr eth_cpy; 26 | struct vlan_hdr *vlh; 27 | __be16 h_proto; 28 | int vlid; 29 | 30 | if (!proto_is_vlan(eth->h_proto)) 31 | return -1; 32 | 33 | /* Careful with the parenthesis here */ 34 | vlh = (void *)(eth + 1); 35 | 36 | /* Still need to do bounds checking */ 37 | if (vlh + 1 > data_end) 38 | return -1; 39 | 40 | /* Save vlan ID for returning, h_proto for updating Ethernet header */ 41 | vlid = bpf_ntohs(vlh->h_vlan_TCI); 42 | h_proto = vlh->h_vlan_encapsulated_proto; 43 | 44 | /* Make a copy of the outer Ethernet header before we cut it off */ 45 | __builtin_memcpy(ð_cpy, eth, sizeof(eth_cpy)); 46 | 47 | /* Actually adjust the head pointer */ 48 | if (bpf_xdp_adjust_head(ctx, (int)sizeof(*vlh))) 49 | return -1; 50 | 51 | /* Need to re-evaluate data *and* data_end and do new bounds checking 52 | * after adjusting head 53 | */ 54 | eth = (void *)(long)ctx->data; 55 | data_end = (void *)(long)ctx->data_end; 56 | if (eth + 1 > data_end) 57 | return -1; 58 | 59 | /* Copy back the old Ethernet header and update the proto type */ 60 | __builtin_memcpy(eth, ð_cpy, sizeof(*eth)); 61 | eth->h_proto = h_proto; 62 | 63 | return vlid; 64 | } 65 | 66 | /* Pushes a new VLAN tag after the Ethernet header. Returns 0 on success, 67 | * -1 on failure. 68 | */ 69 | static __always_inline int vlan_tag_push(struct xdp_md *ctx, 70 | struct ethhdr *eth, int vlid) 71 | { 72 | void *data_end = (void *)(long)ctx->data_end; 73 | struct ethhdr eth_cpy; 74 | struct vlan_hdr *vlh; 75 | 76 | /* First copy the original Ethernet header */ 77 | __builtin_memcpy(ð_cpy, eth, sizeof(eth_cpy)); 78 | 79 | /* Then add space in front of the packet */ 80 | if (bpf_xdp_adjust_head(ctx, 0 - (int)sizeof(*vlh))) 81 | return -1; 82 | 83 | /* Need to re-evaluate data_end and data after head adjustment, and 84 | * bounds check, even though we know there is enough space (as we 85 | * increased it). 86 | */ 87 | data_end = (void *)(long)ctx->data_end; 88 | eth = (void *)(long)ctx->data; 89 | 90 | if (eth + 1 > data_end) 91 | return -1; 92 | 93 | /* Copy back Ethernet header in the right place, populate VLAN tag with 94 | * ID and proto, and set outer Ethernet header to VLAN type. 95 | */ 96 | __builtin_memcpy(eth, ð_cpy, sizeof(*eth)); 97 | 98 | vlh = (void *)(eth + 1); 99 | 100 | if (vlh + 1 > data_end) 101 | return -1; 102 | 103 | vlh->h_vlan_TCI = bpf_htons(vlid); 104 | vlh->h_vlan_encapsulated_proto = eth->h_proto; 105 | 106 | eth->h_proto = bpf_htons(ETH_P_8021Q); 107 | return 0; 108 | } 109 | 110 | /* 111 | * Swaps destination and source MAC addresses inside an Ethernet header 112 | */ 113 | static __always_inline void swap_src_dst_mac(struct ethhdr *eth) 114 | { 115 | __u8 h_tmp[ETH_ALEN]; 116 | 117 | __builtin_memcpy(h_tmp, eth->h_source, ETH_ALEN); 118 | __builtin_memcpy(eth->h_source, eth->h_dest, ETH_ALEN); 119 | __builtin_memcpy(eth->h_dest, h_tmp, ETH_ALEN); 120 | } 121 | 122 | /* 123 | * Swaps destination and source IPv6 addresses inside an IPv6 header 124 | */ 125 | static __always_inline void swap_src_dst_ipv6(struct ipv6hdr *ipv6) 126 | { 127 | struct in6_addr tmp = ipv6->saddr; 128 | 129 | ipv6->saddr = ipv6->daddr; 130 | ipv6->daddr = tmp; 131 | } 132 | 133 | /* 134 | * Swaps destination and source IPv4 addresses inside an IPv4 header 135 | */ 136 | static __always_inline void swap_src_dst_ipv4(struct iphdr *iphdr) 137 | { 138 | __be32 tmp = iphdr->saddr; 139 | 140 | iphdr->saddr = iphdr->daddr; 141 | iphdr->daddr = tmp; 142 | } 143 | 144 | #endif /* __REWRITE_HELPERS_H */ 145 | -------------------------------------------------------------------------------- /headers/bpf_endian.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* Copied from $(LINUX)/tools/testing/selftests/bpf/bpf_endian.h */ 3 | #ifndef __BPF_ENDIAN__ 4 | #define __BPF_ENDIAN__ 5 | 6 | #include 7 | 8 | /* LLVM's BPF target selects the endianness of the CPU 9 | * it compiles on, or the user specifies (bpfel/bpfeb), 10 | * respectively. The used __BYTE_ORDER__ is defined by 11 | * the compiler, we cannot rely on __BYTE_ORDER from 12 | * libc headers, since it doesn't reflect the actual 13 | * requested byte order. 14 | * 15 | * Note, LLVM's BPF target has different __builtin_bswapX() 16 | * semantics. It does map to BPF_ALU | BPF_END | BPF_TO_BE 17 | * in bpfel and bpfeb case, which means below, that we map 18 | * to cpu_to_be16(). We could use it unconditionally in BPF 19 | * case, but better not rely on it, so that this header here 20 | * can be used from application and BPF program side, which 21 | * use different targets. 22 | */ 23 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 24 | # define __bpf_ntohs(x)__builtin_bswap16(x) 25 | # define __bpf_htons(x)__builtin_bswap16(x) 26 | # define __bpf_constant_ntohs(x)___constant_swab16(x) 27 | # define __bpf_constant_htons(x)___constant_swab16(x) 28 | # define __bpf_ntohl(x)__builtin_bswap32(x) 29 | # define __bpf_htonl(x)__builtin_bswap32(x) 30 | # define __bpf_constant_ntohl(x)___constant_swab32(x) 31 | # define __bpf_constant_htonl(x)___constant_swab32(x) 32 | #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 33 | # define __bpf_ntohs(x)(x) 34 | # define __bpf_htons(x)(x) 35 | # define __bpf_constant_ntohs(x)(x) 36 | # define __bpf_constant_htons(x)(x) 37 | # define __bpf_ntohl(x)(x) 38 | # define __bpf_htonl(x)(x) 39 | # define __bpf_constant_ntohl(x)(x) 40 | # define __bpf_constant_htonl(x)(x) 41 | #else 42 | # error "Fix your compiler's __BYTE_ORDER__?!" 43 | #endif 44 | 45 | #define bpf_htons(x)\ 46 | (__builtin_constant_p(x) ?\ 47 | __bpf_constant_htons(x) : __bpf_htons(x)) 48 | #define bpf_ntohs(x)\ 49 | (__builtin_constant_p(x) ?\ 50 | __bpf_constant_ntohs(x) : __bpf_ntohs(x)) 51 | #define bpf_htonl(x)\ 52 | (__builtin_constant_p(x) ?\ 53 | __bpf_constant_htonl(x) : __bpf_htonl(x)) 54 | #define bpf_ntohl(x)\ 55 | (__builtin_constant_p(x) ?\ 56 | __bpf_constant_ntohl(x) : __bpf_ntohl(x)) 57 | 58 | #endif /* __BPF_ENDIAN__ */ 59 | -------------------------------------------------------------------------------- /headers/bpf_helpers.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* Copied from $(LINUX)/tools/testing/selftests/bpf/bpf_helpers.h */ 3 | 4 | /* Added to fix compilation on old Ubuntu systems - please preserve when 5 | updating file! */ 6 | #ifndef __always_inline 7 | # define __always_inline inline __attribute__((always_inline)) 8 | #endif 9 | 10 | #ifndef __BPF_HELPERS_H 11 | #define __BPF_HELPERS_H 12 | 13 | /* helper macro to place programs, maps, license in 14 | * different sections in elf_bpf file. Section names 15 | * are interpreted by elf_bpf loader 16 | */ 17 | #define SEC(NAME) __attribute__((section(NAME), used)) 18 | 19 | /* helper functions called from eBPF programs written in C */ 20 | static void *(*bpf_map_lookup_elem)(void *map, void *key) = 21 | (void *) BPF_FUNC_map_lookup_elem; 22 | static int (*bpf_map_update_elem)(void *map, void *key, void *value, 23 | unsigned long long flags) = 24 | (void *) BPF_FUNC_map_update_elem; 25 | static int (*bpf_map_delete_elem)(void *map, void *key) = 26 | (void *) BPF_FUNC_map_delete_elem; 27 | static int (*bpf_map_push_elem)(void *map, void *value, 28 | unsigned long long flags) = 29 | (void *) BPF_FUNC_map_push_elem; 30 | static int (*bpf_map_pop_elem)(void *map, void *value) = 31 | (void *) BPF_FUNC_map_pop_elem; 32 | static int (*bpf_map_peek_elem)(void *map, void *value) = 33 | (void *) BPF_FUNC_map_peek_elem; 34 | static int (*bpf_probe_read)(void *dst, int size, void *unsafe_ptr) = 35 | (void *) BPF_FUNC_probe_read; 36 | static unsigned long long (*bpf_ktime_get_ns)(void) = 37 | (void *) BPF_FUNC_ktime_get_ns; 38 | static int (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) = 39 | (void *) BPF_FUNC_trace_printk; 40 | static void (*bpf_tail_call)(void *ctx, void *map, int index) = 41 | (void *) BPF_FUNC_tail_call; 42 | static unsigned long long (*bpf_get_smp_processor_id)(void) = 43 | (void *) BPF_FUNC_get_smp_processor_id; 44 | static unsigned long long (*bpf_get_current_pid_tgid)(void) = 45 | (void *) BPF_FUNC_get_current_pid_tgid; 46 | static unsigned long long (*bpf_get_current_uid_gid)(void) = 47 | (void *) BPF_FUNC_get_current_uid_gid; 48 | static int (*bpf_get_current_comm)(void *buf, int buf_size) = 49 | (void *) BPF_FUNC_get_current_comm; 50 | static unsigned long long (*bpf_perf_event_read)(void *map, 51 | unsigned long long flags) = 52 | (void *) BPF_FUNC_perf_event_read; 53 | static int (*bpf_clone_redirect)(void *ctx, int ifindex, int flags) = 54 | (void *) BPF_FUNC_clone_redirect; 55 | static int (*bpf_redirect)(int ifindex, int flags) = 56 | (void *) BPF_FUNC_redirect; 57 | static int (*bpf_redirect_map)(void *map, int key, int flags) = 58 | (void *) BPF_FUNC_redirect_map; 59 | static int (*bpf_perf_event_output)(void *ctx, void *map, 60 | unsigned long long flags, void *data, 61 | int size) = 62 | (void *) BPF_FUNC_perf_event_output; 63 | static int (*bpf_get_stackid)(void *ctx, void *map, int flags) = 64 | (void *) BPF_FUNC_get_stackid; 65 | static int (*bpf_probe_write_user)(void *dst, void *src, int size) = 66 | (void *) BPF_FUNC_probe_write_user; 67 | static int (*bpf_current_task_under_cgroup)(void *map, int index) = 68 | (void *) BPF_FUNC_current_task_under_cgroup; 69 | static int (*bpf_skb_get_tunnel_key)(void *ctx, void *key, int size, int flags) = 70 | (void *) BPF_FUNC_skb_get_tunnel_key; 71 | static int (*bpf_skb_set_tunnel_key)(void *ctx, void *key, int size, int flags) = 72 | (void *) BPF_FUNC_skb_set_tunnel_key; 73 | static int (*bpf_skb_get_tunnel_opt)(void *ctx, void *md, int size) = 74 | (void *) BPF_FUNC_skb_get_tunnel_opt; 75 | static int (*bpf_skb_set_tunnel_opt)(void *ctx, void *md, int size) = 76 | (void *) BPF_FUNC_skb_set_tunnel_opt; 77 | static unsigned long long (*bpf_get_prandom_u32)(void) = 78 | (void *) BPF_FUNC_get_prandom_u32; 79 | static int (*bpf_xdp_adjust_head)(void *ctx, int offset) = 80 | (void *) BPF_FUNC_xdp_adjust_head; 81 | static int (*bpf_xdp_adjust_meta)(void *ctx, int offset) = 82 | (void *) BPF_FUNC_xdp_adjust_meta; 83 | static int (*bpf_get_socket_cookie)(void *ctx) = 84 | (void *) BPF_FUNC_get_socket_cookie; 85 | static int (*bpf_setsockopt)(void *ctx, int level, int optname, void *optval, 86 | int optlen) = 87 | (void *) BPF_FUNC_setsockopt; 88 | static int (*bpf_getsockopt)(void *ctx, int level, int optname, void *optval, 89 | int optlen) = 90 | (void *) BPF_FUNC_getsockopt; 91 | static int (*bpf_sock_ops_cb_flags_set)(void *ctx, int flags) = 92 | (void *) BPF_FUNC_sock_ops_cb_flags_set; 93 | static int (*bpf_sk_redirect_map)(void *ctx, void *map, int key, int flags) = 94 | (void *) BPF_FUNC_sk_redirect_map; 95 | static int (*bpf_sk_redirect_hash)(void *ctx, void *map, void *key, int flags) = 96 | (void *) BPF_FUNC_sk_redirect_hash; 97 | static int (*bpf_sock_map_update)(void *map, void *key, void *value, 98 | unsigned long long flags) = 99 | (void *) BPF_FUNC_sock_map_update; 100 | static int (*bpf_sock_hash_update)(void *map, void *key, void *value, 101 | unsigned long long flags) = 102 | (void *) BPF_FUNC_sock_hash_update; 103 | static int (*bpf_perf_event_read_value)(void *map, unsigned long long flags, 104 | void *buf, unsigned int buf_size) = 105 | (void *) BPF_FUNC_perf_event_read_value; 106 | static int (*bpf_perf_prog_read_value)(void *ctx, void *buf, 107 | unsigned int buf_size) = 108 | (void *) BPF_FUNC_perf_prog_read_value; 109 | static int (*bpf_override_return)(void *ctx, unsigned long rc) = 110 | (void *) BPF_FUNC_override_return; 111 | static int (*bpf_msg_redirect_map)(void *ctx, void *map, int key, int flags) = 112 | (void *) BPF_FUNC_msg_redirect_map; 113 | static int (*bpf_msg_redirect_hash)(void *ctx, 114 | void *map, void *key, int flags) = 115 | (void *) BPF_FUNC_msg_redirect_hash; 116 | static int (*bpf_msg_apply_bytes)(void *ctx, int len) = 117 | (void *) BPF_FUNC_msg_apply_bytes; 118 | static int (*bpf_msg_cork_bytes)(void *ctx, int len) = 119 | (void *) BPF_FUNC_msg_cork_bytes; 120 | static int (*bpf_msg_pull_data)(void *ctx, int start, int end, int flags) = 121 | (void *) BPF_FUNC_msg_pull_data; 122 | static int (*bpf_msg_push_data)(void *ctx, int start, int end, int flags) = 123 | (void *) BPF_FUNC_msg_push_data; 124 | static int (*bpf_msg_pop_data)(void *ctx, int start, int cut, int flags) = 125 | (void *) BPF_FUNC_msg_pop_data; 126 | static int (*bpf_bind)(void *ctx, void *addr, int addr_len) = 127 | (void *) BPF_FUNC_bind; 128 | static int (*bpf_xdp_adjust_tail)(void *ctx, int offset) = 129 | (void *) BPF_FUNC_xdp_adjust_tail; 130 | static int (*bpf_skb_get_xfrm_state)(void *ctx, int index, void *state, 131 | int size, int flags) = 132 | (void *) BPF_FUNC_skb_get_xfrm_state; 133 | static int (*bpf_sk_select_reuseport)(void *ctx, void *map, void *key, __u32 flags) = 134 | (void *) BPF_FUNC_sk_select_reuseport; 135 | static int (*bpf_get_stack)(void *ctx, void *buf, int size, int flags) = 136 | (void *) BPF_FUNC_get_stack; 137 | static int (*bpf_fib_lookup)(void *ctx, struct bpf_fib_lookup *params, 138 | int plen, __u32 flags) = 139 | (void *) BPF_FUNC_fib_lookup; 140 | static int (*bpf_lwt_push_encap)(void *ctx, unsigned int type, void *hdr, 141 | unsigned int len) = 142 | (void *) BPF_FUNC_lwt_push_encap; 143 | static int (*bpf_lwt_seg6_store_bytes)(void *ctx, unsigned int offset, 144 | void *from, unsigned int len) = 145 | (void *) BPF_FUNC_lwt_seg6_store_bytes; 146 | static int (*bpf_lwt_seg6_action)(void *ctx, unsigned int action, void *param, 147 | unsigned int param_len) = 148 | (void *) BPF_FUNC_lwt_seg6_action; 149 | static int (*bpf_lwt_seg6_adjust_srh)(void *ctx, unsigned int offset, 150 | unsigned int len) = 151 | (void *) BPF_FUNC_lwt_seg6_adjust_srh; 152 | static int (*bpf_rc_repeat)(void *ctx) = 153 | (void *) BPF_FUNC_rc_repeat; 154 | static int (*bpf_rc_keydown)(void *ctx, unsigned int protocol, 155 | unsigned long long scancode, unsigned int toggle) = 156 | (void *) BPF_FUNC_rc_keydown; 157 | static unsigned long long (*bpf_get_current_cgroup_id)(void) = 158 | (void *) BPF_FUNC_get_current_cgroup_id; 159 | static void *(*bpf_get_local_storage)(void *map, unsigned long long flags) = 160 | (void *) BPF_FUNC_get_local_storage; 161 | static unsigned long long (*bpf_skb_cgroup_id)(void *ctx) = 162 | (void *) BPF_FUNC_skb_cgroup_id; 163 | static unsigned long long (*bpf_skb_ancestor_cgroup_id)(void *ctx, int level) = 164 | (void *) BPF_FUNC_skb_ancestor_cgroup_id; 165 | static struct bpf_sock *(*bpf_sk_lookup_tcp)(void *ctx, 166 | struct bpf_sock_tuple *tuple, 167 | int size, unsigned long long netns_id, 168 | unsigned long long flags) = 169 | (void *) BPF_FUNC_sk_lookup_tcp; 170 | static struct bpf_sock *(*bpf_sk_lookup_udp)(void *ctx, 171 | struct bpf_sock_tuple *tuple, 172 | int size, unsigned long long netns_id, 173 | unsigned long long flags) = 174 | (void *) BPF_FUNC_sk_lookup_udp; 175 | static int (*bpf_sk_release)(struct bpf_sock *sk) = 176 | (void *) BPF_FUNC_sk_release; 177 | static int (*bpf_skb_vlan_push)(void *ctx, __be16 vlan_proto, __u16 vlan_tci) = 178 | (void *) BPF_FUNC_skb_vlan_push; 179 | static int (*bpf_skb_vlan_pop)(void *ctx) = 180 | (void *) BPF_FUNC_skb_vlan_pop; 181 | static int (*bpf_rc_pointer_rel)(void *ctx, int rel_x, int rel_y) = 182 | (void *) BPF_FUNC_rc_pointer_rel; 183 | static void (*bpf_spin_lock)(struct bpf_spin_lock *lock) = 184 | (void *) BPF_FUNC_spin_lock; 185 | static void (*bpf_spin_unlock)(struct bpf_spin_lock *lock) = 186 | (void *) BPF_FUNC_spin_unlock; 187 | static struct bpf_sock *(*bpf_sk_fullsock)(struct bpf_sock *sk) = 188 | (void *) BPF_FUNC_sk_fullsock; 189 | static struct bpf_tcp_sock *(*bpf_tcp_sock)(struct bpf_sock *sk) = 190 | (void *) BPF_FUNC_tcp_sock; 191 | static struct bpf_sock *(*bpf_get_listener_sock)(struct bpf_sock *sk) = 192 | (void *) BPF_FUNC_get_listener_sock; 193 | static int (*bpf_skb_ecn_set_ce)(void *ctx) = 194 | (void *) BPF_FUNC_skb_ecn_set_ce; 195 | 196 | /* llvm builtin functions that eBPF C program may use to 197 | * emit BPF_LD_ABS and BPF_LD_IND instructions 198 | */ 199 | struct sk_buff; 200 | unsigned long long load_byte(void *skb, 201 | unsigned long long off) asm("llvm.bpf.load.byte"); 202 | unsigned long long load_half(void *skb, 203 | unsigned long long off) asm("llvm.bpf.load.half"); 204 | unsigned long long load_word(void *skb, 205 | unsigned long long off) asm("llvm.bpf.load.word"); 206 | 207 | /* a helper structure used by eBPF C program 208 | * to describe map attributes to elf_bpf loader 209 | */ 210 | struct bpf_map_def { 211 | unsigned int type; 212 | unsigned int key_size; 213 | unsigned int value_size; 214 | unsigned int max_entries; 215 | unsigned int map_flags; 216 | unsigned int inner_map_idx; 217 | unsigned int numa_node; 218 | }; 219 | 220 | #define BPF_ANNOTATE_KV_PAIR(name, type_key, type_val) \ 221 | struct ____btf_map_##name { \ 222 | type_key key; \ 223 | type_val value; \ 224 | }; \ 225 | struct ____btf_map_##name \ 226 | __attribute__ ((section(".maps." #name), used)) \ 227 | ____btf_map_##name = { } 228 | 229 | static int (*bpf_skb_load_bytes)(void *ctx, int off, void *to, int len) = 230 | (void *) BPF_FUNC_skb_load_bytes; 231 | static int (*bpf_skb_load_bytes_relative)(void *ctx, int off, void *to, int len, __u32 start_header) = 232 | (void *) BPF_FUNC_skb_load_bytes_relative; 233 | static int (*bpf_skb_store_bytes)(void *ctx, int off, void *from, int len, int flags) = 234 | (void *) BPF_FUNC_skb_store_bytes; 235 | static int (*bpf_l3_csum_replace)(void *ctx, int off, int from, int to, int flags) = 236 | (void *) BPF_FUNC_l3_csum_replace; 237 | static int (*bpf_l4_csum_replace)(void *ctx, int off, int from, int to, int flags) = 238 | (void *) BPF_FUNC_l4_csum_replace; 239 | static int (*bpf_csum_diff)(void *from, int from_size, void *to, int to_size, int seed) = 240 | (void *) BPF_FUNC_csum_diff; 241 | static int (*bpf_skb_under_cgroup)(void *ctx, void *map, int index) = 242 | (void *) BPF_FUNC_skb_under_cgroup; 243 | static int (*bpf_skb_change_head)(void *, int len, int flags) = 244 | (void *) BPF_FUNC_skb_change_head; 245 | static int (*bpf_skb_pull_data)(void *, int len) = 246 | (void *) BPF_FUNC_skb_pull_data; 247 | static unsigned int (*bpf_get_cgroup_classid)(void *ctx) = 248 | (void *) BPF_FUNC_get_cgroup_classid; 249 | static unsigned int (*bpf_get_route_realm)(void *ctx) = 250 | (void *) BPF_FUNC_get_route_realm; 251 | static int (*bpf_skb_change_proto)(void *ctx, __be16 proto, __u64 flags) = 252 | (void *) BPF_FUNC_skb_change_proto; 253 | static int (*bpf_skb_change_type)(void *ctx, __u32 type) = 254 | (void *) BPF_FUNC_skb_change_type; 255 | static unsigned int (*bpf_get_hash_recalc)(void *ctx) = 256 | (void *) BPF_FUNC_get_hash_recalc; 257 | static unsigned long long (*bpf_get_current_task)(void *ctx) = 258 | (void *) BPF_FUNC_get_current_task; 259 | static int (*bpf_skb_change_tail)(void *ctx, __u32 len, __u64 flags) = 260 | (void *) BPF_FUNC_skb_change_tail; 261 | static long long (*bpf_csum_update)(void *ctx, __u32 csum) = 262 | (void *) BPF_FUNC_csum_update; 263 | static void (*bpf_set_hash_invalid)(void *ctx) = 264 | (void *) BPF_FUNC_set_hash_invalid; 265 | static int (*bpf_get_numa_node_id)(void) = 266 | (void *) BPF_FUNC_get_numa_node_id; 267 | static int (*bpf_probe_read_str)(void *ctx, __u32 size, 268 | const void *unsafe_ptr) = 269 | (void *) BPF_FUNC_probe_read_str; 270 | static unsigned int (*bpf_get_socket_uid)(void *ctx) = 271 | (void *) BPF_FUNC_get_socket_uid; 272 | static unsigned int (*bpf_set_hash)(void *ctx, __u32 hash) = 273 | (void *) BPF_FUNC_set_hash; 274 | static int (*bpf_skb_adjust_room)(void *ctx, __s32 len_diff, __u32 mode, 275 | unsigned long long flags) = 276 | (void *) BPF_FUNC_skb_adjust_room; 277 | 278 | /* Scan the ARCH passed in from ARCH env variable (see Makefile) */ 279 | #if defined(__TARGET_ARCH_x86) 280 | #define bpf_target_x86 281 | #define bpf_target_defined 282 | #elif defined(__TARGET_ARCH_s930x) 283 | #define bpf_target_s930x 284 | #define bpf_target_defined 285 | #elif defined(__TARGET_ARCH_arm64) 286 | #define bpf_target_arm64 287 | #define bpf_target_defined 288 | #elif defined(__TARGET_ARCH_mips) 289 | #define bpf_target_mips 290 | #define bpf_target_defined 291 | #elif defined(__TARGET_ARCH_powerpc) 292 | #define bpf_target_powerpc 293 | #define bpf_target_defined 294 | #elif defined(__TARGET_ARCH_sparc) 295 | #define bpf_target_sparc 296 | #define bpf_target_defined 297 | #else 298 | #undef bpf_target_defined 299 | #endif 300 | 301 | /* Fall back to what the compiler says */ 302 | #ifndef bpf_target_defined 303 | #if defined(__x86_64__) 304 | #define bpf_target_x86 305 | #elif defined(__s390x__) 306 | #define bpf_target_s930x 307 | #elif defined(__aarch64__) 308 | #define bpf_target_arm64 309 | #elif defined(__mips__) 310 | #define bpf_target_mips 311 | #elif defined(__powerpc__) 312 | #define bpf_target_powerpc 313 | #elif defined(__sparc__) 314 | #define bpf_target_sparc 315 | #endif 316 | #endif 317 | 318 | #if defined(bpf_target_x86) 319 | 320 | #define PT_REGS_PARM1(x) ((x)->di) 321 | #define PT_REGS_PARM2(x) ((x)->si) 322 | #define PT_REGS_PARM3(x) ((x)->dx) 323 | #define PT_REGS_PARM4(x) ((x)->cx) 324 | #define PT_REGS_PARM5(x) ((x)->r8) 325 | #define PT_REGS_RET(x) ((x)->sp) 326 | #define PT_REGS_FP(x) ((x)->bp) 327 | #define PT_REGS_RC(x) ((x)->ax) 328 | #define PT_REGS_SP(x) ((x)->sp) 329 | #define PT_REGS_IP(x) ((x)->ip) 330 | 331 | #elif defined(bpf_target_s390x) 332 | 333 | #define PT_REGS_PARM1(x) ((x)->gprs[2]) 334 | #define PT_REGS_PARM2(x) ((x)->gprs[3]) 335 | #define PT_REGS_PARM3(x) ((x)->gprs[4]) 336 | #define PT_REGS_PARM4(x) ((x)->gprs[5]) 337 | #define PT_REGS_PARM5(x) ((x)->gprs[6]) 338 | #define PT_REGS_RET(x) ((x)->gprs[14]) 339 | #define PT_REGS_FP(x) ((x)->gprs[11]) /* Works only with CONFIG_FRAME_POINTER */ 340 | #define PT_REGS_RC(x) ((x)->gprs[2]) 341 | #define PT_REGS_SP(x) ((x)->gprs[15]) 342 | #define PT_REGS_IP(x) ((x)->psw.addr) 343 | 344 | #elif defined(bpf_target_arm64) 345 | 346 | #define PT_REGS_PARM1(x) ((x)->regs[0]) 347 | #define PT_REGS_PARM2(x) ((x)->regs[1]) 348 | #define PT_REGS_PARM3(x) ((x)->regs[2]) 349 | #define PT_REGS_PARM4(x) ((x)->regs[3]) 350 | #define PT_REGS_PARM5(x) ((x)->regs[4]) 351 | #define PT_REGS_RET(x) ((x)->regs[30]) 352 | #define PT_REGS_FP(x) ((x)->regs[29]) /* Works only with CONFIG_FRAME_POINTER */ 353 | #define PT_REGS_RC(x) ((x)->regs[0]) 354 | #define PT_REGS_SP(x) ((x)->sp) 355 | #define PT_REGS_IP(x) ((x)->pc) 356 | 357 | #elif defined(bpf_target_mips) 358 | 359 | #define PT_REGS_PARM1(x) ((x)->regs[4]) 360 | #define PT_REGS_PARM2(x) ((x)->regs[5]) 361 | #define PT_REGS_PARM3(x) ((x)->regs[6]) 362 | #define PT_REGS_PARM4(x) ((x)->regs[7]) 363 | #define PT_REGS_PARM5(x) ((x)->regs[8]) 364 | #define PT_REGS_RET(x) ((x)->regs[31]) 365 | #define PT_REGS_FP(x) ((x)->regs[30]) /* Works only with CONFIG_FRAME_POINTER */ 366 | #define PT_REGS_RC(x) ((x)->regs[1]) 367 | #define PT_REGS_SP(x) ((x)->regs[29]) 368 | #define PT_REGS_IP(x) ((x)->cp0_epc) 369 | 370 | #elif defined(bpf_target_powerpc) 371 | 372 | #define PT_REGS_PARM1(x) ((x)->gpr[3]) 373 | #define PT_REGS_PARM2(x) ((x)->gpr[4]) 374 | #define PT_REGS_PARM3(x) ((x)->gpr[5]) 375 | #define PT_REGS_PARM4(x) ((x)->gpr[6]) 376 | #define PT_REGS_PARM5(x) ((x)->gpr[7]) 377 | #define PT_REGS_RC(x) ((x)->gpr[3]) 378 | #define PT_REGS_SP(x) ((x)->sp) 379 | #define PT_REGS_IP(x) ((x)->nip) 380 | 381 | #elif defined(bpf_target_sparc) 382 | 383 | #define PT_REGS_PARM1(x) ((x)->u_regs[UREG_I0]) 384 | #define PT_REGS_PARM2(x) ((x)->u_regs[UREG_I1]) 385 | #define PT_REGS_PARM3(x) ((x)->u_regs[UREG_I2]) 386 | #define PT_REGS_PARM4(x) ((x)->u_regs[UREG_I3]) 387 | #define PT_REGS_PARM5(x) ((x)->u_regs[UREG_I4]) 388 | #define PT_REGS_RET(x) ((x)->u_regs[UREG_I7]) 389 | #define PT_REGS_RC(x) ((x)->u_regs[UREG_I0]) 390 | #define PT_REGS_SP(x) ((x)->u_regs[UREG_FP]) 391 | 392 | /* Should this also be a bpf_target check for the sparc case? */ 393 | #if defined(__arch64__) 394 | #define PT_REGS_IP(x) ((x)->tpc) 395 | #else 396 | #define PT_REGS_IP(x) ((x)->pc) 397 | #endif 398 | 399 | #endif 400 | 401 | #ifdef bpf_target_powerpc 402 | #define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = (ctx)->link; }) 403 | #define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP 404 | #elif bpf_target_sparc 405 | #define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = PT_REGS_RET(ctx); }) 406 | #define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP 407 | #else 408 | #define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ \ 409 | bpf_probe_read(&(ip), sizeof(ip), (void *)PT_REGS_RET(ctx)); }) 410 | #define BPF_KRETPROBE_READ_RET_IP(ip, ctx) ({ \ 411 | bpf_probe_read(&(ip), sizeof(ip), \ 412 | (void *)(PT_REGS_FP(ctx) + sizeof(ip))); }) 413 | #endif 414 | 415 | #endif 416 | -------------------------------------------------------------------------------- /headers/bpf_util.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* Copied from $(LINUX)/tools/testing/selftests/bpf/bpf_util.h */ 3 | #ifndef __BPF_UTIL__ 4 | #define __BPF_UTIL__ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | static inline unsigned int bpf_num_possible_cpus(void) 12 | { 13 | static const char *fcpu = "/sys/devices/system/cpu/possible"; 14 | unsigned int start, end, possible_cpus = 0; 15 | char buff[128]; 16 | FILE *fp; 17 | int n; 18 | 19 | fp = fopen(fcpu, "r"); 20 | if (!fp) { 21 | printf("Failed to open %s: '%s'!\n", fcpu, strerror(errno)); 22 | exit(1); 23 | } 24 | 25 | while (fgets(buff, sizeof(buff), fp)) { 26 | n = sscanf(buff, "%u-%u", &start, &end); 27 | if (n == 0) { 28 | printf("Failed to retrieve # possible CPUs!\n"); 29 | exit(1); 30 | } else if (n == 1) { 31 | end = start; 32 | } 33 | possible_cpus = start == 0 ? end + 1 : 0; 34 | break; 35 | } 36 | fclose(fp); 37 | 38 | return possible_cpus; 39 | } 40 | 41 | #define __bpf_percpu_val_align __attribute__((__aligned__(8))) 42 | 43 | #define BPF_DECLARE_PERCPU(type, name) \ 44 | struct { type v; /* padding */ } __bpf_percpu_val_align \ 45 | name[bpf_num_possible_cpus()] 46 | #define bpf_percpu(name, cpu) name[(cpu)].v 47 | 48 | #ifndef ARRAY_SIZE 49 | # define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 50 | #endif 51 | 52 | #ifndef sizeof_field 53 | #define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER)) 54 | #endif 55 | 56 | #ifndef offsetofend 57 | #define offsetofend(TYPE, MEMBER) \ 58 | (offsetof(TYPE, MEMBER) + sizeof_field(TYPE, MEMBER)) 59 | #endif 60 | 61 | #endif /* __BPF_UTIL__ */ 62 | -------------------------------------------------------------------------------- /headers/jhash.h: -------------------------------------------------------------------------------- 1 | #ifndef _LINUX_JHASH_H 2 | #define _LINUX_JHASH_H 3 | 4 | /* Copied from $(LINUX)/include/linux/jhash.h (kernel 4.18) */ 5 | 6 | /* jhash.h: Jenkins hash support. 7 | * 8 | * Copyright (C) 2006. Bob Jenkins (bob_jenkins@burtleburtle.net) 9 | * 10 | * http://burtleburtle.net/bob/hash/ 11 | * 12 | * These are the credits from Bob's sources: 13 | * 14 | * lookup3.c, by Bob Jenkins, May 2006, Public Domain. 15 | * 16 | * These are functions for producing 32-bit hashes for hash table lookup. 17 | * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() 18 | * are externally useful functions. Routines to test the hash are included 19 | * if SELF_TEST is defined. You can use this free for any purpose. It's in 20 | * the public domain. It has no warranty. 21 | * 22 | * Copyright (C) 2009-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) 23 | */ 24 | 25 | static inline __u32 rol32(__u32 word, unsigned int shift) 26 | { 27 | return (word << shift) | (word >> ((-shift) & 31)); 28 | } 29 | 30 | /* copy paste of jhash from kernel sources (include/linux/jhash.h) to make sure 31 | * LLVM can compile it into valid sequence of BPF instructions 32 | */ 33 | #define __jhash_mix(a, b, c) \ 34 | { \ 35 | a -= c; a ^= rol32(c, 4); c += b; \ 36 | b -= a; b ^= rol32(a, 6); a += c; \ 37 | c -= b; c ^= rol32(b, 8); b += a; \ 38 | a -= c; a ^= rol32(c, 16); c += b; \ 39 | b -= a; b ^= rol32(a, 19); a += c; \ 40 | c -= b; c ^= rol32(b, 4); b += a; \ 41 | } 42 | 43 | #define __jhash_final(a, b, c) \ 44 | { \ 45 | c ^= b; c -= rol32(b, 14); \ 46 | a ^= c; a -= rol32(c, 11); \ 47 | b ^= a; b -= rol32(a, 25); \ 48 | c ^= b; c -= rol32(b, 16); \ 49 | a ^= c; a -= rol32(c, 4); \ 50 | b ^= a; b -= rol32(a, 14); \ 51 | c ^= b; c -= rol32(b, 24); \ 52 | } 53 | 54 | #define JHASH_INITVAL 0xdeadbeef 55 | 56 | typedef unsigned int u32; 57 | 58 | /* jhash - hash an arbitrary key 59 | * @k: sequence of bytes as key 60 | * @length: the length of the key 61 | * @initval: the previous hash, or an arbitray value 62 | * 63 | * The generic version, hashes an arbitrary sequence of bytes. 64 | * No alignment or length assumptions are made about the input key. 65 | * 66 | * Returns the hash value of the key. The result depends on endianness. 67 | */ 68 | static inline u32 jhash(const void *key, u32 length, u32 initval) 69 | { 70 | u32 a, b, c; 71 | const unsigned char *k = key; 72 | 73 | /* Set up the internal state */ 74 | a = b = c = JHASH_INITVAL + length + initval; 75 | 76 | /* All but the last block: affect some 32 bits of (a,b,c) */ 77 | while (length > 12) { 78 | a += *(u32 *)(k); 79 | b += *(u32 *)(k + 4); 80 | c += *(u32 *)(k + 8); 81 | __jhash_mix(a, b, c); 82 | length -= 12; 83 | k += 12; 84 | } 85 | /* Last block: affect all 32 bits of (c) */ 86 | switch (length) { 87 | case 12: c += (u32)k[11]<<24; /* fall through */ 88 | case 11: c += (u32)k[10]<<16; /* fall through */ 89 | case 10: c += (u32)k[9]<<8; /* fall through */ 90 | case 9: c += k[8]; /* fall through */ 91 | case 8: b += (u32)k[7]<<24; /* fall through */ 92 | case 7: b += (u32)k[6]<<16; /* fall through */ 93 | case 6: b += (u32)k[5]<<8; /* fall through */ 94 | case 5: b += k[4]; /* fall through */ 95 | case 4: a += (u32)k[3]<<24; /* fall through */ 96 | case 3: a += (u32)k[2]<<16; /* fall through */ 97 | case 2: a += (u32)k[1]<<8; /* fall through */ 98 | case 1: a += k[0]; 99 | __jhash_final(a, b, c); 100 | case 0: /* Nothing left to add */ 101 | break; 102 | } 103 | 104 | return c; 105 | } 106 | 107 | /* jhash2 - hash an array of u32's 108 | * @k: the key which must be an array of u32's 109 | * @length: the number of u32's in the key 110 | * @initval: the previous hash, or an arbitray value 111 | * 112 | * Returns the hash value of the key. 113 | */ 114 | static inline u32 jhash2(const u32 *k, u32 length, u32 initval) 115 | { 116 | u32 a, b, c; 117 | 118 | /* Set up the internal state */ 119 | a = b = c = JHASH_INITVAL + (length<<2) + initval; 120 | 121 | /* Handle most of the key */ 122 | while (length > 3) { 123 | a += k[0]; 124 | b += k[1]; 125 | c += k[2]; 126 | __jhash_mix(a, b, c); 127 | length -= 3; 128 | k += 3; 129 | } 130 | 131 | /* Handle the last 3 u32's */ 132 | switch (length) { 133 | case 3: c += k[2]; /* fall through */ 134 | case 2: b += k[1]; /* fall through */ 135 | case 1: a += k[0]; 136 | __jhash_final(a, b, c); 137 | case 0: /* Nothing left to add */ 138 | break; 139 | } 140 | 141 | return c; 142 | } 143 | 144 | 145 | /* __jhash_nwords - hash exactly 3, 2 or 1 word(s) */ 146 | static inline u32 __jhash_nwords(u32 a, u32 b, u32 c, u32 initval) 147 | { 148 | a += initval; 149 | b += initval; 150 | c += initval; 151 | 152 | __jhash_final(a, b, c); 153 | 154 | return c; 155 | } 156 | 157 | static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval) 158 | { 159 | return __jhash_nwords(a, b, c, initval + JHASH_INITVAL + (3 << 2)); 160 | } 161 | 162 | static inline u32 jhash_2words(u32 a, u32 b, u32 initval) 163 | { 164 | return __jhash_nwords(a, b, 0, initval + JHASH_INITVAL + (2 << 2)); 165 | } 166 | 167 | static inline u32 jhash_1word(u32 a, u32 initval) 168 | { 169 | return __jhash_nwords(a, 0, 0, initval + JHASH_INITVAL + (1 << 2)); 170 | } 171 | 172 | #endif /* _LINUX_JHASH_H */ 173 | -------------------------------------------------------------------------------- /headers/linux/err.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | 3 | #ifndef __LINUX_ERR_H 4 | #define __LINUX_ERR_H 5 | 6 | #include 7 | #include 8 | 9 | #define MAX_ERRNO 4095 10 | 11 | #define IS_ERR_VALUE(x) ((x) >= (unsigned long)-MAX_ERRNO) 12 | 13 | static inline void * ERR_PTR(long error_) 14 | { 15 | return (void *) error_; 16 | } 17 | 18 | static inline long PTR_ERR(const void *ptr) 19 | { 20 | return (long) ptr; 21 | } 22 | 23 | static inline bool IS_ERR(const void *ptr) 24 | { 25 | return IS_ERR_VALUE((unsigned long)ptr); 26 | } 27 | 28 | static inline bool IS_ERR_OR_NULL(const void *ptr) 29 | { 30 | return (!ptr) || IS_ERR_VALUE((unsigned long)ptr); 31 | } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /headers/linux/if_link.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ 2 | #ifndef _UAPI_LINUX_IF_LINK_H 3 | #define _UAPI_LINUX_IF_LINK_H 4 | 5 | #include 6 | #include 7 | 8 | /* This struct should be in sync with struct rtnl_link_stats64 */ 9 | struct rtnl_link_stats { 10 | __u32 rx_packets; /* total packets received */ 11 | __u32 tx_packets; /* total packets transmitted */ 12 | __u32 rx_bytes; /* total bytes received */ 13 | __u32 tx_bytes; /* total bytes transmitted */ 14 | __u32 rx_errors; /* bad packets received */ 15 | __u32 tx_errors; /* packet transmit problems */ 16 | __u32 rx_dropped; /* no space in linux buffers */ 17 | __u32 tx_dropped; /* no space available in linux */ 18 | __u32 multicast; /* multicast packets received */ 19 | __u32 collisions; 20 | 21 | /* detailed rx_errors: */ 22 | __u32 rx_length_errors; 23 | __u32 rx_over_errors; /* receiver ring buff overflow */ 24 | __u32 rx_crc_errors; /* recved pkt with crc error */ 25 | __u32 rx_frame_errors; /* recv'd frame alignment error */ 26 | __u32 rx_fifo_errors; /* recv'r fifo overrun */ 27 | __u32 rx_missed_errors; /* receiver missed packet */ 28 | 29 | /* detailed tx_errors */ 30 | __u32 tx_aborted_errors; 31 | __u32 tx_carrier_errors; 32 | __u32 tx_fifo_errors; 33 | __u32 tx_heartbeat_errors; 34 | __u32 tx_window_errors; 35 | 36 | /* for cslip etc */ 37 | __u32 rx_compressed; 38 | __u32 tx_compressed; 39 | 40 | __u32 rx_nohandler; /* dropped, no handler found */ 41 | }; 42 | 43 | /* The main device statistics structure */ 44 | struct rtnl_link_stats64 { 45 | __u64 rx_packets; /* total packets received */ 46 | __u64 tx_packets; /* total packets transmitted */ 47 | __u64 rx_bytes; /* total bytes received */ 48 | __u64 tx_bytes; /* total bytes transmitted */ 49 | __u64 rx_errors; /* bad packets received */ 50 | __u64 tx_errors; /* packet transmit problems */ 51 | __u64 rx_dropped; /* no space in linux buffers */ 52 | __u64 tx_dropped; /* no space available in linux */ 53 | __u64 multicast; /* multicast packets received */ 54 | __u64 collisions; 55 | 56 | /* detailed rx_errors: */ 57 | __u64 rx_length_errors; 58 | __u64 rx_over_errors; /* receiver ring buff overflow */ 59 | __u64 rx_crc_errors; /* recved pkt with crc error */ 60 | __u64 rx_frame_errors; /* recv'd frame alignment error */ 61 | __u64 rx_fifo_errors; /* recv'r fifo overrun */ 62 | __u64 rx_missed_errors; /* receiver missed packet */ 63 | 64 | /* detailed tx_errors */ 65 | __u64 tx_aborted_errors; 66 | __u64 tx_carrier_errors; 67 | __u64 tx_fifo_errors; 68 | __u64 tx_heartbeat_errors; 69 | __u64 tx_window_errors; 70 | 71 | /* for cslip etc */ 72 | __u64 rx_compressed; 73 | __u64 tx_compressed; 74 | 75 | __u64 rx_nohandler; /* dropped, no handler found */ 76 | }; 77 | 78 | /* The struct should be in sync with struct ifmap */ 79 | struct rtnl_link_ifmap { 80 | __u64 mem_start; 81 | __u64 mem_end; 82 | __u64 base_addr; 83 | __u16 irq; 84 | __u8 dma; 85 | __u8 port; 86 | }; 87 | 88 | /* 89 | * IFLA_AF_SPEC 90 | * Contains nested attributes for address family specific attributes. 91 | * Each address family may create a attribute with the address family 92 | * number as type and create its own attribute structure in it. 93 | * 94 | * Example: 95 | * [IFLA_AF_SPEC] = { 96 | * [AF_INET] = { 97 | * [IFLA_INET_CONF] = ..., 98 | * }, 99 | * [AF_INET6] = { 100 | * [IFLA_INET6_FLAGS] = ..., 101 | * [IFLA_INET6_CONF] = ..., 102 | * } 103 | * } 104 | */ 105 | 106 | enum { 107 | IFLA_UNSPEC, 108 | IFLA_ADDRESS, 109 | IFLA_BROADCAST, 110 | IFLA_IFNAME, 111 | IFLA_MTU, 112 | IFLA_LINK, 113 | IFLA_QDISC, 114 | IFLA_STATS, 115 | IFLA_COST, 116 | #define IFLA_COST IFLA_COST 117 | IFLA_PRIORITY, 118 | #define IFLA_PRIORITY IFLA_PRIORITY 119 | IFLA_MASTER, 120 | #define IFLA_MASTER IFLA_MASTER 121 | IFLA_WIRELESS, /* Wireless Extension event - see wireless.h */ 122 | #define IFLA_WIRELESS IFLA_WIRELESS 123 | IFLA_PROTINFO, /* Protocol specific information for a link */ 124 | #define IFLA_PROTINFO IFLA_PROTINFO 125 | IFLA_TXQLEN, 126 | #define IFLA_TXQLEN IFLA_TXQLEN 127 | IFLA_MAP, 128 | #define IFLA_MAP IFLA_MAP 129 | IFLA_WEIGHT, 130 | #define IFLA_WEIGHT IFLA_WEIGHT 131 | IFLA_OPERSTATE, 132 | IFLA_LINKMODE, 133 | IFLA_LINKINFO, 134 | #define IFLA_LINKINFO IFLA_LINKINFO 135 | IFLA_NET_NS_PID, 136 | IFLA_IFALIAS, 137 | IFLA_NUM_VF, /* Number of VFs if device is SR-IOV PF */ 138 | IFLA_VFINFO_LIST, 139 | IFLA_STATS64, 140 | IFLA_VF_PORTS, 141 | IFLA_PORT_SELF, 142 | IFLA_AF_SPEC, 143 | IFLA_GROUP, /* Group the device belongs to */ 144 | IFLA_NET_NS_FD, 145 | IFLA_EXT_MASK, /* Extended info mask, VFs, etc */ 146 | IFLA_PROMISCUITY, /* Promiscuity count: > 0 means acts PROMISC */ 147 | #define IFLA_PROMISCUITY IFLA_PROMISCUITY 148 | IFLA_NUM_TX_QUEUES, 149 | IFLA_NUM_RX_QUEUES, 150 | IFLA_CARRIER, 151 | IFLA_PHYS_PORT_ID, 152 | IFLA_CARRIER_CHANGES, 153 | IFLA_PHYS_SWITCH_ID, 154 | IFLA_LINK_NETNSID, 155 | IFLA_PHYS_PORT_NAME, 156 | IFLA_PROTO_DOWN, 157 | IFLA_GSO_MAX_SEGS, 158 | IFLA_GSO_MAX_SIZE, 159 | IFLA_PAD, 160 | IFLA_XDP, 161 | IFLA_EVENT, 162 | IFLA_NEW_NETNSID, 163 | IFLA_IF_NETNSID, 164 | IFLA_TARGET_NETNSID = IFLA_IF_NETNSID, /* new alias */ 165 | IFLA_CARRIER_UP_COUNT, 166 | IFLA_CARRIER_DOWN_COUNT, 167 | IFLA_NEW_IFINDEX, 168 | IFLA_MIN_MTU, 169 | IFLA_MAX_MTU, 170 | __IFLA_MAX 171 | }; 172 | 173 | 174 | #define IFLA_MAX (__IFLA_MAX - 1) 175 | 176 | /* backwards compatibility for userspace */ 177 | #ifndef __KERNEL__ 178 | #define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) 179 | #define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg)) 180 | #endif 181 | 182 | enum { 183 | IFLA_INET_UNSPEC, 184 | IFLA_INET_CONF, 185 | __IFLA_INET_MAX, 186 | }; 187 | 188 | #define IFLA_INET_MAX (__IFLA_INET_MAX - 1) 189 | 190 | /* ifi_flags. 191 | 192 | IFF_* flags. 193 | 194 | The only change is: 195 | IFF_LOOPBACK, IFF_BROADCAST and IFF_POINTOPOINT are 196 | more not changeable by user. They describe link media 197 | characteristics and set by device driver. 198 | 199 | Comments: 200 | - Combination IFF_BROADCAST|IFF_POINTOPOINT is invalid 201 | - If neither of these three flags are set; 202 | the interface is NBMA. 203 | 204 | - IFF_MULTICAST does not mean anything special: 205 | multicasts can be used on all not-NBMA links. 206 | IFF_MULTICAST means that this media uses special encapsulation 207 | for multicast frames. Apparently, all IFF_POINTOPOINT and 208 | IFF_BROADCAST devices are able to use multicasts too. 209 | */ 210 | 211 | /* IFLA_LINK. 212 | For usual devices it is equal ifi_index. 213 | If it is a "virtual interface" (f.e. tunnel), ifi_link 214 | can point to real physical interface (f.e. for bandwidth calculations), 215 | or maybe 0, what means, that real media is unknown (usual 216 | for IPIP tunnels, when route to endpoint is allowed to change) 217 | */ 218 | 219 | /* Subtype attributes for IFLA_PROTINFO */ 220 | enum { 221 | IFLA_INET6_UNSPEC, 222 | IFLA_INET6_FLAGS, /* link flags */ 223 | IFLA_INET6_CONF, /* sysctl parameters */ 224 | IFLA_INET6_STATS, /* statistics */ 225 | IFLA_INET6_MCAST, /* MC things. What of them? */ 226 | IFLA_INET6_CACHEINFO, /* time values and max reasm size */ 227 | IFLA_INET6_ICMP6STATS, /* statistics (icmpv6) */ 228 | IFLA_INET6_TOKEN, /* device token */ 229 | IFLA_INET6_ADDR_GEN_MODE, /* implicit address generator mode */ 230 | __IFLA_INET6_MAX 231 | }; 232 | 233 | #define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1) 234 | 235 | enum in6_addr_gen_mode { 236 | IN6_ADDR_GEN_MODE_EUI64, 237 | IN6_ADDR_GEN_MODE_NONE, 238 | IN6_ADDR_GEN_MODE_STABLE_PRIVACY, 239 | IN6_ADDR_GEN_MODE_RANDOM, 240 | }; 241 | 242 | /* Bridge section */ 243 | 244 | enum { 245 | IFLA_BR_UNSPEC, 246 | IFLA_BR_FORWARD_DELAY, 247 | IFLA_BR_HELLO_TIME, 248 | IFLA_BR_MAX_AGE, 249 | IFLA_BR_AGEING_TIME, 250 | IFLA_BR_STP_STATE, 251 | IFLA_BR_PRIORITY, 252 | IFLA_BR_VLAN_FILTERING, 253 | IFLA_BR_VLAN_PROTOCOL, 254 | IFLA_BR_GROUP_FWD_MASK, 255 | IFLA_BR_ROOT_ID, 256 | IFLA_BR_BRIDGE_ID, 257 | IFLA_BR_ROOT_PORT, 258 | IFLA_BR_ROOT_PATH_COST, 259 | IFLA_BR_TOPOLOGY_CHANGE, 260 | IFLA_BR_TOPOLOGY_CHANGE_DETECTED, 261 | IFLA_BR_HELLO_TIMER, 262 | IFLA_BR_TCN_TIMER, 263 | IFLA_BR_TOPOLOGY_CHANGE_TIMER, 264 | IFLA_BR_GC_TIMER, 265 | IFLA_BR_GROUP_ADDR, 266 | IFLA_BR_FDB_FLUSH, 267 | IFLA_BR_MCAST_ROUTER, 268 | IFLA_BR_MCAST_SNOOPING, 269 | IFLA_BR_MCAST_QUERY_USE_IFADDR, 270 | IFLA_BR_MCAST_QUERIER, 271 | IFLA_BR_MCAST_HASH_ELASTICITY, 272 | IFLA_BR_MCAST_HASH_MAX, 273 | IFLA_BR_MCAST_LAST_MEMBER_CNT, 274 | IFLA_BR_MCAST_STARTUP_QUERY_CNT, 275 | IFLA_BR_MCAST_LAST_MEMBER_INTVL, 276 | IFLA_BR_MCAST_MEMBERSHIP_INTVL, 277 | IFLA_BR_MCAST_QUERIER_INTVL, 278 | IFLA_BR_MCAST_QUERY_INTVL, 279 | IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, 280 | IFLA_BR_MCAST_STARTUP_QUERY_INTVL, 281 | IFLA_BR_NF_CALL_IPTABLES, 282 | IFLA_BR_NF_CALL_IP6TABLES, 283 | IFLA_BR_NF_CALL_ARPTABLES, 284 | IFLA_BR_VLAN_DEFAULT_PVID, 285 | IFLA_BR_PAD, 286 | IFLA_BR_VLAN_STATS_ENABLED, 287 | IFLA_BR_MCAST_STATS_ENABLED, 288 | IFLA_BR_MCAST_IGMP_VERSION, 289 | IFLA_BR_MCAST_MLD_VERSION, 290 | IFLA_BR_VLAN_STATS_PER_PORT, 291 | IFLA_BR_MULTI_BOOLOPT, 292 | __IFLA_BR_MAX, 293 | }; 294 | 295 | #define IFLA_BR_MAX (__IFLA_BR_MAX - 1) 296 | 297 | struct ifla_bridge_id { 298 | __u8 prio[2]; 299 | __u8 addr[6]; /* ETH_ALEN */ 300 | }; 301 | 302 | enum { 303 | BRIDGE_MODE_UNSPEC, 304 | BRIDGE_MODE_HAIRPIN, 305 | }; 306 | 307 | enum { 308 | IFLA_BRPORT_UNSPEC, 309 | IFLA_BRPORT_STATE, /* Spanning tree state */ 310 | IFLA_BRPORT_PRIORITY, /* " priority */ 311 | IFLA_BRPORT_COST, /* " cost */ 312 | IFLA_BRPORT_MODE, /* mode (hairpin) */ 313 | IFLA_BRPORT_GUARD, /* bpdu guard */ 314 | IFLA_BRPORT_PROTECT, /* root port protection */ 315 | IFLA_BRPORT_FAST_LEAVE, /* multicast fast leave */ 316 | IFLA_BRPORT_LEARNING, /* mac learning */ 317 | IFLA_BRPORT_UNICAST_FLOOD, /* flood unicast traffic */ 318 | IFLA_BRPORT_PROXYARP, /* proxy ARP */ 319 | IFLA_BRPORT_LEARNING_SYNC, /* mac learning sync from device */ 320 | IFLA_BRPORT_PROXYARP_WIFI, /* proxy ARP for Wi-Fi */ 321 | IFLA_BRPORT_ROOT_ID, /* designated root */ 322 | IFLA_BRPORT_BRIDGE_ID, /* designated bridge */ 323 | IFLA_BRPORT_DESIGNATED_PORT, 324 | IFLA_BRPORT_DESIGNATED_COST, 325 | IFLA_BRPORT_ID, 326 | IFLA_BRPORT_NO, 327 | IFLA_BRPORT_TOPOLOGY_CHANGE_ACK, 328 | IFLA_BRPORT_CONFIG_PENDING, 329 | IFLA_BRPORT_MESSAGE_AGE_TIMER, 330 | IFLA_BRPORT_FORWARD_DELAY_TIMER, 331 | IFLA_BRPORT_HOLD_TIMER, 332 | IFLA_BRPORT_FLUSH, 333 | IFLA_BRPORT_MULTICAST_ROUTER, 334 | IFLA_BRPORT_PAD, 335 | IFLA_BRPORT_MCAST_FLOOD, 336 | IFLA_BRPORT_MCAST_TO_UCAST, 337 | IFLA_BRPORT_VLAN_TUNNEL, 338 | IFLA_BRPORT_BCAST_FLOOD, 339 | IFLA_BRPORT_GROUP_FWD_MASK, 340 | IFLA_BRPORT_NEIGH_SUPPRESS, 341 | IFLA_BRPORT_ISOLATED, 342 | IFLA_BRPORT_BACKUP_PORT, 343 | __IFLA_BRPORT_MAX 344 | }; 345 | #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) 346 | 347 | struct ifla_cacheinfo { 348 | __u32 max_reasm_len; 349 | __u32 tstamp; /* ipv6InterfaceTable updated timestamp */ 350 | __u32 reachable_time; 351 | __u32 retrans_time; 352 | }; 353 | 354 | enum { 355 | IFLA_INFO_UNSPEC, 356 | IFLA_INFO_KIND, 357 | IFLA_INFO_DATA, 358 | IFLA_INFO_XSTATS, 359 | IFLA_INFO_SLAVE_KIND, 360 | IFLA_INFO_SLAVE_DATA, 361 | __IFLA_INFO_MAX, 362 | }; 363 | 364 | #define IFLA_INFO_MAX (__IFLA_INFO_MAX - 1) 365 | 366 | /* VLAN section */ 367 | 368 | enum { 369 | IFLA_VLAN_UNSPEC, 370 | IFLA_VLAN_ID, 371 | IFLA_VLAN_FLAGS, 372 | IFLA_VLAN_EGRESS_QOS, 373 | IFLA_VLAN_INGRESS_QOS, 374 | IFLA_VLAN_PROTOCOL, 375 | __IFLA_VLAN_MAX, 376 | }; 377 | 378 | #define IFLA_VLAN_MAX (__IFLA_VLAN_MAX - 1) 379 | 380 | struct ifla_vlan_flags { 381 | __u32 flags; 382 | __u32 mask; 383 | }; 384 | 385 | enum { 386 | IFLA_VLAN_QOS_UNSPEC, 387 | IFLA_VLAN_QOS_MAPPING, 388 | __IFLA_VLAN_QOS_MAX 389 | }; 390 | 391 | #define IFLA_VLAN_QOS_MAX (__IFLA_VLAN_QOS_MAX - 1) 392 | 393 | struct ifla_vlan_qos_mapping { 394 | __u32 from; 395 | __u32 to; 396 | }; 397 | 398 | /* MACVLAN section */ 399 | enum { 400 | IFLA_MACVLAN_UNSPEC, 401 | IFLA_MACVLAN_MODE, 402 | IFLA_MACVLAN_FLAGS, 403 | IFLA_MACVLAN_MACADDR_MODE, 404 | IFLA_MACVLAN_MACADDR, 405 | IFLA_MACVLAN_MACADDR_DATA, 406 | IFLA_MACVLAN_MACADDR_COUNT, 407 | __IFLA_MACVLAN_MAX, 408 | }; 409 | 410 | #define IFLA_MACVLAN_MAX (__IFLA_MACVLAN_MAX - 1) 411 | 412 | enum macvlan_mode { 413 | MACVLAN_MODE_PRIVATE = 1, /* don't talk to other macvlans */ 414 | MACVLAN_MODE_VEPA = 2, /* talk to other ports through ext bridge */ 415 | MACVLAN_MODE_BRIDGE = 4, /* talk to bridge ports directly */ 416 | MACVLAN_MODE_PASSTHRU = 8,/* take over the underlying device */ 417 | MACVLAN_MODE_SOURCE = 16,/* use source MAC address list to assign */ 418 | }; 419 | 420 | enum macvlan_macaddr_mode { 421 | MACVLAN_MACADDR_ADD, 422 | MACVLAN_MACADDR_DEL, 423 | MACVLAN_MACADDR_FLUSH, 424 | MACVLAN_MACADDR_SET, 425 | }; 426 | 427 | #define MACVLAN_FLAG_NOPROMISC 1 428 | 429 | /* VRF section */ 430 | enum { 431 | IFLA_VRF_UNSPEC, 432 | IFLA_VRF_TABLE, 433 | __IFLA_VRF_MAX 434 | }; 435 | 436 | #define IFLA_VRF_MAX (__IFLA_VRF_MAX - 1) 437 | 438 | enum { 439 | IFLA_VRF_PORT_UNSPEC, 440 | IFLA_VRF_PORT_TABLE, 441 | __IFLA_VRF_PORT_MAX 442 | }; 443 | 444 | #define IFLA_VRF_PORT_MAX (__IFLA_VRF_PORT_MAX - 1) 445 | 446 | /* MACSEC section */ 447 | enum { 448 | IFLA_MACSEC_UNSPEC, 449 | IFLA_MACSEC_SCI, 450 | IFLA_MACSEC_PORT, 451 | IFLA_MACSEC_ICV_LEN, 452 | IFLA_MACSEC_CIPHER_SUITE, 453 | IFLA_MACSEC_WINDOW, 454 | IFLA_MACSEC_ENCODING_SA, 455 | IFLA_MACSEC_ENCRYPT, 456 | IFLA_MACSEC_PROTECT, 457 | IFLA_MACSEC_INC_SCI, 458 | IFLA_MACSEC_ES, 459 | IFLA_MACSEC_SCB, 460 | IFLA_MACSEC_REPLAY_PROTECT, 461 | IFLA_MACSEC_VALIDATION, 462 | IFLA_MACSEC_PAD, 463 | __IFLA_MACSEC_MAX, 464 | }; 465 | 466 | #define IFLA_MACSEC_MAX (__IFLA_MACSEC_MAX - 1) 467 | 468 | /* XFRM section */ 469 | enum { 470 | IFLA_XFRM_UNSPEC, 471 | IFLA_XFRM_LINK, 472 | IFLA_XFRM_IF_ID, 473 | __IFLA_XFRM_MAX 474 | }; 475 | 476 | #define IFLA_XFRM_MAX (__IFLA_XFRM_MAX - 1) 477 | 478 | enum macsec_validation_type { 479 | MACSEC_VALIDATE_DISABLED = 0, 480 | MACSEC_VALIDATE_CHECK = 1, 481 | MACSEC_VALIDATE_STRICT = 2, 482 | __MACSEC_VALIDATE_END, 483 | MACSEC_VALIDATE_MAX = __MACSEC_VALIDATE_END - 1, 484 | }; 485 | 486 | /* IPVLAN section */ 487 | enum { 488 | IFLA_IPVLAN_UNSPEC, 489 | IFLA_IPVLAN_MODE, 490 | IFLA_IPVLAN_FLAGS, 491 | __IFLA_IPVLAN_MAX 492 | }; 493 | 494 | #define IFLA_IPVLAN_MAX (__IFLA_IPVLAN_MAX - 1) 495 | 496 | enum ipvlan_mode { 497 | IPVLAN_MODE_L2 = 0, 498 | IPVLAN_MODE_L3, 499 | IPVLAN_MODE_L3S, 500 | IPVLAN_MODE_MAX 501 | }; 502 | 503 | #define IPVLAN_F_PRIVATE 0x01 504 | #define IPVLAN_F_VEPA 0x02 505 | 506 | /* VXLAN section */ 507 | enum { 508 | IFLA_VXLAN_UNSPEC, 509 | IFLA_VXLAN_ID, 510 | IFLA_VXLAN_GROUP, /* group or remote address */ 511 | IFLA_VXLAN_LINK, 512 | IFLA_VXLAN_LOCAL, 513 | IFLA_VXLAN_TTL, 514 | IFLA_VXLAN_TOS, 515 | IFLA_VXLAN_LEARNING, 516 | IFLA_VXLAN_AGEING, 517 | IFLA_VXLAN_LIMIT, 518 | IFLA_VXLAN_PORT_RANGE, /* source port */ 519 | IFLA_VXLAN_PROXY, 520 | IFLA_VXLAN_RSC, 521 | IFLA_VXLAN_L2MISS, 522 | IFLA_VXLAN_L3MISS, 523 | IFLA_VXLAN_PORT, /* destination port */ 524 | IFLA_VXLAN_GROUP6, 525 | IFLA_VXLAN_LOCAL6, 526 | IFLA_VXLAN_UDP_CSUM, 527 | IFLA_VXLAN_UDP_ZERO_CSUM6_TX, 528 | IFLA_VXLAN_UDP_ZERO_CSUM6_RX, 529 | IFLA_VXLAN_REMCSUM_TX, 530 | IFLA_VXLAN_REMCSUM_RX, 531 | IFLA_VXLAN_GBP, 532 | IFLA_VXLAN_REMCSUM_NOPARTIAL, 533 | IFLA_VXLAN_COLLECT_METADATA, 534 | IFLA_VXLAN_LABEL, 535 | IFLA_VXLAN_GPE, 536 | IFLA_VXLAN_TTL_INHERIT, 537 | IFLA_VXLAN_DF, 538 | __IFLA_VXLAN_MAX 539 | }; 540 | #define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1) 541 | 542 | struct ifla_vxlan_port_range { 543 | __be16 low; 544 | __be16 high; 545 | }; 546 | 547 | enum ifla_vxlan_df { 548 | VXLAN_DF_UNSET = 0, 549 | VXLAN_DF_SET, 550 | VXLAN_DF_INHERIT, 551 | __VXLAN_DF_END, 552 | VXLAN_DF_MAX = __VXLAN_DF_END - 1, 553 | }; 554 | 555 | /* GENEVE section */ 556 | enum { 557 | IFLA_GENEVE_UNSPEC, 558 | IFLA_GENEVE_ID, 559 | IFLA_GENEVE_REMOTE, 560 | IFLA_GENEVE_TTL, 561 | IFLA_GENEVE_TOS, 562 | IFLA_GENEVE_PORT, /* destination port */ 563 | IFLA_GENEVE_COLLECT_METADATA, 564 | IFLA_GENEVE_REMOTE6, 565 | IFLA_GENEVE_UDP_CSUM, 566 | IFLA_GENEVE_UDP_ZERO_CSUM6_TX, 567 | IFLA_GENEVE_UDP_ZERO_CSUM6_RX, 568 | IFLA_GENEVE_LABEL, 569 | IFLA_GENEVE_TTL_INHERIT, 570 | IFLA_GENEVE_DF, 571 | __IFLA_GENEVE_MAX 572 | }; 573 | #define IFLA_GENEVE_MAX (__IFLA_GENEVE_MAX - 1) 574 | 575 | enum ifla_geneve_df { 576 | GENEVE_DF_UNSET = 0, 577 | GENEVE_DF_SET, 578 | GENEVE_DF_INHERIT, 579 | __GENEVE_DF_END, 580 | GENEVE_DF_MAX = __GENEVE_DF_END - 1, 581 | }; 582 | 583 | /* PPP section */ 584 | enum { 585 | IFLA_PPP_UNSPEC, 586 | IFLA_PPP_DEV_FD, 587 | __IFLA_PPP_MAX 588 | }; 589 | #define IFLA_PPP_MAX (__IFLA_PPP_MAX - 1) 590 | 591 | /* GTP section */ 592 | 593 | enum ifla_gtp_role { 594 | GTP_ROLE_GGSN = 0, 595 | GTP_ROLE_SGSN, 596 | }; 597 | 598 | enum { 599 | IFLA_GTP_UNSPEC, 600 | IFLA_GTP_FD0, 601 | IFLA_GTP_FD1, 602 | IFLA_GTP_PDP_HASHSIZE, 603 | IFLA_GTP_ROLE, 604 | __IFLA_GTP_MAX, 605 | }; 606 | #define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1) 607 | 608 | /* Bonding section */ 609 | 610 | enum { 611 | IFLA_BOND_UNSPEC, 612 | IFLA_BOND_MODE, 613 | IFLA_BOND_ACTIVE_SLAVE, 614 | IFLA_BOND_MIIMON, 615 | IFLA_BOND_UPDELAY, 616 | IFLA_BOND_DOWNDELAY, 617 | IFLA_BOND_USE_CARRIER, 618 | IFLA_BOND_ARP_INTERVAL, 619 | IFLA_BOND_ARP_IP_TARGET, 620 | IFLA_BOND_ARP_VALIDATE, 621 | IFLA_BOND_ARP_ALL_TARGETS, 622 | IFLA_BOND_PRIMARY, 623 | IFLA_BOND_PRIMARY_RESELECT, 624 | IFLA_BOND_FAIL_OVER_MAC, 625 | IFLA_BOND_XMIT_HASH_POLICY, 626 | IFLA_BOND_RESEND_IGMP, 627 | IFLA_BOND_NUM_PEER_NOTIF, 628 | IFLA_BOND_ALL_SLAVES_ACTIVE, 629 | IFLA_BOND_MIN_LINKS, 630 | IFLA_BOND_LP_INTERVAL, 631 | IFLA_BOND_PACKETS_PER_SLAVE, 632 | IFLA_BOND_AD_LACP_RATE, 633 | IFLA_BOND_AD_SELECT, 634 | IFLA_BOND_AD_INFO, 635 | IFLA_BOND_AD_ACTOR_SYS_PRIO, 636 | IFLA_BOND_AD_USER_PORT_KEY, 637 | IFLA_BOND_AD_ACTOR_SYSTEM, 638 | IFLA_BOND_TLB_DYNAMIC_LB, 639 | __IFLA_BOND_MAX, 640 | }; 641 | 642 | #define IFLA_BOND_MAX (__IFLA_BOND_MAX - 1) 643 | 644 | enum { 645 | IFLA_BOND_AD_INFO_UNSPEC, 646 | IFLA_BOND_AD_INFO_AGGREGATOR, 647 | IFLA_BOND_AD_INFO_NUM_PORTS, 648 | IFLA_BOND_AD_INFO_ACTOR_KEY, 649 | IFLA_BOND_AD_INFO_PARTNER_KEY, 650 | IFLA_BOND_AD_INFO_PARTNER_MAC, 651 | __IFLA_BOND_AD_INFO_MAX, 652 | }; 653 | 654 | #define IFLA_BOND_AD_INFO_MAX (__IFLA_BOND_AD_INFO_MAX - 1) 655 | 656 | enum { 657 | IFLA_BOND_SLAVE_UNSPEC, 658 | IFLA_BOND_SLAVE_STATE, 659 | IFLA_BOND_SLAVE_MII_STATUS, 660 | IFLA_BOND_SLAVE_LINK_FAILURE_COUNT, 661 | IFLA_BOND_SLAVE_PERM_HWADDR, 662 | IFLA_BOND_SLAVE_QUEUE_ID, 663 | IFLA_BOND_SLAVE_AD_AGGREGATOR_ID, 664 | IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE, 665 | IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE, 666 | __IFLA_BOND_SLAVE_MAX, 667 | }; 668 | 669 | #define IFLA_BOND_SLAVE_MAX (__IFLA_BOND_SLAVE_MAX - 1) 670 | 671 | /* SR-IOV virtual function management section */ 672 | 673 | enum { 674 | IFLA_VF_INFO_UNSPEC, 675 | IFLA_VF_INFO, 676 | __IFLA_VF_INFO_MAX, 677 | }; 678 | 679 | #define IFLA_VF_INFO_MAX (__IFLA_VF_INFO_MAX - 1) 680 | 681 | enum { 682 | IFLA_VF_UNSPEC, 683 | IFLA_VF_MAC, /* Hardware queue specific attributes */ 684 | IFLA_VF_VLAN, /* VLAN ID and QoS */ 685 | IFLA_VF_TX_RATE, /* Max TX Bandwidth Allocation */ 686 | IFLA_VF_SPOOFCHK, /* Spoof Checking on/off switch */ 687 | IFLA_VF_LINK_STATE, /* link state enable/disable/auto switch */ 688 | IFLA_VF_RATE, /* Min and Max TX Bandwidth Allocation */ 689 | IFLA_VF_RSS_QUERY_EN, /* RSS Redirection Table and Hash Key query 690 | * on/off switch 691 | */ 692 | IFLA_VF_STATS, /* network device statistics */ 693 | IFLA_VF_TRUST, /* Trust VF */ 694 | IFLA_VF_IB_NODE_GUID, /* VF Infiniband node GUID */ 695 | IFLA_VF_IB_PORT_GUID, /* VF Infiniband port GUID */ 696 | IFLA_VF_VLAN_LIST, /* nested list of vlans, option for QinQ */ 697 | __IFLA_VF_MAX, 698 | }; 699 | 700 | #define IFLA_VF_MAX (__IFLA_VF_MAX - 1) 701 | 702 | struct ifla_vf_mac { 703 | __u32 vf; 704 | __u8 mac[32]; /* MAX_ADDR_LEN */ 705 | }; 706 | 707 | struct ifla_vf_vlan { 708 | __u32 vf; 709 | __u32 vlan; /* 0 - 4095, 0 disables VLAN filter */ 710 | __u32 qos; 711 | }; 712 | 713 | enum { 714 | IFLA_VF_VLAN_INFO_UNSPEC, 715 | IFLA_VF_VLAN_INFO, /* VLAN ID, QoS and VLAN protocol */ 716 | __IFLA_VF_VLAN_INFO_MAX, 717 | }; 718 | 719 | #define IFLA_VF_VLAN_INFO_MAX (__IFLA_VF_VLAN_INFO_MAX - 1) 720 | #define MAX_VLAN_LIST_LEN 1 721 | 722 | struct ifla_vf_vlan_info { 723 | __u32 vf; 724 | __u32 vlan; /* 0 - 4095, 0 disables VLAN filter */ 725 | __u32 qos; 726 | __be16 vlan_proto; /* VLAN protocol either 802.1Q or 802.1ad */ 727 | }; 728 | 729 | struct ifla_vf_tx_rate { 730 | __u32 vf; 731 | __u32 rate; /* Max TX bandwidth in Mbps, 0 disables throttling */ 732 | }; 733 | 734 | struct ifla_vf_rate { 735 | __u32 vf; 736 | __u32 min_tx_rate; /* Min Bandwidth in Mbps */ 737 | __u32 max_tx_rate; /* Max Bandwidth in Mbps */ 738 | }; 739 | 740 | struct ifla_vf_spoofchk { 741 | __u32 vf; 742 | __u32 setting; 743 | }; 744 | 745 | struct ifla_vf_guid { 746 | __u32 vf; 747 | __u64 guid; 748 | }; 749 | 750 | enum { 751 | IFLA_VF_LINK_STATE_AUTO, /* link state of the uplink */ 752 | IFLA_VF_LINK_STATE_ENABLE, /* link always up */ 753 | IFLA_VF_LINK_STATE_DISABLE, /* link always down */ 754 | __IFLA_VF_LINK_STATE_MAX, 755 | }; 756 | 757 | struct ifla_vf_link_state { 758 | __u32 vf; 759 | __u32 link_state; 760 | }; 761 | 762 | struct ifla_vf_rss_query_en { 763 | __u32 vf; 764 | __u32 setting; 765 | }; 766 | 767 | enum { 768 | IFLA_VF_STATS_RX_PACKETS, 769 | IFLA_VF_STATS_TX_PACKETS, 770 | IFLA_VF_STATS_RX_BYTES, 771 | IFLA_VF_STATS_TX_BYTES, 772 | IFLA_VF_STATS_BROADCAST, 773 | IFLA_VF_STATS_MULTICAST, 774 | IFLA_VF_STATS_PAD, 775 | IFLA_VF_STATS_RX_DROPPED, 776 | IFLA_VF_STATS_TX_DROPPED, 777 | __IFLA_VF_STATS_MAX, 778 | }; 779 | 780 | #define IFLA_VF_STATS_MAX (__IFLA_VF_STATS_MAX - 1) 781 | 782 | struct ifla_vf_trust { 783 | __u32 vf; 784 | __u32 setting; 785 | }; 786 | 787 | /* VF ports management section 788 | * 789 | * Nested layout of set/get msg is: 790 | * 791 | * [IFLA_NUM_VF] 792 | * [IFLA_VF_PORTS] 793 | * [IFLA_VF_PORT] 794 | * [IFLA_PORT_*], ... 795 | * [IFLA_VF_PORT] 796 | * [IFLA_PORT_*], ... 797 | * ... 798 | * [IFLA_PORT_SELF] 799 | * [IFLA_PORT_*], ... 800 | */ 801 | 802 | enum { 803 | IFLA_VF_PORT_UNSPEC, 804 | IFLA_VF_PORT, /* nest */ 805 | __IFLA_VF_PORT_MAX, 806 | }; 807 | 808 | #define IFLA_VF_PORT_MAX (__IFLA_VF_PORT_MAX - 1) 809 | 810 | enum { 811 | IFLA_PORT_UNSPEC, 812 | IFLA_PORT_VF, /* __u32 */ 813 | IFLA_PORT_PROFILE, /* string */ 814 | IFLA_PORT_VSI_TYPE, /* 802.1Qbg (pre-)standard VDP */ 815 | IFLA_PORT_INSTANCE_UUID, /* binary UUID */ 816 | IFLA_PORT_HOST_UUID, /* binary UUID */ 817 | IFLA_PORT_REQUEST, /* __u8 */ 818 | IFLA_PORT_RESPONSE, /* __u16, output only */ 819 | __IFLA_PORT_MAX, 820 | }; 821 | 822 | #define IFLA_PORT_MAX (__IFLA_PORT_MAX - 1) 823 | 824 | #define PORT_PROFILE_MAX 40 825 | #define PORT_UUID_MAX 16 826 | #define PORT_SELF_VF -1 827 | 828 | enum { 829 | PORT_REQUEST_PREASSOCIATE = 0, 830 | PORT_REQUEST_PREASSOCIATE_RR, 831 | PORT_REQUEST_ASSOCIATE, 832 | PORT_REQUEST_DISASSOCIATE, 833 | }; 834 | 835 | enum { 836 | PORT_VDP_RESPONSE_SUCCESS = 0, 837 | PORT_VDP_RESPONSE_INVALID_FORMAT, 838 | PORT_VDP_RESPONSE_INSUFFICIENT_RESOURCES, 839 | PORT_VDP_RESPONSE_UNUSED_VTID, 840 | PORT_VDP_RESPONSE_VTID_VIOLATION, 841 | PORT_VDP_RESPONSE_VTID_VERSION_VIOALTION, 842 | PORT_VDP_RESPONSE_OUT_OF_SYNC, 843 | /* 0x08-0xFF reserved for future VDP use */ 844 | PORT_PROFILE_RESPONSE_SUCCESS = 0x100, 845 | PORT_PROFILE_RESPONSE_INPROGRESS, 846 | PORT_PROFILE_RESPONSE_INVALID, 847 | PORT_PROFILE_RESPONSE_BADSTATE, 848 | PORT_PROFILE_RESPONSE_INSUFFICIENT_RESOURCES, 849 | PORT_PROFILE_RESPONSE_ERROR, 850 | }; 851 | 852 | struct ifla_port_vsi { 853 | __u8 vsi_mgr_id; 854 | __u8 vsi_type_id[3]; 855 | __u8 vsi_type_version; 856 | __u8 pad[3]; 857 | }; 858 | 859 | 860 | /* IPoIB section */ 861 | 862 | enum { 863 | IFLA_IPOIB_UNSPEC, 864 | IFLA_IPOIB_PKEY, 865 | IFLA_IPOIB_MODE, 866 | IFLA_IPOIB_UMCAST, 867 | __IFLA_IPOIB_MAX 868 | }; 869 | 870 | enum { 871 | IPOIB_MODE_DATAGRAM = 0, /* using unreliable datagram QPs */ 872 | IPOIB_MODE_CONNECTED = 1, /* using connected QPs */ 873 | }; 874 | 875 | #define IFLA_IPOIB_MAX (__IFLA_IPOIB_MAX - 1) 876 | 877 | 878 | /* HSR section */ 879 | 880 | enum { 881 | IFLA_HSR_UNSPEC, 882 | IFLA_HSR_SLAVE1, 883 | IFLA_HSR_SLAVE2, 884 | IFLA_HSR_MULTICAST_SPEC, /* Last byte of supervision addr */ 885 | IFLA_HSR_SUPERVISION_ADDR, /* Supervision frame multicast addr */ 886 | IFLA_HSR_SEQ_NR, 887 | IFLA_HSR_VERSION, /* HSR version */ 888 | __IFLA_HSR_MAX, 889 | }; 890 | 891 | #define IFLA_HSR_MAX (__IFLA_HSR_MAX - 1) 892 | 893 | /* STATS section */ 894 | 895 | struct if_stats_msg { 896 | __u8 family; 897 | __u8 pad1; 898 | __u16 pad2; 899 | __u32 ifindex; 900 | __u32 filter_mask; 901 | }; 902 | 903 | /* A stats attribute can be netdev specific or a global stat. 904 | * For netdev stats, lets use the prefix IFLA_STATS_LINK_* 905 | */ 906 | enum { 907 | IFLA_STATS_UNSPEC, /* also used as 64bit pad attribute */ 908 | IFLA_STATS_LINK_64, 909 | IFLA_STATS_LINK_XSTATS, 910 | IFLA_STATS_LINK_XSTATS_SLAVE, 911 | IFLA_STATS_LINK_OFFLOAD_XSTATS, 912 | IFLA_STATS_AF_SPEC, 913 | __IFLA_STATS_MAX, 914 | }; 915 | 916 | #define IFLA_STATS_MAX (__IFLA_STATS_MAX - 1) 917 | 918 | #define IFLA_STATS_FILTER_BIT(ATTR) (1 << (ATTR - 1)) 919 | 920 | /* These are embedded into IFLA_STATS_LINK_XSTATS: 921 | * [IFLA_STATS_LINK_XSTATS] 922 | * -> [LINK_XSTATS_TYPE_xxx] 923 | * -> [rtnl link type specific attributes] 924 | */ 925 | enum { 926 | LINK_XSTATS_TYPE_UNSPEC, 927 | LINK_XSTATS_TYPE_BRIDGE, 928 | LINK_XSTATS_TYPE_BOND, 929 | __LINK_XSTATS_TYPE_MAX 930 | }; 931 | #define LINK_XSTATS_TYPE_MAX (__LINK_XSTATS_TYPE_MAX - 1) 932 | 933 | /* These are stats embedded into IFLA_STATS_LINK_OFFLOAD_XSTATS */ 934 | enum { 935 | IFLA_OFFLOAD_XSTATS_UNSPEC, 936 | IFLA_OFFLOAD_XSTATS_CPU_HIT, /* struct rtnl_link_stats64 */ 937 | __IFLA_OFFLOAD_XSTATS_MAX 938 | }; 939 | #define IFLA_OFFLOAD_XSTATS_MAX (__IFLA_OFFLOAD_XSTATS_MAX - 1) 940 | 941 | /* XDP section */ 942 | 943 | #define XDP_FLAGS_UPDATE_IF_NOEXIST (1U << 0) 944 | #define XDP_FLAGS_SKB_MODE (1U << 1) 945 | #define XDP_FLAGS_DRV_MODE (1U << 2) 946 | #define XDP_FLAGS_HW_MODE (1U << 3) 947 | #define XDP_FLAGS_MODES (XDP_FLAGS_SKB_MODE | \ 948 | XDP_FLAGS_DRV_MODE | \ 949 | XDP_FLAGS_HW_MODE) 950 | #define XDP_FLAGS_MASK (XDP_FLAGS_UPDATE_IF_NOEXIST | \ 951 | XDP_FLAGS_MODES) 952 | 953 | /* These are stored into IFLA_XDP_ATTACHED on dump. */ 954 | enum { 955 | XDP_ATTACHED_NONE = 0, 956 | XDP_ATTACHED_DRV, 957 | XDP_ATTACHED_SKB, 958 | XDP_ATTACHED_HW, 959 | XDP_ATTACHED_MULTI, 960 | }; 961 | 962 | enum { 963 | IFLA_XDP_UNSPEC, 964 | IFLA_XDP_FD, 965 | IFLA_XDP_ATTACHED, 966 | IFLA_XDP_FLAGS, 967 | IFLA_XDP_PROG_ID, 968 | IFLA_XDP_DRV_PROG_ID, 969 | IFLA_XDP_SKB_PROG_ID, 970 | IFLA_XDP_HW_PROG_ID, 971 | __IFLA_XDP_MAX, 972 | }; 973 | 974 | #define IFLA_XDP_MAX (__IFLA_XDP_MAX - 1) 975 | 976 | enum { 977 | IFLA_EVENT_NONE, 978 | IFLA_EVENT_REBOOT, /* internal reset / reboot */ 979 | IFLA_EVENT_FEATURES, /* change in offload features */ 980 | IFLA_EVENT_BONDING_FAILOVER, /* change in active slave */ 981 | IFLA_EVENT_NOTIFY_PEERS, /* re-sent grat. arp/ndisc */ 982 | IFLA_EVENT_IGMP_RESEND, /* re-sent IGMP JOIN */ 983 | IFLA_EVENT_BONDING_OPTIONS, /* change in bonding options */ 984 | }; 985 | 986 | /* tun section */ 987 | 988 | enum { 989 | IFLA_TUN_UNSPEC, 990 | IFLA_TUN_OWNER, 991 | IFLA_TUN_GROUP, 992 | IFLA_TUN_TYPE, 993 | IFLA_TUN_PI, 994 | IFLA_TUN_VNET_HDR, 995 | IFLA_TUN_PERSIST, 996 | IFLA_TUN_MULTI_QUEUE, 997 | IFLA_TUN_NUM_QUEUES, 998 | IFLA_TUN_NUM_DISABLED_QUEUES, 999 | __IFLA_TUN_MAX, 1000 | }; 1001 | 1002 | #define IFLA_TUN_MAX (__IFLA_TUN_MAX - 1) 1003 | 1004 | /* rmnet section */ 1005 | 1006 | #define RMNET_FLAGS_INGRESS_DEAGGREGATION (1U << 0) 1007 | #define RMNET_FLAGS_INGRESS_MAP_COMMANDS (1U << 1) 1008 | #define RMNET_FLAGS_INGRESS_MAP_CKSUMV4 (1U << 2) 1009 | #define RMNET_FLAGS_EGRESS_MAP_CKSUMV4 (1U << 3) 1010 | 1011 | enum { 1012 | IFLA_RMNET_UNSPEC, 1013 | IFLA_RMNET_MUX_ID, 1014 | IFLA_RMNET_FLAGS, 1015 | __IFLA_RMNET_MAX, 1016 | }; 1017 | 1018 | #define IFLA_RMNET_MAX (__IFLA_RMNET_MAX - 1) 1019 | 1020 | struct ifla_rmnet_flags { 1021 | __u32 flags; 1022 | __u32 mask; 1023 | }; 1024 | 1025 | #endif /* _UAPI_LINUX_IF_LINK_H */ 1026 | -------------------------------------------------------------------------------- /headers/linux/if_xdp.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ 2 | /* 3 | * if_xdp: XDP socket user-space interface 4 | * Copyright(c) 2018 Intel Corporation. 5 | * 6 | * Author(s): Björn Töpel 7 | * Magnus Karlsson 8 | */ 9 | 10 | #ifndef _LINUX_IF_XDP_H 11 | #define _LINUX_IF_XDP_H 12 | 13 | #include 14 | 15 | /* Options for the sxdp_flags field */ 16 | #define XDP_SHARED_UMEM (1 << 0) 17 | #define XDP_COPY (1 << 1) /* Force copy-mode */ 18 | #define XDP_ZEROCOPY (1 << 2) /* Force zero-copy mode */ 19 | /* If this option is set, the driver might go sleep and in that case 20 | * the XDP_RING_NEED_WAKEUP flag in the fill and/or Tx rings will be 21 | * set. If it is set, the application need to explicitly wake up the 22 | * driver with a poll() (Rx and Tx) or sendto() (Tx only). If you are 23 | * running the driver and the application on the same core, you should 24 | * use this option so that the kernel will yield to the user space 25 | * application. 26 | */ 27 | #define XDP_USE_NEED_WAKEUP (1 << 3) 28 | 29 | /* Flags for xsk_umem_config flags */ 30 | #define XDP_UMEM_UNALIGNED_CHUNK_FLAG (1 << 0) 31 | 32 | struct sockaddr_xdp { 33 | __u16 sxdp_family; 34 | __u16 sxdp_flags; 35 | __u32 sxdp_ifindex; 36 | __u32 sxdp_queue_id; 37 | __u32 sxdp_shared_umem_fd; 38 | }; 39 | 40 | /* XDP_RING flags */ 41 | #define XDP_RING_NEED_WAKEUP (1 << 0) 42 | 43 | struct xdp_ring_offset { 44 | __u64 producer; 45 | __u64 consumer; 46 | __u64 desc; 47 | __u64 flags; 48 | }; 49 | 50 | struct xdp_mmap_offsets { 51 | struct xdp_ring_offset rx; 52 | struct xdp_ring_offset tx; 53 | struct xdp_ring_offset fr; /* Fill */ 54 | struct xdp_ring_offset cr; /* Completion */ 55 | }; 56 | 57 | /* XDP socket options */ 58 | #define XDP_MMAP_OFFSETS 1 59 | #define XDP_RX_RING 2 60 | #define XDP_TX_RING 3 61 | #define XDP_UMEM_REG 4 62 | #define XDP_UMEM_FILL_RING 5 63 | #define XDP_UMEM_COMPLETION_RING 6 64 | #define XDP_STATISTICS 7 65 | #define XDP_OPTIONS 8 66 | 67 | struct xdp_umem_reg { 68 | __u64 addr; /* Start of packet data area */ 69 | __u64 len; /* Length of packet data area */ 70 | __u32 chunk_size; 71 | __u32 headroom; 72 | __u32 flags; 73 | }; 74 | 75 | struct xdp_statistics { 76 | __u64 rx_dropped; /* Dropped for reasons other than invalid desc */ 77 | __u64 rx_invalid_descs; /* Dropped due to invalid descriptor */ 78 | __u64 tx_invalid_descs; /* Dropped due to invalid descriptor */ 79 | }; 80 | 81 | struct xdp_options { 82 | __u32 flags; 83 | }; 84 | 85 | /* Flags for the flags field of struct xdp_options */ 86 | #define XDP_OPTIONS_ZEROCOPY (1 << 0) 87 | 88 | /* Pgoff for mmaping the rings */ 89 | #define XDP_PGOFF_RX_RING 0 90 | #define XDP_PGOFF_TX_RING 0x80000000 91 | #define XDP_UMEM_PGOFF_FILL_RING 0x100000000ULL 92 | #define XDP_UMEM_PGOFF_COMPLETION_RING 0x180000000ULL 93 | 94 | /* Masks for unaligned chunks mode */ 95 | #define XSK_UNALIGNED_BUF_OFFSET_SHIFT 48 96 | #define XSK_UNALIGNED_BUF_ADDR_MASK \ 97 | ((1ULL << XSK_UNALIGNED_BUF_OFFSET_SHIFT) - 1) 98 | 99 | /* Rx/Tx descriptor */ 100 | struct xdp_desc { 101 | __u64 addr; 102 | __u32 len; 103 | __u32 options; 104 | }; 105 | 106 | /* UMEM descriptor is __u64 */ 107 | 108 | #endif /* _LINUX_IF_XDP_H */ 109 | -------------------------------------------------------------------------------- /headers/perf-sys.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* Copied from $(LINUX)/tools/perf/perf-sys.h (kernel 4.18) */ 3 | #ifndef _PERF_SYS_H 4 | #define _PERF_SYS_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | /* 12 | * remove the following headers to allow for userspace program compilation 13 | * #include 14 | * #include 15 | */ 16 | #ifdef __powerpc__ 17 | #define CPUINFO_PROC {"cpu"} 18 | #endif 19 | 20 | #ifdef __s390__ 21 | #define CPUINFO_PROC {"vendor_id"} 22 | #endif 23 | 24 | #ifdef __sh__ 25 | #define CPUINFO_PROC {"cpu type"} 26 | #endif 27 | 28 | #ifdef __hppa__ 29 | #define CPUINFO_PROC {"cpu"} 30 | #endif 31 | 32 | #ifdef __sparc__ 33 | #define CPUINFO_PROC {"cpu"} 34 | #endif 35 | 36 | #ifdef __alpha__ 37 | #define CPUINFO_PROC {"cpu model"} 38 | #endif 39 | 40 | #ifdef __arm__ 41 | #define CPUINFO_PROC {"model name", "Processor"} 42 | #endif 43 | 44 | #ifdef __mips__ 45 | #define CPUINFO_PROC {"cpu model"} 46 | #endif 47 | 48 | #ifdef __arc__ 49 | #define CPUINFO_PROC {"Processor"} 50 | #endif 51 | 52 | #ifdef __xtensa__ 53 | #define CPUINFO_PROC {"core ID"} 54 | #endif 55 | 56 | #ifndef CPUINFO_PROC 57 | #define CPUINFO_PROC { "model name", } 58 | #endif 59 | 60 | static inline int 61 | sys_perf_event_open(struct perf_event_attr *attr, 62 | pid_t pid, int cpu, int group_fd, 63 | unsigned long flags) 64 | { 65 | int fd; 66 | 67 | fd = syscall(__NR_perf_event_open, attr, pid, cpu, 68 | group_fd, flags); 69 | 70 | #ifdef HAVE_ATTR_TEST 71 | if (unlikely(test_attr__enabled)) 72 | test_attr__open(attr, pid, cpu, fd, group_fd, flags); 73 | #endif 74 | return fd; 75 | } 76 | 77 | #endif /* _PERF_SYS_H */ 78 | -------------------------------------------------------------------------------- /xdp_kern_drop.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "headers/bpf_endian.h" 6 | #include "headers/bpf_helpers.h" 7 | #include "common/parsing_helpers.h" 8 | 9 | struct bpf_map_def SEC("maps") drop_count = { 10 | .type = BPF_MAP_TYPE_ARRAY, 11 | .key_size = sizeof(int), 12 | .value_size = sizeof(long), 13 | .max_entries = 2, 14 | }; 15 | 16 | /* LLVM maps __sync_fetch_and_add() as a built-in function to the BPF atomic add 17 | * instruction (that is BPF_STX | BPF_XADD | BPF_W for word sizes) 18 | */ 19 | #ifndef lock_xadd 20 | #define lock_xadd(ptr, val) ((void)__sync_fetch_and_add(ptr, val)) 21 | #endif 22 | 23 | SEC("xdp_ape_drop") 24 | int xdp_ape_drop_func(struct xdp_md *ctx) 25 | { 26 | int eth_type, ip_type, map_key; 27 | struct ethhdr *eth; 28 | struct iphdr *iphdr; 29 | struct ipv6hdr *ipv6hdr; 30 | void *data_end = (void *)(long)ctx->data_end; 31 | void *data = (void *)(long)ctx->data; 32 | struct hdr_cursor nh = { .pos = data }; 33 | long *value; 34 | 35 | eth_type = parse_ethhdr(&nh, data_end, ð); 36 | if (eth_type < 0) 37 | return XDP_ABORTED; 38 | 39 | if (eth_type == bpf_htons(ETH_P_IP)) { 40 | ip_type = parse_iphdr(&nh, data_end, &iphdr); 41 | } else if (eth_type == bpf_htons(ETH_P_IPV6)) { 42 | ip_type = parse_ip6hdr(&nh, data_end, &ipv6hdr); 43 | } else { 44 | return XDP_PASS; 45 | } 46 | 47 | // only support UDP for now 48 | if (ip_type != IPPROTO_UDP) 49 | return XDP_PASS; 50 | 51 | #ifdef UDP_PORT 52 | struct udphdr *udphdr; 53 | 54 | // don't mess with ports outside our purview, if specified 55 | if (parse_udphdr(&nh, data_end, &udphdr) < 0) 56 | return XDP_ABORTED; 57 | if (bpf_ntohs(udphdr->dest) != UDP_PORT) 58 | return XDP_PASS; 59 | #endif /* UDP_PORT */ 60 | 61 | // if we're here, it's a UDP packet with dst port we care about 62 | map_key = 0; 63 | value = bpf_map_lookup_elem(&drop_count, &map_key); 64 | if (value) 65 | lock_xadd(value, 1); 66 | 67 | if ((bpf_get_prandom_u32() % 100) < UDP_DROP_PROB) { 68 | map_key = 1; 69 | value = bpf_map_lookup_elem(&drop_count, &map_key); 70 | if (value) 71 | lock_xadd(value, 1); 72 | return XDP_DROP; 73 | } 74 | 75 | return XDP_PASS; 76 | } 77 | 78 | char _license[] SEC("license") = "GPL"; 79 | -------------------------------------------------------------------------------- /xdp_kern_reflect.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "headers/bpf_endian.h" 6 | #include "headers/bpf_helpers.h" 7 | #include "common/parsing_helpers.h" 8 | 9 | struct bpf_map_def SEC("maps") reflect_count = { 10 | .type = BPF_MAP_TYPE_ARRAY, 11 | .key_size = sizeof(int), 12 | .value_size = sizeof(long), 13 | .max_entries = 2, 14 | }; 15 | 16 | struct bpf_map_def SEC("maps") xsks_map = { 17 | .type = BPF_MAP_TYPE_XSKMAP, 18 | .key_size = sizeof(int), 19 | .value_size = sizeof(int), 20 | .max_entries = 64, 21 | }; 22 | 23 | /* LLVM maps __sync_fetch_and_add() as a built-in function to the BPF atomic add 24 | * instruction (that is BPF_STX | BPF_XADD | BPF_W for word sizes) 25 | */ 26 | #ifndef lock_xadd 27 | #define lock_xadd(ptr, val) ((void)__sync_fetch_and_add(ptr, val)) 28 | #endif 29 | 30 | SEC("xdp_ape_reflect") 31 | int xdp_ape_reflect_func(struct xdp_md *ctx) 32 | { 33 | int eth_type, ip_type, map_key, index; 34 | struct ethhdr *eth; 35 | struct iphdr *iphdr; 36 | struct ipv6hdr *ipv6hdr; 37 | void *data_end = (void *)(long)ctx->data_end; 38 | void *data = (void *)(long)ctx->data; 39 | struct hdr_cursor nh = { .pos = data }; 40 | long *value; 41 | 42 | eth_type = parse_ethhdr(&nh, data_end, ð); 43 | if (eth_type < 0) 44 | return XDP_ABORTED; 45 | 46 | if (eth_type == bpf_htons(ETH_P_IP)) { 47 | ip_type = parse_iphdr(&nh, data_end, &iphdr); 48 | } else if (eth_type == bpf_htons(ETH_P_IPV6)) { 49 | ip_type = parse_ip6hdr(&nh, data_end, &ipv6hdr); 50 | } else { 51 | return XDP_PASS; 52 | } 53 | 54 | // only support UDP for now 55 | if (ip_type != IPPROTO_UDP) 56 | return XDP_PASS; 57 | 58 | #ifdef UDP_PORT 59 | struct udphdr *udphdr; 60 | // don't mess with ports outside our purview, if specified 61 | if (parse_udphdr(&nh, data_end, &udphdr) < 0) 62 | return XDP_ABORTED; 63 | if (bpf_ntohs(udphdr->dest) != UDP_PORT) 64 | return XDP_PASS; 65 | #endif /* UDP_PORT */ 66 | 67 | //if we're here, it's a UDP packet with dst port we care about 68 | map_key = 0; 69 | value = bpf_map_lookup_elem(&reflect_count, &map_key); 70 | if (value) 71 | lock_xadd(value, 1); 72 | 73 | index = ctx->rx_queue_index; 74 | if (bpf_map_lookup_elem(&xsks_map, &index)) { 75 | map_key = 1; 76 | value = bpf_map_lookup_elem(&reflect_count, &map_key); 77 | if (value) 78 | lock_xadd(value, 1); 79 | 80 | return bpf_redirect_map(&xsks_map, index, 0); 81 | } 82 | return XDP_PASS; 83 | } 84 | 85 | char _license[] SEC("license") = "GPL"; 86 | -------------------------------------------------------------------------------- /xdp_kern_scramble.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "headers/bpf_endian.h" 6 | #include "headers/bpf_helpers.h" 7 | #include "common/parsing_helpers.h" 8 | 9 | struct bpf_map_def SEC("maps") scramble_count = { 10 | .type = BPF_MAP_TYPE_ARRAY, 11 | .key_size = sizeof(int), 12 | .value_size = sizeof(long), 13 | .max_entries = 2, 14 | }; 15 | 16 | struct bpf_map_def SEC("maps") xsks_map = { 17 | .type = BPF_MAP_TYPE_XSKMAP, 18 | .key_size = sizeof(int), 19 | .value_size = sizeof(int), 20 | .max_entries = 64, 21 | }; 22 | 23 | /* LLVM maps __sync_fetch_and_add() as a built-in function to the BPF atomic add 24 | * instruction (that is BPF_STX | BPF_XADD | BPF_W for word sizes) 25 | */ 26 | #ifndef lock_xadd 27 | #define lock_xadd(ptr, val) ((void)__sync_fetch_and_add(ptr, val)) 28 | #endif 29 | 30 | SEC("xdp_ape_scramble") 31 | int xdp_ape_scramble_func(struct xdp_md *ctx) 32 | { 33 | int eth_type, ip_type, map_key, index; 34 | struct ethhdr *eth; 35 | struct iphdr *iphdr; 36 | struct ipv6hdr *ipv6hdr; 37 | void *data_end = (void *)(long)ctx->data_end; 38 | void *data = (void *)(long)ctx->data; 39 | struct hdr_cursor nh = { .pos = data }; 40 | long *value; 41 | 42 | eth_type = parse_ethhdr(&nh, data_end, ð); 43 | if (eth_type < 0) 44 | return XDP_ABORTED; 45 | 46 | if (eth_type == bpf_htons(ETH_P_IP)) { 47 | ip_type = parse_iphdr(&nh, data_end, &iphdr); 48 | } else if (eth_type == bpf_htons(ETH_P_IPV6)) { 49 | ip_type = parse_ip6hdr(&nh, data_end, &ipv6hdr); 50 | } else { 51 | return XDP_PASS; 52 | } 53 | 54 | // only support UDP for now 55 | if (ip_type != IPPROTO_UDP) 56 | return XDP_PASS; 57 | 58 | #ifdef UDP_PORT 59 | struct udphdr *udphdr; 60 | // don't mess with ports outside our purview, if specified 61 | if (parse_udphdr(&nh, data_end, &udphdr) < 0) 62 | return XDP_ABORTED; 63 | if (bpf_ntohs(udphdr->dest) != UDP_PORT) 64 | return XDP_PASS; 65 | #endif /* UDP_PORT */ 66 | 67 | //if we're here, it's a UDP packet with dst port we care about 68 | map_key = 0; 69 | value = bpf_map_lookup_elem(&scramble_count, &map_key); 70 | if (value) 71 | lock_xadd(value, 1); 72 | 73 | if ((bpf_get_prandom_u32() % 100) < UDP_SCRAMBLE_PROB) { 74 | map_key = 1; 75 | value = bpf_map_lookup_elem(&scramble_count, &map_key); 76 | if (value) 77 | lock_xadd(value, 1); 78 | 79 | index = ctx->rx_queue_index; 80 | if (bpf_map_lookup_elem(&xsks_map, &index)) 81 | return bpf_redirect_map(&xsks_map, index, 0); 82 | } 83 | 84 | return XDP_PASS; 85 | } 86 | 87 | char _license[] SEC("license") = "GPL"; 88 | -------------------------------------------------------------------------------- /xdp_user_drop.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | static const char *__doc__ = "github.com/sevagh/ape\n"; 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #include /* depend on kernel-headers installed */ 19 | #include 20 | 21 | #include "common/common_libbpf.h" 22 | #include "common/common_params.h" 23 | #include "common/common_user_bpf_xdp.h" 24 | 25 | static const char *default_filename = "xdp_prog_kern.o"; 26 | 27 | static const struct option_wrapper long_options[] = { 28 | 29 | { { "help", no_argument, NULL, 'h' }, "Show help", false }, 30 | 31 | { { "dev", required_argument, NULL, 'd' }, 32 | "Operate on device ", 33 | "", 34 | true }, 35 | 36 | { { "skb-mode", no_argument, NULL, 'S' }, 37 | "Install XDP program in SKB (AKA generic) mode" }, 38 | 39 | { { "native-mode", no_argument, NULL, 'N' }, 40 | "Install XDP program in native mode" }, 41 | 42 | { { "auto-mode", no_argument, NULL, 'A' }, 43 | "Auto-detect SKB or native mode" }, 44 | 45 | { { "force", no_argument, NULL, 'F' }, 46 | "Force install, replacing existing program on interface" }, 47 | 48 | { { "unload", no_argument, NULL, 'U' }, 49 | "Unload XDP program instead of loading" }, 50 | 51 | { { "reuse-maps", no_argument, NULL, 'M' }, "Reuse pinned maps" }, 52 | 53 | { { "quiet", no_argument, NULL, 'q' }, "Quiet mode (no output)" }, 54 | 55 | { { "filename", required_argument, NULL, 1 }, 56 | "Load program from ", 57 | "" }, 58 | 59 | { { "progsec", required_argument, NULL, 2 }, 60 | "Load program in
of the ELF file", 61 | "
" }, 62 | 63 | { { 0, 0, NULL, 0 }, NULL, false } 64 | }; 65 | 66 | #ifndef PATH_MAX 67 | #define PATH_MAX 4096 68 | #endif 69 | 70 | const char *pin_basedir = "/sys/fs/bpf"; 71 | const char *map_name = "drop_count"; 72 | 73 | /* Pinning maps under /sys/fs/bpf in subdir */ 74 | int pin_maps_in_bpf_object(struct bpf_object *bpf_obj, struct config *cfg) 75 | { 76 | char map_filename[PATH_MAX]; 77 | int err, len; 78 | 79 | len = snprintf(map_filename, PATH_MAX, "%s/%s/%s", pin_basedir, 80 | cfg->ifname, map_name); 81 | if (len < 0) { 82 | fprintf(stderr, "ERR: creating map_name\n"); 83 | return EXIT_FAIL_OPTION; 84 | } 85 | 86 | /* Existing/previous XDP prog might not have cleaned up */ 87 | if (access(map_filename, F_OK) != -1) { 88 | if (verbose) 89 | printf(" - Unpinning (remove) prev maps in %s/\n", 90 | cfg->pin_dir); 91 | 92 | /* Basically calls unlink(3) on map_filename */ 93 | err = bpf_object__unpin_maps(bpf_obj, cfg->pin_dir); 94 | if (err) { 95 | fprintf(stderr, "ERR: UNpinning maps in %s\n", 96 | cfg->pin_dir); 97 | return EXIT_FAIL_BPF; 98 | } 99 | } 100 | if (verbose) 101 | printf(" - Pinning maps in %s/\n", cfg->pin_dir); 102 | 103 | /* This will pin all maps in our bpf_object */ 104 | err = bpf_object__pin_maps(bpf_obj, cfg->pin_dir); 105 | if (err) 106 | return EXIT_FAIL_BPF; 107 | 108 | return 0; 109 | } 110 | 111 | int main(int argc, char **argv) 112 | { 113 | struct rlimit r = { RLIM_INFINITY, RLIM_INFINITY }; 114 | 115 | if (setrlimit(RLIMIT_MEMLOCK, &r)) { 116 | perror("setrlimit(RLIMIT_MEMLOCK)"); 117 | return 1; 118 | } 119 | 120 | struct bpf_object *bpf_obj; 121 | int err, len; 122 | 123 | struct config cfg = { 124 | .xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_DRV_MODE, 125 | .ifindex = -1, 126 | .do_unload = false, 127 | }; 128 | /* Set default BPF-ELF object file and BPF program name */ 129 | strncpy(cfg.filename, default_filename, sizeof(cfg.filename)); 130 | /* Cmdline options can change progsec */ 131 | parse_cmdline_args(argc, argv, long_options, &cfg, __doc__); 132 | 133 | /* Required option */ 134 | if (cfg.ifindex == -1) { 135 | fprintf(stderr, "ERR: required option --dev missing\n\n"); 136 | usage(argv[0], __doc__, long_options, (argc == 1)); 137 | return EXIT_FAIL_OPTION; 138 | } 139 | if (cfg.do_unload) { 140 | if (!cfg.reuse_maps) { 141 | /* TODO: Miss unpin of maps on unload */ 142 | } 143 | return xdp_link_detach(cfg.ifindex, cfg.xdp_flags, 0); 144 | } 145 | 146 | len = snprintf(cfg.pin_dir, PATH_MAX, "%s/%s", pin_basedir, cfg.ifname); 147 | if (len < 0) { 148 | fprintf(stderr, "ERR: creating pin dirname\n"); 149 | return EXIT_FAIL_OPTION; 150 | } 151 | 152 | bpf_obj = load_bpf_and_xdp_attach(&cfg); 153 | if (!bpf_obj) 154 | return EXIT_FAIL_BPF; 155 | 156 | if (verbose) { 157 | printf("Success: Loaded BPF-object(%s) and used section(%s)\n", 158 | cfg.filename, cfg.progsec); 159 | printf(" - XDP prog attached on device:%s(ifindex:%d)\n", 160 | cfg.ifname, cfg.ifindex); 161 | } 162 | 163 | /* Use the --dev name as subdir for exporting/pinning maps */ 164 | if (!cfg.reuse_maps) { 165 | err = pin_maps_in_bpf_object(bpf_obj, &cfg); 166 | if (err) { 167 | fprintf(stderr, "ERR: pinning maps\n"); 168 | return err; 169 | } 170 | } 171 | 172 | return EXIT_OK; 173 | } 174 | -------------------------------------------------------------------------------- /xdp_user_reflect.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "common/common_params.h" 30 | #include "common/common_user_bpf_xdp.h" 31 | #include "common/common_libbpf.h" 32 | #include "headers/bpf_endian.h" 33 | //#include "headers/bpf_helpers.h" 34 | #include "common/parsing_helpers.h" 35 | 36 | #define NUM_FRAMES 4096 37 | #define FRAME_SIZE XSK_UMEM__DEFAULT_FRAME_SIZE 38 | #define RX_BATCH_SIZE 64 39 | #define INVALID_UMEM_FRAME UINT64_MAX 40 | 41 | #ifndef MAX_SLEEP_MS 42 | #define MAX_SLEEP_MS 1000 43 | #endif 44 | 45 | struct xsk_umem_info { 46 | struct xsk_ring_prod fq; 47 | struct xsk_ring_cons cq; 48 | struct xsk_umem *umem; 49 | void *buffer; 50 | }; 51 | 52 | struct xsk_socket_info { 53 | struct xsk_ring_cons rx; 54 | struct xsk_ring_prod tx; 55 | struct xsk_umem_info *umem; 56 | struct xsk_socket *xsk; 57 | 58 | uint64_t umem_frame_addr[NUM_FRAMES]; 59 | uint32_t umem_frame_free; 60 | 61 | uint32_t outstanding_tx; 62 | }; 63 | 64 | static inline __u32 xsk_ring_prod__free(struct xsk_ring_prod *r) 65 | { 66 | r->cached_cons = *r->consumer + r->size; 67 | return r->cached_cons - r->cached_prod; 68 | } 69 | 70 | static const char *__doc__ = "github.com/sevagh/ape\n"; 71 | 72 | static const struct option_wrapper long_options[] = { 73 | 74 | { { "help", no_argument, NULL, 'h' }, "Show help", false }, 75 | 76 | { { "dev", required_argument, NULL, 'd' }, 77 | "Operate on device ", 78 | "", 79 | true }, 80 | 81 | { { "reflect-port", required_argument, NULL, 'P' }, 82 | "Port to reflect packets to", 83 | "", 84 | true }, 85 | 86 | { { "skb-mode", no_argument, NULL, 'S' }, 87 | "Install XDP program in SKB (AKA generic) mode" }, 88 | 89 | { { "native-mode", no_argument, NULL, 'N' }, 90 | "Install XDP program in native mode" }, 91 | 92 | { { "auto-mode", no_argument, NULL, 'A' }, 93 | "Auto-detect SKB or native mode" }, 94 | 95 | { { "force", no_argument, NULL, 'F' }, 96 | "Force install, replacing existing program on interface" }, 97 | 98 | { { "copy", no_argument, NULL, 'c' }, "Force copy mode" }, 99 | 100 | { { "zero-copy", no_argument, NULL, 'z' }, "Force zero-copy mode" }, 101 | 102 | { { "queue", required_argument, NULL, 'Q' }, 103 | "Configure interface receive queue for AF_XDP, default=0" }, 104 | 105 | { { "poll-mode", no_argument, NULL, 'p' }, 106 | "Use the poll() API waiting for packets to arrive" }, 107 | 108 | { { "unload", no_argument, NULL, 'U' }, 109 | "Unload XDP program instead of loading" }, 110 | 111 | { { "reuse-maps", no_argument, NULL, 'M' }, "Reuse pinned maps" }, 112 | 113 | { { "quiet", no_argument, NULL, 'q' }, "Quiet mode (no output)" }, 114 | 115 | { { "filename", required_argument, NULL, 1 }, 116 | "Load program from ", 117 | "" }, 118 | 119 | { { "progsec", required_argument, NULL, 2 }, 120 | "Load program in
of the ELF file", 121 | "
" }, 122 | 123 | { { 0, 0, NULL, 0 }, NULL, false } 124 | }; 125 | 126 | static bool global_exit; 127 | 128 | static struct xsk_umem_info *configure_xsk_umem(void *buffer, uint64_t size) 129 | { 130 | struct xsk_umem_info *umem; 131 | int ret; 132 | 133 | umem = calloc(1, sizeof(*umem)); 134 | if (!umem) 135 | return NULL; 136 | 137 | ret = xsk_umem__create(&umem->umem, buffer, size, &umem->fq, &umem->cq, 138 | NULL); 139 | if (ret) { 140 | errno = -ret; 141 | return NULL; 142 | } 143 | 144 | umem->buffer = buffer; 145 | return umem; 146 | } 147 | 148 | static uint64_t xsk_alloc_umem_frame(struct xsk_socket_info *xsk) 149 | { 150 | uint64_t frame; 151 | if (xsk->umem_frame_free == 0) 152 | return INVALID_UMEM_FRAME; 153 | 154 | frame = xsk->umem_frame_addr[--xsk->umem_frame_free]; 155 | xsk->umem_frame_addr[xsk->umem_frame_free] = INVALID_UMEM_FRAME; 156 | return frame; 157 | } 158 | 159 | static void xsk_free_umem_frame(struct xsk_socket_info *xsk, uint64_t frame) 160 | { 161 | assert(xsk->umem_frame_free < NUM_FRAMES); 162 | 163 | xsk->umem_frame_addr[xsk->umem_frame_free++] = frame; 164 | } 165 | 166 | static uint64_t xsk_umem_free_frames(struct xsk_socket_info *xsk) 167 | { 168 | return xsk->umem_frame_free; 169 | } 170 | 171 | static struct xsk_socket_info *xsk_configure_socket(struct config *cfg, 172 | struct xsk_umem_info *umem) 173 | { 174 | struct xsk_socket_config xsk_cfg; 175 | struct xsk_socket_info *xsk_info; 176 | uint32_t idx; 177 | uint32_t prog_id = 0; 178 | int i; 179 | int ret; 180 | 181 | xsk_info = calloc(1, sizeof(*xsk_info)); 182 | if (!xsk_info) 183 | return NULL; 184 | 185 | xsk_info->umem = umem; 186 | xsk_cfg.rx_size = XSK_RING_CONS__DEFAULT_NUM_DESCS; 187 | xsk_cfg.tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS; 188 | xsk_cfg.libbpf_flags = 0; 189 | xsk_cfg.xdp_flags = cfg->xdp_flags; 190 | xsk_cfg.bind_flags = cfg->xsk_bind_flags; 191 | 192 | ret = xsk_socket__create(&xsk_info->xsk, cfg->ifname, cfg->xsk_if_queue, 193 | umem->umem, &xsk_info->rx, &xsk_info->tx, 194 | &xsk_cfg); 195 | 196 | if (ret) 197 | goto error_exit; 198 | 199 | ret = bpf_get_link_xdp_id(cfg->ifindex, &prog_id, cfg->xdp_flags); 200 | if (ret) 201 | goto error_exit; 202 | 203 | /* Initialize umem frame allocation */ 204 | 205 | for (i = 0; i < NUM_FRAMES; i++) 206 | xsk_info->umem_frame_addr[i] = i * FRAME_SIZE; 207 | 208 | xsk_info->umem_frame_free = NUM_FRAMES; 209 | 210 | /* Stuff the receive path with buffers, we assume we have enough */ 211 | ret = xsk_ring_prod__reserve(&xsk_info->umem->fq, 212 | XSK_RING_PROD__DEFAULT_NUM_DESCS, &idx); 213 | 214 | if (ret != XSK_RING_PROD__DEFAULT_NUM_DESCS) 215 | goto error_exit; 216 | 217 | for (i = 0; i < XSK_RING_PROD__DEFAULT_NUM_DESCS; i++) 218 | *xsk_ring_prod__fill_addr(&xsk_info->umem->fq, idx++) = 219 | xsk_alloc_umem_frame(xsk_info); 220 | 221 | xsk_ring_prod__submit(&xsk_info->umem->fq, 222 | XSK_RING_PROD__DEFAULT_NUM_DESCS); 223 | 224 | return xsk_info; 225 | 226 | error_exit: 227 | errno = -ret; 228 | return NULL; 229 | } 230 | 231 | static void complete_tx(struct xsk_socket_info *xsk) 232 | { 233 | unsigned int completed; 234 | uint32_t idx_cq; 235 | 236 | if (!xsk->outstanding_tx) 237 | return; 238 | 239 | sendto(xsk_socket__fd(xsk->xsk), NULL, 0, MSG_DONTWAIT, NULL, 0); 240 | 241 | /* Collect/free completed TX buffers */ 242 | completed = xsk_ring_cons__peek( 243 | &xsk->umem->cq, XSK_RING_CONS__DEFAULT_NUM_DESCS, &idx_cq); 244 | 245 | if (completed > 0) { 246 | for (size_t i = 0; i < completed; i++) 247 | xsk_free_umem_frame( 248 | xsk, *xsk_ring_cons__comp_addr(&xsk->umem->cq, 249 | idx_cq++)); 250 | 251 | xsk_ring_cons__release(&xsk->umem->cq, completed); 252 | } 253 | } 254 | 255 | static bool process_packet(struct xsk_socket_info *xsk, uint64_t addr, 256 | uint32_t len, int udp4_out, int udp6_out, 257 | int reflect_port) 258 | { 259 | uint8_t *pkt = xsk_umem__get_data(xsk->umem->buffer, addr); 260 | 261 | int eth_type, ip_type; 262 | struct ethhdr *eth; 263 | struct iphdr *iphdr; 264 | struct ipv6hdr *ipv6hdr; 265 | struct udphdr *udphdr; 266 | void *data = (void *)pkt; 267 | void *data_end = (void *)pkt + len; 268 | struct hdr_cursor nh = { .pos = data }; 269 | 270 | eth_type = parse_ethhdr(&nh, data_end, ð); 271 | if (eth_type < 0) { 272 | return false; 273 | } 274 | 275 | bool use_ipv6 = false; 276 | 277 | if (eth_type == bpf_htons(ETH_P_IP)) { 278 | ip_type = parse_iphdr(&nh, data_end, &iphdr); 279 | } else if (eth_type == bpf_htons(ETH_P_IPV6)) { 280 | ip_type = parse_ip6hdr(&nh, data_end, &ipv6hdr); 281 | use_ipv6 = true; 282 | } else { 283 | return false; 284 | } 285 | 286 | // only support UDP for now 287 | if (ip_type != IPPROTO_UDP) 288 | return false; 289 | 290 | // invalid udp 291 | if (parse_udphdr(&nh, data_end, &udphdr) < 0) { 292 | return false; 293 | } 294 | 295 | ssize_t sent_bytes; 296 | if (use_ipv6) { 297 | struct sockaddr_in6 sin; 298 | sin.sin6_family = AF_INET6; 299 | inet_pton(AF_INET6, "::1", &sin.sin6_addr); 300 | sin.sin6_port = ntohs(reflect_port); 301 | sent_bytes = sendto(udp6_out, (void *)nh.pos, len, 0, 302 | (struct sockaddr *)&sin, sizeof(sin)); 303 | } else { 304 | struct sockaddr_in sin; 305 | sin.sin_family = AF_INET; 306 | sin.sin_addr.s_addr = inet_addr("0.0.0.0"); 307 | sin.sin_port = ntohs(reflect_port); 308 | sent_bytes = sendto(udp4_out, (void *)nh.pos, len, 0, 309 | (struct sockaddr *)&sin, sizeof(sin)); 310 | } 311 | 312 | if (sent_bytes == -1) { 313 | perror("sendto\n"); 314 | return false; 315 | } 316 | 317 | printf("sent %lu bytes over UDP to port %d\n", sent_bytes, 318 | reflect_port); 319 | 320 | return true; 321 | } 322 | 323 | static void handle_receive_packets(struct xsk_socket_info *xsk, int udp4_out, 324 | int udp6_out, int reflect_port) 325 | { 326 | unsigned int rcvd, stock_frames, i; 327 | uint32_t idx_rx = 0, idx_fq = 0; 328 | int ret; 329 | 330 | rcvd = xsk_ring_cons__peek(&xsk->rx, RX_BATCH_SIZE, &idx_rx); 331 | if (!rcvd) 332 | return; 333 | 334 | /* Stuff the ring with as much frames as possible */ 335 | stock_frames = 336 | xsk_prod_nb_free(&xsk->umem->fq, xsk_umem_free_frames(xsk)); 337 | 338 | if (stock_frames > 0) { 339 | ret = xsk_ring_prod__reserve(&xsk->umem->fq, stock_frames, 340 | &idx_fq); 341 | 342 | /* This should not happen, but just in case */ 343 | while (ret != stock_frames) 344 | ret = xsk_ring_prod__reserve(&xsk->umem->fq, rcvd, 345 | &idx_fq); 346 | 347 | for (i = 0; i < stock_frames; i++) 348 | *xsk_ring_prod__fill_addr(&xsk->umem->fq, idx_fq++) = 349 | xsk_alloc_umem_frame(xsk); 350 | 351 | xsk_ring_prod__submit(&xsk->umem->fq, stock_frames); 352 | } 353 | 354 | /* Process received packets */ 355 | for (i = 0; i < rcvd; i++) { 356 | uint64_t addr = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx)->addr; 357 | uint32_t len = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx++)->len; 358 | 359 | if (!process_packet(xsk, addr, len, udp4_out, udp6_out, 360 | reflect_port)) 361 | xsk_free_umem_frame(xsk, addr); 362 | } 363 | 364 | xsk_ring_cons__release(&xsk->rx, rcvd); 365 | 366 | /* Do we need to wake up the kernel for transmission */ 367 | complete_tx(xsk); 368 | } 369 | 370 | static void rx_and_process(struct config *cfg, 371 | struct xsk_socket_info *xsk_socket, int udp4_out, 372 | int udp6_out, int reflect_port) 373 | { 374 | struct pollfd fds[2]; 375 | int ret, nfds = 1; 376 | 377 | memset(fds, 0, sizeof(fds)); 378 | fds[0].fd = xsk_socket__fd(xsk_socket->xsk); 379 | fds[0].events = POLLIN; 380 | 381 | while (!global_exit) { 382 | if (cfg->xsk_poll_mode) { 383 | ret = poll(fds, nfds, -1); 384 | if (ret <= 0 || ret > 1) 385 | continue; 386 | } 387 | handle_receive_packets(xsk_socket, udp4_out, udp6_out, 388 | reflect_port); 389 | } 390 | } 391 | 392 | static void exit_application(int signal) 393 | { 394 | signal = signal; 395 | global_exit = true; 396 | } 397 | 398 | #ifndef PATH_MAX 399 | #define PATH_MAX 4096 400 | #endif 401 | 402 | const char *pin_basedir = "/sys/fs/bpf"; 403 | const char *map_name = "reflect_count"; 404 | 405 | /* Pinning maps under /sys/fs/bpf in subdir */ 406 | int pin_maps_in_bpf_object(struct bpf_object *bpf_obj, struct config *cfg) 407 | { 408 | char map_filename[PATH_MAX]; 409 | int err, len; 410 | 411 | len = snprintf(map_filename, PATH_MAX, "%s/%s/%s", pin_basedir, 412 | cfg->ifname, map_name); 413 | if (len < 0) { 414 | fprintf(stderr, "ERR: creating map_name\n"); 415 | return EXIT_FAIL_OPTION; 416 | } 417 | 418 | /* Existing/previous XDP prog might not have cleaned up */ 419 | if (access(map_filename, F_OK) != -1) { 420 | if (verbose) 421 | printf(" - Unpinning (remove) prev maps in %s/\n", 422 | cfg->pin_dir); 423 | 424 | /* Basically calls unlink(3) on map_filename */ 425 | err = bpf_object__unpin_maps(bpf_obj, cfg->pin_dir); 426 | if (err) { 427 | fprintf(stderr, "ERR: UNpinning maps in %s\n", 428 | cfg->pin_dir); 429 | return EXIT_FAIL_BPF; 430 | } 431 | } 432 | if (verbose) 433 | printf(" - Pinning maps in %s/\n", cfg->pin_dir); 434 | 435 | /* This will pin all maps in our bpf_object */ 436 | err = bpf_object__pin_maps(bpf_obj, cfg->pin_dir); 437 | if (err) 438 | return EXIT_FAIL_BPF; 439 | 440 | return 0; 441 | } 442 | 443 | int main(int argc, char **argv) 444 | { 445 | struct rlimit r = { RLIM_INFINITY, RLIM_INFINITY }; 446 | 447 | if (setrlimit(RLIMIT_MEMLOCK, &r)) { 448 | perror("setrlimit(RLIMIT_MEMLOCK)"); 449 | return 1; 450 | } 451 | 452 | int xsks_map_fd; 453 | void *packet_buffer; 454 | uint64_t packet_buffer_size; 455 | struct rlimit rlim = { RLIM_INFINITY, RLIM_INFINITY }; 456 | struct config cfg = { .ifindex = -1, 457 | .do_unload = false, 458 | .filename = "", 459 | .progsec = "xdp_ape_reflect" }; 460 | struct xsk_umem_info *umem; 461 | struct xsk_socket_info *xsk_socket; 462 | struct bpf_object *bpf_obj = NULL; 463 | 464 | /* Global shutdown handler */ 465 | signal(SIGINT, exit_application); 466 | 467 | /* Cmdline options can change progsec */ 468 | parse_cmdline_args(argc, argv, long_options, &cfg, __doc__); 469 | 470 | /* Required option */ 471 | if (cfg.ifindex == -1) { 472 | fprintf(stderr, "ERROR: Required option --dev missing\n\n"); 473 | usage(argv[0], __doc__, long_options, (argc == 1)); 474 | return EXIT_FAIL_OPTION; 475 | } 476 | 477 | /* Unload XDP program if requested */ 478 | if (cfg.do_unload) 479 | return xdp_link_detach(cfg.ifindex, cfg.xdp_flags, 0); 480 | 481 | int len = snprintf(cfg.pin_dir, PATH_MAX, "%s/%s", pin_basedir, 482 | cfg.ifname); 483 | if (len < 0) { 484 | fprintf(stderr, "ERR: creating pin dirname\n"); 485 | return EXIT_FAIL_OPTION; 486 | } 487 | 488 | /* Load custom program if configured */ 489 | if (cfg.filename[0] != 0) { 490 | struct bpf_map *map; 491 | 492 | bpf_obj = load_bpf_and_xdp_attach(&cfg); 493 | if (!bpf_obj) { 494 | /* Error handling done in load_bpf_and_xdp_attach() */ 495 | exit(EXIT_FAILURE); 496 | } 497 | 498 | /* We also need to load the xsks_map */ 499 | map = bpf_object__find_map_by_name(bpf_obj, "xsks_map"); 500 | xsks_map_fd = bpf_map__fd(map); 501 | if (xsks_map_fd < 0) { 502 | fprintf(stderr, "ERROR: no xsks map found: %s\n", 503 | strerror(xsks_map_fd)); 504 | exit(EXIT_FAILURE); 505 | } 506 | } 507 | 508 | int err; 509 | 510 | /* Use the --dev name as subdir for exporting/pinning maps */ 511 | if (!cfg.reuse_maps) { 512 | err = pin_maps_in_bpf_object(bpf_obj, &cfg); 513 | if (err) { 514 | fprintf(stderr, "ERR: pinning maps\n"); 515 | return err; 516 | } 517 | } 518 | 519 | /* Allow unlimited locking of memory, so all memory needed for packet 520 | * buffers can be locked. 521 | */ 522 | if (setrlimit(RLIMIT_MEMLOCK, &rlim)) { 523 | fprintf(stderr, "ERROR: setrlimit(RLIMIT_MEMLOCK) \"%s\"\n", 524 | strerror(errno)); 525 | exit(EXIT_FAILURE); 526 | } 527 | 528 | /* Allocate memory for NUM_FRAMES of the default XDP frame size */ 529 | packet_buffer_size = NUM_FRAMES * FRAME_SIZE; 530 | if (posix_memalign(&packet_buffer, 531 | getpagesize(), /* PAGE_SIZE aligned */ 532 | packet_buffer_size)) { 533 | fprintf(stderr, "ERROR: Can't allocate buffer memory \"%s\"\n", 534 | strerror(errno)); 535 | exit(EXIT_FAILURE); 536 | } 537 | 538 | /* Initialize shared packet_buffer for umem usage */ 539 | umem = configure_xsk_umem(packet_buffer, packet_buffer_size); 540 | if (umem == NULL) { 541 | fprintf(stderr, "ERROR: Can't create umem \"%s\"\n", 542 | strerror(errno)); 543 | exit(EXIT_FAILURE); 544 | } 545 | 546 | /* Open and configure the AF_XDP (xsk) socket */ 547 | xsk_socket = xsk_configure_socket(&cfg, umem); 548 | if (xsk_socket == NULL) { 549 | fprintf(stderr, "ERROR: Can't setup AF_XDP socket \"%s\"\n", 550 | strerror(errno)); 551 | exit(EXIT_FAILURE); 552 | } 553 | 554 | /* set up udp4 sender socket */ 555 | int udp4_sender_sock_fd; 556 | err = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 557 | if (err < 0) { 558 | perror("cannot create socket"); 559 | return err; 560 | } 561 | udp4_sender_sock_fd = err; 562 | 563 | struct sockaddr_in udp4_sender_sockaddr; 564 | 565 | memset((char *)&udp4_sender_sockaddr, 0, sizeof(udp4_sender_sockaddr)); 566 | udp4_sender_sockaddr.sin_family = AF_INET; 567 | udp4_sender_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); 568 | udp4_sender_sockaddr.sin_port = htons(0); 569 | 570 | err = bind(udp4_sender_sock_fd, 571 | (struct sockaddr *)&udp4_sender_sockaddr, 572 | sizeof(udp4_sender_sockaddr)); 573 | if (err < 0) { 574 | perror("bind failed"); 575 | return err; 576 | } 577 | 578 | /* set up udp6 sender socket */ 579 | int udp6_sender_sock_fd; 580 | err = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 581 | if (err < 0) { 582 | perror("cannot create socket"); 583 | return err; 584 | } 585 | udp6_sender_sock_fd = err; 586 | 587 | struct sockaddr_in6 udp6_sender_sockaddr; 588 | 589 | memset((char *)&udp6_sender_sockaddr, 0, sizeof(udp6_sender_sockaddr)); 590 | udp6_sender_sockaddr.sin6_family = AF_INET6; 591 | udp6_sender_sockaddr.sin6_addr = in6addr_any; 592 | udp6_sender_sockaddr.sin6_port = htons(0); 593 | 594 | err = bind(udp6_sender_sock_fd, 595 | (struct sockaddr *)&udp6_sender_sockaddr, 596 | sizeof(udp6_sender_sockaddr)); 597 | if (err < 0) { 598 | perror("bind failed"); 599 | return err; 600 | } 601 | 602 | /* Receive and count packets than drop them */ 603 | rx_and_process(&cfg, xsk_socket, udp4_sender_sock_fd, 604 | udp6_sender_sock_fd, cfg.reflect_port); 605 | 606 | /* Cleanup */ 607 | xsk_socket__delete(xsk_socket->xsk); 608 | xsk_umem__delete(umem->umem); 609 | xdp_link_detach(cfg.ifindex, cfg.xdp_flags, 0); 610 | close(udp4_sender_sock_fd); 611 | close(udp6_sender_sock_fd); 612 | 613 | return EXIT_OK; 614 | } 615 | -------------------------------------------------------------------------------- /xdp_user_scramble.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "common/common_params.h" 30 | #include "common/common_user_bpf_xdp.h" 31 | #include "common/common_libbpf.h" 32 | #include "headers/bpf_endian.h" 33 | #include "common/parsing_helpers.h" 34 | 35 | #define NUM_FRAMES 4096 36 | #define FRAME_SIZE XSK_UMEM__DEFAULT_FRAME_SIZE 37 | #define RX_BATCH_SIZE 64 38 | #define INVALID_UMEM_FRAME UINT64_MAX 39 | 40 | struct xsk_umem_info { 41 | struct xsk_ring_prod fq; 42 | struct xsk_ring_cons cq; 43 | struct xsk_umem *umem; 44 | void *buffer; 45 | }; 46 | 47 | struct xsk_socket_info { 48 | struct xsk_ring_cons rx; 49 | struct xsk_ring_prod tx; 50 | struct xsk_umem_info *umem; 51 | struct xsk_socket *xsk; 52 | 53 | uint64_t umem_frame_addr[NUM_FRAMES]; 54 | uint32_t umem_frame_free; 55 | 56 | uint32_t outstanding_tx; 57 | }; 58 | 59 | static inline __u32 xsk_ring_prod__free(struct xsk_ring_prod *r) 60 | { 61 | r->cached_cons = *r->consumer + r->size; 62 | return r->cached_cons - r->cached_prod; 63 | } 64 | 65 | static const char *__doc__ = "github.com/sevagh/ape\n"; 66 | 67 | static const struct option_wrapper long_options[] = { 68 | 69 | { { "help", no_argument, NULL, 'h' }, "Show help", false }, 70 | 71 | { { "dev", required_argument, NULL, 'd' }, 72 | "Operate on device ", 73 | "", 74 | true }, 75 | 76 | { { "max-sleep-ms", required_argument, NULL, 'm' }, 77 | "Nanosleep 0-max_sleep (ms) to delay packets", 78 | "", 79 | true }, 80 | 81 | { { "skb-mode", no_argument, NULL, 'S' }, 82 | "Install XDP program in SKB (AKA generic) mode" }, 83 | 84 | { { "native-mode", no_argument, NULL, 'N' }, 85 | "Install XDP program in native mode" }, 86 | 87 | { { "auto-mode", no_argument, NULL, 'A' }, 88 | "Auto-detect SKB or native mode" }, 89 | 90 | { { "force", no_argument, NULL, 'F' }, 91 | "Force install, replacing existing program on interface" }, 92 | 93 | { { "copy", no_argument, NULL, 'c' }, "Force copy mode" }, 94 | 95 | { { "zero-copy", no_argument, NULL, 'z' }, "Force zero-copy mode" }, 96 | 97 | { { "queue", required_argument, NULL, 'Q' }, 98 | "Configure interface receive queue for AF_XDP, default=0" }, 99 | 100 | { { "poll-mode", no_argument, NULL, 'p' }, 101 | "Use the poll() API waiting for packets to arrive" }, 102 | 103 | { { "unload", no_argument, NULL, 'U' }, 104 | "Unload XDP program instead of loading" }, 105 | 106 | { { "reuse-maps", no_argument, NULL, 'M' }, "Reuse pinned maps" }, 107 | 108 | { { "quiet", no_argument, NULL, 'q' }, "Quiet mode (no output)" }, 109 | 110 | { { "filename", required_argument, NULL, 1 }, 111 | "Load program from ", 112 | "" }, 113 | 114 | { { "progsec", required_argument, NULL, 2 }, 115 | "Load program in
of the ELF file", 116 | "
" }, 117 | 118 | { { 0, 0, NULL, 0 }, NULL, false } 119 | }; 120 | 121 | static bool global_exit; 122 | 123 | static void dawdle(int max_sleep_ms) 124 | { 125 | /* https://gist.github.com/justinloundagin/5536640 */ 126 | struct timespec tv; 127 | int msec = (int)(((double)random() / INT_MAX) * max_sleep_ms); 128 | tv.tv_sec = 0; 129 | tv.tv_nsec = 1000000 * msec; 130 | if (nanosleep(&tv, NULL) == -1) { 131 | perror("nanosleep"); 132 | } 133 | } 134 | 135 | static struct xsk_umem_info *configure_xsk_umem(void *buffer, uint64_t size) 136 | { 137 | struct xsk_umem_info *umem; 138 | int ret; 139 | 140 | umem = calloc(1, sizeof(*umem)); 141 | if (!umem) 142 | return NULL; 143 | 144 | ret = xsk_umem__create(&umem->umem, buffer, size, &umem->fq, &umem->cq, 145 | NULL); 146 | if (ret) { 147 | errno = -ret; 148 | return NULL; 149 | } 150 | 151 | umem->buffer = buffer; 152 | return umem; 153 | } 154 | 155 | static uint64_t xsk_alloc_umem_frame(struct xsk_socket_info *xsk) 156 | { 157 | uint64_t frame; 158 | if (xsk->umem_frame_free == 0) 159 | return INVALID_UMEM_FRAME; 160 | 161 | frame = xsk->umem_frame_addr[--xsk->umem_frame_free]; 162 | xsk->umem_frame_addr[xsk->umem_frame_free] = INVALID_UMEM_FRAME; 163 | return frame; 164 | } 165 | 166 | static void xsk_free_umem_frame(struct xsk_socket_info *xsk, uint64_t frame) 167 | { 168 | assert(xsk->umem_frame_free < NUM_FRAMES); 169 | 170 | xsk->umem_frame_addr[xsk->umem_frame_free++] = frame; 171 | } 172 | 173 | static uint64_t xsk_umem_free_frames(struct xsk_socket_info *xsk) 174 | { 175 | return xsk->umem_frame_free; 176 | } 177 | 178 | static struct xsk_socket_info *xsk_configure_socket(struct config *cfg, 179 | struct xsk_umem_info *umem) 180 | { 181 | struct xsk_socket_config xsk_cfg; 182 | struct xsk_socket_info *xsk_info; 183 | uint32_t idx; 184 | uint32_t prog_id = 0; 185 | int i; 186 | int ret; 187 | 188 | xsk_info = calloc(1, sizeof(*xsk_info)); 189 | if (!xsk_info) 190 | return NULL; 191 | 192 | xsk_info->umem = umem; 193 | xsk_cfg.rx_size = XSK_RING_CONS__DEFAULT_NUM_DESCS; 194 | xsk_cfg.tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS; 195 | xsk_cfg.libbpf_flags = 0; 196 | xsk_cfg.xdp_flags = cfg->xdp_flags; 197 | xsk_cfg.bind_flags = cfg->xsk_bind_flags; 198 | 199 | ret = xsk_socket__create(&xsk_info->xsk, cfg->ifname, cfg->xsk_if_queue, 200 | umem->umem, &xsk_info->rx, &xsk_info->tx, 201 | &xsk_cfg); 202 | 203 | if (ret) 204 | goto error_exit; 205 | 206 | ret = bpf_get_link_xdp_id(cfg->ifindex, &prog_id, cfg->xdp_flags); 207 | if (ret) 208 | goto error_exit; 209 | 210 | /* Initialize umem frame allocation */ 211 | 212 | for (i = 0; i < NUM_FRAMES; i++) 213 | xsk_info->umem_frame_addr[i] = i * FRAME_SIZE; 214 | 215 | xsk_info->umem_frame_free = NUM_FRAMES; 216 | 217 | /* Stuff the receive path with buffers, we assume we have enough */ 218 | ret = xsk_ring_prod__reserve(&xsk_info->umem->fq, 219 | XSK_RING_PROD__DEFAULT_NUM_DESCS, &idx); 220 | 221 | if (ret != XSK_RING_PROD__DEFAULT_NUM_DESCS) 222 | goto error_exit; 223 | 224 | for (i = 0; i < XSK_RING_PROD__DEFAULT_NUM_DESCS; i++) 225 | *xsk_ring_prod__fill_addr(&xsk_info->umem->fq, idx++) = 226 | xsk_alloc_umem_frame(xsk_info); 227 | 228 | xsk_ring_prod__submit(&xsk_info->umem->fq, 229 | XSK_RING_PROD__DEFAULT_NUM_DESCS); 230 | 231 | return xsk_info; 232 | 233 | error_exit: 234 | errno = -ret; 235 | return NULL; 236 | } 237 | 238 | static void complete_tx(struct xsk_socket_info *xsk) 239 | { 240 | unsigned int completed; 241 | uint32_t idx_cq; 242 | 243 | if (!xsk->outstanding_tx) 244 | return; 245 | 246 | sendto(xsk_socket__fd(xsk->xsk), NULL, 0, MSG_DONTWAIT, NULL, 0); 247 | 248 | /* Collect/free completed TX buffers */ 249 | completed = xsk_ring_cons__peek( 250 | &xsk->umem->cq, XSK_RING_CONS__DEFAULT_NUM_DESCS, &idx_cq); 251 | 252 | if (completed > 0) { 253 | for (int i = 0; i < completed; i++) 254 | xsk_free_umem_frame( 255 | xsk, *xsk_ring_cons__comp_addr(&xsk->umem->cq, 256 | idx_cq++)); 257 | 258 | xsk_ring_cons__release(&xsk->umem->cq, completed); 259 | } 260 | } 261 | 262 | static bool process_packet(struct xsk_socket_info *xsk, uint64_t addr, 263 | uint32_t len, int udp4_out, int udp6_out, 264 | int max_sleep_ms) 265 | { 266 | uint8_t *pkt = xsk_umem__get_data(xsk->umem->buffer, addr); 267 | 268 | int eth_type, ip_type; 269 | struct ethhdr *eth; 270 | struct iphdr *iphdr; 271 | struct ipv6hdr *ipv6hdr; 272 | struct udphdr *udphdr; 273 | void *data = (void *)pkt; 274 | void *data_end = (void *)pkt + len; 275 | struct hdr_cursor nh = { .pos = data }; 276 | 277 | eth_type = parse_ethhdr(&nh, data_end, ð); 278 | if (eth_type < 0) { 279 | return false; 280 | } 281 | 282 | bool use_ipv6 = false; 283 | 284 | if (eth_type == bpf_htons(ETH_P_IP)) { 285 | ip_type = parse_iphdr(&nh, data_end, &iphdr); 286 | } else if (eth_type == bpf_htons(ETH_P_IPV6)) { 287 | ip_type = parse_ip6hdr(&nh, data_end, &ipv6hdr); 288 | use_ipv6 = true; 289 | } else { 290 | return false; 291 | } 292 | 293 | // only support UDP for now 294 | if (ip_type != IPPROTO_UDP) 295 | return false; 296 | 297 | // invalid udp 298 | if (parse_udphdr(&nh, data_end, &udphdr) < 0) { 299 | return false; 300 | } 301 | 302 | // sleep randomly to scramble 303 | dawdle(max_sleep_ms); 304 | 305 | ssize_t sent_bytes; 306 | if (use_ipv6) { 307 | struct sockaddr_in6 sin; 308 | sin.sin6_family = AF_INET6; 309 | sin.sin6_port = udphdr->dest; 310 | inet_pton(AF_INET6, "::1", &sin.sin6_addr); 311 | sent_bytes = sendto(udp6_out, (void *)nh.pos, len, 0, 312 | (struct sockaddr *)&sin, sizeof(sin)); 313 | } else { 314 | struct sockaddr_in sin; 315 | sin.sin_family = AF_INET; 316 | sin.sin_port = udphdr->dest; 317 | sin.sin_addr.s_addr = inet_addr("127.0.0.1"); 318 | sent_bytes = sendto(udp4_out, (void *)nh.pos, len, 0, 319 | (struct sockaddr *)&sin, sizeof(sin)); 320 | } 321 | 322 | return true; 323 | } 324 | 325 | static void handle_receive_packets(struct xsk_socket_info *xsk, int udp4_out, 326 | int udp6_out, int max_sleep_ms) 327 | { 328 | unsigned int rcvd, stock_frames, i; 329 | uint32_t idx_rx = 0, idx_fq = 0; 330 | int ret; 331 | 332 | rcvd = xsk_ring_cons__peek(&xsk->rx, RX_BATCH_SIZE, &idx_rx); 333 | if (!rcvd) 334 | return; 335 | 336 | /* Stuff the ring with as much frames as possible */ 337 | stock_frames = 338 | xsk_prod_nb_free(&xsk->umem->fq, xsk_umem_free_frames(xsk)); 339 | 340 | if (stock_frames > 0) { 341 | ret = xsk_ring_prod__reserve(&xsk->umem->fq, stock_frames, 342 | &idx_fq); 343 | 344 | /* This should not happen, but just in case */ 345 | while (ret != stock_frames) 346 | ret = xsk_ring_prod__reserve(&xsk->umem->fq, rcvd, 347 | &idx_fq); 348 | 349 | for (i = 0; i < stock_frames; i++) 350 | *xsk_ring_prod__fill_addr(&xsk->umem->fq, idx_fq++) = 351 | xsk_alloc_umem_frame(xsk); 352 | 353 | xsk_ring_prod__submit(&xsk->umem->fq, stock_frames); 354 | } 355 | 356 | /* Process received packets */ 357 | for (i = 0; i < rcvd; i++) { 358 | uint64_t addr = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx)->addr; 359 | uint32_t len = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx++)->len; 360 | 361 | if (!process_packet(xsk, addr, len, udp4_out, udp6_out, 362 | max_sleep_ms)) 363 | xsk_free_umem_frame(xsk, addr); 364 | } 365 | 366 | xsk_ring_cons__release(&xsk->rx, rcvd); 367 | 368 | /* Do we need to wake up the kernel for transmission */ 369 | complete_tx(xsk); 370 | } 371 | 372 | static void rx_and_process(struct config *cfg, 373 | struct xsk_socket_info *xsk_socket, int udp4_out, 374 | int udp6_out) 375 | { 376 | struct pollfd fds[2]; 377 | int ret, nfds = 1; 378 | 379 | memset(fds, 0, sizeof(fds)); 380 | fds[0].fd = xsk_socket__fd(xsk_socket->xsk); 381 | fds[0].events = POLLIN; 382 | 383 | while (!global_exit) { 384 | if (cfg->xsk_poll_mode) { 385 | ret = poll(fds, nfds, -1); 386 | if (ret <= 0 || ret > 1) 387 | continue; 388 | } 389 | handle_receive_packets(xsk_socket, udp4_out, udp6_out, 390 | cfg->max_sleep_ms); 391 | } 392 | } 393 | 394 | static void exit_application(int signal) 395 | { 396 | signal = signal; 397 | global_exit = true; 398 | } 399 | 400 | #ifndef PATH_MAX 401 | #define PATH_MAX 4096 402 | #endif 403 | 404 | const char *pin_basedir = "/sys/fs/bpf"; 405 | const char *map_name = "scramble_count"; 406 | 407 | /* Pinning maps under /sys/fs/bpf in subdir */ 408 | int pin_maps_in_bpf_object(struct bpf_object *bpf_obj, struct config *cfg) 409 | { 410 | char map_filename[PATH_MAX]; 411 | int err, len; 412 | 413 | len = snprintf(map_filename, PATH_MAX, "%s/%s/%s", pin_basedir, 414 | cfg->ifname, map_name); 415 | if (len < 0) { 416 | fprintf(stderr, "ERR: creating map_name\n"); 417 | return EXIT_FAIL_OPTION; 418 | } 419 | 420 | /* Existing/previous XDP prog might not have cleaned up */ 421 | if (access(map_filename, F_OK) != -1) { 422 | if (verbose) 423 | printf(" - Unpinning (remove) prev maps in %s/\n", 424 | cfg->pin_dir); 425 | 426 | /* Basically calls unlink(3) on map_filename */ 427 | err = bpf_object__unpin_maps(bpf_obj, cfg->pin_dir); 428 | if (err) { 429 | fprintf(stderr, "ERR: UNpinning maps in %s\n", 430 | cfg->pin_dir); 431 | return EXIT_FAIL_BPF; 432 | } 433 | } 434 | if (verbose) 435 | printf(" - Pinning maps in %s/\n", cfg->pin_dir); 436 | 437 | /* This will pin all maps in our bpf_object */ 438 | err = bpf_object__pin_maps(bpf_obj, cfg->pin_dir); 439 | if (err) 440 | return EXIT_FAIL_BPF; 441 | 442 | return 0; 443 | } 444 | 445 | int main(int argc, char **argv) 446 | { 447 | int xsks_map_fd, err; 448 | void *packet_buffer; 449 | uint64_t packet_buffer_size; 450 | struct rlimit rlim = { RLIM_INFINITY, RLIM_INFINITY }; 451 | struct config cfg = { .ifindex = -1, 452 | .do_unload = false, 453 | .filename = "", 454 | .progsec = "xdp_ape_scramble" }; 455 | struct xsk_umem_info *umem; 456 | struct xsk_socket_info *xsk_socket; 457 | struct bpf_object *bpf_obj = NULL; 458 | 459 | /* Allow unlimited locking of memory, so all memory needed for packet 460 | * buffers can be locked. 461 | */ 462 | if (setrlimit(RLIMIT_MEMLOCK, &rlim)) { 463 | fprintf(stderr, "ERROR: setrlimit(RLIMIT_MEMLOCK) \"%s\"\n", 464 | strerror(errno)); 465 | exit(EXIT_FAILURE); 466 | } 467 | 468 | /* Global shutdown handler */ 469 | signal(SIGINT, exit_application); 470 | 471 | /* Cmdline options can change progsec */ 472 | parse_cmdline_args(argc, argv, long_options, &cfg, __doc__); 473 | 474 | /* Required option */ 475 | if (cfg.ifindex == -1) { 476 | fprintf(stderr, "ERROR: Required option --dev missing\n\n"); 477 | usage(argv[0], __doc__, long_options, (argc == 1)); 478 | return EXIT_FAIL_OPTION; 479 | } 480 | 481 | /* Unload XDP program if requested */ 482 | if (cfg.do_unload) 483 | return xdp_link_detach(cfg.ifindex, cfg.xdp_flags, 0); 484 | 485 | int len = snprintf(cfg.pin_dir, PATH_MAX, "%s/%s", pin_basedir, 486 | cfg.ifname); 487 | if (len < 0) { 488 | fprintf(stderr, "ERR: creating pin dirname\n"); 489 | return EXIT_FAIL_OPTION; 490 | } 491 | 492 | /* Load custom program if configured */ 493 | if (cfg.filename[0] != 0) { 494 | struct bpf_map *map; 495 | 496 | bpf_obj = load_bpf_and_xdp_attach(&cfg); 497 | if (!bpf_obj) { 498 | /* Error handling done in load_bpf_and_xdp_attach() */ 499 | exit(EXIT_FAILURE); 500 | } 501 | 502 | /* We also need to load the xsks_map */ 503 | map = bpf_object__find_map_by_name(bpf_obj, "xsks_map"); 504 | xsks_map_fd = bpf_map__fd(map); 505 | if (xsks_map_fd < 0) { 506 | fprintf(stderr, "ERROR: no xsks map found: %s\n", 507 | strerror(xsks_map_fd)); 508 | exit(EXIT_FAILURE); 509 | } 510 | } 511 | 512 | /* Use the --dev name as subdir for exporting/pinning maps */ 513 | if (!cfg.reuse_maps) { 514 | err = pin_maps_in_bpf_object(bpf_obj, &cfg); 515 | if (err) { 516 | fprintf(stderr, "ERR: pinning maps\n"); 517 | return err; 518 | } 519 | } 520 | 521 | /* Allocate memory for NUM_FRAMES of the default XDP frame size */ 522 | packet_buffer_size = NUM_FRAMES * FRAME_SIZE; 523 | if (posix_memalign(&packet_buffer, 524 | getpagesize(), /* PAGE_SIZE aligned */ 525 | packet_buffer_size)) { 526 | fprintf(stderr, "ERROR: Can't allocate buffer memory \"%s\"\n", 527 | strerror(errno)); 528 | exit(EXIT_FAILURE); 529 | } 530 | 531 | /* Initialize shared packet_buffer for umem usage */ 532 | umem = configure_xsk_umem(packet_buffer, packet_buffer_size); 533 | if (umem == NULL) { 534 | fprintf(stderr, "ERROR: Can't create umem \"%s\"\n", 535 | strerror(errno)); 536 | exit(EXIT_FAILURE); 537 | } 538 | 539 | /* Open and configure the AF_XDP (xsk) socket */ 540 | xsk_socket = xsk_configure_socket(&cfg, umem); 541 | if (xsk_socket == NULL) { 542 | fprintf(stderr, "ERROR: Can't setup AF_XDP socket \"%s\"\n", 543 | strerror(errno)); 544 | exit(EXIT_FAILURE); 545 | } 546 | 547 | /* set up udp4 sender socket */ 548 | int udp4_sender_sock_fd; 549 | err = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 550 | if (err < 0) { 551 | perror("cannot create socket"); 552 | return err; 553 | } 554 | udp4_sender_sock_fd = err; 555 | 556 | struct sockaddr_in udp4_sender_sockaddr; 557 | 558 | memset((char *)&udp4_sender_sockaddr, 0, sizeof(udp4_sender_sockaddr)); 559 | udp4_sender_sockaddr.sin_family = AF_INET; 560 | udp4_sender_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); 561 | udp4_sender_sockaddr.sin_port = htons(0); 562 | 563 | err = bind(udp4_sender_sock_fd, 564 | (struct sockaddr *)&udp4_sender_sockaddr, 565 | sizeof(udp4_sender_sockaddr)); 566 | if (err < 0) { 567 | perror("bind failed"); 568 | return err; 569 | } 570 | 571 | /* set up udp6 sender socket */ 572 | int udp6_sender_sock_fd; 573 | err = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 574 | if (err < 0) { 575 | perror("cannot create socket"); 576 | return err; 577 | } 578 | udp6_sender_sock_fd = err; 579 | 580 | struct sockaddr_in6 udp6_sender_sockaddr; 581 | 582 | memset((char *)&udp6_sender_sockaddr, 0, sizeof(udp6_sender_sockaddr)); 583 | udp6_sender_sockaddr.sin6_family = AF_INET6; 584 | udp6_sender_sockaddr.sin6_addr = in6addr_any; 585 | udp6_sender_sockaddr.sin6_port = htons(0); 586 | 587 | err = bind(udp6_sender_sock_fd, 588 | (struct sockaddr *)&udp6_sender_sockaddr, 589 | sizeof(udp6_sender_sockaddr)); 590 | if (err < 0) { 591 | perror("bind failed"); 592 | return err; 593 | } 594 | 595 | /* Receive and count packets than drop them */ 596 | rx_and_process(&cfg, xsk_socket, udp4_sender_sock_fd, 597 | udp6_sender_sock_fd); 598 | 599 | /* Cleanup */ 600 | xsk_socket__delete(xsk_socket->xsk); 601 | xsk_umem__delete(umem->umem); 602 | xdp_link_detach(cfg.ifindex, cfg.xdp_flags, 0); 603 | close(udp4_sender_sock_fd); 604 | close(udp6_sender_sock_fd); 605 | 606 | return EXIT_OK; 607 | } 608 | --------------------------------------------------------------------------------